Auto merge of #12819 - jdm:iterable2, r=nox

Support pair and value iterable WebIDL bindings

The actual iterator implementation and JSAPI calls related to setting up the interface are ported directly from Gecko's Codegen.py, IterableIterator.h, and IterableIterator.webidl. The changes to support multiple interfaces in one file are required because the internal iterator interface the parser generates gets associated with the original interface's WebIDL file. It seemed like a good time to address #571 in that case.

---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #12628 and fix #571.
- [X] There are tests for these changes

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/12819)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2016-08-24 12:47:53 -05:00 committed by GitHub
commit 1370fa5e3b
16 changed files with 847 additions and 147 deletions

View file

@ -21,6 +21,7 @@ from WebIDL import (
IDLType, IDLType,
IDLInterfaceMember, IDLInterfaceMember,
IDLUndefinedValue, IDLUndefinedValue,
IDLWrapperType,
) )
from Configuration import ( from Configuration import (
@ -29,6 +30,7 @@ from Configuration import (
getTypesFromCallback, getTypesFromCallback,
getTypesFromDescriptor, getTypesFromDescriptor,
getTypesFromDictionary, getTypesFromDictionary,
iteratorNativeType
) )
AUTOGENERATED_WARNING_COMMENT = \ AUTOGENERATED_WARNING_COMMENT = \
@ -719,7 +721,9 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
raise TypeError("Can't handle array arguments yet") raise TypeError("Can't handle array arguments yet")
if type.isSequence(): if type.isSequence():
innerInfo = getJSToNativeConversionInfo(innerSequenceType(type), descriptorProvider) innerInfo = getJSToNativeConversionInfo(innerSequenceType(type),
descriptorProvider,
isMember=isMember)
declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">") declType = CGWrapper(innerInfo.declType, pre="Vec<", post=">")
config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs) config = getConversionConfigForType(type, isEnforceRange, isClamp, treatNullAs)
@ -942,7 +946,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull()
callback = type.unroll().callback callback = type.unroll().callback
declType = CGGeneric('%s::%s' % (getModuleFromObject(callback), callback.identifier.name)) declType = CGGeneric(callback.identifier.name)
finalDeclType = CGTemplatedType("Rc", declType) finalDeclType = CGTemplatedType("Rc", declType)
conversion = CGCallbackTempRoot(declType.define()) conversion = CGCallbackTempRoot(declType.define())
@ -1518,6 +1522,46 @@ class MethodDefiner(PropertyDefiner):
"length": 0, "length": 0,
"condition": "Condition::Satisfied"}) "condition": "Condition::Satisfied"})
# Generate the keys/values/entries aliases for value iterables.
maplikeOrSetlikeOrIterable = descriptor.interface.maplikeOrSetlikeOrIterable
if (not static and not unforgeable and
(maplikeOrSetlikeOrIterable and
maplikeOrSetlikeOrIterable.isIterable() and
maplikeOrSetlikeOrIterable.isValueIterator())):
# Add our keys/values/entries/forEach
self.regular.append({
"name": "keys",
"methodInfo": False,
"selfHostedName": "ArrayKeys",
"length": 0,
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "values",
"methodInfo": False,
"selfHostedName": "ArrayValues",
"length": 0,
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "entries",
"methodInfo": False,
"selfHostedName": "ArrayEntries",
"length": 0,
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
self.regular.append({
"name": "forEach",
"methodInfo": False,
"selfHostedName": "ArrayForEach",
"length": 0,
"condition": PropertyDefiner.getControllingCondition(m,
descriptor)
})
isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable")) isUnforgeableInterface = bool(descriptor.interface.getExtendedAttribute("Unforgeable"))
if not static and unforgeable == isUnforgeableInterface: if not static and unforgeable == isUnforgeableInterface:
stringifier = descriptor.operations['Stringifier'] stringifier = descriptor.operations['Stringifier']
@ -1738,7 +1782,7 @@ class CGImports(CGWrapper):
""" """
Generates the appropriate import/use statements. Generates the appropriate import/use statements.
""" """
def __init__(self, child, descriptors, callbacks, imports, config, ignored_warnings=None): def __init__(self, child, descriptors, callbacks, dictionaries, enums, imports, config, ignored_warnings=None):
""" """
Adds a set of imports. Adds a set of imports.
""" """
@ -1752,17 +1796,19 @@ class CGImports(CGWrapper):
] ]
def componentTypes(type): def componentTypes(type):
if type.nullable(): if type.isType() and type.nullable():
type = type.unroll() type = type.unroll()
if type.isUnion(): if type.isUnion():
return type.flatMemberTypes return type.flatMemberTypes
if type.isDictionary():
return [type] + getTypesFromDictionary(type)
return [type] return [type]
def isImportable(type): def isImportable(type):
if not type.isType(): if not type.isType():
assert type.isInterface() assert type.isInterface() or type.isDictionary() or type.isEnum()
return not type.isCallback() return True
return type.isNonCallbackInterface() and not type.builtin return not (type.builtin or type.isSequence() or type.isUnion())
def relatedTypesForSignatures(method): def relatedTypesForSignatures(method):
types = [] types = []
@ -1774,14 +1820,31 @@ class CGImports(CGWrapper):
def getIdentifier(t): def getIdentifier(t):
if t.isType(): if t.isType():
return t.inner.identifier if t.nullable():
assert t.isInterface() t = t.inner
if t.isCallback():
return t.callback.identifier
return t.identifier return t.identifier
assert t.isInterface() or t.isDictionary() or t.isEnum()
return t.identifier
def removeWrapperAndNullableTypes(types):
normalized = []
for t in types:
while (t.isType() and t.nullable()) or isinstance(t, IDLWrapperType):
t = t.inner
if isImportable(t):
normalized += [t]
return normalized
types = [] types = []
for d in descriptors: for d in descriptors:
if not d.interface.isCallback():
types += [d.interface] types += [d.interface]
if d.interface.isIteratorInterface():
types += [d.interface.iterableInterface]
members = d.interface.members + d.interface.namedConstructors members = d.interface.members + d.interface.namedConstructors
constructor = d.interface.ctor() constructor = d.interface.ctor()
if constructor: if constructor:
@ -1796,19 +1859,39 @@ class CGImports(CGWrapper):
elif m.isAttr(): elif m.isAttr():
types += componentTypes(m.type) types += componentTypes(m.type)
# Import the type names used in the callbacks that are being defined.
for c in callbacks: for c in callbacks:
types += relatedTypesForSignatures(c) types += relatedTypesForSignatures(c)
# Import the type names used in the dictionaries that are being defined.
for d in dictionaries:
types += componentTypes(d)
# Normalize the types we've collected and remove any ones which can't be imported.
types = removeWrapperAndNullableTypes(types)
descriptorProvider = config.getDescriptorProvider() descriptorProvider = config.getDescriptorProvider()
extras = []
for t in types: for t in types:
if isImportable(t): # Importing these types in the same module that defines them is an error.
if t in dictionaries or t in enums:
continue
if t.isInterface():
descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name)
imports += ['%s' % descriptor.path] extras += [descriptor.path]
if descriptor.interface.parent:
parentName = getIdentifier(descriptor.interface.parent).name
descriptor = descriptorProvider.getDescriptor(parentName)
extras += [descriptor.path, descriptor.bindingPath]
else:
if t.isEnum():
extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name + 'Values']
extras += [getModuleFromObject(t) + '::' + getIdentifier(t).name]
statements = [] statements = []
if len(ignored_warnings) > 0: if len(ignored_warnings) > 0:
statements.append('#![allow(%s)]' % ','.join(ignored_warnings)) statements.append('#![allow(%s)]' % ','.join(ignored_warnings))
statements.extend('use %s;' % i for i in sorted(set(imports))) statements.extend('use %s;' % i for i in sorted(set(imports + extras)))
CGWrapper.__init__(self, child, CGWrapper.__init__(self, child,
pre='\n'.join(statements) + '\n\n') pre='\n'.join(statements) + '\n\n')
@ -1868,7 +1951,7 @@ def DOMClass(descriptor):
# padding. # padding.
protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList))) protoList.extend(['PrototypeList::ID::Last'] * (descriptor.config.maxProtoChainLength - len(protoList)))
prototypeChainString = ', '.join(protoList) prototypeChainString = ', '.join(protoList)
heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.interface.identifier.name heapSizeOf = 'heap_size_of_raw_self_and_children::<%s>' % descriptor.concreteType
if descriptor.isGlobal(): if descriptor.isGlobal():
globals_ = camel_to_upper_snake(descriptor.name) globals_ = camel_to_upper_snake(descriptor.name)
else: else:
@ -2111,7 +2194,14 @@ def UnionTypes(descriptors, dictionaries, callbacks, config):
# Sort unionStructs by key, retrieve value # Sort unionStructs by key, retrieve value
unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0))) unionStructs = (i[1] for i in sorted(unionStructs.items(), key=operator.itemgetter(0)))
return CGImports(CGList(unionStructs, "\n\n"), [], [], imports, config, ignored_warnings=[]) return CGImports(CGList(unionStructs, "\n\n"),
descriptors=[],
callbacks=[],
dictionaries=[],
enums=[],
imports=imports,
config=config,
ignored_warnings=[])
class Argument(): class Argument():
@ -2442,7 +2532,7 @@ class CGIDLInterface(CGThing):
def define(self): def define(self):
interface = self.descriptor.interface interface = self.descriptor.interface
name = self.descriptor.name name = self.descriptor.concreteType
if (interface.getUserData("hasConcreteDescendant", False) or if (interface.getUserData("hasConcreteDescendant", False) or
interface.getUserData("hasProxyDescendant", False)): interface.getUserData("hasProxyDescendant", False)):
depth = self.descriptor.prototypeDepth depth = self.descriptor.prototypeDepth
@ -2549,6 +2639,8 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
if len(self.descriptor.prototypeChain) == 1: if len(self.descriptor.prototypeChain) == 1:
if self.descriptor.interface.getExtendedAttribute("ExceptionClass"): if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))" getPrototypeProto = "prototype_proto.set(JS_GetErrorPrototype(cx))"
elif self.descriptor.interface.isIteratorInterface():
getPrototypeProto = "prototype_proto.set(JS_GetIteratorPrototype(cx))"
else: else:
getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))" getPrototypeProto = "prototype_proto.set(JS_GetObjectPrototype(cx, global))"
else: else:
@ -2628,6 +2720,55 @@ assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
interface.get()); interface.get());
""" % properties)) """ % properties))
aliasedMembers = [m for m in self.descriptor.interface.members if m.isMethod() and m.aliases]
if aliasedMembers:
def defineAlias(alias):
if alias == "@@iterator":
symbolJSID = "RUST_SYMBOL_TO_JSID(GetWellKnownSymbol(cx, SymbolCode::iterator))"
getSymbolJSID = CGGeneric(fill("rooted!(in(cx) let iteratorId = ${symbolJSID});",
symbolJSID=symbolJSID))
defineFn = "JS_DefinePropertyById2"
prop = "iteratorId.handle()"
elif alias.startswith("@@"):
raise TypeError("Can't handle any well-known Symbol other than @@iterator")
else:
getSymbolJSID = None
defineFn = "JS_DefineProperty"
prop = '"%s"' % alias
return CGList([
getSymbolJSID,
# XXX If we ever create non-enumerable properties that can
# be aliased, we should consider making the aliases
# match the enumerability of the property being aliased.
CGGeneric(fill(
"""
assert!(${defineFn}(cx, prototype.handle(), ${prop}, aliasedVal.handle(),
JSPROP_ENUMERATE, None, None));
""",
defineFn=defineFn,
prop=prop))
], "\n")
def defineAliasesFor(m):
return CGList([
CGGeneric(fill(
"""
assert!(JS_GetProperty(cx, prototype.handle(),
b\"${prop}\0\" as *const u8 as *const _,
aliasedVal.handle_mut()));
""",
prop=m.identifier.name))
] + [defineAlias(alias) for alias in sorted(m.aliases)])
defineAliases = CGList([
CGGeneric(fill("""
// Set up aliases on the interface prototype object we just created.
""")),
CGGeneric("rooted!(in(cx) let mut aliasedVal = UndefinedValue());\n\n")
] + [defineAliasesFor(m) for m in sorted(aliasedMembers)])
code.append(defineAliases)
constructors = self.descriptor.interface.namedConstructors constructors = self.descriptor.interface.namedConstructors
if constructors: if constructors:
decl = "let named_constructors: [(NonNullJSNative, &'static [u8], u32); %d]" % len(constructors) decl = "let named_constructors: [(NonNullJSNative, &'static [u8], u32); %d]" % len(constructors)
@ -2709,7 +2850,7 @@ class CGGetProtoObjectMethod(CGGetPerInterfaceObject):
""" """
def __init__(self, descriptor): def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject", CGGetPerInterfaceObject.__init__(self, descriptor, "GetProtoObject",
"PrototypeList::ID", pub=descriptor.hasDescendants()) "PrototypeList::ID", pub=True)
def definition_body(self): def definition_body(self):
return CGList([ return CGList([
@ -2727,7 +2868,7 @@ class CGGetConstructorObjectMethod(CGGetPerInterfaceObject):
def __init__(self, descriptor): def __init__(self, descriptor):
CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject", CGGetPerInterfaceObject.__init__(self, descriptor, "GetConstructorObject",
"PrototypeList::Constructor", "PrototypeList::Constructor",
pub=descriptor.hasDescendants()) pub=True)
def definition_body(self): def definition_body(self):
return CGList([ return CGList([
@ -2966,11 +3107,21 @@ class CGPerSignatureCall(CGThing):
if self.isFallible(): if self.isFallible():
errorResult = " false" errorResult = " false"
if idlNode.isMethod() and idlNode.isMaplikeOrSetlikeOrIterableMethod():
if idlNode.maplikeOrSetlikeOrIterable.isMaplike() or \
idlNode.maplikeOrSetlikeOrIterable.isSetlike():
raise TypeError('Maplike/Setlike methods are not supported yet')
else:
cgThings.append(CGIterableMethodGenerator(descriptor,
idlNode.maplikeOrSetlikeOrIterable,
idlNode.identifier.name))
else:
cgThings.append(CGCallGenerator( cgThings.append(CGCallGenerator(
errorResult, errorResult,
self.getArguments(), self.argsPre, returnType, self.getArguments(), self.argsPre, returnType,
self.extendedAttributes, descriptor, nativeMethodName, self.extendedAttributes, descriptor, nativeMethodName,
static)) static))
self.cgRoot = CGList(cgThings, "\n") self.cgRoot = CGList(cgThings, "\n")
def getArgs(self): def getArgs(self):
@ -5008,6 +5159,7 @@ class CGInterfaceTrait(CGThing):
def members(): def members():
for m in descriptor.interface.members: for m in descriptor.interface.members:
if (m.isMethod() and not m.isStatic() and if (m.isMethod() and not m.isStatic() and
not m.isMaplikeOrSetlikeOrIterableMethod() and
(not m.isIdentifierLess() or m.isStringifier())): (not m.isIdentifierLess() or m.isStringifier())):
name = CGSpecializedMethod.makeNativeName(descriptor, m) name = CGSpecializedMethod.makeNativeName(descriptor, m)
infallible = 'infallible' in descriptor.getExtendedAttributes(m) infallible = 'infallible' in descriptor.getExtendedAttributes(m)
@ -5068,6 +5220,7 @@ class CGInterfaceTrait(CGThing):
post="}") post="}")
else: else:
self.cgRoot = CGGeneric("") self.cgRoot = CGGeneric("")
self.empty = not methods
def define(self): def define(self):
return self.cgRoot.define() return self.cgRoot.define()
@ -5083,18 +5236,136 @@ class CGWeakReferenceableTrait(CGThing):
return self.code return self.code
def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries=None, enums=None):
if not callbacks:
callbacks = []
if not dictionaries:
dictionaries = []
if not enums:
enums = []
return CGImports(cgthings, descriptors, callbacks, dictionaries, enums, [
'js',
'js::{JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT}',
'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}',
'js::error::throw_type_error',
'js::jsapi::{JSJitInfo_AliasSet, JSJitInfo_ArgType, AutoIdVector, CallArgs, FreeOp}',
'js::jsapi::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_SHARED}',
'js::jsapi::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}',
'js::jsapi::{GetPropertyKeys, Handle, Call, GetWellKnownSymbol}',
'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}',
'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}',
'js::jsapi::{JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}',
'js::jsapi::{JS_GetClass, JS_GetErrorPrototype, JS_GetFunctionPrototype}',
'js::jsapi::{JS_GetGlobalForObject, JS_GetObjectPrototype, JS_GetProperty}',
'js::jsapi::{JS_GetPropertyById, JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_InitializePropertiesFromCompatibleNativeObject}',
'js::jsapi::{JS_AtomizeAndPinString, JS_NewObject, JS_NewObjectWithGivenProto}',
'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty, JS_DefinePropertyById2}',
'js::jsapi::{JS_SplicePrototype, JS_SetReservedSlot, JSAutoCompartment}',
'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec, JS_GetIteratorPrototype}',
'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}',
'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}',
'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}',
'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}',
'js::jsapi::{SymbolCode, jsid}',
'js::jsval::JSVal',
'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
'js::jsval::{NullValue, UndefinedValue}',
'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}',
'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
'js::glue::{RUST_JSID_IS_STRING, int_to_jsid, RUST_SYMBOL_TO_JSID}',
'js::glue::AppendToAutoIdVector',
'js::rust::{GCMethods, define_methods, define_properties}',
'dom',
'dom::bindings',
'dom::bindings::codegen::InterfaceObjectMap',
'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}',
'dom::bindings::interface::{InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass}',
'dom::bindings::interface::{create_callback_interface_object, create_interface_prototype_object}',
'dom::bindings::interface::{create_named_constructors, create_noncallback_interface_object}',
'dom::bindings::interface::{define_guarded_methods, define_guarded_properties}',
'dom::bindings::interface::{ConstantSpec, NonNullJSNative}',
'dom::bindings::interface::ConstantVal::{IntVal, UintVal}',
'dom::bindings::interface::is_exposed_in',
'dom::bindings::iterable::{IteratorType, Iterable}',
'dom::bindings::js::{JS, Root, RootedReference}',
'dom::bindings::js::{OptionalRootedReference}',
'dom::bindings::reflector::{Reflectable}',
'dom::bindings::utils::{DOMClass, DOMJSClass}',
'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}',
'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}',
'dom::bindings::utils::{generic_getter, generic_lenient_getter, generic_lenient_setter}',
'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}',
'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}',
'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}',
'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property, trace_global}',
'dom::bindings::trace::{JSTraceable, RootedTraceable}',
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
'dom::bindings::callback::{CallSetup,ExceptionHandling}',
'dom::bindings::callback::wrap_call_this_object',
'dom::bindings::conversions::{ConversionBehavior, ConversionResult, DOM_OBJECT_SLOT}',
'dom::bindings::conversions::{IDLInterface, is_array_like}',
'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}',
'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
'dom::bindings::error::{Fallible, Error, ErrorResult}',
'dom::bindings::error::Error::JSFailed',
'dom::bindings::error::throw_dom_exception',
'dom::bindings::guard::{Condition, Guard}',
'dom::bindings::proxyhandler',
'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
'dom::bindings::num::Finite',
'dom::bindings::str::{ByteString, DOMString, USVString}',
'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
'dom::browsingcontext::BrowsingContext',
'mem::heap_size_of_raw_self_and_children',
'libc',
'util::prefs::PREFS',
'script_runtime::{store_panic_result, maybe_take_panic_result}',
'std::borrow::ToOwned',
'std::cmp',
'std::mem',
'std::num',
'std::os',
'std::panic::{self, AssertUnwindSafe}',
'std::ptr',
'std::str',
'std::rc',
'std::rc::Rc',
'std::default::Default',
'std::ffi::CString',
], config)
class CGDescriptor(CGThing): class CGDescriptor(CGThing):
def __init__(self, descriptor): def __init__(self, descriptor, config, soleDescriptor):
CGThing.__init__(self) CGThing.__init__(self)
assert not descriptor.concrete or not descriptor.interface.isCallback() assert not descriptor.concrete or not descriptor.interface.isCallback()
reexports = []
def reexportedName(name):
if name.startswith(descriptor.name):
return name
if not soleDescriptor:
return '%s as %s%s' % (name, descriptor.name, name)
return name
cgThings = [] cgThings = []
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback():
cgThings.append(CGGetProtoObjectMethod(descriptor)) cgThings.append(CGGetProtoObjectMethod(descriptor))
reexports.append('GetProtoObject')
if (descriptor.interface.hasInterfaceObject() and if (descriptor.interface.hasInterfaceObject() and
descriptor.shouldHaveGetConstructorObjectMethod()): descriptor.shouldHaveGetConstructorObjectMethod()):
cgThings.append(CGGetConstructorObjectMethod(descriptor)) cgThings.append(CGGetConstructorObjectMethod(descriptor))
reexports.append('GetConstructorObject')
unscopableNames = [] unscopableNames = []
for m in descriptor.interface.members: for m in descriptor.interface.members:
@ -5156,9 +5427,11 @@ class CGDescriptor(CGThing):
cgThings.append(CGNamespace.build([descriptor.name + "Constants"], cgThings.append(CGNamespace.build([descriptor.name + "Constants"],
CGConstant(constMembers), CGConstant(constMembers),
public=True)) public=True))
reexports.append(descriptor.name + 'Constants')
if descriptor.interface.hasInterfaceObject(): if descriptor.interface.hasInterfaceObject() and descriptor.register:
cgThings.append(CGDefineDOMInterfaceMethod(descriptor)) cgThings.append(CGDefineDOMInterfaceMethod(descriptor))
reexports.append('DefineDOMInterface')
cgThings.append(CGConstructorEnabled(descriptor)) cgThings.append(CGConstructorEnabled(descriptor))
if descriptor.proxy: if descriptor.proxy:
@ -5194,6 +5467,7 @@ class CGDescriptor(CGThing):
pass pass
cgThings.append(CGWrapMethod(descriptor)) cgThings.append(CGWrapMethod(descriptor))
reexports.append('Wrap')
haveUnscopables = False haveUnscopables = False
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback():
@ -5206,7 +5480,12 @@ class CGDescriptor(CGThing):
CGGeneric("];\n")], "\n")) CGGeneric("];\n")], "\n"))
if descriptor.concrete or descriptor.hasDescendants(): if descriptor.concrete or descriptor.hasDescendants():
cgThings.append(CGIDLInterface(descriptor)) cgThings.append(CGIDLInterface(descriptor))
cgThings.append(CGInterfaceTrait(descriptor))
interfaceTrait = CGInterfaceTrait(descriptor)
cgThings.append(interfaceTrait)
if not interfaceTrait.empty:
reexports.append('%sMethods' % descriptor.name)
if descriptor.weakReferenceable: if descriptor.weakReferenceable:
cgThings.append(CGWeakReferenceableTrait(descriptor)) cgThings.append(CGWeakReferenceableTrait(descriptor))
@ -5214,11 +5493,13 @@ class CGDescriptor(CGThing):
cgThings.append(CGGeneric(str(properties))) cgThings.append(CGGeneric(str(properties)))
cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables)) cgThings.append(CGCreateInterfaceObjectsMethod(descriptor, properties, haveUnscopables))
cgThings = CGList(cgThings, "\n") cgThings = generate_imports(config, CGList(cgThings, '\n'), [descriptor])
# self.cgRoot = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name), cgThings = CGWrapper(CGNamespace(toBindingNamespace(descriptor.name),
# cgThings), cgThings, public=True),
# post='\n') post='\n')
self.cgRoot = cgThings reexports = ', '.join(map(lambda name: reexportedName(name), reexports))
self.cgRoot = CGList([CGGeneric('pub use self::%sBinding::{%s};' % (descriptor.name, reexports)),
cgThings], '\n')
def define(self): def define(self):
return self.cgRoot.define() return self.cgRoot.define()
@ -5468,8 +5749,8 @@ class CGRegisterProxyHandlersMethod(CGAbstractMethod):
def definition_body(self): def definition_body(self):
return CGList([ return CGList([
CGGeneric("proxy_handlers[Proxies::%s as usize] = codegen::Bindings::%sBinding::DefineProxyHandler();" CGGeneric("proxy_handlers[Proxies::%s as usize] = Bindings::%s::DefineProxyHandler();"
% (desc.name, desc.name)) % (desc.name, '::'.join([desc.name + 'Binding'] * 2)))
for desc in self.descriptors for desc in self.descriptors
], "\n") ], "\n")
@ -5542,7 +5823,7 @@ class CGBindingRoot(CGThing):
for c in mainCallbacks) for c in mainCallbacks)
# Do codegen for all the descriptors # Do codegen for all the descriptors
cgthings.extend([CGDescriptor(x) for x in descriptors]) cgthings.extend([CGDescriptor(x, config, len(descriptors) == 1) for x in descriptors])
# Do codegen for all the callback interfaces. # Do codegen for all the callback interfaces.
cgthings.extend(CGList([CGCallbackInterface(x), cgthings.extend(CGList([CGCallbackInterface(x),
@ -5553,102 +5834,8 @@ class CGBindingRoot(CGThing):
curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n") curr = CGWrapper(CGList(cgthings, "\n\n"), post="\n\n")
# Add imports # Add imports
curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [ curr = generate_imports(config, curr, callbackDescriptors, mainCallbacks,
'js', dictionaries, enums)
'js::{JS_CALLEE, JSCLASS_GLOBAL_SLOT_COUNT}',
'js::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_MASK}',
'js::error::throw_type_error',
'js::jsapi::{JSJitInfo_AliasSet, JSJitInfo_ArgType, AutoIdVector, CallArgs, FreeOp}',
'js::jsapi::{JSITER_SYMBOLS, JSPROP_ENUMERATE, JSPROP_PERMANENT, JSPROP_READONLY, JSPROP_SHARED}',
'js::jsapi::{JSCLASS_RESERVED_SLOTS_SHIFT, JSITER_HIDDEN, JSITER_OWNONLY}',
'js::jsapi::{GetPropertyKeys, Handle}',
'js::jsapi::{HandleId, HandleObject, HandleValue, HandleValueArray}',
'js::jsapi::{INTERNED_STRING_TO_JSID, IsCallable, JS_CallFunctionValue}',
'js::jsapi::{JS_CopyPropertiesFrom, JS_ForwardGetPropertyTo}',
'js::jsapi::{JS_GetClass, JS_GetErrorPrototype, JS_GetFunctionPrototype}',
'js::jsapi::{JS_GetGlobalForObject, JS_GetObjectPrototype, JS_GetProperty}',
'js::jsapi::{JS_GetPropertyById, JS_GetPropertyDescriptorById, JS_GetReservedSlot}',
'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_InitializePropertiesFromCompatibleNativeObject}',
'js::jsapi::{JS_AtomizeAndPinString, JS_NewObject, JS_NewObjectWithGivenProto}',
'js::jsapi::{JS_NewObjectWithoutMetadata, JS_SetProperty}',
'js::jsapi::{JS_SplicePrototype, JS_SetReservedSlot, JSAutoCompartment}',
'js::jsapi::{JSContext, JSClass, JSFreeOp, JSFunctionSpec}',
'js::jsapi::{JSJitGetterCallArgs, JSJitInfo, JSJitMethodCallArgs, JSJitSetterCallArgs}',
'js::jsapi::{JSNative, JSObject, JSNativeWrapper, JSPropertySpec}',
'js::jsapi::{JSString, JSTracer, JSType, JSTypedMethodJitInfo, JSValueType}',
'js::jsapi::{ObjectOpResult, JSJitInfo_OpType, MutableHandle, MutableHandleObject}',
'js::jsapi::{MutableHandleValue, PropertyDescriptor, RootedObject}',
'js::jsapi::{SymbolCode, jsid}',
'js::jsval::JSVal',
'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}',
'js::jsval::{NullValue, UndefinedValue}',
'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, CreateProxyHandler}',
'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}',
'js::glue::{RUST_JSID_IS_STRING, int_to_jsid}',
'js::glue::AppendToAutoIdVector',
'js::rust::{GCMethods, define_methods, define_properties}',
'dom::bindings',
'dom::bindings::codegen::InterfaceObjectMap',
'dom::bindings::global::{GlobalRef, global_root_from_object, global_root_from_reflector}',
'dom::bindings::interface::{InterfaceConstructorBehavior, NonCallbackInterfaceObjectClass}',
'dom::bindings::interface::{create_callback_interface_object, create_interface_prototype_object}',
'dom::bindings::interface::{create_named_constructors, create_noncallback_interface_object}',
'dom::bindings::interface::{define_guarded_methods, define_guarded_properties}',
'dom::bindings::interface::{ConstantSpec, NonNullJSNative}',
'dom::bindings::interface::ConstantVal::{IntVal, UintVal}',
'dom::bindings::interface::is_exposed_in',
'dom::bindings::js::{JS, Root, RootedReference}',
'dom::bindings::js::{OptionalRootedReference}',
'dom::bindings::reflector::{Reflectable}',
'dom::bindings::utils::{DOMClass, DOMJSClass}',
'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}',
'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}',
'dom::bindings::utils::{generic_getter, generic_lenient_getter, generic_lenient_setter}',
'dom::bindings::utils::{generic_method, generic_setter, get_array_index_from_id}',
'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}',
'dom::bindings::utils::{get_proto_or_iface_array, has_property_on_prototype}',
'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property, trace_global}',
'dom::bindings::trace::{JSTraceable, RootedTraceable}',
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
'dom::bindings::callback::{CallSetup,ExceptionHandling}',
'dom::bindings::callback::wrap_call_this_object',
'dom::bindings::conversions::{ConversionBehavior, ConversionResult, DOM_OBJECT_SLOT}',
'dom::bindings::conversions::{IDLInterface, is_array_like}',
'dom::bindings::conversions::{FromJSValConvertible, StringificationBehavior}',
'dom::bindings::conversions::{ToJSValConvertible, jsid_to_str, native_from_handlevalue}',
'dom::bindings::conversions::{native_from_object, private_from_object, root_from_object}',
'dom::bindings::conversions::{root_from_handleobject, root_from_handlevalue}',
'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}',
'dom::bindings::codegen::Bindings::*',
'dom::bindings::error::{Fallible, Error, ErrorResult}',
'dom::bindings::error::Error::JSFailed',
'dom::bindings::error::throw_dom_exception',
'dom::bindings::guard::{Condition, Guard}',
'dom::bindings::proxyhandler',
'dom::bindings::proxyhandler::{ensure_expando_object, fill_property_descriptor}',
'dom::bindings::proxyhandler::{get_expando_object, get_property_descriptor}',
'dom::bindings::num::Finite',
'dom::bindings::str::{ByteString, DOMString, USVString}',
'dom::bindings::weakref::{DOM_WEAK_SLOT, WeakBox, WeakReferenceable}',
'dom::browsingcontext::BrowsingContext',
'mem::heap_size_of_raw_self_and_children',
'libc',
'util::prefs::PREFS',
'script_runtime::{store_panic_result, maybe_take_panic_result}',
'std::borrow::ToOwned',
'std::cmp',
'std::mem',
'std::num',
'std::os',
'std::panic::{self, AssertUnwindSafe}',
'std::ptr',
'std::str',
'std::rc',
'std::rc::Rc',
'std::default::Default',
'std::ffi::CString',
], config)
# Add the auto-generated comment. # Add the auto-generated comment.
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
@ -6250,6 +6437,53 @@ class CallbackSetter(CallbackMember):
return None return None
class CGIterableMethodGenerator(CGGeneric):
"""
Creates methods for iterable interfaces. Unwrapping/wrapping
will be taken care of by the usual method generation machinery in
CGMethodCall/CGPerSignatureCall. Functionality is filled in here instead of
using CGCallGenerator.
"""
def __init__(self, descriptor, iterable, methodName):
if methodName == "forEach":
CGGeneric.__init__(self, fill(
"""
if !IsCallable(arg0) {
throw_type_error(cx, "Argument 1 of ${ifaceName}.forEach is not callable.");
return false;
}
rooted!(in(cx) let arg0 = ObjectValue(&*arg0));
rooted!(in(cx) let mut call_arg1 = UndefinedValue());
rooted!(in(cx) let mut call_arg2 = UndefinedValue());
let mut call_args = vec![UndefinedValue(), UndefinedValue(), ObjectValue(&**_obj)];
rooted!(in(cx) let mut ignoredReturnVal = UndefinedValue());
for i in 0..(*this).get_iterable_length() {
(*this).get_value_at_index(i).to_jsval(cx, call_arg1.handle_mut());
(*this).get_key_at_index(i).to_jsval(cx, call_arg2.handle_mut());
call_args[0] = call_arg1.handle().get();
call_args[1] = call_arg2.handle().get();
let call_args = HandleValueArray { length_: 3, elements_: call_args.as_ptr() };
if !Call(cx, arg1, arg0.handle(), &call_args,
ignoredReturnVal.handle_mut()) {
return false;
}
}
let result = ();
""",
ifaceName=descriptor.interface.identifier.name))
return
CGGeneric.__init__(self, fill(
"""
let result = ${iterClass}::new(&*this,
IteratorType::${itrMethod},
super::${ifaceName}IteratorBinding::Wrap);
""",
iterClass=iteratorNativeType(descriptor, True),
ifaceName=descriptor.interface.identifier.name,
itrMethod=methodName.title()))
def camel_to_upper_snake(s): def camel_to_upper_snake(s):
return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s)) return "_".join(m.group(0).upper() for m in re.finditer("[A-Z][a-z]*", s))
@ -6286,12 +6520,12 @@ class GlobalGenRoots():
pairs = [] pairs = []
for d in config.getDescriptors(hasInterfaceObject=True): for d in config.getDescriptors(hasInterfaceObject=True):
binding = toBindingNamespace(d.name) binding = toBindingNamespace(d.name)
pairs.append((d.name, binding)) pairs.append((d.name, binding, binding))
for ctor in d.interface.namedConstructors: for ctor in d.interface.namedConstructors:
pairs.append((ctor.identifier.name, binding)) pairs.append((ctor.identifier.name, binding, binding))
pairs.sort(key=operator.itemgetter(0)) pairs.sort(key=operator.itemgetter(0))
mappings = [ mappings = [
CGGeneric('b"%s" => codegen::Bindings::%s::DefineDOMInterface as unsafe fn(_, _),' % pair) CGGeneric('b"%s" => codegen::Bindings::%s::%s::DefineDOMInterface as unsafe fn(_, _),' % pair)
for pair in pairs for pair in pairs
] ]
mapType = "phf::Map<&'static [u8], unsafe fn(*mut JSContext, HandleObject)>" mapType = "phf::Map<&'static [u8], unsafe fn(*mut JSContext, HandleObject)>"
@ -6340,15 +6574,17 @@ class GlobalGenRoots():
CGRegisterProxyHandlers(config), CGRegisterProxyHandlers(config),
], "\n") ], "\n")
return CGImports(code, [], [], [ return CGImports(code, descriptors=[], callbacks=[], dictionaries=[], enums=[], imports=[
'dom::bindings::codegen', 'dom::bindings::codegen::Bindings',
'dom::bindings::codegen::PrototypeList::Proxies', 'dom::bindings::codegen::PrototypeList::Proxies',
'libc', 'libc',
], config, ignored_warnings=[]) ], config=config, ignored_warnings=[])
@staticmethod @staticmethod
def InterfaceTypes(config): def InterfaceTypes(config):
descriptors = [d.name for d in config.getDescriptors(register=True, isCallback=False)] descriptors = [d.name for d in config.getDescriptors(register=True,
isCallback=False,
isIteratorInterface=False)]
curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors]) curr = CGList([CGGeneric("pub use dom::%s::%s;\n" % (name.lower(), name)) for name in descriptors])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr return curr
@ -6356,9 +6592,13 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def Bindings(config): def Bindings(config):
descriptors = (set(d.name + "Binding" for d in config.getDescriptors(register=True)) | def leafModule(d):
set(getModuleFromObject(d) for d in config.callbacks) | return getModuleFromObject(d).split('::')[-1]
set(getModuleFromObject(d) for d in config.getDictionaries()))
descriptors = config.getDescriptors(register=True, isIteratorInterface=False)
descriptors = (set(d.name + "Binding" for d in descriptors) |
set(leafModule(d) for d in config.callbacks) |
set(leafModule(d) for d in config.getDictionaries()))
curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)]) curr = CGList([CGGeneric("pub mod %s;\n" % name) for name in sorted(descriptors)])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr return curr

