Lazily define interface objects on globals (fixes #6419)

This commit is contained in:
Anthony Ramine 2016-01-29 01:10:26 +01:00
parent ca979e115b
commit 1559f5a39f
11 changed files with 178 additions and 18 deletions

View file

@ -79,6 +79,8 @@ libc = "0.2"
log = "0.3" log = "0.3"
num = "0.1.24" num = "0.1.24"
rand = "0.3" rand = "0.3"
phf = "0.7.13"
phf_macros = "0.7.13"
ref_slice = "0.1.0" ref_slice = "0.1.0"
rustc-serialize = "0.3" rustc-serialize = "0.3"
selectors = {version = "0.5", features = ["heap_size"]} selectors = {version = "0.5", features = ["heap_size"]}

View file

@ -1779,10 +1779,10 @@ class CGDOMJSClass(CGThing):
} }
if self.descriptor.isGlobal(): if self.descriptor.isGlobal():
assert not self.descriptor.weakReferenceable assert not self.descriptor.weakReferenceable
args["enumerateHook"] = "Some(js::jsapi::JS_EnumerateStandardClasses)" args["enumerateHook"] = "Some(enumerate_global)"
args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL" args["flags"] = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL"
args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1" args["slots"] = "JSCLASS_GLOBAL_SLOT_COUNT + 1"
args["resolveHook"] = "Some(js::jsapi::JS_ResolveStandardClass)" args["resolveHook"] = "Some(resolve_global)"
args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook" args["traceHook"] = "js::jsapi::JS_GlobalObjectTraceHook"
elif self.descriptor.weakReferenceable: elif self.descriptor.weakReferenceable:
args["slots"] = "2" args["slots"] = "2"
@ -2280,11 +2280,8 @@ JS_SetPrototype(cx, obj.handle(), proto.handle());
%(copyUnforgeable)s %(copyUnforgeable)s
(*raw).init_reflector(obj.ptr); (*raw).init_reflector(obj.ptr);
let ret = Root::from_ref(&*raw); Root::from_ref(&*raw)\
""" % {'copyUnforgeable': unforgeable, 'createObject': create})
RegisterBindings::Register(cx, obj.handle());
ret""" % {'copyUnforgeable': unforgeable, 'createObject': create})
class CGIDLInterface(CGThing): class CGIDLInterface(CGThing):
@ -5447,12 +5444,12 @@ class CGBindingRoot(CGThing):
'dom::bindings::utils::{DOMClass, DOMJSClass}', 'dom::bindings::utils::{DOMClass, DOMJSClass}',
'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}', 'dom::bindings::utils::{DOM_PROTO_UNFORGEABLE_HOLDER_SLOT, JSCLASS_DOM_GLOBAL}',
'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}', 'dom::bindings::utils::{ProtoOrIfaceArray, create_dom_global}',
'dom::bindings::utils::{finalize_global, find_enum_string_index, generic_getter}', 'dom::bindings::utils::{enumerate_global, finalize_global, find_enum_string_index}',
'dom::bindings::utils::{generic_lenient_getter, generic_lenient_setter}', '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::{generic_method, generic_setter, get_array_index_from_id}',
'dom::bindings::utils::{get_dictionary_property, get_property_on_prototype}', '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::{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::utils::{throwing_constructor, trace_global}',
'dom::bindings::trace::{JSTraceable, RootedTraceable}', 'dom::bindings::trace::{JSTraceable, RootedTraceable}',
'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}', 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}',
@ -6095,6 +6092,37 @@ class GlobalGenRoots():
call the appropriate define/declare method. 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 @staticmethod
def PrototypeList(config): def PrototypeList(config):
# Prototype ID enum. # Prototype ID enum.

View file

