emscripten-core / emscripten

Emscripten: An LLVM-to-WebAssembly Compiler
Other
25.71k stars 3.3k forks source link

BINARYEN_ASYNC_COMPILATION=0 in Chrome #6092

Closed FalkorX closed 5 years ago

FalkorX commented 6 years ago

I am trying to build a file using WebAssembly with BINARYEN_ASYNC_COMPILATION=0 and SINGLE_FILE=1 (due to the context I'm running in, I cannot really have asynchronous events in my Worker). On Chrome this gives me the message

Uncaught TypeError: Cannot assign to read only property '__GLOBAL__sub_I_bind_cpp' of object '[object Object]'

which refers to the second command in

var real___GLOBAL__sub_I_bind_cpp = asm["__GLOBAL__sub_I_bind_cpp"]; asm["__GLOBAL__sub_I_bind_cpp"] = function() {...}

emitted in the output file.

Since the only thing the code snippet does is inject two additional assertions, I uncommented this and all similar pieces of code and now it works fine.

Is it maybe possible to prevent the emitting of the mentioned code pieces when compiling with BINARYEN_ASYNC_COMPILATION=0?

kripken commented 6 years ago

Is this only in Chrome (as the title suggests)? What is the behavior in other browsers, that can help figure out what's wrong?

FalkorX commented 6 years ago

So apparently in Chrome (63) and Firefox (57) Workers, the code runs fine, asynchronously as well as synchronously.

When I try to run it in an AudioWorkletGlobalScope (which is currently only available in Chrome (63 and Canary 65) behind a flag), it fails with the issue described above. When trying asynchronous compilation, the promise returned by addModule (a.k.a. the file has been loaded) resolves before wasm is compiled, so this is not an option. Also, please be aware that AudioWorkletGlobalScope does intentionally forbid XHR and methods like setTimeout, so SINGLE_FILE=1 and BINARYEN_ASYNC_COMPILATION=0 are the only reasonable options for compiling in this context.

I am also aware that AudioWorklet is experimental and the issue that asm["__GLOBAL__sub_I_bind_cpp"] is read-only might be an error on their side, but I wanted to raise this question rather sooner than later.

juj commented 6 years ago

Is the error something about embind/bind.cpp related, or does the error occur on tiny hello world cases as well without embind?

FalkorX commented 6 years ago

It occurs even with an empty input file, disregard of using --bind or not. It also does not matter if Module.ENVIRONMENT='WEB' or Module.ENVIRONMENT='WORKER'.

kripken commented 6 years ago

What is addModule? An AudioWorklet feature, I guess?

If wasm compilation is synchronous, it should not be possible for anything to execute before the wasm is ready, as the JS will finish execution with the wasm ready. (Unless it happens even before the JS?) So sounds like this might be an AudioWorklet bug, or perhaps this is a limitation of that environment.

Does -s MODULARIZE_INSTANCE=1 help here? That would not put those things it complains about in the global scope (maybe there's a limit on mutability in the global scope? random guess)

FalkorX commented 6 years ago

Yes, you call addModule('proc.js') to register your AudioWorkletProcessor (which would be defined in proc.js in this case and which would contain the wasm). It returns a promise which tells you that it is safe to instantiate the proecssor.

And the synchronous compilation works fine as long as I comment out the lines that I have mentioned. As a matter of fact someone else had the same problem and their resolution was also to use synchronous instantiation of the wasm code, although they used WebAssembly directly and not Emscripten (and thus not relying on the wrapper code).

Emscripten tells me that is doesn't know -s MODULARIZE_INSTANCE=1. And -s MODULARIZE=1 breaks the code, because I specifically have to call a function from the global scope.

FalkorX commented 6 years ago

Turns out, when using -s ASSERTIONS=0 the lines that break the code will not be emitted and everything works fine.

FalkorX commented 6 years ago

Do you think it might be a good idea to leave the asm object alone, just in case? Like so:

var real___GLOBAL__sub_I_bind_cpp = asm["__GLOBAL__sub_I_bind_cpp"];
var __GLOBAL__sub_I_bind_cpp = Module["__GLOBAL__sub_I_bind_cpp"] = function() {
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
  return real___GLOBAL__sub_I_bind_cpp.apply(null, arguments);
};

Edit: the code is emitted by the create_receiving function in emscripten.py, line 1314. The code snippet looks like this:

  receiving = ''
  if not settings['ASSERTIONS']:
    runtime_assertions = ''
  else:
    # assert on the runtime being in a valid state when calling into compiled code. The only exceptions are
    # some support code
    runtime_assertions = '''
  assert(runtimeInitialized, 'you need to wait for the runtime to be ready (e.g. wait for main() to be called)');
  assert(!runtimeExited, 'the runtime was exited (use NO_EXIT_RUNTIME to keep it alive after main() exits)');
'''
    receiving = '\n'.join(['var real_' + s + ' = asm["' + s + '"]; asm["' + s + '''"] = function() {''' + runtime_assertions + '''  return real_''' + s + '''.apply(null, arguments);
};
''' for s in exported_implemented_functions if s not in ['_memcpy', '_memset', 'runPostSets', '_emscripten_replace_memory', '__start_module']])
  if not settings['SWAPPABLE_ASM_MODULE']:
    receiving += ';\n'.join(['var ' + s + ' = Module["' + s + '"] = asm["' + s + '"]' for s in exported_implemented_functions + function_tables(function_table_data, settings)])
  else:
    receiving += 'Module["asm"] = asm;\n' + ';\n'.join(['var ' + s + ' = Module["' + s + '"] = function() {' + runtime_assertions + '  return Module["asm"]["' + s + '"].apply(null, arguments) }' for s in exported_implemented_functions + function_tables(function_table_data, settings)])
  receiving += ';\n'
kripken commented 6 years ago

MODULARIZE_INSTANCE requires latest incoming (it just landed). You can also use MODULARIZE but you need to create your own instance of the exported module.

It's possible that not modifying the asm object is useful, worth investigating. But note that there are a few corner cases there, it's different with assertions, and with sync/async compilation. We do have tests for all that, though, if they pass we should be ok.

FalkorX commented 6 years ago

Oh I didn't know that. I'll give it a try as soon as it is available on the precompiled windows sdk. And MODULARIZE won't really work for me, as I have no means of instantiating it besides pre-js and post-js.

Looking into that would be nice. I guess we are only interested in inserting asserts in calls to Module['funcname'] and not so much to Module['asm']['funcname']? If so, it should be relatively easy to realize.

stale[bot] commented 5 years ago

This issue has been automatically marked as stale because there has been no activity in the past year. It will be closed automatically if no further activity occurs in the next 7 days. Feel free to re-open at any time if this issue is still relevant.

VictorQueiroz commented 4 years ago

I have a build with the following configuration:

-s SINGLE_FILE=1
-s WASM_ASYNC_COMPILATION=0
-s MODULARIZE=1

And I get this error:

TypeError: Cannot assign to read only property '__wasm_call_ctors' of object '[object Object]'

When WASM_ASYNC_COMPILATION option is enabled, everything works fine. Under WebWorker and Node.j, it works fine but fails on AudioWorklet scope. Any idea how can we fix this without disabling WASM_ASYNC_COMPILATION?

After adding this line before the property-redefinition lines fixed the problem but seems to be quite tedious to do that on each build. I tried Babel plugins but could not find any that'd fix this automatically.

Screenshot_20200309_003238

I did not investigate the side effect of this change but might be a simple change to EMScripten code generator?

kripken commented 4 years ago

@VictorQueiroz the line with asm =? not sure why that would help, that's odd. Which browser do you see that fix necessary on?

(And, do you have simple steps to reproduce this problem locally?)