blob: d96969a2e55cf9d771173b2ff03679f40ce8330c [file]
# 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
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(user_pre_js, files_pre_js):
# the normal thing is to just combine the pre-js content
if not settings.ASSERTIONS:
return files_pre_js + user_pre_js
# 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
return files_pre_js + '''
// All the pre-js content up to here must remain later on, we need to run
// it.
if (Module['ENVIRONMENT_IS_PTHREAD']) Module['preRun'] = [];
var necessaryPreJSTasks = Module['preRun'].slice();
''' + user_pre_js + '''
if (!Module['preRun']) throw 'Module.preRun should exist because file support used it; did a pre-js delete it?';
necessaryPreJSTasks.forEach(function(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?';
});
'''
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.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:
func_ptr = args[0]
if settings.MEMORY64:
func_ptr = f'Number({func_ptr})'
call_args = ",".join(args[1:])
return f'getWasmTableEntry({func_ptr})({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))
# Exceptions thrown from C++ exception will be integer numbers.
# longjmp will throw the number Infinity.
# Create a try-catch guard that rethrows the exception if anything else
# than a Number was thrown. To do that quickly and in a code size conserving
# manner, use the compact test "e !== e+0" to check if e was not a Number.
ret = '''\
function invoke_%s(%s) {
var sp = stackSave();
try {
%s
} catch(e) {
stackRestore(sp);
if (e !== e+0) throw e;
_setThrew(1, 0);%s
}
}''' % (sig, ','.join(args), body, 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})'
return f'''
function wasm64Wrapper_{sig}(f) {{
return function({args_in}) {{
return {result};
}};
}}'''