Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Lazily define interface objects on globals (fixes #6419) #9652

Merged
merged 5 commits into from Feb 25, 2016
Merged
Changes from all commits
Commits
File filter...
Filter file types
Jump to…
Jump to file
Failed to load files.

Always

Just for now

@@ -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"]}
@@ -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"
@@ -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,
@@ -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):
@@ -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
@@ -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"):
@@ -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):
@@ -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:
@@ -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."
@@ -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}',
@@ -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([
@@ -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=[])

@@ -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
@@ -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')),
@@ -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.
@@ -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"));
}
@@ -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};
@@ -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};
@@ -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.
@@ -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
}
@@ -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)
@@ -26,6 +26,7 @@
#![doc = "The script crate contains all matters DOM."]

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

extern crate angle;
@@ -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;
ProTip! Use n and p to navigate between commits in a pull request.
You can’t perform that action at this time.