View file

@ -4,7 +4,7 @@
import os import os
from WebIDL import IDLExternalInterface, IDLInterface, WebIDLError from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError
class Configuration: class Configuration:
@ -89,6 +89,8 @@ class Configuration:
getter = lambda x: x.isGlobal() getter = lambda x: x.isGlobal()
elif key == 'isExposedConditionally': elif key == 'isExposedConditionally':
getter = lambda x: x.interface.isExposedConditionally() getter = lambda x: x.interface.isExposedConditionally()
elif key == 'isIteratorInterface':
getter = lambda x: x.interface.isIteratorInterface()
else: else:
getter = lambda x: getattr(x, key) getter = lambda x: getattr(x, key)
curr = filter(lambda x: getter(x) == val, curr) curr = filter(lambda x: getter(x) == val, curr)
@ -177,13 +179,26 @@ class Descriptor(DescriptorProvider):
# Read the desc, and fill in the relevant defaults. # Read the desc, and fill in the relevant defaults.
ifaceName = self.interface.identifier.name ifaceName = self.interface.identifier.name
typeName = desc.get('nativeType', ifaceName) nativeTypeDefault = ifaceName
# For generated iterator interfaces for other iterable interfaces, we
# just use IterableIterator as the native type, templated on the
# nativeType of the iterable interface. That way we can have a
# templated implementation for all the duplicated iterator
# functionality.
if self.interface.isIteratorInterface():
itrName = self.interface.iterableInterface.identifier.name
itrDesc = self.getDescriptor(itrName)
nativeTypeDefault = iteratorNativeType(itrDesc)
typeName = desc.get('nativeType', nativeTypeDefault)
# Callback types do not use JS smart pointers, so we should not use the # Callback types do not use JS smart pointers, so we should not use the
# built-in rooting mechanisms for them. # built-in rooting mechanisms for them.
if self.interface.isCallback(): if self.interface.isCallback():
self.needsRooting = False self.needsRooting = False
ty = "%sBinding::%s" % (ifaceName, ifaceName) ty = 'dom::bindings::codegen::Bindings::%sBinding::%s' % (ifaceName, ifaceName)
pathDefault = ty
self.returnType = "Rc<%s>" % ty self.returnType = "Rc<%s>" % ty
self.argumentType = "???" self.argumentType = "???"
self.nativeType = ty self.nativeType = ty
@ -192,10 +207,15 @@ class Descriptor(DescriptorProvider):
self.returnType = "Root<%s>" % typeName self.returnType = "Root<%s>" % typeName
self.argumentType = "&%s" % typeName self.argumentType = "&%s" % typeName
self.nativeType = "*const %s" % typeName self.nativeType = "*const %s" % typeName
if self.interface.isIteratorInterface():
pathDefault = 'dom::bindings::iterable::IterableIterator'
else:
pathDefault = 'dom::types::%s' % typeName
self.concreteType = typeName self.concreteType = typeName
self.register = desc.get('register', True) self.register = desc.get('register', True)
self.path = desc.get('path', 'dom::types::%s' % typeName) self.path = desc.get('path', pathDefault)
self.bindingPath = 'dom::bindings::codegen::Bindings::%s' % ('::'.join([ifaceName + 'Binding'] * 2))
self.outerObjectHook = desc.get('outerObjectHook', 'None') self.outerObjectHook = desc.get('outerObjectHook', 'None')
self.proxy = False self.proxy = False
self.weakReferenceable = desc.get('weakReferenceable', False) self.weakReferenceable = desc.get('weakReferenceable', False)
@ -377,7 +397,8 @@ class Descriptor(DescriptorProvider):
# Some utility methods # Some utility methods
def getModuleFromObject(object): def getModuleFromObject(object):
return os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding' return ('dom::bindings::codegen::Bindings::' +
os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')
def getTypesFromDescriptor(descriptor): def getTypesFromDescriptor(descriptor):
@ -404,6 +425,8 @@ def getTypesFromDictionary(dictionary):
""" """
Get all member types for this dictionary Get all member types for this dictionary
""" """
if isinstance(dictionary, IDLWrapperType):
dictionary = dictionary.inner
types = [] types = []
curDict = dictionary curDict = dictionary
while curDict: while curDict:
@ -421,3 +444,10 @@ def getTypesFromCallback(callback):
types = [sig[0]] # Return type types = [sig[0]] # Return type
types.extend(arg.type for arg in sig[1]) # Arguments types.extend(arg.type for arg in sig[1]) # Arguments
return types return types
def iteratorNativeType(descriptor, infer=False):
assert descriptor.interface.isIterable()
iterableDecl = descriptor.interface.maplikeOrSetlikeOrIterable
assert iterableDecl.isPairIterator()
return "IterableIterator%s" % ("" if infer else '<%s>' % descriptor.interface.identifier.name)

