def load_metadata_wasm(metadata_raw, DEBUG): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'declares': [], 'globalImports': [], 'staticBump': 0, 'tableSize': 0, 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } legacy_keys = set( ['implementedFunctions', 'initializers', 'simd', 'externs']) assert 'tableSize' in metadata_json.keys() for key, value in metadata_json.items(): if key in legacy_keys: continue if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value # Support older metadata when asmConsts values were lists. We only use the first element # nowadays # TODO(sbc): remove this once binaryen has been changed to only emit the single element metadata['asmConsts'] = { k: v[0] if type(v) is list else v for k, v in metadata['asmConsts'].items() } if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in settings.EXPORTED_FUNCTIONS ] building.user_requested_exports.update(unexpected_exports) settings.EXPORTED_FUNCTIONS.extend(unexpected_exports) return metadata
def load_metadata_wasm(metadata_raw, DEBUG): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'declares': [], 'externs': [], 'staticBump': 0, 'tableSize': 0, 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } legacy_keys = set(['implementedFunctions', 'initializers', 'simd']) assert 'tableSize' in metadata_json.keys() for key, value in metadata_json.items(): if key in legacy_keys: continue # json.loads returns `unicode` for strings but other code in this file # generally works with utf8 encoded `str` objects, and they don't alwasy # mix well. e.g. s.replace(x, y) will blow up is `s` a uts8 str containing # non-ascii and either x or y are unicode objects. # TODO(sbc): Remove this encoding if we switch to unicode elsewhere # (specifically the glue returned from compile_settings) if type(value) == list: value = [asstr(v) for v in value] if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in shared.Settings.EXPORTED_FUNCTIONS ] building.user_requested_exports += unexpected_exports return metadata
def load_metadata_json(metadata_raw): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'declares': [], 'globalImports': [], 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } for key, value in metadata_json.items(): if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) expected_exports = set(settings.EXPORTED_FUNCTIONS) expected_exports.update(asmjs_mangle(s) for s in settings.REQUIRED_EXPORTS) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in expected_exports ] building.user_requested_exports.update(unexpected_exports) settings.EXPORTED_FUNCTIONS.extend(unexpected_exports) return metadata
def load_metadata_wasm(metadata_raw, DEBUG): try: metadata_json = json.loads(metadata_raw) except Exception: logger.error( 'emscript: failure to parse metadata output from wasm-emscripten-finalize. raw output is: \n' + metadata_raw) raise metadata = { 'aliases': {}, 'declares': [], 'implementedFunctions': [], 'externs': [], 'simd': False, # Obsolete, always False 'maxGlobalAlign': 0, 'staticBump': 0, 'tableSize': 0, 'initializers': [], 'exports': [], 'namedGlobals': {}, 'emJsFuncs': {}, 'asmConsts': {}, 'invokeFuncs': [], 'features': [], 'mainReadsParams': 1, } assert 'tableSize' in metadata_json.keys() for key, value in metadata_json.items(): # json.loads returns `unicode` for strings but other code in this file # generally works with utf8 encoded `str` objects, and they don't alwasy # mix well. e.g. s.replace(x, y) will blow up is `s` a uts8 str containing # non-ascii and either x or y are unicode objects. # TODO(sbc): Remove this encoding if we switch to unicode elsewhere # (specifically the glue returned from compile_settings) if type(value) == list: value = [asstr(v) for v in value] if key not in metadata: exit_with_error( 'unexpected metadata key received from wasm-emscripten-finalize: %s', key) metadata[key] = value if not shared.Settings.MINIMAL_RUNTIME: # In regular runtime initializers call the global var version of the export, so they get the mangled name. # In MINIMAL_RUNTIME, the initializers are called directly off the export object for minimal code size. metadata['initializers'] = [ asmjs_mangle(i) for i in metadata['initializers'] ] if DEBUG: logger.debug("Metadata parsed: " + pprint.pformat(metadata)) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in shared.Settings.EXPORTED_FUNCTIONS ] building.user_requested_exports += unexpected_exports # With the wasm backend the set of implemented functions is identical to the set of exports # Set this key here simply so that the shared code that handle it. metadata['implementedFunctions'] = [ asmjs_mangle(x) for x in metadata['exports'] ] return metadata
def finalize_wasm(infile, outfile, memfile): building.save_intermediate(infile, 'base.wasm') args = [] # if we don't need to modify the wasm, don't tell finalize to emit a wasm file modify_wasm = False if settings.WASM2JS: # wasm2js requires full legalization (and will do extra wasm binary # later processing later anyhow) modify_wasm = True if settings.USE_PTHREADS and settings.RELOCATABLE: # HACK: When settings.USE_PTHREADS and settings.RELOCATABLE are set finalize needs to scan # more than just the start function for memory.init instructions. This means it can't run # with setSkipFunctionBodies() enabled. Currently the only way to force this is to set an # output file. # TODO(sbc): Find a better way to do this. modify_wasm = True if settings.GENERATE_SOURCE_MAP: building.emit_wasm_source_map(infile, infile + '.map', outfile) building.save_intermediate(infile + '.map', 'base_wasm.map') args += [ '--output-source-map-url=' + settings.SOURCE_MAP_BASE + os.path.basename(outfile) + '.map' ] modify_wasm = True if settings.DEBUG_LEVEL >= 2 or settings.ASYNCIFY_ADD or settings.ASYNCIFY_ADVISE or settings.ASYNCIFY_ONLY or settings.ASYNCIFY_REMOVE or settings.EMIT_SYMBOL_MAP or settings.EMIT_NAME_SECTION: args.append('-g') if settings.WASM_BIGINT: args.append('--bigint') if settings.DYNCALLS: # we need to add all dyncalls to the wasm modify_wasm = True else: if settings.WASM_BIGINT: args.append('--no-dyncalls') else: args.append('--dyncalls-i64') # we need to add some dyncalls to the wasm modify_wasm = True if settings.LEGALIZE_JS_FFI: # When we dynamically link our JS loader adds functions from wasm modules to # the table. It must add the original versions of them, not legalized ones, # so that indirect calls have the right type, so export those. if settings.RELOCATABLE: args.append('--pass-arg=legalize-js-interface-export-originals') modify_wasm = True else: args.append('--no-legalize-javascript-ffi') if memfile: args.append(f'--separate-data-segments={memfile}') args.append(f'--global-base={settings.GLOBAL_BASE}') modify_wasm = True if settings.SIDE_MODULE: args.append('--side-module') if settings.STACK_OVERFLOW_CHECK >= 2: args.append('--check-stack-overflow') modify_wasm = True if settings.STANDALONE_WASM: args.append('--standalone-wasm') if settings.DEBUG_LEVEL >= 3: args.append('--dwarf') # Currently we have two different ways to extract the metadata from the # wasm binary: # 1. via wasm-emscripten-finalize (binaryen) # 2. via local python code # We also have a 'compare' mode that runs both extraction methods and # checks that they produce identical results. read_metadata = os.environ.get('EMCC_READ_METADATA', 'python') if read_metadata == 'binaryen': metadata = get_metadata_binaryen(infile, outfile, modify_wasm, args) elif read_metadata == 'python': metadata = get_metadata_python(infile, outfile, modify_wasm, args) elif read_metadata == 'compare': shutil.copy2(infile, infile + '.bak') if settings.GENERATE_SOURCE_MAP: shutil.copy2(infile + '.map', infile + '.map.bak') pymetadata = get_metadata_python(infile, outfile, modify_wasm, args) shutil.move(infile + '.bak', infile) if settings.GENERATE_SOURCE_MAP: shutil.move(infile + '.map.bak', infile + '.map') metadata = get_metadata_binaryen(infile, outfile, modify_wasm, args) compare_metadata(metadata, pymetadata) else: assert False if modify_wasm: building.save_intermediate(infile, 'post_finalize.wasm') elif infile != outfile: shutil.copy(infile, outfile) if settings.GENERATE_SOURCE_MAP: building.save_intermediate(infile + '.map', 'post_finalize.map') if memfile: # we have a separate .mem file. binaryen did not strip any trailing zeros, # because it's an ABI question as to whether it is valid to do so or not. # we can do so here, since we make sure to zero out that memory (even in # the dynamic linking case, our loader zeros it out) remove_trailing_zeros(memfile) expected_exports = set(settings.EXPORTED_FUNCTIONS) expected_exports.update(asmjs_mangle(s) for s in settings.REQUIRED_EXPORTS) # Calculate the subset of exports that were explicitly marked with llvm.used. # These are any exports that were not requested on the command line and are # not known auto-generated system functions. unexpected_exports = [ e for e in metadata['exports'] if treat_as_user_function(e) ] unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports] unexpected_exports = [ e for e in unexpected_exports if e not in expected_exports ] building.user_requested_exports.update(unexpected_exports) settings.EXPORTED_FUNCTIONS.extend(unexpected_exports) return metadata