Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/emscripten.py
4128 views
1
# Copyright 2010 The Emscripten Authors. All rights reserved.
2
# Emscripten is available under two separate licenses, the MIT license and the
3
# University of Illinois/NCSA Open Source License. Both these licenses can be
4
# found in the LICENSE file.
5
6
"""A small wrapper script around the core JS compiler. This calls that
7
compiler with the settings given to it. It can also read data from C/C++
8
header files (so that the JS compiler can see the constants in those
9
headers, for the libc implementation in JS).
10
"""
11
12
from tools.toolchain_profiler import ToolchainProfiler
13
14
import os
15
import json
16
import subprocess
17
import logging
18
import pprint
19
import shutil
20
import sys
21
import textwrap
22
23
from tools import building
24
from tools import config
25
from tools import diagnostics
26
from tools import js_manipulation
27
from tools import shared
28
from tools import utils
29
from tools import webassembly
30
from tools import extract_metadata
31
from tools.utils import exit_with_error, path_from_root, removeprefix
32
from tools.shared import DEBUG, asmjs_mangle, in_temp
33
from tools.settings import settings, user_settings
34
35
sys.path.append(path_from_root('third_party'))
36
import leb128
37
38
logger = logging.getLogger('emscripten')
39
40
# helper functions for JS to call into C to do memory operations. these
41
# let us sanitize memory access from the JS side, by calling into C where
42
# it has been instrumented.
43
ASAN_C_HELPERS = ['__asan_loadN', '__asan_storeN']
44
45
46
def maybe_disable_filesystem(imports):
47
"""Disables filesystem if only a limited subset of syscalls is used.
48
49
Our syscalls are static, and so if we see a very limited set of them - in particular,
50
no open() syscall and just simple writing - then we don't need full filesystem support.
51
If FORCE_FILESYSTEM is set, we can't do this. We also don't do it if INCLUDE_FULL_LIBRARY, since
52
not including the filesystem would mean not including the full JS libraries, and the same for
53
MAIN_MODULE=1 since a side module might need the filesystem.
54
"""
55
if any(settings[s] for s in ('FORCE_FILESYSTEM', 'INCLUDE_FULL_LIBRARY')):
56
return
57
if settings.MAIN_MODULE == 1:
58
return
59
60
if settings.FILESYSTEM == 0:
61
# without filesystem support, it doesn't matter what syscalls need
62
settings.SYSCALLS_REQUIRE_FILESYSTEM = 0
63
else:
64
# TODO(sbc): Find a better way to identify wasi syscalls
65
syscall_prefixes = ('__syscall_', 'fd_')
66
side_module_imports = [shared.demangle_c_symbol_name(s) for s in settings.SIDE_MODULE_IMPORTS]
67
all_imports = set(imports).union(side_module_imports)
68
syscalls = {d for d in all_imports if d.startswith(syscall_prefixes) or d == 'path_open'}
69
# check if the only filesystem syscalls are in: close, ioctl, llseek, write
70
# (without open, etc.. nothing substantial can be done, so we can disable
71
# extra filesystem support in that case)
72
if syscalls.issubset({
73
'__syscall_ioctl',
74
'fd_seek',
75
'fd_write',
76
'fd_close',
77
'fd_fdstat_get',
78
}):
79
if DEBUG:
80
logger.debug('very limited syscalls (%s) so disabling full filesystem support', ', '.join(str(s) for s in syscalls))
81
settings.SYSCALLS_REQUIRE_FILESYSTEM = 0
82
83
84
def is_int(x):
85
try:
86
int(x)
87
return True
88
except ValueError:
89
return False
90
91
92
def align_memory(addr):
93
return (addr + 15) & -16
94
95
96
def update_settings_glue(wasm_file, metadata, base_metadata):
97
maybe_disable_filesystem(metadata.imports)
98
99
# Integrate info from backend
100
if settings.SIDE_MODULE:
101
# we don't need any JS library contents in side modules
102
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = []
103
else:
104
syms = settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE + metadata.imports
105
syms = set(syms).difference(metadata.all_exports)
106
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE = sorted(syms)
107
if settings.MAIN_MODULE:
108
settings.WEAK_IMPORTS += webassembly.get_weak_imports(wasm_file)
109
110
if base_metadata:
111
settings.WASM_EXPORTS = base_metadata.all_exports
112
else:
113
settings.WASM_EXPORTS = metadata.all_exports
114
settings.WASM_GLOBAL_EXPORTS = list(metadata.global_exports.keys())
115
settings.HAVE_EM_ASM = bool(settings.MAIN_MODULE or len(metadata.em_asm_consts) != 0)
116
117
# start with the MVP features, and add any detected features.
118
settings.BINARYEN_FEATURES = ['--mvp-features'] + metadata.features
119
if settings.ASYNCIFY == 2:
120
settings.BINARYEN_FEATURES += ['--enable-reference-types']
121
122
if settings.PTHREADS:
123
assert '--enable-threads' in settings.BINARYEN_FEATURES
124
if settings.MEMORY64:
125
assert '--enable-memory64' in settings.BINARYEN_FEATURES
126
127
settings.HAS_MAIN = bool(settings.MAIN_MODULE) or settings.PROXY_TO_PTHREAD or settings.STANDALONE_WASM or 'main' in settings.WASM_EXPORTS or '__main_argc_argv' in settings.WASM_EXPORTS
128
if settings.HAS_MAIN and not settings.MINIMAL_RUNTIME:
129
# Dependencies of `callMain`
130
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$exitJS', '$handleException']
131
132
# When using dynamic linking the main function might be in a side module.
133
# To be safe assume they do take input parameters.
134
settings.MAIN_READS_PARAMS = metadata.main_reads_params or bool(settings.MAIN_MODULE)
135
if settings.MAIN_READS_PARAMS and not settings.STANDALONE_WASM:
136
# callMain depends on this library function
137
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$stringToUTF8OnStack']
138
139
if settings.STACK_OVERFLOW_CHECK and not settings.SIDE_MODULE:
140
# writeStackCookie and checkStackCookie both rely on emscripten_stack_get_end being
141
# exported. In theory it should always be present since its defined in compiler-rt.
142
assert 'emscripten_stack_get_end' in metadata.function_exports
143
144
for deps in metadata.js_deps:
145
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE.extend(deps.split(','))
146
147
148
def apply_static_code_hooks(forwarded_json, code):
149
def inject_code_hooks(name):
150
nonlocal code
151
hook_code = forwarded_json[name]
152
if hook_code:
153
hook_code = f'// Begin {name} hooks\n {hook_code}\n // End {name} hooks'
154
else:
155
hook_code = f'// No {name} hooks'
156
code = code.replace(f'<<< {name} >>>', hook_code)
157
158
inject_code_hooks('ATMODULES')
159
inject_code_hooks('ATPRERUNS')
160
inject_code_hooks('ATINITS')
161
inject_code_hooks('ATPOSTCTORS')
162
if settings.HAS_MAIN:
163
inject_code_hooks('ATMAINS')
164
if not settings.MINIMAL_RUNTIME or settings.HAS_MAIN:
165
inject_code_hooks('ATPOSTRUNS')
166
if settings.EXIT_RUNTIME:
167
inject_code_hooks('ATEXITS')
168
169
return code
170
171
172
@ToolchainProfiler.profile()
173
def compile_javascript(symbols_only=False):
174
stderr_file = os.environ.get('EMCC_STDERR_FILE')
175
if stderr_file:
176
stderr_file = os.path.abspath(stderr_file)
177
logger.info('logging stderr in js compiler phase into %s' % stderr_file)
178
stderr_file = open(stderr_file, 'w')
179
180
# Save settings to a file to work around v8 issue 1579
181
settings_json = json.dumps(settings.external_dict(), sort_keys=True, indent=2)
182
building.write_intermediate(settings_json, 'settings.json')
183
184
# Call js compiler. Passing `-` here mean read the settings from stdin.
185
args = ['-']
186
if symbols_only:
187
args += ['--symbols-only']
188
out = shared.run_js_tool(path_from_root('tools/compiler.mjs'),
189
args, input=settings_json, stdout=subprocess.PIPE, stderr=stderr_file)
190
if symbols_only:
191
glue = None
192
forwarded_data = out
193
else:
194
assert '//FORWARDED_DATA:' in out, 'Did not receive forwarded data in pre output - process failed?'
195
glue, forwarded_data = out.split('//FORWARDED_DATA:')
196
return glue, forwarded_data
197
198
199
def set_memory(static_bump):
200
stack_low = align_memory(settings.GLOBAL_BASE + static_bump)
201
stack_high = align_memory(stack_low + settings.STACK_SIZE)
202
settings.STACK_HIGH = stack_high
203
settings.STACK_LOW = stack_low
204
settings.HEAP_BASE = align_memory(stack_high)
205
206
207
def report_missing_exports_wasm_only(metadata):
208
if diagnostics.is_enabled('undefined'):
209
defined_symbols = {asmjs_mangle(e) for e in metadata.all_exports}
210
missing = set(settings.USER_EXPORTS) - defined_symbols
211
for symbol in sorted(missing):
212
diagnostics.warning('undefined', f'undefined exported symbol: "{symbol}"')
213
214
215
def report_missing_exports(js_symbols):
216
if diagnostics.is_enabled('undefined'):
217
# Report any symbol that was explicitly exported but is present neither
218
# as a native function nor as a JS library function.
219
defined_symbols = {asmjs_mangle(e) for e in settings.WASM_EXPORTS}.union(js_symbols)
220
missing = set(settings.USER_EXPORTS) - defined_symbols
221
for symbol in sorted(missing):
222
diagnostics.warning('undefined', f'undefined exported symbol: "{symbol}"')
223
224
# Special handling for the `_main` symbol
225
226
if settings.STANDALONE_WASM:
227
# standalone mode doesn't use main, and it always reports missing entry point at link time.
228
# In this mode we never expect _main in the export list.
229
return
230
231
if settings.IGNORE_MISSING_MAIN:
232
# The default mode for emscripten is to ignore the missing main function allowing
233
# maximum compatibility.
234
return
235
236
if settings.EXPECT_MAIN and 'main' not in settings.WASM_EXPORTS and '__main_argc_argv' not in settings.WASM_EXPORTS:
237
# For compatibility with the output of wasm-ld we use the same wording here in our
238
# error message as if wasm-ld had failed.
239
exit_with_error('entry symbol not defined (pass --no-entry to suppress): main')
240
241
242
# Test if the parentheses at body[openIdx] and body[closeIdx] are a match to
243
# each other.
244
def parentheses_match(body, openIdx, closeIdx):
245
if closeIdx < 0:
246
closeIdx += len(body)
247
count = 1
248
for i in range(openIdx + 1, closeIdx + 1):
249
if body[i] == body[openIdx]:
250
count += 1
251
elif body[i] == body[closeIdx]:
252
count -= 1
253
if count <= 0:
254
return i == closeIdx
255
return False
256
257
258
def trim_asm_const_body(body):
259
body = body.strip()
260
orig = None
261
while orig != body:
262
orig = body
263
if len(body) > 1 and body[0] == '"' and body[-1] == '"':
264
body = body[1:-1].replace('\\"', '"').strip()
265
if len(body) > 1 and body[0] == '{' and body[-1] == '}' and parentheses_match(body, 0, -1):
266
body = body[1:-1].strip()
267
if len(body) > 1 and body[0] == '(' and body[-1] == ')' and parentheses_match(body, 0, -1):
268
body = body[1:-1].strip()
269
return body
270
271
272
def create_other_export_declarations(tag_exports):
273
return '\n'.join(f'var {asmjs_mangle(name)};' for name in tag_exports)
274
275
276
def create_global_exports(global_exports):
277
lines = []
278
for k, v in global_exports.items():
279
if shared.is_internal_global(k):
280
continue
281
282
v = int(v)
283
284
# Cast the global to an unsigned value from the signed Wasm int32, if it is one of the fields
285
# that have a semantic unsigned meaning.
286
unsigned_globals = ['__stack_base', '__memory_base', '__table_base', '__global_base', '__heap_base']
287
if k in unsigned_globals:
288
v = v & 0xFFFFFFFF
289
290
if settings.RELOCATABLE:
291
v += settings.GLOBAL_BASE
292
mangled = asmjs_mangle(k)
293
if settings.MINIMAL_RUNTIME:
294
lines.append("var %s = %s;" % (mangled, v))
295
else:
296
lines.append("var %s = Module['%s'] = %s;" % (mangled, mangled, v))
297
298
return '\n'.join(lines)
299
300
301
def emscript(in_wasm, out_wasm, outfile_js, js_syms, finalize=True, base_metadata=None):
302
# Overview:
303
# * Run wasm-emscripten-finalize to extract metadata and modify the binary
304
# to use emscripten's wasm<->JS ABI
305
# * Use the metadata to generate the JS glue that goes with the wasm
306
307
# set file locations, so that JS glue can find what it needs
308
settings.WASM_BINARY_FILE = js_manipulation.escape_for_js_string(os.path.basename(out_wasm))
309
310
if finalize:
311
metadata = finalize_wasm(in_wasm, out_wasm, js_syms)
312
else:
313
# Skip finalize and only extract the metadata.
314
if in_wasm != out_wasm:
315
shutil.copy(in_wasm, out_wasm)
316
metadata = get_metadata(in_wasm, out_wasm, False, [])
317
318
if settings.RELOCATABLE and settings.MEMORY64 == 2:
319
metadata.imports += ['__memory_base32']
320
321
# If the binary has already been finalized the settings have already been
322
# updated and we can skip updating them.
323
if finalize:
324
update_settings_glue(out_wasm, metadata, base_metadata)
325
326
if not settings.WASM_BIGINT and metadata.em_js_funcs:
327
for em_js_func, raw in metadata.em_js_funcs.items():
328
c_sig = raw.split('<::>')[0].strip('()')
329
if not c_sig or c_sig == 'void':
330
c_sig = []
331
else:
332
c_sig = c_sig.split(',')
333
signature = metadata.em_js_func_types.get(em_js_func)
334
if signature and len(signature.params) != len(c_sig):
335
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)
336
337
asm_consts = create_asm_consts(metadata)
338
em_js_funcs = create_em_js(metadata)
339
340
if settings.SIDE_MODULE:
341
# When building side modules, valid the EM_ASM and EM_JS string by running
342
# them through node. Without this step, syntax errors are not surfaced
343
# until runtime.
344
# We use subprocess directly here rather than shared.check_call since
345
# check_call doesn't support the `input` argument.
346
if asm_consts:
347
validate = '\n'.join([f'var tmp = {f};' for _, f in asm_consts])
348
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'))
349
if proc.returncode:
350
exit_with_error(f'EM_ASM function validation failed (node returned {proc.returncode})')
351
352
if em_js_funcs:
353
validate = '\n'.join(em_js_funcs)
354
proc = subprocess.run(config.NODE_JS + ['--check', '-'], input=validate.encode('utf-8'))
355
if proc.returncode:
356
exit_with_error(f'EM_JS function validation failed (node returned {proc.returncode})')
357
358
if not outfile_js:
359
report_missing_exports_wasm_only(metadata)
360
logger.debug('emscript: skipping js glue generation')
361
return
362
363
for e in settings.EXPORTED_FUNCTIONS:
364
if not js_manipulation.isidentifier(e):
365
exit_with_error(f'invalid export name: "{e}"')
366
367
# memory and global initializers
368
369
if settings.RELOCATABLE:
370
dylink_sec = webassembly.parse_dylink_section(in_wasm)
371
static_bump = align_memory(dylink_sec.mem_size)
372
set_memory(static_bump)
373
logger.debug('stack_low: %d, stack_high: %d, heap_base: %d', settings.STACK_LOW, settings.STACK_HIGH, settings.HEAP_BASE)
374
375
# When building relocatable output (e.g. MAIN_MODULE) the reported table
376
# size does not include the reserved slot at zero for the null pointer.
377
# So we need to offset the elements by 1.
378
if settings.INITIAL_TABLE == -1:
379
settings.INITIAL_TABLE = dylink_sec.table_size + 1
380
381
if settings.ASYNCIFY == 1:
382
metadata.imports += ['__asyncify_state', '__asyncify_data']
383
384
if metadata.invoke_funcs:
385
settings.DEFAULT_LIBRARY_FUNCS_TO_INCLUDE += ['$getWasmTableEntry']
386
387
glue, forwarded_data = compile_javascript()
388
389
forwarded_json = json.loads(forwarded_data)
390
391
if forwarded_json['warnings']:
392
diagnostics.warning('js-compiler', 'warnings in JS library compilation')
393
394
pre, post = glue.split('// EMSCRIPTEN_END_FUNCS')
395
396
if settings.ASSERTIONS:
397
pre += "function checkIncomingModuleAPI() {\n"
398
for sym in settings.ALL_INCOMING_MODULE_JS_API:
399
if sym not in settings.INCOMING_MODULE_JS_API:
400
pre += f" ignoredModuleProp('{sym}');\n"
401
pre += "}\n"
402
403
report_missing_exports(forwarded_json['librarySymbols'])
404
405
asm_const_pairs = ['%s: %s' % (key, value) for key, value in asm_consts]
406
if asm_const_pairs or settings.MAIN_MODULE:
407
pre += 'var ASM_CONSTS = {\n ' + ', \n '.join(asm_const_pairs) + '\n};\n'
408
if em_js_funcs:
409
pre += '\n'.join(em_js_funcs) + '\n'
410
411
if base_metadata:
412
function_exports = base_metadata.function_exports
413
tag_exports = base_metadata.tag_exports
414
# We want the real values from the final metadata but we only want to
415
# include names from the base_metadata. See phase_link() in link.py.
416
global_exports = {k: v for k, v in metadata.global_exports.items() if k in base_metadata.global_exports}
417
else:
418
function_exports = metadata.function_exports
419
tag_exports = metadata.tag_exports
420
global_exports = metadata.global_exports
421
422
if settings.ASYNCIFY == 1:
423
function_exports['asyncify_start_unwind'] = webassembly.FuncType([webassembly.Type.I32], [])
424
function_exports['asyncify_stop_unwind'] = webassembly.FuncType([], [])
425
function_exports['asyncify_start_rewind'] = webassembly.FuncType([webassembly.Type.I32], [])
426
function_exports['asyncify_stop_rewind'] = webassembly.FuncType([], [])
427
428
parts = [pre]
429
parts += create_module(metadata, function_exports, global_exports, tag_exports, forwarded_json['librarySymbols'])
430
parts.append(post)
431
432
full_js_module = ''.join(parts)
433
full_js_module = apply_static_code_hooks(forwarded_json, full_js_module)
434
utils.write_file(outfile_js, full_js_module)
435
436
metadata.library_definitions = forwarded_json['libraryDefinitions']
437
return metadata
438
439
440
@ToolchainProfiler.profile()
441
def get_metadata(infile, outfile, modify_wasm, args):
442
metadata = extract_metadata.extract_metadata(infile)
443
if modify_wasm:
444
# In some cases we still need to modify the wasm file
445
# using wasm-emscripten-finalize.
446
building.run_binaryen_command('wasm-emscripten-finalize',
447
infile=infile,
448
outfile=outfile,
449
args=args)
450
# When we do this we can generate new imports, so
451
# re-read parts of the metadata post-finalize
452
extract_metadata.update_metadata(outfile, metadata)
453
if DEBUG:
454
logger.debug("Metadata: " + pprint.pformat(metadata.__dict__))
455
return metadata
456
457
458
def finalize_wasm(infile, outfile, js_syms):
459
building.save_intermediate(infile, 'base.wasm')
460
args = []
461
462
# if we don't need to modify the wasm, don't tell finalize to emit a wasm file
463
modify_wasm = False
464
need_name_section = False
465
466
if settings.WASM2JS:
467
# wasm2js requires full legalization (and will do extra wasm binary
468
# later processing later anyhow)
469
modify_wasm = True
470
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:
471
need_name_section = True
472
args.append('-g')
473
if settings.WASM_BIGINT:
474
args.append('--bigint')
475
if settings.DYNCALLS:
476
# we need to add all dyncalls to the wasm
477
modify_wasm = True
478
else:
479
if settings.WASM_BIGINT:
480
args.append('--no-dyncalls')
481
else:
482
args.append('--dyncalls-i64')
483
# we need to add some dyncalls to the wasm
484
modify_wasm = True
485
if settings.AUTODEBUG:
486
# In AUTODEBUG mode we want to delay all legalization until later. This is hack
487
# to force wasm-emscripten-finalize not to do any legalization at all.
488
args.append('--bigint')
489
else:
490
if settings.LEGALIZE_JS_FFI:
491
# When we dynamically link our JS loader adds functions from wasm modules to
492
# the table. It must add the original versions of them, not legalized ones,
493
# so that indirect calls have the right type, so export those.
494
args += building.js_legalization_pass_flags()
495
modify_wasm = True
496
else:
497
args.append('--no-legalize-javascript-ffi')
498
if settings.SIDE_MODULE:
499
args.append('--side-module')
500
if settings.STACK_OVERFLOW_CHECK >= 2:
501
args.append('--check-stack-overflow')
502
modify_wasm = True
503
if settings.STANDALONE_WASM:
504
args.append('--standalone-wasm')
505
506
if settings.DEBUG_LEVEL >= 3:
507
args.append('--dwarf')
508
509
if infile != outfile:
510
shutil.copy(infile, outfile)
511
512
if settings.GENERATE_SOURCE_MAP:
513
building.emit_wasm_source_map(infile, outfile + '.map', outfile)
514
building.save_intermediate(outfile + '.map', 'base_wasm.map')
515
base_url = settings.SOURCE_MAP_BASE + os.path.basename(outfile) + '.map'
516
if modify_wasm:
517
# If we are already modifying, just let Binaryen add the sourcemap URL
518
args += ['--output-source-map-url=' + base_url]
519
else:
520
# Otherwise use objcopy. This avoids re-encoding the file (thus
521
# preserving DWARF) and is faster.
522
523
# Create a file with the contents of the sourceMappingURL section
524
with shared.get_temp_files().get_file('.bin') as url_file:
525
utils.write_binary(url_file,
526
leb128.u.encode(len(base_url)) + base_url.encode('utf-8'))
527
cmd = [shared.LLVM_OBJCOPY,
528
'--add-section',
529
'sourceMappingURL=' + url_file,
530
infile]
531
shared.check_call(cmd)
532
533
# For sections we no longer need, strip now to speed subsequent passes.
534
# If Binaryen is not needed, this is also our last chance to strip.
535
strip_sections = []
536
if not settings.EMIT_PRODUCERS_SECTION:
537
strip_sections += ['producers']
538
if not need_name_section:
539
strip_sections += ['name']
540
541
if strip_sections or not settings.GENERATE_DWARF:
542
building.save_intermediate(outfile, 'strip.wasm')
543
building.strip(infile, outfile, debug=not settings.GENERATE_DWARF,
544
sections=strip_sections)
545
546
metadata = get_metadata(outfile, outfile, modify_wasm, args)
547
548
if settings.GENERATE_SOURCE_MAP:
549
building.save_intermediate(outfile + '.map', 'post_finalize.map')
550
551
expected_exports = set(settings.EXPORTED_FUNCTIONS)
552
expected_exports.update(asmjs_mangle(s) for s in settings.REQUIRED_EXPORTS)
553
expected_exports.update(asmjs_mangle(s) for s in settings.EXPORT_IF_DEFINED)
554
# Assume that when JS symbol dependencies are exported it is because they
555
# are needed by by a JS symbol and are not being explicitly exported due
556
# to EMSCRIPTEN_KEEPALIVE (llvm.used).
557
for deps in js_syms.values():
558
expected_exports.update(asmjs_mangle(s) for s in deps)
559
560
# Calculate the subset of exports that were explicitly marked as
561
# EMSCRIPTEN_KEEPALIVE (llvm.used).
562
# These are any exports that were not requested on the command line and are
563
# not known auto-generated system functions.
564
unexpected_exports = [e for e in metadata.all_exports if shared.is_user_export(e)]
565
unexpected_exports = [asmjs_mangle(e) for e in unexpected_exports]
566
unexpected_exports = [e for e in unexpected_exports if e not in expected_exports]
567
568
if not settings.STANDALONE_WASM and 'main' in metadata.all_exports or '__main_argc_argv' in metadata.all_exports:
569
if 'EXPORTED_FUNCTIONS' in user_settings and '_main' not in settings.USER_EXPORTS:
570
# If `_main` was unexpectedly exported we assume it was added to
571
# EXPORT_IF_DEFINED by `phase_linker_setup` in order that we can detect
572
# it and report this warning. After reporting the warning we explicitly
573
# ignore the export and run as if there was no main function since that
574
# is defined is behaviour for programs that don't include `_main` in
575
# EXPORTED_FUNCTIONS.
576
diagnostics.warning('unused-main', '`main` is defined in the input files, but `_main` is not in `EXPORTED_FUNCTIONS`. Add it to this list if you want `main` to run.')
577
if 'main' in metadata.all_exports:
578
metadata.all_exports.remove('main')
579
else:
580
metadata.all_exports.remove('__main_argc_argv')
581
else:
582
unexpected_exports.append('_main')
583
584
building.user_requested_exports.update(unexpected_exports)
585
settings.EXPORTED_FUNCTIONS.extend(unexpected_exports)
586
587
return metadata
588
589
590
def create_tsd_exported_runtime_methods(metadata):
591
# Use the TypeScript compiler to generate definitions for all of the runtime
592
# exports. The JS from the library any JS docs are included in the file used
593
# for generation.
594
js_doc = 'var RuntimeExports = {};\n'
595
for name in settings.EXPORTED_RUNTIME_METHODS:
596
docs = '/** @type {{any}} */'
597
snippet = ''
598
if name in metadata.library_definitions:
599
definition = metadata.library_definitions[name]
600
if definition['snippet']:
601
snippet = ' = ' + definition['snippet']
602
# Clear the doc so the type is either computed from the snippet or
603
# defined by the definition below.
604
docs = ''
605
if definition['docs']:
606
docs = definition['docs']
607
# TSC does not generate the correct type if there are jsdocs and nothing
608
# is assigned to the property.
609
if not snippet:
610
snippet = ' = null'
611
js_doc += f'{docs}\nRuntimeExports[\'{name}\']{snippet};\n'
612
613
js_doc_file = in_temp('jsdoc.js')
614
tsc_output_file = in_temp('jsdoc.d.ts')
615
utils.write_file(js_doc_file, js_doc)
616
tsc = shared.get_npm_cmd('tsc', missing_ok=True)
617
# Prefer the npm install'd version of tsc since we know that one is compatible
618
# with emscripten output.
619
if not tsc:
620
# Fall back to tsc in the user's PATH.
621
tsc = shutil.which('tsc')
622
if not tsc:
623
exit_with_error('tsc executable not found in node_modules or in $PATH')
624
# Use the full path from the which command so windows can find tsc.
625
tsc = [tsc]
626
cmd = tsc + ['--outFile', tsc_output_file,
627
'--skipLibCheck', # Avoid checking any of the user's types e.g. node_modules/@types.
628
'--declaration',
629
'--emitDeclarationOnly',
630
'--allowJs', js_doc_file]
631
shared.check_call(cmd, cwd=path_from_root())
632
return utils.read_file(tsc_output_file)
633
634
635
def create_tsd(metadata, embind_tsd):
636
out = '// TypeScript bindings for emscripten-generated code. Automatically generated at compile time.\n'
637
if settings.EXPORTED_RUNTIME_METHODS:
638
out += create_tsd_exported_runtime_methods(metadata)
639
# Manually generate definitions for any Wasm function exports.
640
out += 'interface WasmModule {\n'
641
for name, functype in metadata.function_exports.items():
642
mangled = asmjs_mangle(name)
643
should_export = settings.EXPORT_KEEPALIVE and mangled in settings.EXPORTED_FUNCTIONS
644
if not should_export:
645
continue
646
arguments = []
647
for index, type in enumerate(functype.params):
648
arguments.append(f"_{index}: {type_to_ts_type(type)}")
649
out += f' {mangled}({", ".join(arguments)}): '
650
assert len(functype.returns) <= 1, 'One return type only supported'
651
if functype.returns:
652
out += f'{type_to_ts_type(functype.returns[0])}'
653
else:
654
out += 'void'
655
out += ';\n'
656
out += '}\n'
657
out += f'\n{embind_tsd}'
658
# Combine all the various exports.
659
export_interfaces = 'WasmModule'
660
if settings.EXPORTED_RUNTIME_METHODS:
661
export_interfaces += ' & typeof RuntimeExports'
662
# Add in embind definitions.
663
if embind_tsd:
664
export_interfaces += ' & EmbindModule'
665
out += f'export type MainModule = {export_interfaces};\n'
666
if settings.MODULARIZE:
667
return_type = 'MainModule'
668
if settings.WASM_ASYNC_COMPILATION:
669
return_type = f'Promise<{return_type}>'
670
out += f'export default function MainModuleFactory (options?: unknown): {return_type};\n'
671
return out
672
673
674
def create_asm_consts(metadata):
675
asm_consts = {}
676
for addr, const in metadata.em_asm_consts.items():
677
body = trim_asm_const_body(const)
678
max_arity = 16
679
arity = 0
680
for i in range(max_arity):
681
if f'${i}' in const:
682
arity = i + 1
683
args = ', '.join(f'${i}' for i in range(arity))
684
if 'arguments' in body:
685
# arrow functions don't bind `arguments` so we have to use
686
# the old function syntax in this case
687
func = f'function({args}) {{ {body} }}'
688
else:
689
func = f'({args}) => {{ {body} }}'
690
if settings.RELOCATABLE:
691
addr += settings.GLOBAL_BASE
692
asm_consts[addr] = func
693
asm_consts = sorted(asm_consts.items())
694
return asm_consts
695
696
697
def type_to_sig(type):
698
# These should match the conversion in $sigToWasmTypes.
699
return {
700
webassembly.Type.I32: 'i',
701
webassembly.Type.I64: 'j',
702
webassembly.Type.F32: 'f',
703
webassembly.Type.F64: 'd',
704
webassembly.Type.EXTERNREF: 'e',
705
webassembly.Type.VOID: 'v',
706
}[type]
707
708
709
def type_to_ts_type(type):
710
return {
711
webassembly.Type.I32: 'number',
712
webassembly.Type.I64: 'BigInt',
713
webassembly.Type.F32: 'number',
714
webassembly.Type.F64: 'number',
715
webassembly.Type.EXTERNREF: 'any',
716
webassembly.Type.VOID: 'void',
717
}[type]
718
719
720
def func_type_to_sig(type):
721
parameters = [type_to_sig(param) for param in type.params]
722
if type.returns:
723
assert len(type.returns) == 1, "One return type expected."
724
ret = type.returns[0]
725
else:
726
ret = webassembly.Type.VOID
727
return type_to_sig(ret) + ''.join(parameters)
728
729
730
def create_em_js(metadata):
731
em_js_funcs = []
732
separator = '<::>'
733
for name, raw in metadata.em_js_funcs.items():
734
assert separator in raw
735
args, body = raw.split(separator, 1)
736
args = args[1:-1]
737
if args == 'void':
738
args = []
739
else:
740
args = args.split(',')
741
arg_names = [arg.split()[-1].replace('*', '') for arg in args if arg]
742
args = ','.join(arg_names)
743
func = f'function {name}({args}) {body}'
744
if settings.WASM_ESM_INTEGRATION:
745
# Like JS library function EM_JS functions are exported at the point of
746
# declaration in WASM_ESM_INTEGRATION node.
747
func = f'export {func}'
748
em_js_funcs.append(func)
749
if (settings.MAIN_MODULE or settings.ASYNCIFY == 2) and name in metadata.em_js_func_types:
750
sig = func_type_to_sig(metadata.em_js_func_types[name])
751
sig = f'{name}.sig = \'{sig}\';'
752
em_js_funcs.append(sig)
753
754
return em_js_funcs
755
756
757
def add_standard_wasm_imports(send_items_map):
758
extra_sent_items = []
759
760
if settings.SAFE_HEAP:
761
extra_sent_items.append('segfault')
762
extra_sent_items.append('alignfault')
763
764
# Special case for importing memory and table
765
# TODO(sbc): can we make these into normal library symbols?
766
if settings.IMPORTED_MEMORY:
767
send_items_map['memory'] = 'wasmMemory'
768
769
if settings.RELOCATABLE:
770
send_items_map['__indirect_function_table'] = 'wasmTable'
771
if settings.MEMORY64:
772
send_items_map['__table_base32'] = '___table_base32'
773
774
if settings.AUTODEBUG:
775
extra_sent_items += [
776
'log_execution',
777
'get_i32',
778
'get_i64',
779
'get_f32',
780
'get_f64',
781
'get_funcref',
782
'get_externref',
783
'get_anyref',
784
'get_exnref',
785
'set_i32',
786
'set_i64',
787
'set_f32',
788
'set_f64',
789
'set_funcref',
790
'set_externref',
791
'set_anyref',
792
'set_exnref',
793
'load_ptr',
794
'load_val_i32',
795
'load_val_i64',
796
'load_val_f32',
797
'load_val_f64',
798
'store_ptr',
799
'store_val_i32',
800
'store_val_i64',
801
'store_val_f32',
802
'store_val_f64',
803
'memory_grow_pre',
804
'memory_grow_post',
805
]
806
807
if settings.SPLIT_MODULE and settings.ASYNCIFY == 2:
808
# Calls to this function are generated by binaryen so it must be manually
809
# imported.
810
extra_sent_items.append('__load_secondary_module')
811
812
for s in extra_sent_items:
813
send_items_map[s] = s
814
815
816
def create_sending(metadata, library_symbols):
817
# Map of wasm imports to mangled/external/JS names
818
send_items_map = {name: name for name in metadata.invoke_funcs}
819
820
for name in metadata.imports:
821
if name in metadata.em_js_funcs:
822
# EM_JS functions are exported directly at the declaration site in
823
# WASM_ESM_INTEGRATION mode.
824
if not settings.WASM_ESM_INTEGRATION:
825
send_items_map[name] = name
826
else:
827
send_items_map[name] = asmjs_mangle(name)
828
829
add_standard_wasm_imports(send_items_map)
830
831
if settings.MAIN_MODULE:
832
# When including dynamic linking support, also add any JS library functions
833
# that are part of EXPORTED_FUNCTIONS (or in the case of MAIN_MODULE=1 add
834
# all JS library functions). This allows `dlsym(RTLD_DEFAULT)` to lookup JS
835
# library functions, since `wasmImports` acts as the global symbol table.
836
wasm_exports = set(metadata.function_exports)
837
library_symbols = set(library_symbols)
838
if settings.MAIN_MODULE == 1:
839
for f in library_symbols:
840
if shared.is_c_symbol(f):
841
demangled = shared.demangle_c_symbol_name(f)
842
if demangled in wasm_exports:
843
continue
844
send_items_map[demangled] = f
845
else:
846
for f in settings.EXPORTED_FUNCTIONS + settings.SIDE_MODULE_IMPORTS:
847
if f in library_symbols and shared.is_c_symbol(f):
848
demangled = shared.demangle_c_symbol_name(f)
849
if demangled in wasm_exports:
850
continue
851
send_items_map[demangled] = f
852
853
sorted_items = sorted(send_items_map.items())
854
855
if settings.WASM_ESM_INTEGRATION:
856
elems = []
857
for k, v in sorted_items:
858
elems.append(f'{v} as {k}')
859
elems = ',\n '.join(elems)
860
exports = '// Export JS functions to the wasm module with demangled names.\n'
861
exports += f"export {{\n {elems}\n}};\n\n"
862
return exports
863
864
prefix = ''
865
if settings.MAYBE_CLOSURE_COMPILER:
866
# This prevents closure compiler from minifying the field names in this
867
# object.
868
prefix = '/** @export */\n '
869
870
elems = []
871
for k, v in sorted_items:
872
if k == v:
873
elems.append(f'{prefix}{k}')
874
else:
875
elems.append(f'{prefix}{k}: {v}')
876
877
return '{\n ' + ',\n '.join(elems) + '\n}'
878
879
880
def create_reexports(metadata):
881
assert settings.WASM_ESM_INTEGRATION
882
exports = '// Re-export imported wasm functions to the JS entry point. These are user-facing and underscore mangled.\n'
883
wasm_exports = []
884
for exp in building.user_requested_exports:
885
if shared.is_c_symbol(exp):
886
demangled = shared.demangle_c_symbol_name(exp)
887
if demangled in metadata.em_js_funcs:
888
wasm_exports.append(f'{demangled} as {exp}')
889
elif demangled in settings.WASM_EXPORTS:
890
wasm_exports.append(exp)
891
elif demangled == 'main' and '__main_argc_argv' in settings.WASM_EXPORTS:
892
wasm_exports.append('_main')
893
exports += f"export {{ {', '.join(wasm_exports)} }};\n\n"
894
return exports
895
896
897
def install_debug_wrapper(sym):
898
if settings.MINIMAL_RUNTIME or not settings.ASSERTIONS:
899
return False
900
# The emscripten stack functions are called very early (by writeStackCookie) before
901
# the runtime is initialized so we can't create these wrappers that check for
902
# runtimeInitialized.
903
if sym.startswith(('_asan_', 'emscripten_stack_', '_emscripten_stack_')):
904
return False
905
# Likewise `__trap` can occur before the runtime is initialized since it is used in
906
# abort.
907
return sym != '__trap'
908
909
910
def should_export(sym):
911
return settings.EXPORT_ALL or (settings.EXPORT_KEEPALIVE and sym in settings.EXPORTED_FUNCTIONS)
912
913
914
def create_receiving(function_exports, tag_exports, library_symbols):
915
generate_dyncall_assignment = 'dynCalls' in library_symbols
916
receiving = ['\n// Imports from the Wasm binary.']
917
918
if settings.WASM_ESM_INTEGRATION:
919
exported_symbols = tag_exports + list(function_exports.keys())
920
exports = []
921
for sym in exported_symbols:
922
mangled = asmjs_mangle(sym)
923
if mangled != sym:
924
exports.append(f'{sym} as {mangled}')
925
else:
926
exports.append(sym)
927
if not settings.IMPORTED_MEMORY:
928
exports.append('memory as wasmMemory')
929
if not settings.RELOCATABLE:
930
exports.append('__indirect_function_table as wasmTable')
931
receiving.append('import {')
932
receiving.append(' ' + ',\n '.join(exports))
933
receiving.append(f"}} from './{settings.WASM_BINARY_FILE}';")
934
935
if generate_dyncall_assignment:
936
receiving.append('\nfunction assignDynCalls() {')
937
receiving.append(' // Construct dynCalls mapping')
938
for sym in function_exports:
939
if sym.startswith('dynCall_'):
940
sig_str = sym.replace('dynCall_', '')
941
receiving.append(f" dynCalls['{sig_str}'] = {sym};")
942
receiving.append('}')
943
944
return '\n'.join(receiving) + '\n\n'
945
946
# When not declaring asm exports this section is empty and we instead programmatically export
947
# symbols on the global object by calling exportWasmSymbols after initialization
948
if not settings.DECLARE_ASM_MODULE_EXPORTS:
949
return ''
950
951
# Exports are assigned inside a function to variables
952
# existing in top level JS scope, i.e.
953
# var _main;
954
# function assignWasmExports(wasmExport) {
955
# _main = wasmExports["_main"];
956
exports = {name: sig for name, sig in function_exports.items() if name != building.WASM_CALL_CTORS}
957
958
if settings.ASSERTIONS:
959
# In debug builds we generate trapping functions in case
960
# folks try to call/use a reference that was taken before the
961
# wasm module is available.
962
for sym in exports:
963
mangled = asmjs_mangle(sym)
964
export_assignment = ''
965
if (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(mangled) and settings.MODULARIZE != 'instance':
966
export_assignment = f" = Module['{mangled}']"
967
receiving.append(f"var {mangled}{export_assignment} = makeInvalidEarlyAccess('{mangled}');")
968
else:
969
# Declare all exports in a single var statement
970
sep = ',\n '
971
mangled = [asmjs_mangle(s) for s in exports]
972
receiving.append(f'var {sep.join(mangled)};\n')
973
974
if settings.MODULARIZE == 'instance':
975
mangled = [asmjs_mangle(e) for e in exports]
976
esm_exports = [e for e in mangled if should_export(e)]
977
if esm_exports:
978
esm_exports = ', '.join(esm_exports)
979
receiving.append(f'export {{ {esm_exports} }};')
980
981
receiving.append('\nfunction assignWasmExports(wasmExports) {')
982
for sym, sig in exports.items():
983
mangled = asmjs_mangle(sym)
984
if generate_dyncall_assignment and sym.startswith('dynCall_'):
985
sig_str = sym.replace('dynCall_', '')
986
dynCallAssignment = f"dynCalls['{sig_str}'] = "
987
else:
988
dynCallAssignment = ''
989
export_assignment = ''
990
if (settings.MODULARIZE or not settings.MINIMAL_RUNTIME) and should_export(mangled) and settings.MODULARIZE != 'instance':
991
export_assignment = f"Module['{mangled}'] = "
992
if install_debug_wrapper(sym):
993
nargs = len(sig.params)
994
receiving.append(f" {export_assignment}{dynCallAssignment}{mangled} = createExportWrapper('{sym}', {nargs});")
995
else:
996
receiving.append(f" {export_assignment}{dynCallAssignment}{mangled} = wasmExports['{sym}'];")
997
receiving.append('}')
998
999
return '\n'.join(receiving) + '\n'
1000
1001
1002
def create_module(metadata, function_exports, global_exports, tag_exports, library_symbols):
1003
module = []
1004
1005
receiving = create_receiving(function_exports, tag_exports, library_symbols)
1006
receiving += create_global_exports(global_exports)
1007
sending = create_sending(metadata, library_symbols)
1008
1009
module.append(receiving)
1010
1011
if settings.WASM_ESM_INTEGRATION:
1012
module.append(sending)
1013
else:
1014
module.append(create_other_export_declarations(tag_exports))
1015
1016
if settings.PTHREADS or settings.WASM_WORKERS or (settings.IMPORTED_MEMORY and settings.MODULARIZE == 'instance'):
1017
sending = textwrap.indent(sending, ' ').strip()
1018
module.append('''\
1019
var wasmImports;
1020
function assignWasmImports() {
1021
wasmImports = %s;
1022
}
1023
''' % sending)
1024
else:
1025
module.append('var wasmImports = %s;\n' % sending)
1026
1027
if settings.SUPPORT_LONGJMP == 'emscripten' or not settings.DISABLE_EXCEPTION_CATCHING:
1028
module.append(create_invoke_wrappers(metadata))
1029
else:
1030
assert not metadata.invoke_funcs, "invoke_ functions exported but exceptions and longjmp are both disabled"
1031
1032
if settings.MEMORY64 or settings.CAN_ADDRESS_2GB:
1033
module.append(create_pointer_conversion_wrappers(metadata))
1034
1035
if settings.WASM_ESM_INTEGRATION:
1036
module.append(create_reexports(metadata))
1037
1038
return module
1039
1040
1041
def create_invoke_wrappers(metadata):
1042
"""Asm.js-style exception handling: invoke wrapper generation."""
1043
invoke_wrappers = ''
1044
for invoke in metadata.invoke_funcs:
1045
sig = removeprefix(invoke, 'invoke_')
1046
invoke_wrappers += '\n' + js_manipulation.make_invoke(sig) + '\n'
1047
return invoke_wrappers
1048
1049
1050
def create_pointer_conversion_wrappers(metadata):
1051
# TODO(sbc): Move this into somewhere less static. Maybe it can become
1052
# part of library.js file, even though this metadata relates specifically
1053
# to native (non-JS) functions.
1054
#
1055
# The signature format here is similar to the one used for JS libraries
1056
# but with the following as the only valid char:
1057
# '_' - non-pointer argument (pass through unchanged)
1058
# 'p' - pointer/int53 argument (convert to/from BigInt)
1059
# 'P' - same as above but allow `undefined` too (requires extra check)
1060
mapping = {
1061
'sbrk': 'pP',
1062
'_emscripten_stack_alloc': 'pp',
1063
'emscripten_builtin_malloc': 'pp',
1064
'emscripten_builtin_calloc': 'ppp',
1065
'wasmfs_create_node_backend': 'pp',
1066
'malloc': 'pp',
1067
'realloc': 'ppp',
1068
'calloc': 'ppp',
1069
'webidl_malloc': 'pp',
1070
'memalign': 'ppp',
1071
'memcmp': '_ppp',
1072
'memcpy': 'pppp',
1073
'__getTypeName': 'pp',
1074
'setThrew': '_p',
1075
'free': '_p',
1076
'webidl_free': '_p',
1077
'_emscripten_stack_restore': '_p',
1078
'fflush': '_p',
1079
'emscripten_stack_get_end': 'p',
1080
'emscripten_stack_get_base': 'p',
1081
'pthread_self': 'p',
1082
'emscripten_stack_get_current': 'p',
1083
'__errno_location': 'p',
1084
'emscripten_builtin_memalign': 'ppp',
1085
'emscripten_builtin_free': 'vp',
1086
'main': '__PP',
1087
'__main_argc_argv': '__PP',
1088
'emscripten_stack_set_limits': '_pp',
1089
'__set_stack_limits': '_pp',
1090
'__set_thread_state': '_p___',
1091
'__cxa_can_catch': '_ppp',
1092
'__cxa_increment_exception_refcount': '_p',
1093
'__cxa_decrement_exception_refcount': '_p',
1094
'__cxa_get_exception_ptr': 'pp',
1095
'_wasmfs_write_file': '_ppp',
1096
'_wasmfs_mknod': '_p__',
1097
'_wasmfs_symlink': '_pp',
1098
'_wasmfs_chmod': '_p_',
1099
'_wasmfs_lchmod': '_p_',
1100
'_wasmfs_get_cwd': 'p_',
1101
'_wasmfs_identify': '_p',
1102
'_wasmfs_read_file': '_ppp',
1103
'_wasmfs_node_record_dirent': '_pp_',
1104
'__dl_seterr': '_pp',
1105
'_emscripten_run_js_on_main_thread': '__p_p_',
1106
'_emscripten_thread_exit': '_p',
1107
'_emscripten_thread_init': '_p_____',
1108
'_emscripten_thread_free_data': '_p',
1109
'_emscripten_dlsync_self_async': '_p',
1110
'_emscripten_proxy_dlsync_async': '_pp',
1111
'_emscripten_wasm_worker_initialize': '_p_',
1112
'_wasmfs_rename': '_pp',
1113
'_wasmfs_readlink': '_pp',
1114
'_wasmfs_truncate': '_p_',
1115
'_wasmfs_mmap': 'pp____',
1116
'_wasmfs_munmap': '_pp',
1117
'_wasmfs_msync': '_pp_',
1118
'_wasmfs_read': '__pp',
1119
'_wasmfs_pread': '__pp_',
1120
'_wasmfs_utime': '_p__',
1121
'_wasmfs_rmdir': '_p',
1122
'_wasmfs_unlink': '_p',
1123
'_wasmfs_mkdir': '_p_',
1124
'_wasmfs_open': '_p__',
1125
'_wasmfs_mount': '_pp',
1126
'_wasmfs_chdir': '_p',
1127
'asyncify_start_rewind': '_p',
1128
'asyncify_start_unwind': '_p',
1129
'__get_exception_message': '_ppp',
1130
'stbi_image_free': 'vp',
1131
'stbi_load': 'ppppp_',
1132
'stbi_load_from_memory': 'pp_ppp_',
1133
'strerror': 'p_',
1134
'emscripten_proxy_finish': '_p',
1135
'emscripten_proxy_execute_queue': '_p',
1136
'_emval_coro_resume': '_pp',
1137
'_emval_coro_reject': '_pp',
1138
'emscripten_main_runtime_thread_id': 'p',
1139
'_emscripten_set_offscreencanvas_size_on_thread': '_pp__',
1140
'fileno': '_p',
1141
'_emscripten_run_callback_on_thread': '_pp_pp',
1142
'_emscripten_find_dylib': 'ppppp',
1143
}
1144
1145
for function in settings.SIGNATURE_CONVERSIONS:
1146
sym, sig = function.split(':')
1147
mapping[sym] = sig
1148
1149
for f in ASAN_C_HELPERS:
1150
mapping[f] = '_pp'
1151
1152
wrappers = '''
1153
// Argument name here must shadow the `wasmExports` global so
1154
// that it is recognised by metadce and minify-import-export-names
1155
// passes.
1156
function applySignatureConversions(wasmExports) {
1157
// First, make a copy of the incoming exports object
1158
wasmExports = Object.assign({}, wasmExports);
1159
'''
1160
1161
sigs_seen = set()
1162
wrap_functions = []
1163
for symbol, functype in metadata.function_exports.items():
1164
# dynCall_ functions are generated by binaryen. They all take a
1165
# function pointer as their first argument.
1166
# The second part of this check can be removed once the table64
1167
# llvm changs lands.
1168
if symbol.startswith('dynCall_') and functype.params[0] == webassembly.Type.I64:
1169
sig = symbol.split('_')[-1]
1170
sig = ['p' if t == 'p' else '_' for t in sig]
1171
sig.insert(1, 'p')
1172
sig = ''.join(sig)
1173
mapping[symbol] = sig
1174
sig = mapping.get(symbol)
1175
if sig:
1176
if settings.MEMORY64:
1177
if sig not in sigs_seen:
1178
wrappers += js_manipulation.make_wasm64_wrapper(sig)
1179
sigs_seen.add(sig)
1180
wrap_functions.append(symbol)
1181
elif sig[0] == 'p':
1182
if sig not in sigs_seen:
1183
wrappers += js_manipulation.make_unsign_pointer_wrapper(sig)
1184
sigs_seen.add(sig)
1185
wrap_functions.append(symbol)
1186
1187
for f in wrap_functions:
1188
sig = mapping[f]
1189
wrappers += f"\n wasmExports['{f}'] = makeWrapper_{sig}(wasmExports['{f}']);"
1190
wrappers += '\n return wasmExports;\n}'
1191
1192
return wrappers
1193
1194