Skip to content

Commit

Permalink
Auto merge of #9652 - nox:lazy-interface-object, r=Ms2ger
Browse files Browse the repository at this point in the history
Lazily define interface objects on globals (fixes #6419)

<!-- Reviewable:start -->
[<img src="https://reviewable.io/review_button.svg" height="40" alt="Review on Reviewable"/>](https://reviewable.io/reviews/servo/servo/9652)
<!-- Reviewable:end -->
  • Loading branch information
bors-servo committed Feb 25, 2016
2 parents 42f1712 + 2ed1a4a commit b188cb5
Show file tree
Hide file tree
Showing 14 changed files with 260 additions and 105 deletions.
2 changes: 2 additions & 0 deletions components/script/Cargo.toml
Expand Up @@ -79,6 +79,8 @@ libc = "0.2"
log = "0.3"
num = "0.1.24"
rand = "0.3"
phf = "0.7.13"
phf_macros = "0.7.13"
ref_slice = "0.1.0"
rustc-serialize = "0.3"
selectors = {version = "0.5", features = ["heap_size"]}
Expand Down
104 changes: 62 additions & 42 deletions components/script/dom/bindings/codegen/CodegenRust.py
Expand Up @@ -1768,17 +1768,21 @@ def __init__(self, descriptor):
def define(self):
args = {
"domClass": DOMClass(self.descriptor),
"enumerateHook": "None",
"finalizeHook": FINALIZE_HOOK_NAME,
"flags": "0",
"name": str_to_const_array(self.descriptor.interface.identifier.name),
"outerObjectHook": self.descriptor.outerObjectHook,
"resolveHook": "None",
"slots": "1",
"traceHook": TRACE_HOOK_NAME,
}
if self.descriptor.isGlobal():
assert not self.descriptor.weakReferenceable
args["enumerateHook"] = "Some(enumerate_global)"
args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
args["resolveHook"] = "Some(resolve_global)"
args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook"
elif self.descriptor.weakReferenceable:
args["slots"] = "2"
Expand All @@ -1793,8 +1797,8 @@ def define(self):
delProperty: None,
getProperty: None,
setProperty: None,
enumerate: None,
resolve: None,
enumerate: %(enumerateHook)s,
resolve: %(resolveHook)s,
convert: None,
finalize: Some(%(finalizeHook)s),
call: None,
Expand Down Expand Up @@ -2276,11 +2280,8 @@ def definition_body(self):
%(copyUnforgeable)s
(*raw).init_reflector(obj.ptr);
let ret = Root::from_ref(&*raw);
RegisterBindings::Register(cx, obj.handle());
ret""" % {'copyUnforgeable': unforgeable, 'createObject': create})
Root::from_ref(&*raw)\
""" % {'copyUnforgeable': unforgeable, 'createObject': create})


class CGIDLInterface(CGThing):
Expand Down Expand Up @@ -2375,9 +2376,8 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
properties should be a PropertyArrays instance.
"""
def __init__(self, descriptor, properties):
args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global')]
if not descriptor.interface.isCallback():
args.append(Argument('*mut ProtoOrIfaceArray', 'cache'))
args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'),
Argument('*mut ProtoOrIfaceArray', 'cache')]
CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args,
unsafe=True)
self.properties = properties
Expand All @@ -2387,7 +2387,14 @@ def definition_body(self):
if self.descriptor.interface.isCallback():
assert not self.descriptor.interface.ctor() and self.descriptor.interface.hasConstants()
return CGGeneric("""\
create_callback_interface_object(cx, global, sConstants, %s);""" % str_to_const_array(name))
let mut interface = RootedObject::new(cx, ptr::null_mut());
create_callback_interface_object(cx, global, sConstants, %(name)s, interface.handle_mut());
assert!(!interface.ptr.is_null());
(*cache)[PrototypeList::Constructor::%(id)s as usize] = interface.ptr;
if <*mut JSObject>::needs_post_barrier(interface.ptr) {
<*mut JSObject>::post_barrier((*cache).as_mut_ptr().offset(PrototypeList::Constructor::%(id)s as isize));
}
""" % {"id": name, "name": str_to_const_array(name)})

