Skip to content

Commit 969b661

Browse files
committed
[wasm64] Update webidl to support wasm64
The changes to `test/webidl/post.js` make debugging easier since they don't silently swallow errors. I'm not sure why those try/catches where in there since all those calls are expected to succeed.
1 parent 50a7531 commit 969b661

File tree

4 files changed

+93
-31
lines changed

4 files changed

+93
-31
lines changed

test/test_core.py

Lines changed: 6 additions & 3 deletions
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,7 +7670,7 @@ 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:

test/webidl/post.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -186,13 +186,13 @@ console.log('int_array[0] == ' + arrayClass.get_int_array(0));
186186
console.log('int_array[7] == ' + arrayClass.get_int_array(7));
187187

188188
try {
189-
arrayClass.set_int_array(-1, struct);
189+
arrayClass.set_int_array(-1, 42);
190190
} catch (e) {
191191
console.log('idx -1: ' + e);
192192
}
193193

194194
try {
195-
arrayClass.set_int_array(8, struct);
195+
arrayClass.set_int_array(8, 42);
196196
} catch (e) {
197197
console.log('idx 8: ' + e);
198198
}
@@ -270,7 +270,7 @@ if (isMemoryGrowthAllowed) {
270270
intArray = intArray.concat(intArray);
271271
storeArray.setArray(intArray);
272272
}
273-
273+
274274
// Make sure the array was copied to the newly allocated HEAP
275275
var numCopiedEntries = 0;
276276
for (var i = 0; i < intArray.length; i++) {

tools/emscripten.py

Lines changed: 2 additions & 0 deletions
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

Lines changed: 82 additions & 25 deletions
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
@@ -32,24 +33,36 @@
3233
# DEBUG=1 will print debug info in render_function
3334
DEBUG = os.environ.get('IDL_VERBOSE') == '1'
3435

35-
if DEBUG:
36-
print(f'Debug print ON, CHECKS=${CHECKS}')
36+
def dbg(*args):
37+
if DEBUG:
38+
print(*args, file=sys.stderr)
39+
40+
dbg(f'Debug print ON, CHECKS=${CHECKS}')
3741

3842
# We need to avoid some closure errors on the constructors we define here.
3943
CONSTRUCTOR_CLOSURE_SUPPRESSIONS = '/** @suppress {undefinedVars, duplicate} @this{Object} */'
4044

4145

4246
class Dummy:
43-
def __init__(self, init):
44-
for k, v in init.items():
45-
self.__dict__[k] = v
47+
def __init__(self, type):
48+
self.type = type
49+
50+
def __repr__(self):
51+
return f'<Dummy type:{self.type}>'
4652

4753
def getExtendedAttribute(self, _name):
4854
return None
4955

5056

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

@@ -392,13 +405,12 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
392405
all_args = sigs.get(max_args)
393406

394407
if DEBUG:
395-
print('renderfunc', class_name, func_name, list(sigs.keys()), return_type, constructor)
396-
for i in range(max_args):
397-
a = all_args[i]
408+
dbg('renderfunc', class_name, func_name, list(sigs.keys()), return_type, constructor)
409+
for i, a in enumerate(all_args):
398410
if isinstance(a, WebIDL.IDLArgument):
399-
print(' ', a.identifier.name, a.identifier, a.type, a.optional)
411+
dbg(' ', a.identifier.name, a.identifier, a.type, a.optional)
400412
else:
401-
print(' arg%d' % i)
413+
dbg(' arg%d (%s)' % (i, a))
402414

403415
# JS
404416

@@ -409,6 +421,10 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
409421
call_postfix = ''
410422
if return_type != 'Void' and not constructor:
411423
call_prefix = 'return '
424+
425+
if options.wasm64 and (constructor or return_type in interfaces or return_type == 'String'):
426+
call_postfix += ')'
427+
412428
if not constructor:
413429
if return_type in interfaces:
414430
call_prefix += 'wrapPointer('
@@ -420,17 +436,28 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
420436
call_prefix += '!!('
421437
call_postfix += ')'
422438

439+
if options.wasm64 and (constructor or return_type in interfaces or return_type == 'String'):
440+
call_prefix += 'Number('
441+
442+
423443
args = [(all_args[i].identifier.name if isinstance(all_args[i], WebIDL.IDLArgument) else ('arg%d' % i)) for i in range(max_args)]
424444
if not constructor and not is_static:
425445
body = ' var self = this.ptr;\n'
426-
pre_arg = ['self']
446+
if options.wasm64:
447+
pre_arg = ['BigInt(self)']
448+
else:
449+
pre_arg = ['self']
427450
else:
428451
body = ''
429452
pre_arg = []
430453

431454
if any(arg.type.isString() or arg.type.isArray() for arg in all_args):
432455
body += ' ensureCache.prepare();\n'
433456

457+
def is_ptr_arg(i):
458+
t = all_args[i].type
459+
return (t.isArray() or t.isAny() or t.isString() or t.isObject() or t.isInterface())
460+
434461
for i, (js_arg, arg) in enumerate(zip(args, all_args)):
435462
if i >= min_args:
436463
optional = True
@@ -494,9 +521,11 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
494521

495522
if do_default:
496523
if not (arg.type.isArray() and not array_attribute):
497-
body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg)
524+
body += f" if ({js_arg} && typeof {js_arg} === 'object') {js_arg} = {js_arg}.ptr;\n"
498525
if arg.type.isString():
499526
body += " else {0} = ensureString({0});\n".format(js_arg)
527+
if options.wasm64 and is_ptr_arg(i):
528+
body += f' if ({args[i]} === null) {args[i]} = 0;\n'
500529
else:
501530
# an array can be received here
502531
arg_type = arg.type.name
@@ -511,18 +540,45 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
511540
elif arg_type == 'Double':
512541
body += " if (typeof {0} == 'object') {{ {0} = ensureFloat64({0}); }}\n".format(js_arg)
513542

