Skip to content

Commit 80fb9d4

Browse files
authored
[wasm64] Update webidl to support wasm64 (#21151)
1 parent 3958a80 commit 80fb9d4

File tree

3 files changed

+77
-23
lines changed

3 files changed

+77
-23
lines changed

test/test_core.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -7627,7 +7627,6 @@ def test_embind_no_rtti_followed_by_rtti(self):
76277627
self.emcc_args += ['-lembind', '-fno-rtti', '-frtti']
76287628
self.do_run(src, '418\ndotest returned: 42\n')
76297629

7630-
@no_wasm64('webidl not compatible with MEMORY64 yet')
76317630
@parameterized({
76327631
'': ('DEFAULT', False),
76337632
'all': ('ALL', False),
@@ -7646,8 +7645,12 @@ def test_webidl(self, mode, allow_memory_growth):
76467645
self.set_setting('WASM_ASYNC_COMPILATION', 0)
76477646

76487647
# Force IDL checks mode
7648+
if self.is_wasm64():
7649+
args = ['--wasm64']
7650+
else:
7651+
args = []
76497652
with env_modify({'IDL_CHECKS': mode}):
7650-
self.run_process([WEBIDL_BINDER, test_file('webidl/test.idl'), 'glue'])
7653+
self.run_process([WEBIDL_BINDER, test_file('webidl/test.idl'), 'glue'] + args)
76517654
self.assertExists('glue.cpp')
76527655
self.assertExists('glue.js')
76537656

@@ -7667,11 +7670,13 @@ def test_webidl(self, mode, allow_memory_growth):
76677670

76687671
# Export things on "TheModule". This matches the typical use pattern of the bound library
76697672
# being used as Box2D.* or Ammo.*, and we cannot rely on "Module" being always present (closure may remove it).
7670-
self.emcc_args += ['-Wall', '--post-js=glue.js', '--extern-post-js=extern-post.js']
7673+
self.emcc_args += ['--post-js=glue.js', '--extern-post-js=extern-post.js']
76717674
if mode == 'ALL':
76727675
self.emcc_args += ['-sASSERTIONS']
76737676
if allow_memory_growth:
76747677
self.set_setting('ALLOW_MEMORY_GROWTH')
7678+
if self.get_setting('INITIAL_MEMORY') == '4200mb':
7679+
self.set_setting('MAXIMUM_MEMORY', '4300mb')
76757680

76767681
self.do_run_in_out_file_test(test_file('webidl/test.cpp'), out_suffix='_' + mode, includes=['.'])
76777682

tools/emscripten.py

+2
Original file line numberDiff line numberDiff line change
@@ -893,12 +893,14 @@ def create_pointer_conversion_wrappers(metadata):
893893
'stackAlloc': 'pp',
894894
'emscripten_builtin_malloc': 'pp',
895895
'malloc': 'pp',
896+
'webidl_malloc': 'pp',
896897
'memalign': 'ppp',
897898
'memcmp': '_ppp',
898899
'memcpy': 'pppp',
899900
'__getTypeName': 'pp',
900901
'setThrew': '_p',
901902
'free': '_p',
903+
'webidl_free': '_p',
902904
'stackRestore': '_p',
903905
'__cxa_is_pointer_type': '_p',
904906
'stackSave': 'p',

tools/webidl_binder.py

+67-20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
https://emscripten.org/docs/porting/connecting_cpp_and_javascript/WebIDL-Binder.html
99
"""
1010

11+
import argparse
1112
import os
1213
import sys
1314
from typing import List
@@ -55,8 +56,15 @@ def getExtendedAttribute(self, _name):
5556
return None
5657

5758

58-
input_file = sys.argv[1]
59-
output_base = sys.argv[2]
59+
parser = argparse.ArgumentParser()
60+
parser.add_argument('--wasm64', action='store_true', default=False,
61+
help='Build for wasm64')
62+
parser.add_argument('infile')
63+
parser.add_argument('outfile')
64+
options = parser.parse_args()
65+
66+
input_file = options.infile
67+
output_base = options.outfile
6068
cpp_output = output_base + '.cpp'
6169
js_output = output_base + '.js'
6270

@@ -89,7 +97,7 @@ def getExtendedAttribute(self, _name):
8997
#include <emscripten.h>
9098
#include <stdlib.h>
9199
92-
EM_JS_DEPS(webidl_binder, "$intArrayFromString,$UTF8ToString");
100+
EM_JS_DEPS(webidl_binder, "$intArrayFromString,$UTF8ToString,$alignMemory");
93101
''']
94102

95103
mid_c = ['''
@@ -214,7 +222,7 @@ def build_constructor(name):
214222
assert(ensureCache.buffer);
215223
var bytes = view.BYTES_PER_ELEMENT;
216224
var len = array.length * bytes;
217-
len = (len + 7) & -8; // keep things aligned to 8 byte boundaries
225+
len = alignMemory(len, 8); // keep things aligned to 8 byte boundaries
218226
var ret;
219227
if (ensureCache.pos + len >= ensureCache.size) {
220228
// we failed to allocate in the buffer, ensureCache time around :(
@@ -230,13 +238,7 @@ def build_constructor(name):
230238
return ret;
231239
},
232240
copy(array, view, offset) {
233-
offset >>>= 0;
234-
var bytes = view.BYTES_PER_ELEMENT;
235-
switch (bytes) {
236-
case 2: offset >>>= 1; break;
237-
case 4: offset >>>= 2; break;
238-
case 8: offset >>>= 3; break;
239-
}
241+
offset /= view.BYTES_PER_ELEMENT;
240242
for (var i = 0; i < array.length; i++) {
241243
view[offset + i] = array[i];
242244
}
@@ -415,6 +417,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
415417
call_postfix = ''
416418
if return_type != 'Void' and not constructor:
417419
call_prefix = 'return '
420+
421+
ptr_rtn = constructor or return_type in interfaces or return_type == 'String'
422+
if options.wasm64 and ptr_rtn:
423+
call_postfix += ')'
424+
418425
if not constructor:
419426
if return_type in interfaces:
420427
call_prefix += 'wrapPointer('
@@ -426,17 +433,27 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
426433
call_prefix += '!!('
427434
call_postfix += ')'
428435

436+
if options.wasm64 and ptr_rtn:
437+
call_prefix += 'Number('
438+
429439
args = [(all_args[i].identifier.name if isinstance(all_args[i], WebIDL.IDLArgument) else ('arg%d' % i)) for i in range(max_args)]
430440
if not constructor and not is_static:
431441
body = ' var self = this.ptr;\n'
432-
pre_arg = ['self']
442+
if options.wasm64:
443+
pre_arg = ['BigInt(self)']
444+
else:
445+
pre_arg = ['self']
433446
else:
434447
body = ''
435448
pre_arg = []
436449

437450
if any(arg.type.isString() or arg.type.isArray() for arg in all_args):
438451
body += ' ensureCache.prepare();\n'
439452

453+
def is_ptr_arg(i):
454+
t = all_args[i].type
455+
return (t.isArray() or t.isAny() or t.isString() or t.isObject() or t.isInterface())
456+
440457
for i, (js_arg, arg) in enumerate(zip(args, all_args)):
441458
if i >= min_args:
442459
optional = True
@@ -500,9 +517,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
500517

501518
if do_default:
502519
if not (arg.type.isArray() and not array_attribute):
503-
body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg)
520+
body += f" if ({js_arg} && typeof {js_arg} === 'object') {js_arg} = {js_arg}.ptr;\n"
504521
if arg.type.isString():
505522
body += " else {0} = ensureString({0});\n".format(js_arg)
523+
if options.wasm64 and is_ptr_arg(i):
524+
body += f' if ({args[i]} === null) {args[i]} = 0;\n'
506525
else:
507526
# an array can be received here
508527
arg_type = arg.type.name
@@ -517,18 +536,45 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
517536
elif arg_type == 'Double':
518537
body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}); }}\n".format(js_arg)
519538

539+
call_args = pre_arg
540+
541+
for i, arg in enumerate(args):
542+
if options.wasm64 and is_ptr_arg(i):
543+
arg = f'BigInt({arg})'
544+
call_args.append(arg)
545+
520546
c_names = {}
547+
548+
def make_call_args(i):
549+
if pre_arg:
550+
i += 1
551+
return ', '.join(call_args[:i])
552+
521553
for i in range(min_args, max_args):
522-
c_names[i] = 'emscripten_bind_%s_%d' % (bindings_name, i)
523-
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')
524-
c_names[max_args] = 'emscripten_bind_%s_%d' % (bindings_name, max_args)
525-
body += ' %s%s(%s)%s;\n' % (call_prefix, '_' + c_names[max_args], ', '.join(pre_arg + args), call_postfix)
554+
c_names[i] = f'emscripten_bind_{bindings_name}_{i}'
555+
if 'return ' in call_prefix:
556+
after_call = ''
557+
else:
558+
after_call = '; ' + cache + 'return'
559+
args_for_call = make_call_args(i)
560+
body += ' if (%s === undefined) { %s_%s(%s)%s%s }\n' % (args[i], call_prefix, c_names[i],
561+
args_for_call,
562+
call_postfix, after_call)
563+
dbg(call_prefix)
564+
c_names[max_args] = f'emscripten_bind_{bindings_name}_{max_args}'
565+
args_for_call = make_call_args(len(args))
566+
body += ' %s_%s(%s)%s;\n' % (call_prefix, c_names[max_args], args_for_call, call_postfix)
526567
if cache:
527-
body += ' ' + cache + '\n'
568+
body += f' {cache}\n'
569+
570+
if constructor:
571+
declare_name = ' ' + func_name
572+
else:
573+
declare_name = ''
528574
mid_js.append(r'''function%s(%s) {
529575
%s
530576
};
531-
''' % ((' ' + func_name) if constructor else '', ', '.join(args), body[:-1]))
577+
''' % (declare_name, ', '.join(args), body[:-1]))
532578

533579
# C
534580

@@ -538,7 +584,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
538584
continue
539585
sig = list(map(full_typename, raw))
540586
if array_attribute:
541-
sig = [x.replace('[]', '') for x in sig] # for arrays, ignore that this is an array - our get/set methods operate on the elements
587+
# for arrays, ignore that this is an array - our get/set methods operate on the elements
588+
sig = [x.replace('[]', '') for x in sig]
542589

543590
c_arg_types = list(map(type_to_c, sig))
544591
c_class_name = type_to_c(class_name, non_pointing=True)

0 commit comments

Comments
 (0)