Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[webidl] Use sequences instead of arrays #8849

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions tests/webidl/test.idl
Original file line number Diff line number Diff line change
Expand Up @@ -147,21 +147,21 @@ interface StructInArray {

interface ArrayClass {
void ArrayClass();
[BoundsChecked] attribute long[] int_array;
[Value] attribute StructInArray[] struct_array;
attribute StructInArray[] struct_ptr_array;
[BoundsChecked] attribute sequence<long> int_array;
[Value] attribute sequence<StructInArray> struct_array;
attribute sequence<StructInArray> struct_ptr_array;
};

interface ReceiveArrays {
void ReceiveArrays();

void giveMeArrays(float[] vertices, long[] triangles, long num);
void giveMeArrays(sequence<float> vertices, sequence<long> triangles, long num);
};

interface StoreArray {
void StoreArray();

void setArray([Const] long[] array);
void setArray([Const] sequence<long> array);
long getArrayValue(long index);
};

6 changes: 4 additions & 2 deletions third_party/WebIDL.py
Original file line number Diff line number Diff line change
Expand Up @@ -2697,8 +2697,10 @@ def finish(self, scope):
raise WebIDLError("An attribute cannot be of a dictionary type",
[self.location])
if self.type.isSequence() and not self.getExtendedAttribute("Cached"):
raise WebIDLError("A non-cached attribute cannot be of a sequence "
"type", [self.location])
# Pass on this error until FrozenArray attributes are implemented.
# raise WebIDLError("A non-cached attribute cannot be of a sequence "
# "type", [self.location])
pass
if self.type.isUnion():
for f in self.type.unroll().flatMemberTypes:
if f.isDictionary():
Expand Down
155 changes: 80 additions & 75 deletions tools/webidl_binder.py
Original file line number Diff line number Diff line change
Expand Up @@ -276,53 +276,42 @@ def build_constructor(name):

C_FLOATS = ['float', 'double']

def full_typename(arg):
return ('const ' if arg.getExtendedAttribute('Const') else '') + arg.type.name + ('[]' if arg.type.isArray() else '')

def type_to_c(t, non_pointing=False):
#print 'to c ', t
def base_type_to_c(t):
if t == 'Long':
ret = 'int'
elif t == 'UnsignedLong':
ret = 'unsigned int'
elif t == 'Short':
ret = 'short'
elif t == 'UnsignedShort':
ret = 'unsigned short'
elif t == 'Byte':
ret = 'char'
elif t == 'Octet':
ret = 'unsigned char'
elif t == 'Void':
ret = 'void'
elif t == 'String':
ret = 'char*'
elif t == 'Float':
ret = 'float'
elif t == 'Double':
ret = 'double'
elif t == 'Boolean':
ret = 'bool'
elif t == 'Any' or t == 'VoidPtr':
ret = 'void*'
elif t in interfaces:
ret = (interfaces[t].getExtendedAttribute('Prefix') or [''])[0] + t + ('' if non_pointing else '*')
else:
ret = t
return ret

def base_type_to_c(t, non_pointing=False):
t = t.replace(' (Wrapper)', '')
if t == 'Long':
ret = 'int'
elif t == 'UnsignedLong':
ret = 'unsigned int'
elif t == 'Short':
ret = 'short'
elif t == 'UnsignedShort':
ret = 'unsigned short'
elif t == 'Byte':
ret = 'char'
elif t == 'Octet':
ret = 'unsigned char'
elif t == 'Void':
ret = 'void'
elif t == 'String':
ret = 'char*'
elif t == 'Float':
ret = 'float'
elif t == 'Double':
ret = 'double'
elif t == 'Boolean':
ret = 'bool'
elif t == 'Any' or t == 'VoidPtr':
ret = 'void*'
elif t in interfaces:
ret = (interfaces[t].getExtendedAttribute('Prefix') or [''])[0] + t + ('' if non_pointing else '*')
else:
ret = t
return ret

prefix = ''
suffix = ''
if '[]' in t:
t = t.replace('[]', '')
suffix = '*'
if 'const ' in t:
t = t.replace('const ', '')
prefix = 'const '
return prefix + base_type_to_c(t) + suffix
def type_to_c(typ, non_pointing=False):
if typ.isSequence():
return base_type_to_c(typ.inner.name, non_pointing) + '*'
return base_type_to_c(typ.name, non_pointing)

def take_addr_if_nonpointer(m):
if m.getExtendedAttribute('Ref') or m.getExtendedAttribute('Value'):
Expand All @@ -335,7 +324,7 @@ def deref_if_nonpointer(m):
return ''

def type_to_cdec(raw):
name = ret = type_to_c(raw.type.name, non_pointing=True)
name = ret = type_to_c(raw.type, non_pointing=True)
if raw.getExtendedAttribute('Const'): ret = 'const ' + ret
if raw.type.name not in interfaces: return ret
if raw.getExtendedAttribute('Ref'):
Expand All @@ -344,9 +333,12 @@ def type_to_cdec(raw):
return ret
return ret + '*'

def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, operator, constructor, func_scope, call_content=None, const=False, array_attribute=False):
def render_function(class_name, func_name, sigs, return_type, non_pointer, copy, operator, constructor, func_scope, call_content=None, const=False, sequence_attribute=False):
global mid_c, mid_js, js_impl_methods