View file

@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType):
return typedefType.complete(scope) return typedefType.complete(scope)
elif obj.isCallback() and not obj.isInterface(): elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name assert self.name.name == obj.identifier.name
return IDLCallbackType(self.location, obj) return IDLCallbackType(obj.location, obj)
if self._promiseInnerType and not self._promiseInnerType.isComplete(): if self._promiseInnerType and not self._promiseInnerType.isComplete():
self._promiseInnerType = self._promiseInnerType.complete(scope) self._promiseInnerType = self._promiseInnerType.complete(scope)
@ -6534,7 +6534,7 @@ class Parser(Tokenizer):
type = IDLTypedefType(self.getLocation(p, 1), obj.innerType, type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
obj.identifier.name) obj.identifier.name)
elif obj.isCallback() and not obj.isInterface(): elif obj.isCallback() and not obj.isInterface():
type = IDLCallbackType(self.getLocation(p, 1), obj) type = IDLCallbackType(obj.location, obj)
else: else:
type = IDLWrapperType(self.getLocation(p, 1), p[1]) type = IDLWrapperType(self.getLocation(p, 1), p[1])
p[0] = self.handleModifiers(type, p[2]) p[0] = self.handleModifiers(type, p[2])

View file

@ -0,0 +1,22 @@
diff --git a/components/script/dom/bindings/codegen/parser/WebIDL.py b/components/script/dom/bindings/codegen/parser/WebIDL.py
index da32340..81c52b7 100644
--- a/components/script/dom/bindings/codegen/parser/WebIDL.py
+++ b/components/script/dom/bindings/codegen/parser/WebIDL.py
@@ -2170,7 +2170,7 @@ class IDLUnresolvedType(IDLType):
return typedefType.complete(scope)
elif obj.isCallback() and not obj.isInterface():
assert self.name.name == obj.identifier.name
- return IDLCallbackType(self.location, obj)
+ return IDLCallbackType(obj.location, obj)
if self._promiseInnerType and not self._promiseInnerType.isComplete():
self._promiseInnerType = self._promiseInnerType.complete(scope)
@@ -6521,7 +6521,7 @@ class Parser(Tokenizer):
type = IDLTypedefType(self.getLocation(p, 1), obj.innerType,
obj.identifier.name)
elif obj.isCallback() and not obj.isInterface():
- type = IDLCallbackType(self.getLocation(p, 1), obj)
+ type = IDLCallbackType(obj.location, obj)
else:
type = IDLWrapperType(self.getLocation(p, 1), p[1])
p[0] = self.handleModifiers(type, p[2])