if len(self.descriptor.prototypeChain) == 1:
if self.descriptor.interface.getExtendedAttribute("ExceptionClass"):
Expand Down Expand Up @@ -2668,14 +2675,14 @@ def define(self):

def definition_body(self):
if self.descriptor.interface.isCallback():
code = "CreateInterfaceObjects(cx, global);"
function = "GetConstructorObject"
else:
code = """\
function = "GetProtoObject"
return CGGeneric("""\
assert!(!global.get().is_null());
let mut proto = RootedObject::new(cx, ptr::null_mut());
GetProtoObject(cx, global, proto.handle_mut());
assert!(!proto.ptr.is_null());
"""
return CGGeneric("assert!(!global.get().is_null());\n" + code)
%s(cx, global, proto.handle_mut());
assert!(!proto.ptr.is_null());""" % function)


def needCx(returnType, arguments, considerTypes):
Expand Down Expand Up @@ -4952,7 +4959,8 @@ def __init__(self, descriptor):
cgThings = []
if not descriptor.interface.isCallback():
cgThings.append(CGGetProtoObjectMethod(descriptor))
if descriptor.interface.hasInterfaceObject() and descriptor.hasDescendants():
if (descriptor.interface.hasInterfaceObject() and
descriptor.shouldHaveGetConstructorObjectMethod()):
cgThings.append(CGGetConstructorObjectMethod(descriptor))

for m in descriptor.interface.members:
Expand Down Expand Up @@ -5277,23 +5285,6 @@ def getDictionaryDependencies(dictionary):
return deps


class CGRegisterProtos(CGAbstractMethod):
def __init__(self, config):
arguments = [
Argument('*mut JSContext', 'cx'),
Argument('HandleObject', 'global'),
]
CGAbstractMethod.__init__(self, None, 'Register', 'void', arguments,
unsafe=False, pub=True)
self.config = config

def definition_body(self):
return CGList([
CGGeneric("codegen::Bindings::%sBinding::DefineDOMInterface(cx, global);" % desc.name)
for desc in self.config.getDescriptors(hasInterfaceObject=True, register=True)
], "\n")