if return_type is None:
return_type = WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.void]

legacy_mode = CHECKS not in ['ALL', 'FAST']
all_checks = CHECKS == 'ALL'

Expand All @@ -357,7 +349,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
all_args = sigs.get(max_args)

if DEBUG:
print('renderfunc', class_name, func_name, list(sigs.keys()), return_type, constructor)
print('renderfunc', class_name, func_name, list(sigs.keys()), return_type.name, constructor)
for i in range(max_args):
a = all_args[i]
if isinstance(a, WebIDL.IDLArgument):
Expand All @@ -370,15 +362,18 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
cache = ('getCache(%s)[this.ptr] = this;' % class_name) if constructor else ''
call_prefix = '' if not constructor else 'this.ptr = '
call_postfix = ''
if return_type != 'Void' and not constructor: call_prefix = 'return '
if not return_type.isVoid() and not constructor: call_prefix = 'return '
if not constructor:
if return_type in interfaces:
if return_type.isInterface():
call_prefix += 'wrapPointer('
call_postfix += ', ' + return_type.name + ')'
elif return_type.isSequence():
call_prefix += 'wrapPointer('
call_postfix += ', ' + return_type + ')'
elif return_type == 'String':
call_postfix += ', ' + return_type.inner.name + ')'
elif return_type.isString():
call_prefix += 'UTF8ToString('
call_postfix += ')'
elif return_type == 'Boolean':
elif return_type.isBoolean():
call_prefix += '!!('
call_postfix += ')'

Expand All @@ -390,7 +385,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
body = ''
pre_arg = []

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

full_name = "%s::%s" % (class_name, func_name)
Expand Down Expand Up @@ -450,13 +445,13 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
do_default = True

if do_default:
if not (arg.type.isArray() and not array_attribute):
if not (arg.type.isSequence() and not sequence_attribute):
body += " if ({0} && typeof {0} === 'object') {0} = {0}.ptr;\n".format(js_arg)
if arg.type.isString():
body += " else {0} = ensureString({0});\n".format(js_arg)
else:
# an array can be received here
arg_type = arg.type.name
arg_type = arg.type.inner.name
if arg_type in ['Byte', 'Octet']:
body += " if (typeof {0} == 'object') {{ {0} = ensureInt8({0}); }}\n".format(js_arg)
elif arg_type in ['Short', 'UnsignedShort']:
Expand Down Expand Up @@ -485,20 +480,28 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
for i in range(min_args, max_args+1):
raw = sigs.get(i)
if raw is None: 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

c_arg_types = list(map(type_to_c, sig))
if sequence_attribute:
# For array attributes, the get/set methods operate on the
# elements, not the array as a whole.
arg_types = [arg.type.inner if arg.type.isSequence() else arg.type
for arg in raw]
else:
arg_types = [arg.type for arg in raw]

const_qualifiers = ['const ' if arg.getExtendedAttribute('Const') else ''
for arg in raw]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the const code seems like a new addition - I don't see any previous handling of const that it replaces? Is that a bugfix?

c_arg_types = [c + type_to_c(typ)
for c, typ in zip(const_qualifiers, arg_types)]

normal_args = ', '.join(['%s %s' % (c_arg_types[j], args[j]) for j in range(i)])
if constructor:
full_args = normal_args
else:
full_args = type_to_c(class_name, non_pointing=True) + '* self' + ('' if not normal_args else ', ' + normal_args)
full_args = base_type_to_c(class_name, non_pointing=True) + '* self' + ('' if not normal_args else ', ' + normal_args)
call_args = ', '.join(['%s%s' % ('*' if raw[j].getExtendedAttribute('Ref') else '', args[j]) for j in range(i)])
if constructor:
call = 'new ' + type_to_c(class_name, non_pointing=True)
call = 'new ' + base_type_to_c(class_name, non_pointing=True)
call += '(' + call_args + ')'
elif call_content is not None:
call = call_content
Expand All @@ -510,7 +513,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
cast_self = 'self'
if class_name != func_scope:
# this function comes from an ancestor class; for operators, we must cast it
cast_self = 'dynamic_cast<' + type_to_c(func_scope) + '>(' + cast_self + ')'
cast_self = 'dynamic_cast<' + base_type_to_c(func_scope) + '>(' + cast_self + ')'
maybe_deref = deref_if_nonpointer(raw[0])
if '=' in operator:
call = '(*%s %s %s%s)' % (cast_self, operator, maybe_deref, args[0])
Expand All @@ -521,7 +524,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,

