Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
emscripten-core
GitHub Repository: emscripten-core/emscripten
Path: blob/main/tools/js_manipulation.py
6165 views
1
# Copyright 2020 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
import re
7
8
from . import shared, utils
9
from .settings import settings
10
11
emscripten_license = '''\
12
/**
13
* @license
14
* Copyright 2010 The Emscripten Authors
15
* SPDX-License-Identifier: MIT
16
*/
17
'''
18
19
# handle the above form, and also what closure can emit which is stuff like
20
# /*
21
#
22
# Copyright 2019 The Emscripten Authors
23
# SPDX-License-Identifier: MIT
24
#
25
# Copyright 2017 The Emscripten Authors
26
# SPDX-License-Identifier: MIT
27
# */
28
emscripten_license_regex = r'\/\*\*?(\s*\*?\s*@license)?(\s*\*?\s*Copyright \d+ The Emscripten Authors\s*\*?\s*SPDX-License-Identifier: MIT)+\s*\*\/\s*'
29
30
31
def add_files_pre_js(pre_js_list, files_pre_js):
32
# the normal thing is to just combine the pre-js content
33
filename = shared.get_temp_files().get('.js').name
34
utils.write_file(filename, files_pre_js)
35
pre_js_list.insert(0, filename)
36
if not settings.ASSERTIONS:
37
return
38
39
# if a user pre-js tramples the file code's changes to Module.preRun
40
# that could be confusing. show a clear error at runtime if assertions are
41
# enabled
42
pre = shared.get_temp_files().get('.js').name
43
post = shared.get_temp_files().get('.js').name
44
utils.write_file(pre, '''
45
// All the pre-js content up to here must remain later on, we need to run
46
// it.
47
if ((typeof ENVIRONMENT_IS_WASM_WORKER != 'undefined' && ENVIRONMENT_IS_WASM_WORKER) || (typeof ENVIRONMENT_IS_PTHREAD != 'undefined' && ENVIRONMENT_IS_PTHREAD) || (typeof ENVIRONMENT_IS_AUDIO_WORKLET != 'undefined' && ENVIRONMENT_IS_AUDIO_WORKLET)) Module['preRun'] = [];
48
var necessaryPreJSTasks = Module['preRun'].slice();
49
''')
50
utils.write_file(post, '''
51
if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?';
52
necessaryPreJSTasks.forEach((task) => {
53
if (Module['preRun'].indexOf(task) < 0) throw 'All preRun tasks that exist before user pre-js code should remain after; did you replace Module or modify Module.preRun?';
54
});
55
''')
56
57
pre_js_list.insert(1, pre)
58
pre_js_list.append(post)
59
60
61
def handle_license(js_target):
62
# ensure we emit the license if and only if we need to, and exactly once
63
js = utils.read_file(js_target)
64
# first, remove the license as there may be more than once
65
processed_js = re.sub(emscripten_license_regex, '', js)
66
if settings.EMIT_EMSCRIPTEN_LICENSE:
67
processed_js = emscripten_license + processed_js
68
if processed_js != js:
69
utils.write_file(js_target, processed_js)
70
71
72
# Returns the given string with escapes added so that it can safely be placed inside a string in JS code.
73
def escape_for_js_string(s):
74
s = s.replace('\\', '/').replace("'", "\\'").replace('"', '\\"')
75
return s
76
77
78
def legalize_sig(sig):
79
# with BigInt support all sigs are legal since we can use i64s.
80
if settings.WASM_BIGINT:
81
return sig
82
legal = [sig[0]]
83
# a return of i64 is legalized into an i32 (and the high bits are
84
# accessible on the side through getTempRet0).
85
if legal[0] == 'j':
86
legal[0] = 'i'
87
# a parameter of i64 is legalized into i32, i32
88
for s in sig[1:]:
89
if s != 'j':
90
legal.append(s)
91
else:
92
legal.append('i')
93
legal.append('i')
94
return ''.join(legal)
95
96
97
def is_legal_sig(sig):
98
# with BigInt support all sigs are legal since we can use i64s.
99
if settings.WASM_BIGINT:
100
return True
101
return sig == legalize_sig(sig)
102
103
104
def isidentifier(name):
105
# https://stackoverflow.com/questions/43244604/check-that-a-string-is-a-valid-javascript-identifier-name-using-python-3
106
return name.replace('$', '_').isidentifier()
107
108
109
def make_dynCall(sig, args):
110
if settings.MEMORY64:
111
args = list(args)
112
args[0] = f'Number({args[0]})'
113
# wasm2c and asyncify are not yet compatible with direct wasm table calls so use
114
# the legacy DYNCALLS mechanism.
115
if settings.DYNCALLS or not is_legal_sig(sig):
116
args = ','.join(args)
117
if settings.MAIN_MODULE or settings.SIDE_MODULE:
118
# In dynamic linking mode not all of the dynCall_xxx function are defined
119
# in the main module so might not be available as global symbols.
120
# See `registerDynCallSymbols` in `libdylink.js`.
121
return "dynCalls['%s'](%s)" % (sig, args)
122
return 'dynCall_%s(%s)' % (sig, args)
123
else:
124
call_args = ",".join(args[1:])
125
return f'getWasmTableEntry({args[0]})({call_args})'
126
127
128
def make_invoke(sig):
129
legal_sig = legalize_sig(sig) # TODO: do this in extcall, jscall?
130
args = ['index'] + ['a' + str(i) for i in range(1, len(legal_sig))]
131
ret = 'return ' if sig[0] != 'v' else ''
132
# For function that needs to return a genuine i64 (i.e. if legal_sig[0] is 'j')
133
# we need to return an actual BigInt, even in the exceptional case because
134
# wasm won't implicitly convert undefined to 0 in this case.
135
exceptional_ret = '\n return 0n;' if legal_sig[0] == 'j' else ''
136
body = '%s%s;' % (ret, make_dynCall(sig, args))
137
# Create a try-catch guard that rethrows the Emscripten EH exception.
138
if settings.EXCEPTION_STACK_TRACES:
139
# Exceptions thrown from C++ and longjmps will be an instance of
140
# EmscriptenEH.
141
maybe_rethrow = 'if (!(e instanceof EmscriptenEH)) throw e;'
142
else:
143
# Exceptions thrown from C++ will be a pointer (number) and longjmp will
144
# throw the number Infinity. Use the compact and fast "e !== e+0" test to
145
# check if e was not a Number.
146
maybe_rethrow = 'if (e !== e+0) throw e;'
147
148
ret = '''\
149
function invoke_%s(%s) {
150
var sp = stackSave();
151
try {
152
%s
153
} catch(e) {
154
stackRestore(sp);
155
%s
156
_setThrew(1, 0);%s
157
}
158
}''' % (sig, ','.join(args), body, maybe_rethrow, exceptional_ret)
159
160
return ret
161
162
163
def make_wasm64_wrapper(sig):
164
assert 'p' in sig.lower()
165
n_args = len(sig) - 1
166
args = ['a%d' % i for i in range(n_args)]
167
args_converted = args.copy()
168
for i, arg_type in enumerate(sig[1:]):
169
if arg_type == 'p':
170
args_converted[i] = f'BigInt({args_converted[i]})'
171
elif arg_type == 'P':
172
args_converted[i] = f'BigInt({args_converted[i]} ? {args_converted[i]} : 0)'
173
else:
174
assert arg_type == '_'
175
176
args_in = ', '.join(args)
177
args_out = ', '.join(args_converted)
178
result = f'f({args_out})'
179
if sig[0] == 'p':
180
result = f'Number({result})'
181
182
# We can't use an arrow function for the inner wrapper here since there
183
# are certain places we need to avoid strict mode still.
184
# e.g. emscripten_get_callstack (getCallstack) which uses the `arguments`
185
# global.
186
return f' var makeWrapper_{sig} = (f) => ({args_in}) => {result};\n'
187
188
189
def make_unsign_pointer_wrapper(sig):
190
assert sig[0] == 'p'
191
n_args = len(sig) - 1
192
args = ','.join('a%d' % i for i in range(n_args))
193
return f' var makeWrapper_{sig} = (f) => ({args}) => f({args}) >>> 0;\n'
194
195