View file

@ -2,6 +2,7 @@ wget https://hg.mozilla.org/mozilla-central/raw-file/tip/dom/bindings/parser/Web
patch < abstract.patch patch < abstract.patch
patch < debug.patch patch < debug.patch
patch < pref-main-thread.patch patch < pref-main-thread.patch
patch < callback-location.patch
wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz wget https://hg.mozilla.org/mozilla-central/archive/tip.tar.gz/dom/bindings/parser/tests/ -O tests.tar.gz
rm -r tests rm -r tests

View file

@ -0,0 +1,161 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#![allow(unsafe_code)]
//! Implementation of `iterable<...>` and `iterable<..., ...>` WebIDL declarations.
use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyAndValueResult;
use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyOrValueResult;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{Reflector, Reflectable, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use js::conversions::ToJSValConvertible;
use js::jsapi::{JSContext, JSObject, MutableHandleValue, MutableHandleObject, HandleValue};
use js::jsval::UndefinedValue;
use std::cell::Cell;
use std::ptr;
/// The values that an iterator will iterate over.
#[derive(JSTraceable, HeapSizeOf)]
pub enum IteratorType {
/// The keys of the iterable object.
Keys,
/// The values of the iterable object.
Values,
/// The keys and values of the iterable object combined.
Entries,
}
/// A DOM object that can be iterated over using a pair value iterator.
pub trait Iterable {
/// The type of the key of the iterator pair.
type Key: ToJSValConvertible;
/// The type of the value of the iterator pair.
type Value: ToJSValConvertible;
/// Return the number of entries that can be iterated over.
fn get_iterable_length(&self) -> u32;
/// Return the value at the provided index.
fn get_value_at_index(&self, index: u32) -> Self::Value;
/// Return the key at the provided index.
fn get_key_at_index(&self, index: u32) -> Self::Key;
}
/// An iterator over the iterable entries of a given DOM interface.
//FIXME: #12811 prevents dom_struct with type parameters
//#[dom_struct]
#[must_root]
#[privatize]
#[derive(JSTraceable)]
#[derive(HeapSizeOf)]
pub struct IterableIterator<T: Reflectable + JSTraceable + Iterable> {
reflector: Reflector,
iterable: JS<T>,
type_: IteratorType,
index: Cell<u32>,
}
impl<T: Reflectable + JSTraceable + Iterable> Reflectable for IterableIterator<T> {
fn reflector<'a>(&'a self) -> &'a Reflector {
&self.reflector
}
fn init_reflector(&mut self, obj: *mut JSObject) {
self.reflector.set_jsobject(obj);
}
}
impl<T: Reflectable + JSTraceable + Iterable> ToJSValConvertible for IterableIterator<T> {
#[allow(unsafe_code)]
unsafe fn to_jsval(&self,
cx: *mut JSContext,
rval: MutableHandleValue) {
let object = Reflectable::reflector(self).get_jsobject();
object.to_jsval(cx, rval)
}
}
impl<T: Reflectable + JSTraceable + Iterable> IterableIterator<T> {
/// Create a new iterator instance for the provided iterable DOM interface.
pub fn new(iterable: &T,
type_: IteratorType,
wrap: fn(*mut JSContext, GlobalRef, Box<IterableIterator<T>>)
-> Root<Self>) -> Root<Self> {
let iterator = box IterableIterator {
reflector: Reflector::new(),
type_: type_,
iterable: JS::from_ref(iterable),
index: Cell::new(0),
};
let global = iterable.global();
reflect_dom_object(iterator, global.r(), wrap)
}
/// Return the next value from the iterable object.
#[allow(non_snake_case)]
pub fn Next(&self, cx: *mut JSContext) -> Fallible<*mut JSObject> {
let index = self.index.get();
rooted!(in(cx) let mut value = UndefinedValue());
rooted!(in(cx) let mut rval = ptr::null_mut());
if index >= self.iterable.get_iterable_length() {
return dict_return(cx, rval.handle_mut(), true, value.handle())
.map(|_| rval.handle().get());
}
let result = match self.type_ {
IteratorType::Keys => {
unsafe {
self.iterable.get_key_at_index(index).to_jsval(cx, value.handle_mut());
}
dict_return(cx, rval.handle_mut(), false, value.handle())
}
IteratorType::Values => {
unsafe {
self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut());
}
dict_return(cx, rval.handle_mut(), false, value.handle())
}
IteratorType::Entries => {
rooted!(in(cx) let mut key = UndefinedValue());
unsafe {
self.iterable.get_key_at_index(index).to_jsval(cx, key.handle_mut());
self.iterable.get_value_at_index(index).to_jsval(cx, value.handle_mut());
}
key_and_value_return(cx, rval.handle_mut(), key.handle(), value.handle())
}
};
self.index.set(index + 1);
result.map(|_| rval.handle().get())
}
}
fn dict_return(cx: *mut JSContext,
result: MutableHandleObject,
done: bool,
value: HandleValue) -> Fallible<()> {
let mut dict = unsafe { IterableKeyOrValueResult::empty(cx) };
dict.done = done;
dict.value = value.get();
rooted!(in(cx) let mut dict_value = UndefinedValue());
unsafe {
dict.to_jsval(cx, dict_value.handle_mut());
}
result.set(dict_value.to_object());
Ok(())
}
fn key_and_value_return(cx: *mut JSContext,
result: MutableHandleObject,
key: HandleValue,
value: HandleValue) -> Fallible<()> {
let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) };
dict.done = false;
dict.value = Some(vec![key.get(), value.get()]);
rooted!(in(cx) let mut dict_value = UndefinedValue());
unsafe {
dict.to_jsval(cx, dict_value.handle_mut());
}
result.set(dict_value.to_object());
Ok(())
}

