diff --git a/test/test_core.py b/test/test_core.py index cad06bae631fb..90477dbed15a8 100644 --- a/test/test_core.py +++ b/test/test_core.py @@ -7627,7 +7627,6 @@ def test_embind_no_rtti_followed_by_rtti(self): self.emcc_args += ['-lembind', '-fno-rtti', '-frtti'] self.do_run(src, '418\ndotest returned: 42\n') - @no_wasm64('webidl not compatible with MEMORY64 yet') @parameterized({ '': ('DEFAULT', False), 'all': ('ALL', False), @@ -7646,8 +7645,12 @@ def test_webidl(self, mode, allow_memory_growth): self.set_setting('WASM_ASYNC_COMPILATION', 0) # Force IDL checks mode + if self.is_wasm64(): + args = ['--wasm64'] + else: + args = [] with env_modify({'IDL_CHECKS': mode}): - self.run_process([WEBIDL_BINDER, test_file('webidl/test.idl'), 'glue']) + self.run_process([WEBIDL_BINDER, test_file('webidl/test.idl'), 'glue'] + args) self.assertExists('glue.cpp') self.assertExists('glue.js') @@ -7667,7 +7670,7 @@ def test_webidl(self, mode, allow_memory_growth): # Export things on "TheModule". This matches the typical use pattern of the bound library # being used as Box2D.* or Ammo.*, and we cannot rely on "Module" being always present (closure may remove it). - self.emcc_args += ['-Wall', '--post-js=glue.js', '--extern-post-js=extern-post.js'] + self.emcc_args += ['--post-js=glue.js', '--extern-post-js=extern-post.js'] if mode == 'ALL': self.emcc_args += ['-sASSERTIONS'] if allow_memory_growth: diff --git a/test/webidl/post.js b/test/webidl/post.js index ed08f6c5bd73a..84a007c6fef9d 100644 --- a/test/webidl/post.js +++ b/test/webidl/post.js @@ -270,7 +270,7 @@ if (isMemoryGrowthAllowed) { intArray = intArray.concat(intArray); storeArray.setArray(intArray); } - + // Make sure the array was copied to the newly allocated HEAP var numCopiedEntries = 0; for (var i = 0; i < intArray.length; i++) { diff --git a/tools/emscripten.py b/tools/emscripten.py index d531a380ddfa0..24f9095f347d5 100644 --- a/tools/emscripten.py +++ b/tools/emscripten.py @@ -893,12 +893,14 @@ def create_pointer_conversion_wrappers(metadata): 'stackAlloc': 'pp', 'emscripten_builtin_malloc': 'pp', 'malloc': 'pp', + 'webidl_malloc': 'pp', 'memalign': 'ppp', 'memcmp': '_ppp', 'memcpy': 'pppp', '__getTypeName': 'pp', 'setThrew': '_p', 'free': '_p', + 'webidl_free': '_p', 'stackRestore': '_p', '__cxa_is_pointer_type': '_p', 'stackSave': 'p', diff --git a/tools/webidl_binder.py b/tools/webidl_binder.py index 0996e5a13fbde..ecc62a1b45cab 100644 --- a/tools/webidl_binder.py +++ b/tools/webidl_binder.py @@ -8,6 +8,7 @@ https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html """ +import argparse import os import sys from typing import List @@ -32,8 +33,11 @@ # DEBUG=1 will print debug info in render_function DEBUG = os.environ.get('IDL_VERBOSE') == '1' -if DEBUG: - print(f'Debug print ON, CHECKS=${CHECKS}') +def dbg(*args): + if DEBUG: + print(*args, file=sys.stderr) + +dbg(f'Debug print ON, CHECKS=${CHECKS}') # We need to avoid some closure errors on the constructors we define here. CONSTRUCTOR_CLOSURE_SUPPRESSIONS = '/** @suppress {undefinedVars, duplicate} @this{Object} */' @@ -48,8 +52,15 @@ def getExtendedAttribute(self, _name): return None -input_file = sys.argv[1] -output_base = sys.argv[2] +parser = argparse.ArgumentParser() +parser.add_argument('--wasm64', action='store_true', default=False, + help='Build for wasm64') +parser.add_argument('infile') +parser.add_argument('outfile') +options = parser.parse_args() + +input_file = options.infile +output_base = options.outfile cpp_output = output_base + '.cpp' js_output = output_base + '.js' @@ -392,13 +403,13 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, all_args = sigs.get(max_args) if DEBUG: - print('renderfunc', class_name, func_name, list(sigs.keys()), return_type, constructor) - for i in range(max_args): - a = all_args[i] + dbg('renderfunc', class_name, func_name, list(sigs.keys()), return_type, constructor) + for i, a in enumerate(all_args): + dbg(type(a)) if isinstance(a, WebIDL.IDLArgument): - print(' ', a.identifier.name, a.identifier, a.type, a.optional) + dbg(' ', a.identifier.name, a.identifier, a.type, a.optional) else: - print(' arg%d' % i) + dbg(' arg%d' % i) # JS @@ -410,6 +421,9 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, if return_type != 'Void' and not constructor: call_prefix = 'return ' if not constructor: + if options.wasm64 and (return_type in interfaces or return_type == 'String'): + call_postfix += ')' + if return_type in interfaces: call_prefix += 'wrapPointer(' call_postfix += ', ' + return_type + ')' @@ -420,10 +434,17 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, call_prefix += '!!(' call_postfix += ')' + if options.wasm64 and (return_type in interfaces or return_type == 'String'): + call_prefix += 'Number(' + + args = [(all_args[i].identifier.name if isinstance(all_args[i], WebIDL.IDLArgument) else ('arg%d' % i)) for i in range(max_args)] if not constructor and not is_static: body = ' var self = this.ptr;\n' - pre_arg = ['self'] + if options.wasm64: + pre_arg = ['BigInt(self)'] + else: + pre_arg = ['self'] else: body = '' pre_arg = [] @@ -431,6 +452,12 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, if any(arg.type.isString() or arg.type.isArray() for arg in all_args): body += ' ensureCache.prepare();\n' + def is_ptr_arg(i): + if not isinstance(all_args[i], WebIDL.IDLArgument): + return False + t = all_args[i].type + return (t.isArray() or t.isAny() or t.isString() or t.isObject() or t.isInterface()) + for i, (js_arg, arg) in enumerate(zip(args, all_args)): if i >= min_args: optional = True @@ -494,9 +521,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, if do_default: if not (arg.type.isArray() and not array_attribute): - body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg) + body += f" if ({js_arg} && typeof {js_arg} === 'object') {js_arg} = {js_arg}.ptr;\n" if arg.type.isString(): body += " else {0} = ensureString({0});\n".format(js_arg) + if options.wasm64 and is_ptr_arg(i): + body += f' if ({args[i]} === null) {args[i]} = 0;\n' else: # an array can be received here arg_type = arg.type.name @@ -511,18 +540,44 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, elif arg_type == 'Double': body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}); }}\n".format(js_arg) + call_args = pre_arg + + for i, arg in enumerate(args): + if options.wasm64 and is_ptr_arg(i): + arg = f'BigInt({arg})' + call_args.append(arg) + c_names = {} + + def make_call_args(i): + if pre_arg: + i += 1 + return ', '.join(call_args[:i]) + for i in range(min_args, max_args): - c_names[i] = 'emscripten_bind_%s_%d' % (bindings_name, i) - body += ' if (%s === undefined) { %s%s(%s)%s%s }\n' % (args[i], call_prefix, '_' + c_names[i], ', '.join(pre_arg + args[:i]), call_postfix, '' if 'return ' in call_prefix else '; ' + (cache or ' ') + 'return') - c_names[max_args] = 'emscripten_bind_%s_%d' % (bindings_name, max_args) - body += ' %s%s(%s)%s;\n' % (call_prefix, '_' + c_names[max_args], ', '.join(pre_arg + args), call_postfix) + c_names[i] = f'emscripten_bind_{bindings_name}_{i}' + if 'return ' in call_prefix: + after_call = '' + else: + after_call = '; ' + cache + 'return' + args_for_call = make_call_args(i) + body += ' if (%s === undefined) { %s_%s(%s)%s%s }\n' % (args[i], call_prefix, c_names[i], + args_for_call, + call_postfix, after_call) + c_names[max_args] = f'emscripten_bind_{bindings_name}_{max_args}' + args_for_call = make_call_args(len(args)) + body += ' %s_%s(%s)%s;\n' % (call_prefix, c_names[max_args], args_for_call, call_postfix) if cache: - body += ' ' + cache + '\n' + body += f' {cache}\n' + + if constructor: + declare_name = ' ' + func_name + else: + declare_name = '' mid_js.append(r'''function%s(%s) { %s }; -''' % ((' ' + func_name) if constructor else '', ', '.join(args), body[:-1])) +''' % (declare_name, ', '.join(args), body[:-1])) # C @@ -532,7 +587,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, continue sig = list(map(full_typename, raw)) if array_attribute: - sig = [x.replace('[]', '') for x in sig] # for arrays, ignore that this is an array - our get/set methods operate on the elements + # for arrays, ignore that this is an array - our get/set methods operate on the elements + sig = [x.replace('[]', '') for x in sig] c_arg_types = list(map(type_to_c, sig)) c_class_name = type_to_c(class_name, non_pointing=True)