Esempio n. 1
0
def report_missing_symbols(js_library_funcs):
    # Report any symbol that was explicitly exported but is present neither
    # as a native function nor as a JS library function.
    defined_symbols = set(
        asmjs_mangle(e) for e in settings.WASM_EXPORTS).union(js_library_funcs)
    missing = set(settings.USER_EXPORTED_FUNCTIONS) - defined_symbols
    for symbol in sorted(missing):
        diagnostics.warning('undefined',
                            f'undefined exported symbol: "{symbol}"')

    # Special hanlding for the `_main` symbol

    if settings.STANDALONE_WASM:
        # standalone mode doesn't use main, and it always reports missing entry point at link time.
        # In this mode we never expect _main in the export list.
        return

    # PROXY_TO_PTHREAD only makes sense with a main(), so error if one is
    # missing. note that when main() might arrive from another module we cannot
    # error here.
    if settings.PROXY_TO_PTHREAD and '_main' not in defined_symbols and \
       not settings.RELOCATABLE:
        exit_with_error(
            'PROXY_TO_PTHREAD proxies main() for you, but no main exists')

    if settings.IGNORE_MISSING_MAIN:
        # The default mode for emscripten is to ignore the missing main function allowing
        # maximum compatibility.
        return

    if settings.EXPECT_MAIN and 'main' not in settings.WASM_EXPORTS:
        # For compatibility with the output of wasm-ld we use the same wording here in our
        # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode).
        exit_with_error(
            'entry symbol not defined (pass --no-entry to suppress): main')
Esempio n. 2
0
def report_missing_symbols(all_implemented, pre):
    # the initial list of missing functions are that the user explicitly exported
    # but were not implemented in compiled code
    missing = set(shared.Settings.USER_EXPORTED_FUNCTIONS) - all_implemented

    for requested in sorted(missing):
        if (f'function {requested}(') not in pre:
            diagnostics.warning('undefined',
                                f'undefined exported symbol: "{requested}"')

    # Special hanlding for the `_main` symbol

    if shared.Settings.STANDALONE_WASM:
        # standalone mode doesn't use main, and it always reports missing entry point at link time.
        # In this mode we never expect _main in the export list.
        return

    if shared.Settings.IGNORE_MISSING_MAIN:
        # The default mode for emscripten is to ignore the missing main function allowing
        # maximum compatibility.
        return

    if shared.Settings.EXPECT_MAIN and '_main' not in all_implemented:
        # For compatibility with the output of wasm-ld we use the same wording here in our
        # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode).
        exit_with_error(
            'entry symbol not defined (pass --no-entry to suppress): main')
Esempio n. 3
0
def report_missing_symbols(all_implemented, pre):
    # the initial list of missing functions are that the user explicitly exported
    # but were not implemented in compiled code
    missing = list(
        set(shared.Settings.USER_EXPORTED_FUNCTIONS) - all_implemented)

    for requested in missing:
        if ('function ' + asstr(requested)) in pre:
            continue
        # special-case malloc, EXPORTED by default for internal use, but we bake in a
        # trivial allocator and warn at runtime if used in ASSERTIONS
        if missing == '_malloc':
            continue
        diagnostics.warning('undefined', 'undefined exported function: "%s"',
                            requested)

    # Special hanlding for the `_main` symbol

    if shared.Settings.STANDALONE_WASM:
        # standalone mode doesn't use main, and it always reports missing entry point at link time.
        # In this mode we never expect _main in the export list.
        return

    if shared.Settings.IGNORE_MISSING_MAIN:
        # The default mode for emscripten is to ignore the missing main function allowing
        # maximum compatibility.
        return

    if shared.Settings.EXPECT_MAIN and '_main' not in all_implemented:
        # For compatibility with the output of wasm-ld we use the same wording here in our
        # error message as if wasm-ld had failed (i.e. in LLD_REPORT_UNDEFINED mode).
        exit_with_error(
            'entry symbol not defined (pass --no-entry to suppress): main')
Esempio n. 4
0
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