View file

@ -136,6 +136,7 @@ pub mod global;
pub mod guard; pub mod guard;
pub mod inheritance; pub mod inheritance;
pub mod interface; pub mod interface;
pub mod iterable;
pub mod js; pub mod js;
pub mod num; pub mod num;
pub mod proxyhandler; pub mod proxyhandler;

View file

@ -384,6 +384,8 @@ pub mod storageevent;
pub mod stylesheet; pub mod stylesheet;
pub mod stylesheetlist; pub mod stylesheetlist;
pub mod testbinding; pub mod testbinding;
pub mod testbindingiterable;
pub mod testbindingpairiterable;
pub mod testbindingproxy; pub mod testbindingproxy;
pub mod text; pub mod text;
pub mod textdecoder; pub mod textdecoder;

View file

@ -0,0 +1,43 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// check-tidy: no specs after this line
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::TestBindingIterableBinding::{self, TestBindingIterableMethods};
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
#[dom_struct]
pub struct TestBindingIterable {
reflector: Reflector,
vals: DOMRefCell<Vec<DOMString>>,
}
impl TestBindingIterable {
fn new(global: GlobalRef) -> Root<TestBindingIterable> {
reflect_dom_object(box TestBindingIterable {
reflector: Reflector::new(),
vals: DOMRefCell::new(vec![]),
}, global, TestBindingIterableBinding::Wrap)
}
pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingIterable>> {
Ok(TestBindingIterable::new(global))
}
}
impl TestBindingIterableMethods for TestBindingIterable {
fn Add(&self, v: DOMString) { self.vals.borrow_mut().push(v); }
fn Length(&self) -> u32 { self.vals.borrow().len() as u32 }
fn GetItem(&self, n: u32) -> DOMString { self.vals.borrow().get(n as usize).unwrap().clone() }
fn IndexedGetter(&self, n: u32, found: &mut bool) -> DOMString {
let s = self.GetItem(n);
*found = true;
s
}
}