543+
call_args = pre_arg
544+
545+
for i, arg in enumerate(args):
546+
if options.wasm64 and is_ptr_arg(i):
547+
arg = f'BigInt({arg})'
548+
call_args.append(arg)
549+
514550
c_names = {}
551+
552+
def make_call_args(i):
553+
if pre_arg:
554+
i += 1
555+
return ', '.join(call_args[:i])
556+
515557
for i in range(min_args, max_args):
516-
c_names[i] = 'emscripten_bind_%s_%d' % (bindings_name, i)
517-
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')
518-
c_names[max_args] = 'emscripten_bind_%s_%d' % (bindings_name, max_args)
519-
body += ' %s%s(%s)%s;\n' % (call_prefix, '_' + c_names[max_args], ', '.join(pre_arg + args), call_postfix)
558+
c_names[i] = f'emscripten_bind_{bindings_name}_{i}'
559+
if 'return ' in call_prefix:
560+
after_call = ''
561+
else:
562+
after_call = '; ' + cache + 'return'
563+
args_for_call = make_call_args(i)
564+
body += ' if (%s === undefined) { %s_%s(%s)%s%s }\n' % (args[i], call_prefix, c_names[i],
565+
args_for_call,
566+
call_postfix, after_call)
567+
dbg(call_prefix)
568+
c_names[max_args] = f'emscripten_bind_{bindings_name}_{max_args}'
569+
args_for_call = make_call_args(len(args))
570+
body += ' %s_%s(%s)%s;\n' % (call_prefix, c_names[max_args], args_for_call, call_postfix)
520571
if cache:
521-
body += ' ' + cache + '\n'
572+
body += f' {cache}\n'
573+
574+
if constructor:
575+
declare_name = ' ' + func_name
576+
else:
577+
declare_name = ''
522578
mid_js.append(r'''function%s(%s) {
523579
%s
524580
};
525-
''' % ((' ' + func_name) if constructor else '', ', '.join(args), body[:-1]))
581+
''' % (declare_name, ', '.join(args), body[:-1]))
526582

527583
# C
528584

@@ -532,7 +588,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer,
532588
continue
533589
sig = list(map(full_typename, raw))
534590
if array_attribute:
535-
sig = [x.replace('[]', '') for x in sig] # for arrays, ignore that this is an array - our get/set methods operate on the elements
591+
# for arrays, ignore that this is an array - our get/set methods operate on the elements
592+
sig = [x.replace('[]', '') for x in sig]
536593

537594
c_arg_types = list(map(type_to_c, sig))
538595
c_class_name = type_to_c(class_name, non_pointing=True)
@@ -726,9 +783,9 @@ def add_bounds_check_impl():
726783
attr = m.identifier.name
727784

728785
if m.type.isArray():
729-
get_sigs = {1: [Dummy({'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long]})]}
730-
set_sigs = {2: [Dummy({'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long]}),
731-
Dummy({'type': m.type})]}
786+
get_sigs = {1: [Dummy(type=WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long])]}
787+
set_sigs = {2: [Dummy(type=WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long]),
788+
Dummy(type=m.type.inner)]}
732789
get_call_content = take_addr_if_nonpointer(m) + 'self->' + attr + '[arg0]'
733790
set_call_content = 'self->' + attr + '[arg0] = ' + deref_if_nonpointer(m) + 'arg1'
734791
if m.getExtendedAttribute('BoundsChecked'):
@@ -740,7 +797,7 @@ def add_bounds_check_impl():
740797
set_call_content = "(%s, %s)" % (bounds_check, set_call_content)
741798
else:
742799
get_sigs = {0: []}
743-
set_sigs = {1: [Dummy({'type': m.type})]}
800+
set_sigs = {1: [Dummy(type=m.type)]}
744801
get_call_content = take_addr_if_nonpointer(m) + 'self->' + attr
745802
set_call_content = 'self->' + attr + ' = ' + deref_if_nonpointer(m) + 'arg0'
746803

0 commit comments

Comments
 (0)