diff --git a/emcc.py b/emcc.py index 0ac6ae1e0bbee..7d48da5930b31 100755 --- a/emcc.py +++ b/emcc.py @@ -1216,12 +1216,19 @@ def is_supported_link_flag(f): link_flags = [f for f in link_flags if is_supported_link_flag(f[1])] + if shared.Settings.MINIMAL_RUNTIME: + # Remove the default exported functions 'memcpy', 'memset', 'malloc', 'free', etc. - those should only be linked in if used + shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = [] + if shared.Settings.USE_PTHREADS: # These runtime methods are called from worker.js shared.Settings.EXPORTED_RUNTIME_METHODS += ['establishStackSpace', 'dynCall_ii'] if shared.Settings.STACK_OVERFLOW_CHECK: - shared.Settings.EXPORTED_RUNTIME_METHODS += ['writeStackCookie', 'checkStackCookie', 'abortStackOverflow'] + if shared.Settings.MINIMAL_RUNTIME: + shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$abortStackOverflow'] + else: + shared.Settings.EXPORTED_RUNTIME_METHODS += ['writeStackCookie', 'checkStackCookie', 'abortStackOverflow'] if shared.Settings.MODULARIZE_INSTANCE: shared.Settings.MODULARIZE = 1 @@ -1520,9 +1527,6 @@ def is_supported_link_flag(f): if options.shell_path == shared.path_from_root('src', 'shell.html'): options.shell_path = shared.path_from_root('src', 'shell_minimal_runtime.html') - # Remove the default exported functions 'memcpy', 'memset', 'malloc', 'free', etc. - those should only be linked in if used - shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = [] - if shared.Settings.ASSERTIONS and shared.Settings.MINIMAL_RUNTIME: # In ASSERTIONS-builds, functions UTF8ArrayToString() and stringToUTF8Array() (which are not JS library functions), both # use warnOnce(), which in MINIMAL_RUNTIME is a JS library function, so explicitly have to mark dependency to warnOnce() @@ -1796,9 +1800,6 @@ def check_human_readable_list(items): assert not shared.Settings.ALLOW_MEMORY_GROWTH, 'memory growth is not supported with shared asm.js modules' if shared.Settings.MINIMAL_RUNTIME: - if shared.Settings.ALLOW_MEMORY_GROWTH: - logging.warning('-s ALLOW_MEMORY_GROWTH=1 is not yet supported with -s MINIMAL_RUNTIME=1') - if shared.Settings.EMTERPRETIFY: exit_with_error('-s EMTERPRETIFY=1 is not supported with -s MINIMAL_RUNTIME=1') @@ -1838,10 +1839,6 @@ def check_human_readable_list(items): if shared.Settings.ALLOW_MEMORY_GROWTH: shared.Settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['emscripten_trace_report_memory_layout'] - # MINIMAL_RUNTIME always use separate .asm.js file for best performance and memory usage - if shared.Settings.MINIMAL_RUNTIME and not shared.Settings.WASM: - options.separate_asm = True - if shared.Settings.STANDALONE_WASM: if not shared.Settings.WASM_BACKEND: exit_with_error('STANDALONE_WASM is only available in the upstream wasm backend path') @@ -3499,6 +3496,7 @@ def minify_html(filename, options): size_before = os.path.getsize(filename) start_time = time.time() run_process(shared.NODE_JS + [shared.path_from_root('third_party', 'html-minifier', 'cli.js'), filename, '-o', filename] + opts) + elapsed_time = time.time() - start_time size_after = os.path.getsize(filename) delta = size_after - size_before diff --git a/emscripten.py b/emscripten.py index 3f1d353f8df7a..6b3e12258fa09 100644 --- a/emscripten.py +++ b/emscripten.py @@ -1581,7 +1581,7 @@ def setup_function_pointers(function_table_sigs): def create_basic_funcs(function_table_sigs, invoke_function_names): basic_funcs = shared.Settings.RUNTIME_FUNCS_TO_IMPORT - if shared.Settings.STACK_OVERFLOW_CHECK: + if shared.Settings.STACK_OVERFLOW_CHECK and not shared.Settings.MINIMAL_RUNTIME: basic_funcs += ['abortStackOverflow'] if shared.Settings.EMTERPRETIFY: basic_funcs += ['abortStackOverflowEmterpreter'] diff --git a/src/library.js b/src/library.js index 9c686be62b38b..1d6c41f49df8c 100644 --- a/src/library.js +++ b/src/library.js @@ -1288,6 +1288,7 @@ LibraryManager.library = { }, #if MINIMAL_RUNTIME + $abortStackOverflow__deps: ['$stackSave'], $abortStackOverflow: function(allocSize) { abort('Stack overflow! Attempted to allocate ' + allocSize + ' bytes on the stack, but stack has only ' + (STACK_MAX - stackSave() + allocSize) + ' bytes available!'); }, diff --git a/src/library_trace.js b/src/library_trace.js index 27812a49f1894..ca606732e0c0e 100644 --- a/src/library_trace.js +++ b/src/library_trace.js @@ -265,7 +265,9 @@ var LibraryTracing = { 'stack_base': STACK_BASE, 'stack_top': STACKTOP, 'stack_max': STACK_MAX, +#if !MINIMAL_RUNTIME 'dynamic_base': DYNAMIC_BASE, +#endif 'dynamic_top': HEAP32[DYNAMICTOP_PTR>>2], 'total_memory': HEAP8.length }; diff --git a/src/modules.js b/src/modules.js index 0b9cbeaae64fd..b5780aea6b721 100644 --- a/src/modules.js +++ b/src/modules.js @@ -416,10 +416,6 @@ function exportRuntime() { 'makeBigInt', 'dynCall', 'getCompilerSetting', - 'stackSave', - 'stackRestore', - 'stackAlloc', - 'establishStackSpace', 'print', 'printErr', 'getTempRet0', @@ -431,12 +427,18 @@ function exportRuntime() { if (!MINIMAL_RUNTIME) { runtimeElements.push('Pointer_stringify'); runtimeElements.push('warnOnce'); + runtimeElements.push('stackSave'); + runtimeElements.push('stackRestore'); + runtimeElements.push('stackAlloc'); + runtimeElements.push('establishStackSpace'); } if (STACK_OVERFLOW_CHECK) { runtimeElements.push('writeStackCookie'); runtimeElements.push('checkStackCookie'); - runtimeElements.push('abortStackOverflow'); + if (!MINIMAL_RUNTIME) { + runtimeElements.push('abortStackOverflow'); + } } if (USE_PTHREADS) { diff --git a/src/postamble_minimal.js b/src/postamble_minimal.js index cf58c69836cd5..6fd79ae4b1924 100644 --- a/src/postamble_minimal.js +++ b/src/postamble_minimal.js @@ -65,14 +65,14 @@ var imports = { #endif }; -#if ASSERTIONS -if (!Module['wasm']) throw 'Must load WebAssembly Module in to variable Module.wasm before adding compiled output .js script to the DOM'; -#endif - #if DECLARE_ASM_MODULE_EXPORTS /*** ASM_MODULE_EXPORTS_DECLARES ***/ #endif +#if ASSERTIONS +// In synchronous Wasm compilation mode, Module['wasm'] should contain a typed array of the Wasm object data. +if (!Module['wasm']) throw 'Must load WebAssembly Module in to variable Module.wasm before adding compiled output .js script to the DOM'; +#endif WebAssembly.instantiate(Module['wasm'], imports).then(function(output) { var asm = output.instance.exports; #if DECLARE_ASM_MODULE_EXPORTS == 0 diff --git a/src/preamble_minimal.js b/src/preamble_minimal.js index f0570e7fe5f40..bd65c5ff630fa 100644 --- a/src/preamble_minimal.js +++ b/src/preamble_minimal.js @@ -14,9 +14,7 @@ function assert(condition, text) { function abort(what) { throw what; } -function abortStackOverflow(allocSize) { - abort('Stack overflow when attempting to allocate ' + allocSize + ' bytes on the stack!'); -} + var tempRet0 = 0; var setTempRet0 = function(value) { tempRet0 = value; @@ -45,9 +43,6 @@ var GLOBAL_BASE = {{{ GLOBAL_BASE }}}, STACK_BASE = {{{ getQuoted('STACK_BASE') }}}, STACKTOP = STACK_BASE, STACK_MAX = {{{ getQuoted('STACK_MAX') }}} -#if MEMORYPROFILER - , DYNAMIC_BASE = {{{ getQuoted('DYNAMIC_BASE') }}} -#endif #if USES_DYNAMIC_ALLOC , DYNAMICTOP_PTR = {{{ DYNAMICTOP_PTR }}}; #endif @@ -103,6 +98,26 @@ assert({{{ WASM_MEM_MAX }}} % WASM_PAGE_SIZE == 0); assert(buffer.byteLength === {{{ TOTAL_MEMORY }}}); #endif // ASSERTIONS +#if ALLOW_MEMORY_GROWTH +// In ALLOW_MEMORY_GROWTH, we need to be able to re-initialize the +// typed array buffer and heap views to the buffer whenever the heap +// is resized. +var HEAP8, HEAP16, HEAP32, HEAPU8, HEAPU16, HEAPU32, HEAPF32, HEAPF64; +function updateGlobalBufferAndViews(b) { + buffer = b; + HEAP8 = new Int8Array(b); + HEAP16 = new Int16Array(b); + HEAP32 = new Int32Array(b); + HEAPU8 = new Uint8Array(b); + HEAPU16 = new Uint16Array(b); + HEAPU32 = new Uint32Array(b); + HEAPF32 = new Float32Array(b); + HEAPF64 = new Float64Array(b); +} +updateGlobalBufferAndViews(buffer); +#else +// In non-ALLOW_MEMORY_GROWTH scenario, we only need to initialize +// the heap once, so optimize code size to do it statically here. var HEAP8 = new Int8Array(buffer); var HEAP16 = new Int16Array(buffer); var HEAP32 = new Int32Array(buffer); @@ -111,6 +126,7 @@ var HEAPU16 = new Uint16Array(buffer); var HEAPU32 = new Uint32Array(buffer); var HEAPF32 = new Float32Array(buffer); var HEAPF64 = new Float64Array(buffer); +#endif #if !WASM HEAPU8.set(new Uint8Array(Module['mem']), GLOBAL_BASE); @@ -135,6 +151,7 @@ var wasmTable = new WebAssembly.Table({ #endif // WASM #include "runtime_stack_check.js" +#include "runtime_assertions.js" #if ASSERTIONS var runtimeInitialized = false; diff --git a/src/shell_minimal.js b/src/shell_minimal.js index 62a1dc4c8f698..bbe4bdd31ac2d 100644 --- a/src/shell_minimal.js +++ b/src/shell_minimal.js @@ -33,9 +33,13 @@ if (ENVIRONMENT_IS_NODE) { #if WASM Module['wasm'] = fs.readFileSync(__dirname + '/{{{ TARGET_BASENAME }}}.wasm'); #else +#if SEPARATE_ASM eval(fs.readFileSync(__dirname + '/{{{ TARGET_BASENAME }}}.asm.js')+''); +#endif +#if MEM_INIT_METHOD == 1 Module['mem'] = fs.readFileSync(__dirname + '/{{{ TARGET_BASENAME }}}.mem'); #endif +#endif } #endif @@ -77,6 +81,12 @@ function ready() { #if USE_PTHREADS +#if !MODULARIZE +// In MODULARIZE mode _scriptDir needs to be captured already at the very top of the page immediately when the page is parsed, so it is generated there +// before the page load. In non-MODULARIZE modes generate it here. +var _scriptDir = (typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined; +#endif + var ENVIRONMENT_IS_PTHREAD; if (!ENVIRONMENT_IS_PTHREAD) ENVIRONMENT_IS_PTHREAD = false; // ENVIRONMENT_IS_PTHREAD=true will have been preset in pthread-main.js. Make it false in the main runtime thread. @@ -84,16 +94,18 @@ if (typeof ENVIRONMENT_IS_PTHREAD === 'undefined') { // ENVIRONMENT_IS_PTHREAD=true will have been preset in pthread-main.js. Make it false in the main runtime thread. // N.B. this line needs to appear without 'var' keyword to avoid 'var hoisting' from occurring. (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/var) ENVIRONMENT_IS_PTHREAD = false; -} else { +} +#if MODULARIZE +else { var buffer = {{{EXPORT_NAME}}}.buffer; var tempDoublePtr = {{{EXPORT_NAME}}}.tempDoublePtr; var STATICTOP = {{{EXPORT_NAME}}}.STATICTOP; - var DYNAMIC_BASE = {{{EXPORT_NAME}}}.DYNAMIC_BASE; var DYNAMICTOP_PTR = {{{EXPORT_NAME}}}.DYNAMICTOP_PTR; var STACK_BASE = {{{EXPORT_NAME}}}.STACK_BASE; var STACKTOP = {{{EXPORT_NAME}}}.STACKTOP; var STACK_MAX = {{{EXPORT_NAME}}}.STACK_MAX; } +#endif var currentScriptUrl = typeof _scriptDir !== 'undefined' ? _scriptDir : ((typeof document !== 'undefined' && document.currentScript) ? document.currentScript.src : undefined); #endif // USE_PTHREADS diff --git a/src/shell_minimal_runtime.html b/src/shell_minimal_runtime.html index 013cb800f6e9d..a2a501bf45e60 100644 --- a/src/shell_minimal_runtime.html +++ b/src/shell_minimal_runtime.html @@ -29,45 +29,98 @@ }); } -#if MODULARIZE -#if WASM +#if !MODULARIZE + var Module = {}; +#endif + +function revokeURL(url) { + URL.revokeObjectURL(url) +} + +// Depending on the build flags that one uses, different files need to be downloaded +// to load the compiled page. What follows is a matrix of all different combinations that +// affect how code is downloaded. When developing your own shell file, you can copy the whole +// matrix, or just focus on a single/specific set of download schemes to use. + +#if MODULARIZE && WASM + // Modularize + no streaming wasm compilation, no separate .mem init file Promise.all([s('{{{ TARGET_BASENAME }}}.js'), b('{{{ TARGET_BASENAME }}}.wasm')]).then((r) => { - r[0]({ wasm: r[1] }); + var js = r[0]; // Detour the JS code to a separate variable to avoid instantiating with 'r' as this to avoid strict ECMAScript/Firefox GC problems that cause a leak, see https://bugzilla.mozilla.org/show_bug.cgi?id=1540101 + js({ wasm: r[1] }); }); -#else -#if MEM_INIT_METHOD == 1 - Promise.all([s('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { - r[0]({ asm: r[1], mem: r[2] }); +#endif + +#if MODULARIZE && !WASM && !SEPARATE_ASM && MEM_INIT_METHOD == 0 + // Modularize + asm.js embedded in main runtime .js file, no separate .mem init file + s('{{{ TARGET_BASENAME }}}.js').then((r) => { + r(); }); -#else +#endif + +#if MODULARIZE && !WASM && !SEPARATE_ASM && MEM_INIT_METHOD == 1 + // Modularize + asm.js embedded in main runtime .js file, separate .mem init file + Promise.all([s('{{{ TARGET_BASENAME }}}.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { + var js = r[0]; // Detour the JS code to a separate variable to avoid instantiating with 'r' as this to avoid strict ECMAScript/Firefox GC problems that cause a leak, see https://bugzilla.mozilla.org/show_bug.cgi?id=1540101 + js({ mem: r[1] }); + }); +#endif + +#if MODULARIZE && !WASM && SEPARATE_ASM && MEM_INIT_METHOD == 0 + // Modularize + asm.js in its own file, no separate .mem init file Promise.all([s('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js')]).then((r) => { - r[0]({ asm: r[1] }); + var js = r[0]; // Detour the JS code to a separate variable to avoid instantiating with 'r' as this to avoid strict ECMAScript/Firefox GC problems that cause a leak, see https://bugzilla.mozilla.org/show_bug.cgi?id=1540101 + js({ asm: r[1] }); }); -#endif // MEM_INIT_METHOD -#endif // WASM -#else // MODULARIZE - var Module = {}; -#if WASM +#endif + +#if MODULARIZE && !WASM && SEPARATE_ASM && MEM_INIT_METHOD == 1 + // Modularize + asm.js in its own file, separate .mem init file + Promise.all([s('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { + var js = r[0]; // Detour the JS code to a separate variable to avoid instantiating with 'r' as this to avoid strict ECMAScript/Firefox GC problems that cause a leak, see https://bugzilla.mozilla.org/show_bug.cgi?id=1540101 + js({ asm: r[1], mem: r[2] }); + }); +#endif + +#if !MODULARIZE && WASM + // No modularize, no streaming wasm compilation, no separate .mem init file Promise.all([b('{{{ TARGET_BASENAME }}}.js'), b('{{{ TARGET_BASENAME }}}.wasm')]).then((r) => { Module.wasm = r[1]; var url = URL.createObjectURL(new Blob([r[0]], { type: 'application/javascript' })); - s(url).then(() => { URL.revokeObjectURL(url) }); + s(url).then(() => { revokeURL(url) }); }); -#else -#if MEM_INIT_METHOD == 1 - Promise.all([b('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { - Module.mem = r[2]; +#endif + +#if !MODULARIZE && !WASM && !SEPARATE_ASM && MEM_INIT_METHOD == 0 + // No modularize, asm.js embedded in main runtime .js file, no separate .mem init file + s('{{{ TARGET_BASENAME }}}.js'); +#endif + +#if !MODULARIZE && !WASM && !SEPARATE_ASM && MEM_INIT_METHOD == 1 + // No modularize, asm.js embedded in main runtime .js file, separate .mem init file + Promise.all([b('{{{ TARGET_BASENAME }}}.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { + Module.mem = r[1]; var url = URL.createObjectURL(new Blob([r[0]], { type: 'application/javascript' })); - s(url).then(() => { URL.revokeObjectURL(url) }); + s(url).then(() => { revokeURL(url) }); }); -#else +#endif + +#if !MODULARIZE && !WASM && SEPARATE_ASM && MEM_INIT_METHOD == 0 + // No modularize, asm.js in its own file, no separate .mem init file Promise.all([b('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js')]).then((r) => { var url = URL.createObjectURL(new Blob([r[0]], { type: 'application/javascript' })); - s(url).then(() => { URL.revokeObjectURL(url) }); + s(url).then(() => { revokeURL(url) }); + }); +#endif + +#if !MODULARIZE && !WASM && SEPARATE_ASM && MEM_INIT_METHOD == 1 + // No modularize, asm.js in its own file, separate .mem init file + Promise.all([b('{{{ TARGET_BASENAME }}}.js'), s('{{{ TARGET_BASENAME }}}.asm.js'), b('{{{ TARGET_BASENAME }}}.mem')]).then((r) => { + Module.mem = r[2]; + var url = URL.createObjectURL(new Blob([r[0]], { type: 'application/javascript' })); + s(url).then(() => { revokeURL(url) }); }); -#endif // MEM_INIT_METHOD -#endif // WASM -#endif // MODULARIZE +#endif + diff --git a/tests/test_browser.py b/tests/test_browser.py index ac0422ce9501a..ef3da059b058b 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -3335,6 +3335,13 @@ def test_async_stack_overflow(self): def test_async_bad_whitelist(self): self.btest('browser/async_bad_whitelist.cpp', '0', args=['-s', 'ASYNCIFY', '-s', 'ASYNCIFY_WHITELIST=["waka"]', '--profiling']) + # Tests that when building with -s MINIMAL_RUNTIME=1, the build can use -s MODULARIZE=1 as well. + @no_wasm_backend('MINIMAL_RUNTIME not yet for wasm backend') + def test_minimal_runtime_modularize(self): + create_test_file('src.cpp', self.with_report_result(open(path_from_root('tests', 'browser_test_hello_world.c')).read())) + self.compile_btest(['src.cpp', '-o', 'test.html', '-s', 'MODULARIZE=1', '-s', 'MINIMAL_RUNTIME=1']) + self.run_browser('test.html', None, '/report_result?0') + @requires_sync_compilation def test_modularize(self): for opts in [[], ['-O1'], ['-O2', '-profiling'], ['-O2'], ['-O2', '--closure', '1']]: @@ -4055,6 +4062,15 @@ def test_canvas_size_proxy(self): def test_custom_messages_proxy(self): self.btest(path_from_root('tests', 'custom_messages_proxy.c'), expected='1', args=['--proxy-to-worker', '--shell-file', path_from_root('tests', 'custom_messages_proxy_shell.html'), '--post-js', path_from_root('tests', 'custom_messages_proxy_postjs.js')]) + # Tests that when building with -s MINIMAL_RUNTIME=1, the build can use -s WASM=0 --separate-asm as well. + @no_wasm_backend('asm.js') + def test_minimal_runtime_separate_asm(self): + for opts in [['-s', 'MINIMAL_RUNTIME=1']]: + print(opts) + create_test_file('src.cpp', self.with_report_result(open(path_from_root('tests', 'browser_test_hello_world.c')).read())) + self.compile_btest(['src.cpp', '-o', 'test.html', '-s', 'WASM=0', '--separate-asm'] + opts) + self.run_browser('test.html', None, '/report_result?0') + @no_wasm_backend('asm.js') def test_separate_asm(self): for opts in [['-O0'], ['-O1'], ['-O2'], ['-O2', '--closure', '1']]: diff --git a/tests/test_core.py b/tests/test_core.py index d7513768e729a..803a24c6734a3 100644 --- a/tests/test_core.py +++ b/tests/test_core.py @@ -1925,6 +1925,19 @@ def test_runtime_stacksave(self): src = open(path_from_root('tests', 'core', 'test_runtime_stacksave.c')).read() self.do_run(src, 'success') + # Tests that -s MINIMAL_RUNTIME=1 builds can utilize -s ALLOW_MEMORY_GROWTH=1 option. + @no_wasm_backend('MINIMAL_RUNTIME not yet for wasm backend') + def test_minimal_runtime_memorygrowth(self): + if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): + self.skipTest('test needs to modify memory growth') + self.set_setting('MINIMAL_RUNTIME', 1) + src = open(path_from_root('tests', 'core', 'test_memorygrowth.c')).read() + # Fail without memory growth + self.do_run(src, 'OOM', assert_returncode=None) + # Win with it + self.emcc_args += ['-Wno-almost-asm', '-s', 'ALLOW_MEMORY_GROWTH'] + self.do_run(src, '*pre: hello,4.955*\n*hello,4.955*\n*hello,4.955*') + def test_memorygrowth(self): if self.has_changed_setting('ALLOW_MEMORY_GROWTH'): self.skipTest('test needs to modify memory growth') diff --git a/tests/test_other.py b/tests/test_other.py index bd5d05480f16e..6662a14ac7829 100644 --- a/tests/test_other.py +++ b/tests/test_other.py @@ -9537,11 +9537,11 @@ def test_minimal_runtime_code_size(self): hello_webgl2_sources = hello_webgl_sources + ['-s', 'USE_WEBGL2=1'] test_cases = [ - (asmjs + opts, hello_world_sources, {'a.html': 981, 'a.js': 289, 'a.asm.js': 113, 'a.mem': 6}), - (opts, hello_world_sources, {'a.html': 968, 'a.js': 604, 'a.wasm': 86}), - (asmjs + opts, hello_webgl_sources, {'a.html': 881, 'a.js': 4918, 'a.asm.js': 11139, 'a.mem': 321}), - (opts, hello_webgl_sources, {'a.html': 857, 'a.js': 4874, 'a.wasm': 8841}), - (opts, hello_webgl2_sources, {'a.html': 857, 'a.js': 5362, 'a.wasm': 8841}) # Compare how WebGL2 sizes stack up with WebGL 1 + (asmjs + opts, hello_world_sources, {'a.html': 1453, 'a.js': 289, 'a.asm.js': 113, 'a.mem': 6}), + (opts, hello_world_sources, {'a.html': 1446, 'a.js': 604, 'a.wasm': 86}), + (asmjs + opts, hello_webgl_sources, {'a.html': 1581, 'a.js': 4918, 'a.asm.js': 11139, 'a.mem': 321}), + (opts, hello_webgl_sources, {'a.html': 1563, 'a.js': 4874, 'a.wasm': 8841}), + (opts, hello_webgl2_sources, {'a.html': 1563, 'a.js': 5362, 'a.wasm': 8841}) # Compare how WebGL2 sizes stack up with WebGL 1 ] success = True