View file

@ -0,0 +1,54 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// check-tidy: no specs after this line
use dom::bindings::cell::DOMRefCell;
use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding;
use dom::bindings::codegen::Bindings::TestBindingPairIterableBinding::TestBindingPairIterableMethods;
use dom::bindings::error::Fallible;
use dom::bindings::global::GlobalRef;
use dom::bindings::iterable::Iterable;
use dom::bindings::js::Root;
use dom::bindings::reflector::{Reflector, reflect_dom_object};
use dom::bindings::str::DOMString;
#[dom_struct]
pub struct TestBindingPairIterable {
reflector: Reflector,
map: DOMRefCell<Vec<(DOMString, u32)>>,
}
impl Iterable for TestBindingPairIterable {
type Key = DOMString;
type Value = u32;
fn get_iterable_length(&self) -> u32 {
self.map.borrow().len() as u32
}
fn get_value_at_index(&self, index: u32) -> u32 {
self.map.borrow().iter().nth(index as usize).map(|a| &a.1).unwrap().clone()
}
fn get_key_at_index(&self, index: u32) -> DOMString {
self.map.borrow().iter().nth(index as usize).map(|a| &a.0).unwrap().clone()
}
}
impl TestBindingPairIterable {
fn new(global: GlobalRef) -> Root<TestBindingPairIterable> {
reflect_dom_object(box TestBindingPairIterable {
reflector: Reflector::new(),
map: DOMRefCell::new(vec![]),
}, global, TestBindingPairIterableBinding::TestBindingPairIterableWrap)
}
pub fn Constructor(global: GlobalRef) -> Fallible<Root<TestBindingPairIterable>> {
Ok(TestBindingPairIterable::new(global))
}
}
impl TestBindingPairIterableMethods for TestBindingPairIterable {
fn Add(&self, key: DOMString, value: u32) {
self.map.borrow_mut().push((key, value));
}
}

