Implement WebIDL namespaces

This commit is contained in:
Anthony Ramine 2016-09-02 17:26:54 +02:00
parent 8ba0cf30a1
commit 0b689a8a31
5 changed files with 123 additions and 30 deletions

View file

@ -26,6 +26,7 @@ from WebIDL import (
) )
from Configuration import ( from Configuration import (
MakeNativeName,
MemberIsUnforgeable, MemberIsUnforgeable,
getModuleFromObject, getModuleFromObject,
getTypesFromCallback, getTypesFromCallback,
@ -80,7 +81,7 @@ def toStringBool(arg):
def toBindingNamespace(arg): def toBindingNamespace(arg):
return re.sub("((_workers)?$)", "Binding\\1", arg) return re.sub("((_workers)?$)", "Binding\\1", MakeNativeName(arg))
def stripTrailingWhitespace(text): def stripTrailingWhitespace(text):
@ -96,9 +97,6 @@ def innerSequenceType(type):
return type.inner.inner if type.nullable() else type.inner return type.inner.inner if type.nullable() else type.inner
def MakeNativeName(name):
return name[0].upper() + name[1:]
builtinNames = { builtinNames = {
IDLType.Tags.bool: 'bool', IDLType.Tags.bool: 'bool',
IDLType.Tags.int8: 'i8', IDLType.Tags.int8: 'i8',
@ -1811,7 +1809,8 @@ class CGImports(CGWrapper):
def isImportable(type): def isImportable(type):
if not type.isType(): if not type.isType():
assert type.isInterface() or type.isDictionary() or type.isEnum() assert (type.isInterface() or type.isDictionary() or
type.isEnum() or type.isNamespace())
return True return True
return not (type.builtin or type.isSequence() or type.isUnion()) return not (type.builtin or type.isSequence() or type.isUnion())
@ -1830,7 +1829,7 @@ class CGImports(CGWrapper):
if t.isCallback(): if t.isCallback():
return t.callback.identifier return t.callback.identifier
return t.identifier return t.identifier
assert t.isInterface() or t.isDictionary() or t.isEnum() assert t.isInterface() or t.isDictionary() or t.isEnum() or t.isNamespace()
return t.identifier return t.identifier
def removeWrapperAndNullableTypes(types): def removeWrapperAndNullableTypes(types):
@ -1881,7 +1880,7 @@ class CGImports(CGWrapper):
# Importing these types in the same module that defines them is an error. # Importing these types in the same module that defines them is an error.
if t in dictionaries or t in enums: if t in dictionaries or t in enums:
continue continue
if t.isInterface(): if t.isInterface() or t.isNamespace():
descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name) descriptor = descriptorProvider.getDescriptor(getIdentifier(t).name)
extras += [descriptor.path] extras += [descriptor.path]
if descriptor.interface.parent: if descriptor.interface.parent:
@ -2060,6 +2059,17 @@ class CGInterfaceObjectJSClass(CGThing):
self.descriptor = descriptor self.descriptor = descriptor
def define(self): def define(self):
if self.descriptor.interface.isNamespace():
classString = self.descriptor.interface.getExtendedAttribute("ClassString")
if classString:
classString = classString[0]
else:
classString = "Object"
return """\
static NAMESPACE_OBJECT_CLASS: NamespaceObjectClass = unsafe {
NamespaceObjectClass::new(%s)
};
""" % str_to_const_array(classString)
if self.descriptor.interface.ctor(): if self.descriptor.interface.ctor():
constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME constructorBehavior = "InterfaceConstructorBehavior::call(%s)" % CONSTRUCT_HOOK_NAME
else: else:
@ -2657,6 +2667,28 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
def definition_body(self): def definition_body(self):
name = self.descriptor.interface.identifier.name name = self.descriptor.interface.identifier.name
if self.descriptor.interface.isNamespace():
if self.descriptor.interface.getExtendedAttribute("ProtoObjectHack"):
proto = "JS_GetObjectPrototype(cx, global)"
else:
proto = "JS_NewPlainObject(cx)"
if self.properties.static_methods.length():
methods = self.properties.static_methods.variableName()
else:
methods = "&[]"
return CGGeneric("""\
rooted!(in(cx) let proto = %(proto)s);
assert!(!proto.is_null());
rooted!(in(cx) let mut namespace = ptr::null_mut());
create_namespace_object(cx, global, proto.handle(), &NAMESPACE_OBJECT_CLASS,
%(methods)s, %(name)s, namespace.handle_mut());
assert!(!namespace.is_null());
assert!((*cache)[PrototypeList::Constructor::%(id)s as usize].is_null());
(*cache)[PrototypeList::Constructor::%(id)s as usize] = namespace.get();
<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize),
ptr::null_mut(),
namespace.get());
""" % {"id": MakeNativeName(name), "methods": methods, "name": str_to_const_array(name), "proto": proto})
if self.descriptor.interface.isCallback(): if self.descriptor.interface.isCallback():
assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants() assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
return CGGeneric("""\ return CGGeneric("""\
@ -2871,7 +2903,7 @@ class CGGetPerInterfaceObject(CGAbstractMethod):
Argument('MutableHandleObject', 'rval')] Argument('MutableHandleObject', 'rval')]
CGAbstractMethod.__init__(self, descriptor, name, CGAbstractMethod.__init__(self, descriptor, name,
'void', args, pub=pub, unsafe=True) 'void', args, pub=pub, unsafe=True)
self.id = idPrefix + "::" + self.descriptor.name self.id = idPrefix + "::" + MakeNativeName(self.descriptor.name)
def definition_body(self): def definition_body(self):
return CGGeneric(""" return CGGeneric("""
@ -3014,7 +3046,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod):
return CGAbstractMethod.define(self) return CGAbstractMethod.define(self)
def definition_body(self): def definition_body(self):
if self.descriptor.interface.isCallback(): if self.descriptor.interface.isCallback() or self.descriptor.interface.isNamespace():
function = "GetConstructorObject" function = "GetConstructorObject"
else: else:
function = "GetProtoObject" function = "GetProtoObject"
@ -3074,7 +3106,7 @@ class CGCallGenerator(CGThing):
call = CGGeneric(nativeMethodName) call = CGGeneric(nativeMethodName)
if static: if static:
call = CGWrapper(call, pre="%s::" % descriptor.interface.identifier.name) call = CGWrapper(call, pre="%s::" % MakeNativeName(descriptor.interface.identifier.name))
else: else:
call = CGWrapper(call, pre="%s." % object) call = CGWrapper(call, pre="%s." % object)
call = CGList([call, CGWrapper(args, pre="(", post=")")]) call = CGList([call, CGWrapper(args, pre="(", post=")")])
@ -5452,6 +5484,8 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::js::OptionalRootedReference', 'dom::bindings::js::OptionalRootedReference',
'dom::bindings::js::Root', 'dom::bindings::js::Root',
'dom::bindings::js::RootedReference', 'dom::bindings::js::RootedReference',
'dom::bindings::namespace::NamespaceObjectClass',
'dom::bindings::namespace::create_namespace_object',
'dom::bindings::reflector::MutReflectable', 'dom::bindings::reflector::MutReflectable',
'dom::bindings::reflector::Reflectable', 'dom::bindings::reflector::Reflectable',
'dom::bindings::utils::DOMClass', 'dom::bindings::utils::DOMClass',
@ -5559,7 +5593,7 @@ class CGDescriptor(CGThing):
return name return name
cgThings = [] cgThings = []
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
cgThings.append(CGGetProtoObjectMethod(descriptor)) cgThings.append(CGGetProtoObjectMethod(descriptor))
reexports.append('GetProtoObject') reexports.append('GetProtoObject')
if (descriptor.interface.hasInterfaceObject() and if (descriptor.interface.hasInterfaceObject() and
@ -5620,7 +5654,7 @@ class CGDescriptor(CGThing):
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback():
cgThings.append(CGInterfaceObjectJSClass(descriptor)) cgThings.append(CGInterfaceObjectJSClass(descriptor))
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
cgThings.append(CGPrototypeJSClass(descriptor)) cgThings.append(CGPrototypeJSClass(descriptor))
# If there are no constant members, don't make a module for constants # If there are no constant members, don't make a module for constants
@ -5677,7 +5711,7 @@ class CGDescriptor(CGThing):
reexports.append('Wrap') reexports.append('Wrap')
haveUnscopables = False haveUnscopables = False
if not descriptor.interface.isCallback(): if not descriptor.interface.isCallback() and not descriptor.interface.isNamespace():
if unscopableNames: if unscopableNames:
haveUnscopables = True haveUnscopables = True
cgThings.append( cgThings.append(
@ -5704,7 +5738,7 @@ class CGDescriptor(CGThing):
cgThings, public=True), cgThings, public=True),
post='\n') post='\n')
reexports = ', '.join(map(lambda name: reexportedName(name), reexports)) reexports = ', '.join(map(lambda name: reexportedName(name), reexports))
self.cgRoot = CGList([CGGeneric('pub use self::%sBinding::{%s};' % (descriptor.name, reexports)), self.cgRoot = CGList([CGGeneric('pub use self::%s::{%s};' % (toBindingNamespace(descriptor.name), reexports)),
cgThings], '\n') cgThings], '\n')
def define(self): def define(self):
@ -6758,10 +6792,12 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def PrototypeList(config): def PrototypeList(config):
# Prototype ID enum. # Prototype ID enum.
interfaces = config.getDescriptors(isCallback=False) interfaces = config.getDescriptors(isCallback=False, isNamespace=False)
protos = [d.name for d in interfaces] protos = [d.name for d in interfaces]
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True) constructors = sorted([MakeNativeName(d.name)
if d.shouldHaveGetConstructorObjectMethod()] for d in config.getDescriptors(hasInterfaceObject=True)
if d.shouldHaveGetConstructorObjectMethod()])
proxies = [d.name for d in config.getDescriptors(proxy=True)] proxies = [d.name for d in config.getDescriptors(proxy=True)]
return CGList([ return CGList([
@ -6798,10 +6834,13 @@ class GlobalGenRoots():
@staticmethod @staticmethod
def InterfaceTypes(config): def InterfaceTypes(config):
descriptors = [d.name for d in config.getDescriptors(register=True, descriptors = sorted([MakeNativeName(d.name)
for d in config.getDescriptors(register=True,
isCallback=False, isCallback=False,
isIteratorInterface=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(),
MakeNativeName(name)))
for name in descriptors])
curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT) curr = CGWrapper(curr, pre=AUTOGENERATED_WARNING_COMMENT)
return curr return curr
@ -6812,7 +6851,7 @@ class GlobalGenRoots():
return getModuleFromObject(d).split('::')[-1] return getModuleFromObject(d).split('::')[-1]
descriptors = config.getDescriptors(register=True, isIteratorInterface=False) descriptors = config.getDescriptors(register=True, isIteratorInterface=False)
descriptors = (set(d.name + "Binding" for d in descriptors) | descriptors = (set(toBindingNamespace(d.name) for d in descriptors) |
set(leafModule(d) for d in config.callbacks) | set(leafModule(d) for d in config.callbacks) |
set(leafModule(d) for d in config.getDictionaries())) 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)])

View file

@ -4,7 +4,7 @@
import os import os
from WebIDL import IDLExternalInterface, IDLInterface, IDLWrapperType, WebIDLError from WebIDL import IDLExternalInterface, IDLWrapperType, WebIDLError
class Configuration: class Configuration:
@ -30,10 +30,9 @@ class Configuration:
raise WebIDLError("Servo does not support external interfaces.", raise WebIDLError("Servo does not support external interfaces.",
[thing.location]) [thing.location])
# Some toplevel things are sadly types, and those have an assert not thing.isType()
# isInterface that doesn't mean the same thing as IDLObject's
# isInterface()... if not thing.isInterface() and not thing.isNamespace():
if not isinstance(thing, IDLInterface):
continue continue
iface = thing iface = thing
@ -83,6 +82,8 @@ class Configuration:
getter = lambda x: x.interface.hasInterfaceObject() getter = lambda x: x.interface.hasInterfaceObject()
elif key == 'isCallback': elif key == 'isCallback':
getter = lambda x: x.interface.isCallback() getter = lambda x: x.interface.isCallback()
elif key == 'isNamespace':
getter = lambda x: x.interface.isNamespace()
elif key == 'isJSImplemented': elif key == 'isJSImplemented':
getter = lambda x: x.interface.isJSImplemented() getter = lambda x: x.interface.isJSImplemented()
elif key == 'isGlobal': elif key == 'isGlobal':
@ -210,7 +211,7 @@ class Descriptor(DescriptorProvider):
if self.interface.isIteratorInterface(): if self.interface.isIteratorInterface():
pathDefault = 'dom::bindings::iterable::IterableIterator' pathDefault = 'dom::bindings::iterable::IterableIterator'
else: else:
pathDefault = 'dom::types::%s' % typeName pathDefault = 'dom::types::%s' % MakeNativeName(typeName)
self.concreteType = typeName self.concreteType = typeName
self.register = desc.get('register', True) self.register = desc.get('register', True)
@ -223,6 +224,7 @@ class Descriptor(DescriptorProvider):
# If we're concrete, we need to crawl our ancestor interfaces and mark # If we're concrete, we need to crawl our ancestor interfaces and mark
# them as having a concrete descendant. # them as having a concrete descendant.
self.concrete = (not self.interface.isCallback() and self.concrete = (not self.interface.isCallback() and
not self.interface.isNamespace() and
not self.interface.getExtendedAttribute("Abstract")) not self.interface.getExtendedAttribute("Abstract"))
self.hasUnforgeableMembers = (self.concrete and self.hasUnforgeableMembers = (self.concrete and
any(MemberIsUnforgeable(m, self) for m in any(MemberIsUnforgeable(m, self) for m in
@ -381,7 +383,7 @@ class Descriptor(DescriptorProvider):
def shouldHaveGetConstructorObjectMethod(self): def shouldHaveGetConstructorObjectMethod(self):
assert self.interface.hasInterfaceObject() assert self.interface.hasInterfaceObject()
return self.interface.isCallback() or self.hasDescendants() return self.interface.isCallback() or self.interface.isNamespace() or self.hasDescendants()
def isExposedConditionally(self): def isExposedConditionally(self):
return self.interface.isExposedConditionally() return self.interface.isExposedConditionally()
@ -396,6 +398,12 @@ class Descriptor(DescriptorProvider):
# Some utility methods # Some utility methods
def MakeNativeName(name):
return name[0].upper() + name[1:]
def getModuleFromObject(object): def getModuleFromObject(object):
return ('dom::bindings::codegen::Bindings::' + return ('dom::bindings::codegen::Bindings::' +
os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding') os.path.basename(object.location.filename()).split('.webidl')[0] + 'Binding')

View file

@ -257,7 +257,8 @@ pub unsafe fn create_named_constructors(
} }
} }
unsafe fn create_object( /// Create a new object with a unique type.
pub unsafe fn create_object(
cx: *mut JSContext, cx: *mut JSContext,
proto: HandleObject, proto: HandleObject,
class: &'static JSClass, class: &'static JSClass,
@ -316,7 +317,9 @@ pub unsafe fn is_exposed_in(object: HandleObject, globals: Globals) -> bool {
globals.contains(dom_class.global) globals.contains(dom_class.global)
} }
unsafe fn define_on_global_object( /// Define a property with a given name on the global object. Should be called
/// through the resolve hook.
pub unsafe fn define_on_global_object(
cx: *mut JSContext, cx: *mut JSContext,
global: HandleObject, global: HandleObject,
name: &[u8], name: &[u8],

View file

@ -140,6 +140,7 @@ pub mod inheritance;
pub mod interface; pub mod interface;
pub mod iterable; pub mod iterable;
pub mod js; pub mod js;
pub mod namespace;
pub mod num; pub mod num;
pub mod proxyhandler; pub mod proxyhandler;
pub mod refcounted; pub mod refcounted;

View file

@ -0,0 +1,42 @@
/* 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/. */
//! Machinery to initialise namespace objects.
use dom::bindings::guard::Guard;
use dom::bindings::interface::{create_object, define_on_global_object};
use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec, MutableHandleObject};
use libc;
use std::ptr;
/// The class of a namespace object.
#[derive(Copy, Clone)]
pub struct NamespaceObjectClass(JSClass);
unsafe impl Sync for NamespaceObjectClass {}
impl NamespaceObjectClass {
/// Create a new `NamespaceObjectClass` structure.
pub const unsafe fn new(name: &'static [u8]) -> Self {
NamespaceObjectClass(JSClass {
name: name as *const _ as *const libc::c_char,
flags: 0,
cOps: ptr::null_mut(),
reserved: [ptr::null_mut(); 3],
})
}
}
/// Create a new namespace object.
pub unsafe fn create_namespace_object(
cx: *mut JSContext,
global: HandleObject,
proto: HandleObject,
class: &'static NamespaceObjectClass,
methods: &[Guard<&'static [JSFunctionSpec]>],
name: &[u8],
rval: MutableHandleObject) {
create_object(cx, proto, &class.0, methods, &[], &[], rval);
define_on_global_object(cx, global, name, rval.handle());
}