class CGRegisterProxyHandlersMethod(CGAbstractMethod):
def __init__(self, descriptors):
docs = "Create the global vtables used by the generated DOM bindings to implement JS proxies."
Expand Down Expand Up @@ -5436,12 +5427,12 @@ def __init__(self, config, prefix, webIDLFile):
'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::{finalize_global, find_enum_string_index, generic_getter}',
'dom::bindings::utils::{generic_lenient_getter, generic_lenient_setter}',
'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, set_dictionary_property}',
'dom::bindings::utils::{is_platform_object, resolve_global, set_dictionary_property}',
'dom::bindings::utils::{throwing_constructor, trace_global}',
'dom::bindings::trace::{JSTraceable, RootedTraceable}',
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
Expand Down Expand Up @@ -6084,12 +6075,44 @@ class GlobalGenRoots():
call the appropriate define/declare method.
"""

@staticmethod
def InterfaceObjectMap(config):
mods = [
"dom::bindings::codegen",
"js::jsapi::{HandleObject, JSContext}",
"phf",
]
imports = CGList([CGGeneric("use %s;" % mod) for mod in mods], "\n")

pairs = []
for d in config.getDescriptors(hasInterfaceObject=True):
binding = toBindingNamespace(d.name)
pairs.append((d.name, binding))
for ctor in d.interface.namedConstructors:
pairs.append((ctor.identifier.name, binding))
pairs.sort(key=operator.itemgetter(0))
mappings = [
CGGeneric('b"%s" => codegen::Bindings::%s::DefineDOMInterface as fn(_, _),' % pair)
for pair in pairs
]
mapType = "phf::Map<&'static [u8], fn(*mut JSContext, HandleObject)>"
phf = CGWrapper(
CGIndenter(CGList(mappings, "\n")),
pre="pub static MAP: %s = phf_map! {\n" % mapType,
post="\n};\n")

return CGList([
CGGeneric(AUTOGENERATED_WARNING_COMMENT),
CGList([imports, phf], "\n\n")
])

@staticmethod
def PrototypeList(config):
# Prototype ID enum.
interfaces = config.getDescriptors(isCallback=False)
protos = [d.name for d in interfaces]
constructors = [d.name for d in interfaces if d.hasDescendants()]
constructors = [d.name for d in config.getDescriptors(hasInterfaceObject=True)
if d.shouldHaveGetConstructorObjectMethod()]
proxies = [d.name for d in config.getDescriptors(proxy=True)]

return CGList([
Expand All @@ -6115,15 +6138,12 @@ def PrototypeList(config):
def RegisterBindings(config):
# TODO - Generate the methods we want
code = CGList([
CGRegisterProtos(config),
CGRegisterProxyHandlers(config),
], "\n")

return CGImports(code, [], [], [
'dom::bindings::codegen',
'dom::bindings::codegen::PrototypeList::Proxies',
'js::jsapi::JSContext',
'js::jsapi::HandleObject',
'libc',
], ignored_warnings=[])

Expand Down
4 changes: 4 additions & 0 deletions components/script/dom/bindings/codegen/Configuration.py
Expand Up @@ -345,6 +345,10 @@ def hasDescendants(self):
return (self.interface.getUserData("hasConcreteDescendant", False) or
self.interface.getUserData("hasProxyDescendant", False))

def shouldHaveGetConstructorObjectMethod(self):
assert self.interface.hasInterfaceObject()
return self.interface.isCallback() or self.hasDescendants()

def isGlobal(self):
"""
Returns true if this is the primary interface for a global object
Expand Down
1 change: 1 addition & 0 deletions components/script/dom/bindings/codegen/GlobalGen.py
Expand Up @@ -62,6 +62,7 @@ def main():
to_generate = [
('PrototypeList', 'PrototypeList.rs'),
('RegisterBindings', 'RegisterBindings.rs'),
('InterfaceObjectMap', 'InterfaceObjectMap.rs'),
('InterfaceTypes', 'InterfaceTypes.rs'),
('InheritTypes', 'InheritTypes.rs'),
('Bindings', os.path.join('Bindings', 'mod.rs')),
Expand Down
13 changes: 7 additions & 6 deletions components/script/dom/bindings/interface.rs
Expand Up @@ -178,13 +178,14 @@ pub unsafe fn create_callback_interface_object(
cx: *mut JSContext,
receiver: HandleObject,
constants: &'static [ConstantSpec],
name: &'static [u8]) {
name: &'static [u8],
rval: MutableHandleObject) {
assert!(!constants.is_empty());
let interface_object = RootedObject::new(cx, JS_NewObject(cx, ptr::null()));
assert!(!interface_object.ptr.is_null());
define_constants(cx, interface_object.handle(), constants);
define_name(cx, interface_object.handle(), name);
define_on_global_object(cx, receiver, name, interface_object.handle());
rval.set(JS_NewObject(cx, ptr::null()));
assert!(!rval.ptr.is_null());
define_constants(cx, rval.handle(), constants);
define_name(cx, rval.handle(), name);
define_on_global_object(cx, receiver, name, rval.handle());
}

/// Create the interface prototype object of a non-callback interface.
Expand Down
3 changes: 3 additions & 0 deletions components/script/dom/bindings/mod.rs
Expand Up @@ -159,6 +159,9 @@ pub mod codegen {
pub mod Bindings {
include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs"));
}
pub mod InterfaceObjectMap {
include!(concat!(env!("OUT_DIR"), "/InterfaceObjectMap.rs"));
}
pub mod InterfaceTypes {
include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
}
Expand Down
70 changes: 61 additions & 9 deletions components/script/dom/bindings/utils.rs
Expand Up @@ -4,6 +4,7 @@

//! Various utilities to glue JavaScript and the DOM implementation together.

use dom::bindings::codegen::InterfaceObjectMap;
use dom::bindings::codegen::PrototypeList;
use dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class};
Expand All @@ -18,17 +19,18 @@ use js;
use js::error::throw_type_error;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, WrapperNew};
use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT};
use js::glue::{RUST_JSID_TO_INT, UnwrapObject};
use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING};
use js::glue::{RUST_JSID_TO_INT, RUST_JSID_TO_STRING, UnwrapObject};
use js::jsapi::{CallArgs, CompartmentOptions, DOMCallbacks, GetGlobalForObjectCrossCompartment};
use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSClass, JSContext};
use js::jsapi::{JSJitInfo, JSObject, JSTraceOp, JSTracer, JSVersion, JSWrapObjectCallbacks};
use js::jsapi::{JS_DeletePropertyById1, JS_FireOnNewGlobalObject};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetClass, JS_GetProperty, JS_GetPrototype};
use js::jsapi::{JS_GetReservedSlot, JS_HasProperty, JS_HasPropertyById, JS_InitStandardClasses};
use js::jsapi::{JS_IsExceptionPending, JS_NewGlobalObject, JS_ObjectToOuterObject, JS_SetProperty};
use js::jsapi::{JS_SetReservedSlot, MutableHandleValue, ObjectOpResult, OnNewGlobalHookOption};
use js::jsapi::{RootedObject};
use js::jsapi::{JS_DeletePropertyById1, JS_EnumerateStandardClasses, JS_FireOnNewGlobalObject};
use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetClass, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty};
use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject, JS_NewGlobalObject};
use js::jsapi::{JS_ObjectToOuterObject, JS_ResolveStandardClass, JS_SetProperty};
use js::jsapi::{JS_SetReservedSlot, JS_StringHasLatin1Chars, MutableHandleValue, ObjectOpResult};
use js::jsapi::{OnNewGlobalHookOption, RootedObject};
use js::jsval::{JSVal};
use js::jsval::{PrivateValue, UndefinedValue};
use js::rust::{GCMethods, ToString};
Expand All @@ -38,6 +40,7 @@ use std::default::Default;
use std::ffi::CString;
use std::os::raw::c_void;
use std::ptr;
use std::slice;
use util::non_geckolib::jsstring_to_str;

