def emscript(in_wasm, out_wasm, outfile_js, memfile, DEBUG): # Overview: # * Run wasm-emscripten-finalize to extract metadata and modify the binary # to use emscripten's wasm<->JS ABI # * Use the metadata to generate the JS glue that goes with the wasm if settings.SINGLE_FILE: # placeholder strings for JS glue, to be replaced with subresource locations in do_binaryen settings.WASM_BINARY_FILE = '<<< WASM_BINARY_FILE >>>' else: # set file locations, so that JS glue can find what it needs settings.WASM_BINARY_FILE = js_manipulation.escape_for_js_string( os.path.basename(out_wasm)) metadata = finalize_wasm(in_wasm, out_wasm, memfile, DEBUG) update_settings_glue(metadata, DEBUG) if settings.SIDE_MODULE: if metadata['asmConsts']: exit_with_error('EM_ASM is not supported in side modules') if metadata['emJsFuncs']: exit_with_error('EM_JS is not supported in side modules') logger.debug('emscript: skipping remaining js glue generation') return if DEBUG: logger.debug('emscript: js compiler glue') t = time.time() # memory and global initializers if settings.RELOCATABLE: dylink_sec = webassembly.parse_dylink_section(in_wasm) static_bump = align_memory(dylink_sec.mem_size) set_memory(static_bump) logger.debug('stack_base: %d, stack_max: %d, heap_base: %d', settings.STACK_BASE, settings.STACK_MAX, settings.HEAP_BASE) # When building relocatable output (e.g. MAIN_MODULE) the reported table # size does not include the reserved slot at zero for the null pointer. # So we need to offset the elements by 1. if settings.INITIAL_TABLE == -1: settings.INITIAL_TABLE = dylink_sec.table_size + 1 glue, forwarded_data = compile_settings() if DEBUG: logger.debug(' emscript: glue took %s seconds' % (time.time() - t)) t = time.time() forwarded_json = json.loads(forwarded_data) pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') exports = metadata['exports'] if settings.ASYNCIFY: exports += [ 'asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind' ] report_missing_symbols(forwarded_json['libraryFunctions']) if not outfile_js: logger.debug('emscript: skipping remaining js glue generation') return if settings.MINIMAL_RUNTIME: # In MINIMAL_RUNTIME, atinit exists in the postamble part post = apply_static_code_hooks(forwarded_json, post) else: # In regular runtime, atinits etc. exist in the preamble part pre = apply_static_code_hooks(forwarded_json, pre) asm_consts = create_asm_consts(metadata) em_js_funcs = create_em_js(metadata) asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts] asm_const_map = 'var ASM_CONSTS = {\n ' + ', \n '.join( asm_const_pairs) + '\n};\n' pre = pre.replace('// === Body ===', ('// === Body ===\n\n' + asm_const_map + '\n'.join(em_js_funcs) + '\n')) with open(outfile_js, 'w') as out: out.write(normalize_line_endings(pre)) pre = None invoke_funcs = metadata['invokeFuncs'] sending = create_sending(invoke_funcs, metadata) receiving = create_receiving(exports) if settings.MINIMAL_RUNTIME: if settings.DECLARE_ASM_MODULE_EXPORTS: post = compute_minimal_runtime_initializer_and_exports( post, exports, receiving) receiving = '' module = create_module(sending, receiving, invoke_funcs, metadata) write_output_file(out, module) out.write(normalize_line_endings(post)) module = None
def emscript(in_wasm, out_wasm, outfile_js, memfile, temp_files, DEBUG): # Overview: # * Run wasm-emscripten-finalize to extract metadata and modify the binary # to use emscripten's wasm<->JS ABI # * Use the metadata to generate the JS glue that goes with the wasm if shared.Settings.SINGLE_FILE: # placeholder strings for JS glue, to be replaced with subresource locations in do_binaryen shared.Settings.WASM_BINARY_FILE = '<<< WASM_BINARY_FILE >>>' else: # set file locations, so that JS glue can find what it needs shared.Settings.WASM_BINARY_FILE = shared.JS.escape_for_js_string( os.path.basename(out_wasm)) metadata = finalize_wasm(in_wasm, out_wasm, memfile, DEBUG) update_settings_glue(metadata, DEBUG) if shared.Settings.SIDE_MODULE: logger.debug('emscript: skipping remaining js glue generation') return if DEBUG: logger.debug('emscript: js compiler glue') t = time.time() glue, forwarded_data = compile_settings(temp_files) if DEBUG: logger.debug(' emscript: glue took %s seconds' % (time.time() - t)) t = time.time() forwarded_json = json.loads(forwarded_data) # For the wasm backend the implementedFunctions from compiler.js should # always be empty. This only gets populated for __asm function when using # the JS backend. assert not forwarded_json['Functions']['implementedFunctions'] pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') exports = metadata['exports'] if shared.Settings.ASYNCIFY: exports += [ 'asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind' ] all_exports = exports + list(metadata['namedGlobals'].keys()) all_exports = set([asmjs_mangle(e) for e in all_exports]) report_missing_symbols(all_exports, pre) if not outfile_js: logger.debug('emscript: skipping remaining js glue generation') return # memory and global initializers if shared.Settings.RELOCATABLE: static_bump = align_memory( webassembly.parse_dylink_section(in_wasm)[0]) memory = Memory(static_bump) logger.debug('stack_base: %d, stack_max: %d, dynamic_base: %d', memory.stack_base, memory.stack_max, memory.dynamic_base) pre = apply_memory(pre, memory) post = apply_memory(post, memory) pre = apply_static_code_hooks( pre) # In regular runtime, atinits etc. exist in the preamble part post = apply_static_code_hooks( post) # In MINIMAL_RUNTIME, atinit exists in the postamble part # merge forwarded data shared.Settings.EXPORTED_FUNCTIONS = forwarded_json['EXPORTED_FUNCTIONS'] asm_consts = create_asm_consts(metadata) em_js_funcs = create_em_js(forwarded_json, metadata) asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts] asm_const_map = 'var ASM_CONSTS = {\n ' + ', \n '.join( asm_const_pairs) + '\n};\n' pre = pre.replace('// === Body ===', ('// === Body ===\n\n' + asm_const_map + '\n'.join(em_js_funcs) + '\n')) with open(outfile_js, 'w') as out: out.write(pre) pre = None invoke_funcs = metadata['invokeFuncs'] sending = create_sending(invoke_funcs, metadata) receiving = create_receiving(exports) if shared.Settings.MINIMAL_RUNTIME: post = compute_minimal_runtime_initializer_and_exports( post, exports, receiving) receiving = '' module = create_module(sending, receiving, invoke_funcs, metadata) write_output_file(out, post, module) module = None
def emscript(in_wasm, out_wasm, outfile_js, memfile): # Overview: # * Run wasm-emscripten-finalize to extract metadata and modify the binary # to use emscripten's wasm<->JS ABI # * Use the metadata to generate the JS glue that goes with the wasm if settings.SINGLE_FILE: # placeholder strings for JS glue, to be replaced with subresource locations in do_binaryen settings.WASM_BINARY_FILE = '<<< WASM_BINARY_FILE >>>' else: # set file locations, so that JS glue can find what it needs settings.WASM_BINARY_FILE = js_manipulation.escape_for_js_string( os.path.basename(out_wasm)) metadata = finalize_wasm(in_wasm, out_wasm, memfile) update_settings_glue(out_wasm, metadata) if not settings.WASM_BIGINT and metadata['emJsFuncs']: module = webassembly.Module(in_wasm) types = module.get_types() import_map = {} for imp in module.get_imports(): import_map[imp.field] = imp for em_js_func, raw in metadata.get('emJsFuncs', {}).items(): c_sig = raw.split('<::>')[0].strip('()') if not c_sig or c_sig == 'void': c_sig = [] else: c_sig = c_sig.split(',') if em_js_func in import_map: imp = import_map[em_js_func] assert (imp.kind == webassembly.ExternType.FUNC) signature = types[imp.type] if len(signature.params) != len(c_sig): diagnostics.warning( 'em-js-i64', 'using 64-bit arguments in EM_JS function without WASM_BIGINT is not yet fully supported: `%s` (%s, %s)', em_js_func, c_sig, signature.params) if settings.SIDE_MODULE: if metadata['asmConsts']: exit_with_error('EM_ASM is not supported in side modules') if metadata['emJsFuncs']: exit_with_error('EM_JS is not supported in side modules') logger.debug('emscript: skipping remaining js glue generation') return if DEBUG: logger.debug('emscript: js compiler glue') t = time.time() # memory and global initializers if settings.RELOCATABLE: dylink_sec = webassembly.parse_dylink_section(in_wasm) static_bump = align_memory(dylink_sec.mem_size) set_memory(static_bump) logger.debug('stack_base: %d, stack_max: %d, heap_base: %d', settings.STACK_BASE, settings.STACK_MAX, settings.HEAP_BASE) # When building relocatable output (e.g. MAIN_MODULE) the reported table # size does not include the reserved slot at zero for the null pointer. # So we need to offset the elements by 1. if settings.INITIAL_TABLE == -1: settings.INITIAL_TABLE = dylink_sec.table_size + 1 if settings.ASYNCIFY: metadata['globalImports'] += [ '__asyncify_state', '__asyncify_data' ] invoke_funcs = metadata['invokeFuncs'] if invoke_funcs: settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getWasmTableEntry'] glue, forwarded_data = compile_settings() if DEBUG: logger.debug(' emscript: glue took %s seconds' % (time.time() - t)) t = time.time() forwarded_json = json.loads(forwarded_data) if forwarded_json['warnings']: diagnostics.warning('js-compiler', 'warnings in JS library compilation') pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') if settings.ASSERTIONS: pre += "function checkIncomingModuleAPI() {\n" for sym in settings.ALL_INCOMING_MODULE_JS_API: if sym not in settings.INCOMING_MODULE_JS_API: pre += f" ignoredModuleProp('{sym}');\n" pre += "}\n" exports = metadata['exports'] if settings.ASYNCIFY: exports += [ 'asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind' ] report_missing_symbols(forwarded_json['librarySymbols']) if not outfile_js: logger.debug('emscript: skipping remaining js glue generation') return if settings.MINIMAL_RUNTIME: # In MINIMAL_RUNTIME, atinit exists in the postamble part post = apply_static_code_hooks(forwarded_json, post) else: # In regular runtime, atinits etc. exist in the preamble part pre = apply_static_code_hooks(forwarded_json, pre) asm_consts = create_asm_consts(metadata) em_js_funcs = create_em_js(metadata) asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts] asm_const_map = 'var ASM_CONSTS = {\n ' + ', \n '.join( asm_const_pairs) + '\n};\n' pre = pre.replace('// === Body ===', ('// === Body ===\n\n' + asm_const_map + '\n'.join(em_js_funcs) + '\n')) with open(outfile_js, 'w', encoding='utf-8') as out: out.write(normalize_line_endings(pre)) pre = None sending = create_sending(invoke_funcs, metadata) receiving = create_receiving(exports) if settings.MINIMAL_RUNTIME: if settings.DECLARE_ASM_MODULE_EXPORTS: post = compute_minimal_runtime_initializer_and_exports( post, exports, receiving) receiving = '' module = create_module(sending, receiving, invoke_funcs, metadata) write_output_file(out, module) out.write(normalize_line_endings(post)) module = None
def emscript(infile, outfile_js, memfile, temp_files, DEBUG): # Overview: # * Run wasm-emscripten-finalize to extract metadata and modify the binary # to use emscripten's wasm<->JS ABI # * Use the metadata to generate the JS glue that goes with the wasm metadata = finalize_wasm(infile, memfile, DEBUG) update_settings_glue(metadata, DEBUG) if not outfile_js: logger.debug('emscript: skipping js compiler glue') return if DEBUG: logger.debug('emscript: js compiler glue') if DEBUG: t = time.time() glue, forwarded_data = compile_settings(temp_files) if DEBUG: logger.debug(' emscript: glue took %s seconds' % (time.time() - t)) t = time.time() forwarded_json = json.loads(forwarded_data) # For the wasm backend the implementedFunctions from compiler.js should # always be empty. This only gets populated for __asm function when using # the JS backend. assert not forwarded_json['Functions']['implementedFunctions'] pre, post = glue.split('// EMSCRIPTEN_END_FUNCS') # memory and global initializers # In minimal runtime, global initializers are run after the Wasm Module instantiation has finished. if not shared.Settings.MINIMAL_RUNTIME: global_initializers = ', '.join('{ func: function() { %s() } }' % i for i in metadata['initializers']) # In regular runtime, global initializers are recorded in an __ATINIT__ array. global_initializers = '__ATINIT__.push(%s);' % global_initializers if shared.Settings.USE_PTHREADS: global_initializers = 'if (!ENVIRONMENT_IS_PTHREAD) ' + global_initializers pre += '\n' + global_initializers + '\n' if shared.Settings.RELOCATABLE: static_bump = align_memory(webassembly.parse_dylink_section(infile)[0]) memory = Memory(static_bump) logger.debug('stack_base: %d, stack_max: %d, dynamic_base: %d', memory.stack_base, memory.stack_max, memory.dynamic_base) pre = apply_memory(pre, memory) post = apply_memory(post, memory) pre = apply_static_code_hooks( pre) # In regular runtime, atinits etc. exist in the preamble part post = apply_static_code_hooks( post) # In MINIMAL_RUNTIME, atinit exists in the postamble part # merge forwarded data shared.Settings.EXPORTED_FUNCTIONS = forwarded_json['EXPORTED_FUNCTIONS'] exports = metadata['exports'] # Store exports for Closure compiler to be able to track these as globals in # -s DECLARE_ASM_MODULE_EXPORTS=0 builds. shared.Settings.MODULE_EXPORTS = [(asmjs_mangle(f), f) for f in exports] if shared.Settings.ASYNCIFY: exports += [ 'asyncify_start_unwind', 'asyncify_stop_unwind', 'asyncify_start_rewind', 'asyncify_stop_rewind' ] all_exports = exports + list(metadata['namedGlobals'].keys()) all_exports = set([asmjs_mangle(e) for e in all_exports]) report_missing_symbols(all_exports, pre) asm_consts = create_asm_consts(metadata) em_js_funcs = create_em_js(forwarded_json, metadata) asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts] asm_const_map = 'var ASM_CONSTS = {\n ' + ', \n '.join( asm_const_pairs) + '\n};\n' pre = pre.replace('// === Body ===', ('// === Body ===\n\n' + asm_const_map + '\n'.join(em_js_funcs) + '\n')) with open(outfile_js, 'w') as out: out.write(pre) pre = None invoke_funcs = metadata['invokeFuncs'] sending = create_sending(invoke_funcs, metadata) receiving = create_receiving(exports, metadata['initializers']) if shared.Settings.MINIMAL_RUNTIME: post = compute_minimal_runtime_initializer_and_exports( post, metadata['initializers'], exports, receiving) receiving = '' module = create_module(sending, receiving, invoke_funcs, metadata) write_output_file(out, post, module) module = None
def get_weak_imports(main_wasm): dylink_sec = webassembly.parse_dylink_section(main_wasm) for symbols in dylink_sec.import_info.values(): for symbol, flags in symbols.items(): if flags & webassembly.SYMBOL_BINDING_MASK == webassembly.SYMBOL_BINDING_WEAK: settings.WEAK_IMPORTS.append(symbol)