pre = ''

basic_return = 'return ' if constructor or return_type is not 'Void' else ''
basic_return = 'return ' if constructor or not return_type.isVoid() else ''
return_prefix = basic_return
return_postfix = ''
if non_pointer:
Expand All @@ -537,12 +540,12 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
%s%s EMSCRIPTEN_KEEPALIVE %s(%s) {
%s %s%s%s;
}
''' % (maybe_const, type_to_c(class_name) if constructor else c_return_type, c_names[i], full_args, pre, return_prefix, call, return_postfix)]
''' % (maybe_const, base_type_to_c(class_name) if constructor else c_return_type, c_names[i], full_args, pre, return_prefix, call, return_postfix)]

if not constructor:
if i == max_args:
dec_args = ', '.join([type_to_cdec(raw[j]) + ' ' + args[j] for j in range(i)])
js_call_args = ', '.join(['%s%s' % (('(int)' if sig[j] in interfaces else '') + take_addr_if_nonpointer(raw[j]), args[j]) for j in range(i)])
js_call_args = ', '.join(['%s%s' % (('(int)' if arg_types[j].isInterface() else '') + take_addr_if_nonpointer(raw[j]), args[j]) for j in range(i)])

js_impl_methods += [r''' %s %s(%s) %s {
%sEM_ASM_%s({
Expand Down Expand Up @@ -630,9 +633,9 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
return_type = None
for ret, args in m.signatures():
if return_type is None:
return_type = ret.name
return_type = ret
else:
assert return_type == ret.name, 'overloads must have the same return type'
assert return_type == ret, 'overloads must have the same return type'
for i in range(len(args)+1):
if i == len(args) or args[i].optional:
assert i not in sigs, 'overloading must differentiate by # of arguments (cannot have two signatures that differ by types but not by length)'
Expand All @@ -653,7 +656,8 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
if not m.isAttr(): continue
attr = m.identifier.name

if m.type.isArray():
if m.type.isSequence():
get_return_type = m.type.inner
get_sigs = { 1: [Dummy({ 'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long] })] }
set_sigs = { 2: [Dummy({ 'type': WebIDL.BuiltinTypes[WebIDL.IDLBuiltinType.Types.long] }),
Dummy({ 'type': m.type })] }
Expand All @@ -664,6 +668,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
get_call_content = "(%s, %s)" % (bounds_check, get_call_content)
set_call_content = "(%s, %s)" % (bounds_check, set_call_content)
else:
get_return_type = m.type
get_sigs = { 0: [] }
set_sigs = { 1: [Dummy({ 'type': m.type })] }
get_call_content = take_addr_if_nonpointer(m) + 'self->' + attr
Expand All @@ -673,15 +678,15 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
mid_js += [r'''
%s.prototype['%s'] = %s.prototype.%s = ''' % (name, get_name, name, get_name)]
render_function(name,
get_name, get_sigs, m.type.name,
get_name, get_sigs, get_return_type,
None,
None,
None,
False,
func_scope=interface,
call_content=get_call_content,
const=m.getExtendedAttribute('Const'),
array_attribute=m.type.isArray())
sequence_attribute=m.type.isSequence())

if m.readonly:
mid_js += [r'''
Expand All @@ -691,15 +696,15 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
mid_js += [r'''
%s.prototype['%s'] = %s.prototype.%s = ''' % (name, set_name, name, set_name)]
render_function(name,
set_name, set_sigs, 'Void',
set_name, set_sigs, None,
None,
None,
None,
False,
func_scope=interface,
call_content=set_call_content,
const=m.getExtendedAttribute('Const'),
array_attribute=m.type.isArray())
sequence_attribute=m.type.isSequence())
mid_js += [r'''
Object.defineProperty(%s.prototype, '%s', { get: %s.prototype.%s, set: %s.prototype.%s });''' % (name, attr, name, get_name, name, set_name)]

Expand All @@ -708,7 +713,7 @@ def render_function(class_name, func_name, sigs, return_type, non_pointer, copy,
mid_js += [r'''
%s.prototype['__destroy__'] = %s.prototype.__destroy__ = ''' % (name, name)]
render_function(name,
'__destroy__', { 0: [] }, 'Void',
'__destroy__', { 0: [] }, None,
None,
None,
None,
Expand All @@ -724,7 +729,7 @@ class %s : public %s {
public:
%s
};
''' % (name, type_to_c(js_impl, non_pointing=True), '\n'.join(js_impl_methods))]
''' % (name, base_type_to_c(js_impl, non_pointing=True), '\n'.join(js_impl_methods))]

deferred_js = []

Expand Down