From 833e0d2fac7724967e15cff61c95610f18c6b958 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 13 Dec 2015 09:00:26 +0100 Subject: Refactor prototype initialisation The function do_create_interface_objects is removed in favour of 4 functions: create_callback_interface_object, create_interface_prototype_object, create_noncallback_interface_object and create_named_constructors. While this increases the amount of codegen'd code, this greatly improves the readability of the code involved in this part of DOM, instead of having one function doing 4 different things. We can always find a more adequate abstraction later. NativeProperties and everything related to the interface objects have been removed from the utils module. --- components/script/dom/bindings/interface.rs | 155 ++++++++++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 components/script/dom/bindings/interface.rs (limited to 'components/script/dom/bindings/interface.rs') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs new file mode 100644 index 00000000000..a7420c2aeb4 --- /dev/null +++ b/components/script/dom/bindings/interface.rs @@ -0,0 +1,155 @@ +/* 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 interface prototype objects and interface objects. + +use dom::bindings::utils::{ConstantSpec, NonNullJSNative, define_constants}; +use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec, JSObject}; +use js::jsapi::{JSPropertySpec, JS_DefineProperty1, JS_GetFunctionObject}; +use js::jsapi::{JS_LinkConstructorAndPrototype, JS_NewFunction}; +use js::jsapi::{JS_NewObjectWithUniqueType, MutableHandleObject, RootedObject}; +use js::rust::{define_methods, define_properties}; +use js::{JSFUN_CONSTRUCTOR, JSPROP_PERMANENT, JSPROP_READONLY}; +use libc; +use std::ptr; + +/// Create and define the interface object of a callback interface. +pub unsafe fn create_callback_interface_object( + cx: *mut JSContext, + receiver: HandleObject, + constructor_native: NonNullJSNative, + length: u32, + constants: &'static [ConstantSpec], + name: &'static [u8]) { + assert!(!constants.is_empty()); + let interface_object = + RootedObject::new(cx, create_constructor(cx, constructor_native, length, name)); + assert!(!interface_object.ptr.is_null()); + define_constants(cx, interface_object.handle(), constants); + define_on_global_object(cx, receiver, name, interface_object.handle()); +} + +/// Create the interface prototype object of a non-callback interface. +pub unsafe fn create_interface_prototype_object( + cx: *mut JSContext, + proto: HandleObject, + class: &'static JSClass, + regular_methods: Option<&'static [JSFunctionSpec]>, + regular_properties: Option<&'static [JSPropertySpec]>, + constants: &'static [ConstantSpec], + rval: MutableHandleObject) { + create_object(cx, proto, class, regular_methods, regular_properties, constants, rval); +} + +/// Create and define the interface object of a non-callback interface. +pub unsafe fn create_noncallback_interface_object( + cx: *mut JSContext, + receiver: HandleObject, + constructor_native: NonNullJSNative, + static_methods: Option<&'static [JSFunctionSpec]>, + static_properties: Option<&'static [JSPropertySpec]>, + constants: &'static [ConstantSpec], + interface_prototype_object: HandleObject, + name: &'static [u8], + length: u32) { + assert!(!interface_prototype_object.ptr.is_null()); + + let interface_object = + RootedObject::new(cx, create_constructor(cx, constructor_native, length, name)); + assert!(!interface_object.ptr.is_null()); + + if let Some(static_methods) = static_methods { + define_methods(cx, interface_object.handle(), static_methods).unwrap(); + } + + if let Some(static_properties) = static_properties { + define_properties(cx, interface_object.handle(), static_properties).unwrap(); + } + + define_constants(cx, interface_object.handle(), constants); + + assert!(JS_LinkConstructorAndPrototype(cx, interface_object.handle(), + interface_prototype_object)); + define_on_global_object(cx, receiver, name, interface_object.handle()); +} + +/// Create and define the named constructors of a non-callback interface. +pub unsafe fn create_named_constructors( + cx: *mut JSContext, + receiver: HandleObject, + named_constructors: &[(NonNullJSNative, &'static [u8], u32)], + interface_prototype_object: HandleObject) { + let mut constructor = RootedObject::new(cx, ptr::null_mut()); + + for &(native, name, arity) in named_constructors { + assert!(*name.last().unwrap() == b'\0'); + + constructor.ptr = create_constructor(cx, native, arity, name); + assert!(!constructor.ptr.is_null()); + + assert!(JS_DefineProperty1(cx, + constructor.handle(), + b"prototype\0".as_ptr() as *const libc::c_char, + interface_prototype_object, + JSPROP_PERMANENT | JSPROP_READONLY, + None, + None)); + + define_on_global_object(cx, receiver, name, constructor.handle()); + } +} + +unsafe fn create_constructor( + cx: *mut JSContext, + constructor_native: NonNullJSNative, + ctor_nargs: u32, + name: &'static [u8]) + -> *mut JSObject { + assert!(*name.last().unwrap() == b'\0'); + + let fun = JS_NewFunction(cx, + Some(constructor_native), + ctor_nargs, + JSFUN_CONSTRUCTOR, + name.as_ptr() as *const _); + assert!(!fun.is_null()); + + let constructor = JS_GetFunctionObject(fun); + assert!(!constructor.is_null()); + + constructor +} + +unsafe fn create_object( + cx: *mut JSContext, + proto: HandleObject, + class: &'static JSClass, + methods: Option<&'static [JSFunctionSpec]>, + properties: Option<&'static [JSPropertySpec]>, + constants: &'static [ConstantSpec], + rval: MutableHandleObject) { + rval.set(JS_NewObjectWithUniqueType(cx, class, proto)); + assert!(!rval.ptr.is_null()); + if let Some(methods) = methods { + define_methods(cx, rval.handle(), methods).unwrap(); + } + if let Some(properties) = properties { + define_properties(cx, rval.handle(), properties).unwrap(); + } + define_constants(cx, rval.handle(), constants); +} + +unsafe fn define_on_global_object( + cx: *mut JSContext, + receiver: HandleObject, + name: &'static [u8], + obj: HandleObject) { + assert!(*name.last().unwrap() == b'\0'); + assert!(JS_DefineProperty1(cx, + receiver, + name.as_ptr() as *const libc::c_char, + obj, + 0, + None, None)); +} -- cgit v1.2.3 From 7693aecf1521c17e59ca8f6863b91cd7ee1c4074 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 4 Jan 2016 15:34:52 +0100 Subject: Use the object prototype for callback interface objects window.NodeFilter's prototype should be the object prototype. --- components/script/dom/bindings/interface.rs | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) (limited to 'components/script/dom/bindings/interface.rs') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index a7420c2aeb4..cf4b7cc7924 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -5,10 +5,11 @@ //! Machinery to initialise interface prototype objects and interface objects. use dom::bindings::utils::{ConstantSpec, NonNullJSNative, define_constants}; -use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec, JSObject}; -use js::jsapi::{JSPropertySpec, JS_DefineProperty1, JS_GetFunctionObject}; -use js::jsapi::{JS_LinkConstructorAndPrototype, JS_NewFunction}; -use js::jsapi::{JS_NewObjectWithUniqueType, MutableHandleObject, RootedObject}; +use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec}; +use js::jsapi::{JSObject, JSPropertySpec, JS_DefineProperty1, JS_DefineProperty2}; +use js::jsapi::{JS_GetFunctionObject, JS_InternString, JS_LinkConstructorAndPrototype}; +use js::jsapi::{JS_NewFunction, JS_NewObject, JS_NewObjectWithUniqueType}; +use js::jsapi::{MutableHandleObject, RootedObject, RootedString}; use js::rust::{define_methods, define_properties}; use js::{JSFUN_CONSTRUCTOR, JSPROP_PERMANENT, JSPROP_READONLY}; use libc; @@ -18,15 +19,13 @@ use std::ptr; pub unsafe fn create_callback_interface_object( cx: *mut JSContext, receiver: HandleObject, - constructor_native: NonNullJSNative, - length: u32, constants: &'static [ConstantSpec], name: &'static [u8]) { assert!(!constants.is_empty()); - let interface_object = - RootedObject::new(cx, create_constructor(cx, constructor_native, length, name)); + 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()); } @@ -140,6 +139,19 @@ unsafe fn create_object( define_constants(cx, rval.handle(), constants); } +unsafe fn define_name(cx: *mut JSContext, obj: HandleObject, name: &'static [u8]) { + assert!(*name.last().unwrap() == b'\0'); + let name = + RootedString::new(cx, JS_InternString(cx, name.as_ptr() as *const libc::c_char)); + assert!(!name.ptr.is_null()); + assert!(JS_DefineProperty2(cx, + obj, + b"name\0".as_ptr() as *const libc::c_char, + name.handle(), + JSPROP_READONLY, + None, None)); +} + unsafe fn define_on_global_object( cx: *mut JSContext, receiver: HandleObject, -- cgit v1.2.3 From e1d3e4df2acfd1c660016755aa73de1b359f420b Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 4 Jan 2016 16:16:34 +0100 Subject: Describe non-callback interface objects with JSClass structures JS_NewFunction doesn't allow us to set the prototype of the interface objects. --- components/script/dom/bindings/interface.rs | 177 ++++++++++++++++++++++++---- 1 file changed, 155 insertions(+), 22 deletions(-) (limited to 'components/script/dom/bindings/interface.rs') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index cf4b7cc7924..838afc7b166 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -4,17 +4,109 @@ //! Machinery to initialise interface prototype objects and interface objects. +use dom::bindings::codegen::PrototypeList; +use dom::bindings::conversions::get_dom_class; use dom::bindings::utils::{ConstantSpec, NonNullJSNative, define_constants}; -use js::jsapi::{HandleObject, JSClass, JSContext, JSFunctionSpec}; -use js::jsapi::{JSObject, JSPropertySpec, JS_DefineProperty1, JS_DefineProperty2}; -use js::jsapi::{JS_GetFunctionObject, JS_InternString, JS_LinkConstructorAndPrototype}; -use js::jsapi::{JS_NewFunction, JS_NewObject, JS_NewObjectWithUniqueType}; -use js::jsapi::{MutableHandleObject, RootedObject, RootedString}; +use js::glue::UncheckedUnwrapObject; +use js::jsapi::{Class, ClassExtension, ClassSpec, HandleObject, HandleValue, JSClass}; +use js::jsapi::{JSContext, JSFunctionSpec, JSObject, JSPropertySpec, JSString}; +use js::jsapi::{JS_DefineProperty1, JS_DefineProperty2, JS_DefineProperty4}; +use js::jsapi::{JS_GetFunctionObject, JS_GetPrototype, JS_InternString}; +use js::jsapi::{JS_LinkConstructorAndPrototype, JS_NewFunction, JS_NewObject}; +use js::jsapi::{JS_NewObjectWithUniqueType, MutableHandleObject, MutableHandleValue}; +use js::jsapi::{ObjectOps, RootedObject, RootedString, Value}; use js::rust::{define_methods, define_properties}; use js::{JSFUN_CONSTRUCTOR, JSPROP_PERMANENT, JSPROP_READONLY}; use libc; use std::ptr; +/// A constructor class hook. +pub type ConstructorClassHook = + unsafe extern "C" fn(cx: *mut JSContext, argc: u32, vp: *mut Value) -> bool; + +/// A has_instance class hook. +pub type HasInstanceClassHook = + unsafe extern "C" fn(cx: *mut JSContext, + obj: HandleObject, + vp: MutableHandleValue, + bp: *mut bool) + -> bool; + +/// A fun_to_string class hook. +pub type FunToStringHook = + unsafe extern "C" fn(cx: *mut JSContext, + obj: HandleObject, + indent: u32) + -> *mut JSString; + +/// The class of a non-callback interface object. +#[derive(Copy, Clone)] +pub struct NonCallbackInterfaceObjectClass { + /// The SpiderMonkey Class structure. + pub class: Class, +} +unsafe impl Sync for NonCallbackInterfaceObjectClass {} + +impl NonCallbackInterfaceObjectClass { + /// Create a new `NonCallbackInterfaceObjectClass` structure. + pub const fn new( + constructor: ConstructorClassHook, + has_instance: HasInstanceClassHook, + fun_to_string: FunToStringHook) + -> NonCallbackInterfaceObjectClass { + NonCallbackInterfaceObjectClass { + class: Class { + name: b"Function\0" as *const _ as *const libc::c_char, + flags: 0, + addProperty: None, + delProperty: None, + getProperty: None, + setProperty: None, + enumerate: None, + resolve: None, + convert: None, + finalize: None, + call: Some(constructor), + hasInstance: Some(has_instance), + construct: Some(constructor), + trace: None, + spec: ClassSpec { + createConstructor: None, + createPrototype: None, + constructorFunctions: ptr::null(), + constructorProperties: ptr::null(), + prototypeFunctions: ptr::null(), + prototypeProperties: ptr::null(), + finishInit: None, + flags: 0, + }, + ext: ClassExtension { + outerObject: None, + innerObject: None, + isWrappedNative: false, + weakmapKeyDelegateOp: None, + objectMovedOp: None, + }, + ops: ObjectOps { + lookupProperty: None, + defineProperty: None, + hasProperty: None, + getProperty: None, + setProperty: None, + getOwnPropertyDescriptor: None, + deleteProperty: None, + watch: None, + unwatch: None, + getElements: None, + enumerate: None, + thisObject: None, + funToString: Some(fun_to_string), + } + }, + } + } +} + /// Create and define the interface object of a callback interface. pub unsafe fn create_callback_interface_object( cx: *mut JSContext, @@ -45,31 +137,26 @@ pub unsafe fn create_interface_prototype_object( pub unsafe fn create_noncallback_interface_object( cx: *mut JSContext, receiver: HandleObject, - constructor_native: NonNullJSNative, + proto: HandleObject, + class: &'static NonCallbackInterfaceObjectClass, static_methods: Option<&'static [JSFunctionSpec]>, static_properties: Option<&'static [JSPropertySpec]>, constants: &'static [ConstantSpec], interface_prototype_object: HandleObject, name: &'static [u8], length: u32) { - assert!(!interface_prototype_object.ptr.is_null()); - - let interface_object = - RootedObject::new(cx, create_constructor(cx, constructor_native, length, name)); - assert!(!interface_object.ptr.is_null()); - - if let Some(static_methods) = static_methods { - define_methods(cx, interface_object.handle(), static_methods).unwrap(); - } - - if let Some(static_properties) = static_properties { - define_properties(cx, interface_object.handle(), static_properties).unwrap(); - } - - define_constants(cx, interface_object.handle(), constants); - + let mut interface_object = RootedObject::new(cx, ptr::null_mut()); + create_object(cx, + proto, + &*(class as *const _ as *const JSClass), + static_methods, + static_properties, + constants, + interface_object.handle_mut()); assert!(JS_LinkConstructorAndPrototype(cx, interface_object.handle(), interface_prototype_object)); + define_name(cx, interface_object.handle(), name); + define_length(cx, interface_object.handle(), length); define_on_global_object(cx, receiver, name, interface_object.handle()); } @@ -99,6 +186,43 @@ pub unsafe fn create_named_constructors( } } +/// Return whether a value is an instance of a given prototype. +/// http://heycam.github.io/webidl/#es-interface-hasinstance +pub unsafe fn has_instance( + cx: *mut JSContext, + prototype: HandleObject, + value: HandleValue, + id: PrototypeList::ID, + index: usize) + -> Result { + if !value.is_object() { + // Step 1. + return Ok(false); + } + let mut value = RootedObject::new(cx, value.to_object()); + + // Steps 2-3 only concern callback interface objects. + + if let Ok(dom_class) = get_dom_class(UncheckedUnwrapObject(value.ptr, /* stopAtOuter = */ 0)) { + if dom_class.interface_chain[index] == id { + // Step 4. + return Ok(true); + } + } + + while JS_GetPrototype(cx, value.handle(), value.handle_mut()) { + if value.ptr.is_null() { + // Step 5.2. + return Ok(false); + } else if value.ptr as *const _ == prototype.ptr { + // Step 5.3. + return Ok(true); + } + } + // JS_GetPrototype threw an exception. + Err(()) +} + unsafe fn create_constructor( cx: *mut JSContext, constructor_native: NonNullJSNative, @@ -152,6 +276,15 @@ unsafe fn define_name(cx: *mut JSContext, obj: HandleObject, name: &'static [u8] None, None)); } +unsafe fn define_length(cx: *mut JSContext, obj: HandleObject, length: u32) { + assert!(JS_DefineProperty4(cx, + obj, + b"length\0".as_ptr() as *const libc::c_char, + length, + JSPROP_READONLY, + None, None)); +} + unsafe fn define_on_global_object( cx: *mut JSContext, receiver: HandleObject, -- cgit v1.2.3 From a1a9021aad90cdc06816341b951e9d2fedaa43ee Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Mon, 4 Jan 2016 16:37:18 +0100 Subject: Inline create_constructor into its only caller --- components/script/dom/bindings/interface.rs | 41 +++++++++-------------------- 1 file changed, 13 insertions(+), 28 deletions(-) (limited to 'components/script/dom/bindings/interface.rs') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index 838afc7b166..4066e5617cb 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -9,12 +9,12 @@ use dom::bindings::conversions::get_dom_class; use dom::bindings::utils::{ConstantSpec, NonNullJSNative, define_constants}; use js::glue::UncheckedUnwrapObject; use js::jsapi::{Class, ClassExtension, ClassSpec, HandleObject, HandleValue, JSClass}; -use js::jsapi::{JSContext, JSFunctionSpec, JSObject, JSPropertySpec, JSString}; -use js::jsapi::{JS_DefineProperty1, JS_DefineProperty2, JS_DefineProperty4}; -use js::jsapi::{JS_GetFunctionObject, JS_GetPrototype, JS_InternString}; -use js::jsapi::{JS_LinkConstructorAndPrototype, JS_NewFunction, JS_NewObject}; -use js::jsapi::{JS_NewObjectWithUniqueType, MutableHandleObject, MutableHandleValue}; -use js::jsapi::{ObjectOps, RootedObject, RootedString, Value}; +use js::jsapi::{JSContext, JSFunctionSpec, JSPropertySpec, JSString, JS_DefineProperty1}; +use js::jsapi::{JS_DefineProperty2, JS_DefineProperty4, JS_GetFunctionObject}; +use js::jsapi::{JS_GetPrototype, JS_InternString, JS_LinkConstructorAndPrototype}; +use js::jsapi::{JS_NewFunction, JS_NewObject, JS_NewObjectWithUniqueType}; +use js::jsapi::{MutableHandleObject, MutableHandleValue, ObjectOps, RootedObject}; +use js::jsapi::{RootedString, Value}; use js::rust::{define_methods, define_properties}; use js::{JSFUN_CONSTRUCTOR, JSPROP_PERMANENT, JSPROP_READONLY}; use libc; @@ -171,7 +171,13 @@ pub unsafe fn create_named_constructors( for &(native, name, arity) in named_constructors { assert!(*name.last().unwrap() == b'\0'); - constructor.ptr = create_constructor(cx, native, arity, name); + let fun = JS_NewFunction(cx, + Some(native), + arity, + JSFUN_CONSTRUCTOR, + name.as_ptr() as *const libc::c_char); + assert!(!fun.is_null()); + constructor.ptr = JS_GetFunctionObject(fun); assert!(!constructor.ptr.is_null()); assert!(JS_DefineProperty1(cx, @@ -223,27 +229,6 @@ pub unsafe fn has_instance( Err(()) } -unsafe fn create_constructor( - cx: *mut JSContext, - constructor_native: NonNullJSNative, - ctor_nargs: u32, - name: &'static [u8]) - -> *mut JSObject { - assert!(*name.last().unwrap() == b'\0'); - - let fun = JS_NewFunction(cx, - Some(constructor_native), - ctor_nargs, - JSFUN_CONSTRUCTOR, - name.as_ptr() as *const _); - assert!(!fun.is_null()); - - let constructor = JS_GetFunctionObject(fun); - assert!(!constructor.is_null()); - - constructor -} - unsafe fn create_object( cx: *mut JSContext, proto: HandleObject, -- cgit v1.2.3 From d13da7d9b30a6671ed45e2f2c290405a5c92aafe Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 13 Dec 2015 09:00:26 +0100 Subject: Fix prototypes of interface objects (fixes #2665) --- components/script/dom/bindings/interface.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) (limited to 'components/script/dom/bindings/interface.rs') diff --git a/components/script/dom/bindings/interface.rs b/components/script/dom/bindings/interface.rs index 4066e5617cb..96f4d7c470a 100644 --- a/components/script/dom/bindings/interface.rs +++ b/components/script/dom/bindings/interface.rs @@ -144,20 +144,19 @@ pub unsafe fn create_noncallback_interface_object( constants: &'static [ConstantSpec], interface_prototype_object: HandleObject, name: &'static [u8], - length: u32) { - let mut interface_object = RootedObject::new(cx, ptr::null_mut()); + length: u32, + rval: MutableHandleObject) { create_object(cx, proto, &*(class as *const _ as *const JSClass), static_methods, static_properties, constants, - interface_object.handle_mut()); - assert!(JS_LinkConstructorAndPrototype(cx, interface_object.handle(), - interface_prototype_object)); - define_name(cx, interface_object.handle(), name); - define_length(cx, interface_object.handle(), length); - define_on_global_object(cx, receiver, name, interface_object.handle()); + rval); + assert!(JS_LinkConstructorAndPrototype(cx, rval.handle(), interface_prototype_object)); + define_name(cx, rval.handle(), name); + define_length(cx, rval.handle(), length); + define_on_global_object(cx, receiver, name, rval.handle()); } /// Create and define the named constructors of a non-callback interface. -- cgit v1.2.3