View file

@ -0,0 +1,16 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
dictionary IterableKeyOrValueResult {
any value;
boolean done = false;
};
dictionary IterableKeyAndValueResult {
sequence<any> value;
boolean done = false;
};

View file

@ -0,0 +1,14 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor]
interface TestBindingIterable {
void add(DOMString arg);
readonly attribute unsigned long length;
getter DOMString getItem(unsigned long index);
iterable<DOMString>;
};

View file

@ -0,0 +1,12 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// This interface is entirely internal to Servo, and should not be accessible to
// web pages.
[Pref="dom.testbinding.enabled", Exposed=(Window,Worker), Constructor]
interface TestBindingPairIterable {
void add(DOMString key, unsigned long value);
iterable<DOMString, unsigned long>;
};

View file

@ -6858,6 +6858,12 @@
"url": "/_mozilla/mozilla/interfaces.worker" "url": "/_mozilla/mozilla/interfaces.worker"
} }
], ],
"mozilla/iterable.html": [
{
"path": "mozilla/iterable.html",
"url": "/_mozilla/mozilla/iterable.html"
}
],
"mozilla/lenient_this.html": [ "mozilla/lenient_this.html": [
{ {
"path": "mozilla/lenient_this.html", "path": "mozilla/lenient_this.html",

View file

@ -0,0 +1,3 @@
[iterable.html]
type: testharness
prefs: [dom.testbinding.enabled:true]

View file

@ -0,0 +1,95 @@
<!doctype html>
<meta charset="utf-8">
<title>Value and pair iterable bindings</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
function collect(iter) {
var collection = [];
for (element of iter) {
collection.push(element);
}
return collection;
}
test(function() {
var t = new TestBindingIterable();
var empty = true;
t.forEach(function() { empty = false; });
assert_true(empty);
}, "Empty value iterator");
test(function() {
var t = new TestBindingIterable();
function is_iterator(o) {
return o[Symbol.iterator]() === o;
}
assert_true(is_iterator(t.keys()));
assert_true(is_iterator(t.values()));
assert_true(is_iterator(t.entries()));
}, "Iterable iterators are iterators");
test(function() {
var t = new TestBindingIterable();
t.add("first");
t.add("second");
t.add("third");
assert_array_equals(collect(t.keys()), [0, 1, 2]);
assert_array_equals(collect(t.values()), ["first", "second", "third"]);
var expected = [[0, "first"], [1, "second"], [2, "third"]];
var i = 0;
for (entry of t.entries()) {
assert_array_equals(entry, expected[i++]);
}
t.add("fourth");
assert_array_equals(collect(t.keys()), [0, 1, 2, 3]);
assert_array_equals(collect(t.values()), ["first", "second", "third", "fourth"]);
var expected = [[0, "first"], [1, "second"], [2, "third"], [3, "fourth"]];
var i = 0;
for (entry of t.entries()) {
assert_array_equals(entry, expected[i++]);
}
}, "Iterators iterate over values");
test(function() {
var t = new TestBindingPairIterable();
var empty = true;
t.forEach(function() { empty = false; });
assert_true(empty);
}, "Empty pair iterator");
test(function() {
var t = new TestBindingPairIterable();
function is_iterator(o) {
return o[Symbol.iterator]() === o;
}
assert_true(is_iterator(t.keys()));
assert_true(is_iterator(t.values()));
assert_true(is_iterator(t.entries()));
}, "Pair iterable iterators are iterators");
test(function() {
var t = new TestBindingPairIterable();
t.add("first", 0);
t.add("second", 1);
t.add("third", 2);
assert_array_equals(collect(t.keys()), ["first", "second", "third"]);
assert_array_equals(collect(t.values()), [0, 1, 2]);
var expected = [["first", 0], ["second", 1], ["third", 2]];
var i = 0;
for (entry of t.entries()) {
assert_array_equals(entry, expected[i++]);
}
t.add("fourth", 3);
assert_array_equals(collect(t.keys()), ["first", "second", "third", "fourth"]);
assert_array_equals(collect(t.values()), [0, 1, 2, 3]);
var expected = [["first", 0], ["second", 1], ["third", 2], ["fourth", 3]];
var i = 0;
for (entry of t.entries()) {
assert_array_equals(entry, expected[i++]);
}
}, "Pair iterators iterate over key/value pairs");
</script>