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