Android-runtime: WebAssembly won't initialize without the debugger

Created on 23 Dec 2019  路  3Comments  路  Source: NativeScript/android-runtime

Environment
Provide version numbers for the following components (information can be retrieved by running tns info in your project folder or by inspecting the package.json of the project):

  • CLI: 6.3.0
  • Cross-platform modules: 6.3.1
  • Android Runtime: 6.3.0
  • iOS Runtime (if applicable): Not Applicable
  • Plugin(s): None

Devices:

  • Oneplus 7T Pro McLaren - Android 10
  • Emulator - Android 9/SDK 29.

Describe the bug

I'm not sure why, but I can't get the promise from WebAssembly.compile(...) or WebAssembly.instantiate(...) to resolve without connecting the chrome debugger to my app and stepping through JavaScript.

What makes this really odd, is that it doesn't matter what JS-code I step through.

To Reproduce

  1. Start the app normally with tns run android
  2. Attempt to initialize the WASM code.
  3. The promise is never resolved or rejected.

But it will resolve if I use the chrome debugger:
Steps to load with the debugger.

  1. Start the app with tns debug android
  2. Attempt to initialize the WASM code.
  3. Connect the chrome-debugger via devtools://devtools/bundled/inspector.html?experiments=true&ws=localhost:40000
  4. Enter and run this code
(function() { debugger; })();
  1. Step to next line a few time and let it continue.
  2. If the promise wasn't resolved, press arrow-up to go back in the console.

Expected behavior
The promise from WebAssembly.compile(...) or WebAssembly.instantiate(...) to either resolve or reject without the debugger.

Sample project

Demo project here: https://github.com/m-abs/tns-wasm
WASM code is a very simple "Hello World" program.

Additional context

bug ready for release

Most helpful comment

The WebAssembly's async compile API registers some ongoing work on one of v8::Platform's background threads, and eventually in the future, a task will be posted to the foreground thread to resolve the promise for the compilation.

Currently the Android runtime doesn't handle this scenario properly. For this to work we need to pump the V8 message loop and run the microtasks:

while (v8::platform::PumpMessageLoop(Runtime::platform, isolate, MessageLoopBehavior::kDoNotWait)) {
    isolate->RunMicrotasks();
}

We will investigate what would be the best approach to handle this case in the runtime.

Until then you could use WebAssembly's synchronous API:

let utf8ToString = (h, p) => {
    let s = "";
    for (let i = p; h[i]; i++) {
        s += String.fromCharCode(h[i]);
    }
    return s;
};

let buffer;
const importObject = {
    env: {
        puts(index) {
            console.log(utf8ToString(buffer, index));
        }
    }
};

const wasmCode = new Uint8Array([
    0, 97, 115, 109, 1, 0, 0, 0, 1, 138, 128, 128, 128, 0, 2, 96, 1, 
    127, 1, 127, 96, 0, 1, 127, 2, 140, 128, 128, 128, 0, 1, 3, 101, 
    110, 118, 4, 112, 117, 116, 115, 0, 0, 3, 130, 128, 128, 128, 0, 
    1, 1, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 
    128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 
    128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 
    110, 0, 1, 10, 143, 128, 128, 128, 0, 1, 137, 128, 128, 128, 0, 0, 
    65, 16, 16, 0, 26, 65, 0, 11, 11, 148, 128, 128, 128, 0, 1, 0, 65, 
    16, 11, 14, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 
    100, 33, 0
]);

const wasmModule = new WebAssembly.Module(wasmCode);
const moduleInstance = new WebAssembly.Instance(wasmModule, importObject);
buffer = new Uint8Array(moduleInstance.exports.memory.buffer);
let result = moduleInstance.exports.main();
console.log(result);

All 3 comments

I looked briefly into this. I'm not 100% sure why this issue is occurring with this method of bootstrapping Wasm, but the other common way of bootstrapping Wasm works fine for me.

It might have something to do with the way it loads and converts it into an int8 array. In my test version -- I require the JS file that has the array already defined. So it could be bit different issue causing the issue.

Thank you for responding so fast.

I looked briefly into this. I'm not 100% sure why this issue is occurring with this method of bootstrapping Wasm, but the other common way of bootstrapping Wasm works fine for me.

Which method did you use?
I used this one because {N} doesn't support fetch+arraybuffer and emscripten's loader didn't work either.

It might have something to do with the way it loads and converts it into an int8 array.

I've tried using a hardcoded Uint8Array with the WASM code, unfortunately no change.

The WebAssembly's async compile API registers some ongoing work on one of v8::Platform's background threads, and eventually in the future, a task will be posted to the foreground thread to resolve the promise for the compilation.

Currently the Android runtime doesn't handle this scenario properly. For this to work we need to pump the V8 message loop and run the microtasks:

while (v8::platform::PumpMessageLoop(Runtime::platform, isolate, MessageLoopBehavior::kDoNotWait)) {
    isolate->RunMicrotasks();
}

We will investigate what would be the best approach to handle this case in the runtime.

Until then you could use WebAssembly's synchronous API:

let utf8ToString = (h, p) => {
    let s = "";
    for (let i = p; h[i]; i++) {
        s += String.fromCharCode(h[i]);
    }
    return s;
};

let buffer;
const importObject = {
    env: {
        puts(index) {
            console.log(utf8ToString(buffer, index));
        }
    }
};

const wasmCode = new Uint8Array([
    0, 97, 115, 109, 1, 0, 0, 0, 1, 138, 128, 128, 128, 0, 2, 96, 1, 
    127, 1, 127, 96, 0, 1, 127, 2, 140, 128, 128, 128, 0, 1, 3, 101, 
    110, 118, 4, 112, 117, 116, 115, 0, 0, 3, 130, 128, 128, 128, 0, 
    1, 1, 4, 132, 128, 128, 128, 0, 1, 112, 0, 0, 5, 131, 128, 128, 
    128, 0, 1, 0, 1, 6, 129, 128, 128, 128, 0, 0, 7, 145, 128, 128, 
    128, 0, 2, 6, 109, 101, 109, 111, 114, 121, 2, 0, 4, 109, 97, 105, 
    110, 0, 1, 10, 143, 128, 128, 128, 0, 1, 137, 128, 128, 128, 0, 0, 
    65, 16, 16, 0, 26, 65, 0, 11, 11, 148, 128, 128, 128, 0, 1, 0, 65, 
    16, 11, 14, 104, 101, 108, 108, 111, 44, 32, 119, 111, 114, 108, 
    100, 33, 0
]);

const wasmModule = new WebAssembly.Module(wasmCode);
const moduleInstance = new WebAssembly.Instance(wasmModule, importObject);
buffer = new Uint8Array(moduleInstance.exports.memory.buffer);
let result = moduleInstance.exports.main();
console.log(result);
Was this page helpful?
0 / 5 - 0 ratings