/// Proxy handler for a WindowProxy.
Expand Down Expand Up @@ -334,7 +337,6 @@ pub fn create_dom_global(cx: *mut JSContext,
PrivateValue(Box::into_raw(proto_array) as *const libc::c_void));

let _ac = JSAutoCompartment::new(cx, obj.ptr);
JS_InitStandardClasses(cx, obj.handle());
JS_FireOnNewGlobalObject(cx, obj.handle());
obj.ptr
}
Expand Down Expand Up @@ -366,6 +368,56 @@ pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) {
}
}

/// Enumerate lazy properties of a global object.
pub unsafe extern "C" fn enumerate_global(cx: *mut JSContext, obj: HandleObject) -> bool {
assert!(JS_IsGlobalObject(obj.get()));
if !JS_EnumerateStandardClasses(cx, obj) {
return false;
}
for init_fun in InterfaceObjectMap::MAP.values() {
init_fun(cx, obj);
}
true
}

/// Resolve a lazy global property, for interface objects and named constructors.
pub unsafe extern "C" fn resolve_global(
cx: *mut JSContext,
obj: HandleObject,
id: HandleId,
rval: *mut bool)
-> bool {
assert!(JS_IsGlobalObject(obj.get()));
if !JS_ResolveStandardClass(cx, obj, id, rval) {
return false;
}
if *rval {
return true;
}
if !RUST_JSID_IS_STRING(id) {
*rval = false;
return true;
}

let string = RUST_JSID_TO_STRING(id);
if !JS_StringHasLatin1Chars(string) {
*rval = false;
return true;
}
let mut length = 0;
let ptr = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), string, &mut length);
assert!(!ptr.is_null());
let bytes = slice::from_raw_parts(ptr, length as usize);

if let Some(init_fun) = InterfaceObjectMap::MAP.get(bytes) {
init_fun(cx, obj);
*rval = true;
} else {
*rval = false;
}
true
}

unsafe extern "C" fn wrap(cx: *mut JSContext,
_existing: HandleObject,
obj: HandleObject)
Expand Down
2 changes: 2 additions & 0 deletions components/script/lib.rs
Expand Up @@ -26,6 +26,7 @@
#![doc = "The script crate contains all matters DOM."]

#![plugin(heapsize_plugin)]
#![plugin(phf_macros)]
#![plugin(plugins)]

extern crate angle;
Expand Down Expand Up @@ -55,6 +56,7 @@ extern crate msg;
extern crate net_traits;
extern crate num;
extern crate offscreen_gl_context;
extern crate phf;
#[macro_use]
extern crate profile_traits;
extern crate rand;
Expand Down

0 comments on commit b188cb5

Please sign in to comment.