diff --git a/Cargo.lock b/Cargo.lock index 4d77cee7d6bf..82e8e26d8e2f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -257,9 +257,9 @@ dependencies = [ [[package]] name = "js" version = "0.1.0" -source = "git+https://github.com/servo/rust-mozjs#41fb0d80a5ed5614ca13a120cdb3281e599d4e04" +source = "git+https://github.com/servo/rust-mozjs#01117b7a4782857ac8242ff17245b28816de19af" dependencies = [ - "mozjs-sys 0.0.0 (git+https://github.com/servo/mozjs#47cd6a4e60c75642ba182f0df9a42b71ec7c2c88)", + "mozjs-sys 0.0.0 (git+https://github.com/servo/mozjs#cbc5759c6d4019acf41ae64f0ba222f8c2c70d4a)", ] [[package]] @@ -311,7 +311,7 @@ source = "git+https://github.com/Kimundi/lazy-static.rs#e62a65372f1dd9019e37eb93 [[package]] name = "mozjs-sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#47cd6a4e60c75642ba182f0df9a42b71ec7c2c88" +source = "git+https://github.com/servo/mozjs#cbc5759c6d4019acf41ae64f0ba222f8c2c70d4a" [[package]] name = "msg" @@ -392,7 +392,7 @@ dependencies = [ "gfx 0.0.1", "http 0.1.0-pre (git+https://github.com/servo/rust-http?ref=servo#92019011b0cdf1bffc8c584830de1bf330d79d0d)", "hubbub 0.1.0 (git+https://github.com/servo/rust-hubbub#c7f868e688de6e9cbdc26aa09292ed072bc2648b)", - "js 0.1.0 (git+https://github.com/servo/rust-mozjs#41fb0d80a5ed5614ca13a120cdb3281e599d4e04)", + "js 0.1.0 (git+https://github.com/servo/rust-mozjs#01117b7a4782857ac8242ff17245b28816de19af)", "msg 0.0.1", "net 0.0.1", "plugins 0.0.1", diff --git a/components/layout/wrapper.rs b/components/layout/wrapper.rs index 4b4f3eef939a..7c0ad205a647 100644 --- a/components/layout/wrapper.rs +++ b/components/layout/wrapper.rs @@ -521,7 +521,7 @@ impl<'ln> TLayoutNode for ThreadSafeLayoutNode<'ln> { } unsafe fn get<'a>(&'a self) -> &'a Node { // this change. - mem::transmute::<*mut Node,&'a Node>(self.get_jsmanaged().unsafe_get()) + mem::transmute::<*const Node,&'a Node>(self.get_jsmanaged().unsafe_get()) } fn first_child(&self) -> Option> { @@ -619,7 +619,7 @@ impl<'ln> ThreadSafeLayoutNode<'ln> { // FIXME(pcwalton): Workaround until Rust gets multiple lifetime parameters on // implementations. ThreadSafeLayoutElement { - element: &mut *element, + element: &*element, } } } diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index cc5ebbe96a5e..965aebadefaf 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -157,6 +157,7 @@ pub trait AttrHelpers<'a> { impl<'a> AttrHelpers<'a> for JSRef<'a, Attr> { fn set_value(self, set_type: AttrSettingType, value: AttrValue) { let owner = self.owner.root(); + owner.init(); let node: JSRef = NodeCast::from_ref(*owner); let namespace_is_null = self.namespace == namespace::Null; diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index d450dd25688d..8ea449c52f6a 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -5,16 +5,17 @@ //! Base classes to work with IDL callbacks. use dom::bindings::global::global_object_for_js_object; -use dom::bindings::js::JSRef; +use dom::bindings::js::{JSRef, RootablePointer, RootableValue}; use dom::bindings::trace::Traceable; use dom::bindings::utils::Reflectable; use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable}; -use js::jsapi::JS_GetProperty; +use js::jsapi::{JS_GetProperty, JS_IsExceptionPending, JS_ReportPendingException}; use js::jsval::{JSVal, UndefinedValue}; use std::ptr; /// The exception handling used for a call. +#[deriving(PartialEq, Eq)] pub enum ExceptionHandling { /// Report any exception and don't throw it to the caller code. ReportExceptions, @@ -97,37 +98,37 @@ impl CallbackInterface { /// or `Err(())` otherwise. If it returns `Err(())`, a JSAPI exception is /// pending. pub fn GetCallableProperty(&self, cx: *mut JSContext, name: &str) -> Result { - let mut callable = UndefinedValue(); + let mut callable = UndefinedValue().root_value(); unsafe { let name = name.to_c_str(); - if JS_GetProperty(cx, self.callback(), name.as_ptr(), &mut callable) == 0 { + let callback = self.callback().root_ptr(); // XXX unrooted + if !JS_GetProperty(cx, callback.handle(), name.as_ptr(), callable.mut_handle_()) { return Err(()); } - if !callable.is_object() || - JS_ObjectIsCallable(cx, callable.to_object()) == 0 { + if !callable.raw_().is_object() || + !JS_ObjectIsCallable(cx, callable.raw_().to_object()) { // FIXME(#347) //ThrowErrorMessage(cx, MSG_NOT_CALLABLE, description.get()); return Err(()); } } - Ok(callable) + Ok(*callable.raw_()) } } /// Wraps the reflector for `p` into the compartment of `cx`. pub fn WrapCallThisObject(cx: *mut JSContext, p: JSRef) -> *mut JSObject { - let mut obj = p.reflector().get_jsobject(); - assert!(obj.is_not_null()); + let mut obj = p.reflector().get_jsobject().root_ptr(); + assert!(obj.raw().is_not_null()); unsafe { - if JS_WrapObject(cx, &mut obj) == 0 { + if !JS_WrapObject(cx, obj.mut_handle()) { return ptr::null_mut(); } + return *obj.raw(); } - - return obj; } /// A class that performs whatever setup we need to safely make a call while @@ -136,7 +137,7 @@ pub struct CallSetup { /// The `JSContext` used for the call. cx: *mut JSContext, /// The exception handling used for the call. - _handling: ExceptionHandling + handling: ExceptionHandling } impl CallSetup { @@ -148,7 +149,7 @@ impl CallSetup { let cx = global.root_ref().get_cx(); CallSetup { cx: cx, - _handling: handling + handling: handling } } @@ -157,3 +158,15 @@ impl CallSetup { self.cx } } + +impl Drop for CallSetup { + fn drop(&mut self) { + if self.handling == ReportExceptions { + unsafe { + if JS_IsExceptionPending(self.cx) { + JS_ReportPendingException(self.cx); + } + } + } + } +} diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index 5b0ee22fe7bf..cf466c206b02 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -199,7 +199,7 @@ def getPerSignatureCall(signature, argConversionStartsAt=0, signatureIndex=0): code = ( "if argc < %d {\n" " throw_type_error(cx, \"Not enough arguments to %s.\");\n" - " return 0;\n" + " return false;\n" "}" % (requiredArgs, methodName)) self.cgRoot.prepend( CGWrapper(CGGeneric(code), pre="\n", post="\n")) @@ -378,11 +378,11 @@ def pickFirstSignature(condition, filterLambda): CGSwitch("argcount", argCountCases, CGGeneric("throw_type_error(cx, \"Not enough arguments to %s.\");\n" - "return 0;\n" % methodName))) + "return false;\n" % methodName))) #XXXjdm Avoid unreachable statement warnings #overloadCGThings.append( # CGGeneric('fail!("We have an always-returning default case");\n' - # 'return 0;')) + # 'return false;')) self.cgRoot = CGWrapper(CGList(overloadCGThings, "\n"), pre="\n") @@ -499,7 +499,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None, # failureCode will prevent pending exceptions from being set in cases when # they really should be! if exceptionCode is None: - exceptionCode = "return 0;" + exceptionCode = "return false;" needsRooting = typeNeedsRooting(type, descriptorProvider) @@ -723,7 +723,7 @@ def wrapObjectTemplate(templateBody, isDefinitelyObject, type, if invalidEnumValueFatal: handleInvalidEnumValueCode = exceptionCode else: - handleInvalidEnumValueCode = "return 1;" + handleInvalidEnumValueCode = "return true;" template = ( "match FindEnumStringIndex(cx, ${val}, %(values)s) {\n" @@ -817,7 +817,7 @@ def wrapObjectTemplate(templateBody, isDefinitelyObject, type, declType = CGGeneric(typeName) template = ("match %s::new(cx, ${val}) {\n" " Ok(dictionary) => dictionary,\n" - " Err(_) => return 0,\n" + " Err(_) => return false,\n" "}" % typeName) return handleOptional(template, declType, handleDefaultNull("%s::empty()" % typeName)) @@ -900,6 +900,7 @@ def instantiateJSToNativeConversionTemplate(templateBody, replacements, if needsRooting: rootBody = "let %s = %s.root();" % (declName, declName) result.append(CGGeneric(rootBody)) + result.append(CGGeneric("%s.init();" % declName)) result.append(CGGeneric("")) return result; @@ -1007,7 +1008,7 @@ def define(self): return self.converter.define() -def wrapForType(jsvalRef, result='result', successCode='return 1;'): +def wrapForType(jsvalRef, result='result', successCode='return true;'): """ Reflect a Rust value into JS. @@ -1176,12 +1177,14 @@ def __init__(self, descriptor, name, static): for m in methods] # FIXME Check for an existing iterator on the interface first. - if any(m.isGetter() and m.isIndexed() for m in methods): - self.regular.append({"name": 'iterator', - "methodInfo": False, - "nativeName": "JS_ArrayIterator", - "length": 0, - "flags": "JSPROP_ENUMERATE" }) + #if any(m.isGetter() and m.isIndexed() for m in methods): + # self.regular.append({"name": '@@iterator', + # "methodInfo": False, + # "selfHostedName": "ArrayValues", + # "length": 0, + # "flags": "JSPROP_ENUMERATE", + #"condition": MemberCondition(None, None) + # "pref": None }) def generateArray(self, array, name): if len(array) == 0: @@ -1203,9 +1206,9 @@ def stringDecl(m): decls = ''.join([stringDecl(m) for m in array]) return decls + self.generatePrefableArray( array, name, - ' JSFunctionSpec {name: &%s_name as *const u8 as *const libc::c_char, call: JSNativeWrapper {op: Some(%s), info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *const libc::c_char }', - ' JSFunctionSpec {name: 0 as *const libc::c_char, call: JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *const libc::c_char }', - 'JSFunctionSpec', + ' Struct_JSFunctionSpec {name: &%s_name as *const u8 as *const libc::c_char, call: Struct_JSNativeWrapper {op: Some(%s), info: %s}, nargs: %s, flags: %s as u16, selfHostedName: 0 as *const libc::c_char }', + ' Struct_JSFunctionSpec {name: 0 as *const libc::c_char, call: Struct_JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, nargs: 0, flags: 0, selfHostedName: 0 as *const libc::c_char }', + 'Struct_JSFunctionSpec', specData) class AttrDefiner(PropertyDefiner): @@ -1237,13 +1240,13 @@ def getter(attr): accessor = "genericGetter" jitinfo = "&%s_getterinfo" % attr.identifier.name - return ("JSPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}" + return ("Struct_JSNativeWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}" % {"info" : jitinfo, "native" : accessor}) def setter(attr): if attr.readonly: - return "JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}" + return "Struct_JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}" if self.static: accessor = 'set_' + attr.identifier.name @@ -1255,7 +1258,7 @@ def setter(attr): accessor = "genericSetter" jitinfo = "&%s_setterinfo" % attr.identifier.name - return ("JSStrictPropertyOpWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}" + return ("Struct_JSNativeWrapper {op: Some(%(native)s), info: %(info)s as *const JSJitInfo}" % {"info" : jitinfo, "native" : accessor}) @@ -1272,9 +1275,9 @@ def stringDecl(attr): return decls + self.generatePrefableArray( array, name, - ' JSPropertySpec { name: &%s_name as *const u8 as *const libc::c_char, tinyid: 0, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }', - ' JSPropertySpec { name: 0 as *const libc::c_char, tinyid: 0, flags: 0, getter: JSPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}, setter: JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo} }', - 'JSPropertySpec', + ' Struct_JSPropertySpec { name: &%s_name as *const u8 as *const libc::c_char, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }', + ' Struct_JSPropertySpec { name: 0 as *const libc::c_char, flags: 0, getter: Struct_JSNativeWrapper {op: None, info: 0 as *const JSJitInfo}, setter: Struct_JSNativeWrapper {op: None, info: 0 as *const JSJitInfo} }', + 'Struct_JSPropertySpec', specData) class ConstDefiner(PropertyDefiner): @@ -1436,9 +1439,11 @@ def __init__(self, descriptor): def define(self): traceHook = "Some(%s)" % TRACE_HOOK_NAME if self.descriptor.isGlobal(): + traceHook = "JS_GlobalObjectTraceHook" flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL" slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1" else: + traceHook = TRACE_HOOK_NAME flags = "0" slots = "1" return """ @@ -1446,64 +1451,59 @@ def define(self): static Class: DOMJSClass = DOMJSClass { base: js::Class { name: &Class_name as *const u8 as *const libc::c_char, - flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint), //JSCLASS_HAS_RESERVED_SLOTS(%s), + flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK as libc::c_uint) << JSCLASS_RESERVED_SLOTS_SHIFT as uint), //JSCLASS_HAS_RESERVED_SLOTS(%s), addProperty: Some(JS_PropertyStub), - delProperty: Some(JS_PropertyStub), + delProperty: Some(JS_DeletePropertyStub), getProperty: Some(JS_PropertyStub), setProperty: Some(JS_StrictPropertyStub), enumerate: Some(JS_EnumerateStub), resolve: Some(JS_ResolveStub), convert: Some(JS_ConvertStub), finalize: Some(%s), - checkAccess: None, call: None, hasInstance: None, construct: None, - trace: %s, + trace: Some(%s), + + spec: js::ClassSpec { + createConstructor: None, + createPrototype: None, + constructorFunctions: 0 as *const JSFunctionSpec, + prototypeFunctions: 0 as *const JSFunctionSpec, + finishInit: None, + }, ext: js::ClassExtension { - equality: 0 as *const u8, outerObject: %s, innerObject: None, iteratorObject: 0 as *const u8, - unused: 0 as *const u8, - isWrappedNative: 0 as *const u8, + isWrappedNative: false, + weakmapKeyDelegateOp: None, }, ops: js::ObjectOps { - lookupGeneric: 0 as *const u8, - lookupProperty: 0 as *const u8, - lookupElement: 0 as *const u8, - lookupSpecial: 0 as *const u8, - defineGeneric: 0 as *const u8, - defineProperty: 0 as *const u8, - defineElement: 0 as *const u8, - defineSpecial: 0 as *const u8, - getGeneric: 0 as *const u8, - getProperty: 0 as *const u8, - getElement: 0 as *const u8, - getElementIfPresent: 0 as *const u8, - getSpecial: 0 as *const u8, - setGeneric: 0 as *const u8, - setProperty: 0 as *const u8, - setElement: 0 as *const u8, - setSpecial: 0 as *const u8, - getGenericAttributes: 0 as *const u8, - getPropertyAttributes: 0 as *const u8, - getElementAttributes: 0 as *const u8, - getSpecialAttributes: 0 as *const u8, - setGenericAttributes: 0 as *const u8, - setPropertyAttributes: 0 as *const u8, - setElementAttributes: 0 as *const u8, - setSpecialAttributes: 0 as *const u8, - deleteProperty: 0 as *const u8, - deleteElement: 0 as *const u8, - deleteSpecial: 0 as *const u8, + lookupGeneric: None, + lookupProperty: None, + lookupElement: None, + defineGeneric: None, + defineProperty: None, + defineElement: None, + getGeneric: None, + getProperty: None, + getElement: None, + setGeneric: None, + setProperty: None, + setElement: None, + getGenericAttributes: None, + setGenericAttributes: None, + deleteProperty: None, + deleteElement: None, + watch: None, + unwatch: None, + slice: None, enumerate: 0 as *const u8, - typeOf: 0 as *const u8, thisObject: %s, - clear: 0 as *const u8, }, }, dom_class: %s @@ -1519,6 +1519,97 @@ def define(self): def str_to_const_array(s): return "[" + (", ".join(map(lambda x: "'" + x + "' as u8", list(s)) + ['0 as u8'])) + "]" + +class CGDOMProxyJSClass(CGThing): + """ + Generate a DOMJSClass for a given proxy descriptor + """ + def __init__(self, descriptor): + CGThing.__init__(self) + self.descriptor = descriptor + + def declare(self): + return "" + + def define(self): + flags = ["JSCLASS_IS_DOMJSCLASS", + "js::NON_NATIVE", + "js::JSCLASS_IS_PROXY", + "js::JSCLASS_IMPLEMENTS_BARRIERS", + "((js::PROXY_MINIMUM_SLOTS & js::JSCLASS_RESERVED_SLOTS_MASK as u32) << js::JSCLASS_RESERVED_SLOTS_SHIFT)"] + # We don't use an IDL annotation for JSCLASS_EMULATES_UNDEFINED because + # we don't want people ever adding that to any interface other than + # HTMLAllCollection. So just hardcode it here. + if self.descriptor.interface.identifier.name == "HTMLAllCollection": + flags.append("JSCLASS_EMULATES_UNDEFINED") + return """ +static Class_name: [u8, ..%i] = %s; +static mut Class: DOMJSClass = DOMJSClass { + base: js::Class { + name: &Class_name as *const u8 as *const libc::c_char, + flags: %s, + addProperty: Some(JS_PropertyStub), + delProperty: Some(JS_DeletePropertyStub), + getProperty: Some(JS_PropertyStub), + setProperty: Some(JS_StrictPropertyStub), + enumerate: Some(JS_EnumerateStub), + resolve: Some(JS_ResolveStub), + convert: Some(proxy_Convert), + finalize: Some(proxy_Finalize), + call: None, + hasInstance: Some(proxy_HasInstance), + construct: None, + trace: Some(proxy_Trace), + + spec: js::ClassSpec { + createConstructor: None, + createPrototype: None, + constructorFunctions: 0 as *const JSFunctionSpec, + prototypeFunctions: 0 as *const JSFunctionSpec, + finishInit: None, + }, + + ext: js::ClassExtension { + outerObject: None, + innerObject: Some(proxy_innerObject), + iteratorObject: 0 as *const u8, + isWrappedNative: false, + weakmapKeyDelegateOp: Some(proxy_WeakmapKeyDelegate), + }, + + ops: js::ObjectOps { + lookupGeneric: Some(proxy_LookupGeneric), + lookupProperty: Some(proxy_LookupProperty), + lookupElement: Some(proxy_LookupElement), + defineGeneric: Some(proxy_DefineGeneric), + defineProperty: Some(proxy_DefineProperty), + defineElement: Some(proxy_DefineElement), + getGeneric: Some(proxy_GetGeneric), + getProperty: Some(proxy_GetProperty), + getElement: Some(proxy_GetElement), + setGeneric: Some(proxy_SetGeneric), + setProperty: Some(proxy_SetProperty), + setElement: Some(proxy_SetElement), + getGenericAttributes: Some(proxy_GetGenericAttributes), + setGenericAttributes: Some(proxy_SetGenericAttributes), + deleteProperty: Some(proxy_DeleteProperty), + deleteElement: Some(proxy_DeleteElement), + watch: Some(proxy_Watch), + unwatch: Some(proxy_Unwatch), + slice: Some(proxy_Slice), + + enumerate: 0 as *const u8, + thisObject: None, + }, + }, + dom_class: %s +}; +""" % (len(self.descriptor.interface.identifier.name) + 1, + str_to_const_array(self.descriptor.interface.identifier.name), + " | ".join(flags), + CGIndenter(CGGeneric(DOMClass(self.descriptor))).define()) + + class CGPrototypeJSClass(CGThing): def __init__(self, descriptor): CGThing.__init__(self) @@ -1527,23 +1618,22 @@ def __init__(self, descriptor): def define(self): return """ static PrototypeClassName__: [u8, ..%s] = %s; -static PrototypeClass: JSClass = JSClass { +static PrototypeClass: JSClass = Struct_JSClass { name: &PrototypeClassName__ as *const u8 as *const libc::c_char, - flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT as uint, //JSCLASS_HAS_RESERVED_SLOTS(1) + flags: ((1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT) as libc::c_uint, //JSCLASS_HAS_RESERVED_SLOTS(1) addProperty: Some(JS_PropertyStub), - delProperty: Some(JS_PropertyStub), + delProperty: Some(JS_DeletePropertyStub), getProperty: Some(JS_PropertyStub), setProperty: Some(JS_StrictPropertyStub), enumerate: Some(JS_EnumerateStub), resolve: Some(JS_ResolveStub), convert: Some(JS_ConvertStub), finalize: None, - checkAccess: None, call: None, hasInstance: None, construct: None, trace: None, - reserved: [0 as *mut libc::c_void, ..40] + reserved: [0 as *mut libc::c_void, ..31] }; """ % (len(self.descriptor.interface.identifier.name + "Prototype") + 1, str_to_const_array(self.descriptor.interface.identifier.name + "Prototype")) @@ -1762,13 +1852,13 @@ def _decorators(self): if self.alwaysInline: decorators.append('#[inline(always)]') + if self.pub: + decorators.append('pub') + if self.extern: decorators.append('unsafe') decorators.append('extern') - if self.pub: - decorators.append('pub') - if not decorators: return '' return ' '.join(decorators) + ' ' @@ -1799,26 +1889,22 @@ def CreateBindingJSObject(descriptor, parent=None): assert not descriptor.isGlobal() create += """ let handler = RegisterBindings::proxy_handlers[PrototypeList::proxies::%s as uint]; -let mut private = PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void); -let obj = with_compartment(aCx, proto, || { - NewProxyObject(aCx, handler, - &private, - proto, %s, - ptr::null_mut(), ptr::null_mut()) -}); -assert!(obj.is_not_null()); +let private = PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void).root_value(); +private.init(); +let obj = NewProxyObject(aCx, handler, &mut Class.base, private.handle_(), *proto.raw(), *%s.raw()).root_ptr(); +obj.init(); +assert!(obj.raw().is_not_null()); """ % (descriptor.name, parent) else: if descriptor.isGlobal(): - create += "let obj = CreateDOMGlobal(aCx, &Class.base as *const js::Class as *const JSClass);\n" + create += "let obj = CreateDOMGlobal(aCx, &Class.base as *const js::Class as *const JSClass).root_ptr();\n" else: - create += ("let obj = with_compartment(aCx, proto, || {\n" - " JS_NewObject(aCx, &Class.base as *const js::Class as *const JSClass, &*proto, &*%s)\n" - "});\n" % parent) - create += """assert!(obj.is_not_null()); + create += ("let obj = JS_NewObject(aCx, &Class.base as *const js::Class as *const JSClass, proto.handle(), %s.handle()).root_ptr();\n" % parent) + create += """obj.init(); +assert!(obj.raw().is_not_null()); -JS_SetReservedSlot(obj, DOM_OBJECT_SLOT as u32, +JS_SetReservedSlot(*obj.raw(), DOM_OBJECT_SLOT as u32, PrivateValue(squirrel_away_unique(aObject) as *const libc::c_void)); """ return create @@ -1842,29 +1928,35 @@ def __init__(self, descriptor): def definition_body(self): if not self.descriptor.isGlobal(): return CGGeneric("""\ -let scope = aScope.reflector().get_jsobject(); -assert!(scope.is_not_null()); -assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0); +let _ar = JSAutoRequest::new(aCx); +let mut scope = aScope.reflector().get_jsobject().root_ptr(); +scope.init(); +assert!(scope.raw().is_not_null()); +assert!(((*JS_GetClass(*scope.raw())).flags & JSCLASS_IS_GLOBAL) != 0); -let proto = with_compartment(aCx, scope, || GetProtoObject(aCx, scope, scope)); -assert!(proto.is_not_null()); +let _ac = JSAutoCompartment::new(aCx, *scope.raw()); +let mut proto = GetProtoObject(aCx, *scope.raw(), *scope.raw()).root_ptr(); +proto.init(); +assert!(proto.raw().is_not_null()); %s -raw.reflector().set_jsobject(obj); +raw.reflector().set_jsobject(*obj.raw()); Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor, "scope")) else: return CGGeneric("""\ +let _ar = JSAutoRequest::new(aCx); %s -with_compartment(aCx, obj, || { - let proto = GetProtoObject(aCx, obj, obj); - JS_SetPrototype(aCx, obj, proto); +let _ac = JSAutoCompartment::new(aCx, *obj.raw()); + +let proto = GetProtoObject(aCx, *obj.raw(), *obj.raw()).root_ptr(); +proto.init(); +JS_SetPrototype(aCx, obj.handle(), proto.handle()); - raw.reflector().set_jsobject(obj); +raw.reflector().set_jsobject(*obj.raw()); - RegisterBindings::Register(aCx, obj); -}); +RegisterBindings::Register(aCx, *obj.raw()); Temporary::new(raw)""" % CreateBindingJSObject(self.descriptor)) @@ -1899,9 +1991,9 @@ class CGAbstractExternMethod(CGAbstractMethod): Abstract base class for codegen of implementation-only (no declaration) static methods. """ - def __init__(self, descriptor, name, returnType, args): + def __init__(self, descriptor, name, returnType, args, pub=False): CGAbstractMethod.__init__(self, descriptor, name, returnType, args, - inline=False, extern=True) + inline=False, extern=True, pub=pub) class PropertyArrays(): def __init__(self, descriptor): @@ -1962,27 +2054,27 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): """ def __init__(self, descriptor, properties): assert not descriptor.interface.isCallback() - args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal'), + args = [Argument('*mut JSContext', 'aCx'), Argument('*mut JSObject', 'aGlobal', mutable=True), Argument('*mut JSObject', 'aReceiver')] CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args) self.properties = properties def definition_body(self): protoChain = self.descriptor.prototypeChain if len(protoChain) == 1: - getParentProto = "JS_GetObjectPrototype(aCx, aGlobal)" + getParentProto = "JS_GetObjectPrototype(aCx, aGlobal.handle()).root_ptr()" else: parentProtoName = self.descriptor.prototypeChain[-2] - getParentProto = ("%s::GetProtoObject(aCx, aGlobal, aReceiver)" % + getParentProto = ("%s::GetProtoObject(aCx, *aGlobal.raw(), aReceiver).root_ptr()" % toBindingNamespace(parentProtoName)) - getParentProto = ("let parentProto: *mut JSObject = %s;\n" - "assert!(parentProto.is_not_null());\n") % getParentProto + getParentProto = ("let aGlobal = aGlobal.root_ptr();\n" + "aGlobal.init();\n" + "let parentProto = %s;\n" + "parentProto.init();\n" + "assert!(parentProto.raw().is_not_null());\n") % getParentProto if self.descriptor.concrete: - if self.descriptor.proxy: - domClass = "&Class" - else: - domClass = "&Class.dom_class" + domClass = "&Class.dom_class" else: domClass = "ptr::null()" @@ -2000,12 +2092,13 @@ def definition_body(self): else: constructor = 'None' - call = """return CreateInterfaceObjects2(aCx, aGlobal, aReceiver, parentProto, + call = """return CreateInterfaceObjects2(aCx, aGlobal.handle(), aReceiver, parentProto.handle(), &PrototypeClass, %s, %s, &sNativeProperties);""" % (constructor, domClass) return CGList([ + CGGeneric("let _ac = JSAutoCompartment::new(aCx, aGlobal);"), CGGeneric(getParentProto), CGGeneric(call % self.properties.variableNames()) ], "\n") @@ -2089,6 +2182,7 @@ def define(self): def definition_body(self): body = """\ let traps = ProxyTraps { + preventExtensions: None, getPropertyDescriptor: Some(getPropertyDescriptor), getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), defineProperty: Some(defineProperty), @@ -2103,19 +2197,17 @@ def definition_body(self): keys: ptr::null(), iterate: None, + isExtensible: None, call: None, construct: None, nativeCall: ptr::null(), hasInstance: None, - typeOf: None, objectClassIs: None, - obj_toString: Some(obj_toString), + className: Some(className), fun_toString: None, //regexp_toShared: ptr::null(), defaultValue: None, - iteratorNext: None, finalize: Some(%s), - getElementIfPresent: None, getPrototypeOf: None, trace: Some(%s) }; @@ -2196,6 +2288,8 @@ def __init__(self, errorResult, arguments, argsPre, returnType, call = CGWrapper(call, pre="%s." % object) call = CGList([call, CGWrapper(args, pre="(", post=")")]) + self.cgRoot.append(CGGeneric("debug!(\"calling %s::%s\");\n" % (descriptorProvider.interface.identifier.name, nativeMethodName))) + self.cgRoot.append(CGList([ CGGeneric("let result: "), result, @@ -2203,6 +2297,7 @@ def __init__(self, errorResult, arguments, argsPre, returnType, call, CGGeneric(";"), ])) + self.cgRoot.append(CGGeneric("debug!(\"finished calling %s::%s\");\n" % (descriptorProvider.interface.identifier.name, nativeMethodName))) if isFallible: if static: @@ -2222,7 +2317,8 @@ def __init__(self, errorResult, arguments, argsPre, returnType, "};\n" % (glob, errorResult))) if typeRetValNeedsRooting(returnType): - self.cgRoot.append(CGGeneric("let result = result.root();")) + self.cgRoot.append(CGGeneric("let result = result.root();\n" + "result.init();\n")) def define(self): return self.cgRoot.define() @@ -2278,7 +2374,7 @@ def __init__(self, returnType, argsPre, arguments, nativeMethodName, static, i in range(argConversionStartsAt, self.argCount)]) cgThings.append(CGCallGenerator( - ' false as JSBool' if self.isFallible() else None, + ' false' if self.isFallible() else None, self.getArguments(), self.argsPre, returnType, self.extendedAttributes, descriptor, nativeMethodName, static)) @@ -2392,7 +2488,7 @@ def __init__(self, argsPre, argType, nativeMethodName, descriptor, attr): setter=True) def wrap_return_value(self): # We have no return value - return "\nreturn 1;" + return "\nreturn true;" def getArgc(self): return "1" def getArgvDecl(self): @@ -2408,13 +2504,13 @@ class CGAbstractBindingMethod(CGAbstractExternMethod): CGThing which is already properly indented. """ def __init__(self, descriptor, name, args, unwrapFailureCode=None): - CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) if unwrapFailureCode is None: self.unwrapFailureCode = ( 'throw_type_error(cx, "\\"this\\" object does not ' 'implement interface %s.");\n' - 'return 0;' % descriptor.interface.identifier.name) + 'return false;' % descriptor.interface.identifier.name) else: self.unwrapFailureCode = unwrapFailureCode @@ -2425,11 +2521,12 @@ def definition_body(self): # consumption by FailureFatalCastableObjectUnwrapper. unwrapThis = str(CastableObjectUnwrapper( FakeCastableDescriptor(self.descriptor), - "obj", self.unwrapFailureCode)) + "*obj.raw()", self.unwrapFailureCode)) unwrapThis = CGGeneric( - "let obj: *mut JSObject = JS_THIS_OBJECT(cx, vp as *mut JSVal);\n" - "if obj.is_null() {\n" - " return false as JSBool;\n" + "let obj = JS_THIS_OBJECT(cx, vp).root_ptr();\n" + "obj.init();\n" + "if obj.raw().is_null() {\n" + " return false;\n" "}\n" "\n" "let this: JS<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis)) @@ -2453,7 +2550,7 @@ def __init__(self, descriptor, name): Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp'), ] - CGAbstractMethod.__init__(self, descriptor, name, "JSBool", args, extern=True) + CGAbstractMethod.__init__(self, descriptor, name, "bool", args, extern=True) def definition_body(self): return self.generate_code() @@ -2473,8 +2570,8 @@ def __init__(self, descriptor): def generate_code(self): return CGGeneric( - "let _info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" - "return CallJitMethodOp(_info, cx, obj, this.unsafe_get() as *mut libc::c_void, argc, vp);") + "let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" + "return CallJitMethodOp(info, cx, obj.handle(), this.unsafe_get() as *const libc::c_void, argc, vp);") class CGSpecializedMethod(CGAbstractExternMethod): """ @@ -2487,7 +2584,7 @@ def __init__(self, descriptor, method): args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')] - CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args) + CGAbstractExternMethod.__init__(self, descriptor, name, 'bool', args) def definition_body(self): nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, @@ -2495,7 +2592,8 @@ def definition_body(self): return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), self.descriptor, self.method), pre="let this = JS::from_raw(this);\n" - "let this = this.root();\n") + "let mut this = this.root();\n" + "this.init();\n") @staticmethod def makeNativeName(descriptor, method): @@ -2537,8 +2635,8 @@ def __init__(self, descriptor, lenientThis=False): def generate_code(self): return CGGeneric( - "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" - "return CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, vp);\n") + "let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" + "return CallJitGetterOp(info, cx, obj.handle(), this.unsafe_get() as *const libc::c_void, vp);\n") class CGSpecializedGetter(CGAbstractExternMethod): """ @@ -2552,7 +2650,7 @@ def __init__(self, descriptor, attr): Argument('JSHandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), Argument('*mut JSVal', 'vp') ] - CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) def definition_body(self): nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, @@ -2561,7 +2659,8 @@ def definition_body(self): return CGWrapper(CGGetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), pre="let this = JS::from_raw(this);\n" - "let this = this.root();\n") + "let this = this.root();\n" + "this.init();\n") @staticmethod def makeNativeName(descriptor, attr): @@ -2612,12 +2711,12 @@ def generate_code(self): return CGGeneric( "let mut undef = UndefinedValue();\n" "let argv: *mut JSVal = if argc != 0 { JS_ARGV(cx, vp) } else { &mut undef as *mut JSVal };\n" - "let info: *const JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" - "if CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *mut libc::c_void, argv) == 0 {\n" - " return 0;\n" + "let info = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp));\n" + "if !CallJitSetterOp(info, cx, obj.handle(), this.unsafe_get() as *const libc::c_void, argv) {\n" + " return false;\n" "}\n" "*vp = UndefinedValue();\n" - "return 1;") + "return true;") class CGSpecializedSetter(CGAbstractExternMethod): """ @@ -2631,7 +2730,7 @@ def __init__(self, descriptor, attr): Argument('JSHandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), Argument('*mut JSVal', 'argv')] - CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + CGAbstractExternMethod.__init__(self, descriptor, name, "bool", args) def definition_body(self): nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, @@ -2639,7 +2738,8 @@ def definition_body(self): return CGWrapper(CGSetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), pre="let this = JS::from_raw(this);\n" - "let this = this.root();\n") + "let this = this.root();\n" + "this.init();\n") @staticmethod def makeNativeName(descriptor, attr): @@ -2662,7 +2762,7 @@ def generate_code(self): "let argv = JS_ARGV(cx, vp);\n" "if (argc == 0) {\n" " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n" - " return 0;\n" + " return false;\n" "}\n" % self.attr.identifier.name) call = CGSetterCall([], self.attr.type, nativeName, self.descriptor, self.attr) @@ -2678,30 +2778,34 @@ def __init__(self, descriptor, member): self.member = member self.descriptor = descriptor - def defineJitInfo(self, infoName, opName, infallible): - protoID = "PrototypeList::id::%s as u32" % self.descriptor.name + def defineJitInfo(self, infoName, opName, opType, infallible, returnTypes): + protoID = "PrototypeList::id::%s as u16" % self.descriptor.name depth = self.descriptor.interface.inheritanceDepth() - failstr = "true" if infallible else "false" + failstr = "1" if infallible else "0" + retType = reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, "") return ("\n" "static %s: JSJitInfo = JSJitInfo {\n" " op: %s as *const u8,\n" " protoID: %s,\n" " depth: %s,\n" - " isInfallible: %s, /* False in setters. */\n" - " isConstant: false /* Only relevant for getters. */\n" - "};\n" % (infoName, opName, protoID, depth, failstr)) + " type_and_aliasSet: %s as u8 | (2 /*AliasEverything*/ << 4),\n" + " returnType: %s as u8,\n" + " infallible_and_isMovable_and_isInSlot_and_isTypedMethod_and_slotIndex: 0,\n" + "};\n" % (infoName, opName, protoID, depth, opType, retType)) def define(self): if self.member.isAttr(): getterinfo = ("%s_getterinfo" % self.member.identifier.name) getter = ("get_%s" % self.member.identifier.name) getterinfal = "infallible" in self.descriptor.getExtendedAttributes(self.member, getter=True) - result = self.defineJitInfo(getterinfo, getter, getterinfal) + result = self.defineJitInfo(getterinfo, getter, "Getter", getterinfal, + [self.member.type]) if not self.member.readonly: setterinfo = ("%s_setterinfo" % self.member.identifier.name) setter = ("set_%s" % self.member.identifier.name) # Setters are always fallible, since they have to do a typed unwrap. - result += self.defineJitInfo(setterinfo, setter, False) + result += self.defineJitInfo(setterinfo, setter, "Setter", False, + [BuiltinTypes[IDLBuiltinType.Types.void]]) return result if self.member.isMethod(): methodinfo = ("%s_methodinfo" % self.member.identifier.name) @@ -2721,10 +2825,90 @@ def define(self): # No arguments and infallible return boxing methodInfal = True - result = self.defineJitInfo(methodinfo, method, methodInfal) + result = self.defineJitInfo(methodinfo, method, "Method", methodInfal, + [s[0] for s in sigs]) return result raise TypeError("Illegal member type to CGPropertyJITInfo") + @staticmethod + def getJSReturnTypeTag(t): + if t.nullable(): + # Sometimes it might return null, sometimes not + return "JSVAL_TYPE_UNKNOWN" + if t.isVoid(): + # No return, every time + return "JSVAL_TYPE_UNDEFINED" + if t.isArray(): + # No idea yet + assert False + if t.isSequence(): + return "JSVAL_TYPE_OBJECT" + if t.isGeckoInterface(): + return "JSVAL_TYPE_OBJECT" + if t.isString(): + return "JSVAL_TYPE_STRING" + if t.isEnum(): + return "JSVAL_TYPE_STRING" + if t.isCallback(): + return "JSVAL_TYPE_OBJECT" + if t.isAny(): + # The whole point is to return various stuff + return "JSVAL_TYPE_UNKNOWN" + if t.isObject(): + return "JSVAL_TYPE_OBJECT" + if t.isSpiderMonkeyInterface(): + return "JSVAL_TYPE_OBJECT" + if t.isUnion(): + u = t.unroll() + if u.hasNullableType: + # Might be null or not + return "JSVAL_TYPE_UNKNOWN" + return reduce(CGMemberJITInfo.getSingleReturnType, + u.flatMemberTypes, "") + if t.isDictionary(): + return "JSVAL_TYPE_OBJECT" + if t.isDate(): + return "JSVAL_TYPE_OBJECT" + if not t.isPrimitive(): + raise TypeError("No idea what type " + str(t) + " is.") + tag = t.tag() + if tag == IDLType.Tags.bool: + return "JSVAL_TYPE_BOOLEAN" + if tag in [IDLType.Tags.int8, IDLType.Tags.uint8, + IDLType.Tags.int16, IDLType.Tags.uint16, + IDLType.Tags.int32]: + return "JSVAL_TYPE_INT32" + if tag in [IDLType.Tags.int64, IDLType.Tags.uint64, + IDLType.Tags.float, + IDLType.Tags.double]: + # These all use JS_NumberValue, which can return int or double. + # But TI treats "double" as meaning "int or double", so we're + # good to return JSVAL_TYPE_DOUBLE here. + return "JSVAL_TYPE_DOUBLE" + if tag != IDLType.Tags.uint32: + raise TypeError("No idea what type " + str(t) + " is.") + # uint32 is sometimes int and sometimes double. + return "JSVAL_TYPE_DOUBLE" + + @staticmethod + def getSingleReturnType(existingType, t): + type = CGMemberJITInfo.getJSReturnTypeTag(t) + if existingType == "": + # First element of the list; just return its type + return type + + if type == existingType: + return existingType + if ((type == "JSVAL_TYPE_DOUBLE" and + existingType == "JSVAL_TYPE_INT32") or + (existingType == "JSVAL_TYPE_DOUBLE" and + type == "JSVAL_TYPE_INT32")): + # Promote INT32 to DOUBLE as needed + return "JSVAL_TYPE_DOUBLE" + # Different types + return "JSVAL_TYPE_UNKNOWN" + + def getEnumValueName(value): # Some enum values can be empty strings. Others might have weird # characters in them. Deal with the former by returning "_empty", @@ -3508,7 +3692,7 @@ def __init__(self, descriptor, operation): template, _, declType, needsRooting = getJSToNativeConversionTemplate( argument.type, descriptor, treatNullAs=argument.treatNullAs) templateValues = { - "val": "(*desc).value", + "val": "desc.value", } self.cgRoot.prepend(instantiateJSToNativeConversionTemplate( template, templateValues, declType, argument.identifier.name, @@ -3571,7 +3755,7 @@ def __init__(self, descriptor): class CGProxyUnwrap(CGAbstractMethod): def __init__(self, descriptor): - args = [Argument('*mut JSObject', 'obj')] + args = [Argument('JSHandleObject', 'obj')] CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*const ' + descriptor.concreteType, args, alwaysInline=True) def definition_body(self): @@ -3579,14 +3763,15 @@ def definition_body(self): obj = js::UnwrapObject(obj); }*/ //MOZ_ASSERT(IsProxy(obj)); -let box_ = GetProxyPrivate(obj).to_private() as *const %s; +let box_ = GetProxyPrivate(*obj).to_private() as *const %s; return box_;""" % self.descriptor.concreteType) class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), Argument('bool', 'set'), - Argument('*mut JSPropertyDescriptor', 'desc')] + args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', 'proxy'), + Argument('JSHandleId', 'id'), + Argument('MutableHandle', 'desc', mutable=True), + Argument('u32', 'flags')] CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor", "bool", args) self.descriptor = descriptor @@ -3596,37 +3781,38 @@ def getBody(self): setOrIndexedGet = "" if indexedGetter or indexedSetter: - setOrIndexedGet += "let index = GetArrayIndexFromId(cx, id);\n" + setOrIndexedGet += "let index = GetArrayIndexFromId(cx, *id);\n" if indexedGetter: readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None) - fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly - templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor} + fillDescriptor = "FillPropertyDescriptor(&mut *desc, *proxy, %s);\nreturn true;" % readonly + templateValues = {'jsvalRef': 'desc.value', 'successCode': fillDescriptor} get = ("if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" + "}\n") if indexedSetter or self.descriptor.operations['NamedSetter']: - setOrIndexedGet += "if set != 0 {\n" + setOrIndexedGet += "if flags & 0x02 /*SET*/ == 0 {\n" if indexedSetter: setOrIndexedGet += (" if index.is_some() {\n" + " let index = index.unwrap();\n") if not 'IndexedCreator' in self.descriptor.operations: # FIXME need to check that this is a 'supported property index' assert False - setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" + + setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, *proxy, false);\n" + " return true;\n" + " }\n") if self.descriptor.operations['NamedSetter']: - setOrIndexedGet += " if RUST_JSID_IS_STRING(id) {\n" + setOrIndexedGet += " if RUST_JSID_IS_STRING(*id) {\n" if not 'NamedCreator' in self.descriptor.operations: # FIXME need to check that this is a 'supported property name' assert False - setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, proxy, false);\n" + + setOrIndexedGet += (" FillPropertyDescriptor(&mut *desc, *proxy, false);\n" + " return true;\n" + " }\n") setOrIndexedGet += "}" @@ -3636,44 +3822,49 @@ def getBody(self): "}") setOrIndexedGet += "\n\n" elif indexedGetter: - setOrIndexedGet += ("if !set {\n" + + setOrIndexedGet += ("if flags & 0x02 == 0 /*SET*/ {\n" + CGIndenter(CGGeneric(get)).define() + "}\n\n") namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter: readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None) - fillDescriptor = "FillPropertyDescriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly - templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor} + fillDescriptor = "FillPropertyDescriptor(&mut *desc, *proxy, %s);\nreturn true;" % readonly + templateValues = {'jsvalRef': 'desc.value', 'successCode': fillDescriptor} # Once we start supporting OverrideBuiltins we need to make # ResolveOwnProperty or EnumerateOwnProperties filter out named # properties that shadow prototype properties. namedGet = ("\n" + - "if !set && RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" + - " let name = jsid_to_str(cx, id);\n" + + "if flags & 0x02 /*SET*/ == 0 && RUST_JSID_IS_STRING(*id) && !HasPropertyOnPrototype(cx, proxy, id) {\n" + + " let name = jsid_to_str(cx, *id);\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" + "}\n") else: namedGet = "" - return setOrIndexedGet + """let expando: *mut JSObject = GetExpandoObject(proxy); + return setOrIndexedGet + """let expando = GetExpandoObject(*proxy).root_ptr(); +expando.init(); //if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { -if expando.is_not_null() { - let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED; - if JS_GetPropertyDescriptorById(cx, expando, id, flags, desc) == 0 { - return false; +if expando.raw().is_not_null() { + let flags = if flags & 0x02 /*SET*/ != 0 { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED; + { + let desc2 = desc.clone(); + if !JS_GetPropertyDescriptorById(cx, expando.handle(), id, flags, desc2) { + return false; + } } - if (*desc).obj.is_not_null() { + if desc.obj.is_not_null() { // Pretend the property lives on the wrapper. - (*desc).obj = proxy; + desc.obj = *proxy; return true; } } """ + namedGet + """ -(*desc).obj = ptr::null_mut(); +desc.obj = ptr::null_mut(); return true;""" def definition_body(self): @@ -3681,9 +3872,9 @@ def definition_body(self): class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), - Argument('*const JSPropertyDescriptor', 'desc')] + args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', 'proxy'), + Argument('JSHandleId', 'id'), + Argument('MutableHandle', 'desc', mutable=True)] CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args) self.descriptor = descriptor def getBody(self): @@ -3693,17 +3884,18 @@ def getBody(self): if indexedSetter: if not (self.descriptor.operations['IndexedCreator'] is indexedSetter): raise TypeError("Can't handle creator that's different from the setter") - set += ("let index = GetArrayIndexFromId(cx, id);\n" + + set += ("let index = GetArrayIndexFromId(cx, *id);\n" + "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() + " return true;\n" + "}\n") elif self.descriptor.operations['IndexedGetter']: - set += ("if GetArrayIndexFromId(cx, id).is_some() {\n" + + set += ("if GetArrayIndexFromId(cx, *id).is_some() {\n" + " return false;\n" + " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" + "}\n") % self.descriptor.name @@ -3712,19 +3904,21 @@ def getBody(self): if namedSetter: if not self.descriptor.operations['NamedCreator'] is namedSetter: raise TypeError("Can't handle creator that's different from the setter") - set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" + + set += ("if RUST_JSID_IS_STRING(*id) {\n" + " let name = jsid_to_str(cx, id);\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" + "}\n") elif self.descriptor.operations['NamedGetter']: - set += ("if RUST_JSID_IS_STRING(id) {\n" + - " let name = jsid_to_str(cx, id);\n" + + set += ("if RUST_JSID_IS_STRING(*id) {\n" + + " let name = jsid_to_str(cx, *id);\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + " if (found) {\n" " return false;\n" + @@ -3739,19 +3933,20 @@ def definition_body(self): class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), Argument('*mut bool', 'bp')] + args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', 'proxy'), + Argument('JSHandleId', 'id'), Argument('*mut bool', 'bp')] CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args) self.descriptor = descriptor def getBody(self): indexedGetter = self.descriptor.operations['IndexedGetter'] if indexedGetter: - indexed = ("let index = GetArrayIndexFromId(cx, id);\n" + + indexed = ("let index = GetArrayIndexFromId(cx, *id);\n" + "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" + " *bp = found;\n" + " return true;\n" + @@ -3761,11 +3956,12 @@ def getBody(self): namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter: - named = ("if RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" + - " let name = jsid_to_str(cx, id);\n" + + named = ("if RUST_JSID_IS_STRING(*id) && !HasPropertyOnPrototype(cx, proxy, id) {\n" + + " let name = jsid_to_str(cx, *id);\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" + " *bp = found;\n" " return true;\n" @@ -3774,11 +3970,10 @@ def getBody(self): else: named = "" - return indexed + """let expando: *mut JSObject = GetExpandoObject(proxy); -if expando.is_not_null() { - let mut b: JSBool = 1; - let ok = JS_HasPropertyById(cx, expando, id, &mut b) != 0; - *bp = b != 0; + return indexed + """let expando = GetExpandoObject(*proxy).root_ptr(); +expando.init(); +if expando.raw().is_not_null() { + let ok = JS_HasPropertyById(cx, expando.handle(), id, bp); if !ok || *bp { return ok; } @@ -3792,37 +3987,39 @@ def definition_body(self): class CGDOMJSProxyHandler_get(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('*mut JSObject', 'receiver'), Argument('jsid', 'id'), - Argument('*mut JSVal', 'vp')] + args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', 'proxy'), + Argument('JSHandleObject', 'receiver'), Argument('JSHandleId', 'id'), + Argument('JSMutableHandleValue', 'vp', mutable=True)] CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args) self.descriptor = descriptor def getBody(self): - getFromExpando = """let expando = GetExpandoObject(proxy); -if expando.is_not_null() { - let mut hasProp = 0; - if JS_HasPropertyById(cx, expando, id, &mut hasProp) == 0 { + getFromExpando = """let expando = GetExpandoObject(*proxy).root_ptr(); +expando.init(); +if expando.raw().is_not_null() { + let mut hasProp = false; + if !JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) { return false; } - if hasProp != 0 { - return JS_GetPropertyById(cx, expando, id, vp) != 0; + if hasProp { + return JS_GetPropertyById(cx, expando.handle(), id, vp); } }""" templateValues = { - 'jsvalRef': '*vp', + 'jsvalRef': '*vp.unnamed_field1', 'successCode': 'return true;', } indexedGetter = self.descriptor.operations['IndexedGetter'] if indexedGetter: - getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, id);\n" + + getIndexedOrExpando = ("let index = GetArrayIndexFromId(cx, *id);\n" + "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) getIndexedOrExpando += """ // Even if we don't have this index, we don't forward the @@ -3836,11 +4033,12 @@ def getBody(self): namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter and False: #XXXjdm unfinished - getNamed = ("if (JSID_IS_STRING(id)) {\n" + - " let name = jsid_to_str(cx, id);\n" + + getNamed = ("if (JSID_IS_STRING(*id)) {\n" + + " let name = jsid_to_str(cx, *id);\n" + " let this = UnwrapProxy(proxy);\n" + " let this = JS::from_raw(this);\n" + " let this = this.root();\n" + + " this.init();\n" + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "}\n") % (self.descriptor.concreteType) else: @@ -3851,59 +4049,46 @@ def getBody(self): %s let mut found = false; -if !GetPropertyOnPrototype(cx, proxy, id, &mut found, vp) { - return false; +{ + let vp2 = vp.clone(); + if !GetPropertyOnPrototype(cx, proxy, id, &mut found, Some(vp2)) { + return false; + } } if found { return true; } %s -*vp = UndefinedValue(); +*vp.unnamed_field1 = UndefinedValue(); return true;""" % (getIndexedOrExpando, getNamed) def definition_body(self): return CGGeneric(self.getBody()) -class CGDOMJSProxyHandler_obj_toString(CGAbstractExternMethod): + +class CGDOMJSProxyHandler_className(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy')] - CGAbstractExternMethod.__init__(self, descriptor, "obj_toString", "*mut JSString", args) + args = [Argument('*mut JSContext', 'cx'), + Argument('JSHandleObject', 'proxy')] + CGAbstractExternMethod.__init__(self, descriptor, "className", "*const libc::c_char", args) self.descriptor = descriptor - def getBody(self): - stringifier = self.descriptor.operations['Stringifier'] - if stringifier: - nativeName = MakeNativeName(stringifier.identifier.name) - signature = stringifier.signatures()[0] - returnType = signature[0] - extendedAttributes = self.descriptor.getExtendedAttributes(stringifier) - infallible = 'infallible' in extendedAttributes - if not infallible: - error = CGGeneric( - ('ThrowMethodFailedWithDetails(cx, rv, "%s", "toString");\n' + - "return NULL;") % self.descriptor.interface.identifier.name) - else: - error = None - call = CGCallGenerator(error, [], "", returnType, extendedAttributes, self.descriptor, nativeName, False, object="UnwrapProxy(proxy)") - return call.define() + """ - -JSString* jsresult; -return xpc_qsStringToJsstring(cx, result, &jsresult) ? jsresult : NULL;""" - - return """let s = "%s".to_c_str(); - _obj_toString(cx, s.as_ptr())""" % self.descriptor.name def definition_body(self): - return CGGeneric(self.getBody()) + return CGGeneric('static ClassName: [u8, ..%d] = [%s];\n' + 'return &ClassName as *const u8 as *const libc::c_char;\n' % \ + (len(self.descriptor.name) + 1, + ', '.join(map(lambda x: "'" + x + "' as u8", self.descriptor.name) + ['0 as u8']))) + class CGAbstractClassHook(CGAbstractExternMethod): """ Meant for implementing JSClass hooks, like Finalize or Trace. Does very raw 'this' unwrapping as it assumes that the unwrapped type is always known. """ - def __init__(self, descriptor, name, returnType, args): + def __init__(self, descriptor, name, returnType, args, pub=False): CGAbstractExternMethod.__init__(self, descriptor, name, returnType, - args) + args, pub=pub) def definition_body_prologue(self): return CGGeneric("""\ @@ -3934,7 +4119,7 @@ class CGClassTraceHook(CGAbstractClassHook): def __init__(self, descriptor): args = [Argument('*mut JSTracer', 'trc'), Argument('*mut JSObject', 'obj')] CGAbstractClassHook.__init__(self, descriptor, TRACE_HOOK_NAME, 'void', - args) + args, pub=True) def generate_code(self): return CGGeneric("(*this).trace(%s);" % self.args[0].name) @@ -3946,7 +4131,7 @@ class CGClassConstructHook(CGAbstractExternMethod): def __init__(self, descriptor): args = [Argument('*mut JSContext', 'cx'), Argument('u32', 'argc'), Argument('*mut JSVal', 'vp')] CGAbstractExternMethod.__init__(self, descriptor, CONSTRUCT_HOOK_NAME, - 'JSBool', args) + 'bool', args) self._ctor = self.descriptor.interface.ctor() def define(self): @@ -3958,6 +4143,7 @@ def definition_body(self): preamble = CGGeneric("""\ let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object()); let global = global.root(); +global.init(); """) nativeName = MakeNativeName(self._ctor.identifier.name) callGenerator = CGMethodCall(["&global.root_ref()"], nativeName, True, @@ -3976,16 +4162,6 @@ def __init__(self, descriptor): def generate_code(self): return CGGeneric(finalizeHook(self.descriptor, self.name, self.args[0].name)) -class CGDOMJSProxyHandlerDOMClass(CGThing): - def __init__(self, descriptor): - CGThing.__init__(self) - self.descriptor = descriptor - - def define(self): - return """ -static Class: DOMClass = """ + DOMClass(self.descriptor) + """; - -""" class CGInterfaceTrait(CGThing): @@ -4171,10 +4347,10 @@ def __init__(self, descriptor): if descriptor.concrete: if descriptor.proxy: #cgThings.append(CGProxyIsProxy(descriptor)) + cgThings.append(CGDOMProxyJSClass(descriptor)) cgThings.append(CGProxyUnwrap(descriptor)) - cgThings.append(CGDOMJSProxyHandlerDOMClass(descriptor)) cgThings.append(CGDOMJSProxyHandler_getOwnPropertyDescriptor(descriptor)) - cgThings.append(CGDOMJSProxyHandler_obj_toString(descriptor)) + cgThings.append(CGDOMJSProxyHandler_className(descriptor)) cgThings.append(CGDOMJSProxyHandler_get(descriptor)) cgThings.append(CGDOMJSProxyHandler_hasOwn(descriptor)) if descriptor.operations['IndexedSetter'] or descriptor.operations['NamedSetter']: @@ -4320,7 +4496,7 @@ def memberInit(memberInfo): " } else {\n" " throw_type_error(cx, \"Value not an object.\");\n" " return Err(());\n" - " };\n" + " }.root_ptr();\n" " Ok(${selfName} {\n" "${initParent}" "${initMembers}" @@ -4368,7 +4544,7 @@ def indent(s): conversion = "Some(%s)" % conversion conversion = ( - "match get_dictionary_property(cx, object, \"%s\") {\n" + "match get_dictionary_property(cx, object.handle(), \"%s\") {\n" " Err(()) => return Err(()),\n" " Ok(Some(value)) => {\n" "%s\n" @@ -4494,37 +4670,51 @@ def __init__(self, config, prefix, webIDLFile): 'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}', 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}', 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}', - 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSID_VOID, JSJitInfo}', + 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSJitInfo}', 'js::{JSPROP_ENUMERATE, JSPROP_NATIVE_ACCESSORS, JSPROP_SHARED}', 'js::{JSRESOLVE_ASSIGNING, JSRESOLVE_QUALIFIED}', 'js::jsapi::{JS_CallFunctionValue, JS_GetClass, JS_GetGlobalForObject}', 'js::jsapi::{JS_GetObjectPrototype, JS_GetProperty, JS_GetPropertyById}', - 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot}', + 'js::jsapi::{JS_GetPropertyDescriptorById, JS_GetReservedSlot, Struct_JSClass}', 'js::jsapi::{JS_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}', - 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetPrototype}', - 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSBool, JSContext}', + 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetPrototype, JSHandleId}', + 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSContext, JSID_VOID}', 'js::jsapi::{JSClass, JSFreeOp, JSFunctionSpec, JSHandleObject, jsid}', - 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor, JS_ArrayIterator}', + 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor}', 'js::jsapi::{JSPropertyOpWrapper, JSPropertySpec, JS_PropertyStub}', + 'js::jsapi::{Struct_JSPropertySpec, Struct_JSFunctionSpec, Struct_JSNativeWrapper}', + 'js::jsapi::{Struct_JSStrictPropertyOpWrapper, Struct_JSPropertyOpWrapper}', 'js::jsapi::{JSStrictPropertyOpWrapper, JSString, JSTracer, JS_ConvertStub}', 'js::jsapi::{JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub}', - 'js::jsval::JSVal', + 'js::jsapi::{JSMutableHandleValue, JS_DeletePropertyStub, MutableHandle}', + 'js::jsapi::JS_GlobalObjectTraceHook', + 'js::jsfriendapi::{Getter, Setter, Method}', + 'js::jsval::{JSVal, JSVAL_TYPE_DOUBLE, JSVAL_TYPE_INT32, JSVAL_TYPE_UNDEFINED}', + 'js::jsval::{JSVAL_TYPE_BOOLEAN, JSVAL_TYPE_MAGIC, JSVAL_TYPE_STRING}', + 'js::jsval::{JSVAL_TYPE_NULL, JSVAL_TYPE_OBJECT, JSVAL_TYPE_UNKNOWN}', 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}', 'js::jsval::{NullValue, UndefinedValue}', - 'js::glue::{CallJitMethodOp, CallJitPropertyOp, CreateProxyHandler}', + 'js::glue::{CallJitMethodOp, CallJitGetterOp, CreateProxyHandler}', 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps}', - 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO}', - 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}', - 'js::rust::with_compartment', + 'js::glue::{RUST_FUNCTION_VALUE_TO_JITINFO, CallJitSetterOp}', + 'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING, CallFunctionValue}', + 'js::glue::{proxy_LookupGeneric, proxy_LookupProperty, proxy_LookupElement}', + 'js::glue::{proxy_DefineGeneric, proxy_DefineProperty, proxy_DefineElement}', + 'js::glue::{proxy_GetGeneric, proxy_SetGeneric, proxy_SetProperty, proxy_SetElement}', + 'js::glue::{proxy_GetGenericAttributes, proxy_SetGenericAttributes, proxy_DeleteProperty}', + 'js::glue::{proxy_DeleteElement, proxy_Trace, proxy_WeakmapKeyDelegate, proxy_Finalize}', + 'js::glue::{proxy_HasInstance, proxy_innerObject, proxy_Watch}', + 'js::glue::{proxy_Unwatch, proxy_Slice, proxy_Convert, proxy_GetProperty, proxy_GetElement}', + 'js::rust::{JSAutoRequest, JSAutoCompartment}', 'dom::types::*', 'dom::bindings', 'dom::bindings::global::GlobalRef', 'dom::bindings::global::global_object_for_js_object', - 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}', + 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary, RootablePointer}', 'dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}', 'dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}', - 'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2}', - 'dom::bindings::utils::ConstantSpec', + 'dom::bindings::js::RootableValue', + 'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2, ConstantSpec, dump_js_backtrace}', 'dom::bindings::utils::{dom_object_slot, DOM_OBJECT_SLOT, DOMClass}', 'dom::bindings::utils::{DOMJSClass, JSCLASS_DOM_GLOBAL}', 'dom::bindings::utils::{FindEnumStringIndex, GetArrayIndexFromId}', @@ -4550,7 +4740,7 @@ def __init__(self, config, prefix, webIDLFile): 'dom::bindings::error::throw_dom_exception', 'dom::bindings::error::throw_type_error', 'dom::bindings::proxyhandler', - 'dom::bindings::proxyhandler::{_obj_toString, defineProperty}', + 'dom::bindings::proxyhandler::{defineProperty}', 'dom::bindings::proxyhandler::{FillPropertyDescriptor, GetExpandoObject}', 'dom::bindings::proxyhandler::{delete_, getPropertyDescriptor}', 'dom::bindings::str::ByteString', @@ -4944,7 +5134,10 @@ def getMethodImpls(self, method): bodyWithThis = string.Template( setupCall+ - "let thisObjJS = WrapCallThisObject(s.GetContext(), thisObj);\n" + "let cx = s.GetContext();\n" + "let _ar = JSAutoRequest::new(cx);\n" + "let _ar = JSAutoCompartment::new(cx, self.parent.callback());\n" + "let thisObjJS = WrapCallThisObject(cx, thisObj);\n" "if thisObjJS.is_null() {\n" " return Err(FailureUnknown);\n" "}\n" @@ -5103,14 +5296,14 @@ def getImpl(self): "${returnResult}").substitute(replacements) return CGList([ CGGeneric(pre), - CGWrapper(CGIndenter(CGGeneric(body)), - pre="with_compartment(cx, self.parent.callback(), || {\n", - post="})") + CGWrapper(CGGeneric(body), + pre="let _ar = JSAutoRequest::new(cx);\n" + "let _ac = JSAutoCompartment::new(cx, self.parent.callback());\n") ], "\n").define() def getResultConversion(self): replacements = { - "val": "rval", + "val": "*rval", "declName": "rvalDecl", } @@ -5242,7 +5435,9 @@ def __init__(self, sig, name, descriptorProvider, needThisHandling, rethrowConte CallbackMember.__init__(self, sig, name, descriptorProvider, needThisHandling, rethrowContentException) def getRvalDecl(self): - return "let mut rval = UndefinedValue();\n" + return """let mut rval = UndefinedValue().root_value();\n +rval.init(); +let mut rval = rval.mut_handle_();\n""" def getCall(self): replacements = { @@ -5250,17 +5445,21 @@ def getCall(self): "getCallable": self.getCallableDecl() } if self.argCount > 0: - replacements["argv"] = "argv.as_mut_ptr()" + replacements["argv"] = "argv.as_ptr()" replacements["argc"] = "argc" else: replacements["argv"] = "nullptr" replacements["argc"] = "0" return string.Template("${getCallable}" + "callable.init();\n" "let ok = unsafe {\n" - " JS_CallFunctionValue(cx, ${thisObj}, callable,\n" - " ${argc}, ${argv}, &mut rval)\n" + " let thisObj = ${thisObj}.root_ptr();\n" + " thisObj.init();\n" + " let rval = rval.clone();\n" + " CallFunctionValue(cx, thisObj.handle(), callable.handle_(),\n" + " ${argc} as libc::size_t, ${argv}, rval)\n" "};\n" - "if ok == 0 {\n" + "if !ok {\n" " return Err(FailureUnknown);\n" "}\n").substitute(replacements) @@ -5273,7 +5472,7 @@ def getThisObj(self): return "aThisObj" def getCallableDecl(self): - return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n"; + return "let callable = ObjectValue(unsafe {&*self.parent.callback()}).root_value();\n"; class CallbackOperationBase(CallbackMethod): """ @@ -5299,16 +5498,16 @@ def getCallableDecl(self): getCallableFromProp = string.Template( 'match self.parent.GetCallableProperty(cx, "${methodName}") {\n' ' Err(_) => return Err(FailureUnknown),\n' - ' Ok(callable) => callable,\n' + ' Ok(callable) => callable.root_value(),\n' '}').substitute(replacements) if not self.singleOperation: return 'JS::Rooted callable(cx);\n' + getCallableFromProp return ( - 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n' + 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) };\n' 'let callable =\n' + CGIndenter( CGIfElseWrapper('isCallable', - CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'), + CGGeneric('unsafe { ObjectValue(&*self.parent.callback()).root_value() }'), CGGeneric(getCallableFromProp))).define() + ';\n') class CallbackOperation(CallbackOperationBase): diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 2afd91fd08b8..939e5dae3d45 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -4,24 +4,22 @@ //! Conversions of Rust values to and from `JSVal`. -use dom::bindings::js::{JS, JSRef, Root}; +use dom::bindings::js::{JS, JSRef, Root, RootableValue}; use dom::bindings::str::ByteString; use dom::bindings::utils::{Reflectable, Reflector}; use dom::bindings::utils::jsstring_to_str; use dom::bindings::utils::unwrap_jsmanaged; use servo_util::str::DOMString; -use js::jsapi::{JSBool, JSContext, JSObject}; -use js::jsapi::{JS_ValueToUint64, JS_ValueToInt64}; -use js::jsapi::{JS_ValueToECMAUint32, JS_ValueToECMAInt32}; -use js::jsapi::{JS_ValueToUint16, JS_ValueToNumber, JS_ValueToBoolean}; -use js::jsapi::{JS_ValueToString, JS_GetStringCharsAndLength}; +use js::jsapi::{JSContext, JSObject, JSHandleValue}; +use js::jsapi::{JS_GetStringCharsAndLength}; use js::jsapi::{JS_NewUCStringCopyN, JS_NewStringCopyN}; use js::jsapi::{JS_WrapValue}; use js::jsval::JSVal; use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value}; use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue}; -use js::glue::RUST_JS_NumberValue; +use js::glue::{RUST_JS_NumberValue, ToString, ToBoolean, ToNumber, ToUint16, ToInt32}; +use js::glue::{ToUint32, ToInt64, ToUint64}; use libc; use std::default; use std::slice; @@ -59,19 +57,22 @@ impl ToJSValConvertible for () { impl ToJSValConvertible for JSVal { fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - let mut value = *self; - if unsafe { JS_WrapValue(cx, &mut value) } == 0 { + let mut value = (*self).root_value(); + value.init(); + if unsafe { !JS_WrapValue(cx, value.mut_handle_()) } { fail!("JS_WrapValue failed."); } - value + *value.raw_() } } unsafe fn convert_from_jsval( cx: *mut JSContext, value: JSVal, - convert_fn: unsafe extern "C" fn(*mut JSContext, JSVal, *mut T) -> JSBool) -> Result { + convert_fn: unsafe extern "C" fn(*mut JSContext, JSHandleValue, *mut T) -> bool) -> Result { let mut ret = default::Default::default(); - if convert_fn(cx, value, &mut ret) == 0 { + let value = value.root_value(); + value.init(); + if !convert_fn(cx, value.handle_(), &mut ret) { Err(()) } else { Ok(ret) @@ -86,9 +87,10 @@ impl ToJSValConvertible for bool { } impl FromJSValConvertible<()> for bool { - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToBoolean) }; - result.map(|b| b != 0) + fn from_jsval(_cx: *mut JSContext, val: JSVal, _option: ()) -> Result { + let val = val.root_value(); + val.init(); + Ok(unsafe { ToBoolean(val.handle_()) }) } } @@ -100,7 +102,7 @@ impl ToJSValConvertible for i8 { impl FromJSValConvertible<()> for i8 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + let result = unsafe { convert_from_jsval(cx, val, ToInt32) }; result.map(|v| v as i8) } } @@ -113,7 +115,7 @@ impl ToJSValConvertible for u8 { impl FromJSValConvertible<()> for u8 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + let result = unsafe { convert_from_jsval(cx, val, ToInt32) }; result.map(|v| v as u8) } } @@ -126,7 +128,7 @@ impl ToJSValConvertible for i16 { impl FromJSValConvertible<()> for i16 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + let result = unsafe { convert_from_jsval(cx, val, ToInt32) }; result.map(|v| v as i16) } } @@ -139,7 +141,7 @@ impl ToJSValConvertible for u16 { impl FromJSValConvertible<()> for u16 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToUint16) } + unsafe { convert_from_jsval(cx, val, ToUint16) } } } @@ -151,7 +153,7 @@ impl ToJSValConvertible for i32 { impl FromJSValConvertible<()> for i32 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) } + unsafe { convert_from_jsval(cx, val, ToInt32) } } } @@ -163,7 +165,7 @@ impl ToJSValConvertible for u32 { impl FromJSValConvertible<()> for u32 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToECMAUint32) } + unsafe { convert_from_jsval(cx, val, ToUint32) } } } @@ -177,7 +179,7 @@ impl ToJSValConvertible for i64 { impl FromJSValConvertible<()> for i64 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToInt64) } + unsafe { convert_from_jsval(cx, val, ToInt64) } } } @@ -191,7 +193,7 @@ impl ToJSValConvertible for u64 { impl FromJSValConvertible<()> for u64 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToUint64) } + unsafe { convert_from_jsval(cx, val, ToUint64) } } } @@ -205,7 +207,7 @@ impl ToJSValConvertible for f32 { impl FromJSValConvertible<()> for f32 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) }; + let result = unsafe { convert_from_jsval(cx, val, ToNumber) }; result.map(|f| f as f32) } } @@ -220,7 +222,7 @@ impl ToJSValConvertible for f64 { impl FromJSValConvertible<()> for f64 { fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result { - unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) } + unsafe { convert_from_jsval(cx, val, ToNumber) } } } @@ -257,7 +259,9 @@ impl FromJSValConvertible for DOMString { if nullBehavior == Empty && value.is_null() { Ok("".to_string()) } else { - let jsstr = unsafe { JS_ValueToString(cx, value) }; + let value = value.root_value(); + value.init(); + let jsstr = unsafe { ToString(cx, value.handle_()) }; if jsstr.is_null() { debug!("JS_ValueToString failed"); Err(()) @@ -285,7 +289,9 @@ impl ToJSValConvertible for ByteString { impl FromJSValConvertible<()> for ByteString { fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result { unsafe { - let string = JS_ValueToString(cx, value); + let value = value.root_value(); + value.init(); + let string = ToString(cx, value.handle_()); if string.is_null() { debug!("JS_ValueToString failed"); return Err(()); @@ -309,11 +315,12 @@ impl ToJSValConvertible for Reflector { fn to_jsval(&self, cx: *mut JSContext) -> JSVal { let obj = self.get_jsobject(); assert!(obj.is_not_null()); - let mut value = ObjectValue(unsafe { &*obj }); - if unsafe { JS_WrapValue(cx, &mut value) } == 0 { + let mut value = ObjectValue(unsafe { &*obj }).root_value(); + value.init(); + if unsafe { !JS_WrapValue(cx, value.mut_handle_()) } { fail!("JS_WrapValue failed."); } - value + *value.raw_() } } @@ -369,10 +376,6 @@ impl> FromJSValConvertible<()> f impl ToJSValConvertible for *mut JSObject { fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - let mut wrapped = ObjectOrNullValue(*self); - unsafe { - assert!(JS_WrapValue(cx, &mut wrapped) != 0); - } - wrapped + ObjectOrNullValue(*self).to_jsval(cx) } } diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index e2e06d8205af..dd42413650fe 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -6,11 +6,12 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::global::GlobalRef; +use dom::bindings::js::RootableValue; use dom::domexception::DOMException; -use js::jsapi::{JSContext, JSBool, JSObject}; +use js::jsapi::{JSContext, JSObject}; use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException}; -use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR}; +use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, Struct_JSErrorFormatString, JSEXN_TYPEERR}; use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain}; use js::glue::{ReportError}; use js::rust::with_compartment; @@ -47,23 +48,23 @@ pub type ErrorResult = Fallible<()>; /// Set a pending DOM exception for the given `result` on `cx`. pub fn throw_dom_exception(cx: *mut JSContext, global: &GlobalRef, result: Error) { - assert!(unsafe { JS_IsExceptionPending(cx) } == 0); + assert!(unsafe { !JS_IsExceptionPending(cx) }); let exception = DOMException::new_from_error(global, result).root(); - let thrown = exception.to_jsval(cx); + let thrown = exception.to_jsval(cx).root_value(); unsafe { - JS_SetPendingException(cx, thrown); + JS_SetPendingException(cx, thrown.handle_()); } } /// Report a pending exception, thereby clearing it. pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { unsafe { - if JS_IsExceptionPending(cx) != 0 { + if JS_IsExceptionPending(cx) { let saved = JS_SaveFrameChain(cx); with_compartment(cx, obj, || { JS_ReportPendingException(cx); }); - if saved != 0 { + if saved { JS_RestoreFrameChain(cx); } } @@ -72,13 +73,13 @@ pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { /// Throw an exception to signal that a `JSVal` can not be converted to any of /// the types in an IDL union type. -pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) -> JSBool { - assert!(unsafe { JS_IsExceptionPending(cx) } == 0); +pub fn throw_not_in_union(cx: *mut JSContext, names: &'static str) -> bool { + assert!(unsafe { !JS_IsExceptionPending(cx) }); let message = format!("argument could not be converted to any of: {}", names); message.with_c_str(|string| { unsafe { ReportError(cx, string) }; }); - return 0; + return false; } /// Format string used to throw `TypeError`s. @@ -89,8 +90,7 @@ static ERROR_FORMAT_STRING_STRING: [libc::c_char, ..4] = [ 0 as libc::c_char, ]; -/// Format string struct used to throw `TypeError`s. -static ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString { +static ERROR_FORMAT_STRING: JSErrorFormatString = Struct_JSErrorFormatString { format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char, argCount: 1, exnType: JSEXN_TYPEERR as i16, diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index 291f22426cb8..a9f9c0b715f5 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -99,6 +99,13 @@ impl<'a> Reflectable for GlobalRef<'a> { } impl<'a, 'b> GlobalRoot<'a, 'b> { + pub fn init(&self) { + match *self { + WindowRoot(ref window) => window.init(), + WorkerRoot(ref worker) => worker.init(), + } + } + /// Obtain a safe reference to the global object that cannot outlive the /// lifetime of this root. pub fn root_ref<'c>(&'c self) -> GlobalRef<'c> { diff --git a/components/script/dom/bindings/init.rs b/components/script/dom/bindings/init.rs new file mode 100644 index 000000000000..21c06f614364 --- /dev/null +++ b/components/script/dom/bindings/init.rs @@ -0,0 +1,28 @@ +/* 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/. */ + +use dom::bindings::proxyhandler::dom_proxy_shadows; +use dom::bindings::utils::instance_class_matches_proto; +use js; +use js::jsapi::JSRuntime; +use js::jsfriendapi::{SetDOMCallbacks, DOMCallbacks}; +use js::glue::{GetProxyHandlerFamily, SetDOMProxyInformation}; + +static dom_callbacks: DOMCallbacks = DOMCallbacks { + instanceClassMatchesProto: Some(instance_class_matches_proto), +}; + +pub fn global_init() { + unsafe { + let family = GetProxyHandlerFamily(); + SetDOMProxyInformation(family, js::PROXY_EXTRA_SLOT + js::PROXY_PRIVATE_SLOT, + Some(dom_proxy_shadows)); + } +} + +pub fn init(rt: *mut JSRuntime) { + unsafe { + SetDOMCallbacks(rt, &dom_callbacks); + } +} diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index a0521b17535c..74e79a82a3bf 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -49,13 +49,23 @@ use dom::bindings::utils::{Reflector, Reflectable}; use dom::node::Node; use dom::xmlhttprequest::{XMLHttpRequest, TrustedXHRAddress}; use dom::worker::{Worker, TrustedWorkerAddress}; -use js::jsapi::JSObject; +use js::{ContextFriendFields, THING_ROOT_OBJECT, THING_ROOT_ID, THING_ROOT_VALUE}; +use js::THING_ROOT_STRING; +use js::glue::{insertObjectLinkedListElement, getPersistentRootedObjectList}; +use js::glue::{objectIsPoisoned, objectRelocate, objectNeedsPostBarrier, objectPostBarrier}; +use js::jsapi::{JSContext, JSObject, JS_IsInRequest, JS_GetRuntime, Handle, MutableHandle, jsid}; +use js::jsapi::{JSFunction, JSString, JSPropertyDescriptor}; +use js::jsval::JSVal; use layout_interface::TrustedNodeAddress; use script_task::StackRoots; +use libc; use std::cell::{Cell, RefCell}; +use std::default::Default; +use std::intrinsics::TypeId; use std::kinds::marker::ContravariantLifetime; use std::mem; +use std::ptr; /// A type that represents a JS-owned value that is rooted for the lifetime of this value. /// Importantly, it requires explicit rooting in order to interact with the inner value. @@ -64,8 +74,53 @@ use std::mem; #[allow(unrooted_must_root)] pub struct Temporary { inner: JS, - /// On-stack JS pointer to assuage conservative stack scanner - _js_ptr: *mut JSObject, + _js_ptr: Box, +} + +struct PersistentRootedObjectElement { + next: *mut PersistentRootedObjectElement, + prev: *mut PersistentRootedObjectElement, + _isSentinel: bool, + _ptr: *mut JSObject, +} + +impl PersistentRootedObjectElement { + fn new(ptr: *mut JSObject) -> PersistentRootedObjectElement { + PersistentRootedObjectElement { + next: ptr::null_mut(), + prev: ptr::null_mut(), + _isSentinel: false, + _ptr: ptr, + } + } + + fn init(&mut self) { + assert!(self.next.is_null()); + assert!(self.prev.is_null()); + let roots = StackRoots.get().unwrap(); + let rt = unsafe { JS_GetRuntime((**roots).cx) }; + self.next = self as *mut _; + self.prev = self as *mut _; + unsafe { + let list = getPersistentRootedObjectList(rt); + insertObjectLinkedListElement(list, self as *mut _ as *mut _); + } + } +} + +impl Drop for PersistentRootedObjectElement { + fn drop(&mut self) { + assert!(!self.next.is_null()); + assert!(!self.prev.is_null()); + if self.next != self as *mut _ { + unsafe { + (*self.prev).next = self.next; + (*self.next).prev = self.prev; + self.next = self as *mut _; + self.prev = self as *mut _; + } + } + } } impl PartialEq for Temporary { @@ -77,9 +132,12 @@ impl PartialEq for Temporary { impl Temporary { /// Create a new `Temporary` value from a JS-owned value. pub fn new(inner: JS) -> Temporary { + let mut js_ptr = + box PersistentRootedObjectElement::new(inner.reflector().get_jsobject()); + js_ptr.init(); Temporary { inner: inner, - _js_ptr: inner.reflector().get_jsobject(), + _js_ptr: js_ptr, } } @@ -187,7 +245,7 @@ impl, U: Reflectable> JS { impl Reflectable for JS { fn reflector<'a>(&'a self) -> &'a Reflector { unsafe { - (*self.unsafe_get()).reflector() + (*self.ptr).reflector() } } } @@ -196,56 +254,131 @@ impl JS { /// Returns an unsafe pointer to the interior of this JS object without touching the borrow /// flags. This is the only method that be safely accessed from layout. (The fact that this /// is unsafe is what necessitates the layout wrappers.) - pub unsafe fn unsafe_get(&self) -> *mut T { - mem::transmute_copy(&self.ptr) + pub unsafe fn unsafe_get(&self) -> *const T { + self.ptr } +} + +impl JS { + //XXXjdm It would be lovely if this could be private. + pub unsafe fn transmute_copy(&self) -> JS { + mem::transmute_copy(self) + } +} + +/// A mutable JS<T> value, with nullability represented by an enclosing +/// Option wrapper. Must be used in place of traditional internal mutability +/// to ensure that the proper GC barriers are enforced. +#[jstraceable] +pub struct MutNullableJS { + ptr: Cell>> +} + +impl, U: Reflectable> MutNullableJS { + pub fn new(initial: Option) -> MutNullableJS { + MutNullableJS { + ptr: Cell::new(initial.map(|initial| unsafe { initial.get_js() })) + } + } +} + +impl Default for MutNullableJS { + fn default() -> MutNullableJS { + MutNullableJS { + ptr: Cell::new(None::>) + } + } +} +impl MutNullableJS { /// Store an unrooted value in this field. This is safe under the assumption that JS /// values are only used as fields in DOM types that are reachable in the GC graph, /// so this unrooted value becomes transitively rooted for the lifetime of its new owner. - pub fn assign(&mut self, val: Temporary) { - *self = unsafe { val.inner() }; + pub fn assign>(&self, val: Option) { + let reflector = val.as_ref() + .map(|val| unsafe { val.get_js() }.reflector().get_jsobject()) + .unwrap_or(ptr::null_mut()); + unsafe { + assert!(!objectIsPoisoned(reflector)); + } + if unsafe { objectNeedsPostBarrier(reflector) } { + self.ptr.set(val.as_ref().map(|val| unsafe { val.get_js() })); + let raw_ptr = self.ptr.get() + .map(|val| val.reflector().rootable()) + .unwrap_or(ptr::null_mut()); + unsafe { + objectPostBarrier(raw_ptr); + } + } else if unsafe { objectNeedsPostBarrier(self.ptr + .get() + .map(|val| val.reflector().get_jsobject()) + .unwrap_or(ptr::null_mut())) } { + let raw_ptr = self.ptr.get() + .map(|val| val.reflector().rootable()) + .unwrap_or(ptr::null_mut()); + unsafe { + objectRelocate(raw_ptr); + self.ptr.set(val.map(|val| val.get_js())); + } + } else { + self.ptr.set(val.map(|val| unsafe { val.get_js() })); + } } -} -impl JS { - //XXXjdm It would be lovely if this could be private. - pub unsafe fn transmute(self) -> JS { - mem::transmute(self) + /// Set the inner value to null, without making API users jump through useless + /// type-ascription hoops. + pub fn clear(&self) { + self.assign(None::>); } - pub unsafe fn transmute_copy(&self) -> JS { - mem::transmute_copy(self) + /// Retrieve a copy of the current optional inner value. + pub fn get(&self) -> Option> { + self.ptr.get().map(|inner| Temporary::new(inner)) } -} + /// Retrieve a copy of the inner optional JS<T>. For use by layout, which + /// can't use safe types like Temporary. + pub unsafe fn get_inner(&self) -> Option> { + self.ptr.get() + } +} /// Get an `Option>` out of an `Option>` pub trait RootedReference { fn root_ref<'a>(&'a self) -> Option>; + fn init(&self); } impl<'a, 'b, T: Reflectable> RootedReference for Option> { fn root_ref<'a>(&'a self) -> Option> { self.as_ref().map(|root| root.root_ref()) } + + fn init(&self) { + self.as_ref().map(|root| root.init()); + } } /// Get an `Option>>` out of an `Option>>` pub trait OptionalRootedReference { fn root_ref<'a>(&'a self) -> Option>>; + fn init(&self); } impl<'a, 'b, T: Reflectable> OptionalRootedReference for Option>> { fn root_ref<'a>(&'a self) -> Option>> { self.as_ref().map(|inner| inner.root_ref()) } + + fn init(&self) { + self.as_ref().map(|inner| inner.init()); + } } /// Trait that allows extracting a `JS` value from a variety of rooting-related containers, /// which in general is an unsafe operation since they can outlive the rooted lifetime of the /// original value. -/*definitely not public*/ trait Assignable { +pub trait Assignable { unsafe fn get_js(&self) -> JS; } @@ -362,14 +495,16 @@ impl, U: Reflectable> TemporaryPushable for Vec> { /// An opaque, LIFO rooting mechanism. pub struct RootCollection { - roots: RefCell>, + roots: RefCell>, + cx: *mut JSContext, } impl RootCollection { /// Create an empty collection of roots - pub fn new() -> RootCollection { + pub fn new(cx: *mut JSContext) -> RootCollection { RootCollection { roots: RefCell::new(vec!()), + cx: cx, } } @@ -379,18 +514,31 @@ impl RootCollection { Root::new(self, unrooted) } + /// Create a new stack-bounded root that will not outlive this collection + fn new_raw_root<'a, 'b, S: RootableSMPointerType, T: RootablePointer>(&'a self, unrooted: T) -> Root<'a, 'b, libc::c_void, *mut S> { + Root::from_raw_ptr(self, unrooted.pointer()) + } + + /// Create a new stack-bounded root that will not outlive this collection + fn new_raw_value_root<'a, 'b, S: RootableSMValueType+'static>(&'a self, unrooted: S) -> Root<'a, 'b, libc::c_void, S> { + Root::from_raw_value(self, unrooted) + } + /// Track a stack-based root to ensure LIFO root ordering - fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) { + fn root<'a, 'b, T, S>(&self, untracked: &Root<'a, 'b, T, S>) { let mut roots = self.roots.borrow_mut(); - roots.push(untracked.js_ptr); - debug!(" rooting {:?}", untracked.js_ptr); + let ptr = &untracked.js_ptr as *const S as *mut libc::c_void; + roots.push(ptr); + debug!(" rooting {:p}", ptr); } /// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated - fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) { + fn unroot<'a, 'b, T, S>(&self, rooted: &Root<'a, 'b, T, S>) { let mut roots = self.roots.borrow_mut(); - debug!("unrooting {:?} (expecting {:?}", roots.last().unwrap(), rooted.js_ptr); - assert!(*roots.last().unwrap() == rooted.js_ptr); + let expected = &rooted.js_ptr as *const S as *mut libc::c_void; + let actual = *roots.last().unwrap(); + debug!("unrooting {:p} (expecting {:p})", actual, expected); + assert!(actual == expected); roots.pop().unwrap(); } } @@ -400,48 +548,224 @@ impl RootCollection { /// for the same JS value. `Root`s cannot outlive the associated `RootCollection` object. /// Attempts to transfer ownership of a `Root` via moving will trigger dynamic unrooting /// failures due to incorrect ordering. -pub struct Root<'a, 'b, T> { +pub struct Root<'a, 'b, T, S=*mut JSObject> { + stack: Cell<*mut *const *const libc::c_void>, + prev: Cell<*const *const libc::c_void>, + js_ptr: S, + _mCheckNotUsedAsTemporary_statementDone: bool, /// List that ensures correct dynamic root ordering root_list: &'a RootCollection, /// Reference to rooted value that must not outlive this container jsref: JSRef<'b, T>, - /// On-stack JS pointer to assuage conservative stack scanner - js_ptr: *mut JSObject, } -impl<'b, 'a: 'b, T: Reflectable> Root<'a, 'b, T> { +impl<'b, 'a: 'b, T: Reflectable, S: 'static> Root<'a, 'b, T, S> { + pub fn init(&self) { + assert!(self.stack.get().is_null()); + let this_id = TypeId::of::(); + let kind = if this_id == TypeId::of::<*mut JSObject>() { + THING_ROOT_OBJECT + } else if this_id == TypeId::of::<*mut JSString>() { + THING_ROOT_STRING + } else if this_id == TypeId::of::() { + THING_ROOT_VALUE + } else if this_id == TypeId::of::() { + THING_ROOT_ID + } else { + fail!("unknown type being rooted") + }; + unsafe { + assert!(JS_IsInRequest(JS_GetRuntime(self.root_list.cx))); + self.root_list.root(self); + let cxfields: *mut ContextFriendFields = mem::transmute(self.root_list.cx); + self.stack.set(&mut (*cxfields).thingGCRooters[kind as uint]); + self.prev.set(*self.stack.get()); + *self.stack.get() = self as *const Root as *const *const libc::c_void; + } + } +} + +pub trait RootableSMType {} +impl RootableSMType for JSVal {} +impl RootableSMType for JSObject {} +impl RootableSMType for jsid {} +impl RootableSMType for JSFunction {} +impl RootableSMType for JSString {} +impl RootableSMType for JSPropertyDescriptor {} + +pub trait RootableSMPointerType: RootableSMType {} +pub trait RootableSMValueType: RootableSMType {} + +impl RootableSMPointerType for JSObject {} +impl RootableSMPointerType for JSFunction {} +impl RootableSMPointerType for JSString {} + +impl RootableSMValueType for JSVal {} +impl RootableSMValueType for jsid {} +impl RootableSMValueType for JSPropertyDescriptor {} + +pub trait RootablePointer { + fn root_ptr<'a, 'b>(self) -> Root<'a, 'b, libc::c_void, *mut S>; + fn pointer(&self) -> *mut S; +} + +pub trait RootableValue { + fn root_value<'a, 'b>(self) -> Root<'a, 'b, libc::c_void, S>; +} + +fn raw_root_ptr_impl<'a, 'b, S: RootableSMPointerType, T: RootablePointer>(ptr: T) -> Root<'a, 'b, libc::c_void, *mut S> { + let collection = StackRoots.get().unwrap(); + unsafe { + (**collection).new_raw_root(ptr.pointer()) + } +} + +fn raw_root_value_impl<'a, 'b, S: RootableSMValueType+'static>(val: S) -> Root<'a, 'b, libc::c_void, S> { + let collection = StackRoots.get().unwrap(); + unsafe { + (**collection).new_raw_value_root(val) + } +} + +impl RootablePointer for *mut S { + fn root_ptr<'a, 'b>(self) -> Root<'a, 'b, libc::c_void, *mut S> { + raw_root_ptr_impl(self) + } + + fn pointer(&self) -> *mut S { + *self + } +} + +impl RootableValue for S { + fn root_value<'a, 'b>(self) -> Root<'a, 'b, libc::c_void, S> { + raw_root_value_impl(self) + } +} + +impl<'a, 'b, S: RootableSMPointerType> Root<'a, 'b, libc::c_void, *mut S> { + fn from_raw_ptr(roots: &'a RootCollection, unrooted: *mut S) -> Root<'a, 'b, libc::c_void, *mut S> { + Root { + stack: Cell::new(ptr::null_mut()), + prev: Cell::new(ptr::null()), + root_list: roots, + _mCheckNotUsedAsTemporary_statementDone: true, + jsref: JSRef { + ptr: ptr::null(), + chain: ContravariantLifetime, + }, + js_ptr: unrooted, + } + } +} + +impl<'a, 'b, S: RootableSMPointerType+'static> Root<'a, 'b, libc::c_void, *mut S> { + pub fn handle<'c>(&'c self) -> Handle<'c, *mut S> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } + Handle { + unnamed_field1: &self.js_ptr, + } + } + + pub fn mut_handle<'c>(&'c mut self) -> MutableHandle<'c, *mut S> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } + MutableHandle { + unnamed_field1: &mut self.js_ptr + } + } + + pub fn raw<'c>(&'c self) -> &'c *mut S { + &self.js_ptr + } +} + +impl<'a, 'b, S: RootableSMValueType+'static> Root<'a, 'b, libc::c_void, S> { + fn from_raw_value(roots: &'a RootCollection, unrooted: S) -> Root<'a, 'b, libc::c_void, S> { + Root { + stack: Cell::new(ptr::null_mut()), + prev: Cell::new(ptr::null()), + root_list: roots, + _mCheckNotUsedAsTemporary_statementDone: true, + jsref: JSRef { + ptr: ptr::null(), + chain: ContravariantLifetime, + }, + js_ptr: unrooted, + } + } + + pub fn handle_<'c>(&'c self) -> Handle<'c, S> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } + Handle { + unnamed_field1: &self.js_ptr, + } + } + + pub fn mut_handle_<'c>(&'c mut self) -> MutableHandle<'c, S> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } + MutableHandle { + unnamed_field1: &mut self.js_ptr, + } + } + + pub fn raw_<'c>(&'c self) -> &'c S { + &self.js_ptr + } +} + +impl<'a, 'b, T: Reflectable> Root<'a, 'b, T> { /// Create a new stack-bounded root for the provided JS-owned value. /// It cannot not outlive its associated `RootCollection`, and it contains a `JSRef` /// which cannot outlive this new `Root`. fn new(roots: &'a RootCollection, unrooted: &JS) -> Root<'a, 'b, T> { - let root = Root { + Root { + stack: Cell::new(ptr::null_mut()), + prev: Cell::new(ptr::null()), root_list: roots, + _mCheckNotUsedAsTemporary_statementDone: true, jsref: JSRef { - ptr: unrooted.ptr.clone(), + ptr: unrooted.ptr, chain: ContravariantLifetime, }, js_ptr: unrooted.reflector().get_jsobject(), - }; - roots.root(&root); - root + } } /// Obtain a safe reference to the wrapped JS owned-value that cannot outlive /// the lifetime of this root. pub fn root_ref<'b>(&'b self) -> JSRef<'b,T> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } self.jsref.clone() } } #[unsafe_destructor] -impl<'b, 'a: 'b, T: Reflectable> Drop for Root<'a, 'b, T> { +impl<'b, 'a: 'b, T: Reflectable, S> Drop for Root<'a, 'b, T, S> { fn drop(&mut self) { + unsafe { + assert!(self.stack.get() != ptr::null_mut(), "Uninitialized root encountered"); + assert!(*self.stack.get() == self as *mut Root as *const *const libc::c_void); + *self.stack.get() = self.prev.get(); + } self.root_list.unroot(self); } } impl<'b, 'a: 'b, T: Reflectable> Deref> for Root<'a, 'b, T> { fn deref<'c>(&'c self) -> &'c JSRef<'b, T> { + if self.stack.get() == ptr::null_mut() { + self.init(); + } &self.jsref } } @@ -506,3 +830,9 @@ impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> { self.deref().reflector() } } + +impl Reflectable for libc::c_void { + fn reflector<'a>(&'a self) -> &'a Reflector { + fail!("shouldn't try to reflect SM types") + } +} diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 3c26206c373e..0a8ad2ae5953 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -4,115 +4,98 @@ ///! Utilities for the implementation of JSAPI proxy handlers. +use dom::bindings::js::RootablePointer; use dom::bindings::utils::delete_property_by_id; -use dom::bindings::utils::is_dom_proxy; -use js::jsapi::{JSContext, jsid, JSPropertyDescriptor, JSObject, JSString, jschar}; -use js::jsapi::{JS_GetPropertyDescriptorById, JS_NewUCString, JS_malloc, JS_free}; -use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto}; -use js::jsapi::{JS_ReportErrorFlagsAndNumber, JS_StrictPropertyStub}; +use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject}; +use js::jsapi::{JS_GetPropertyDescriptorById, JS_AlreadyHasOwnPropertyById}; +use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto, MutableHandle}; +use js::jsapi::{JS_StrictPropertyStub, JSHandleObject, JSHandleId}; use js::jsapi::{JSREPORT_WARNING, JSREPORT_STRICT, JSREPORT_STRICT_MODE_ERROR}; +use js::jsapi::{JS_ReportErrorFlagsAndNumber}; +use js::jsfriendapi::{DOMProxyShadowsResult, DoesntShadowUnique, ShadowCheckFailed, Shadows}; +use js::jsfriendapi::DoesntShadow; use js::jsval::ObjectValue; use js::glue::GetProxyExtra; use js::glue::{GetObjectProto, GetObjectParent, SetProxyExtra, GetProxyHandler}; -use js::glue::InvokeGetOwnPropertyDescriptor; +use js::glue::{InvokeGetOwnPropertyDescriptor, InvokeHasOwn}; use js::glue::RUST_js_GetErrorMessage; use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY, JSRESOLVE_QUALIFIED}; use libc; use std::mem; use std::ptr; -use std::string; -use std::mem::size_of; static JSPROXYSLOT_EXPANDO: u32 = 0; -pub unsafe extern fn getPropertyDescriptor(cx: *mut JSContext, proxy: *mut JSObject, - id: jsid, set: bool, - desc: *mut JSPropertyDescriptor) - -> bool { - let handler = GetProxyHandler(proxy); - if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, set, desc) { - return false; +pub unsafe extern fn getPropertyDescriptor(cx: *mut JSContext, + proxy: JSHandleObject, + id: JSHandleId, + mut desc: MutableHandle, + flags: u32) -> bool { + let handler = GetProxyHandler(*proxy); + { + let desc2 = desc.clone(); + if !InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc2, flags) { + return false; + } } - if (*desc).obj.is_not_null() { + if desc.obj.is_not_null() { return true; } //let proto = JS_GetPrototype(proxy); - let proto = GetObjectProto(proxy); - if proto.is_null() { - (*desc).obj = ptr::null_mut(); + let mut proto = ptr::null_mut().root_ptr(); + assert!(GetObjectProto(cx, proxy, proto.mut_handle())); + if proto.raw().is_null() { + desc.obj = ptr::null_mut(); return true; } - JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc) != 0 + JS_GetPropertyDescriptorById(cx, proto.handle(), id, JSRESOLVE_QUALIFIED, desc) } -pub unsafe fn defineProperty_(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, - desc: *mut JSPropertyDescriptor) -> bool { +pub unsafe fn defineProperty_(cx: *mut JSContext, proxy: JSHandleObject, id: JSHandleId, + desc: MutableHandle) -> bool { static JSMSG_GETTER_ONLY: libc::c_uint = 160; //FIXME: Workaround for https://github.com/mozilla/rust/issues/13385 - let setter: *const libc::c_void = mem::transmute((*desc).setter); + let setter: *const libc::c_void = mem::transmute(desc.setter); let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub); - if ((*desc).attrs & JSPROP_GETTER) != 0 && setter == setter_stub { + if (desc.attrs & JSPROP_GETTER) != 0 && setter == setter_stub { return JS_ReportErrorFlagsAndNumber(cx, JSREPORT_WARNING | JSREPORT_STRICT | JSREPORT_STRICT_MODE_ERROR, Some(RUST_js_GetErrorMessage), ptr::null_mut(), - JSMSG_GETTER_ONLY) != 0; + JSMSG_GETTER_ONLY); } - let expando = EnsureExpandoObject(cx, proxy); + let expando = EnsureExpandoObject(cx, *proxy); if expando.is_null() { return false; } - return JS_DefinePropertyById(cx, expando, id, (*desc).value, (*desc).getter, - (*desc).setter, (*desc).attrs) != 0; + return JS_DefinePropertyById(cx, expando, *id, desc.value, + desc.getter, desc.setter, desc.attrs); } -pub unsafe extern fn defineProperty(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, - desc: *mut JSPropertyDescriptor) -> bool { +pub unsafe extern fn defineProperty(cx: *mut JSContext, proxy: JSHandleObject, id: JSHandleId, + desc: MutableHandle) -> bool { defineProperty_(cx, proxy, id, desc) } -pub unsafe extern fn delete_(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, +pub unsafe extern fn delete_(cx: *mut JSContext, proxy: JSHandleObject, id: JSHandleId, bp: *mut bool) -> bool { - let expando = EnsureExpandoObject(cx, proxy); - if expando.is_null() { + let expando = EnsureExpandoObject(cx, *proxy).root_ptr(); + if expando.raw().is_null() { return false; } - return delete_property_by_id(cx, expando, id, &mut *bp); -} - -pub fn _obj_toString(cx: *mut JSContext, className: *const libc::c_char) -> *mut JSString { - unsafe { - let name = string::raw::from_buf(className as *const i8 as *const u8); - let nchars = "[object ]".len() + name.len(); - let chars: *mut jschar = JS_malloc(cx, (nchars + 1) as libc::size_t * (size_of::() as libc::size_t)) as *mut jschar; - if chars.is_null() { - return ptr::null_mut(); - } - - let result = format!("[object {}]", name); - let result = result.as_slice(); - for (i, c) in result.chars().enumerate() { - *chars.offset(i as int) = c as jschar; - } - *chars.offset(nchars as int) = 0; - let jsstr = JS_NewUCString(cx, chars, nchars as libc::size_t); - if jsstr.is_null() { - JS_free(cx, chars as *mut libc::c_void); - } - jsstr - } + return delete_property_by_id(cx, expando.handle(), id, &mut *bp); } pub fn GetExpandoObject(obj: *mut JSObject) -> *mut JSObject { unsafe { - assert!(is_dom_proxy(obj)); + //XXXjdm it would be nice to assert that obj's class is a proxy class let val = GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); if val.is_undefined() { ptr::null_mut() @@ -124,19 +107,25 @@ pub fn GetExpandoObject(obj: *mut JSObject) -> *mut JSObject { pub fn EnsureExpandoObject(cx: *mut JSContext, obj: *mut JSObject) -> *mut JSObject { unsafe { - assert!(is_dom_proxy(obj)); - let mut expando = GetExpandoObject(obj); - if expando.is_null() { - expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), - ptr::null_mut(), - GetObjectParent(obj)); - if expando.is_null() { - return ptr::null_mut(); - } - - SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(&*expando)); + //XXXjdm it would be nice to assert that obj's class is a proxy class + let expando = GetExpandoObject(obj).root_ptr(); + expando.init(); + if !expando.raw().is_null() { + return *expando.raw(); } - return expando; + + let o = ptr::null_mut().root_ptr(); + o.init(); + let parent = GetObjectParent(obj).root_ptr(); + parent.init(); + let expando = JS_NewObjectWithGivenProto(cx, ptr::null(), o.handle(), parent.handle()).root_ptr(); + expando.init(); + if expando.raw().is_null() { + return ptr::null_mut(); + } + + SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(&**expando.raw())); + return *expando.raw(); } } @@ -145,5 +134,38 @@ pub fn FillPropertyDescriptor(desc: &mut JSPropertyDescriptor, obj: *mut JSObjec desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE; desc.getter = None; desc.setter = None; - desc.shortid = 0; +} + +pub unsafe extern fn dom_proxy_shadows(cx: *mut JSContext, + proxy: JSHandleObject, + id: JSHandleId) -> DOMProxyShadowsResult { + let v = GetProxyExtra(*proxy, JSPROXYSLOT_EXPANDO); + if v.is_object() { + let object = v.to_object().root_ptr(); + let mut hasOwn = false; + if !JS_AlreadyHasOwnPropertyById(cx, object.handle(), id, &mut hasOwn) { + return ShadowCheckFailed; + } + return if hasOwn { + Shadows + } else { + DoesntShadow + }; + } + + if v.is_undefined() { + return DoesntShadow; + } + + let mut hasOwn = false; + let handler = GetProxyHandler(*proxy); + if !InvokeHasOwn(handler, cx, proxy, id, &mut hasOwn) { + return ShadowCheckFailed; + } + + if hasOwn { + Shadows + } else { + DoesntShadowUnique + } } diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs new file mode 100644 index 000000000000..a97b16dfb1c4 --- /dev/null +++ b/components/script/dom/bindings/refcounted.rs @@ -0,0 +1,62 @@ +/* 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/. */ + +use dom::bindings::trace::trace_object; +use dom::bindings::utils::Reflectable; + +use js::jsapi::{JSTracer, JSObject}; + +use libc; +use std::cell::RefCell; +use std::collections::hashmap::HashMap; + +local_data_key!(pub LiveReferences: LiveDOMReferences) + +pub struct LiveDOMReferences { + table: RefCell> +} + +impl LiveDOMReferences { + pub fn initialize() { + LiveReferences.replace(Some(LiveDOMReferences { + table: RefCell::new(HashMap::new()), + })); + } + + pub fn addref(&self, obj: &T) { + let reflector = obj.reflector().get_jsobject(); + let mut table = self.table.borrow_mut(); + let entry = table.find_or_insert(reflector, 0); + *entry += 1; + } + + pub fn release(&self, obj: &T) { + let reflector = obj.reflector().get_jsobject(); + let refcount = { + let mut table = self.table.borrow_mut(); + let entry = table.get_mut(&reflector); + assert!(*entry != 0); + *entry -= 1; + *entry + }; + if refcount == 0 { + assert!(self.table.borrow_mut().remove(&reflector)); + } + } +} + +impl Drop for LiveDOMReferences { + fn drop(&mut self) { + assert!(self.table.borrow().keys().count() == 0); + } +} + +pub extern fn trace_refcounted_objects(tracer: *mut JSTracer, data: *mut libc::c_void) { + let table = data as *const RefCell>; + let table = unsafe { (*table).borrow() }; + for reflector in table.keys() { + trace_object(tracer, "", *reflector); + } +} + diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 0d87892aecf3..4054e13df80c 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -27,12 +27,15 @@ use dom::bindings::js::JS; use dom::bindings::utils::{Reflectable, Reflector}; -use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSTRACE_OBJECT}; +use dom::eventtarget::EventTarget; + +use js::jsapi::{JSObject, JSTracer, JS_CallValueTracer, JS_CallObjectTracer}; use js::jsval::JSVal; use libc; -use std::rc::Rc; use std::cell::{Cell, RefCell}; +use std::collections::HashSet; +use std::rc::Rc; use url::Url; use servo_util::atom::Atom; @@ -58,19 +61,18 @@ pub trait JSTraceable { } /// Trace a `JSVal`. -pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: JSVal) { - if !val.is_markable() { +pub fn trace_jsval(tracer: *mut JSTracer, description: &str, orig_val: JSVal) { + if !orig_val.is_markable() { return; } + debug!("tracing value {:s}", description); + let name = description.to_c_str(); + let mut val = orig_val; unsafe { - let name = description.to_c_str(); - (*tracer).debugPrinter = None; - (*tracer).debugPrintIndex = -1; - (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void; - debug!("tracing value {:s}", description); - JS_CallTracer(tracer, val.to_gcthing(), val.trace_kind()); + JS_CallValueTracer(tracer, &mut val, name.as_ptr()); } + assert!(val == orig_val); } /// Trace the `JSObject` held by `reflector`. @@ -80,15 +82,20 @@ pub fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Ref } /// Trace a `JSObject`. -pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: *mut JSObject) { +pub fn trace_object(tracer: *mut JSTracer, description: &str, orig_obj: *mut JSObject) { + debug!("tracing {:s}", description); + let name = description.to_c_str(); + let mut obj = orig_obj; unsafe { - let name = description.to_c_str(); - (*tracer).debugPrinter = None; - (*tracer).debugPrintIndex = -1; - (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void; - debug!("tracing {:s}", description); - JS_CallTracer(tracer, obj as *mut libc::c_void, JSTRACE_OBJECT); + JS_CallObjectTracer(tracer, &mut obj, name.as_ptr()); } + // FIXME: JS_CallObjectTracer could theoretically do something that can + // cause pointers to shuffle around. We need to pass a *mut *mut JSObject + // to JS_CallObjectTracer, but the Encodable trait doesn't allow us + // to obtain a mutable reference to self (and thereby self.cb); + // All we can do right now is scream loudly if this actually causes + // a problem in practice. + assert!(obj == orig_obj); } /// Encapsulates a type that cannot easily have `Encodable` derived automagically, @@ -174,7 +181,6 @@ impl JSTraceable for Traceable> { } } - impl JSTraceable for Cell { fn trace(&self, trc: *mut JSTracer) { self.get().trace(trc) @@ -239,3 +245,55 @@ impl<'a> JSTraceable for &'a str { } } +#[allow(unrooted_must_root)] +#[jstraceable] +pub struct RootedVec { + v: Vec> +} + +local_data_key!(pub RootedCollections: RefCell>) + +#[unsafe_destructor] +impl Drop for RootedVec { + fn drop(&mut self) { + let collections = RootedCollections.get(); + let mut collections = collections.as_ref().unwrap().borrow_mut(); + assert!(collections.remove(&(self as *mut RootedVec as *const ()))); + } +} + +impl RootedVec { + pub fn new() -> RootedVec { + RootedVec { + v: vec!() + } + } + + pub fn init(&self) { + let collections = RootedCollections.get(); + let mut collections = collections.as_ref().unwrap().borrow_mut(); + collections.insert(self as *const RootedVec as *const ()); + } +} + +impl Deref>> for RootedVec { + fn deref<'a>(&'a self) -> &'a Vec> { + &self.v + } +} + +impl DerefMut>> for RootedVec { + fn deref_mut<'a>(&'a mut self) -> &'a mut Vec> { + &mut self.v + } +} + +pub extern fn trace_collections(tracer: *mut JSTracer, data: *mut libc::c_void) { + let collections = data as *const RefCell>>; //XXXjdm + let collections = unsafe { (*collections).borrow() }; + for collection in collections.iter() { + unsafe { + let _ = (**collection).trace(tracer); + } + } +} diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index 1a48cfc08d2e..28efebaf1696 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -4,12 +4,13 @@ //! Various utilities to glue JavaScript and the DOM implementation together. +use dom::bindings::codegen::Bindings::WindowBinding; use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; use dom::bindings::conversions::IDLInterface; use dom::bindings::error::throw_type_error; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JS, Temporary, Root}; +use dom::bindings::js::{JS, Temporary, Root, RootablePointer, RootableValue}; use dom::bindings::trace::Untraceable; use dom::browsercontext; use dom::window; @@ -22,25 +23,25 @@ use std::mem; use std::cmp::PartialEq; use std::ptr; use std::slice; -use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily}; -use js::glue::{UnwrapObject, GetProxyHandlerExtra}; +//use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily}; +//use js::glue::{UnwrapObject, GetProxyHandlerExtra}; +use js::glue::{CompartmentOptions_SetVersion, CompartmentOptions_SetTraceGlobal, UnwrapObject}; use js::glue::{IsWrapper, RUST_JSID_TO_STRING, RUST_JSID_IS_INT}; -use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_INT}; -use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction}; +use js::glue::{RUST_JSID_IS_STRING, RUST_JSID_TO_INT, ToString, NewGlobalObject}; +use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction, JSVERSION_LATEST}; use js::jsapi::{JS_DefineProperties, JS_ForwardGetPropertyTo}; use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength}; -use js::jsapi::{JS_ObjectIsRegExp, JS_ObjectIsDate, JSHandleObject}; -use js::jsapi::JS_GetFunctionObject; +use js::jsapi::{JS_ObjectIsRegExp, JS_ObjectIsDate, JSHandleObject, JSMutableHandleValue}; +use js::jsapi::{JS_GetFunctionObject}; use js::jsapi::{JS_HasPropertyById, JS_GetPrototype}; use js::jsapi::{JS_GetProperty, JS_HasProperty}; use js::jsapi::{JS_DefineFunctions, JS_DefineProperty}; -use js::jsapi::{JS_ValueToString, JS_GetReservedSlot, JS_SetReservedSlot}; -use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass}; -use js::jsapi::{JSFunctionSpec, JSPropertySpec}; -use js::jsapi::{JS_NewGlobalObject, JS_InitStandardClasses}; +use js::jsapi::{JS_GetReservedSlot, JS_SetReservedSlot}; +use js::jsapi::{JSContext, JSObject, jsid, JSClass}; +use js::jsapi::{JSFunctionSpec, JSPropertySpec, JSHandleId}; +use js::jsapi::{JS_InitStandardClasses}; use js::jsapi::{JSString}; use js::jsapi::JS_DeletePropertyById2; -use js::jsfriendapi::JS_ObjectToOuterObject; use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; use js::jsval::JSVal; use js::jsval::{PrivateValue, ObjectValue, NullValue}; @@ -50,6 +51,13 @@ use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT}; use js::JSFUN_CONSTRUCTOR; use js; +pub type RustPropertyOp = Option bool>; +pub type RustStrictPropertyOp = Option bool>; + #[allow(raw_pointer_deriving)] #[jstraceable] pub struct GlobalStaticData { @@ -69,26 +77,10 @@ fn is_dom_class(clasp: *const JSClass) -> bool { } } -/// Returns whether `obj` is a DOM object implemented as a proxy. -pub fn is_dom_proxy(obj: *mut JSObject) -> bool { - unsafe { - (js_IsObjectProxyClass(obj) || js_IsFunctionProxyClass(obj)) && - IsProxyHandlerFamily(obj) - } -} - -/// Returns the index of the slot wherein a pointer to the reflected DOM object -/// is stored. -/// -/// Fails if `obj` is not a DOM object. pub unsafe fn dom_object_slot(obj: *mut JSObject) -> u32 { let clasp = JS_GetClass(obj); - if is_dom_class(&*clasp) { - DOM_OBJECT_SLOT as u32 - } else { - assert!(is_dom_proxy(obj)); - DOM_PROXY_OBJECT_SLOT as u32 - } + assert!(is_dom_class(clasp)); + DOM_OBJECT_SLOT as u32 } /// Get the DOM object from the given reflector. @@ -106,11 +98,6 @@ pub unsafe fn get_dom_class(obj: *mut JSObject) -> Result { let domjsclass: *const DOMJSClass = clasp as *const DOMJSClass; return Ok((*domjsclass).dom_class); } - if is_dom_proxy(obj) { - debug!("proxy dom object"); - let dom_class: *const DOMClass = GetProxyHandlerExtra(obj) as *const DOMClass; - return Ok(*dom_class); - } debug!("not a dom object"); return Err(()); } @@ -128,7 +115,7 @@ pub fn unwrap_jsmanaged(mut obj: *mut JSObject, let dom_class = get_dom_class(obj).or_else(|_| { if IsWrapper(obj) == 1 { debug!("found wrapper"); - obj = UnwrapObject(obj, /* stopAtOuter = */ 0, ptr::null_mut()); + obj = UnwrapObject(obj, /* stopAtOuter = */ false); if obj.is_null() { debug!("unwrapping security wrapper failed"); Err(()) @@ -176,7 +163,7 @@ pub fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString { /// string, or if the string does not contain valid UTF-16. pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString { unsafe { - assert!(RUST_JSID_IS_STRING(id) != 0); + assert!(RUST_JSID_IS_STRING(id)); jsstring_to_str(cx, RUST_JSID_TO_STRING(id)) } } @@ -186,7 +173,10 @@ pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString { // We use slot 0 for holding the raw object. This is safe for both // globals and non-globals. pub static DOM_OBJECT_SLOT: uint = 0; -static DOM_PROXY_OBJECT_SLOT: uint = js::JSSLOT_PROXY_PRIVATE as uint; + +#[allow(dead_code)] +#[static_assert] +static DOM_SLOT_IS_PROXY_PRIVATE: bool = DOM_OBJECT_SLOT == js::JSSLOT_PROXY_PRIVATE as uint; // NOTE: This is baked into the Ion JIT as 0 in codegen for LGetDOMProperty and // LSetDOMProperty. Those constants need to be changed accordingly if this value @@ -281,13 +271,13 @@ pub struct NativeProperties { /// A JSNative that cannot be null. pub type NonNullJSNative = - unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> JSBool; + unsafe extern "C" fn (arg1: *mut JSContext, arg2: c_uint, arg3: *mut JSVal) -> bool; /// Creates the *interface prototype object* and the *interface object* (if /// needed). /// Fails on JSAPI failure. -pub fn CreateInterfaceObjects2(cx: *mut JSContext, global: *mut JSObject, receiver: *mut JSObject, - protoProto: *mut JSObject, +pub fn CreateInterfaceObjects2(cx: *mut JSContext, global: JSHandleObject, receiver: *mut JSObject, + protoProto: JSHandleObject, protoClass: &'static JSClass, constructor: Option<(NonNullJSNative, &'static str, u32)>, domClass: *const DOMClass, @@ -315,58 +305,65 @@ pub fn CreateInterfaceObjects2(cx: *mut JSContext, global: *mut JSObject, receiv /// Creates the *interface object*. /// Fails on JSAPI failure. -fn CreateInterfaceObject(cx: *mut JSContext, global: *mut JSObject, receiver: *mut JSObject, +fn CreateInterfaceObject(cx: *mut JSContext, global: JSHandleObject, receiver: *mut JSObject, constructorNative: NonNullJSNative, ctorNargs: u32, proto: *mut JSObject, members: &'static NativeProperties, name: *const libc::c_char) { + let proto = proto.root_ptr(); + proto.init(); + let receiver = receiver.root_ptr(); + receiver.init(); unsafe { let fun = JS_NewFunction(cx, Some(constructorNative), ctorNargs, JSFUN_CONSTRUCTOR, global, name); assert!(fun.is_not_null()); - let constructor = JS_GetFunctionObject(fun); - assert!(constructor.is_not_null()); + let constructor = JS_GetFunctionObject(fun).root_ptr(); + constructor.init(); + assert!(constructor.raw().is_not_null()); match members.staticMethods { - Some(staticMethods) => DefineMethods(cx, constructor, staticMethods), + Some(staticMethods) => DefineMethods(cx, constructor.handle(), staticMethods), _ => (), } match members.staticAttrs { - Some(staticProperties) => DefineProperties(cx, constructor, staticProperties), + Some(staticProperties) => DefineProperties(cx, constructor.handle(), staticProperties), _ => (), } match members.consts { - Some(constants) => DefineConstants(cx, constructor, constants), + Some(constants) => DefineConstants(cx, constructor.handle(), constants), _ => (), } - if proto.is_not_null() { - assert!(JS_LinkConstructorAndPrototype(cx, constructor, proto) != 0); + if proto.raw().is_not_null() { + assert!(JS_LinkConstructorAndPrototype(cx, constructor.handle(), proto.handle())); } - let mut alreadyDefined = 0; - assert!(JS_AlreadyHasOwnProperty(cx, receiver, name, &mut alreadyDefined) != 0); + let mut alreadyDefined = false; + assert!(JS_AlreadyHasOwnProperty(cx, receiver.handle(), name, &mut alreadyDefined)); - if alreadyDefined == 0 { - assert!(JS_DefineProperty(cx, receiver, name, - ObjectValue(&*constructor), - None, None, 0) != 0); + let constructorval = ObjectValue(&**constructor.raw()).root_value(); + constructorval.init(); + if !alreadyDefined { + assert!(JS_DefineProperty(cx, receiver.handle(), name, + constructorval.handle_(), + 0, None, None)); } } } /// Defines constants on `obj`. /// Fails on JSAPI failure. -fn DefineConstants(cx: *mut JSContext, obj: *mut JSObject, constants: &'static [ConstantSpec]) { +fn DefineConstants(cx: *mut JSContext, obj: JSHandleObject, constants: &'static [ConstantSpec]) { for spec in constants.iter() { + let jsval = spec.get_value().root_value(); unsafe { - assert!(JS_DefineProperty(cx, obj, spec.name.as_ptr() as *const libc::c_char, - spec.get_value(), None, None, - JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT) != 0); + assert!(JS_DefineProperty(cx, obj, spec.name.as_ptr() as *const libc::c_char, jsval.handle_(), + JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT, + None, None)); } } } @@ -374,55 +371,56 @@ fn DefineConstants(cx: *mut JSContext, obj: *mut JSObject, constants: &'static [ /// Defines methods on `obj`. The last entry of `methods` must contain zeroed /// memory. /// Fails on JSAPI failure. -fn DefineMethods(cx: *mut JSContext, obj: *mut JSObject, methods: &'static [JSFunctionSpec]) { +fn DefineMethods(cx: *mut JSContext, obj: JSHandleObject, methods: &'static [JSFunctionSpec]) { unsafe { - assert!(JS_DefineFunctions(cx, obj, methods.as_ptr()) != 0); + assert!(JS_DefineFunctions(cx, obj, methods.as_ptr())); } } /// Defines attributes on `obj`. The last entry of `properties` must contain /// zeroed memory. /// Fails on JSAPI failure. -fn DefineProperties(cx: *mut JSContext, obj: *mut JSObject, properties: &'static [JSPropertySpec]) { +fn DefineProperties(cx: *mut JSContext, obj: JSHandleObject, properties: &'static [JSPropertySpec]) { unsafe { - assert!(JS_DefineProperties(cx, obj, properties.as_ptr()) != 0); + assert!(JS_DefineProperties(cx, obj, properties.as_ptr())); } } /// Creates the *interface prototype object*. /// Fails on JSAPI failure. -fn CreateInterfacePrototypeObject(cx: *mut JSContext, global: *mut JSObject, - parentProto: *mut JSObject, +fn CreateInterfacePrototypeObject(cx: *mut JSContext, global: JSHandleObject, + parentProto: JSHandleObject, protoClass: &'static JSClass, members: &'static NativeProperties) -> *mut JSObject { unsafe { - let ourProto = JS_NewObjectWithUniqueType(cx, protoClass, &*parentProto, &*global); - assert!(ourProto.is_not_null()); + let ourProto = JS_NewObjectWithUniqueType(cx, protoClass, parentProto, global).root_ptr(); + ourProto.init(); + assert!(ourProto.raw().is_not_null()); match members.methods { - Some(methods) => DefineMethods(cx, ourProto, methods), + Some(methods) => DefineMethods(cx, ourProto.handle(), methods), _ => (), } match members.attrs { - Some(properties) => DefineProperties(cx, ourProto, properties), + Some(properties) => DefineProperties(cx, ourProto.handle(), properties), _ => (), } match members.consts { - Some(constants) => DefineConstants(cx, ourProto, constants), + Some(constants) => DefineConstants(cx, ourProto.handle(), constants), _ => (), } - return ourProto; + return *ourProto.raw(); } } /// A throwing constructor, for those interfaces that have neither /// `NoInterfaceObject` nor `Constructor`. -pub unsafe extern fn ThrowingConstructor(cx: *mut JSContext, _argc: c_uint, _vp: *mut JSVal) -> JSBool { +pub unsafe extern fn ThrowingConstructor(cx: *mut JSContext, _argc: c_uint, _vp: *mut JSVal) -> bool { throw_type_error(cx, "Illegal constructor."); - return 0; + return false; } /// Construct and cache the ProtoOrIfaceArray for the given global. @@ -492,26 +490,29 @@ impl Reflector { } } -pub fn GetPropertyOnPrototype(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, found: *mut bool, - vp: *mut JSVal) -> bool { +pub fn GetPropertyOnPrototype(cx: *mut JSContext, proxy: JSHandleObject, id: JSHandleId, found: *mut bool, + vp: Option) -> bool { unsafe { //let proto = GetObjectProto(proxy); - let proto = JS_GetPrototype(proxy); - if proto.is_null() { + let mut proto = ptr::null_mut().root_ptr(); + if !JS_GetPrototype(cx, proxy, proto.mut_handle()) { + return false; + } + if proto.raw().is_null() { *found = false; return true; } - let mut hasProp = 0; - if JS_HasPropertyById(cx, proto, id, &mut hasProp) == 0 { + let mut hasProp = false; + if !JS_HasPropertyById(cx, proto.handle(), id, &mut hasProp) { return false; } - *found = hasProp != 0; - let no_output = vp.is_null(); - if hasProp == 0 || no_output { + *found = hasProp; + let no_output = vp.as_ref().map_or(true, |vp| vp.deref().is_null()); + if !hasProp || no_output { return true; } - JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp) != 0 + JS_ForwardGetPropertyTo(cx, proto.handle(), id, proxy, vp.unwrap()) } } @@ -519,7 +520,7 @@ pub fn GetPropertyOnPrototype(cx: *mut JSContext, proxy: *mut JSObject, id: jsid /// `jsid` is not an integer. pub fn GetArrayIndexFromId(_cx: *mut JSContext, id: jsid) -> Option { unsafe { - if RUST_JSID_IS_INT(id) != 0 { + if RUST_JSID_IS_INT(id) { return Some(RUST_JSID_TO_INT(id) as u32); } return None; @@ -547,13 +548,16 @@ pub fn FindEnumStringIndex(cx: *mut JSContext, v: JSVal, values: &[&'static str]) -> Result, ()> { unsafe { - let jsstr = JS_ValueToString(cx, v); - if jsstr.is_null() { + let v = v.root_value(); + v.init(); + let jsstr = ToString(cx, v.handle_()).root_ptr(); + jsstr.init(); + if jsstr.raw().is_null() { return Err(()); } let mut length = 0; - let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length); + let chars = JS_GetStringCharsAndLength(cx, *jsstr.raw(), &mut length); if chars.is_null() { return Err(()); } @@ -571,72 +575,80 @@ pub fn FindEnumStringIndex(cx: *mut JSContext, /// Returns `Err(())` on JSAPI failure (there is a pending exception), and /// `Ok(None)` if there was no property with the given name. pub fn get_dictionary_property(cx: *mut JSContext, - object: *mut JSObject, + object: JSHandleObject, property: &str) -> Result, ()> { use std::c_str::CString; - fn has_property(cx: *mut JSContext, object: *mut JSObject, property: &CString, - found: &mut JSBool) -> bool { - unsafe { - JS_HasProperty(cx, object, property.as_ptr(), found) != 0 + fn has_property(cx: *mut JSContext, object: JSHandleObject, + property: &CString) -> Result { + let mut found = false; + if unsafe { JS_HasProperty(cx, object, property.as_ptr(), &mut found) } { + Ok(found) + } else { + Err(()) } } - fn get_property(cx: *mut JSContext, object: *mut JSObject, property: &CString, - value: &mut JSVal) -> bool { + /*fn get_property(cx: *mut JSContext, object: JSHandleObject, property: &CString, + value: JSMutableHandleValue) -> bool { unsafe { JS_GetProperty(cx, object, property.as_ptr(), value) != 0 } - } + }*/ let property = property.to_c_str(); if object.is_null() { return Ok(None); } - let mut found: JSBool = 0; - if !has_property(cx, object, &property, &mut found) { - return Err(()); - } - - if found == 0 { + let found = try!(has_property(cx, object, &property)); + if !found { return Ok(None); } - let mut value = NullValue(); - if !get_property(cx, object, &property, &mut value) { + let mut value = NullValue().root_value(); + /*if !get_property(cx, object, &property, mut_value_handle(&mut value)) { + return Err(()); + }*/ + if unsafe { !JS_GetProperty(cx, object, property.as_ptr(), value.mut_handle_()) } { return Err(()); } - Ok(Some(value)) + Ok(Some(*value.raw_())) } -pub fn HasPropertyOnPrototype(cx: *mut JSContext, proxy: *mut JSObject, id: jsid) -> bool { +pub fn HasPropertyOnPrototype(cx: *mut JSContext, proxy: JSHandleObject, id: JSHandleId) -> bool { // MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler); let mut found = false; - return !GetPropertyOnPrototype(cx, proxy, id, &mut found, ptr::null_mut()) || found; + return !GetPropertyOnPrototype(cx, proxy, id, &mut found, None) || found; } /// Returns whether `obj` can be converted to a callback interface per IDL. pub fn IsConvertibleToCallbackInterface(cx: *mut JSContext, obj: *mut JSObject) -> bool { unsafe { - JS_ObjectIsDate(cx, obj) == 0 && JS_ObjectIsRegExp(cx, obj) == 0 + let obj = obj.root_ptr(); + !JS_ObjectIsDate(cx, obj.handle()) && !JS_ObjectIsRegExp(cx, obj.handle()) } } /// Create a DOM global object with the given class. pub fn CreateDOMGlobal(cx: *mut JSContext, class: *const JSClass) -> *mut JSObject { unsafe { - let obj = JS_NewGlobalObject(cx, class, ptr::null_mut()); - if obj.is_null() { + //XXXjdm need to trace the protoiface cache, too + let obj = NewGlobalObject(cx, class, ptr::null_mut(), 0 /*FireOnNewGlobalHook*/).root_ptr(); + obj.init(); + if obj.raw().is_null() { return ptr::null_mut(); } - with_compartment(cx, obj, || { - JS_InitStandardClasses(cx, obj); + with_compartment(cx, *obj.raw(), || { + CompartmentOptions_SetVersion(cx, JSVERSION_LATEST); + CompartmentOptions_SetTraceGlobal(cx, Some(WindowBinding::_trace)); + JS_InitStandardClasses(cx, obj.handle()); }); - initialize_global(obj); - obj + initialize_global(*obj.raw()); + *obj.raw() } } +/* /// Callback to outerize windows when wrapping. pub unsafe extern fn wrap_for_same_compartment(cx: *mut JSContext, obj: *mut JSObject) -> *mut JSObject { JS_ObjectToOuterObject(cx, obj) @@ -647,30 +659,28 @@ pub unsafe extern fn pre_wrap(cx: *mut JSContext, _scope: *mut JSObject, obj: *mut JSObject, _flags: c_uint) -> *mut JSObject { JS_ObjectToOuterObject(cx, obj) } +*/ /// Callback to outerize windows. -pub extern fn outerize_global(_cx: *mut JSContext, obj: JSHandleObject) -> *mut JSObject { - unsafe { - debug!("outerizing"); - let obj = *obj.unnamed_field1; - let win: Root = - unwrap_jsmanaged(obj, - IDLInterface::get_prototype_id(None::), - IDLInterface::get_prototype_depth(None::)) - .unwrap() - .root(); - win.deref().browser_context.deref().borrow().as_ref().unwrap().window_proxy() - } -} - -pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: *mut JSObject, - id: jsid, bp: &mut bool) -> bool { - let mut value = UndefinedValue(); - if JS_DeletePropertyById2(cx, object, id, &mut value) == 0 { +pub unsafe extern fn outerize_global(_cx: *mut JSContext, obj: JSHandleObject) -> *mut JSObject { + debug!("outerizing"); + let win: Root = + unwrap_jsmanaged(*obj, + IDLInterface::get_prototype_id(None::), + IDLInterface::get_prototype_depth(None::)) + .unwrap() + .root(); + win.deref().browser_context.deref().borrow().as_ref().unwrap().window_proxy() +} + +pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: JSHandleObject, + id: JSHandleId, bp: &mut bool) -> bool { + let mut found = false; + if !JS_DeletePropertyById2(cx, object, id, &mut found) { return false; } - *bp = value.to_boolean(); + *bp = found; return true; } @@ -751,3 +761,21 @@ pub fn xml_name_type(name: &str) -> XMLName { true => Name } } + +pub fn dump_js_backtrace(cx: *mut JSContext) { + extern { + fn js_DumpBacktrace(cx: *mut JSContext); + } + unsafe { + js_DumpBacktrace(cx); + } +} + +pub extern fn instance_class_matches_proto(protoObject: *mut JSObject, + protoID: u32, + depth: u32) -> bool { + match unsafe { get_dom_class(protoObject) } { + Ok(clasp) => clasp.interface_chain[depth as uint] as u32 == protoID, + _ => false, + } +} diff --git a/components/script/dom/browsercontext.rs b/components/script/dom/browsercontext.rs index 927078d4d302..ac4e37d6b0a8 100644 --- a/components/script/dom/browsercontext.rs +++ b/components/script/dom/browsercontext.rs @@ -2,16 +2,27 @@ * 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/. */ -use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary, RootablePointer}; use dom::bindings::trace::Traceable; -use dom::bindings::utils::Reflectable; +use dom::bindings::utils::{Reflectable}; use dom::document::Document; use dom::window::Window; -use js::jsapi::JSObject; -use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps}; +use js; +use js::jsapi::{JSObject, JS_PropertyStub, JS_StrictPropertyStub, JS_DeletePropertyStub}; +use js::jsapi::{JS_EnumerateStub, JS_ResolveStub, JSFunctionSpec}; +use js::jsval::PrivateValue; +use js::glue::{WrapperNew, CreateWrapperProxyHandler, ProxyTraps, SetProxyExtra}; +use js::glue::{proxy_LookupGeneric, proxy_LookupProperty, proxy_LookupElement}; +use js::glue::{proxy_DefineGeneric, proxy_DefineProperty, proxy_DefineElement}; +use js::glue::{proxy_GetGeneric, proxy_SetGeneric, proxy_SetProperty, proxy_SetElement}; +use js::glue::{proxy_GetGenericAttributes, proxy_SetGenericAttributes, proxy_DeleteProperty}; +use js::glue::{proxy_DeleteElement, proxy_Trace, proxy_WeakmapKeyDelegate, proxy_Finalize}; +use js::glue::{proxy_HasInstance, proxy_innerObject, proxy_Watch}; +use js::glue::{proxy_Unwatch, proxy_Slice, proxy_Convert, proxy_GetProperty, proxy_GetElement}; use js::rust::with_compartment; +use libc; use libc::c_void; use std::ptr; @@ -56,16 +67,80 @@ impl BrowserContext { let handler = js_info.as_ref().unwrap().dom_static.windowproxy_handler; assert!(handler.deref().is_not_null()); - let parent = win.deref().reflector().get_jsobject(); + let obj = win.deref().reflector().get_jsobject().root_ptr(); let cx = js_info.as_ref().unwrap().js_context.deref().deref().ptr; - let wrapper = with_compartment(cx, parent, || unsafe { - WrapperNew(cx, parent, *handler.deref()) + let wrapper = with_compartment(cx, *obj.raw(), || unsafe { + WrapperNew(cx, obj.handle(), obj.handle(), *handler.deref(), + &mut ProxyClass, true) }); assert!(wrapper.is_not_null()); + unsafe { + SetProxyExtra(wrapper, 0, PrivateValue(*obj.raw() as *const libc::c_void)); + } self.window_proxy = Traceable::new(wrapper); } } +static proxy_name: [u8, ..6] = ['P' as u8, 'r' as u8, 'o' as u8, 'x' as u8, 'y' as u8, 0]; +static mut ProxyClass: js::Class = js::Class { + name: &proxy_name as *const u8 as *const libc::c_char, + flags: js::NON_NATIVE | js::JSCLASS_IS_PROXY | js::JSCLASS_IMPLEMENTS_BARRIERS | + ((js::PROXY_MINIMUM_SLOTS & js::JSCLASS_RESERVED_SLOTS_MASK as u32) << js::JSCLASS_RESERVED_SLOTS_SHIFT), + addProperty: Some(JS_PropertyStub), + delProperty: Some(JS_DeletePropertyStub), + getProperty: Some(JS_PropertyStub), + setProperty: Some(JS_StrictPropertyStub), + enumerate: Some(JS_EnumerateStub), + resolve: Some(JS_ResolveStub), + convert: Some(proxy_Convert), + finalize: Some(proxy_Finalize), + call: None, + hasInstance: Some(proxy_HasInstance), + construct: None, + trace: Some(proxy_Trace), + + spec: js::ClassSpec { + createConstructor: None, + createPrototype: None, + constructorFunctions: 0 as *const JSFunctionSpec, + prototypeFunctions: 0 as *const JSFunctionSpec, + finishInit: None, + }, + + ext: js::ClassExtension { + outerObject: None, + innerObject: Some(proxy_innerObject), + iteratorObject: 0 as *const u8, + isWrappedNative: false, + weakmapKeyDelegateOp: Some(proxy_WeakmapKeyDelegate), + }, + + ops: js::ObjectOps { + lookupGeneric: Some(proxy_LookupGeneric), + lookupProperty: Some(proxy_LookupProperty), + lookupElement: Some(proxy_LookupElement), + defineGeneric: Some(proxy_DefineGeneric), + defineProperty: Some(proxy_DefineProperty), + defineElement: Some(proxy_DefineElement), + getGeneric: Some(proxy_GetGeneric), + getProperty: Some(proxy_GetProperty), + getElement: Some(proxy_GetElement), + setGeneric: Some(proxy_SetGeneric), + setProperty: Some(proxy_SetProperty), + setElement: Some(proxy_SetElement), + getGenericAttributes: Some(proxy_GetGenericAttributes), + setGenericAttributes: Some(proxy_SetGenericAttributes), + deleteProperty: Some(proxy_DeleteProperty), + deleteElement: Some(proxy_DeleteElement), + watch: Some(proxy_Watch), + unwatch: Some(proxy_Unwatch), + slice: Some(proxy_Slice), + + enumerate: 0 as *const u8, + thisObject: None, + }, +}; + #[jstraceable] #[must_root] pub struct SessionHistoryEntry { @@ -83,6 +158,7 @@ impl SessionHistoryEntry { } static proxy_handler: ProxyTraps = ProxyTraps { + preventExtensions: None, getPropertyDescriptor: None, getOwnPropertyDescriptor: None, defineProperty: None, @@ -97,19 +173,17 @@ static proxy_handler: ProxyTraps = ProxyTraps { keys: 0 as *const u8, iterate: None, + isExtensible: None, call: None, construct: None, nativeCall: 0 as *const u8, hasInstance: None, - typeOf: None, objectClassIs: None, - obj_toString: None, + className: None, fun_toString: None, - //regexp_toShared: 0 as *u8, + //regexp_toShared: 0 as *const u8, defaultValue: None, - iteratorNext: None, finalize: None, - getElementIfPresent: None, getPrototypeOf: None, trace: None }; diff --git a/components/script/dom/dedicatedworkerglobalscope.rs b/components/script/dom/dedicatedworkerglobalscope.rs index d043bde2b620..a2b1afe7a7b1 100644 --- a/components/script/dom/dedicatedworkerglobalscope.rs +++ b/components/script/dom/dedicatedworkerglobalscope.rs @@ -8,7 +8,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::InheritTypes::DedicatedWorkerGlobalScopeDerived; use dom::bindings::codegen::InheritTypes::{EventTargetCast, WorkerGlobalScopeCast}; use dom::bindings::global; -use dom::bindings::js::{JSRef, Temporary, RootCollection}; +use dom::bindings::js::{JSRef, Temporary, RootCollection, RootableValue}; use dom::bindings::trace::Untraceable; use dom::bindings::utils::{Reflectable, Reflector}; use dom::eventtarget::{EventTarget, EventTargetHelpers}; @@ -26,9 +26,10 @@ use script_task::StackRootTLS; use servo_net::resource_task::{ResourceTask, load_whole_resource}; use js::glue::JS_STRUCTURED_CLONE_VERSION; -use js::jsapi::{JSContext, JS_ReadStructuredClone, JS_WriteStructuredClone}; +use js::jsapi::JSContext; +use js::jsfriendapi::{JS_ReadStructuredClone, JS_WriteStructuredClone}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::Cx; +use js::rust::{Cx, JSAutoRequest, JSAutoCompartment}; use std::rc::Rc; use std::ptr; @@ -91,8 +92,6 @@ impl DedicatedWorkerGlobalScope { .native() .named(format!("Web Worker at {}", worker_url.serialize())) .spawn(proc() { - let roots = RootCollection::new(); - let _stack_roots_tls = StackRootTLS::new(&roots); let (url, source) = match load_whole_resource(&resource_task, worker_url.clone()) { Err(_) => { @@ -105,9 +104,15 @@ impl DedicatedWorkerGlobalScope { }; let (_js_runtime, js_context) = ScriptTask::new_rt_and_cx(); + let roots = RootCollection::new(js_context.ptr); + let _stack_roots_tls = StackRootTLS::new(&roots); + let mut _ar = Some(JSAutoRequest::new(js_context.deref().ptr)); + let global = DedicatedWorkerGlobalScope::new( worker_url, worker, js_context.clone(), resource_task, parent_sender, own_sender, receiver).root(); + let _ac = JSAutoCompartment::new(js_context.deref().ptr, global.reflector().get_jsobject()); + match js_context.evaluate_script( global.reflector().get_jsobject(), source, url.serialize(), 1) { Ok(_) => (), @@ -122,15 +127,15 @@ impl DedicatedWorkerGlobalScope { loop { match global.receiver.deref().recv_opt() { Ok(DOMMessage(data, nbytes)) => { - let mut message = UndefinedValue(); + let mut message = UndefinedValue().root_value(); unsafe { assert!(JS_ReadStructuredClone( js_context.ptr, data as *const u64, nbytes, - JS_STRUCTURED_CLONE_VERSION, &mut message, - ptr::null(), ptr::null_mut()) != 0); + JS_STRUCTURED_CLONE_VERSION, message.mut_handle_(), + ptr::null(), ptr::null_mut())); } - MessageEvent::dispatch_jsval(target, &global::Worker(scope), message); + MessageEvent::dispatch_jsval(target, &global::Worker(scope), *message.raw_()); global.delayed_release_worker(); }, Ok(XHRProgressMsg(addr, progress)) => { @@ -152,11 +157,14 @@ impl DedicatedWorkerGlobalScope { impl<'a> DedicatedWorkerGlobalScopeMethods for JSRef<'a, DedicatedWorkerGlobalScope> { fn PostMessage(self, cx: *mut JSContext, message: JSVal) { + let message = message.root_value(); let mut data = ptr::null_mut(); let mut nbytes = 0; + let transferable = UndefinedValue().root_value(); unsafe { - assert!(JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes, - ptr::null(), ptr::null_mut()) != 0); + assert!(JS_WriteStructuredClone(cx, message.handle_(), &mut data, &mut nbytes, + ptr::null(), ptr::null_mut(), + transferable.handle_())); } let ScriptChan(ref sender) = self.parent_sender; diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index 63f205206fa2..f957304d6611 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -21,8 +21,8 @@ use dom::bindings::error::{ErrorResult, Fallible, NotSupported, InvalidCharacter use dom::bindings::error::{HierarchyRequest, NamespaceError}; use dom::bindings::global::GlobalRef; use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable, TemporaryPushable}; -use dom::bindings::js::OptionalRootable; +use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalSettable}; +use dom::bindings::js::{TemporaryPushable, OptionalRootable}; use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::bindings::utils::{xml_name_type, InvalidXMLName, Name, QName}; @@ -32,7 +32,7 @@ use dom::documentfragment::DocumentFragment; use dom::documenttype::DocumentType; use dom::domimplementation::DOMImplementation; use dom::element::{Element, AttributeHandlers, get_attribute_parts}; -use dom::element::{HTMLHtmlElementTypeId, HTMLHeadElementTypeId, HTMLTitleElementTypeId}; +use dom::element::{HTMLHeadElementTypeId, HTMLTitleElementTypeId}; use dom::element::{HTMLBodyElementTypeId, HTMLFrameSetElementTypeId}; use dom::event::Event; use dom::eventtarget::{EventTarget, NodeTargetTypeId, EventTargetHelpers}; @@ -60,10 +60,12 @@ use servo_util::namespace; use servo_util::namespace::{Namespace, Null}; use servo_util::str::{DOMString, split_html_space_chars}; +use url::Url; + use std::collections::hashmap::HashMap; use std::ascii::StrAsciiExt; use std::cell::{Cell, RefCell}; -use url::Url; +use std::default::Default; use time; #[deriving(PartialEq)] @@ -80,7 +82,7 @@ pub struct Document { reflector_: Reflector, pub window: JS, idmap: Traceable>>>>, - implementation: Cell>>, + implementation: MutNullableJS, content_type: DOMString, last_modified: Traceable>>, pub encoding_name: Traceable>, @@ -242,6 +244,7 @@ impl<'a> DocumentHelpers<'a> for JSRef<'a, Document> { // FIXME https://github.com/mozilla/rust/issues/13195 // Use mangle() when it exists again. let root = self.GetDocumentElement().expect("The element is in the document, so there must be a document element.").root(); + root.init(); match idmap.find_mut(&id) { Some(elements) => { let new_node: JSRef = NodeCast::from_ref(element); @@ -289,7 +292,7 @@ impl Document { reflector_: Reflector::new(), window: JS::from_rooted(window), idmap: Traceable::new(RefCell::new(HashMap::new())), - implementation: Cell::new(None), + implementation: Default::default(), content_type: match content_type { Some(string) => string.clone(), None => match is_html_document { @@ -346,6 +349,7 @@ trait PrivateDocumentHelpers { impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> { fn createNodeList(self, callback: |node: JSRef| -> bool) -> Temporary { let window = self.window.root(); + window.init(); match self.GetDocumentElement().root() { None => { @@ -366,14 +370,9 @@ impl<'a> PrivateDocumentHelpers for JSRef<'a, Document> { } fn get_html_element(self) -> Option> { - match self.GetDocumentElement().root() { - Some(ref root) if { - let root: JSRef = NodeCast::from_ref(**root); - root.type_id() == ElementNodeTypeId(HTMLHtmlElementTypeId) - } => Some(Temporary::from_rooted(HTMLHtmlElementCast::to_ref(**root).unwrap())), - - _ => None, - } + self.GetDocumentElement().root().as_ref().and_then(|root| { + HTMLHtmlElementCast::to_ref(**root) + }).map(|elem| Temporary::from_rooted(elem)) } } @@ -383,7 +382,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { if self.implementation.get().is_none() { self.implementation.assign(Some(DOMImplementation::new(self))); } - Temporary::new(self.implementation.get().as_ref().unwrap().clone()) + self.implementation.get().unwrap() } // http://dom.spec.whatwg.org/#dom-document-url @@ -583,6 +582,7 @@ impl<'a> DocumentMethods for JSRef<'a, Document> { // http://dom.spec.whatwg.org/#dom-document-createevent fn CreateEvent(self, interface: DOMString) -> Fallible> { let window = self.window.root(); + window.init(); match interface.as_slice().to_ascii_lower().as_slice() { "uievents" | "uievent" => Ok(EventCast::from_temporary( diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index a4ae4734ffc6..6a56bf9729bc 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::ElementBinding; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; use dom::bindings::codegen::Bindings::NamedNodeMapBinding::NamedNodeMapMethods; use dom::bindings::codegen::InheritTypes::{ElementDerived, NodeCast}; -use dom::bindings::js::{JS, JSRef, Temporary, TemporaryPushable}; +use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, TemporaryPushable}; use dom::bindings::js::{OptionalSettable, OptionalRootable, Root}; use dom::bindings::trace::Traceable; use dom::bindings::utils::{Reflectable, Reflector}; @@ -40,7 +40,8 @@ use servo_util::namespace::{Namespace, Null}; use servo_util::str::DOMString; use std::ascii::StrAsciiExt; -use std::cell::{Cell, RefCell}; +use std::cell::RefCell; +use std::default::Default; use std::mem; #[jstraceable] @@ -52,8 +53,8 @@ pub struct Element { pub prefix: Option, pub attrs: RefCell>>, pub style_attribute: Traceable>>, - pub attr_list: Cell>>, - class_list: Cell>>, + pub attr_list: MutNullableJS, + class_list: MutNullableJS, } impl ElementDerived for EventTarget { @@ -157,8 +158,8 @@ impl Element { namespace: namespace, prefix: prefix, attrs: RefCell::new(vec!()), - attr_list: Cell::new(None), - class_list: Cell::new(None), + attr_list: Default::default(), + class_list: Default::default(), style_attribute: Traceable::new(RefCell::new(None)), } } @@ -323,9 +324,9 @@ pub trait AttributeHandlers { impl<'a> AttributeHandlers for JSRef<'a, Element> { fn get_attribute(self, namespace: Namespace, local_name: &str) -> Option> { let local_name = Atom::from_slice(local_name); - self.attrs.borrow().iter().map(|attr| attr.root()).find(|attr| { + self.attrs.borrow().iter().map(|attr| *attr.root()).find(|attr| { *attr.local_name() == local_name && attr.namespace == namespace - }).map(|x| Temporary::from_rooted(*x)) + }).map(|x| Temporary::from_rooted(x)) } fn set_attribute_from_parser(self, local_name: Atom, @@ -559,11 +560,11 @@ impl<'a> ElementMethods for JSRef<'a, Element> { // http://dom.spec.whatwg.org/#dom-element-classlist fn ClassList(self) -> Temporary { match self.class_list.get() { - Some(class_list) => Temporary::new(class_list), + Some(class_list) => class_list, None => { - let class_list = DOMTokenList::new(self, "class").root(); - self.class_list.assign(Some(class_list.deref().clone())); - Temporary::from_rooted(*class_list) + let class_list = DOMTokenList::new(self, "class"); + self.class_list.assign(Some(class_list)); + self.class_list.get().unwrap() } } } @@ -572,7 +573,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> { fn Attributes(self) -> Temporary { match self.attr_list.get() { None => (), - Some(ref list) => return Temporary::new(list.clone()), + Some(list) => return list, } let doc = { @@ -582,7 +583,7 @@ impl<'a> ElementMethods for JSRef<'a, Element> { let window = doc.deref().window.root(); let list = NamedNodeMap::new(*window, self); self.attr_list.assign(Some(list)); - Temporary::new(self.attr_list.get().as_ref().unwrap().clone()) + self.attr_list.get().unwrap() } // http://dom.spec.whatwg.org/#dom-element-getattribute @@ -915,7 +916,9 @@ impl<'a> VirtualMethods for JSRef<'a, Element> { match self.get_attribute(Null, "id").root() { Some(attr) => { + attr.init(); let doc = document_from_node(*self).root(); + doc.init(); let value = attr.deref().Value(); if !value.is_empty() { let value = Atom::from_slice(value.as_slice()); @@ -936,7 +939,9 @@ impl<'a> VirtualMethods for JSRef<'a, Element> { match self.get_attribute(Null, "id").root() { Some(attr) => { + attr.init(); let doc = document_from_node(*self).root(); + doc.init(); let value = attr.deref().Value(); if !value.is_empty() { let value = Atom::from_slice(value.as_slice()); diff --git a/components/script/dom/event.rs b/components/script/dom/event.rs index 69caf3f137c0..858ac31becd9 100644 --- a/components/script/dom/event.rs +++ b/components/script/dom/event.rs @@ -6,12 +6,13 @@ use dom::bindings::codegen::Bindings::EventBinding; use dom::bindings::codegen::Bindings::EventBinding::{EventConstants, EventMethods}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::js::{MutNullableJS, JSRef, Temporary}; use dom::bindings::trace::Traceable; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::eventtarget::EventTarget; use servo_util::str::DOMString; use std::cell::{Cell, RefCell}; +use std::default::Default; use time; @@ -40,8 +41,8 @@ pub enum EventTypeId { pub struct Event { pub type_id: EventTypeId, reflector_: Reflector, - pub current_target: Cell>>, - pub target: Cell>>, + pub current_target: MutNullableJS, + pub target: MutNullableJS, type_: Traceable>, pub phase: Traceable>, pub canceled: Traceable>, @@ -60,8 +61,8 @@ impl Event { Event { type_id: type_id, reflector_: Reflector::new(), - current_target: Cell::new(None), - target: Cell::new(None), + current_target: Default::default(), + target: Default::default(), phase: Traceable::new(Cell::new(PhaseNone)), type_: Traceable::new(RefCell::new("".to_string())), canceled: Traceable::new(Cell::new(false)), @@ -108,11 +109,11 @@ impl<'a> EventMethods for JSRef<'a, Event> { } fn GetTarget(self) -> Option> { - self.target.get().as_ref().map(|target| Temporary::new(target.clone())) + self.target.get() } fn GetCurrentTarget(self) -> Option> { - self.current_target.get().as_ref().map(|target| Temporary::new(target.clone())) + self.current_target.get() } fn DefaultPrevented(self) -> bool { @@ -158,7 +159,7 @@ impl<'a> EventMethods for JSRef<'a, Event> { self.stop_immediate.deref().set(false); self.canceled.deref().set(false); self.trusted.deref().set(false); - self.target.set(None); + self.target.clear(); *self.type_.deref().borrow_mut() = type_; self.bubbles.deref().set(bubbles); self.cancelable.deref().set(cancelable); diff --git a/components/script/dom/eventdispatcher.rs b/components/script/dom/eventdispatcher.rs index 0fa26d607155..3cbb370e1bc8 100644 --- a/components/script/dom/eventdispatcher.rs +++ b/components/script/dom/eventdispatcher.rs @@ -5,7 +5,8 @@ use dom::bindings::callback::ReportExceptions; use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::InheritTypes::{EventTargetCast, NodeCast, NodeDerived}; -use dom::bindings::js::{JS, JSRef, OptionalSettable, OptionalRootable, Root}; +use dom::bindings::js::{JS, JSRef, OptionalSettable, OptionalRootable}; +use dom::bindings::trace::RootedVec; use dom::eventtarget::{Capturing, Bubbling, EventTarget}; use dom::event::{Event, PhaseAtTarget, PhaseNone, PhaseBubbling, PhaseCapturing}; use dom::node::{Node, NodeHelpers}; @@ -26,28 +27,29 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, let type_ = event.Type(); //TODO: no chain if not participating in a tree - let mut chain: Vec> = if target.deref().is_node() { + let mut chain: RootedVec = RootedVec::new(); + chain.init(); + + if target.deref().is_node() { let target_node: JSRef = NodeCast::to_ref(target).unwrap(); - target_node.ancestors().map(|ancestor| { + for ancestor in target_node.ancestors() { let ancestor_target: JSRef = EventTargetCast::from_ref(ancestor); - JS::from_rooted(ancestor_target).root() - }).collect() - } else { - vec!() - }; + chain.push(JS::from_rooted(ancestor_target)); + } + } event.deref().phase.deref().set(PhaseCapturing); //FIXME: The "callback this value" should be currentTarget /* capturing */ - for cur_target in chain.as_slice().iter().rev() { + for cur_target in chain.as_slice().iter().rev().map(|node| node.root()) { let stopped = match cur_target.get_listeners_for(type_.as_slice(), Capturing) { Some(listeners) => { event.current_target.assign(Some(cur_target.deref().clone())); for listener in listeners.iter() { // Explicitly drop any exception on the floor. - let _ = listener.HandleEvent_(**cur_target, event, ReportExceptions); + let _ = listener.HandleEvent_(*cur_target, event, ReportExceptions); if event.deref().stop_immediate.deref().get() { break; @@ -86,13 +88,13 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, if event.deref().bubbles.deref().get() && !event.deref().stop_propagation.deref().get() { event.deref().phase.deref().set(PhaseBubbling); - for cur_target in chain.iter() { + for cur_target in chain.as_slice().iter().map(|node| node.root()) { let stopped = match cur_target.deref().get_listeners_for(type_.as_slice(), Bubbling) { Some(listeners) => { event.deref().current_target.assign(Some(cur_target.deref().clone())); for listener in listeners.iter() { // Explicitly drop any exception on the floor. - let _ = listener.HandleEvent_(**cur_target, event, ReportExceptions); + let _ = listener.HandleEvent_(*cur_target, event, ReportExceptions); if event.deref().stop_immediate.deref().get() { break; @@ -125,15 +127,9 @@ pub fn dispatch_event<'a, 'b>(target: JSRef<'a, EventTarget>, None => {} } - // Root ordering restrictions mean we need to unroot the chain entries - // in the same order they were rooted. - while chain.len() > 0 { - let _ = chain.pop(); - } - event.dispatching.deref().set(false); event.phase.deref().set(PhaseNone); - event.current_target.set(None); + event.current_target.clear(); !event.DefaultPrevented() } diff --git a/components/script/dom/eventtarget.rs b/components/script/dom/eventtarget.rs index 682379bfdce5..3e97ab5341d1 100644 --- a/components/script/dom/eventtarget.rs +++ b/components/script/dom/eventtarget.rs @@ -7,7 +7,7 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::Bindings::EventListenerBinding::EventListener; use dom::bindings::codegen::Bindings::EventTargetBinding::EventTargetMethods; use dom::bindings::error::{Fallible, InvalidState, report_pending_exception}; -use dom::bindings::js::JSRef; +use dom::bindings::js::{JSRef, RootablePointer}; use dom::bindings::trace::Traceable; use dom::bindings::utils::{Reflectable, Reflector}; use dom::event::Event; @@ -16,14 +16,16 @@ use dom::node::NodeTypeId; use dom::workerglobalscope::WorkerGlobalScopeId; use dom::xmlhttprequest::XMLHttpRequestId; use dom::virtualmethods::VirtualMethods; -use js::jsapi::{JS_CompileUCFunction, JS_GetFunctionObject, JS_CloneFunctionObject}; -use js::jsapi::{JSContext, JSObject}; use servo_util::str::DOMString; -use libc::{c_char, size_t}; -use std::cell::RefCell; -use std::ptr; + +use js::glue::CompileEventHandler; +use js::jsapi::{JS_CloneFunctionObject}; +use js::jsapi::{JSContext, JSObject}; +use js::rust::{JSAutoRequest, JSAutoCompartment}; use url::Url; +use libc::{c_char, size_t}; +use std::cell::RefCell; use std::collections::hashmap::HashMap; #[deriving(PartialEq)] @@ -177,6 +179,8 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { scope: *mut JSObject, ty: &str, source: DOMString) { + let scope = scope.root_ptr(); + scope.init(); let url = url.serialize().to_c_str(); let name = ty.to_c_str(); let lineno = 0; //XXXjdm need to get a real number here @@ -187,24 +191,27 @@ impl<'a> EventTargetHelpers for JSRef<'a, EventTarget> { static arg_names: [*const c_char, ..1] = [&arg_name as *const c_char]; let source: Vec = source.as_slice().utf16_units().collect(); + + let _ar = JSAutoRequest::new(cx); + let _ac = JSAutoCompartment::new(cx, *scope.raw()); let handler = unsafe { - JS_CompileUCFunction(cx, - ptr::null_mut(), - name.as_ptr(), - nargs, - &arg_names as *const *const i8 as *mut *const i8, - source.as_ptr(), - source.len() as size_t, - url.as_ptr(), - lineno) + CompileEventHandler(cx, + name.as_ptr(), + nargs, + arg_names.as_ptr(), + source.as_ptr(), + source.len() as size_t, + url.as_ptr(), + lineno).root_ptr() }; - if handler.is_null() { + handler.init(); + if handler.raw().is_null() { report_pending_exception(cx, self.reflector().get_jsobject()); return; } let funobj = unsafe { - JS_CloneFunctionObject(cx, JS_GetFunctionObject(handler), scope) + JS_CloneFunctionObject(cx, handler.handle(), scope.handle()) }; assert!(funobj.is_not_null()); self.set_event_handler_common(ty, Some(EventHandlerNonNull::new(funobj))); diff --git a/components/script/dom/htmlimageelement.rs b/components/script/dom/htmlimageelement.rs index 7ffe03eff811..4f37362634ed 100644 --- a/components/script/dom/htmlimageelement.rs +++ b/components/script/dom/htmlimageelement.rs @@ -92,7 +92,7 @@ pub trait LayoutHTMLImageElementHelpers { impl LayoutHTMLImageElementHelpers for JS { unsafe fn image(&self) -> Option { - (*self.unsafe_get()).image.borrow().clone() + (*self.unsafe_get()).image.deref().borrow().clone() } } diff --git a/components/script/dom/mouseevent.rs b/components/script/dom/mouseevent.rs index 9efc5a97d3ec..2332417da189 100644 --- a/components/script/dom/mouseevent.rs +++ b/components/script/dom/mouseevent.rs @@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{UIEventCast, MouseEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable}; +use dom::bindings::js::{MutNullableJS, JSRef, RootedReference, Temporary, OptionalSettable}; use dom::bindings::trace::Traceable; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, MouseEventTypeId}; @@ -18,6 +18,7 @@ use dom::uievent::UIEvent; use dom::window::Window; use servo_util::str::DOMString; use std::cell::Cell; +use std::default::Default; #[jstraceable] #[must_root] @@ -32,7 +33,7 @@ pub struct MouseEvent { pub alt_key: Traceable>, pub meta_key: Traceable>, pub button: Traceable>, - pub related_target: Cell>> + pub related_target: MutNullableJS } impl MouseEventDerived for Event { @@ -54,7 +55,7 @@ impl MouseEvent { alt_key: Traceable::new(Cell::new(false)), meta_key: Traceable::new(Cell::new(false)), button: Traceable::new(Cell::new(0)), - related_target: Cell::new(None) + related_target: Default::default(), } } @@ -142,7 +143,7 @@ impl<'a> MouseEventMethods for JSRef<'a, MouseEvent> { } fn GetRelatedTarget(self) -> Option> { - self.related_target.get().clone().map(|target| Temporary::new(target)) + self.related_target.get() } fn InitMouseEvent(self, diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index b62225314e23..0f0dea67d45e 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -22,9 +22,9 @@ use dom::bindings::codegen::InheritTypes::HTMLOptGroupElementDerived; use dom::bindings::error::{Fallible, NotFound, HierarchyRequest, Syntax}; use dom::bindings::global::GlobalRef; use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root, OptionalUnrootable}; +use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, Root}; use dom::bindings::js::{OptionalSettable, TemporaryPushable, OptionalRootedRootable}; -use dom::bindings::js::{ResultRootable, OptionalRootable}; +use dom::bindings::js::{ResultRootable, OptionalRootable, MutNullableJS}; use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::utils; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; @@ -57,7 +57,8 @@ use js::jsapi::{JSContext, JSObject, JSRuntime}; use js::jsfriendapi; use libc; use libc::uintptr_t; -use std::cell::{Cell, RefCell, Ref, RefMut}; +use std::cell::{RefCell, Ref, RefMut}; +use std::default::Default; use std::iter::{Map, Filter}; use std::mem; use style; @@ -80,25 +81,25 @@ pub struct Node { type_id: NodeTypeId, /// The parent of this node. - parent_node: Cell>>, + parent_node: MutNullableJS, /// The first child of this node. - first_child: Cell>>, + first_child: MutNullableJS, /// The last child of this node. - last_child: Cell>>, + last_child: MutNullableJS, /// The next sibling of this node. - next_sibling: Cell>>, + next_sibling: MutNullableJS, /// The previous sibling of this node. - prev_sibling: Cell>>, + prev_sibling: MutNullableJS, /// The document that this node belongs to. - owner_doc: Cell>>, + owner_doc: MutNullableJS, /// The live list of children return by .childNodes. - child_list: Cell>>, + child_list: MutNullableJS, /// A bitfield of flags for node items. flags: Traceable>, @@ -260,6 +261,7 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { fn node_inserted(self) { assert!(self.parent_node().is_some()); let document = document_from_node(self).root(); + document.init(); let is_in_doc = self.is_in_doc(); for node in self.traverse_preorder() { @@ -267,7 +269,8 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { } let parent = self.parent_node().root(); - parent.map(|parent| vtable_for(&*parent).child_inserted(self)); + parent.init(); + parent.as_ref().map(|parent| vtable_for(&**parent).child_inserted(self)); document.deref().content_changed(); } @@ -358,9 +361,9 @@ impl<'a> PrivateNodeHelpers for JSRef<'a, Node> { } } - child.prev_sibling.set(None); - child.next_sibling.set(None); - child.parent_node.set(None); + child.prev_sibling.clear(); + child.next_sibling.clear(); + child.parent_node.clear(); } } @@ -461,25 +464,25 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { } fn parent_node(&self) -> Option> { - self.deref().parent_node.get().map(|node| Temporary::new(node)) + self.deref().parent_node.get() } fn first_child(&self) -> Option> { - self.deref().first_child.get().map(|node| Temporary::new(node)) + self.deref().first_child.get() } fn last_child(&self) -> Option> { - self.deref().last_child.get().map(|node| Temporary::new(node)) + self.deref().last_child.get() } /// Returns the previous sibling of this node. Fails if this node is borrowed mutably. fn prev_sibling(&self) -> Option> { - self.deref().prev_sibling.get().map(|node| Temporary::new(node)) + self.deref().prev_sibling.get() } /// Returns the next sibling of this node. Fails if this node is borrowed mutably. fn next_sibling(&self) -> Option> { - self.deref().next_sibling.get().map(|node| Temporary::new(node)) + self.deref().next_sibling.get() } #[inline] @@ -578,7 +581,7 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { fn is_parent_of(&self, child: JSRef) -> bool { match child.parent_node() { - Some(parent) if parent == Temporary::from_rooted(*self) => true, + Some(ref parent) if parent == &Temporary::from_rooted(*self) => true, _ => false } } @@ -651,7 +654,7 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { } fn owner_doc(&self) -> Temporary { - Temporary::new(self.owner_doc.get().as_ref().unwrap().clone()) + self.owner_doc.get().unwrap() } fn set_owner_doc(&self, document: JSRef) { @@ -664,7 +667,11 @@ impl<'m, 'n> NodeHelpers<'m, 'n> for JSRef<'n, Node> { fn children(&self) -> AbstractNodeChildrenIterator<'n> { AbstractNodeChildrenIterator { - current_node: self.first_child.get().map(|node| (*node.root()).clone()), + current_node: self.first_child.get().map(|node| { + let node = node.root(); + node.init(); + (*node).clone() + }), } } @@ -777,32 +784,32 @@ impl LayoutNodeHelpers for JS { #[inline] unsafe fn parent_node_ref(&self) -> Option> { - (*self.unsafe_get()).parent_node.get() + (*self.unsafe_get()).parent_node.get_inner() } #[inline] unsafe fn first_child_ref(&self) -> Option> { - (*self.unsafe_get()).first_child.get() + (*self.unsafe_get()).first_child.get_inner() } #[inline] unsafe fn last_child_ref(&self) -> Option> { - (*self.unsafe_get()).last_child.get() + (*self.unsafe_get()).last_child.get_inner() } #[inline] unsafe fn prev_sibling_ref(&self) -> Option> { - (*self.unsafe_get()).prev_sibling.get() + (*self.unsafe_get()).prev_sibling.get_inner() } #[inline] unsafe fn next_sibling_ref(&self) -> Option> { - (*self.unsafe_get()).next_sibling.get() + (*self.unsafe_get()).next_sibling.get_inner() } #[inline] unsafe fn owner_doc_for_layout(&self) -> JS { - (*self.unsafe_get()).owner_doc.get().unwrap() + (*self.unsafe_get()).owner_doc.get_inner().unwrap() } } @@ -1025,13 +1032,13 @@ impl Node { eventtarget: EventTarget::new_inherited(NodeTargetTypeId(type_id)), type_id: type_id, - parent_node: Cell::new(None), - first_child: Cell::new(None), - last_child: Cell::new(None), - next_sibling: Cell::new(None), - prev_sibling: Cell::new(None), - owner_doc: Cell::new(doc.unrooted()), - child_list: Cell::new(None), + parent_node: Default::default(), + first_child: Default::default(), + last_child: Default::default(), + next_sibling: Default::default(), + prev_sibling: Default::default(), + owner_doc: MutNullableJS::new(doc), + child_list: Default::default(), flags: Traceable::new(RefCell::new(NodeFlags::new(type_id))), @@ -1304,7 +1311,7 @@ impl Node { fn pre_remove(child: JSRef, parent: JSRef) -> Fallible> { // Step 1. match child.parent_node() { - Some(node) if node != Temporary::from_rooted(parent) => return Err(NotFound), + Some(ref node) if node != &Temporary::from_rooted(parent) => return Err(NotFound), _ => () } @@ -1342,6 +1349,7 @@ impl Node { Some(doc) => JS::from_rooted(doc).root(), None => node.owner_doc().root() }; + document.init(); // Step 2. // XXXabinader: clone() for each node as trait? @@ -1396,6 +1404,7 @@ impl Node { NodeCast::from_temporary(pi) }, }.root(); + copy.init(); // Step 3. let document = if copy.is_document() { @@ -1404,6 +1413,7 @@ impl Node { } else { JS::from_rooted(*document).root() }; + document.init(); assert!(&*copy.owner_doc().root() == &*document); // Step 4 (some data already copied in step 2). @@ -1420,6 +1430,7 @@ impl Node { // FIXME: https://github.com/mozilla/servo/issues/1737 let window = document.deref().window.root(); + window.init(); for attr in node_elem.deref().attrs.borrow().iter().map(|attr| attr.root()) { copy_elem.deref().attrs.borrow_mut().push_unrooted( &Attr::new(*window, @@ -1535,7 +1546,7 @@ impl<'a> NodeMethods for JSRef<'a, Node> { // http://dom.spec.whatwg.org/#dom-node-parentnode fn GetParentNode(self) -> Option> { - self.parent_node.get().map(|node| Temporary::new(node)) + self.parent_node.get() } // http://dom.spec.whatwg.org/#dom-node-parentelement @@ -1558,34 +1569,34 @@ impl<'a> NodeMethods for JSRef<'a, Node> { fn ChildNodes(self) -> Temporary { match self.child_list.get() { None => (), - Some(ref list) => return Temporary::new(list.clone()), + Some(list) => return list, } let doc = self.owner_doc().root(); let window = doc.deref().window.root(); let child_list = NodeList::new_child_list(*window, self); self.child_list.assign(Some(child_list)); - Temporary::new(self.child_list.get().as_ref().unwrap().clone()) + self.child_list.get().unwrap() } // http://dom.spec.whatwg.org/#dom-node-firstchild fn GetFirstChild(self) -> Option> { - self.first_child.get().map(|node| Temporary::new(node)) + self.first_child.get() } // http://dom.spec.whatwg.org/#dom-node-lastchild fn GetLastChild(self) -> Option> { - self.last_child.get().map(|node| Temporary::new(node)) + self.last_child.get() } // http://dom.spec.whatwg.org/#dom-node-previoussibling fn GetPreviousSibling(self) -> Option> { - self.prev_sibling.get().map(|node| Temporary::new(node)) + self.prev_sibling.get() } // http://dom.spec.whatwg.org/#dom-node-nextsibling fn GetNextSibling(self) -> Option> { - self.next_sibling.get().map(|node| Temporary::new(node)) + self.next_sibling.get() } // http://dom.spec.whatwg.org/#dom-node-nodevalue diff --git a/components/script/dom/treewalker.rs b/components/script/dom/treewalker.rs index ba667c222e86..c4401da330bb 100644 --- a/components/script/dom/treewalker.rs +++ b/components/script/dom/treewalker.rs @@ -266,9 +266,9 @@ impl<'a, 'b> PrivateTreeWalkerHelpers<'a, 'b> for JSRef<'a, TreeWalker> { // "5. If result is FILTER_REJECT or sibling is null, // then set sibling to node's next sibling if type is next, // and node's previous sibling if type is previous." - match (result, sibling_op) { + match (result, &sibling_op) { (Ok(NodeFilterConstants::FILTER_REJECT), _) - | (_, None) => sibling_op = next_sibling(node), + | (_, &None) => sibling_op = next_sibling(node), _ => {} } } diff --git a/components/script/dom/uievent.rs b/components/script/dom/uievent.rs index eac29a4ab249..86501d47324c 100644 --- a/components/script/dom/uievent.rs +++ b/components/script/dom/uievent.rs @@ -9,7 +9,7 @@ use dom::bindings::codegen::InheritTypes::{EventCast, UIEventDerived}; use dom::bindings::error::Fallible; use dom::bindings::global::GlobalRef; use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, RootedReference, Temporary, OptionalSettable}; +use dom::bindings::js::{MutNullableJS, JSRef, RootedReference, Temporary, OptionalSettable}; use dom::bindings::trace::Traceable; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::event::{Event, EventTypeId, UIEventTypeId}; @@ -17,12 +17,13 @@ use dom::window::Window; use servo_util::str::DOMString; use std::cell::Cell; +use std::default::Default; #[jstraceable] #[must_root] pub struct UIEvent { pub event: Event, - view: Cell>>, + view: MutNullableJS, detail: Traceable> } @@ -36,7 +37,7 @@ impl UIEvent { pub fn new_inherited(type_id: EventTypeId) -> UIEvent { UIEvent { event: Event::new_inherited(type_id), - view: Cell::new(None), + view: Default::default(), detail: Traceable::new(Cell::new(0)), } } @@ -70,7 +71,7 @@ impl UIEvent { impl<'a> UIEventMethods for JSRef<'a, UIEvent> { fn GetView(self) -> Option> { - self.view.get().map(|view| Temporary::new(view)) + self.view.get() } fn Detail(self) -> i32 { diff --git a/components/script/dom/window.rs b/components/script/dom/window.rs index d0c33ebc1cac..9439d470df88 100644 --- a/components/script/dom/window.rs +++ b/components/script/dom/window.rs @@ -8,7 +8,8 @@ use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::codegen::InheritTypes::EventTargetCast; use dom::bindings::error::{Fallible, InvalidCharacter}; use dom::bindings::global; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalSettable}; +use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalSettable, RootableValue}; +use dom::bindings::js::RootablePointer; use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::browsercontext::BrowserContext; @@ -29,7 +30,8 @@ use servo_net::image_cache_task::ImageCacheTask; use servo_util::str::{DOMString,HTML_SPACE_CHARACTERS}; use servo_util::task::{spawn_named}; -use js::jsapi::{JS_CallFunctionValue, JS_EvaluateUCScript}; +use js::glue::CallFunctionValue; +use js::jsapi::JS_EvaluateUCScript; use js::jsapi::JSContext; use js::jsapi::{JS_GC, JS_GetRuntime}; use js::jsval::JSVal; @@ -44,6 +46,7 @@ use std::cell::{Cell, RefCell}; use std::cmp; use std::comm::{channel, Sender}; use std::comm::Select; +use std::default::Default; use std::hash::{Hash, sip}; use std::io::timer::Timer; use std::ptr; @@ -81,16 +84,16 @@ pub struct Window { eventtarget: EventTarget, pub script_chan: ScriptChan, pub control_chan: ScriptControlChan, - console: Cell>>, - location: Cell>>, - navigator: Cell>>, + console: MutNullableJS, + location: MutNullableJS, + navigator: MutNullableJS, pub image_cache_task: ImageCacheTask, pub active_timers: Traceable>>, next_timer_handle: Traceable>, pub compositor: Untraceable>, pub browser_context: Traceable>>, pub page: Rc, - performance: Cell>>, + performance: MutNullableJS, pub navigationStart: u64, pub navigationStartPrecise: f64, screen: Cell>>, @@ -225,7 +228,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { let location = Location::new(self, page); self.location.assign(Some(location)); } - Temporary::new(self.location.get().as_ref().unwrap().clone()) + self.location.get().unwrap() } fn Console(self) -> Temporary { @@ -233,7 +236,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { let console = Console::new(&global::Window(self)); self.console.assign(Some(console)); } - Temporary::new(self.console.get().as_ref().unwrap().clone()) + self.console.get().unwrap() } fn Navigator(self) -> Temporary { @@ -241,7 +244,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { let navigator = Navigator::new(self); self.navigator.assign(Some(navigator)); } - Temporary::new(self.navigator.get().as_ref().unwrap().clone()) + self.navigator.get().unwrap() } fn SetTimeout(self, _cx: *mut JSContext, callback: JSVal, timeout: i32) -> i32 { @@ -289,7 +292,7 @@ impl<'a> WindowMethods for JSRef<'a, Window> { let performance = Performance::new(self); self.performance.assign(Some(performance)); } - Temporary::new(self.performance.get().as_ref().unwrap().clone()) + self.performance.get().unwrap() } fn GetOnclick(self) -> Option { @@ -381,20 +384,22 @@ trait PrivateWindowHelpers { impl<'a> WindowHelpers for JSRef<'a, Window> { fn evaluate_js_with_result(self, code: &str) -> JSVal { - let global = self.reflector().get_jsobject(); + let global = self.reflector().get_jsobject().root_ptr(); + global.init(); let code: Vec = code.as_slice().utf16_units().collect(); - let mut rval = UndefinedValue(); + let mut rval = UndefinedValue().root_value(); + rval.init(); let filename = "".to_c_str(); let cx = self.get_cx(); - with_compartment(cx, global, || { + with_compartment(cx, *global.raw(), || { unsafe { - if JS_EvaluateUCScript(cx, global, code.as_ptr(), - code.len() as libc::c_uint, - filename.as_ptr(), 1, &mut rval) == 0 { + if !JS_EvaluateUCScript(cx, global.handle(), code.as_ptr(), + code.len() as libc::c_uint, + filename.as_ptr(), 1, rval.mut_handle_()) { debug!("error evaluating JS string"); } - rval + *rval.raw_() } }) } @@ -434,19 +439,22 @@ impl<'a> WindowHelpers for JSRef<'a, Window> { } fn handle_fire_timer(self, timer_id: TimerId, cx: *mut JSContext) { - let this_value = self.reflector().get_jsobject(); + let this_value = self.reflector().get_jsobject().root_ptr(); + this_value.init(); let data = match self.active_timers.deref().borrow().find(&timer_id) { None => return, Some(timer_handle) => timer_handle.data, }; + let fval = (*data.funval.deref()).root_value(); // TODO: Support extra arguments. This requires passing a `*JSVal` array as `argv`. - with_compartment(cx, this_value, || { - let mut rval = NullValue(); + with_compartment(cx, *this_value.raw(), || { + let mut rval = NullValue().root_value(); unsafe { - JS_CallFunctionValue(cx, this_value, *data.funval, - 0, ptr::null_mut(), &mut rval); + CallFunctionValue(cx, this_value.handle(), + fval.handle_(), + 0, ptr::null(), rval.mut_handle_()); } }); @@ -529,16 +537,16 @@ impl Window { eventtarget: EventTarget::new_inherited(WindowTypeId), script_chan: script_chan, control_chan: control_chan, - console: Cell::new(None), + console: Default::default(), compositor: Untraceable::new(compositor), page: page, - location: Cell::new(None), - navigator: Cell::new(None), + location: Default::default(), + navigator: Default::default(), image_cache_task: image_cache_task, active_timers: Traceable::new(RefCell::new(HashMap::new())), next_timer_handle: Traceable::new(Cell::new(0)), browser_context: Traceable::new(RefCell::new(None)), - performance: Cell::new(None), + performance: Default::default(), navigationStart: time::get_time().sec as u64, navigationStartPrecise: time::precise_time_s(), screen: Cell::new(None), diff --git a/components/script/dom/worker.rs b/components/script/dom/worker.rs index 04bac9e2c9a5..3139cf7294de 100644 --- a/components/script/dom/worker.rs +++ b/components/script/dom/worker.rs @@ -8,7 +8,8 @@ use dom::bindings::codegen::Bindings::EventHandlerBinding::EventHandlerNonNull; use dom::bindings::codegen::InheritTypes::EventTargetCast; use dom::bindings::error::{Fallible, Syntax}; use dom::bindings::global::{GlobalRef, GlobalField}; -use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::js::{JS, JSRef, Temporary, RootableValue}; +use dom::bindings::refcounted::LiveReferences; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; use dom::dedicatedworkerglobalscope::DedicatedWorkerGlobalScope; use dom::eventtarget::{EventTarget, EventTargetHelpers, WorkerTypeId}; @@ -18,13 +19,13 @@ use script_task::{ScriptChan, DOMMessage}; use servo_util::str::DOMString; use js::glue::JS_STRUCTURED_CLONE_VERSION; -use js::jsapi::{JSContext, JS_AddObjectRoot, JS_RemoveObjectRoot}; -use js::jsapi::{JS_ReadStructuredClone, JS_WriteStructuredClone}; +use js::jsapi::{JSContext/*, JS_AddObjectRoot, JS_RemoveObjectRoot*/}; +use js::jsfriendapi::{JS_ReadStructuredClone, JS_WriteStructuredClone}; use js::jsval::{JSVal, UndefinedValue}; +use js::rust::JSAutoCompartment; use url::UrlParser; use libc::{c_void, size_t}; -use std::cell::Cell; use std::ptr; pub struct TrustedWorkerAddress(pub *const c_void); @@ -33,7 +34,6 @@ pub struct TrustedWorkerAddress(pub *const c_void); #[must_root] pub struct Worker { eventtarget: EventTarget, - refcount: Cell, global: GlobalField, /// Sender to the Receiver associated with the DedicatedWorkerGlobalScope /// this Worker created. @@ -44,7 +44,6 @@ impl Worker { fn new_inherited(global: &GlobalRef, sender: ScriptChan) -> Worker { Worker { eventtarget: EventTarget::new_inherited(WorkerTypeId), - refcount: Cell::new(0), global: GlobalField::from_rooted(global), sender: sender, } @@ -83,44 +82,31 @@ impl Worker { let worker = unsafe { JS::from_trusted_worker_address(address).root() }; let global = worker.global.root(); + let cx = global.root_ref().get_cx(); + let _ac = JSAutoCompartment::new(cx, global.root_ref().reflector().get_jsobject()); - let mut message = UndefinedValue(); + let mut message = UndefinedValue().root_value(); unsafe { assert!(JS_ReadStructuredClone( global.root_ref().get_cx(), data as *const u64, nbytes, - JS_STRUCTURED_CLONE_VERSION, &mut message, - ptr::null(), ptr::null_mut()) != 0); + JS_STRUCTURED_CLONE_VERSION, message.mut_handle_(), + ptr::null(), ptr::null_mut())); } let target: JSRef = EventTargetCast::from_ref(*worker); - MessageEvent::dispatch_jsval(target, &global.root_ref(), message); + MessageEvent::dispatch_jsval(target, &global.root_ref(), *message.raw_()); } } impl Worker { // Creates a trusted address to the object, and roots it. Always pair this with a release() pub fn addref(&self) -> TrustedWorkerAddress { - let refcount = self.refcount.get(); - if refcount == 0 { - let cx = self.global.root().root_ref().get_cx(); - unsafe { - JS_AddObjectRoot(cx, self.reflector().rootable()); - } - } - self.refcount.set(refcount + 1); + LiveReferences.get().unwrap().addref(self); TrustedWorkerAddress(self as *const Worker as *const c_void) } pub fn release(&self) { - let refcount = self.refcount.get(); - assert!(refcount > 0) - self.refcount.set(refcount - 1); - if refcount == 1 { - let cx = self.global.root().root_ref().get_cx(); - unsafe { - JS_RemoveObjectRoot(cx, self.reflector().rootable()); - } - } + LiveReferences.get().unwrap().release(self); } pub fn handle_release(address: TrustedWorkerAddress) { @@ -131,11 +117,14 @@ impl Worker { impl<'a> WorkerMethods for JSRef<'a, Worker> { fn PostMessage(self, cx: *mut JSContext, message: JSVal) { + let message = message.root_value(); let mut data = ptr::null_mut(); let mut nbytes = 0; + let transferable = UndefinedValue().root_value(); unsafe { - assert!(JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes, - ptr::null(), ptr::null_mut()) != 0); + assert!(JS_WriteStructuredClone(cx, message.handle_(), &mut data, &mut nbytes, + ptr::null(), ptr::null_mut(), + transferable.handle_())); } self.addref(); diff --git a/components/script/dom/workerglobalscope.rs b/components/script/dom/workerglobalscope.rs index a77513f3b21c..b1dd26a09b8e 100644 --- a/components/script/dom/workerglobalscope.rs +++ b/components/script/dom/workerglobalscope.rs @@ -115,7 +115,7 @@ impl<'a> WorkerGlobalScopeMethods for JSRef<'a, WorkerGlobalScope> { self.reflector().get_jsobject(), source, url.serialize(), 1) { Ok(_) => (), Err(_) => { - println!("evaluate_script failed"); + println!("worker evaluate_script failed"); return Err(FailureUnknown); } } diff --git a/components/script/dom/xmlhttprequest.rs b/components/script/dom/xmlhttprequest.rs index 7bb38cf49df3..703d62a1b6df 100644 --- a/components/script/dom/xmlhttprequest.rs +++ b/components/script/dom/xmlhttprequest.rs @@ -13,7 +13,8 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::error::{Error, ErrorResult, Fallible, InvalidState, InvalidAccess}; use dom::bindings::error::{Network, Syntax, Security, Abort, Timeout}; use dom::bindings::global::{GlobalField, GlobalRef, WorkerField}; -use dom::bindings::js::{JS, JSRef, Temporary, OptionalRootedRootable}; +use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary, OptionalRootedRootable, RootableValue}; +use dom::bindings::refcounted::LiveReferences; use dom::bindings::str::ByteString; use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::utils::{Reflectable, Reflector, reflect_dom_object}; @@ -37,8 +38,7 @@ use http::headers::request::Header; use http::method::{Method, Get, Head, Connect, Trace, ExtensionMethod}; use http::status::Status; -use js::jsapi::{JS_AddObjectRoot, JS_ParseJSON, JS_RemoveObjectRoot, JSContext}; -use js::jsapi::JS_ClearPendingException; +use js::jsapi::{JS_ParseJSON, JS_ClearPendingException, JSContext}; use js::jsval::{JSVal, NullValue, UndefinedValue}; use libc; @@ -53,6 +53,7 @@ use servo_util::task::spawn_named; use std::ascii::StrAsciiExt; use std::cell::{Cell, RefCell}; use std::comm::{Sender, Receiver, channel}; +use std::default::Default; use std::io::{BufReader, MemWriter, Timer}; use std::from_str::FromStr; use std::path::BytesContainer; @@ -115,7 +116,7 @@ pub struct XMLHttpRequest { status_text: Traceable>, response: Traceable>, response_type: Traceable>, - response_xml: Cell>>, + response_xml: MutNullableJS, response_headers: Untraceable>, // Associated concepts @@ -129,7 +130,6 @@ pub struct XMLHttpRequest { send_flag: Traceable>, global: GlobalField, - pinned_count: Traceable>, timer: Untraceable>, fetch_time: Traceable>, timeout_pinned: Traceable>, @@ -149,7 +149,7 @@ impl XMLHttpRequest { status_text: Traceable::new(RefCell::new(ByteString::new(vec!()))), response: Traceable::new(RefCell::new(ByteString::new(vec!()))), response_type: Traceable::new(Cell::new(_empty)), - response_xml: Cell::new(None), + response_xml: Default::default(), response_headers: Untraceable::new(RefCell::new(ResponseHeaderCollection::new())), request_method: Untraceable::new(RefCell::new(Get)), @@ -163,7 +163,6 @@ impl XMLHttpRequest { upload_events: Traceable::new(Cell::new(false)), global: GlobalField::from_rooted(global), - pinned_count: Traceable::new(Cell::new(0)), timer: Untraceable::new(RefCell::new(Timer::new().unwrap())), fetch_time: Traceable::new(Cell::new(0)), timeout_pinned: Traceable::new(Cell::new(false)), @@ -472,8 +471,8 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> { } // Step 8 - let upload_target = *self.upload.root(); - let event_target: JSRef = EventTargetCast::from_ref(upload_target); + let upload_target = self.upload.root(); + let event_target: JSRef = EventTargetCast::from_ref(*upload_target); if event_target.has_handlers() { self.upload_events.deref().set(true); } @@ -640,14 +639,14 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> { Json => { let decoded = UTF_8.decode(self.response.deref().borrow().as_slice(), DecodeReplace).unwrap().to_string(); let decoded: Vec = decoded.as_slice().utf16_units().collect(); - let mut vp = UndefinedValue(); + let mut vp = UndefinedValue().root_value(); unsafe { - if JS_ParseJSON(cx, decoded.as_ptr(), decoded.len() as u32, &mut vp) == 0 { + if !JS_ParseJSON(cx, decoded.as_ptr(), decoded.len() as u32, vp.mut_handle_()) { JS_ClearPendingException(cx); return NullValue(); } } - vp + *vp.raw_() } _ => { // XXXManishearth handle other response types @@ -667,7 +666,7 @@ impl<'a> XMLHttpRequestMethods for JSRef<'a, XMLHttpRequest> { } } fn GetResponseXML(self) -> Option> { - self.response_xml.get().map(|response| Temporary::new(response)) + self.response_xml.get() } } @@ -715,11 +714,7 @@ trait PrivateXMLHttpRequestHelpers { impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { // Creates a trusted address to the object, and roots it. Always pair this with a release() unsafe fn to_trusted(self) -> TrustedXHRAddress { - if self.pinned_count.deref().get() == 0 { - JS_AddObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable()); - } - let pinned_count = self.pinned_count.deref().get(); - self.pinned_count.deref().set(pinned_count + 1); + LiveReferences.get().unwrap().addref(&self); TrustedXHRAddress(self.deref() as *const XMLHttpRequest as *const libc::c_void) } @@ -730,14 +725,7 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { // meaningful during an async fetch return; } - assert!(self.pinned_count.deref().get() > 0) - let pinned_count = self.pinned_count.deref().get(); - self.pinned_count.deref().set(pinned_count - 1); - if self.pinned_count.deref().get() == 0 { - unsafe { - JS_RemoveObjectRoot(self.global.root().root_ref().get_cx(), self.reflector().rootable()); - } - } + LiveReferences.get().unwrap().release(&self); } fn change_ready_state(self, rs: XMLHttpRequestState) { @@ -865,6 +853,7 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { fn dispatch_progress_event(self, upload: bool, type_: DOMString, loaded: u64, total: Option) { let global = self.global.root(); + global.init(); let upload_target = *self.upload.root(); let progressevent = ProgressEvent::new(&global.root_ref(), type_, false, false, @@ -911,8 +900,8 @@ impl<'a> PrivateXMLHttpRequestHelpers for JSRef<'a, XMLHttpRequest> { match oneshot.recv_opt() { Ok(_) => { let ScriptChan(ref chan) = script_chan; - terminate_sender.map(|s| s.send_opt(Timeout)); chan.send(XHRProgressMsg(addr, TimeoutMsg)); + terminate_sender.map(|s| s.send_opt(Timeout)); }, Err(_) => { // This occurs if xhr.timeout (the sender) goes out of scope (i.e, xhr went out of scope) diff --git a/components/script/html/hubbub_html_parser.rs b/components/script/html/hubbub_html_parser.rs index 097f8438cb5a..f598630a5387 100644 --- a/components/script/html/hubbub_html_parser.rs +++ b/components/script/html/hubbub_html_parser.rs @@ -411,7 +411,9 @@ pub fn parse_html(page: &Page, unsafe { debug!("append child {:x} {:x}", parent, child); let child: Root = from_hubbub_node(child).root(); + child.init(); let parent: Root = from_hubbub_node(parent).root(); + parent.init(); assert!(parent.deref().AppendChild(*child).is_ok()); } child diff --git a/components/script/lib.rs b/components/script/lib.rs index 695aef3c1b57..2f51e72b7f60 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -5,7 +5,7 @@ #![comment = "The Servo Parallel Browser Project"] #![license = "MPL"] -#![feature(globs, macro_rules, struct_variant, phase, unsafe_destructor)] +#![feature(globs, macro_rules, struct_variant, phase, unsafe_destructor, default_type_params)] #![deny(unused_imports, unused_variable)] #![allow(non_snake_case)] @@ -59,8 +59,10 @@ pub mod dom { pub mod callback; pub mod error; pub mod conversions; + pub mod init; mod proxyhandler; pub mod str; + pub mod refcounted; pub mod trace; /// Generated JS-Rust bindings. @@ -214,3 +216,18 @@ pub mod html { pub mod layout_interface; pub mod page; pub mod script_task; + +pub fn init() { + unsafe { + js::jsfriendapi::PR_GetCurrentThread(); + js::jsapi::JS_Init(); + } + self::dom::bindings::init::global_init(); +} + +pub fn shutdown() { + // Not strictly necessary, and hard to synchronize with native script tasks + /*unsafe { + js::jsapi::JS_ShutDown(); + }*/ +} diff --git a/components/script/page.rs b/components/script/page.rs index da2b99c0c6ff..20633bba106b 100644 --- a/components/script/page.rs +++ b/components/script/page.rs @@ -5,7 +5,7 @@ use dom::attr::AttrHelpers; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::InheritTypes::{NodeCast, ElementCast}; -use dom::bindings::js::{JS, JSRef, Temporary}; +use dom::bindings::js::{MutNullableJS, JS, JSRef, Temporary}; use dom::bindings::js::OptionalRootable; use dom::bindings::trace::{Traceable, Untraceable}; use dom::bindings::utils::GlobalStaticData; @@ -31,6 +31,7 @@ use servo_util::namespace::Null; use servo_util::str::DOMString; use std::cell::{Cell, RefCell, Ref, RefMut}; use std::comm::{channel, Receiver, Empty, Disconnected}; +use std::default::Default; use std::mem::replace; use std::rc::Rc; use url::Url; @@ -79,7 +80,7 @@ pub struct Page { pub resize_event: Untraceable>>, /// Pending scroll to fragment event, if any - pub fragment_node: Cell>>, + pub fragment_node: MutNullableJS, /// Associated resource task for use by DOM objects like XMLHttpRequest pub resource_task: Untraceable, @@ -153,7 +154,7 @@ impl Page { url: Untraceable::new(RefCell::new(None)), next_subpage_id: Traceable::new(Cell::new(SubpageId(0))), resize_event: Untraceable::new(Cell::new(None)), - fragment_node: Cell::new(None), + fragment_node: Default::default(), last_reflow_id: Traceable::new(Cell::new(0)), resource_task: Untraceable::new(resource_task), constellation_chan: Untraceable::new(constellation_chan), diff --git a/components/script/script_task.rs b/components/script/script_task.rs index 88c84ef5b741..610156a17c92 100644 --- a/components/script/script_task.rs +++ b/components/script/script_task.rs @@ -5,6 +5,7 @@ //! The script task is the task that owns the DOM in memory, runs JavaScript, and spawns parsing //! and layout tasks. +use dom::bindings; use dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; use dom::bindings::codegen::Bindings::DOMRectBinding::DOMRectMethods; use dom::bindings::codegen::Bindings::ElementBinding::ElementMethods; @@ -14,9 +15,9 @@ use dom::bindings::conversions::{FromJSValConvertible, Empty}; use dom::bindings::global; use dom::bindings::js::{JS, JSRef, RootCollection, Temporary, OptionalSettable}; use dom::bindings::js::OptionalRootable; -use dom::bindings::trace::JSTraceable; +use dom::bindings::refcounted::{LiveReferences, LiveDOMReferences, trace_refcounted_objects}; +use dom::bindings::trace::{JSTraceable, RootedCollections, trace_collections}; use dom::bindings::utils::Reflectable; -use dom::bindings::utils::{wrap_for_same_compartment, pre_wrap}; use dom::document::{Document, HTMLDocument, DocumentHelpers}; use dom::element::{Element, HTMLButtonElementTypeId, HTMLInputElementTypeId}; use dom::element::{HTMLSelectElementTypeId, HTMLTextAreaElementTypeId, HTMLOptionElementTypeId}; @@ -56,10 +57,11 @@ use servo_util::geometry::to_frac_px; use servo_util::task::spawn_named_with_send_on_failure; use geom::point::Point2D; -use js::jsapi::{JS_SetWrapObjectCallbacks, JS_SetGCZeal, JS_DEFAULT_ZEAL_FREQ, JS_GC}; -use js::jsapi::{JSContext, JSRuntime, JSTracer}; +use js::JS_DEFAULT_ZEAL_FREQ; +use js::jsapi::{/*JS_SetWrapObjectCallbacks,*/ JS_SetGCZeal, JS_GC}; +use js::jsapi::{JSContext, JSRuntime, JSTracer, JS_AddExtraGCRootsTracer}; use js::jsapi::{JS_SetGCParameter, JSGC_MAX_BYTES}; -use js::rust::{Cx, RtUtils}; +use js::rust::{Cx, RtUtils, JSAutoRequest}; use js::rust::with_compartment; use js; use url::Url; @@ -67,6 +69,7 @@ use url::Url; use libc::size_t; use std::any::{Any, AnyRefExt}; use std::cell::RefCell; +use std::collections::HashSet; use std::comm::{channel, Sender, Receiver, Select}; use std::mem::replace; use std::rc::Rc; @@ -273,7 +276,7 @@ impl ScriptTaskFactory for ScriptTask { // This must always be the very last operation performed before the task completes failsafe.neuter(); - }, FailureMsg(failure_msg), const_chan, false); + }, FailureMsg(failure_msg), const_chan, true); } } @@ -293,7 +296,7 @@ impl ScriptTask { window_size: WindowSizeData) -> Rc { let (js_runtime, js_context) = ScriptTask::new_rt_and_cx(); - unsafe { + /*unsafe { // JS_SetWrapObjectCallbacks clobbers the existing wrap callback, // and JSCompartment::wrap crashes if that happens. The only way // to retrieve the default callback is as the result of @@ -306,7 +309,7 @@ impl ScriptTask { callback, Some(wrap_for_same_compartment), Some(pre_wrap)); - } + }*/ let page = Page::new(id, None, layout_chan, window_size, resource_task.clone(), @@ -342,11 +345,21 @@ impl ScriptTask { } pub fn new_rt_and_cx() -> (js::rust::rt, Rc) { + js::rust::init_thread(); + RootedCollections.replace(Some(RefCell::new(HashSet::new()))); + LiveDOMReferences::initialize(); let js_runtime = js::rust::rt(); assert!({ let ptr: *mut JSRuntime = (*js_runtime).ptr; ptr.is_not_null() }); + bindings::init::init((*js_runtime).ptr); + unsafe { + JS_AddExtraGCRootsTracer((*js_runtime).ptr, Some(trace_collections), + RootedCollections.get().as_ref().unwrap().deref() as *const _ as *mut _); + JS_AddExtraGCRootsTracer((*js_runtime).ptr, Some(trace_refcounted_objects), + LiveReferences.get().as_ref().unwrap().deref() as *const _ as *mut _); + } // Unconstrain the runtime's threshold on nominal heap size, to avoid // triggering GC too often if operating continuously near an arbitrary @@ -365,7 +378,7 @@ impl ScriptTask { js_context.set_default_options_and_version(); js_context.set_logging_error_reporter(); unsafe { - JS_SetGCZeal((*js_context).ptr, 0, JS_DEFAULT_ZEAL_FREQ); + JS_SetGCZeal((*js_context).ptr, 0, js::JS_DEFAULT_ZEAL_FREQ); } (js_runtime, js_context) @@ -385,7 +398,10 @@ impl ScriptTask { /// Handle incoming control messages. fn handle_msgs(&self) -> bool { - let roots = RootCollection::new(); + let cx = self.js_context.borrow().as_ref().unwrap().deref().ptr; + let mut _ar = Some(JSAutoRequest::new(cx)); + + let roots = RootCollection::new(self.get_cx()); let _stack_roots_tls = StackRootTLS::new(&roots); // Handle pending resize events. @@ -493,8 +509,16 @@ impl ScriptTask { FromScript(NavigateMsg(direction)) => self.handle_navigate_msg(direction), FromConstellation(ReflowCompleteMsg(id, reflow_id)) => self.handle_reflow_complete_msg(id, reflow_id), FromConstellation(ResizeInactiveMsg(id, new_size)) => self.handle_resize_inactive_msg(id, new_size), - FromConstellation(ExitPipelineMsg(id)) => if self.handle_exit_pipeline_msg(id) { return false }, - FromScript(ExitWindowMsg(id)) => self.handle_exit_window_msg(id), + FromConstellation(ExitPipelineMsg(id)) => { + // Ensure that the JS request has exited before the runtime is destroyed + _ar = None; + if self.handle_exit_pipeline_msg(id) { return false } + } + FromScript(ExitWindowMsg(id)) => { + // No need to exit the JS request yet; we can still receive messages that + // can require rooting (such as page damage) + self.handle_exit_window_msg(id) + } FromConstellation(ResizeMsg(..)) => fail!("should have handled ResizeMsg already"), FromScript(XHRProgressMsg(addr, progress)) => XMLHttpRequest::handle_xhr_progress(addr, progress), FromScript(DOMMessage(..)) => fail!("unexpected message"), @@ -725,6 +749,9 @@ impl ScriptTask { let cx = self.js_context.borrow(); let cx = cx.as_ref().unwrap(); + + let _ar = JSAutoRequest::new(cx.deref().ptr); + // Create the window and document objects. let window = Window::new(cx.deref().ptr, page.clone(), @@ -877,8 +904,9 @@ impl ScriptTask { page.reflow(ReflowForDisplay, self.control_chan.clone(), &*self.compositor) } - let mut fragment_node = page.fragment_node.get(); - match fragment_node.take().map(|node| node.root()) { + let fragment_node = page.fragment_node.get(); + page.fragment_node.clear(); + match fragment_node.map(|node| node.root()) { Some(node) => self.scroll_fragment_point(pipeline_id, *node), None => {} } diff --git a/components/util/lib.rs b/components/util/lib.rs index 89fcec41d933..da9c953f57d6 100644 --- a/components/util/lib.rs +++ b/components/util/lib.rs @@ -6,7 +6,7 @@ #![deny(unused_imports, unused_variable)] -#![feature(phase)] +#![feature(phase, unsafe_destructor)] #[phase(plugin, link)] extern crate log; diff --git a/ports/cef/Cargo.lock b/ports/cef/Cargo.lock index 4836ef6ae2c7..881881fb9776 100644 --- a/ports/cef/Cargo.lock +++ b/ports/cef/Cargo.lock @@ -10,7 +10,7 @@ dependencies = [ "gfx 0.0.1", "glfw 0.0.1 (git+https://github.com/servo/glfw-rs?ref=servo#955dbe919870b0536f79123232d87c0efe3c552e)", "glut 0.0.1 (git+https://github.com/servo/rust-glut#01af0162ea0322ad1a40d6adb023a39813605949)", - "js 0.1.0 (git+https://github.com/servo/rust-mozjs#41fb0d80a5ed5614ca13a120cdb3281e599d4e04)", + "js 0.1.0 (git+https://github.com/servo/rust-mozjs#01117b7a4782857ac8242ff17245b28816de19af)", "layers 0.1.0 (git+https://github.com/servo/rust-layers#ed570335738913fb41fc62a024389dfa415962e8)", "msg 0.0.1", "net 0.0.1", @@ -269,9 +269,9 @@ dependencies = [ [[package]] name = "js" version = "0.1.0" -source = "git+https://github.com/servo/rust-mozjs#41fb0d80a5ed5614ca13a120cdb3281e599d4e04" +source = "git+https://github.com/servo/rust-mozjs#01117b7a4782857ac8242ff17245b28816de19af" dependencies = [ - "mozjs-sys 0.0.0 (git+https://github.com/servo/mozjs#47cd6a4e60c75642ba182f0df9a42b71ec7c2c88)", + "mozjs-sys 0.0.0 (git+https://github.com/servo/mozjs#cbc5759c6d4019acf41ae64f0ba222f8c2c70d4a)", ] [[package]] @@ -323,7 +323,7 @@ source = "git+https://github.com/Kimundi/lazy-static.rs#e62a65372f1dd9019e37eb93 [[package]] name = "mozjs-sys" version = "0.0.0" -source = "git+https://github.com/servo/mozjs#47cd6a4e60c75642ba182f0df9a42b71ec7c2c88" +source = "git+https://github.com/servo/mozjs#cbc5759c6d4019acf41ae64f0ba222f8c2c70d4a" [[package]] name = "msg" @@ -404,7 +404,7 @@ dependencies = [ "gfx 0.0.1", "http 0.1.0-pre (git+https://github.com/servo/rust-http?ref=servo#92019011b0cdf1bffc8c584830de1bf330d79d0d)", "hubbub 0.1.0 (git+https://github.com/servo/rust-hubbub#c7f868e688de6e9cbdc26aa09292ed072bc2648b)", - "js 0.1.0 (git+https://github.com/servo/rust-mozjs#41fb0d80a5ed5614ca13a120cdb3281e599d4e04)", + "js 0.1.0 (git+https://github.com/servo/rust-mozjs#01117b7a4782857ac8242ff17245b28816de19af)", "msg 0.0.1", "net 0.0.1", "plugins 0.0.1", diff --git a/src/lib.rs b/src/lib.rs index 6434f0c364c2..3235a215136d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,7 +5,7 @@ #![comment = "The Servo Parallel Browser Project"] #![license = "MPL"] -#![feature(globs, macro_rules, phase, thread_local)] +#![feature(globs, macro_rules, phase, thread_local, unsafe_destructor)] #![deny(unused_imports, unused_variable)] @@ -158,5 +158,7 @@ pub fn run(opts: opts::Opts) { memory_profiler_chan); pool.shutdown(); + + script::shutdown(); } diff --git a/src/main.rs b/src/main.rs index a1ced51d823c..f21353d15575 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ #![deny(unused_imports, unused_variable)] +extern crate script; extern crate servo; extern crate native; extern crate "util" as servo_util; @@ -25,6 +26,8 @@ use std::os; #[start] #[allow(dead_code)] fn start(argc: int, argv: *const *const u8) -> int { + script::init(); + native::start(argc, argv, proc() { opts::from_cmdline_args(os::args().as_slice()).map(run); }) diff --git a/src/test/wpt/metadata/XMLHttpRequest/open-url-worker-simple.htm.ini b/src/test/wpt/metadata/XMLHttpRequest/open-url-worker-simple.htm.ini new file mode 100644 index 000000000000..04367aad802c --- /dev/null +++ b/src/test/wpt/metadata/XMLHttpRequest/open-url-worker-simple.htm.ini @@ -0,0 +1,4 @@ +[open-url-worker-simple.htm] + type: testharness + expected: OK + diff --git a/tests/content/test_global.html b/tests/content/test_global.html index 24cfbc5f6ba8..18afe998340d 100644 --- a/tests/content/test_global.html +++ b/tests/content/test_global.html @@ -2,6 +2,7 @@