@ -62,6 +62,7 @@ def main():
to_generate = [ to_generate = [
('PrototypeList', 'PrototypeList.rs'), ('PrototypeList', 'PrototypeList.rs'),
('RegisterBindings', 'RegisterBindings.rs'), ('RegisterBindings', 'RegisterBindings.rs'),
('InterfaceObjectMap', 'InterfaceObjectMap.rs'),
('InterfaceTypes', 'InterfaceTypes.rs'), ('InterfaceTypes', 'InterfaceTypes.rs'),
('InheritTypes', 'InheritTypes.rs'), ('InheritTypes', 'InheritTypes.rs'),
('Bindings', os.path.join('Bindings', 'mod.rs')), ('Bindings', os.path.join('Bindings', 'mod.rs')),

View file

@ -159,6 +159,9 @@ pub mod codegen {
pub mod Bindings { pub mod Bindings {
include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs")); include!(concat!(env!("OUT_DIR"), "/Bindings/mod.rs"));
} }
pub mod InterfaceObjectMap {
include!(concat!(env!("OUT_DIR"), "/InterfaceObjectMap.rs"));
}
pub mod InterfaceTypes { pub mod InterfaceTypes {
include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs")); include!(concat!(env!("OUT_DIR"), "/InterfaceTypes.rs"));
} }

View file

@ -4,6 +4,7 @@
//! Various utilities to glue JavaScript and the DOM implementation together. //! 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;
use dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH}; use dom::bindings::codegen::PrototypeList::{MAX_PROTO_CHAIN_LENGTH, PROTO_OR_IFACE_LENGTH};
use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class}; use dom::bindings::conversions::{DOM_OBJECT_SLOT, is_dom_class};
@ -18,16 +19,18 @@ use js;
use js::error::throw_type_error; use js::error::throw_type_error;
use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper}; use js::glue::{CallJitGetterOp, CallJitMethodOp, CallJitSetterOp, IsWrapper};
use js::glue::{GetCrossCompartmentWrapper, WrapperNew}; use js::glue::{GetCrossCompartmentWrapper, WrapperNew};
use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT}; use js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, RUST_JSID_IS_INT, RUST_JSID_IS_STRING};
use js::glue::{RUST_JSID_TO_INT, UnwrapObject}; use js::glue::{RUST_JSID_TO_INT, RUST_JSID_TO_STRING, UnwrapObject};
use js::jsapi::{CallArgs, CompartmentOptions, DOMCallbacks, GetGlobalForObjectCrossCompartment}; use js::jsapi::{CallArgs, CompartmentOptions, DOMCallbacks, GetGlobalForObjectCrossCompartment};
use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSClass, JSContext}; use js::jsapi::{HandleId, HandleObject, HandleValue, Heap, JSAutoCompartment, JSClass, JSContext};
use js::jsapi::{JSJitInfo, JSObject, JSTraceOp, JSTracer, JSVersion, JSWrapObjectCallbacks}; use js::jsapi::{JSJitInfo, JSObject, JSTraceOp, JSTracer, JSVersion, JSWrapObjectCallbacks};
use js::jsapi::{JS_DeletePropertyById1, JS_FireOnNewGlobalObject, JS_ForwardGetPropertyTo}; use js::jsapi::{JS_DeletePropertyById1, JS_EnumerateStandardClasses, JS_FireOnNewGlobalObject};
use js::jsapi::{JS_GetClass, JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty}; use js::jsapi::{JS_ForwardGetPropertyTo, JS_GetClass, JS_GetLatin1StringCharsAndLength};
use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_NewGlobalObject}; use js::jsapi::{JS_GetProperty, JS_GetPrototype, JS_GetReservedSlot, JS_HasProperty};
use js::jsapi::{JS_ObjectToOuterObject, JS_SetProperty, JS_SetReservedSlot}; use js::jsapi::{JS_HasPropertyById, JS_IsExceptionPending, JS_IsGlobalObject, JS_NewGlobalObject};
use js::jsapi::{MutableHandleValue, ObjectOpResult, OnNewGlobalHookOption, RootedObject}; 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::{JSVal};
use js::jsval::{PrivateValue, UndefinedValue}; use js::jsval::{PrivateValue, UndefinedValue};
use js::rust::{GCMethods, ToString}; use js::rust::{GCMethods, ToString};
@ -37,6 +40,7 @@ use std::default::Default;
use std::ffi::CString; use std::ffi::CString;
use std::os::raw::c_void; use std::os::raw::c_void;
use std::ptr; use std::ptr;
use std::slice;
use util::non_geckolib::jsstring_to_str; use util::non_geckolib::jsstring_to_str;
/// Proxy handler for a WindowProxy. /// Proxy handler for a WindowProxy.
@ -364,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, unsafe extern "C" fn wrap(cx: *mut JSContext,
_existing: HandleObject, _existing: HandleObject,
obj: HandleObject) obj: HandleObject)

