mohanson/pywasm

Reattaching imported functions

Closed this issue · 2 comments

void4 commented

I call a runtime instance several times, but each time I need to reattach env functions (because apparently, the old ones refer to outdated data). On first try

runtime.imps = newimps

does not seem to work. Is this possible? I'd prefer to do this without having to reload the entire runtime (which would necessitate loading the .wasm binary from the filessystem again etc.)

You should directly modify the relevant elements in the store object, because WebAssembly has an initialization step, and the imps parameter will not be used after this step.

https://github.com/mohanson/pywasm/blob/master/pywasm/execution.py#L296

https://github.com/mohanson/pywasm/blob/master/pywasm/execution.py#L205

But I don't approve of this usage. You may be able to add a reset() function, or some other things, to solve it at the application layer.

void4 commented

My problem is that the functions need to refer to some data in an object distance, which may be different for every invocation.

Because there may be many threads/interleaved invocations of several interpreters, I can't declare this instance to be global inside the function. I tried the nonlocal keyword, but it seems to refer to the old data anyway, it is apparently bound when the function is defined.

I also can't reinstantiate the Runtime because I need the memory to be preserved.

I'll try to modify it directly. Update:

import pywasm

data = board = legal = None

def env_abort(_: pywasm.Ctx):
  return

def env_board(ctx: pywasm.Ctx, index: int):
  return data[index]

def env_rights(ctx: pywasm.Ctx):
  r_3f = int(board.can_claim_threefold_repetition())
  r_50 = int(board.can_claim_fifty_moves())

  r_K = int(board.has_kingside_castling_rights(chess.WHITE))
  r_Q = int(board.has_queenside_castling_rights(chess.WHITE))
  r_k = int(board.has_kingside_castling_rights(chess.WHITE))
  r_q = int(board.has_queenside_castling_rights(chess.WHITE))

  if board.has_legal_en_passant():
    epdata = board.ep_square
  else:
    epdata = 0

  return r3f << 6 | r_50 << 5 | epdata << 4 | r_K << 3 | r_Q << 2 | r_k << 1 | r_q

def env_randint(ctx: pywasm.Ctx, max: int):
  return randint(0, max-1)

print("OUTSIDE", legal)

def env_legal(ctx: pywasm.Ctx, index: int):
  print("INSIDE", legal)
  return legal[index]

def env_maxlegal(ctx: pywasm.Ctx):
  return len(legal)

def env_color(ctx: pywasm.Ctx):
  return 1 if board.turn == chess.WHITE else 0

def env_log(ctx: pywasm.Ctx, msg: int):
  print("Msg", msg)
  return 0



imps = {
'env': {
  'abort': env_abort,
  "color": env_color,
  "board": env_board,
  "rights": env_rights,
  "maxlegal": env_maxlegal,
  "randint": env_randint,
  "legal": env_legal,
  "log": env_log
}
}

# Update data (only for visualization purposes)
data = board = legal = 1

# Redefine functions

def env_maxlegal(ctx: pywasm.Ctx):
  return len(legal)

# [...]

runtime = "players/b/random.wasm"

runtime = pywasm.load(runtime, imps)

for index, thing in enumerate(runtime.store.function_list):
    if isinstance(thing, pywasm.execution.HostFunc):
        runtime.store.function_list[index].hostcode = locals()[thing.hostcode.__name__]

I'm sorry you had to witness this absolute hack, haha

It works! Thank you :)