Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions emcc.py
Original file line number Diff line number Diff line change
Expand Up @@ -1298,7 +1298,8 @@ def check(input_file):
if shared.Building.is_wasm_only() and shared.Settings.EVAL_CTORS:
logging.debug('disabling EVAL_CTORS, as in wasm-only mode it hurts more than it helps. TODO: a wasm version of it')
shared.Settings.EVAL_CTORS = 0
if shared.Settings.BINARYEN_ASYNC_COMPILATION == 1 and shared.Building.is_wasm_only():
# async compilation requires wasm-only mode, and also not interpreting (the interpreter needs sync input)
if shared.Settings.BINARYEN_ASYNC_COMPILATION == 1 and shared.Building.is_wasm_only() and 'interpret' not in shared.Settings.BINARYEN_METHOD:
# async compilation requires a swappable module - we swap it in when it's ready
shared.Settings.SWAPPABLE_ASM_MODULE = 1
else:
Expand Down Expand Up @@ -2339,7 +2340,7 @@ def un_src(): # use this if you want to modify the script and need it to be inli
else:
assert len(asm_mods) == 0, 'no --separate-asm means no client code mods are possible'

if shared.Settings.BINARYEN:
if shared.Settings.BINARYEN and not shared.Settings.BINARYEN_ASYNC_COMPILATION:
# We need to load the wasm file before anything else, it has to be synchronously ready TODO: optimize
un_src()
script_inline = '''
Expand Down
25 changes: 20 additions & 5 deletions src/preamble.js
Original file line number Diff line number Diff line change
Expand Up @@ -2153,16 +2153,28 @@ function integrateWasmJS(Module) {

function getBinary() {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we still need this getBinary() function as well, or could we just have one getBinaryPromise() function that also contains the content of this function?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We use it when we need synchronous access to it - for the interpreter, or when async compilation is off. Also, I think it's nice it's separate, conceptually.

var binary;
if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
if (Module['wasmBinary']) {
binary = Module['wasmBinary'];
assert(binary, "on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)");
binary = new Uint8Array(binary);
} else {
} else if (Module['readBinary']) {
binary = Module['readBinary'](wasmBinaryFile);
} else {
throw "on the web, we need the wasm binary to be preloaded and set on Module['wasmBinary']. emcc.py will do that for you when generating HTML (but not JS)";
}
return binary;
}

function getBinaryPromise() {
// if we don't have the binary yet, and have the Fetch api, use that
if (!Module['wasmBinary'] && typeof fetch === 'function') {
return fetch(wasmBinaryFile).then(function(response) { return response.arrayBuffer() });
}
// Otherwise, getBinary should be able to get it synchronously
return new Promise(function(resolve, reject) {
resolve(getBinary());
});
}

// do-method functions

function doJustAsm(global, env, providedBuffer) {
Expand Down Expand Up @@ -2212,7 +2224,9 @@ function integrateWasmJS(Module) {
#if BINARYEN_ASYNC_COMPILATION
Module['printErr']('asynchronously preparing wasm');
addRunDependency('wasm-instantiate'); // we can't run yet
WebAssembly.instantiate(getBinary(), info).then(function(output) {
getBinaryPromise().then(function(binary) {
Copy link
Collaborator

@juj juj Mar 9, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main .html file is supposed to download the .wasm file. At this point, is it not possible that the .wasm file download is still in progress, but has not finished yet, and therefore getBinaryPromise() will kick off a second parallel download redundantly?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not as it currently works - the html downloads the wasm binary before starting to do anything else. Another reason it's good to get rid of that thing ;)

return WebAssembly.instantiate(binary, info)
}).then(function(output) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey, since working on this, can you change the #if #endif in here to a #if #else #endif to avoid the "unreachable code" console warnings below?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea, fixing.

// receiveInstance() will swap in the exports (to Module.asm) so they can be called
receiveInstance(output.instance);
removeRunDependency('wasm-instantiate');
Expand All @@ -2221,7 +2235,7 @@ function integrateWasmJS(Module) {
Module['quit'](1, reason);
});
return {}; // no exports yet; we'll fill them in later
#endif
#else
var instance;
try {
instance = new WebAssembly.Instance(new WebAssembly.Module(getBinary()), info)
Expand All @@ -2234,6 +2248,7 @@ function integrateWasmJS(Module) {
}
receiveInstance(instance);
return exports;
#endif
}

function doWasmPolyfill(global, env, providedBuffer, method) {
Expand Down
10 changes: 10 additions & 0 deletions src/shell.js
Original file line number Diff line number Diff line change
Expand Up @@ -169,6 +169,16 @@ else if (ENVIRONMENT_IS_WEB || ENVIRONMENT_IS_WORKER) {
return xhr.responseText;
};

if (ENVIRONMENT_IS_WORKER) {
Module['readBinary'] = function read(url) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, false);
xhr.responseType = 'arraybuffer';
xhr.send(null);
return xhr.response;
};
}

Module['readAsync'] = function readAsync(url, onload, onerror) {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
Expand Down
21 changes: 13 additions & 8 deletions tests/test_browser.py
Original file line number Diff line number Diff line change
Expand Up @@ -1352,7 +1352,7 @@ def test_egl_width_height(self):
Popen([PYTHON, EMCC, '-O2', os.path.join(self.get_dir(), 'test_egl_width_height.c'), '-o', 'page.html', '-lEGL', '-lGL']).communicate()
self.run_browser('page.html', 'Should print "(300, 150)" -- the size of the canvas in pixels', '/report_result?1')

def test_worker(self):
def do_test_worker(self, args=[]):
# Test running in a web worker
open('file.dat', 'w').write('data for worker')
html_file = open('main.html', 'w')
Expand All @@ -1374,15 +1374,17 @@ def test_worker(self):
''')
html_file.close()

# no file data
for file_data in [0, 1]:
print 'file data', file_data
output = Popen([PYTHON, EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'] + (['--preload-file', 'file.dat'] if file_data else []) , stdout=PIPE, stderr=PIPE).communicate()
assert len(output[0]) == 0, output[0]
assert os.path.exists('worker.js'), output
if not file_data: self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone
for file_data in [1, 0]:
cmd = [PYTHON, EMCC, path_from_root('tests', 'hello_world_worker.cpp'), '-o', 'worker.js'] + (['--preload-file', 'file.dat'] if file_data else []) + args
print cmd
subprocess.check_call(cmd)
assert os.path.exists('worker.js')
self.run_browser('main.html', '', '/report_result?hello%20from%20worker,%20and%20|' + ('data%20for%20w' if file_data else '') + '|')

def test_worker(self):
self.do_test_worker()
self.assertContained('you should not see this text when in a worker!', run_js('worker.js')) # code should run standalone too

def test_chunked_synchronous_xhr(self):
main = 'chunked_sync_xhr.html'
worker_filename = "download_and_checksum_worker.js"
Expand Down Expand Up @@ -3337,6 +3339,9 @@ def test_binaryen_async(self):
print opts, expect
self.btest('binaryen_async.c', expected=str(expect), args=['-s', 'BINARYEN=1', '--shell-file', 'shell.html'] + opts)

def test_binaryen_worker(self):
self.do_test_worker(['-s', 'WASM=1'])

def test_utf8_textdecoder(self):
self.btest('benchmark_utf8.cpp', expected='0', args=['--embed-file', path_from_root('tests/utf8_corpus.txt') + '@/utf8_corpus.txt'])

Expand Down