View file

@ -26,6 +26,7 @@
#![doc = "The script crate contains all matters DOM."] #![doc = "The script crate contains all matters DOM."]
#![plugin(heapsize_plugin)] #![plugin(heapsize_plugin)]
#![plugin(phf_macros)]
#![plugin(plugins)] #![plugin(plugins)]
extern crate angle; extern crate angle;
@ -55,6 +56,7 @@ extern crate msg;
extern crate net_traits; extern crate net_traits;
extern crate num; extern crate num;
extern crate offscreen_gl_context; extern crate offscreen_gl_context;
extern crate phf;
#[macro_use] #[macro_use]
extern crate profile_traits; extern crate profile_traits;
extern crate rand; extern crate rand;

View file

@ -1428,6 +1428,15 @@ dependencies = [
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "phf_macros"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.7.13" version = "0.7.13"
@ -1622,6 +1631,8 @@ dependencies = [
"net_traits 0.0.1", "net_traits 0.0.1",
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_macros 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"profile_traits 0.0.1", "profile_traits 0.0.1",
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",

11
ports/cef/Cargo.lock generated
View file

@ -1332,6 +1332,15 @@ dependencies = [
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "phf_macros"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.7.13" version = "0.7.13"
@ -1508,6 +1517,8 @@ dependencies = [
"net_traits 0.0.1", "net_traits 0.0.1",
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_macros 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"profile_traits 0.0.1", "profile_traits 0.0.1",
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",

11
ports/gonk/Cargo.lock generated
View file

@ -1313,6 +1313,15 @@ dependencies = [
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "phf_macros"
version = "0.7.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"phf_generator 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_shared 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]] [[package]]
name = "phf_shared" name = "phf_shared"
version = "0.7.13" version = "0.7.13"
@ -1489,6 +1498,8 @@ dependencies = [
"net_traits 0.0.1", "net_traits 0.0.1",
"num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "num 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)",
"offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)", "offscreen_gl_context 0.1.0 (git+https://github.com/ecoal95/rust-offscreen-rendering-context)",
"phf 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"phf_macros 0.7.13 (registry+https://github.com/rust-lang/crates.io-index)",
"plugins 0.0.1", "plugins 0.0.1",
"profile_traits 0.0.1", "profile_traits 0.0.1",
"rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.3.12 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -34494,7 +34494,16 @@
}, },
"local_changes": { "local_changes": {
"deleted": [], "deleted": [],
"items": {}, "items": {
"testharness": {
"WebIDL/ecmascript-binding/interface-object.html": [
{
"path": "WebIDL/ecmascript-binding/interface-object.html",
"url": "/WebIDL/ecmascript-binding/interface-object.html"
}
]
}
},
"reftest_nodes": {} "reftest_nodes": {}
}, },
"reftest_nodes": { "reftest_nodes": {

View file

@ -0,0 +1,28 @@
<!doctype html>
<meta charset="utf-8">
<title>Interface objects</title>
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script>
test(function () {
assert_equals(typeof window.Blob, "function")
delete window.Blob;
assert_equals(window.Blob, undefined);
}, "An interface object deleted after it has been accessed is undefined");
test(function () {
delete window.File;
assert_equals(window.File, undefined);
}, "An interface object deleted before it has been defined is undefined");
test(function () {
delete window.ImageData;
assert_equals(Object.getOwnPropertyDescriptor(window, "ImageData"), undefined);
delete window.ImageData;
assert_equals(Object.getOwnPropertyDescriptor(window, "ImageData"), undefined);
}, "Interface objects deleted multiple times stay deleted");
test(function () {
assert_equals(window["abc\udc88"], undefined);
}, "Fancy property names don't break the resolve hook on Window");
</script>