diff options
Diffstat (limited to 'components/script/dom/bindings')
-rw-r--r-- | components/script/dom/bindings/callback.rs | 117 | ||||
-rw-r--r-- | components/script/dom/bindings/cell.rs | 4 | ||||
-rw-r--r-- | components/script/dom/bindings/codegen/Bindings.conf | 2 | ||||
-rw-r--r-- | components/script/dom/bindings/codegen/CodegenRust.py | 1454 | ||||
-rw-r--r-- | components/script/dom/bindings/codegen/Configuration.py | 8 | ||||
-rw-r--r-- | components/script/dom/bindings/conversions.rs | 368 | ||||
-rw-r--r-- | components/script/dom/bindings/error.rs | 47 | ||||
-rw-r--r-- | components/script/dom/bindings/global.rs | 41 | ||||
-rw-r--r-- | components/script/dom/bindings/js.rs | 543 | ||||
-rw-r--r-- | components/script/dom/bindings/proxyhandler.rs | 139 | ||||
-rw-r--r-- | components/script/dom/bindings/refcounted.rs | 33 | ||||
-rw-r--r-- | components/script/dom/bindings/structuredclone.rs | 15 | ||||
-rw-r--r-- | components/script/dom/bindings/trace.rs | 227 | ||||
-rw-r--r-- | components/script/dom/bindings/utils.rs | 378 |
14 files changed, 1810 insertions, 1566 deletions
diff --git a/components/script/dom/bindings/callback.rs b/components/script/dom/bindings/callback.rs index 4baccae098e..bc6077c8cb7 100644 --- a/components/script/dom/bindings/callback.rs +++ b/components/script/dom/bindings/callback.rs @@ -6,15 +6,22 @@ use dom::bindings::error::{Fallible, Error}; use dom::bindings::global::global_object_for_js_object; -use dom::bindings::js::JSRef; use dom::bindings::utils::Reflectable; -use js::jsapi::{JSContext, JSObject, JS_WrapObject, JS_ObjectIsCallable, JS_GetGlobalObject}; +use js::jsapi::{JSContext, JSObject, JS_WrapObject, IsCallable}; use js::jsapi::{JS_GetProperty, JS_IsExceptionPending, JS_ReportPendingException}; +use js::jsapi::{RootedObject, RootedValue, MutableHandleObject, Heap}; +use js::jsapi::{JSAutoCompartment}; +use js::jsapi::{JS_BeginRequest, JS_EndRequest}; +use js::jsapi::{JS_EnterCompartment, JS_LeaveCompartment, JSCompartment}; +use js::jsapi::GetGlobalForObjectCrossCompartment; +use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain}; use js::jsval::{JSVal, UndefinedValue}; -use js::rust::with_compartment; use std::ffi::CString; use std::ptr; +use std::rc::Rc; +use std::intrinsics::return_address; +use std::default::Default; /// The exception handling used for a call. #[derive(Copy, Clone, PartialEq)] @@ -26,7 +33,7 @@ pub enum ExceptionHandling { } /// A common base class for representing IDL callback function types. -#[derive(Copy, Clone,PartialEq)] +#[derive(PartialEq)] #[jstraceable] pub struct CallbackFunction { object: CallbackObject @@ -34,17 +41,23 @@ pub struct CallbackFunction { impl CallbackFunction { /// Create a new `CallbackFunction` for this object. - pub fn new(callback: *mut JSObject) -> CallbackFunction { + pub fn new() -> CallbackFunction { CallbackFunction { object: CallbackObject { - callback: callback + callback: Heap::default() } } } + + /// Initialize the callback function with a value. + /// Should be called once this object is done moving. + pub fn init(&mut self, callback: *mut JSObject) { + self.object.callback.set(callback); + } } /// A common base class for representing IDL callback interface types. -#[derive(Copy, Clone,PartialEq)] +#[derive(PartialEq)] #[jstraceable] pub struct CallbackInterface { object: CallbackObject @@ -53,18 +66,23 @@ pub struct CallbackInterface { /// A common base class for representing IDL callback function and /// callback interface types. #[allow(raw_pointer_derive)] -#[derive(Copy, Clone,PartialEq)] #[jstraceable] struct CallbackObject { /// The underlying `JSObject`. - callback: *mut JSObject, + callback: Heap<*mut JSObject>, +} + +impl PartialEq for CallbackObject { + fn eq(&self, other: &CallbackObject) -> bool { + self.callback.get() == other.callback.get() + } } /// A trait to be implemented by concrete IDL callback function and /// callback interface types. pub trait CallbackContainer { /// Create a new CallbackContainer object for the given `JSObject`. - fn new(callback: *mut JSObject) -> Self; + fn new(callback: *mut JSObject) -> Rc<Self>; /// Returns the underlying `JSObject`. fn callback(&self) -> *mut JSObject; } @@ -72,83 +90,103 @@ pub trait CallbackContainer { impl CallbackInterface { /// Returns the underlying `JSObject`. pub fn callback(&self) -> *mut JSObject { - self.object.callback + self.object.callback.get() } } impl CallbackFunction { /// Returns the underlying `JSObject`. pub fn callback(&self) -> *mut JSObject { - self.object.callback + self.object.callback.get() } } impl CallbackInterface { /// Create a new CallbackInterface object for the given `JSObject`. - pub fn new(callback: *mut JSObject) -> CallbackInterface { + pub fn new() -> CallbackInterface { CallbackInterface { object: CallbackObject { - callback: callback + callback: Heap::default() } } } + /// Initialize the callback function with a value. + /// Should be called once this object is done moving. + pub fn init(&mut self, callback: *mut JSObject) { + self.object.callback.set(callback); + } + /// Returns the property with the given `name`, if it is a callable object, /// or an error otherwise. pub fn get_callable_property(&self, cx: *mut JSContext, name: &str) -> Fallible<JSVal> { - let mut callable = UndefinedValue(); + let mut callable = RootedValue::new(cx, UndefinedValue()); + let obj = RootedObject::new(cx, self.callback()); unsafe { let c_name = CString::new(name).unwrap(); - if JS_GetProperty(cx, self.callback(), c_name.as_ptr(), &mut callable) == 0 { + if JS_GetProperty(cx, obj.handle(), c_name.as_ptr(), + callable.handle_mut()) == 0 { return Err(Error::JSFailed); } - if !callable.is_object() || - JS_ObjectIsCallable(cx, callable.to_object()) == 0 { + if !callable.ptr.is_object() || + IsCallable(callable.ptr.to_object()) == 0 { return Err(Error::Type( format!("The value of the {} property is not callable", name))); } } - Ok(callable) + Ok(callable.ptr) } } /// Wraps the reflector for `p` into the compartment of `cx`. pub fn wrap_call_this_object<T: Reflectable>(cx: *mut JSContext, - p: JSRef<T>) -> *mut JSObject { - let mut obj = p.reflector().get_jsobject(); - assert!(!obj.is_null()); + p: &T, + mut rval: MutableHandleObject) { + rval.set(p.reflector().get_jsobject().get()); + assert!(!rval.get().is_null()); unsafe { - if JS_WrapObject(cx, &mut obj) == 0 { - return ptr::null_mut(); + if JS_WrapObject(cx, rval) == 0 { + rval.set(ptr::null_mut()); } } - - return obj; } /// A class that performs whatever setup we need to safely make a call while /// this class is on the stack. After `new` returns, the call is safe to make. pub struct CallSetup { + /// The compartment for reporting exceptions. + /// As a RootedObject, this must be the first field in order to + /// determine the final address on the stack correctly. + exception_compartment: RootedObject, /// The `JSContext` used for the call. cx: *mut JSContext, + /// The compartment we were in before the call. + old_compartment: *mut JSCompartment, /// The exception handling used for the call. - _handling: ExceptionHandling, + handling: ExceptionHandling, } impl CallSetup { /// Performs the setup needed to make a call. #[allow(unrooted_must_root)] - pub fn new<T: CallbackContainer>(callback: T, handling: ExceptionHandling) -> CallSetup { + pub fn new<T: CallbackContainer>(callback: &T, handling: ExceptionHandling) -> CallSetup { let global = global_object_for_js_object(callback.callback()); - let global = global.root(); let cx = global.r().get_cx(); + unsafe { JS_BeginRequest(cx); } + let exception_compartment = unsafe { + GetGlobalForObjectCrossCompartment(callback.callback()) + }; CallSetup { + exception_compartment: + RootedObject::new_with_addr(cx, exception_compartment, + unsafe { return_address() }), cx: cx, - _handling: handling, + old_compartment: unsafe { JS_EnterCompartment(cx, callback.callback()) }, + handling: handling, } } @@ -160,14 +198,23 @@ impl CallSetup { impl Drop for CallSetup { fn drop(&mut self) { - let need_to_deal_with_exception = unsafe { JS_IsExceptionPending(self.cx) } != 0; + unsafe { JS_LeaveCompartment(self.cx, self.old_compartment); } + let need_to_deal_with_exception = + self.handling == ExceptionHandling::Report && + unsafe { JS_IsExceptionPending(self.cx) } != 0; if need_to_deal_with_exception { unsafe { - let old_global = JS_GetGlobalObject(self.cx); - with_compartment(self.cx, old_global, || { - JS_ReportPendingException(self.cx) - }); + let old_global = RootedObject::new(self.cx, self.exception_compartment.ptr); + let saved = JS_SaveFrameChain(self.cx) != 0; + { + let _ac = JSAutoCompartment::new(self.cx, old_global.ptr); + JS_ReportPendingException(self.cx); + } + if saved { + JS_RestoreFrameChain(self.cx); + } } } + unsafe { JS_EndRequest(self.cx); } } } diff --git a/components/script/dom/bindings/cell.rs b/components/script/dom/bindings/cell.rs index 177fed9397b..6513e250166 100644 --- a/components/script/dom/bindings/cell.rs +++ b/components/script/dom/bindings/cell.rs @@ -40,7 +40,9 @@ impl<T> DOMRefCell<T> { /// so you have to be careful in trace code! #[allow(unsafe_code)] pub unsafe fn borrow_for_gc_trace<'a>(&'a self) -> &'a T { - debug_assert!(task_state::get().contains(SCRIPT | IN_GC)); + // FIXME: IN_GC isn't reliable enough - doesn't catch minor GCs + // https://github.com/servo/servo/issues/6389 + //debug_assert!(task_state::get().contains(SCRIPT | IN_GC)); &*self.value.as_unsafe_cell().get() } diff --git a/components/script/dom/bindings/codegen/Bindings.conf b/components/script/dom/bindings/codegen/Bindings.conf index 439ddfbd59e..48b6067d00c 100644 --- a/components/script/dom/bindings/codegen/Bindings.conf +++ b/components/script/dom/bindings/codegen/Bindings.conf @@ -15,7 +15,7 @@ DOMInterfaces = { 'Window': { - 'outerObjectHook': 'Some(bindings::utils::outerize_global as extern fn(*mut JSContext, JSHandleObject) -> *mut JSObject)', + 'outerObjectHook': 'Some(bindings::utils::outerize_global)', }, #FIXME(jdm): This should be 'register': False, but then we don't generate enum types diff --git a/components/script/dom/bindings/codegen/CodegenRust.py b/components/script/dom/bindings/codegen/CodegenRust.py index aa1b1ae14d3..f9e929c68da 100644 --- a/components/script/dom/bindings/codegen/CodegenRust.py +++ b/components/script/dom/bindings/codegen/CodegenRust.py @@ -8,12 +8,15 @@ import operator import os import re import string +import textwrap +import functools from WebIDL import ( BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLType, + IDLInterfaceMember, IDLUndefinedValue, ) @@ -103,15 +106,16 @@ class CastableObjectUnwrapper(): codeOnFailure is the code to run if unwrapping fails. """ - def __init__(self, descriptor, source, codeOnFailure): + def __init__(self, descriptor, source, codeOnFailure, handletype): self.substitution = { "source": source, "codeOnFailure": CGIndenter(CGGeneric(codeOnFailure), 8).define(), + "handletype": handletype, } def __str__(self): return string.Template("""\ -match native_from_reflector_jsmanaged(${source}) { +match native_from_handle${handletype}(${source}) { Ok(val) => val, Err(()) => { ${codeOnFailure} @@ -119,6 +123,136 @@ ${codeOnFailure} }""").substitute(self.substitution) +# We'll want to insert the indent at the beginnings of lines, but we +# don't want to indent empty lines. So only indent lines that have a +# non-newline character on them. +lineStartDetector = re.compile("^(?=[^\n#])", re.MULTILINE) + + +def indent(s, indentLevel=2): + """ + Indent C++ code. + + Weird secret feature: this doesn't indent lines that start with # (such as + #include lines or #ifdef/#endif). + """ + if s == "": + return s + return re.sub(lineStartDetector, indentLevel * " ", s) + + +# dedent() and fill() are often called on the same string multiple +# times. We want to memoize their return values so we don't keep +# recomputing them all the time. +def memoize(fn): + """ + Decorator to memoize a function of one argument. The cache just + grows without bound. + """ + cache = {} + @functools.wraps(fn) + def wrapper(arg): + retval = cache.get(arg) + if retval is None: + retval = cache[arg] = fn(arg) + return retval + return wrapper + +@memoize +def dedent(s): + """ + Remove all leading whitespace from s, and remove a blank line + at the beginning. + """ + if s.startswith('\n'): + s = s[1:] + return textwrap.dedent(s) + + +# This works by transforming the fill()-template to an equivalent +# string.Template. +fill_multiline_substitution_re = re.compile(r"( *)\$\*{(\w+)}(\n)?") + + +@memoize +def compile_fill_template(template): + """ + Helper function for fill(). Given the template string passed to fill(), + do the reusable part of template processing and return a pair (t, + argModList) that can be used every time fill() is called with that + template argument. + + argsModList is list of tuples that represent modifications to be + made to args. Each modification has, in order: i) the arg name, + ii) the modified name, iii) the indent depth. + """ + t = dedent(template) + assert t.endswith("\n") or "\n" not in t + argModList = [] + + def replace(match): + """ + Replaces a line like ' $*{xyz}\n' with '${xyz_n}', + where n is the indent depth, and add a corresponding entry to + argModList. + + Note that this needs to close over argModList, so it has to be + defined inside compile_fill_template(). + """ + indentation, name, nl = match.groups() + depth = len(indentation) + + # Check that $*{xyz} appears by itself on a line. + prev = match.string[:match.start()] + if (prev and not prev.endswith("\n")) or nl is None: + raise ValueError("Invalid fill() template: $*{%s} must appear by itself on a line" % name) + + # Now replace this whole line of template with the indented equivalent. + modified_name = name + "_" + str(depth) + argModList.append((name, modified_name, depth)) + return "${" + modified_name + "}" + + t = re.sub(fill_multiline_substitution_re, replace, t) + return (string.Template(t), argModList) + +def fill(template, **args): + """ + Convenience function for filling in a multiline template. + + `fill(template, name1=v1, name2=v2)` is a lot like + `string.Template(template).substitute({"name1": v1, "name2": v2})`. + + However, it's shorter, and has a few nice features: + + * If `template` is indented, fill() automatically dedents it! + This makes code using fill() with Python's multiline strings + much nicer to look at. + + * If `template` starts with a blank line, fill() strips it off. + (Again, convenient with multiline strings.) + + * fill() recognizes a special kind of substitution + of the form `$*{name}`. + + Use this to paste in, and automatically indent, multiple lines. + (Mnemonic: The `*` is for "multiple lines"). + + A `$*` substitution must appear by itself on a line, with optional + preceding indentation (spaces only). The whole line is replaced by the + corresponding keyword argument, indented appropriately. If the + argument is an empty string, no output is generated, not even a blank + line. + """ + + t, argModList = compile_fill_template(template) + # Now apply argModList to args + for (name, modified_name, depth) in argModList: + if not (args[name] == "" or args[name].endswith("\n")): + raise ValueError("Argument %s with value %r is missing a newline" % (name, args[name])) + args[modified_name] = indent(args[name], depth) + + return t.substitute(args) + class CGThing(): """ Abstract base class for things that spit out code. @@ -232,14 +366,13 @@ class CGMethodCall(CGThing): # Doesn't matter which of the possible signatures we use, since # they all have the same types up to that point; just use # possibleSignatures[0] - caseBody = [CGGeneric("let argv_start = JS_ARGV(cx, vp);")] - caseBody.extend([ CGArgumentConverter(possibleSignatures[0][1][i], - i, "argv_start", "argc", + caseBody = [ CGArgumentConverter(possibleSignatures[0][1][i], + i, "args", "argc", descriptor) for i in - range(0, distinguishingIndex) ]) + range(0, distinguishingIndex) ] # Select the right overload from our set. - distinguishingArg = "(*argv_start.offset(%d))" % distinguishingIndex + distinguishingArg = "args.get(%d)" % distinguishingIndex def pickFirstSignature(condition, filterLambda): sigs = filter(filterLambda, possibleSignatures) @@ -282,7 +415,7 @@ class CGMethodCall(CGThing): # also allow the unwrapping test to skip having to do codegen # for the null-or-undefined case, which we already handled # above. - caseBody.append(CGGeneric("if (%s).is_object() {" % + caseBody.append(CGGeneric("if %s.get().is_object() {" % (distinguishingArg))) for idx, sig in enumerate(interfacesSigs): caseBody.append(CGIndenter(CGGeneric("loop {"))); @@ -319,7 +452,7 @@ class CGMethodCall(CGThing): # XXXbz Now we're supposed to check for distinguishingArg being # an array or a platform object that supports indexed # properties... skip that last for now. It's a bit of a pain. - pickFirstSignature("%s.isObject() && IsArrayLike(cx, &%s.toObject())" % + pickFirstSignature("%s.get().isObject() && IsArrayLike(cx, &%s.get().toObject())" % (distinguishingArg, distinguishingArg), lambda s: (s[1][distinguishingIndex].type.isArray() or @@ -328,14 +461,14 @@ class CGMethodCall(CGThing): # Check for Date objects # XXXbz Do we need to worry about security wrappers around the Date? - pickFirstSignature("%s.isObject() && JS_ObjectIsDate(cx, &%s.toObject())" % + pickFirstSignature("%s.get().isObject() && JS_ObjectIsDate(cx, &%s.get().toObject())" % (distinguishingArg, distinguishingArg), lambda s: (s[1][distinguishingIndex].type.isDate() or s[1][distinguishingIndex].type.isObject())) # Check for vanilla JS objects # XXXbz Do we need to worry about security wrappers? - pickFirstSignature("%s.is_object() && !is_platform_object(%s.to_object())" % + pickFirstSignature("%s.get().is_object() && !is_platform_object(%s.get().to_object())" % (distinguishingArg, distinguishingArg), lambda s: (s[1][distinguishingIndex].type.isCallback() or s[1][distinguishingIndex].type.isCallbackInterface() or @@ -524,7 +657,7 @@ def getJSToNativeConversionInfo(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 JSFalse;" needsRooting = typeNeedsRooting(type, descriptorProvider) @@ -581,11 +714,11 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, # Handle the non-object cases by wrapping up the whole # thing in an if cascade. templateBody = ( - "if (${val}).is_object() {\n" + + "if ${val}.get().is_object() {\n" + CGIndenter(CGGeneric(templateBody)).define() + "\n") if type.nullable(): templateBody += ( - "} else if (${val}).is_null_or_undefined() {\n" + "} else if ${val}.get().is_null_or_undefined() {\n" " %s\n") % nullValue templateBody += ( "} else {\n" + @@ -621,8 +754,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, if descriptor.interface.isCallback(): name = descriptor.nativeType - declType = CGGeneric(name) - template = "%s::new((${val}).to_object())" % name + declType = CGWrapper(CGGeneric(name), pre="Rc<", post=">") + template = "%s::new(${val}.get().to_object())" % name if type.nullable(): declType = CGWrapper(declType, pre="Option<", post=">") template = wrapObjectTemplate("Some(%s)" % template, "None", @@ -658,17 +791,15 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, templateBody = str(CastableObjectUnwrapper( descriptor, - "(${val}).to_object()", - unwrapFailureCode)) + "${val}", + unwrapFailureCode, + "value")) declType = CGGeneric(descriptorType) if type.nullable(): templateBody = "Some(%s)" % templateBody declType = CGWrapper(declType, pre="Option<", post=">") - if isMember: - templateBody += ".root()" - templateBody = wrapObjectTemplate(templateBody, "None", isDefinitelyObject, type, failureCode) @@ -681,8 +812,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, assert not isEnforceRange and not isClamp treatAs = { - "Default": "Default", - "EmptyString": "Empty", + "Default": "StringificationBehavior::Default", + "EmptyString": "StringificationBehavior::Empty", } if treatNullAs not in treatAs: raise TypeError("We don't support [TreatNullAs=%s]" % treatNullAs) @@ -794,23 +925,25 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, assert not type.treatNonObjectAsNull() or not type.treatNonCallableAsNull() declType = CGGeneric('%s::%s' % (type.unroll().module(), type.unroll().identifier.name)) + finalDeclType = CGTemplatedType("Rc", declType) conversion = CGCallbackTempRoot(declType.define()) if type.nullable(): declType = CGTemplatedType("Option", declType) + finalDeclType = CGTemplatedType("Option", finalDeclType) conversion = CGWrapper(conversion, pre="Some(", post=")") if allowTreatNonObjectAsNull and type.treatNonObjectAsNull(): if not isDefinitelyObject: - haveObject = "${val}.is_object()" + haveObject = "${val}.get().is_object()" template = CGIfElseWrapper(haveObject, conversion, CGGeneric("None")).define() else: template = conversion else: - template = CGIfElseWrapper("JS_ObjectIsCallable(cx, ${val}.to_object()) != 0", + template = CGIfElseWrapper("IsCallable(${val}.get().to_object()) != 0", conversion, onFailureNotCallable(failureCode)).define() template = wrapObjectTemplate( @@ -829,29 +962,47 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, else: default = None - return JSToNativeConversionInfo(template, default, declType, needsRooting=needsRooting) + return JSToNativeConversionInfo(template, default, finalDeclType, needsRooting=needsRooting) if type.isAny(): assert not isEnforceRange and not isClamp - declType = CGGeneric("JSVal") - - if defaultValue is None: - default = None - elif isinstance(defaultValue, IDLNullValue): - default = "NullValue()" - elif isinstance(defaultValue, IDLUndefinedValue): - default = "UndefinedValue()" + declType = "" + default = "" + if isMember == "Dictionary": + # TODO: Need to properly root dictionaries + # https://github.com/servo/servo/issues/6381 + declType = CGGeneric("JSVal") + + if defaultValue is None: + default = None + elif isinstance(defaultValue, IDLNullValue): + default = "NullValue()" + elif isinstance(defaultValue, IDLUndefinedValue): + default = "UndefinedValue()" + else: + raise TypeError("Can't handle non-null, non-undefined default value here") else: - raise TypeError("Can't handle non-null, non-undefined default value here") + declType = CGGeneric("HandleValue") + + if defaultValue is None: + default = None + elif isinstance(defaultValue, IDLNullValue): + default = "HandleValue::null()" + elif isinstance(defaultValue, IDLUndefinedValue): + default = "HandleValue::undefined()" + else: + raise TypeError("Can't handle non-null, non-undefined default value here") return handleOptional("${val}", declType, default) if type.isObject(): assert not isEnforceRange and not isClamp + # TODO: Need to root somehow + # https://github.com/servo/servo/issues/6382 declType = CGGeneric("*mut JSObject") - templateBody = wrapObjectTemplate("${val}.to_object()", + templateBody = wrapObjectTemplate("${val}.get().to_object()", "ptr::null_mut()", isDefinitelyObject, type, failureCode) @@ -871,7 +1022,7 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None, " Err(_) => return 0,\n" "}" % typeName) - return handleOptional(template, declType, handleDefaultNull("%s::empty()" % typeName)) + return handleOptional(template, declType, handleDefaultNull("%s::empty(cx)" % typeName)) if type.isVoid(): # This one only happens for return values, and its easy: Just @@ -948,11 +1099,6 @@ def instantiateJSToNativeConversionTemplate(templateBody, replacements, # conversion. result.append(CGGeneric("")) - if needsRooting: - rootBody = "let %s = %s.root();" % (declName, declName) - result.append(CGGeneric(rootBody)) - result.append(CGGeneric("")) - return result; def convertConstIDLValueToJSVal(value): @@ -979,7 +1125,7 @@ class CGArgumentConverter(CGThing): argument list, and the argv and argc strings and generates code to unwrap the argument to the right native type. """ - def __init__(self, argument, index, argv, argc, descriptorProvider, + def __init__(self, argument, index, args, argc, descriptorProvider, invalidEnumValueFatal=True): CGThing.__init__(self) assert(not argument.defaultValue or argument.optional) @@ -987,12 +1133,12 @@ class CGArgumentConverter(CGThing): replacer = { "index": index, "argc": argc, - "argv": argv + "args": args } condition = string.Template("${index} < ${argc}").substitute(replacer) replacementVariables = { - "val": string.Template("(*${argv}.offset(${index}))").substitute(replacer), + "val": string.Template("${args}.get(${index})").substitute(replacer), } info = getJSToNativeConversionInfo( @@ -1032,7 +1178,7 @@ class CGArgumentConverter(CGThing): else: assert argument.optional variadicConversion = { - "val": string.Template("(*${argv}.offset(variadicArg as isize))").substitute(replacer), + "val": string.Template("${args}.get(variadicArg)").substitute(replacer), } innerConverter = instantiateJSToNativeConversionTemplate( template, variadicConversion, declType, "slot", @@ -1066,16 +1212,17 @@ class CGArgumentConverter(CGThing): return self.converter.define() -def wrapForType(jsvalRef, result='result', successCode='return 1;'): +def wrapForType(jsvalRef, result='result', successCode='return 1;', pre=''): """ Reflect a Rust value into JS. - * 'jsvalRef': a Rust reference to the JSVal in which to store the result + * 'jsvalRef': a MutableHandleValue in which to store the result of the conversion; * 'result': the name of the variable in which the Rust value is stored; * 'successCode': the code to run once we have done the conversion. + * 'pre': code to run before the conversion if rooting is necessary """ - wrap = "%s = (%s).to_jsval(cx);" % (jsvalRef, result) + wrap = "%s\n(%s).to_jsval(cx, %s);" % (pre, result, jsvalRef) if successCode: wrap += "\n%s" % successCode return wrap @@ -1142,8 +1289,8 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): result = CGWrapper(result, pre="Option<", post=">") return result if returnType.isCallback(): - result = CGGeneric('%s::%s' % (returnType.unroll().module(), - returnType.unroll().identifier.name)) + result = CGGeneric('Rc<%s::%s>' % (returnType.unroll().module(), + returnType.unroll().identifier.name)) if returnType.nullable(): result = CGWrapper(result, pre="Option<", post=">") return result @@ -1152,6 +1299,8 @@ def getRetvalDeclarationForType(returnType, descriptorProvider): if returnType.nullable(): result = CGWrapper(result, pre="Option<", post=">") return result + # TODO: Return the value through a MutableHandleValue outparam + # https://github.com/servo/servo/issues/6307 if returnType.isAny(): return CGGeneric("JSVal") if returnType.isObject() or returnType.isSpiderMonkeyInterface(): @@ -1258,9 +1407,9 @@ class MethodDefiner(PropertyDefiner): # 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', + self.regular.append({"name": '@@iterator', "methodInfo": False, - "nativeName": "JS_ArrayIterator", + "selfHostedName": "ArrayValues", "length": 0, "flags": "JSPROP_ENUMERATE" }) @@ -1280,21 +1429,31 @@ class MethodDefiner(PropertyDefiner): return "" def specData(m): - if m.get("methodInfo", True): - identifier = m.get("nativeName", m["name"]) - jitinfo = "&%s_methodinfo" % identifier - accessor = "genericMethod as NonNullJSNative" - else: + # TODO: Use something like JS_FNSPEC + # https://github.com/servo/servo/issues/6391 + if "selfHostedName" in m: + selfHostedName = '%s as *const u8 as *const i8' % str_to_const_array(m["selfHostedName"]) + assert not m.get("methodInfo", True) + accessor = "None" jitinfo = "0 as *const JSJitInfo" - accessor = m.get("nativeName", m["name"]) - if accessor[0:3] != 'JS_': - accessor = "%s as NonNullJSNative" % accessor - return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], m["flags"]) + else: + selfHostedName = "0 as *const i8" + if m.get("methodInfo", True): + identifier = m.get("nativeName", m["name"]) + jitinfo = "&%s_methodinfo" % identifier + accessor = "Some(genericMethod)" + else: + jitinfo = "0 as *const JSJitInfo" + accessor = 'Some(%s)' % m.get("nativeName", m["name"]) + if m["name"].startswith("@@"): + return ('(SymbolCode::%s as i32 + 1)' % m["name"][2:], accessor, jitinfo, m["length"], m["flags"], selfHostedName) + return (str_to_const_array(m["name"]), accessor, jitinfo, m["length"], m["flags"], selfHostedName) + return self.generatePrefableArray( array, name, - ' JSFunctionSpec { name: %s 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 { name: %s as *const u8 as *const libc::c_char, call: JSNativeWrapper {op: %s, info: %s}, nargs: %s, flags: %s as u16, selfHostedName: %s }', + ' JSFunctionSpec { name: 0 as *const i8, call: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }, nargs: 0, flags: 0, selfHostedName: 0 as *const i8 }', 'JSFunctionSpec', specData) @@ -1314,12 +1473,12 @@ class AttrDefiner(PropertyDefiner): return "" def flags(attr): - return "JSPROP_SHARED | JSPROP_ENUMERATE | JSPROP_NATIVE_ACCESSORS" + return "JSPROP_SHARED | JSPROP_ENUMERATE" def getter(attr): if self.static: accessor = 'get_' + attr.identifier.name - jitinfo = "0" + jitinfo = "0 as *const JSJitInfo" else: if attr.hasLenientThis(): accessor = "genericLenientGetter" @@ -1327,17 +1486,17 @@ class AttrDefiner(PropertyDefiner): accessor = "genericGetter" jitinfo = "&%s_getterinfo" % attr.identifier.name - return ("JSPropertyOpWrapper {op: Some(%(native)s as NonNullJSNative), info: %(info)s as *const JSJitInfo}" + return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" % {"info" : jitinfo, "native" : accessor}) def setter(attr): if attr.readonly and not attr.getExtendedAttribute("PutForwards"): - return "JSStrictPropertyOpWrapper {op: None, info: 0 as *const JSJitInfo}" + return "JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }" if self.static: accessor = 'set_' + attr.identifier.name - jitinfo = "0" + jitinfo = "0 as *const JSJitInfo" else: if attr.hasLenientThis(): accessor = "genericLenientSetter" @@ -1345,7 +1504,7 @@ class AttrDefiner(PropertyDefiner): accessor = "genericSetter" jitinfo = "&%s_setterinfo" % attr.identifier.name - return ("JSStrictPropertyOpWrapper {op: Some(%(native)s as NonNullJSNative), info: %(info)s as *const JSJitInfo}" + return ("JSNativeWrapper { op: Some(%(native)s), info: %(info)s }" % {"info" : jitinfo, "native" : accessor}) @@ -1355,8 +1514,8 @@ class AttrDefiner(PropertyDefiner): return self.generatePrefableArray( array, name, - ' JSPropertySpec { name: %s 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 { name: %s as *const u8 as *const libc::c_char, flags: ((%s) & 0xFF) as u8, getter: %s, setter: %s }', + ' JSPropertySpec { name: 0 as *const i8, flags: 0, getter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo }, setter: JSNativeWrapper { op: None, info: 0 as *const JSJitInfo } }', 'JSPropertySpec', specData) @@ -1562,8 +1721,9 @@ class CGDOMJSClass(CGThing): self.descriptor = descriptor def define(self): - traceHook = 'Some(%s as unsafe extern "C" fn(*mut JSTracer, *mut JSObject))' % TRACE_HOOK_NAME + traceHook = 'Some(%s)' % TRACE_HOOK_NAME if self.descriptor.isGlobal(): + traceHook = "Some(js::jsapi::_Z24JS_GlobalObjectTraceHookP8JSTracerP8JSObject)" flags = "JSCLASS_IS_GLOBAL | JSCLASS_DOM_GLOBAL" slots = "JSCLASS_GLOBAL_SLOT_COUNT + 1" else: @@ -1571,66 +1731,54 @@ class CGDOMJSClass(CGThing): slots = "1" return """\ static Class: DOMJSClass = DOMJSClass { - base: js::Class { + base: js::jsapi::Class { name: %s as *const u8 as *const libc::c_char, - flags: JSCLASS_IS_DOMJSCLASS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), //JSCLASS_HAS_RESERVED_SLOTS(%s), - addProperty: Some(JS_PropertyStub), - delProperty: Some(JS_PropertyStub), - getProperty: Some(JS_PropertyStub), - setProperty: Some(JS_StrictPropertyStub), - enumerate: Some(JS_EnumerateStub), - resolve: Some(JS_ResolveStub), - convert: Some(JS_ConvertStub), - finalize: Some(%s as unsafe extern "C" fn(*mut JSFreeOp, *mut JSObject)), - checkAccess: None, + flags: JSCLASS_IS_DOMJSCLASS | JSCLASS_IMPLEMENTS_BARRIERS | %s | (((%s) & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT), //JSCLASS_HAS_RESERVED_SLOTS(%s), + addProperty: None, + delProperty: None, + getProperty: None, + setProperty: None, + enumerate: None, + resolve: None, + convert: None, + finalize: Some(%s), call: None, hasInstance: None, construct: None, trace: %s, - ext: js::ClassExtension { - equality: 0 as *const u8, + spec: js::jsapi::ClassSpec { + createConstructor: None, + createPrototype: None, + constructorFunctions: 0 as *const js::jsapi::JSFunctionSpec, + constructorProperties: 0 as *const js::jsapi::JSPropertySpec, + prototypeFunctions: 0 as *const js::jsapi::JSFunctionSpec, + prototypeProperties: 0 as *const js::jsapi::JSPropertySpec, + finishInit: None, + flags: 0, + }, + + ext: js::jsapi::ClassExtension { outerObject: %s, innerObject: None, - iteratorObject: 0 as *const u8, - unused: 0 as *const u8, - isWrappedNative: 0 as *const u8, + isWrappedNative: 0, + weakmapKeyDelegateOp: None, + objectMovedOp: 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, - - enumerate: 0 as *const u8, - typeOf: 0 as *const u8, + ops: js::jsapi::ObjectOps { + lookupProperty: None, + defineProperty: None, + hasProperty: None, + getProperty: None, + setProperty: None, + getOwnPropertyDescriptor: None, + deleteProperty: None, + watch: None, + unwatch: None, + getElements: None, + enumerate: None, thisObject: %s, - clear: 0 as *const u8, }, }, dom_class: %s @@ -1640,7 +1788,7 @@ static Class: DOMJSClass = DOMJSClass { FINALIZE_HOOK_NAME, traceHook, self.descriptor.outerObjectHook, self.descriptor.outerObjectHook, - CGIndenter(CGGeneric(DOMClass(self.descriptor))).define()) + CGGeneric(DOMClass(self.descriptor)).define()) def str_to_const_array(s): return "b\"%s\\0\"" % s @@ -1655,20 +1803,19 @@ class CGPrototypeJSClass(CGThing): static PrototypeClass: JSClass = JSClass { name: %s as *const u8 as *const libc::c_char, flags: (1 & JSCLASS_RESERVED_SLOTS_MASK) << JSCLASS_RESERVED_SLOTS_SHIFT, //JSCLASS_HAS_RESERVED_SLOTS(1) - addProperty: Some(JS_PropertyStub), - delProperty: Some(JS_PropertyStub), - getProperty: Some(JS_PropertyStub), - setProperty: Some(JS_StrictPropertyStub), - enumerate: Some(JS_EnumerateStub), - resolve: Some(JS_ResolveStub), - convert: Some(JS_ConvertStub), + addProperty: None, + delProperty: None, + getProperty: None, + setProperty: None, + enumerate: None, + resolve: None, + convert: None, 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; 25] }; """ % str_to_const_array(self.descriptor.interface.identifier.name + "Prototype") @@ -1742,13 +1889,7 @@ class CGGeneric(CGThing): class CGCallbackTempRoot(CGGeneric): def __init__(self, name): - val = "%s::new(tempRoot)" % name - define = """\ -{ - let tempRoot = ${val}.to_object(); - %s -}""" % val - CGGeneric.__init__(self, define) + CGGeneric.__init__(self, "%s::new(${val}.get().to_object())" % name) def getAllTypes(descriptors, dictionaries, callbacks): @@ -1792,12 +1933,13 @@ def UnionTypes(descriptors, dictionaries, callbacks, config): 'dom::bindings::codegen::PrototypeList', 'dom::bindings::conversions::FromJSValConvertible', 'dom::bindings::conversions::ToJSValConvertible', - 'dom::bindings::conversions::native_from_reflector_jsmanaged', - 'dom::bindings::conversions::StringificationBehavior::Default', + 'dom::bindings::conversions::native_from_handlevalue', + 'dom::bindings::conversions::StringificationBehavior', 'dom::bindings::error::throw_not_in_union', - 'dom::bindings::js::Unrooted', + 'dom::bindings::js::Root', 'dom::types::*', 'js::jsapi::JSContext', + 'js::jsapi::{HandleValue, MutableHandleValue}', 'js::jsval::JSVal', 'util::str::DOMString', ] @@ -1917,32 +2059,36 @@ class CGAbstractMethod(CGThing): assert(False) # Override me! def CreateBindingJSObject(descriptor, parent=None): - create = "let mut raw: Unrooted<%s> = Unrooted::from_raw(&*object);\n" % descriptor.concreteType + create = "let mut raw = boxed::into_raw(object);\nlet _rt = RootedTraceable::new(&*raw);\n" if descriptor.proxy: assert not descriptor.isGlobal() create += """ let handler = RegisterBindings::proxy_handlers[PrototypeList::Proxies::%s as usize]; -let mut private = PrivateValue(boxed::into_raw(object) as *const libc::c_void); -let obj = with_compartment(cx, proto, || { +let private = RootedValue::new(cx, PrivateValue(raw as *const libc::c_void)); +let obj = { + let _ac = JSAutoCompartment::new(cx, proto.ptr); NewProxyObject(cx, handler, - &private, - proto, %s, + private.handle(), + proto.ptr, %s.get(), ptr::null_mut(), ptr::null_mut()) -}); -assert!(!obj.is_null());\ +}; +assert!(!obj.is_null()); +let obj = RootedObject::new(cx, obj);\ """ % (descriptor.name, parent) else: if descriptor.isGlobal(): - create += "let obj = create_dom_global(cx, &Class.base as *const js::Class as *const JSClass);\n" + create += "let obj = RootedObject::new(cx, create_dom_global(cx, &Class.base as *const js::jsapi::Class as *const JSClass, Some(%s)));\n" % TRACE_HOOK_NAME else: - create += ("let obj = with_compartment(cx, proto, || {\n" - " JS_NewObject(cx, &Class.base as *const js::Class as *const JSClass, &*proto, &*%s)\n" - "});\n" % parent) + create += ("let obj = {\n" + " let _ac = JSAutoCompartment::new(cx, proto.ptr);\n" + " JS_NewObjectWithGivenProto(cx, &Class.base as *const js::jsapi::Class as *const JSClass, proto.handle())\n" + "};\n" + "let obj = RootedObject::new(cx, obj);\n") create += """\ -assert!(!obj.is_null()); +assert!(!obj.ptr.is_null()); -JS_SetReservedSlot(obj, DOM_OBJECT_SLOT, - PrivateValue(boxed::into_raw(object) as *const libc::c_void));""" +JS_SetReservedSlot(obj.ptr, DOM_OBJECT_SLOT, + PrivateValue(raw as *const libc::c_void));""" return create class CGWrapMethod(CGAbstractMethod): @@ -1958,37 +2104,46 @@ class CGWrapMethod(CGAbstractMethod): else: args = [Argument('*mut JSContext', 'cx'), Argument("Box<%s>" % descriptor.concreteType, 'object', mutable=True)] - retval = 'Temporary<%s>' % descriptor.concreteType + retval = 'Root<%s>' % descriptor.concreteType CGAbstractMethod.__init__(self, descriptor, 'Wrap', retval, args, pub=True) def definition_body(self): if not self.descriptor.isGlobal(): return CGGeneric("""\ +let _ar = JSAutoRequest::new(cx); let scope = scope.reflector().get_jsobject(); -assert!(!scope.is_null()); -assert!(((*JS_GetClass(scope)).flags & JSCLASS_IS_GLOBAL) != 0); +assert!(!scope.get().is_null()); +assert!(((*JS_GetClass(scope.get())).flags & JSCLASS_IS_GLOBAL) != 0); -let proto = with_compartment(cx, scope, || GetProtoObject(cx, scope, scope)); -assert!(!proto.is_null()); +let mut proto = RootedObject::new(cx, ptr::null_mut()); +{ + let _ac = JSAutoCompartment::new(cx, scope.get()); + GetProtoObject(cx, scope, scope, proto.handle_mut()) +} +assert!(!proto.ptr.is_null()); %s -raw.reflector().set_jsobject(obj); +(*raw).init_reflector(obj.ptr); -Temporary::from_unrooted(raw)""" % CreateBindingJSObject(self.descriptor, "scope")) +Root::from_ref(&*raw)""" % CreateBindingJSObject(self.descriptor, "scope")) else: return CGGeneric("""\ +let _ar = JSAutoRequest::new(cx); %s -with_compartment(cx, obj, || { - let proto = GetProtoObject(cx, obj, obj); - JS_SetPrototype(cx, obj, proto); - raw.reflector().set_jsobject(obj); +let _ac = JSAutoCompartment::new(cx, obj.ptr); +let mut proto = RootedObject::new(cx, ptr::null_mut()); +GetProtoObject(cx, obj.handle(), obj.handle(), proto.handle_mut()); +JS_SetPrototype(cx, obj.handle(), proto.handle()); + +(*raw).init_reflector(obj.ptr); - RegisterBindings::Register(cx, obj); -}); +let ret = Root::from_ref(&*raw); -Temporary::from_unrooted(raw)""" % CreateBindingJSObject(self.descriptor)) +RegisterBindings::Register(cx, obj.handle()); + +ret""" % CreateBindingJSObject(self.descriptor)) class CGIDLInterface(CGThing): @@ -2083,21 +2238,23 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): properties should be a PropertyArrays instance. """ def __init__(self, descriptor, properties): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'global'), - Argument('*mut JSObject', 'receiver')] - CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', '*mut JSObject', args) + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'), + Argument('HandleObject', 'receiver'), + Argument('MutableHandleObject', 'rval')] + CGAbstractMethod.__init__(self, descriptor, 'CreateInterfaceObjects', 'void', args) self.properties = properties def definition_body(self): protoChain = self.descriptor.prototypeChain if len(protoChain) == 1: - getParentProto = "JS_GetObjectPrototype(cx, global)" + getParentProto = "parent_proto.ptr = JS_GetObjectPrototype(cx, global)" else: parentProtoName = self.descriptor.prototypeChain[-2] - getParentProto = ("%s::GetProtoObject(cx, global, receiver)" % + getParentProto = ("%s::GetProtoObject(cx, global, receiver, parent_proto.handle_mut())" % toBindingNamespace(parentProtoName)) - getParentProto = ("let parent_proto: *mut JSObject = %s;\n" - "assert!(!parent_proto.is_null());\n") % getParentProto + getParentProto = ("let mut parent_proto = RootedObject::new(cx, ptr::null_mut());\n" + "%s;\n" + "assert!(!parent_proto.ptr.is_null());\n") % getParentProto if self.descriptor.interface.isCallback(): protoClass = "None" @@ -2127,10 +2284,10 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod): constructor = 'None' call = """\ -return do_create_interface_objects(cx, global, receiver, parent_proto, - %s, %s, - %s, - &sNativeProperties);""" % (protoClass, constructor, domClass) +do_create_interface_objects(cx, receiver, parent_proto.handle(), + %s, %s, + %s, + &sNativeProperties, rval);""" % (protoClass, constructor, domClass) return CGList([ CGGeneric(getParentProto), @@ -2143,10 +2300,11 @@ class CGGetPerInterfaceObject(CGAbstractMethod): constructor object). """ def __init__(self, descriptor, name, idPrefix="", pub=False): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'global'), - Argument('*mut JSObject', 'receiver')] + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'global'), + Argument('HandleObject', 'receiver'), + Argument('MutableHandleObject', 'rval')] CGAbstractMethod.__init__(self, descriptor, name, - '*mut JSObject', args, pub=pub) + 'void', args, pub=pub) self.id = idPrefix + "ID::" + self.descriptor.name def definition_body(self): return CGGeneric(""" @@ -2157,19 +2315,22 @@ class CGGetPerInterfaceObject(CGAbstractMethod): wrapper and global is the sandbox's global. */ -assert!(((*JS_GetClass(global)).flags & JSCLASS_DOM_GLOBAL) != 0); +assert!(((*JS_GetClass(global.get())).flags & JSCLASS_DOM_GLOBAL) != 0); /* Check to see whether the interface objects are already installed */ -let proto_or_iface_array = get_proto_or_iface_array(global); -let cached_object: *mut JSObject = (*proto_or_iface_array)[%s as usize]; -if cached_object.is_null() { - let tmp: *mut JSObject = CreateInterfaceObjects(cx, global, receiver); - assert!(!tmp.is_null()); - (*proto_or_iface_array)[%s as usize] = tmp; - tmp -} else { - cached_object -}""" % (self.id, self.id)) +let proto_or_iface_array = get_proto_or_iface_array(global.get()); +rval.set((*proto_or_iface_array)[%s as usize]); +if !rval.get().is_null() { + return; +} + +CreateInterfaceObjects(cx, global, receiver, rval); +assert!(!rval.get().is_null()); +(*proto_or_iface_array)[%s as usize] = rval.get(); +if <*mut JSObject>::needs_post_barrier(rval.get()) { + <*mut JSObject>::post_barrier((*proto_or_iface_array).as_mut_ptr().offset(%s as isize)) +} +""" % (self.id, self.id, self.id)) class CGGetProtoObjectMethod(CGGetPerInterfaceObject): """ @@ -2224,40 +2385,39 @@ class CGDefineProxyHandler(CGAbstractMethod): body = """\ let traps = ProxyTraps { - getPropertyDescriptor: Some(get_property_descriptor as unsafe extern "C" fn(*mut JSContext, *mut JSObject, jsid, bool, *mut JSPropertyDescriptor) -> bool), - getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor as unsafe extern "C" fn(*mut JSContext, *mut JSObject, jsid, bool, *mut JSPropertyDescriptor) -> bool), - defineProperty: Some(%s as unsafe extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut JSPropertyDescriptor) -> bool), - getOwnPropertyNames: Some(proxyhandler::get_own_property_names as unsafe extern "C" fn(*mut JSContext, *mut JSObject, *mut AutoIdVector) -> bool), - delete_: Some(%s as unsafe extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> bool), - enumerate: Some(proxyhandler::enumerate as unsafe extern "C" fn(*mut JSContext, *mut JSObject, *mut AutoIdVector) -> bool), - + enter: None, + getOwnPropertyDescriptor: Some(getOwnPropertyDescriptor), + defineProperty: Some(%s), + ownPropertyKeys: Some(proxyhandler::own_property_keys), + delete_: Some(%s), + enumerate: None, + preventExtensions: Some(proxyhandler::prevent_extensions), + isExtensible: Some(proxyhandler::is_extensible), has: None, - hasOwn: Some(hasOwn as unsafe extern "C" fn(*mut JSContext, *mut JSObject, jsid, *mut bool) -> bool), - get: Some(get as unsafe extern "C" fn(*mut JSContext, *mut JSObject, *mut JSObject, jsid, *mut JSVal) -> bool), + get: Some(get), set: None, - keys: None, - iterate: None, - call: None, construct: None, - nativeCall: ptr::null(), + getPropertyDescriptor: Some(get_property_descriptor), + hasOwn: Some(hasOwn), + getOwnEnumerablePropertyKeys: None, + nativeCall: None, hasInstance: None, - typeOf: None, objectClassIs: None, - obj_toString: Some(obj_toString as unsafe extern "C" fn(*mut JSContext, *mut JSObject) -> *mut js::jsapi::JSString), + className: Some(className), fun_toString: None, - //regexp_toShared: ptr::null(), + boxedValue_unbox: None, defaultValue: None, - iteratorNext: None, - finalize: Some(%s as unsafe extern "C" fn(*mut JSFreeOp, *mut JSObject)), - getElementIfPresent: None, - getPrototypeOf: None, - trace: Some(%s as unsafe extern "C" fn(*mut JSTracer, *mut JSObject)) + trace: Some(%s), + finalize: Some(%s), + objectMoved: None, + isCallable: None, + isConstructor: None, }; CreateProxyHandler(&traps, &Class as *const _ as *const _)\ -""" % (customDefineProperty, customDelete, FINALIZE_HOOK_NAME, - TRACE_HOOK_NAME) +""" % (customDefineProperty, customDelete, TRACE_HOOK_NAME, + FINALIZE_HOOK_NAME) return CGGeneric(body) @@ -2270,7 +2430,7 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): assert descriptor.interface.hasInterfaceObject() args = [ Argument('*mut JSContext', 'cx'), - Argument('*mut JSObject', 'global'), + Argument('HandleObject', 'global'), ] CGAbstractMethod.__init__(self, descriptor, 'DefineDOMInterface', 'void', args, pub=True) @@ -2279,10 +2439,17 @@ class CGDefineDOMInterfaceMethod(CGAbstractMethod): def definition_body(self): if self.descriptor.interface.isCallback(): - code = "CreateInterfaceObjects(cx, global, global);" + code = """\ +let mut obj = RootedObject::new(cx, ptr::null_mut()); +CreateInterfaceObjects(cx, global, global, obj.handle_mut()); +""" else: - code = "assert!(!GetProtoObject(cx, global, global).is_null());" - return CGGeneric("assert!(!global.is_null());\n" + code) + code = """\ +let mut proto = RootedObject::new(cx, ptr::null_mut()); +GetProtoObject(cx, global, global, proto.handle_mut()); +assert!(!proto.ptr.is_null()); +""" + return CGGeneric("assert!(!global.get().is_null());\n" + code) def needCx(returnType, arguments, considerTypes): return (considerTypes and @@ -2329,7 +2496,7 @@ class CGCallGenerator(CGThing): if static: call = CGWrapper(call, pre="%s::" % descriptorProvider.interface.identifier.name) else: - call = CGWrapper(call, pre="%s.r()." % object) + call = CGWrapper(call, pre="%s." % object) call = CGList([call, CGWrapper(args, pre="(", post=")")]) self.cgRoot.append(CGList([ @@ -2344,8 +2511,7 @@ class CGCallGenerator(CGThing): if static: glob = "" else: - glob = " let global = global_object_for_js_object(this.r().reflector().get_jsobject());\n"\ - " let global = global.root();\n" + glob = " let global = global_object_for_js_object(this.reflector().get_jsobject().get());\n" self.cgRoot.append(CGGeneric( "let result = match result {\n" @@ -2357,9 +2523,6 @@ class CGCallGenerator(CGThing): " },\n" "};" % (glob, errorResult))) - if typeRetValNeedsRooting(returnType): - self.cgRoot.append(CGGeneric("let result = result.root();")) - def define(self): return self.cgRoot.define() @@ -2403,22 +2566,15 @@ class CGPerSignatureCall(CGThing): self.argsPre = argsPre self.arguments = arguments self.argCount = len(arguments) - if self.argCount > argConversionStartsAt: - # Insert our argv in there - cgThings = [CGGeneric(self.getArgvDecl())] - else: - cgThings = [] - cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgv(), + cgThings = [] + cgThings.extend([CGArgumentConverter(arguments[i], i, self.getArgs(), self.getArgc(), self.descriptor, invalidEnumValueFatal=not setter) for i in range(argConversionStartsAt, self.argCount)]) errorResult = None if self.isFallible(): - if nativeMethodName == "NamedSetter": - errorResult = " false" - else: - errorResult = " false as JSBool" + errorResult = " JSFalse" cgThings.append(CGCallGenerator( errorResult, @@ -2427,10 +2583,8 @@ class CGPerSignatureCall(CGThing): static)) self.cgRoot = CGList(cgThings, "\n") - def getArgv(self): - return "argv" if self.argCount > 0 else "" - def getArgvDecl(self): - return "\nlet argv = JS_ARGV(cx, vp);\n" + def getArgs(self): + return "args" if self.argCount > 0 else "" def getArgc(self): return "argc" def getArguments(self): @@ -2445,7 +2599,7 @@ class CGPerSignatureCall(CGThing): return not 'infallible' in self.extendedAttributes def wrap_return_value(self): - return wrapForType('*vp') + return wrapForType('args.rval()') def define(self): return (self.cgRoot.define() + "\n" + self.wrap_return_value()) @@ -2551,7 +2705,7 @@ 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, "u8", args) if unwrapFailureCode is None: self.unwrapFailureCode = ( @@ -2568,14 +2722,20 @@ class CGAbstractBindingMethod(CGAbstractExternMethod): # consumption by FailureFatalCastableObjectUnwrapper. unwrapThis = str(CastableObjectUnwrapper( FakeCastableDescriptor(self.descriptor), - "obj", self.unwrapFailureCode)) + "obj.handle()", self.unwrapFailureCode, "object")) 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 args = CallArgs::from_vp(vp, argc);\n" + "let thisobj = args.thisv();\n" + "if !thisobj.get().is_null_or_undefined() && !thisobj.get().is_object() {\n" + " return JSFalse;\n" "}\n" + "let obj = if thisobj.get().is_object() {\n" + " RootedObject::new(cx, thisobj.get().to_object())\n" + "} else {\n" + " RootedObject::new(cx, GetGlobalForObjectCrossCompartment(JS_CALLEE(cx, vp).to_object_or_null()))\n" + "};\n" "\n" - "let this: Unrooted<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis)) + "let this: Root<%s> = %s;\n" % (self.descriptor.concreteType, unwrapThis)) return CGList([ unwrapThis, self.generate_code() ], "\n") def generate_code(self): @@ -2596,12 +2756,11 @@ class CGAbstractStaticBindingMethod(CGAbstractMethod): Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp'), ] - CGAbstractMethod.__init__(self, descriptor, name, "JSBool", args, extern=True) + CGAbstractMethod.__init__(self, descriptor, name, "u8", args, extern=True) def definition_body(self): preamble = CGGeneric("""\ let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object()); -let global = global.root(); """) return CGList([preamble, self.generate_code()]) @@ -2621,7 +2780,7 @@ class CGGenericMethod(CGAbstractBindingMethod): 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);") + "return CallJitMethodOp(_info, cx, obj.handle(), this.r() as *const _ as *const libc::c_void as *mut libc::c_void, argc, vp);") class CGSpecializedMethod(CGAbstractExternMethod): """ @@ -2631,18 +2790,19 @@ class CGSpecializedMethod(CGAbstractExternMethod): def __init__(self, descriptor, method): self.method = method name = method.identifier.name - args = [Argument('*mut JSContext', 'cx'), Argument('JSHandleObject', '_obj'), + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), - Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')] - CGAbstractExternMethod.__init__(self, descriptor, name, 'JSBool', args) + Argument('*const JSJitMethodCallArgs', 'args')] + CGAbstractExternMethod.__init__(self, descriptor, name, 'u8', args) def definition_body(self): nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) return CGWrapper(CGMethodCall([], nativeName, self.method.isStatic(), self.descriptor, self.method), - pre="let this = Unrooted::from_raw(this);\n" - "let this = this.root();\n") + pre="let this = &*this;\n" + "let args = &*args;\n" + "let argc = args.argc_;\n") @staticmethod def makeNativeName(descriptor, method): @@ -2661,14 +2821,16 @@ class CGStaticMethod(CGAbstractStaticBindingMethod): def generate_code(self): nativeName = CGSpecializedMethod.makeNativeName(self.descriptor, self.method) - return CGMethodCall(["global.r()"], nativeName, True, self.descriptor, self.method) + setupArgs = CGGeneric("let mut args = CallArgs::from_vp(vp, argc);\n") + call = CGMethodCall(["global.r()"], nativeName, True, self.descriptor, self.method) + return CGList([setupArgs, call]) class CGGenericGetter(CGAbstractBindingMethod): """ A class for generating the C++ code for an IDL attribute getter. """ def __init__(self, descriptor, lenientThis=False): - args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', '_argc'), + args = [Argument('*mut JSContext', 'cx'), Argument('libc::c_uint', 'argc'), Argument('*mut JSVal', 'vp')] if lenientThis: name = "genericLenientGetter" @@ -2685,7 +2847,7 @@ class CGGenericGetter(CGAbstractBindingMethod): 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);") + "return CallJitGetterOp(info, cx, obj.handle(), this.r() as *const _ as *const libc::c_void as *mut libc::c_void, argc, vp);") class CGSpecializedGetter(CGAbstractExternMethod): """ @@ -2696,10 +2858,10 @@ class CGSpecializedGetter(CGAbstractExternMethod): self.attr = attr name = 'get_' + attr.identifier.name args = [ Argument('*mut JSContext', 'cx'), - Argument('JSHandleObject', '_obj'), + Argument('HandleObject', '_obj'), Argument('*const %s' % descriptor.concreteType, 'this'), - Argument('*mut JSVal', 'vp') ] - CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + Argument('JSJitGetterCallArgs', 'args') ] + CGAbstractExternMethod.__init__(self, descriptor, name, "u8", args) def definition_body(self): nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, @@ -2707,8 +2869,7 @@ class CGSpecializedGetter(CGAbstractExternMethod): return CGWrapper(CGGetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), - pre="let this = Unrooted::from_raw(this);\n" - "let this = this.root();\n") + pre="let this = &*this;\n") @staticmethod def makeNativeName(descriptor, attr): @@ -2734,8 +2895,10 @@ class CGStaticGetter(CGAbstractStaticBindingMethod): def generate_code(self): nativeName = CGSpecializedGetter.makeNativeName(self.descriptor, self.attr) - return CGGetterCall(["global.r()"], self.attr.type, nativeName, self.descriptor, + setupArgs = CGGeneric("let mut args = CallArgs::from_vp(vp, argc);\n") + call = CGGetterCall(["global.r()"], self.attr.type, nativeName, self.descriptor, self.attr) + return CGList([setupArgs, call]) class CGGenericSetter(CGAbstractBindingMethod): @@ -2758,10 +2921,8 @@ class CGGenericSetter(CGAbstractBindingMethod): 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" + "if CallJitSetterOp(info, cx, obj.handle(), this.r() as *const _ as *const libc::c_void as *mut libc::c_void, argc, vp) == 0 {\n" " return 0;\n" "}\n" "*vp = UndefinedValue();\n" @@ -2776,18 +2937,17 @@ class CGSpecializedSetter(CGAbstractExternMethod): self.attr = attr name = 'set_' + attr.identifier.name args = [ Argument('*mut JSContext', 'cx'), - Argument('JSHandleObject', 'obj'), + Argument('HandleObject', 'obj'), Argument('*const %s' % descriptor.concreteType, 'this'), - Argument('*mut JSVal', 'argv')] - CGAbstractExternMethod.__init__(self, descriptor, name, "JSBool", args) + Argument('JSJitSetterCallArgs', 'args')] + CGAbstractExternMethod.__init__(self, descriptor, name, "u8", args) def definition_body(self): nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr) return CGWrapper(CGSetterCall([], self.attr.type, nativeName, self.descriptor, self.attr), - pre="let this = Unrooted::from_raw(this);\n" - "let this = this.root();\n") + pre="let this = &*this;\n") @staticmethod def makeNativeName(descriptor, attr): @@ -2808,7 +2968,7 @@ class CGStaticSetter(CGAbstractStaticBindingMethod): nativeName = CGSpecializedSetter.makeNativeName(self.descriptor, self.attr) checkForArg = CGGeneric( - "let argv = JS_ARGV(cx, vp);\n" + "let args = CallArgs::from_vp(vp, argc);\n" "if (argc == 0) {\n" " throw_type_error(cx, \"Not enough arguments to %s setter.\");\n" " return 0;\n" @@ -2831,17 +2991,17 @@ class CGSpecializedForwardingSetter(CGSpecializedSetter): assert all(ord(c) < 128 for c in attrName) assert all(ord(c) < 128 for c in forwardToAttrName) return CGGeneric("""\ -let mut v = UndefinedValue(); -if JS_GetProperty(cx, *obj.unnamed_field1, b"%s".as_ptr() as *const i8, &mut v) == 0 { - return 0; +let mut v = RootedValue::new(cx, UndefinedValue()); +if JS_GetProperty(cx, obj, %s as *const u8 as *const i8, v.handle_mut()) == 0 { + return JSFalse; } -if !v.is_object() { +if !v.ptr.is_object() { throw_type_error(cx, "Value.%s is not an object."); - return 0; + return JSFalse; } -let target_obj = v.to_object(); -JS_SetProperty(cx, target_obj, b"%s".as_ptr() as *const i8, argv.offset(0)) -""" % (attrName, attrName, forwardToAttrName)) +let target_obj = RootedObject::new(cx, v.ptr.to_object()); +JS_SetProperty(cx, target_obj.handle(), %s as *const u8 as *const i8, args.get(0)) +""" % (str_to_const_array(attrName), attrName, str_to_const_array(forwardToAttrName))) class CGMemberJITInfo(CGThing): """ @@ -2852,52 +3012,253 @@ class CGMemberJITInfo(CGThing): self.member = member self.descriptor = descriptor - def defineJitInfo(self, infoName, opName, infallible): - protoID = "PrototypeList::ID::%s as u32" % self.descriptor.name - depth = self.descriptor.interface.inheritanceDepth() - failstr = "true" if infallible else "false" - return ("const %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)) + def defineJitInfo(self, infoName, opName, opType, infallible, movable, + aliasSet, alwaysInSlot, lazilyInSlot, slotIndex, + returnTypes, args): + """ + aliasSet is a JSJitInfo::AliasSet value, without the "JSJitInfo::" bit. + + args is None if we don't want to output argTypes for some + reason (e.g. we have overloads or we're not a method) and + otherwise an iterable of the arguments for this method. + """ + assert(not movable or aliasSet != "AliasEverything") # Can't move write-aliasing things + assert(not alwaysInSlot or movable) # Things always in slots had better be movable + + def jitInfoInitializer(isTypedMethod): + initializer = fill( + """ + JSJitInfo { + _bindgen_data_1_: ${opName} as *const ::libc::c_void, + protoID: PrototypeList::ID::${name} as u16, + depth: ${depth}, + _bitfield_1: ((JSJitInfo_OpType::${opType} as u32) << 0) | + ((JSJitInfo_AliasSet::${aliasSet} as u32) << 4) | + ((JSValueType::${returnType} as u32) << 8) | + ((${isInfallible} as u32) << 16) | + ((${isMovable} as u32) << 17) | + ((${isAlwaysInSlot} as u32) << 18) | + ((${isLazilyCachedInSlot} as u32) << 19) | + ((${isTypedMethod} as u32) << 20) | + ((${slotIndex} as u32) << 21) + } + """, + opName=opName, + name=self.descriptor.name, + depth=self.descriptor.interface.inheritanceDepth(), + opType=opType, + aliasSet=aliasSet, + returnType=reduce(CGMemberJITInfo.getSingleReturnType, returnTypes, + ""), + isInfallible=toStringBool(infallible), + isMovable=toStringBool(movable), + isAlwaysInSlot=toStringBool(alwaysInSlot), + isLazilyCachedInSlot=toStringBool(lazilyInSlot), + isTypedMethod=toStringBool(isTypedMethod), + slotIndex=slotIndex) + return initializer.rstrip() + + return ("\n" + "const %s: JSJitInfo = %s;\n" + % (infoName, jitInfoInitializer(False))) 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) - if not self.member.readonly or self.member.getExtendedAttribute("PutForwards"): + + movable = self.mayBeMovable() and getterinfal + aliasSet = self.aliasSet() + + isAlwaysInSlot = self.member.getExtendedAttribute("StoreInSlot") + if self.member.slotIndex is not None: + assert isAlwaysInSlot or self.member.getExtendedAttribute("Cached") + isLazilyCachedInSlot = not isAlwaysInSlot + slotIndex = memberReservedSlot(self.member) + # We'll statically assert that this is not too big in + # CGUpdateMemberSlotsMethod, in the case when + # isAlwaysInSlot is true. + else: + isLazilyCachedInSlot = False + slotIndex = "0" + + result = self.defineJitInfo(getterinfo, getter, "Getter", + getterinfal, movable, aliasSet, + isAlwaysInSlot, isLazilyCachedInSlot, + slotIndex, + [self.member.type], None) + if (not self.member.readonly or + self.member.getExtendedAttribute("PutForwards")): 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 += "\n" + self.defineJitInfo(setterinfo, setter, False) + result += self.defineJitInfo(setterinfo, setter, "Setter", + False, False, "AliasEverything", + False, False, "0", + [BuiltinTypes[IDLBuiltinType.Types.void]], + None) return result if self.member.isMethod(): methodinfo = ("%s_methodinfo" % self.member.identifier.name) - # Actually a JSJitMethodOp, but JSJitPropertyOp by struct definition. method = ("%s" % self.member.identifier.name) # Methods are infallible if they are infallible, have no arguments # to unwrap, and have a return type that's infallible to wrap up for # return. - methodInfal = False sigs = self.member.signatures() - if len(sigs) == 1: + if len(sigs) != 1: # Don't handle overloading. If there's more than one signature, # one of them must take arguments. + methodInfal = False + args = None + movable = False + else: sig = sigs[0] - if len(sig[1]) == 0: - # No arguments and infallible return boxing - methodInfal = True + # For methods that affect nothing, it's OK to set movable to our + # notion of infallible on the C++ side, without considering + # argument conversions, since argument conversions that can + # reliably throw would be effectful anyway and the jit doesn't + # move effectful things. + hasInfallibleImpl = "infallible" in self.descriptor.getExtendedAttributes(self.member) + movable = self.mayBeMovable() and hasInfallibleImpl + # XXXbz can we move the smarts about fallibility due to arg + # conversions into the JIT, using our new args stuff? + if (len(sig[1]) != 0): + # We have arguments or our return-value boxing can fail + methodInfal = False + else: + methodInfal = hasInfallibleImpl + # For now, only bother to output args if we're side-effect-free. + if self.member.affects == "Nothing": + args = sig[1] + else: + args = None - result = self.defineJitInfo(methodinfo, method, methodInfal) + aliasSet = self.aliasSet() + result = self.defineJitInfo(methodinfo, method, "Method", + methodInfal, movable, aliasSet, + False, False, "0", + [s[0] for s in sigs], args) return result raise TypeError("Illegal member type to CGPropertyJITInfo") + def mayBeMovable(self): + """ + Returns whether this attribute or method may be movable, just + based on Affects/DependsOn annotations. + """ + affects = self.member.affects + dependsOn = self.member.dependsOn + assert affects in IDLInterfaceMember.AffectsValues + assert dependsOn in IDLInterfaceMember.DependsOnValues + # Things that are DependsOn=DeviceState are not movable, because we + # don't want them coalesced with each other or loop-hoisted, since + # their return value can change even if nothing is going on from our + # point of view. + return (affects == "Nothing" and + (dependsOn != "Everything" and dependsOn != "DeviceState")) + + def aliasSet(self): + """Returns the alias set to store in the jitinfo. This may not be the + effective alias set the JIT uses, depending on whether we have enough + information about our args to allow the JIT to prove that effectful + argument conversions won't happen. + + """ + dependsOn = self.member.dependsOn + assert dependsOn in IDLInterfaceMember.DependsOnValues + + if dependsOn == "Nothing" or dependsOn == "DeviceState": + assert self.member.affects == "Nothing" + return "AliasNone" + + if dependsOn == "DOMState": + assert self.member.affects == "Nothing" + return "AliasDOMSets" + + return "AliasEverything" + + @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.isMozMap(): + 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.unrestricted_float, IDLType.Tags.float, + IDLType.Tags.unrestricted_double, 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", @@ -2933,7 +3294,7 @@ pub enum %s { inner = """\ use dom::bindings::conversions::ToJSValConvertible; -use js::jsapi::JSContext; +use js::jsapi::{JSContext, MutableHandleValue}; use js::jsval::JSVal; pub const strings: &'static [&'static str] = &[ @@ -2941,8 +3302,8 @@ pub const strings: &'static [&'static str] = &[ ]; impl ToJSValConvertible for super::%s { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - strings[*self as usize].to_jsval(cx) + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + strings[*self as usize].to_jsval(cx, rval); } } """ % (",\n ".join(['"%s"' % val for val in enum.values()]), enum.identifier.name) @@ -3047,7 +3408,7 @@ class CGUnionStruct(CGThing): " e%s(%s)," % (v["name"], v["typeName"]) for v in templateVars ] enumConversions = [ - " %s::e%s(ref inner) => inner.to_jsval(cx)," % (self.type, v["name"]) for v in templateVars + " %s::e%s(ref inner) => inner.to_jsval(cx, rval)," % (self.type, v["name"]) for v in templateVars ] # XXXManishearth The following should be #[must_root], # however we currently allow it till #2661 is fixed @@ -3058,7 +3419,7 @@ pub enum %s { } impl ToJSValConvertible for %s { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { match *self { %s } @@ -3147,7 +3508,7 @@ class CGUnionConversionStruct(CGThing): if hasObjectTypes: assert interfaceObject templateBody = CGList([interfaceObject], "\n") - conversions.append(CGIfWrapper(templateBody, "value.is_object()")) + conversions.append(CGIfWrapper(templateBody, "value.get().is_object()")) otherMemberTypes = [ t for t in memberTypes if t.isPrimitive() or t.isString() or t.isEnum() @@ -3173,7 +3534,7 @@ class CGUnionConversionStruct(CGThing): "Err(())" % ", ".join(names))) method = CGWrapper( CGIndenter(CGList(conversions, "\n\n")), - pre="fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<%s, ()> {\n" % self.type, + pre="fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result<%s, ()> {\n" % self.type, post="\n}") return CGWrapper( CGIndenter(CGList([ @@ -3190,7 +3551,7 @@ class CGUnionConversionStruct(CGThing): return CGWrapper( CGIndenter(jsConversion, 4), - pre="fn TryConvertTo%s(cx: *mut JSContext, value: JSVal) -> %s {\n" % (t.name, returnType), + pre="fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n" % (t.name, returnType), post="\n}") def define(self): @@ -3236,6 +3597,7 @@ class ClassMethod(ClassItem): override indicates whether to flag the method as MOZ_OVERRIDE """ assert not override or virtual + assert not (override and static) self.returnType = returnType self.args = args self.inline = False @@ -3378,9 +3740,14 @@ class ClassConstructor(ClassItem): def getBody(self, cgClass): initializers = [" parent: %s" % str(self.baseConstructors[0])] return (self.body + ( - "%s {\n" + "let mut ret = Rc::new(%s {\n" "%s\n" - "}") % (cgClass.name, '\n'.join(initializers))) + "});\n" + "match rc::get_mut(&mut ret) {\n" + " Some(ref mut callback) => callback.parent.init(%s),\n" + " None => unreachable!(),\n" + "};\n" + "ret") % (cgClass.name, '\n'.join(initializers), self.args[0].name)) def declare(self, cgClass): args = ', '.join([a.declare() for a in self.args]) @@ -3391,7 +3758,7 @@ class ClassConstructor(ClassItem): body = ' {\n' + body + '}' return string.Template("""\ -pub fn ${decorators}new(${args}) -> ${className}${body} +pub fn ${decorators}new(${args}) -> Rc<${className}>${body} """).substitute({ 'decorators': self.getDecorators(True), 'className': cgClass.getNameString(), 'args': args, @@ -3692,17 +4059,18 @@ class CGProxySpecialOperation(CGPerSignatureCall): argument = arguments[1] info = getJSToNativeConversionInfo( argument.type, descriptor, treatNullAs=argument.treatNullAs, - exceptionCode="return false;") + exceptionCode="return JSFalse;") template = info.template declType = info.declType needsRooting = info.needsRooting templateValues = { - "val": "(*desc).value", + "val": "value.handle()", } self.cgRoot.prepend(instantiateJSToNativeConversionTemplate( template, templateValues, declType, argument.identifier.name, needsRooting)) + self.cgRoot.prepend(CGGeneric("let value = RootedValue::new(cx, desc.get().value);")) elif operation.isGetter(): self.cgRoot.prepend(CGGeneric("let mut found = false;")) @@ -3777,7 +4145,7 @@ class CGProxyNamedDeleter(CGProxySpecialOperation): class CGProxyUnwrap(CGAbstractMethod): def __init__(self, descriptor): - args = [Argument('*mut JSObject', 'obj')] + args = [Argument('HandleObject', 'obj')] CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*const ' + descriptor.concreteType, args, alwaysInline=True) def definition_body(self): @@ -3786,103 +4154,78 @@ class CGProxyUnwrap(CGAbstractMethod): obj = js::UnwrapObject(obj); }*/ //MOZ_ASSERT(IsProxy(obj)); -let box_ = GetProxyPrivate(obj).to_private() as *const %s; +let box_ = GetProxyPrivate(*obj.ptr).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('HandleObject', 'proxy'), + Argument('HandleId', 'id'), + Argument('MutableHandle<JSPropertyDescriptor>', 'desc')] CGAbstractExternMethod.__init__(self, descriptor, "getOwnPropertyDescriptor", - "bool", args) + "u8", args) self.descriptor = descriptor def getBody(self): indexedGetter = self.descriptor.operations['IndexedGetter'] indexedSetter = self.descriptor.operations['IndexedSetter'] - setOrIndexedGet = "" + get = "" if indexedGetter or indexedSetter: - setOrIndexedGet += "let index = get_array_index_from_id(cx, id);\n" + get = "let index = get_array_index_from_id(cx, id);\n" if indexedGetter: readonly = toStringBool(self.descriptor.operations['IndexedSetter'] is None) - fillDescriptor = "fill_property_descriptor(&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 = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + - CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" + - "}\n") - - if indexedSetter or self.descriptor.operations['NamedSetter']: - setOrIndexedGet += "if set {\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 += (" fill_property_descriptor(&mut *desc, proxy, false);\n" + - " return true;\n" + - " }\n") - if self.descriptor.operations['NamedSetter']: - setOrIndexedGet += " if RUST_JSID_IS_STRING(id) != 0 {\n" - if not 'NamedCreator' in self.descriptor.operations: - # FIXME need to check that this is a 'supported property name' - assert False - setOrIndexedGet += (" fill_property_descriptor(&mut *desc, proxy, false);\n" + - " return true;\n" + - " }\n") - setOrIndexedGet += "}" - if indexedGetter: - setOrIndexedGet += (" else {\n" + - CGIndenter(CGGeneric(get)).define() + - "}") - setOrIndexedGet += "\n\n" - elif indexedGetter: - setOrIndexedGet += ("if !set {\n" + - CGIndenter(CGGeneric(get)).define() + - "}\n\n") + fillDescriptor = "desc.get().value = result_root.ptr;\nfill_property_descriptor(&mut *desc.ptr, *proxy.ptr, %s);\nreturn JSTrue;" % readonly + templateValues = { + 'jsvalRef': 'result_root.handle_mut()', + 'successCode': fillDescriptor, + 'pre': 'let mut result_root = RootedValue::new(cx, UndefinedValue());' + } + get += ("if index.is_some() {\n" + + " let index = index.unwrap();\n" + + " let this = UnwrapProxy(proxy);\n" + + " let this = &*this;\n" + + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" + + "}\n") namedGetter = self.descriptor.operations['NamedGetter'] if namedGetter: readonly = toStringBool(self.descriptor.operations['NamedSetter'] is None) - fillDescriptor = "fill_property_descriptor(&mut *desc, proxy, %s);\nreturn true;" % readonly - templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor} + fillDescriptor = "desc.get().value = result_root.ptr;\nfill_property_descriptor(&mut *desc.ptr, *proxy.ptr, %s);\nreturn JSTrue;" % readonly + templateValues = { + 'jsvalRef': 'result_root.handle_mut()', + 'successCode': fillDescriptor, + 'pre': 'let mut result_root = RootedValue::new(cx, UndefinedValue());' + } # 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 && !has_property_on_prototype(cx, proxy, id) {\n" + + "if RUST_JSID_IS_STRING(id) != 0 && !has_property_on_prototype(cx, proxy, id) {\n" + " let name = jsid_to_str(cx, id);\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" + "}\n") else: namedGet = "" - return setOrIndexedGet + """\ -let expando: *mut JSObject = get_expando_object(proxy); + return get + """\ +let expando = RootedObject::new(cx, get_expando_object(proxy)); //if (!xpc::WrapperFactory::IsXrayWrapper(proxy) && (expando = GetExpandoObject(proxy))) { -if !expando.is_null() { - let flags = if set { JSRESOLVE_ASSIGNING } else { 0 } | JSRESOLVE_QUALIFIED; - if JS_GetPropertyDescriptorById(cx, expando, id, flags, desc) == 0 { - return false; +if !expando.ptr.is_null() { + if JS_GetPropertyDescriptorById(cx, expando.handle(), id, desc) == 0 { + return JSFalse; } - if !(*desc).obj.is_null() { + if !desc.get().obj.is_null() { // Pretend the property lives on the wrapper. - (*desc).obj = proxy; - return true; + desc.get().obj = *proxy.ptr; + return JSTrue; } } """ + namedGet + """\ -(*desc).obj = ptr::null_mut(); -return true;""" +desc.get().obj = ptr::null_mut(); +return JSTrue;""" def definition_body(self): return CGGeneric(self.getBody()) @@ -3890,10 +4233,11 @@ return true;""" # TODO(Issue 5876) class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), - Argument('*mut JSPropertyDescriptor', 'desc')] - CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "bool", args) + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), + Argument('HandleId', 'id'), + Argument('Handle<JSPropertyDescriptor>', 'desc'), + Argument('*mut ObjectOpResult', 'opresult')] + CGAbstractExternMethod.__init__(self, descriptor, "defineProperty", "u8", args) self.descriptor = descriptor def getBody(self): set = "" @@ -3906,14 +4250,13 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() + - " return true;\n" + + " return JSTrue;\n" + "}\n") elif self.descriptor.operations['IndexedGetter']: set += ("if get_array_index_from_id(cx, id).is_some() {\n" + - " return false;\n" + + " return JSFalse;\n" + " //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" + "}\n") % self.descriptor.name @@ -3924,30 +4267,30 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" + " let name = jsid_to_str(cx, id);\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + - " return true;\n" + + " (*opresult).code_ = 0; /* SpecialCodes::OkCode */\n" + + " return JSTrue;\n" + "} else {\n" + - " return false;\n" + + " return JSFalse;\n" + "}\n") else: - if self.descriptor.operations['NamedGetter']: - set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" + - " let name = jsid_to_str(cx, id);\n" + - " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + - CGProxyNamedPresenceChecker(self.descriptor).define() + - " if (found) {\n" + - # TODO(Issue 5876) - " //return js::IsInNonStrictPropertySet(cx)\n" + - " // ? opresult.succeed()\n" + - " // : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, \"${name}\");\n" + - " return true;\n" + - " }\n" + - "}" - ) % (self.descriptor.name, self.descriptor.name) + set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" + + " let name = jsid_to_str(cx, id);\n" + + " let this = UnwrapProxy(proxy);\n" + + " let this = &*this;\n" + + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + + " if (found) {\n" + # TODO(Issue 5876) + " //return js::IsInNonStrictPropertySet(cx)\n" + + " // ? opresult.succeed()\n" + + " // : ThrowErrorMessage(cx, MSG_NO_NAMED_SETTER, \"${name}\");\n" + + " (*opresult).code_ = 0; /* SpecialCodes::OkCode */\n" + + " return JSTrue;\n" + + " }\n" + + " (*opresult).code_ = 0; /* SpecialCodes::OkCode */\n" + + " return JSTrue;\n" + "}\n") % (self.descriptor.name, self.descriptor.name) set += "return proxyhandler::define_property(%s);" % ", ".join(a.name for a in self.args) return set @@ -3956,10 +4299,10 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod): class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), - Argument('*mut bool', 'bp')] - CGAbstractExternMethod.__init__(self, descriptor, "delete", "bool", args) + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), + Argument('HandleId', 'id'), + Argument('*mut ObjectOpResult', 'res')] + CGAbstractExternMethod.__init__(self, descriptor, "delete", "u8", args) self.descriptor = descriptor def getBody(self): @@ -3967,10 +4310,9 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): if self.descriptor.operations['NamedDeleter']: set += ("let name = jsid_to_str(cx, id);\n" + "let this = UnwrapProxy(proxy);\n" + - "let this = Unrooted::from_raw(this);\n" + - "let this = this.root();\n" + + "let this = &*this;\n" + "%s") % (CGProxyNamedDeleter(self.descriptor).define()) - set += "return proxyhandler::delete(%s);" % ", ".join(a.name for a in self.args) + set += "return proxyhandler::delete(%s) as u8;" % ", ".join(a.name for a in self.args) return set def definition_body(self): @@ -3978,9 +4320,9 @@ class CGDOMJSProxyHandler_delete(CGAbstractExternMethod): class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): def __init__(self, descriptor): - args = [Argument('*mut JSContext', 'cx'), Argument('*mut JSObject', 'proxy'), - Argument('jsid', 'id'), Argument('*mut bool', 'bp')] - CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "bool", args) + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), + Argument('HandleId', 'id'), Argument('*mut u8', 'bp')] + CGAbstractExternMethod.__init__(self, descriptor, "hasOwn", "u8", args) self.descriptor = descriptor def getBody(self): indexedGetter = self.descriptor.operations['IndexedGetter'] @@ -3989,11 +4331,10 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" + - " *bp = found;\n" + - " return true;\n" + + " *bp = found as u8;\n" + + " return JSTrue;\n" + "}\n\n") else: indexed = "" @@ -4003,57 +4344,56 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod): named = ("if RUST_JSID_IS_STRING(id) != 0 && !has_property_on_prototype(cx, proxy, id) {\n" + " let name = jsid_to_str(cx, id);\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" + - " *bp = found;\n" - " return true;\n" + " *bp = found as u8;\n" + " return JSTrue;\n" "}\n" + "\n") else: named = "" return indexed + """\ -let expando: *mut JSObject = get_expando_object(proxy); -if !expando.is_null() { - let mut b: JSBool = 1; - let ok = JS_HasPropertyById(cx, expando, id, &mut b) != 0; - *bp = b != 0; - if !ok || *bp { - return ok; +let expando = RootedObject::new(cx, get_expando_object(proxy)); +if !expando.ptr.is_null() { + let mut b: u8 = 1; + let ok = JS_HasPropertyById(cx, expando.handle(), id, &mut b) != 0; + *bp = (b != 0) as u8; + if !ok || *bp != 0 { + return ok as u8; } } """ + named + """\ -*bp = false; -return true;""" +*bp = JSFalse; +return JSTrue;""" def definition_body(self): return CGGeneric(self.getBody()) 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')] - CGAbstractExternMethod.__init__(self, descriptor, "get", "bool", args) + args = [Argument('*mut JSContext', 'cx'), Argument('HandleObject', 'proxy'), + Argument('HandleObject', '_receiver'), Argument('HandleId', 'id'), + Argument('MutableHandleValue', 'vp')] + CGAbstractExternMethod.__init__(self, descriptor, "get", "u8", args) self.descriptor = descriptor def getBody(self): getFromExpando = """\ -let expando = get_expando_object(proxy); -if !expando.is_null() { +let expando = RootedObject::new(cx, get_expando_object(proxy)); +if !expando.ptr.is_null() { let mut hasProp = 0; - if JS_HasPropertyById(cx, expando, id, &mut hasProp) == 0 { - return false; + if JS_HasPropertyById(cx, expando.handle(), id, &mut hasProp) == 0 { + return JSFalse; } if hasProp != 0 { - return JS_GetPropertyById(cx, expando, id, vp) != 0; + return (JS_GetPropertyById(cx, expando.handle(), id, vp) != 0) as u8; } }""" templateValues = { - 'jsvalRef': '*vp', - 'successCode': 'return true;', + 'jsvalRef': 'vp', + 'successCode': 'return JSTrue;', } indexedGetter = self.descriptor.operations['IndexedGetter'] @@ -4062,8 +4402,7 @@ if !expando.is_null() { "if index.is_some() {\n" + " let index = index.unwrap();\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define()) getIndexedOrExpando += """\ // Even if we don't have this index, we don't forward the @@ -4080,8 +4419,7 @@ if !expando.is_null() { getNamed = ("if (RUST_JSID_IS_STRING(id) != 0) {\n" + " let name = jsid_to_str(cx, id);\n" + " let this = UnwrapProxy(proxy);\n" + - " let this = Unrooted::from_raw(this);\n" + - " let this = this.root();\n" + + " let this = &*this;\n" + CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "}\n") else: @@ -4094,26 +4432,26 @@ if !expando.is_null() { %s let mut found = false; if !get_property_on_prototype(cx, proxy, id, &mut found, vp) { - return false; + return JSFalse; } if found { - return true; + return JSTrue; } %s -*vp = UndefinedValue(); -return true;""" % (getIndexedOrExpando, getNamed) +*vp.ptr = UndefinedValue(); +return JSTrue;""" % (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('HandleObject', '_proxy')] + CGAbstractExternMethod.__init__(self, descriptor, "className", "*const i8", args) self.descriptor = descriptor def getBody(self): - return """proxyhandler::object_to_string(cx, "%s")""" % self.descriptor.name + return '%s as *const u8 as *const i8' % str_to_const_array(self.descriptor.name) def definition_body(self): return CGGeneric(self.getBody()) @@ -4165,7 +4503,8 @@ class CGClassTraceHook(CGAbstractClassHook): self.traceGlobal = descriptor.isGlobal() def generate_code(self): - body = [CGGeneric("(*this).trace(%s);" % self.args[0].name)] + body = [CGGeneric("if this.is_null() { return; } // GC during obj creation\n" + "(*this).trace(%s);" % self.args[0].name)] if self.traceGlobal: body += [CGGeneric("trace_global(trc, obj);")] return CGList(body, "\n") @@ -4177,7 +4516,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) + 'u8', args) self._ctor = self.descriptor.interface.ctor() def define(self): @@ -4188,7 +4527,7 @@ class CGClassConstructHook(CGAbstractExternMethod): def definition_body(self): preamble = CGGeneric("""\ let global = global_object_for_js_object(JS_CALLEE(cx, vp).to_object()); -let global = global.root(); +let args = CallArgs::from_vp(vp, argc); """) name = self._ctor.identifier.name nativeName = MakeNativeName(self.descriptor.binaryNameFor(name)) @@ -4201,7 +4540,7 @@ class CGClassFinalizeHook(CGAbstractClassHook): A hook for finalize, used to release our native object. """ def __init__(self, descriptor): - args = [Argument('*mut JSFreeOp', '_fop'), Argument('*mut JSObject', 'obj')] + args = [Argument('*mut FreeOp', '_fop'), Argument('*mut JSObject', 'obj')] CGAbstractClassHook.__init__(self, descriptor, FINALIZE_HOOK_NAME, 'void', args) @@ -4391,7 +4730,7 @@ class CGDescriptor(CGThing): 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)) @@ -4427,7 +4766,7 @@ class CGDescriptor(CGThing): return self.cgRoot.define() class CGNonNamespacedEnum(CGThing): - def __init__(self, enumName, names, values, comment="", deriving=""): + def __init__(self, enumName, names, values, comment="", deriving="", repr=""): if not values: values = [] @@ -4449,6 +4788,8 @@ class CGNonNamespacedEnum(CGThing): # Build the enum body. enumstr = comment + 'pub enum %s {\n%s\n}\n' % (enumName, ',\n'.join(entries)) + if repr: + enumstr = ('#[repr(%s)]\n' % repr) + enumstr if deriving: enumstr = ('#[derive(%s)]\n' % deriving) + enumstr curr = CGGeneric(enumstr) @@ -4523,13 +4864,13 @@ class CGDictionary(CGThing): def memberInit(memberInfo): member, _ = memberInfo name = self.makeMemberName(member.identifier.name) - conversion = self.getMemberConversion(memberInfo) + conversion = self.getMemberConversion(memberInfo, member.type) return CGGeneric("%s: %s,\n" % (name, conversion.define())) def memberInsert(memberInfo): member, _ = memberInfo name = self.makeMemberName(member.identifier.name) - insertion = ("set_dictionary_property(cx, obj, \"%s\", &mut self.%s.to_jsval(cx)).unwrap();" % (name, name)) + insertion = ("let mut %s = RootedValue::new(cx, UndefinedValue());\nself.%s.to_jsval(cx, %s.handle_mut());\nset_dictionary_property(cx, obj.handle(), \"%s\", %s.handle()).unwrap();" % (name, name, name, name, name)) return CGGeneric("%s\n" % insertion) memberInits = CGList([memberInit(m) for m in self.memberInfo]) @@ -4537,14 +4878,14 @@ class CGDictionary(CGThing): return string.Template( "impl ${selfName} {\n" - " pub fn empty() -> ${selfName} {\n" - " ${selfName}::new(ptr::null_mut(), NullValue()).unwrap()\n" + " pub fn empty(cx: *mut JSContext) -> ${selfName} {\n" + " ${selfName}::new(cx, HandleValue::null()).unwrap()\n" " }\n" - " pub fn new(cx: *mut JSContext, val: JSVal) -> Result<${selfName}, ()> {\n" - " let object = if val.is_null_or_undefined() {\n" - " ptr::null_mut()\n" - " } else if val.is_object() {\n" - " val.to_object()\n" + " pub fn new(cx: *mut JSContext, val: HandleValue) -> Result<${selfName}, ()> {\n" + " let object = if val.get().is_null_or_undefined() {\n" + " RootedObject::new(cx, ptr::null_mut())\n" + " } else if val.get().is_object() {\n" + " RootedObject::new(cx, val.get().to_object())\n" " } else {\n" " throw_type_error(cx, \"Value not an object.\");\n" " return Err(());\n" @@ -4557,10 +4898,10 @@ class CGDictionary(CGThing): "}\n" "\n" "impl ToJSValConvertible for ${selfName} {\n" - " fn to_jsval(&self, cx: *mut JSContext) -> JSVal {\n" - " let obj = unsafe { JS_NewObject(cx, 0 as *const JSClass, 0 as *const JSObject, 0 as *const JSObject) };\n" + " fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {\n" + " let obj = unsafe { RootedObject::new(cx, JS_NewObject(cx, ptr::null())) };\n" "${insertMembers}" - " ObjectOrNullValue(obj)\n" + " rval.set(ObjectOrNullValue(obj.ptr))\n" " }\n" "}\n").substitute({ "selfName": self.makeClassName(d), @@ -4587,7 +4928,7 @@ class CGDictionary(CGThing): declType = CGWrapper(info.declType, pre="Option<", post=">") return declType.define() - def getMemberConversion(self, memberInfo): + def getMemberConversion(self, memberInfo, memberType): def indent(s): return CGIndenter(CGGeneric(s), 8).define() @@ -4595,8 +4936,10 @@ class CGDictionary(CGThing): templateBody = info.template default = info.default declType = info.declType - replacements = { "val": "value" } + replacements = { "val": "rval.handle()" } conversion = string.Template(templateBody).substitute(replacements) + if memberType.isAny(): + conversion = "%s.get()" % conversion assert (member.defaultValue is None) == (default is None) if not default: @@ -4604,14 +4947,16 @@ class CGDictionary(CGThing): conversion = "Some(%s)" % conversion conversion = ( - "match try!(get_dictionary_property(cx, object, \"%s\")) {\n" - " Some(value) => {\n" + "{\n" + "let mut rval = RootedValue::new(cx, UndefinedValue());\n" + "match try!(get_dictionary_property(cx, object.handle(), \"%s\", rval.handle_mut())) {\n" + " true => {\n" "%s\n" " },\n" - " None => {\n" + " false => {\n" "%s\n" " },\n" - "}") % (member.identifier.name, indent(conversion), indent(default)) + "}\n}") % (member.identifier.name, indent(conversion), indent(default)) return CGGeneric(conversion) @@ -4641,7 +4986,7 @@ class CGRegisterProtos(CGAbstractMethod): def __init__(self, config): arguments = [ Argument('*mut JSContext', 'cx'), - Argument('*mut JSObject', 'global'), + Argument('HandleObject', 'global'), ] CGAbstractMethod.__init__(self, None, 'Register', 'void', arguments, unsafe=False, pub=True) @@ -4732,38 +5077,41 @@ class CGBindingRoot(CGThing): # Add imports curr = CGImports(curr, descriptors + callbackDescriptors, mainCallbacks, [ 'js', - 'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}', - 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}', + 'js::JS_CALLEE', + 'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS, JSCLASS_IMPLEMENTS_BARRIERS}', 'js::{JSCLASS_IS_GLOBAL, JSCLASS_RESERVED_SLOTS_SHIFT}', - 'js::{JSCLASS_RESERVED_SLOTS_MASK, JSID_VOID, JSJitInfo}', - 'js::{JSPROP_ENUMERATE, JSPROP_NATIVE_ACCESSORS, JSPROP_SHARED}', - 'js::{JSRESOLVE_ASSIGNING, JSRESOLVE_QUALIFIED}', + 'js::{JSCLASS_RESERVED_SLOTS_MASK}', + 'js::{JSPROP_ENUMERATE, JSPROP_SHARED}', '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_HasProperty, JS_HasPropertyById, JS_IsExceptionPending}', - 'js::jsapi::{JS_NewObject, JS_ObjectIsCallable, JS_SetProperty, JS_SetPrototype}', - 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSBool, JSContext}', - 'js::jsapi::{JSClass, JSFreeOp, JSFunctionSpec, JSHandleObject, jsid}', - 'js::jsapi::{JSNativeWrapper, JSObject, JSPropertyDescriptor, JS_ArrayIterator}', - 'js::jsapi::{JSPropertyOpWrapper, JSPropertySpec, JS_PropertyStub}', - 'js::jsapi::{JSStrictPropertyOpWrapper, JSString, JSTracer, JS_ConvertStub}', - 'js::jsapi::{JS_StrictPropertyStub, JS_EnumerateStub, JS_ResolveStub}', - 'js::jsapi::{JSMutableHandleValue, JSHandleId, JSType}', + 'js::jsapi::{JS_NewObjectWithGivenProto, JS_NewObject, IsCallable, JS_SetProperty, JS_SetPrototype}', + 'js::jsapi::{JS_SetReservedSlot, JS_WrapValue, JSContext}', + 'js::jsapi::{JSClass, FreeOp, JSFreeOp, JSFunctionSpec, jsid}', + 'js::jsapi::{MutableHandleValue, MutableHandleObject, HandleObject, HandleValue, RootedObject, RootedValue}', + 'js::jsapi::{JSNativeWrapper, JSNative, JSObject, JSPropertyDescriptor}', + 'js::jsapi::{JSPropertySpec}', + 'js::jsapi::{JSString, JSTracer, JSJitInfo, JSJitInfo_OpType, JSJitInfo_AliasSet}', + 'js::jsapi::{MutableHandle, Handle, HandleId, JSType, JSValueType}', + 'js::jsapi::{SymbolCode, ObjectOpResult, HandleValueArray}', + 'js::jsapi::{JSJitGetterCallArgs, JSJitSetterCallArgs, JSJitMethodCallArgs, CallArgs}', + 'js::jsapi::{JSAutoCompartment, JSAutoRequest, JS_ComputeThis}', + 'js::jsapi::GetGlobalForObjectCrossCompartment', 'js::jsval::JSVal', 'js::jsval::{ObjectValue, ObjectOrNullValue, PrivateValue}', 'js::jsval::{NullValue, UndefinedValue}', - 'js::glue::{CallJitMethodOp, CallJitPropertyOp, CreateProxyHandler}', - 'js::glue::{GetProxyPrivate, NewProxyObject, ProxyTraps, AutoIdVector}', + 'js::glue::{CallJitMethodOp, CallJitGetterOp, CallJitSetterOp, 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::rust::GCMethods', + 'js::{JSTrue, JSFalse}', 'dom::bindings', 'dom::bindings::global::GlobalRef', 'dom::bindings::global::global_object_for_js_object', - 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary, Unrooted}', - 'dom::bindings::js::{OptionalOptionalRootable, OptionalRootable}', - 'dom::bindings::js::{OptionalRootedReference, ResultRootable, Rootable}', + 'dom::bindings::js::{JS, Root, RootedReference}', + 'dom::bindings::js::{OptionalRootedReference}', 'dom::bindings::utils::{create_dom_global, do_create_interface_objects}', 'dom::bindings::utils::ConstantSpec', 'dom::bindings::utils::{DOMClass}', @@ -4780,16 +5128,16 @@ class CGBindingRoot(CGThing): 'dom::bindings::utils::{NativeProperties, NativePropertyHooks}', 'dom::bindings::utils::ConstantVal::{IntVal, UintVal}', 'dom::bindings::utils::NonNullJSNative', - 'dom::bindings::trace::JSTraceable', + 'dom::bindings::trace::{JSTraceable, RootedTraceable}', 'dom::bindings::callback::{CallbackContainer,CallbackInterface,CallbackFunction}', 'dom::bindings::callback::{CallSetup,ExceptionHandling}', 'dom::bindings::callback::wrap_call_this_object', 'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}', - 'dom::bindings::conversions::{native_from_reflector, native_from_reflector_jsmanaged}', + 'dom::bindings::conversions::{native_from_reflector, native_from_handlevalue, native_from_handleobject}', 'dom::bindings::conversions::DOM_OBJECT_SLOT', 'dom::bindings::conversions::IDLInterface', 'dom::bindings::conversions::jsid_to_str', - 'dom::bindings::conversions::StringificationBehavior::{Default, Empty}', + 'dom::bindings::conversions::StringificationBehavior', 'dom::bindings::codegen::{PrototypeList, RegisterBindings, UnionTypes}', 'dom::bindings::codegen::Bindings::*', 'dom::bindings::error::{Fallible, Error, ErrorResult}', @@ -4812,6 +5160,10 @@ class CGBindingRoot(CGThing): 'std::num', 'std::ptr', 'std::str', + 'std::rc', + 'std::rc::Rc', + 'std::default::Default', + 'std::ffi::CString', ]) # Add the auto-generated comment. @@ -4918,7 +5270,7 @@ class CGCallback(CGClass): bases=[ClassBase(baseName)], constructors=self.getConstructors(), methods=realMethods+getters+setters, - decorators="#[derive(PartialEq,Copy,Clone)]#[jstraceable]") + decorators="#[derive(PartialEq)]#[jstraceable]") def getConstructors(self): return [ClassConstructor( @@ -4927,7 +5279,7 @@ class CGCallback(CGClass): visibility="pub", explicit=False, baseConstructors=[ - "%s::new(aCallback)" % self.baseName + "%s::new()" % self.baseName ])] def getMethodImpls(self, method): @@ -4936,13 +5288,13 @@ class CGCallback(CGClass): # Strip out the JSContext*/JSObject* args # that got added. assert args[0].name == "cx" and args[0].argType == "*mut JSContext" - assert args[1].name == "aThisObj" and args[1].argType == "*mut JSObject" + assert args[1].name == "aThisObj" and args[1].argType == "HandleObject" args = args[2:] # Record the names of all the arguments, so we can use them when we call # the private method. argnames = [arg.name for arg in args] - argnamesWithThis = ["s.get_context()", "thisObjJS"] + argnames - argnamesWithoutThis = ["s.get_context()", "ptr::null_mut()"] + argnames + argnamesWithThis = ["s.get_context()", "thisObjJS.handle()"] + argnames + argnamesWithoutThis = ["s.get_context()", "thisObjJS.handle()"] + argnames # Now that we've recorded the argnames for our call to our private # method, insert our optional argument for deciding whether the # CallSetup should re-throw exceptions on aRv. @@ -4951,12 +5303,12 @@ class CGCallback(CGClass): # And now insert our template argument. argsWithoutThis = list(args) - args.insert(0, Argument("JSRef<T>", "thisObj")) + args.insert(0, Argument("&T", "thisObj")) # And the self argument - method.args.insert(0, Argument(None, "self")) - args.insert(0, Argument(None, "self")) - argsWithoutThis.insert(0, Argument(None, "self")) + method.args.insert(0, Argument(None, "&self")) + args.insert(0, Argument(None, "&self")) + argsWithoutThis.insert(0, Argument(None, "&self")) setupCall = ("let s = CallSetup::new(self, aExceptionHandling);\n" "if s.get_context().is_null() {\n" @@ -4965,8 +5317,9 @@ class CGCallback(CGClass): bodyWithThis = string.Template( setupCall+ - "let thisObjJS = wrap_call_this_object(s.get_context(), thisObj);\n" - "if thisObjJS.is_null() {\n" + "let mut thisObjJS = RootedObject::new(s.get_context(), ptr::null_mut());\n" + "wrap_call_this_object(s.get_context(), thisObj, thisObjJS.handle_mut());\n" + "if thisObjJS.ptr.is_null() {\n" " return Err(JSFailed);\n" "}\n" "return ${methodName}(${callArgs});").substitute({ @@ -4975,6 +5328,7 @@ class CGCallback(CGClass): }) bodyWithoutThis = string.Template( setupCall + + "let thisObjJS = RootedObject::new(s.get_context(), ptr::null_mut());" "return ${methodName}(${callArgs});").substitute({ "callArgs" : ", ".join(argnamesWithoutThis), "methodName": 'self.' + method.name, @@ -5017,7 +5371,7 @@ class CGCallbackFunctionImpl(CGGeneric): def __init__(self, callback): impl = string.Template("""\ impl CallbackContainer for ${type} { - fn new(callback: *mut JSObject) -> ${type} { + fn new(callback: *mut JSObject) -> Rc<${type}> { ${type}::new(callback) } @@ -5027,8 +5381,8 @@ impl CallbackContainer for ${type} { } impl ToJSValConvertible for ${type} { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - self.callback().to_jsval(cx) + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + self.callback().to_jsval(cx, rval); } }\ """).substitute({"type": callback.name}) @@ -5127,14 +5481,12 @@ class CallbackMember(CGNativeMember): "${returnResult}").substitute(replacements) return CGList([ CGGeneric(pre), - CGWrapper(CGIndenter(CGGeneric(body)), - pre="with_compartment(cx, self.parent.callback(), || {\n", - post="})") + CGGeneric(body), ], "\n").define() def getResultConversion(self): replacements = { - "val": "rval", + "val": "rval.handle()", } info = getJSToNativeConversionInfo( @@ -5153,6 +5505,8 @@ class CallbackMember(CGNativeMember): if self.retvalType is None or self.retvalType.isVoid(): retval = "()" + elif self.retvalType.isAny(): + retval = "rvalDecl.get()" else: retval = "rvalDecl" @@ -5177,16 +5531,17 @@ class CallbackMember(CGNativeMember): argval = arg.identifier.name if arg.variadic: - argval = argval + "[idx]" + argval = argval + "[idx].get()" jsvalIndex = "%d + idx" % i else: jsvalIndex = "%d" % i if arg.optional and not arg.defaultValue: argval += ".clone().unwrap()" - conversion = wrapForType("argv[%s]" % jsvalIndex, - result=argval, - successCode="") + conversion = wrapForType( + "argv_root.handle_mut()", result=argval, + successCode="argv[%s] = argv_root.ptr;" % jsvalIndex, + pre="let mut argv_root = RootedValue::new(cx, UndefinedValue());") if arg.variadic: conversion = string.Template( "for idx in 0..${arg}.len() {\n" + @@ -5216,7 +5571,7 @@ class CallbackMember(CGNativeMember): # We want to allow the caller to pass in a "this" object, as # well as a JSContext. return [Argument("*mut JSContext", "cx"), - Argument("*mut JSObject", "aThisObj")] + args + Argument("HandleObject", "aThisObj")] + args def getCallSetup(self): if self.needThisHandling: @@ -5230,7 +5585,7 @@ class CallbackMember(CGNativeMember): "}\n") def getArgcDecl(self): - return CGGeneric("let mut argc = %s as u32;" % self.argCountStr); + return CGGeneric("let mut argc = %s;" % self.argCountStr); @staticmethod def ensureASCIIName(idlObject): @@ -5252,7 +5607,7 @@ class CallbackMethod(CallbackMember): CallbackMember.__init__(self, sig, name, descriptorProvider, needThisHandling) def getRvalDecl(self): - return "let mut rval = UndefinedValue();\n" + return "let mut rval = RootedValue::new(cx, UndefinedValue());\n" def getCall(self): replacements = { @@ -5260,15 +5615,16 @@ class CallbackMethod(CallbackMember): "getCallable": self.getCallableDecl() } if self.argCount > 0: - replacements["argv"] = "argv.as_mut_ptr()" + replacements["argv"] = "argv.as_ptr()" replacements["argc"] = "argc" else: replacements["argv"] = "ptr::null_mut()" replacements["argc"] = "0" return string.Template("${getCallable}" "let ok = unsafe {\n" - " JS_CallFunctionValue(cx, ${thisObj}, callable,\n" - " ${argc}, ${argv}, &mut rval)\n" + " let rootedThis = RootedObject::new(cx, ${thisObj});\n" + " JS_CallFunctionValue(cx, rootedThis.handle(), callable.handle(),\n" + " &HandleValueArray { length_: ${argc} as ::libc::size_t, elements_: ${argv} }, rval.handle_mut())\n" "};\n" "if ok == 0 {\n" " return Err(JSFailed);\n" @@ -5280,10 +5636,10 @@ class CallCallback(CallbackMethod): descriptorProvider, needThisHandling=True) def getThisObj(self): - return "aThisObj" + return "aThisObj.get()" def getCallableDecl(self): - return "let callable = ObjectValue(unsafe {&*self.parent.callback()});\n" + return "let callable = RootedValue::new(cx, ObjectValue(unsafe {&*self.parent.callback()}));\n" class CallbackOperationBase(CallbackMethod): """ @@ -5300,23 +5656,23 @@ class CallbackOperationBase(CallbackMethod): # This relies on getCallableDecl declaring a boolean # isCallable in the case when we're a single-operation # interface. - return "if isCallable { aThisObj } else { self.parent.callback() }" + return "if isCallable { aThisObj.get() } else { self.parent.callback() }" def getCallableDecl(self): replacements = { "methodName": self.methodName } getCallableFromProp = string.Template( - 'try!(self.parent.get_callable_property(cx, "${methodName}"))' + 'RootedValue::new(cx, try!(self.parent.get_callable_property(cx, "${methodName}")))' ).substitute(replacements) if not self.singleOperation: return 'JS::Rooted<JS::Value> callable(cx);\n' + getCallableFromProp return ( - 'let isCallable = unsafe { JS_ObjectIsCallable(cx, self.parent.callback()) != 0 };\n' + 'let isCallable = unsafe { IsCallable(self.parent.callback()) != 0 };\n' 'let callable =\n' + CGIndenter( CGIfElseWrapper('isCallable', - CGGeneric('unsafe { ObjectValue(&*self.parent.callback()) }'), + CGGeneric('unsafe { RootedValue::new(cx, ObjectValue(&*self.parent.callback())) }'), CGGeneric(getCallableFromProp))).define() + ';\n') class CallbackOperation(CallbackOperationBase): @@ -5399,7 +5755,7 @@ class GlobalGenRoots(): return CGList([ CGGeneric(AUTOGENERATED_WARNING_COMMENT), CGGeneric("pub const MAX_PROTO_CHAIN_LENGTH: usize = %d;\n\n" % config.maxProtoChainLength), - CGNonNamespacedEnum('ID', protos, [0], deriving="PartialEq, Copy, Clone"), + CGNonNamespacedEnum('ID', protos, [0], deriving="PartialEq, Copy, Clone", repr="u16"), CGNonNamespacedEnum('Proxies', proxies, [0], deriving="PartialEq, Copy, Clone"), ]) @@ -5416,7 +5772,7 @@ class GlobalGenRoots(): 'dom::bindings::codegen', 'dom::bindings::codegen::PrototypeList::Proxies', 'js::jsapi::JSContext', - 'js::jsapi::JSObject', + 'js::jsapi::HandleObject', 'libc', ], ignored_warnings=[]) @@ -5442,7 +5798,7 @@ class GlobalGenRoots(): descriptors = config.getDescriptors(register=True, isCallback=False) allprotos = [CGGeneric("use dom::types::*;\n"), - CGGeneric("use dom::bindings::js::{JS, JSRef, LayoutJS, Rootable, Temporary};\n"), + CGGeneric("use dom::bindings::js::{JS, LayoutJS, Root};\n"), CGGeneric("use dom::bindings::trace::JSTraceable;\n"), CGGeneric("use dom::bindings::utils::Reflectable;\n"), CGGeneric("use js::jsapi::JSTracer;\n\n"), @@ -5476,7 +5832,7 @@ impl ${selfName} for ${baseName} { pub struct ${name}Cast; impl ${name}Cast { #[inline] - pub fn to_ref<'a, T: ${toBound}+Reflectable>(base: JSRef<'a, T>) -> Option<JSRef<'a, ${name}>> { + pub fn to_ref<'a, T: ${toBound}+Reflectable>(base: &'a T) -> Option<&'a ${name}> { match base.${checkFn}() { true => Some(unsafe { mem::transmute(base) }), false => None @@ -5484,7 +5840,7 @@ impl ${name}Cast { } #[inline] - pub fn to_borrowed_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, ${name}>> { + pub fn to_borrowed_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a &'b T) -> Option<&'a &'b ${name}> { match base.${checkFn}() { true => Some(unsafe { mem::transmute(base) }), false => None @@ -5503,20 +5859,20 @@ impl ${name}Cast { } #[inline] - pub fn to_temporary<T: ${toBound}+Reflectable>(base: Temporary<T>) -> Option<Temporary<${name}>> { - match base.root().r().${checkFn}() { + pub fn to_root<T: ${toBound}+Reflectable>(base: Root<T>) -> Option<Root<${name}>> { + match base.r().${checkFn}() { true => Some(unsafe { mem::transmute(base) }), false => None } } #[inline] - pub fn from_ref<'a, T: ${fromBound}+Reflectable>(derived: JSRef<'a, T>) -> JSRef<'a, ${name}> { + pub fn from_ref<'a, T: ${fromBound}+Reflectable>(derived: &'a T) -> &'a ${name} { unsafe { mem::transmute(derived) } } #[inline] - pub fn from_borrowed_ref<'a, 'b, T: ${fromBound}+Reflectable>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, ${name}> { + pub fn from_borrowed_ref<'a, 'b, T: ${fromBound}+Reflectable>(derived: &'a &'b T) -> &'a &'b ${name} { unsafe { mem::transmute(derived) } } @@ -5527,7 +5883,7 @@ impl ${name}Cast { } #[inline] - pub fn from_temporary<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<${name}> { + pub fn from_root<T: ${fromBound}+Reflectable>(derived: Root<T>) -> Root<${name}> { unsafe { mem::transmute(derived) } } diff --git a/components/script/dom/bindings/codegen/Configuration.py b/components/script/dom/bindings/codegen/Configuration.py index 8741cb9eacf..6e4bcbed34c 100644 --- a/components/script/dom/bindings/codegen/Configuration.py +++ b/components/script/dom/bindings/codegen/Configuration.py @@ -148,16 +148,16 @@ class Descriptor(DescriptorProvider): if self.interface.isCallback(): self.needsRooting = False ty = "%sBinding::%s" % (ifaceName, ifaceName) - self.returnType = ty + self.returnType = "Rc<%s>"% ty self.argumentType = "???" self.memberType = "???" self.nativeType = ty else: self.needsRooting = True - self.returnType = "Temporary<%s>" % ifaceName - self.argumentType = "JSRef<%s>" % ifaceName + self.returnType = "Root<%s>" % ifaceName + self.argumentType = "&%s" % ifaceName self.memberType = "Root<%s>" % ifaceName - self.nativeType = "Unrooted<%s>" % ifaceName + self.nativeType = "Root<%s>" % ifaceName self.concreteType = ifaceName self.register = desc.get('register', True) diff --git a/components/script/dom/bindings/conversions.rs b/components/script/dom/bindings/conversions.rs index 089193cca43..317744907c7 100644 --- a/components/script/dom/bindings/conversions.rs +++ b/components/script/dom/bindings/conversions.rs @@ -24,17 +24,17 @@ //! | USVString | `USVString` | //! | ByteString | `ByteString` | //! | object | `*mut JSObject` | -//! | interface types | `JSRef<T>` | `Temporary<T>` | +//! | interface types | `&T` | `Root<T>` | //! | dictionary types | `&T` | *unsupported* | //! | enumeration types | `T` | -//! | callback function types | `T` | +//! | callback function types | `Rc<T>` | //! | nullable types | `Option<T>` | //! | sequences | `Vec<T>` | //! | union types | `T` | use dom::bindings::codegen::PrototypeList; use dom::bindings::error::throw_type_error; -use dom::bindings::js::{JSRef, Root, Unrooted}; +use dom::bindings::js::Root; use dom::bindings::num::Finite; use dom::bindings::str::{ByteString, USVString}; use dom::bindings::utils::{Reflectable, Reflector, DOMClass}; @@ -43,14 +43,15 @@ use util::str::DOMString; use js; use js::glue::{RUST_JSID_TO_STRING, RUST_JSID_IS_STRING}; use js::glue::RUST_JS_NumberValue; -use js::jsapi::{JSBool, JSContext, JSObject, JSString, jsid}; -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::rust::{ToUint64, ToInt64}; +use js::rust::{ToUint32, ToInt32}; +use js::rust::{ToUint16, ToNumber, ToBoolean, ToString}; +use js::jsapi::{JSContext, JSObject, JSString}; +use js::jsapi::{JS_StringHasLatin1Chars, JS_GetLatin1StringCharsAndLength, JS_GetTwoByteStringCharsAndLength}; use js::jsapi::{JS_NewUCStringCopyN, JS_NewStringCopyN}; use js::jsapi::{JS_WrapValue}; use js::jsapi::{JSClass, JS_GetClass}; +use js::jsapi::{HandleId, HandleValue, HandleObject, MutableHandleValue}; use js::jsval::JSVal; use js::jsval::{UndefinedValue, NullValue, BooleanValue, Int32Value, UInt32Value}; use js::jsval::{StringValue, ObjectValue, ObjectOrNullValue}; @@ -60,6 +61,9 @@ use num::Float; use std::borrow::ToOwned; use std::default; use std::slice; +use std::ptr; +use std::rc::Rc; +use core::nonzero::NonZero; /// A trait to retrieve the constants necessary to check if a `JSObject` /// implements a given interface. @@ -74,7 +78,7 @@ pub trait IDLInterface { /// A trait to convert Rust types to `JSVal`s. pub trait ToJSValConvertible { /// Convert `self` to a `JSVal`. JSAPI failure causes a task failure. - fn to_jsval(&self, cx: *mut JSContext) -> JSVal; + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue); } /// A trait to convert `JSVal`s to Rust types. @@ -85,206 +89,201 @@ pub trait FromJSValConvertible { /// Optional configuration of type `T` can be passed as the `option` /// argument. /// If it returns `Err(())`, a JSAPI exception is pending. - fn from_jsval(cx: *mut JSContext, val: JSVal, option: Self::Config) -> Result<Self, ()>; + fn from_jsval(cx: *mut JSContext, val: HandleValue, option: Self::Config) -> Result<Self, ()>; } impl ToJSValConvertible for () { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - UndefinedValue() + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(UndefinedValue()); } } impl ToJSValConvertible for JSVal { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - let mut value = *self; - if unsafe { JS_WrapValue(cx, &mut value) } == 0 { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(*self); + if unsafe { JS_WrapValue(cx, rval) } == 0 { panic!("JS_WrapValue failed."); } - value } } -unsafe fn convert_from_jsval<T: default::Default>( - cx: *mut JSContext, value: JSVal, - convert_fn: unsafe extern "C" fn(*mut JSContext, JSVal, *mut T) -> JSBool) -> Result<T, ()> { - let mut ret = default::Default::default(); - if convert_fn(cx, value, &mut ret) == 0 { - Err(()) - } else { - Ok(ret) +impl ToJSValConvertible for HandleValue { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(self.get()); + if unsafe { JS_WrapValue(cx, rval) } == 0 { + panic!("JS_WrapValue failed."); + } } } - impl ToJSValConvertible for bool { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - BooleanValue(*self) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(BooleanValue(*self)); } } impl FromJSValConvertible for bool { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<bool, ()> { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToBoolean) }; - result.map(|b| b != 0) + fn from_jsval(_cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<bool, ()> { + Ok(ToBoolean(val)) } } impl ToJSValConvertible for i8 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - Int32Value(*self as i32) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(Int32Value(*self as i32)); } } impl FromJSValConvertible for i8 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i8, ()> { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i8, ()> { + let result = ToInt32(cx, val); result.map(|v| v as i8) } } impl ToJSValConvertible for u8 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - Int32Value(*self as i32) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(Int32Value(*self as i32)); } } impl FromJSValConvertible for u8 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u8, ()> { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u8, ()> { + let result = ToInt32(cx, val); result.map(|v| v as u8) } } impl ToJSValConvertible for i16 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - Int32Value(*self as i32) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(Int32Value(*self as i32)); } } impl FromJSValConvertible for i16 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i16, ()> { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) }; + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i16, ()> { + let result = ToInt32(cx, val); result.map(|v| v as i16) } } impl ToJSValConvertible for u16 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - Int32Value(*self as i32) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(Int32Value(*self as i32)); } } impl FromJSValConvertible for u16 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u16, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToUint16) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u16, ()> { + ToUint16(cx, val) } } impl ToJSValConvertible for i32 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - Int32Value(*self) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(Int32Value(*self)); } } impl FromJSValConvertible for i32 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i32, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToECMAInt32) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i32, ()> { + ToInt32(cx, val) } } impl ToJSValConvertible for u32 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { - UInt32Value(*self) + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(UInt32Value(*self)); } } impl FromJSValConvertible for u32 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u32, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToECMAUint32) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u32, ()> { + ToUint32(cx, val) } } impl ToJSValConvertible for i64 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { unsafe { - RUST_JS_NumberValue(*self as f64) + rval.set(RUST_JS_NumberValue(*self as f64)); } } } impl FromJSValConvertible for i64 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<i64, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToInt64) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<i64, ()> { + ToInt64(cx, val) } } impl ToJSValConvertible for u64 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { unsafe { - RUST_JS_NumberValue(*self as f64) + rval.set(RUST_JS_NumberValue(*self as f64)); } } } impl FromJSValConvertible for u64 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<u64, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToUint64) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<u64, ()> { + ToUint64(cx, val) } } impl ToJSValConvertible for f32 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { unsafe { - RUST_JS_NumberValue(*self as f64) + rval.set(RUST_JS_NumberValue(*self as f64)); } } } impl FromJSValConvertible for f32 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f32, ()> { - let result = unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) }; + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f32, ()> { + let result = ToNumber(cx, val); result.map(|f| f as f32) } } impl ToJSValConvertible for f64 { - fn to_jsval(&self, _cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, _cx: *mut JSContext, rval: MutableHandleValue) { unsafe { - RUST_JS_NumberValue(*self) + rval.set(RUST_JS_NumberValue(*self)); } } } impl FromJSValConvertible for f64 { type Config = (); - fn from_jsval(cx: *mut JSContext, val: JSVal, _option: ()) -> Result<f64, ()> { - unsafe { convert_from_jsval(cx, val, JS_ValueToNumber) } + fn from_jsval(cx: *mut JSContext, val: HandleValue, _option: ()) -> Result<f64, ()> { + ToNumber(cx, val) } } impl<T: Float + ToJSValConvertible> ToJSValConvertible for Finite<T> { #[inline] - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { let value = **self; - value.to_jsval(cx) + value.to_jsval(cx, rval); } } impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite<T> { type Config = (); - fn from_jsval(cx: *mut JSContext, value: JSVal, option: ()) -> Result<Finite<T>, ()> { + fn from_jsval(cx: *mut JSContext, value: HandleValue, option: ()) -> Result<Finite<T>, ()> { let result = try!(FromJSValConvertible::from_jsval(cx, value, option)); match Finite::new(result) { Some(v) => Ok(v), @@ -297,21 +296,22 @@ impl<T: Float + FromJSValConvertible<Config=()>> FromJSValConvertible for Finite } impl ToJSValConvertible for str { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { unsafe { let string_utf16: Vec<u16> = self.utf16_units().collect(); - let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr(), string_utf16.len() as libc::size_t); + let jsstr = JS_NewUCStringCopyN(cx, string_utf16.as_ptr() as *const i16, + string_utf16.len() as libc::size_t); if jsstr.is_null() { panic!("JS_NewUCStringCopyN failed"); } - StringValue(&*jsstr) + rval.set(StringValue(&*jsstr)); } } } impl ToJSValConvertible for DOMString { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - (**self).to_jsval(cx) + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + (**self).to_jsval(cx, rval); } } @@ -333,18 +333,36 @@ impl default::Default for StringificationBehavior { /// Convert the given `JSString` to a `DOMString`. Fails if the string does not /// contain valid UTF-16. pub fn jsstring_to_str(cx: *mut JSContext, s: *mut JSString) -> DOMString { - unsafe { - let mut length = 0; - let chars = JS_GetStringCharsAndLength(cx, s, &mut length); + let mut length = 0; + let latin1 = unsafe { JS_StringHasLatin1Chars(s) != 0 }; + if latin1 { + let chars = unsafe { + JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length) + }; + assert!(!chars.is_null()); + + let mut buf = String::with_capacity(length as usize); + for i in 0..(length as isize) { + unsafe { + buf.push(*chars.offset(i) as char); + } + } + buf + } else { + let chars = unsafe { + JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), s, &mut length) + }; assert!(!chars.is_null()); - let char_vec = slice::from_raw_parts(chars, length as usize); + let char_vec = unsafe { + slice::from_raw_parts(chars as *const u16, length as usize) + }; String::from_utf16(char_vec).unwrap() } } /// Convert the given `jsid` to a `DOMString`. Fails if the `jsid` is not a /// string, or if the string does not contain valid UTF-16. -pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString { +pub fn jsid_to_str(cx: *mut JSContext, id: HandleId) -> DOMString { unsafe { assert!(RUST_JSID_IS_STRING(id) != 0); jsstring_to_str(cx, RUST_JSID_TO_STRING(id)) @@ -353,15 +371,16 @@ pub fn jsid_to_str(cx: *mut JSContext, id: jsid) -> DOMString { impl FromJSValConvertible for DOMString { type Config = StringificationBehavior; - fn from_jsval(cx: *mut JSContext, value: JSVal, + fn from_jsval(cx: *mut JSContext, value: HandleValue, null_behavior: StringificationBehavior) -> Result<DOMString, ()> { - if null_behavior == StringificationBehavior::Empty && value.is_null() { + if null_behavior == StringificationBehavior::Empty && + value.get().is_null() { Ok("".to_owned()) } else { - let jsstr = unsafe { JS_ValueToString(cx, value) }; + let jsstr = ToString(cx, value); if jsstr.is_null() { - debug!("JS_ValueToString failed"); + debug!("ToString failed"); Err(()) } else { Ok(jsstring_to_str(cx, jsstr)) @@ -371,56 +390,75 @@ impl FromJSValConvertible for DOMString { } impl ToJSValConvertible for USVString { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - self.0.to_jsval(cx) + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + self.0.to_jsval(cx, rval); } } impl FromJSValConvertible for USVString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: JSVal, _: ()) + fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<USVString, ()> { - let jsstr = unsafe { JS_ValueToString(cx, value) }; + let jsstr = ToString(cx, value); if jsstr.is_null() { - debug!("JS_ValueToString failed"); - Err(()) - } else { - unsafe { - let mut length = 0; - let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length); - assert!(!chars.is_null()); - let char_vec = slice::from_raw_parts(chars, length as usize); - Ok(USVString(String::from_utf16_lossy(char_vec))) - } + debug!("ToString failed"); + return Err(()); + } + let latin1 = unsafe { JS_StringHasLatin1Chars(jsstr) != 0 }; + if latin1 { + return Ok(USVString(jsstring_to_str(cx, jsstr))); + } + unsafe { + let mut length = 0; + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length); + assert!(!chars.is_null()); + let char_vec = slice::from_raw_parts(chars as *const u16, length as usize); + Ok(USVString(String::from_utf16_lossy(char_vec))) } } } impl ToJSValConvertible for ByteString { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { unsafe { let jsstr = JS_NewStringCopyN(cx, self.as_ptr() as *const libc::c_char, self.len() as libc::size_t); if jsstr.is_null() { panic!("JS_NewStringCopyN failed"); } - StringValue(&*jsstr) + rval.set(StringValue(&*jsstr)); } } } impl FromJSValConvertible for ByteString { type Config = (); - fn from_jsval(cx: *mut JSContext, value: JSVal, _option: ()) -> Result<ByteString, ()> { - unsafe { - let string = JS_ValueToString(cx, value); - if string.is_null() { - debug!("JS_ValueToString failed"); - return Err(()); - } + fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ()) -> Result<ByteString, ()> { + let string = ToString(cx, value); + if string.is_null() { + debug!("ToString failed"); + return Err(()); + } + + let latin1 = unsafe { JS_StringHasLatin1Chars(string) != 0 }; + if latin1 { + let mut length = 0; + let chars = unsafe { + JS_GetLatin1StringCharsAndLength(cx, ptr::null(), + string, &mut length) + }; + assert!(!chars.is_null()); + + let char_vec = unsafe { + Vec::from_raw_buf(chars as *mut u8, length as usize) + }; + + return Ok(ByteString::new(char_vec)); + } + unsafe { let mut length = 0; - let chars = JS_GetStringCharsAndLength(cx, string, &mut length); + let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), string, &mut length); let char_vec = slice::from_raw_parts(chars, length as usize); if char_vec.iter().any(|&c| c > 0xFF) { @@ -434,14 +472,13 @@ impl FromJSValConvertible for ByteString { } impl ToJSValConvertible for Reflector { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - let obj = self.get_jsobject(); + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + let obj = self.get_jsobject().get(); assert!(!obj.is_null()); - let mut value = ObjectValue(unsafe { &*obj }); - if unsafe { JS_WrapValue(cx, &mut value) } == 0 { + rval.set(ObjectValue(unsafe { &*obj })); + if unsafe { JS_WrapValue(cx, rval) } == 0 { panic!("JS_WrapValue failed."); } - value } } @@ -454,11 +491,10 @@ pub 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 { - use js::glue::{js_IsObjectProxyClass, js_IsFunctionProxyClass, IsProxyHandlerFamily}; - + use js::glue::IsProxyHandlerFamily; unsafe { - (js_IsObjectProxyClass(obj) || js_IsFunctionProxyClass(obj)) && - IsProxyHandlerFamily(obj) + let clasp = JS_GetClass(obj); + ((*clasp).flags & js::JSCLASS_IS_PROXY) != 0 && IsProxyHandlerFamily(obj) != 0 } } @@ -467,29 +503,24 @@ pub fn is_dom_proxy(obj: *mut JSObject) -> bool { // We use slot 0 for holding the raw object. This is safe for both // globals and non-globals. pub const DOM_OBJECT_SLOT: u32 = 0; -const DOM_PROXY_OBJECT_SLOT: u32 = js::JSSLOT_PROXY_PRIVATE; - -/// 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 - } else { - assert!(is_dom_proxy(obj)); - DOM_PROXY_OBJECT_SLOT - } -} /// Get the DOM object from the given reflector. pub unsafe fn native_from_reflector<T>(obj: *mut JSObject) -> *const T { use js::jsapi::JS_GetReservedSlot; + use js::glue::GetProxyPrivate; - let slot = dom_object_slot(obj); - let value = JS_GetReservedSlot(obj, slot); - value.to_private() as *const T + let clasp = JS_GetClass(obj); + let value = if is_dom_class(clasp) { + JS_GetReservedSlot(obj, DOM_OBJECT_SLOT) + } else { + assert!(is_dom_proxy(obj)); + GetProxyPrivate(obj) + }; + if value.is_undefined() { + ptr::null() + } else { + value.to_private() as *const T + } } /// Get the `DOMClass` from `obj`, or `Err(())` if `obj` is not a DOM object. @@ -518,17 +549,16 @@ unsafe fn get_dom_class(obj: *mut JSObject) -> Result<DOMClass, ()> { /// Returns Err(()) if `obj` is an opaque security wrapper or if the object is /// not a reflector for a DOM object of the given type (as defined by the /// proto_id and proto_depth). -pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unrooted<T>, ()> +pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Root<T>, ()> where T: Reflectable + IDLInterface { use js::glue::{IsWrapper, UnwrapObject}; - use std::ptr; unsafe { let dom_class = try!(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 = */ 0); if obj.is_null() { debug!("unwrapping security wrapper failed"); Err(()) @@ -547,7 +577,9 @@ pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unro let proto_depth = <T as IDLInterface>::get_prototype_depth(); if dom_class.interface_chain[proto_depth] == proto_id { debug!("good prototype"); - Ok(Unrooted::from_raw(native_from_reflector(obj))) + let native = native_from_reflector(obj); + assert!(!native.is_null()); + Ok(Root::new(NonZero::new(native))) } else { debug!("bad prototype"); Err(()) @@ -555,37 +587,54 @@ pub fn native_from_reflector_jsmanaged<T>(mut obj: *mut JSObject) -> Result<Unro } } +/// Get a Rooted<T> for a DOM object accessible from a HandleValue +pub fn native_from_handlevalue<T>(v: HandleValue) -> Result<Root<T>, ()> + where T: Reflectable + IDLInterface +{ + native_from_reflector_jsmanaged(v.get().to_object()) +} + +/// Get a Rooted<T> for a DOM object accessible from a HandleObject +pub fn native_from_handleobject<T>(obj: HandleObject) -> Result<Root<T>, ()> + where T: Reflectable + IDLInterface +{ + native_from_reflector_jsmanaged(obj.get()) +} + impl<T: Reflectable> ToJSValConvertible for Root<T> { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - self.r().reflector().to_jsval(cx) + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + self.r().reflector().to_jsval(cx, rval); } } -impl<'a, T: Reflectable> ToJSValConvertible for JSRef<'a, T> { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - self.reflector().to_jsval(cx) +impl<'a, T: Reflectable> ToJSValConvertible for &'a T { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + self.reflector().to_jsval(cx, rval); } } -impl<'a, T: Reflectable> ToJSValConvertible for Unrooted<T> { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - self.reflector().to_jsval(cx) +impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + match self { + &Some(ref value) => value.to_jsval(cx, rval), + &None => rval.set(NullValue()), + } } } -impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { +impl<T: ToJSValConvertible> ToJSValConvertible for Option<Rc<T>> { + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { match self { - &Some(ref value) => value.to_jsval(cx), - &None => NullValue(), + &Some(ref value) => (**value).to_jsval(cx, rval), + &None => rval.set(NullValue()), } } } impl<X: default::Default, T: FromJSValConvertible<Config=X>> FromJSValConvertible for Option<T> { type Config = (); - fn from_jsval(cx: *mut JSContext, value: JSVal, _: ()) -> Result<Option<T>, ()> { - if value.is_null_or_undefined() { + fn from_jsval(cx: *mut JSContext, value: HandleValue, _: ()) -> Result<Option<T>, ()> { + if value.get().is_null_or_undefined() { Ok(None) } else { let option: X = default::Default::default(); @@ -596,11 +645,10 @@ impl<X: default::Default, T: FromJSValConvertible<Config=X>> FromJSValConvertibl } impl ToJSValConvertible for *mut JSObject { - fn to_jsval(&self, cx: *mut JSContext) -> JSVal { - let mut wrapped = ObjectOrNullValue(*self); + fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { + rval.set(ObjectOrNullValue(*self)); unsafe { - assert!(JS_WrapValue(cx, &mut wrapped) != 0); + assert!(JS_WrapValue(cx, rval) != 0); } - wrapped } } diff --git a/components/script/dom/bindings/error.rs b/components/script/dom/bindings/error.rs index 7d1e7ecbdd6..d228679631a 100644 --- a/components/script/dom/bindings/error.rs +++ b/components/script/dom/bindings/error.rs @@ -6,20 +6,22 @@ use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::global::GlobalRef; -use dom::bindings::js::Rootable; use dom::domexception::{DOMException, DOMErrorName}; use util::str::DOMString; -use js::jsapi::{JSContext, JSObject}; +use js::jsapi::{JSContext, JSObject, RootedValue}; use js::jsapi::{JS_IsExceptionPending, JS_SetPendingException, JS_ReportPendingException}; -use js::jsapi::{JS_ReportErrorNumber, JSErrorFormatString, JSEXN_TYPEERR, JSEXN_RANGEERR}; +use js::jsapi::{JS_ReportErrorNumber1, JSErrorFormatString, JSExnType}; use js::jsapi::{JS_SaveFrameChain, JS_RestoreFrameChain}; -use js::rust::with_compartment; +use js::jsapi::JSAutoCompartment; +use js::jsval::UndefinedValue; +use js::JSFalse; use libc; use std::ffi::CString; use std::ptr; +use std::mem; /// DOM exceptions that can be thrown by a native DOM method. #[derive(Debug, Clone)] @@ -116,10 +118,11 @@ pub fn throw_dom_exception(cx: *mut JSContext, global: GlobalRef, }; assert!(unsafe { JS_IsExceptionPending(cx) } == 0); - let exception = DOMException::new(global, code).root(); - let thrown = exception.to_jsval(cx); + let exception = DOMException::new(global, code); + let mut thrown = RootedValue::new(cx, UndefinedValue()); + exception.to_jsval(cx, thrown.handle_mut()); unsafe { - JS_SetPendingException(cx, thrown); + JS_SetPendingException(cx, thrown.handle()); } } @@ -128,9 +131,10 @@ pub fn report_pending_exception(cx: *mut JSContext, obj: *mut JSObject) { unsafe { if JS_IsExceptionPending(cx) != 0 { let saved = JS_SaveFrameChain(cx); - with_compartment(cx, obj, || { + { + let _ac = JSAutoCompartment::new(cx, obj); JS_ReportPendingException(cx); - }); + } if saved != 0 { JS_RestoreFrameChain(cx); } @@ -158,25 +162,26 @@ static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [ static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString { format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char, argCount: 1, - exnType: JSEXN_TYPEERR as i16, + exnType: JSExnType::JSEXN_TYPEERR as i16, }; /// Format string struct used to throw `RangeError`s. static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString { format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char, argCount: 1, - exnType: JSEXN_RANGEERR as i16, + exnType: JSExnType::JSEXN_RANGEERR as i16, }; /// Callback used to throw javascript errors. /// See throw_js_error for info about error_number. unsafe extern fn get_error_message(_user_ref: *mut libc::c_void, - _locale: *const libc::c_char, - error_number: libc::c_uint) -> *const JSErrorFormatString + error_number: libc::c_uint) + -> *const JSErrorFormatString { - match error_number as i32 { - JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString, - JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString, + let num: JSExnType = mem::transmute(error_number); + match num { + JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString, + JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString, _ => panic!("Bad js error number given to get_error_message: {}", error_number) } } @@ -188,20 +193,18 @@ unsafe extern fn get_error_message(_user_ref: *mut libc::c_void, fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) { let error = CString::new(error).unwrap(); unsafe { - JS_ReportErrorNumber(cx, - Some(get_error_message as - unsafe extern "C" fn(*mut libc::c_void, *const libc::c_char, - libc::c_uint) -> *const JSErrorFormatString), + JS_ReportErrorNumber1(cx, + Some(get_error_message), ptr::null_mut(), error_number, error.as_ptr()); } } /// Throw a `TypeError` with the given message. pub fn throw_type_error(cx: *mut JSContext, error: &str) { - throw_js_error(cx, error, JSEXN_TYPEERR as u32); + throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32); } /// Throw a `RangeError` with the given message. pub fn throw_range_error(cx: *mut JSContext, error: &str) { - throw_js_error(cx, error, JSEXN_RANGEERR as u32); + throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32); } diff --git a/components/script/dom/bindings/global.rs b/components/script/dom/bindings/global.rs index d1294173dd1..a6924fbb281 100644 --- a/components/script/dom/bindings/global.rs +++ b/components/script/dom/bindings/global.rs @@ -9,7 +9,7 @@ use dom::bindings::codegen::Bindings::WindowBinding::WindowMethods; use dom::bindings::conversions::native_from_reflector_jsmanaged; -use dom::bindings::js::{JS, JSRef, Rootable, Root, Unrooted}; +use dom::bindings::js::{JS, Root}; use dom::bindings::utils::{Reflectable, Reflector}; use dom::document::DocumentHelpers; use dom::workerglobalscope::{WorkerGlobalScope, WorkerGlobalScopeHelpers}; @@ -21,7 +21,7 @@ use msg::constellation_msg::{PipelineId, WorkerId}; use net_traits::ResourceTask; use js::{JSCLASS_IS_GLOBAL, JSCLASS_IS_DOMJSCLASS}; -use js::glue::{GetGlobalForObjectCrossCompartment}; +use js::jsapi::{GetGlobalForObjectCrossCompartment}; use js::jsapi::{JSContext, JSObject}; use js::jsapi::{JS_GetClass}; use url::Url; @@ -30,9 +30,9 @@ use url::Url; #[derive(Copy, Clone)] pub enum GlobalRef<'a> { /// A reference to a `Window` object. - Window(JSRef<'a, window::Window>), + Window(&'a window::Window), /// A reference to a `WorkerGlobalScope` object. - Worker(JSRef<'a, WorkerGlobalScope>), + Worker(&'a WorkerGlobalScope), } /// A stack-based rooted reference to a global object. @@ -55,15 +55,6 @@ pub enum GlobalField { Worker(JS<WorkerGlobalScope>), } -/// An unrooted reference to a global object. -#[must_root] -pub enum GlobalUnrooted { - /// An unrooted reference to a `Window` object. - Window(Unrooted<window::Window>), - /// An unrooted reference to a `WorkerGlobalScope` object. - Worker(Unrooted<WorkerGlobalScope>), -} - impl<'a> GlobalRef<'a> { /// Get the `JSContext` for the `JSRuntime` associated with the thread /// this global object is on. @@ -76,7 +67,7 @@ impl<'a> GlobalRef<'a> { /// Extract a `Window`, causing task failure if the global object is not /// a `Window`. - pub fn as_window<'b>(&'b self) -> JSRef<'b, window::Window> { + pub fn as_window<'b>(&'b self) -> &'b window::Window { match *self { GlobalRef::Window(window) => window, GlobalRef::Worker(_) => panic!("expected a Window scope"), @@ -104,7 +95,7 @@ impl<'a> GlobalRef<'a> { pub fn resource_task(&self) -> ResourceTask { match *self { GlobalRef::Window(ref window) => { - let doc = window.Document().root(); + let doc = window.Document(); let doc = doc.r(); let loader = doc.loader(); loader.resource_task.clone() @@ -182,8 +173,8 @@ impl GlobalField { /// Create a new `GlobalField` from a rooted reference. pub fn from_rooted(global: &GlobalRef) -> GlobalField { match *global { - GlobalRef::Window(window) => GlobalField::Window(JS::from_rooted(window)), - GlobalRef::Worker(worker) => GlobalField::Worker(JS::from_rooted(worker)), + GlobalRef::Window(window) => GlobalField::Window(JS::from_ref(window)), + GlobalRef::Worker(worker) => GlobalField::Worker(JS::from_ref(worker)), } } @@ -196,30 +187,20 @@ impl GlobalField { } } -impl GlobalUnrooted { - /// Create a stack-bounded root for this reference. - pub fn root(&self) -> GlobalRoot { - match *self { - GlobalUnrooted::Window(ref window) => GlobalRoot::Window(window.root()), - GlobalUnrooted::Worker(ref worker) => GlobalRoot::Worker(worker.root()), - } - } -} - /// Returns the global object of the realm that the given JS object was created in. #[allow(unrooted_must_root)] -pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalUnrooted { +pub fn global_object_for_js_object(obj: *mut JSObject) -> GlobalRoot { unsafe { let global = GetGlobalForObjectCrossCompartment(obj); let clasp = JS_GetClass(global); assert!(((*clasp).flags & (JSCLASS_IS_DOMJSCLASS | JSCLASS_IS_GLOBAL)) != 0); match native_from_reflector_jsmanaged(global) { - Ok(window) => return GlobalUnrooted::Window(window), + Ok(window) => return GlobalRoot::Window(window), Err(_) => (), } match native_from_reflector_jsmanaged(global) { - Ok(worker) => return GlobalUnrooted::Worker(worker), + Ok(worker) => return GlobalRoot::Worker(worker), Err(_) => (), } diff --git a/components/script/dom/bindings/js.rs b/components/script/dom/bindings/js.rs index fc22078360f..400200c3df3 100644 --- a/components/script/dom/bindings/js.rs +++ b/components/script/dom/bindings/js.rs @@ -11,178 +11,32 @@ //! //! Here is a brief overview of the important types: //! -//! - `JSRef<T>`: a freely-copyable reference to a rooted DOM object. //! - `Root<T>`: a stack-based reference to a rooted DOM object. //! - `JS<T>`: a reference to a DOM object that can automatically be traced by //! the GC when encountered as a field of a Rust structure. -//! - `Temporary<T>`: a reference to a DOM object that will remain rooted for -//! the duration of its lifetime. //! -//! The rule of thumb is as follows: +//! `JS<T>` does not allow access to their inner value without explicitly +//! creating a stack-based root via the `root` method. This returns a `Root<T>`, +//! which causes the JS-owned value to be uncollectable for the duration of the +//! `Root` object's lifetime. A reference to the object can then be obtained +//! from the `Root` object. These references are not allowed to outlive their +//! originating `Root<T>`. //! -//! - All methods return `Temporary<T>`, to ensure the value remains alive -//! until it is stored somewhere that is reachable by the GC. -//! - All functions take `JSRef<T>` arguments, to ensure that they will remain -//! uncollected for the duration of their usage. -//! - All DOM structs contain `JS<T>` fields and derive the `JSTraceable` -//! trait, to ensure that they are transitively marked as reachable by the GC -//! if the enclosing value is reachable. -//! - All methods for type `T` are implemented for `JSRef<T>`, to ensure that -//! the self value will not be collected for the duration of the method call. -//! -//! Both `Temporary<T>` and `JS<T>` do not allow access to their inner value -//! without explicitly creating a stack-based root via the `root` method -//! through the `Rootable<T>` trait. This returns a `Root<T>`, which causes the -//! JS-owned value to be uncollectable for the duration of the `Root` object's -//! lifetime. A `JSRef<T>` can be obtained from a `Root<T>` by calling the `r` -//! method. These `JSRef<T>` values are not allowed to outlive their -//! originating `Root<T>`, to ensure that all interactions with the enclosed -//! value only occur when said value is uncollectable, and will cause static -//! lifetime errors if misused. -//! -//! Other miscellaneous helper traits: -//! -//! - `OptionalRootable` and `OptionalOptionalRootable`: make rooting `Option` -//! values easy via a `root` method -//! - `ResultRootable`: make rooting successful `Result` values easy -//! - `TemporaryPushable`: allows mutating vectors of `JS<T>` with new elements -//! of `JSRef`/`Temporary` -//! - `RootedReference`: makes obtaining an `Option<JSRef<T>>` from an -//! `Option<Root<T>>` easy use dom::bindings::trace::JSTraceable; -use dom::bindings::trace::RootedVec; +use dom::bindings::trace::trace_reflector; use dom::bindings::utils::{Reflector, Reflectable}; use dom::node::Node; -use js::jsapi::JSObject; -use js::jsval::JSVal; +use js::jsapi::{JSObject, Heap, JSTracer}; +use js::jsval::{JSVal, UndefinedValue}; use layout_interface::TrustedNodeAddress; use script_task::STACK_ROOTS; use core::nonzero::NonZero; -use libc; use std::cell::{Cell, UnsafeCell}; use std::default::Default; -use std::intrinsics::return_address; -use std::marker::PhantomData; use std::ops::Deref; -/// An unrooted, JS-owned value. Must not be held across a GC. -/// -/// This is used in particular to wrap pointers extracted from a reflector. -#[must_root] -pub struct Unrooted<T> { - ptr: NonZero<*const T> -} - -impl<T: Reflectable> Unrooted<T> { - /// Create a new JS-owned value wrapped from a raw Rust pointer. - pub unsafe fn from_raw(raw: *const T) -> Unrooted<T> { - assert!(!raw.is_null()); - Unrooted { - ptr: NonZero::new(raw) - } - } - - /// Create a new unrooted value from a `JS<T>`. - #[allow(unrooted_must_root)] - pub fn from_js(ptr: JS<T>) -> Unrooted<T> { - Unrooted { - ptr: ptr.ptr - } - } - - /// Create a new unrooted value from a `Temporary<T>`. - #[allow(unrooted_must_root)] - pub fn from_temporary(ptr: Temporary<T>) -> Unrooted<T> { - Unrooted::from_js(ptr.inner) - } - - /// Get the `Reflector` for this pointer. - pub fn reflector<'a>(&'a self) -> &'a Reflector { - unsafe { - (**self.ptr).reflector() - } - } - - /// Returns an unsafe pointer to the interior of this object. - pub unsafe fn unsafe_get(&self) -> *const T { - *self.ptr - } -} - -impl<T: Reflectable> Rootable<T> for Unrooted<T> { - /// Create a stack-bounded root for this value. - fn root(&self) -> Root<T> { - STACK_ROOTS.with(|ref collection| { - let RootCollectionPtr(collection) = collection.get().unwrap(); - unsafe { - Root::new(&*collection, self.ptr) - } - }) - } -} - -impl<T> Copy for Unrooted<T> {} -impl<T> Clone for Unrooted<T> { - fn clone(&self) -> Unrooted<T> { *self } -} - -/// 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. Can be assigned into JS-owned member fields (i.e. -/// `JS<T>` types) safely via the `JS<T>::assign` method or -/// `OptionalSettable::assign` (for `Option<JS<T>>` fields). -#[allow(unrooted_must_root)] -pub struct Temporary<T> { - inner: JS<T>, - /// On-stack JS pointer to assuage conservative stack scanner - _js_ptr: *mut JSObject, -} - -impl<T> Clone for Temporary<T> { - fn clone(&self) -> Temporary<T> { - Temporary { - inner: self.inner, - _js_ptr: self._js_ptr, - } - } -} - -impl<T> PartialEq for Temporary<T> { - fn eq(&self, other: &Temporary<T>) -> bool { - self.inner == other.inner - } -} - -impl<T: Reflectable> Temporary<T> { - /// Create a new `Temporary` value from an unrooted value. - #[allow(unrooted_must_root)] - pub fn from_unrooted(unrooted: Unrooted<T>) -> Temporary<T> { - Temporary { - inner: JS { ptr: unrooted.ptr }, - _js_ptr: unrooted.reflector().get_jsobject(), - } - } - - /// Create a new `Temporary` value from a rooted value. - #[allow(unrooted_must_root)] - pub fn from_rooted<U: Assignable<T>>(root: U) -> Temporary<T> { - let inner = JS::from_rooted(root); - Temporary { - inner: inner, - _js_ptr: inner.reflector().get_jsobject(), - } - } -} - -impl<T: Reflectable> Rootable<T> for Temporary<T> { - /// Create a stack-bounded root for this value. - fn root(&self) -> Root<T> { - self.inner.root() - } -} - /// A traced reference to a DOM object. Must only be used as a field in other /// DOM objects. #[must_root] @@ -198,6 +52,32 @@ impl<T> JS<T> { } } } +impl<T: Reflectable> JS<T> { + /// Root this JS-owned value to prevent its collection as garbage. + pub fn root(&self) -> Root<T> { + Root::new(self.ptr) + } + /// Create a JS<T> from a Root<T> + /// XXX Not a great API. Should be a call on Root<T> instead + pub fn from_rooted(root: &Root<T>) -> JS<T> { + JS { + ptr: unsafe { NonZero::new(&**root) } + } + } + /// Create a JS<T> from a &T + pub fn from_ref(obj: &T) -> JS<T> { + JS { + ptr: unsafe { NonZero::new(&*obj) } + } + } + /// Store an rooted value in this field. This is safe under the + /// assumption that JS<T> 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: Root<T>) { + self.ptr = val.ptr.clone(); + } +} /// An unrooted reference to a DOM object for use in layout. `Layout*Helpers` /// traits must be implemented on this. @@ -208,7 +88,7 @@ pub struct LayoutJS<T> { impl<T: Reflectable> LayoutJS<T> { /// Get the reflector. pub unsafe fn get_jsobject(&self) -> *mut JSObject { - (**self.ptr).reflector().get_jsobject() + (**self.ptr).reflector().get_jsobject().get() } } @@ -217,14 +97,12 @@ impl<T> Copy for JS<T> {} impl<T> Copy for LayoutJS<T> {} impl<T> PartialEq for JS<T> { - #[allow(unrooted_must_root)] fn eq(&self, other: &JS<T>) -> bool { self.ptr == other.ptr } } impl<T> PartialEq for LayoutJS<T> { - #[allow(unrooted_must_root)] fn eq(&self, other: &LayoutJS<T>) -> bool { self.ptr == other.ptr } @@ -260,29 +138,6 @@ impl LayoutJS<Node> { } } -impl<T: Reflectable> Rootable<T> for JS<T> { - /// Root this JS-owned value to prevent its collection as garbage. - fn root(&self) -> Root<T> { - STACK_ROOTS.with(|ref collection| { - let RootCollectionPtr(collection) = collection.get().unwrap(); - unsafe { - Root::new(&*collection, self.ptr) - } - }) - } -} - -impl<U: Reflectable> JS<U> { - /// Create a `JS<T>` from any JS-managed pointer. - pub fn from_rooted<T: Assignable<U>>(root: T) -> JS<U> { - unsafe { - root.get_js() - } - } -} - -//XXXjdm This is disappointing. This only gets called from trace hooks, in theory, -// so it's safe to assume that self is rooted and thereby safe to access. impl<T: Reflectable> Reflectable for JS<T> { fn reflector<'a>(&'a self) -> &'a Reflector { unsafe { @@ -298,19 +153,50 @@ impl<T: Reflectable> Reflectable for JS<T> { pub trait HeapGCValue: JSTraceable { } -impl HeapGCValue for JSVal { +impl HeapGCValue for Heap<JSVal> { } impl<T: Reflectable> HeapGCValue for JS<T> { } -/// A holder that provides interior mutability for GC-managed values such as -/// `JSVal` and `JS<T>`. +/// A holder that provides interior mutability for GC-managed JSVals. /// /// Must be used in place of traditional interior mutability to ensure proper /// GC barriers are enforced. #[must_root] #[jstraceable] +pub struct MutHeapJSVal { + val: UnsafeCell<Heap<JSVal>>, +} + +impl MutHeapJSVal { + /// Create a new `MutHeapJSVal`. + pub fn new() -> MutHeapJSVal { + MutHeapJSVal { + val: UnsafeCell::new(Heap::default()), + } + } + + /// Set this `MutHeapJSVal` to the given value, calling write barriers as + /// appropriate. + pub fn set(&self, val: JSVal) { + unsafe { + let cell = self.val.get(); + (*cell).set(val); + } + } + + /// Set the value in this `MutHeapJSVal`, calling read barriers as appropriate. + pub fn get(&self) -> JSVal { + unsafe { (*self.val.get()).get() } + } +} + + +/// A holder that provides interior mutability for GC-managed values such as +/// `JS<T>`. +#[must_root] +#[jstraceable] pub struct MutHeap<T: HeapGCValue+Copy> { val: Cell<T>, } @@ -323,13 +209,12 @@ impl<T: HeapGCValue+Copy> MutHeap<T> { } } - /// Set this `MutHeap` to the given value, calling write barriers as - /// appropriate. + /// Set this `MutHeap` to the given value. pub fn set(&self, val: T) { self.val.set(val) } - /// Set the value in this `MutHeap`, calling read barriers as appropriate. + /// Set the value in this `MutHeap`. pub fn get(&self) -> T { self.val.get() } @@ -353,8 +238,7 @@ impl<T: HeapGCValue+Copy> MutNullableHeap<T> { } } - /// Set this `MutNullableHeap` to the given value, calling write barriers - /// as appropriate. + /// Set this `MutNullableHeap` to the given value. pub fn set(&self, val: Option<T>) { self.ptr.set(val); } @@ -368,14 +252,14 @@ impl<T: HeapGCValue+Copy> MutNullableHeap<T> { impl<T: Reflectable> MutNullableHeap<JS<T>> { /// Retrieve a copy of the current inner value. If it is `None`, it is /// initialized with the result of `cb` first. - pub fn or_init<F>(&self, cb: F) -> Temporary<T> - where F: FnOnce() -> Temporary<T> + pub fn or_init<F>(&self, cb: F) -> Root<T> + where F: FnOnce() -> Root<T> { match self.get() { - Some(inner) => Temporary::from_rooted(inner), + Some(inner) => Root::from_rooted(inner), None => { let inner = cb(); - self.set(Some(JS::from_rooted(inner.clone()))); + self.set(Some(JS::from_rooted(&inner))); inner }, } @@ -396,16 +280,6 @@ impl<T: HeapGCValue+Copy> Default for MutNullableHeap<T> { } } -impl<T: Reflectable> JS<T> { - /// Store an unrooted value in this field. This is safe under the - /// assumption that JS<T> 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<T>) { - *self = val.inner.clone(); - } -} - impl<T: Reflectable> LayoutJS<T> { /// Returns an unsafe pointer to the interior of this JS object. This is /// the only method that be safely accessed from layout. (The fact that @@ -419,129 +293,36 @@ impl<T: Reflectable> LayoutJS<T> { pub trait RootedReference<T> { /// Obtain a safe optional reference to the wrapped JS owned-value that /// cannot outlive the lifetime of this root. - fn r<'a>(&'a self) -> Option<JSRef<'a, T>>; + fn r<'a>(&'a self) -> Option<&'a T>; } impl<T: Reflectable> RootedReference<T> for Option<Root<T>> { - fn r<'a>(&'a self) -> Option<JSRef<'a, T>> { + fn r<'a>(&'a self) -> Option<&'a T> { self.as_ref().map(|root| root.r()) } } -/// Get an `Option<Option<JSRef<T>>>` out of an `Option<Option<Root<T>>>` +/// Get an `Option<Option<&T>>` out of an `Option<Option<Root<T>>>` pub trait OptionalRootedReference<T> { /// Obtain a safe optional optional reference to the wrapped JS owned-value /// that cannot outlive the lifetime of this root. - fn r<'a>(&'a self) -> Option<Option<JSRef<'a, T>>>; + fn r<'a>(&'a self) -> Option<Option<&'a T>>; } impl<T: Reflectable> OptionalRootedReference<T> for Option<Option<Root<T>>> { - fn r<'a>(&'a self) -> Option<Option<JSRef<'a, T>>> { + fn r<'a>(&'a self) -> Option<Option<&'a T>> { self.as_ref().map(|inner| inner.r()) } } -/// Trait that allows extracting a `JS<T>` 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. -pub trait Assignable<T> { - /// Extract an unrooted `JS<T>`. - unsafe fn get_js(&self) -> JS<T>; -} - -impl<T> Assignable<T> for JS<T> { - unsafe fn get_js(&self) -> JS<T> { - self.clone() - } -} - -impl<'a, T: Reflectable> Assignable<T> for JSRef<'a, T> { - unsafe fn get_js(&self) -> JS<T> { - JS { - ptr: self.ptr - } - } -} - -impl<T: Reflectable> Assignable<T> for Temporary<T> { - unsafe fn get_js(&self) -> JS<T> { - self.inner.clone() - } -} - - -/// Root a rootable `Option` type (used for `Option<Temporary<T>>`) -pub trait OptionalRootable<T> { - /// Root the inner value, if it exists. - fn root(&self) -> Option<Root<T>>; -} - -impl<T: Reflectable, U: Rootable<T>> OptionalRootable<T> for Option<U> { - fn root(&self) -> Option<Root<T>> { - self.as_ref().map(|inner| inner.root()) - } -} - -/// Root a rootable `Option<Option>` type (used for `Option<Option<JS<T>>>`) -pub trait OptionalOptionalRootable<T> { - /// Root the inner value, if it exists. - fn root(&self) -> Option<Option<Root<T>>>; -} - -impl<T: Reflectable, U: OptionalRootable<T>> OptionalOptionalRootable<T> for Option<U> { - fn root(&self) -> Option<Option<Root<T>>> { - self.as_ref().map(|inner| inner.root()) - } -} - -/// Root a rootable `Result` type (any of `Temporary<T>` or `JS<T>`) -pub trait ResultRootable<T,U> { - /// Root the inner value, if it exists. - fn root(self) -> Result<Root<T>, U>; -} - -impl<T: Reflectable, U, V: Rootable<T>> ResultRootable<T, U> for Result<V, U> { - fn root(self) -> Result<Root<T>, U> { - self.map(|inner| inner.root()) - } -} - -/// Root a rootable type. -pub trait Rootable<T> { - /// Root the value. - fn root(&self) -> Root<T>; -} - - -/// Provides a facility to push unrooted values onto lists of rooted values. -/// This is safe under the assumption that said lists are reachable via the GC -/// graph, and therefore the new values are transitively rooted for the -/// lifetime of their new owner. -pub trait TemporaryPushable<T> { - /// Push a new value onto this container. - fn push_unrooted(&mut self, val: &T); - /// Insert a new value into this container. - fn insert_unrooted(&mut self, index: usize, val: &T); -} - -impl<T: Assignable<U>, U: Reflectable> TemporaryPushable<T> for Vec<JS<U>> { - fn push_unrooted(&mut self, val: &T) { - self.push(unsafe { val.get_js() }); - } - - fn insert_unrooted(&mut self, index: usize, val: &T) { - self.insert(index, unsafe { val.get_js() }); - } -} - -/// An opaque, LIFO rooting mechanism. This tracks roots and ensures that they -/// are destructed in a LIFO order. +/// A rooting mechanism for reflectors on the stack. +/// LIFO is not required. /// /// See also [*Exact Stack Rooting - Storing a GCPointer on the CStack*] /// (https://developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/Internals/GC/Exact_Stack_Rooting). #[no_move] pub struct RootCollection { - roots: UnsafeCell<RootedVec<*mut JSObject>>, + roots: UnsafeCell<Vec<*const Reflector>>, } /// A pointer to a RootCollection, for use in global variables. @@ -555,142 +336,114 @@ impl Clone for RootCollectionPtr { impl RootCollection { /// Create an empty collection of roots pub fn new() -> RootCollection { - let addr = unsafe { - return_address() as *const libc::c_void - }; - RootCollection { - roots: UnsafeCell::new(RootedVec::new_with_destination_address(addr)), + roots: UnsafeCell::new(vec!()), } } - /// Track a stack-based root as a pointer to ensure LIFO root ordering. - fn root<'b>(&self, untracked_js_ptr: *mut JSObject) { + /// Start tracking a stack-based root + fn root<'b>(&self, untracked_reflector: *const Reflector) { unsafe { - let roots = self.roots.get(); - (*roots).push(untracked_js_ptr); - debug!(" rooting {:?}", untracked_js_ptr); + let mut roots = &mut *self.roots.get(); + roots.push(untracked_reflector); + assert!(!(*untracked_reflector).get_jsobject().is_null()) } } - /// Stop tracking a stack-based root, asserting if LIFO root ordering has - /// been violated + /// Stop tracking a stack-based root, asserting if the reflector isn't found fn unroot<'b, T: Reflectable>(&self, rooted: &Root<T>) { unsafe { - let roots = self.roots.get(); - let unrooted = (*roots).pop().unwrap(); - debug!("unrooted {:?} (expecting {:?}", unrooted, rooted.js_ptr); - assert!(unrooted == rooted.js_ptr); + let mut roots = &mut *self.roots.get(); + let old_reflector = &*rooted.r().reflector(); + match roots.iter().rposition(|r| *r == old_reflector) { + Some(idx) => { + roots.remove(idx); + }, + None => panic!("Can't remove a root that was never rooted!") + } } } } +/// SM Callback that traces the rooted reflectors +pub unsafe fn trace_roots(tracer: *mut JSTracer) { + STACK_ROOTS.with(|ref collection| { + let RootCollectionPtr(collection) = collection.get().unwrap(); + let collection = &*(*collection).roots.get(); + for root in collection.iter() { + trace_reflector(tracer, "reflector", &**root); + } + }); +} + /// A rooted reference to a DOM object. /// /// The JS value is pinned for the duration of this object's lifetime; roots /// are additive, so this object's destruction will not invalidate other roots /// 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. -#[no_move] +/// `RootCollection` object. pub struct Root<T: Reflectable> { - /// List that ensures correct dynamic root ordering - root_list: &'static RootCollection, /// Reference to rooted value that must not outlive this container ptr: NonZero<*const T>, - /// On-stack JS pointer to assuage conservative stack scanner - js_ptr: *mut JSObject, + /// List that ensures correct dynamic root ordering + root_list: *const RootCollection, } impl<T: Reflectable> Root<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`. - #[inline] - fn new(roots: &'static RootCollection, unrooted: NonZero<*const T>) - -> Root<T> { - let js_ptr = unsafe { - (**unrooted).reflector().get_jsobject() - }; - roots.root(js_ptr); - Root { - root_list: roots, - ptr: unrooted, - js_ptr: js_ptr, - } + /// It cannot not outlive its associated `RootCollection`, and it gives + /// out references which cannot outlive this new `Root`. + pub fn new(unrooted: NonZero<*const T>) + -> Root<T> { + STACK_ROOTS.with(|ref collection| { + let RootCollectionPtr(collection) = collection.get().unwrap(); + unsafe { (*collection).root(&*(**unrooted).reflector()) } + Root { + ptr: unrooted, + root_list: collection, + } + }) } - /// Obtain a safe reference to the wrapped JS owned-value that cannot - /// outlive the lifetime of this root. - pub fn r<'b>(&'b self) -> JSRef<'b, T> { - JSRef { - ptr: self.ptr, - chain: PhantomData, - } + /// Generate a new root from a reference + pub fn from_ref(unrooted: &T) -> Root<T> { + Root::new(unsafe { NonZero::new(&*unrooted) }) } - /// Obtain an unsafe reference to the wrapped JS owned-value that can + /// Obtain a safe reference to the wrapped JS owned-value that cannot /// outlive the lifetime of this root. - /// - /// DO NOT CALL. - pub fn get_unsound_ref_forever<'b>(&self) -> JSRef<'b, T> { - JSRef { - ptr: self.ptr, - chain: PhantomData, - } + pub fn r<'a>(&'a self) -> &'a T { + &**self } -} -impl<T: Reflectable> Drop for Root<T> { - fn drop(&mut self) { - self.root_list.unroot(self); + /// Don't use this. Don't make me find you. + pub fn get_unsound_ref_forever<'a, 'b>(&'a self) -> &'b T { + unsafe { &**self.ptr } } -} -impl<'a, T: Reflectable> Deref for JSRef<'a, T> { - type Target = T; - fn deref<'b>(&'b self) -> &'b T { - unsafe { - &**self.ptr - } + /// Generate a new root from a JS<T> reference + #[allow(unrooted_must_root)] + pub fn from_rooted(js: JS<T>) -> Root<T> { + js.root() } } -/// A reference to a DOM object that is guaranteed to be alive. This is freely -/// copyable. -pub struct JSRef<'a, T> { - ptr: NonZero<*const T>, - chain: PhantomData<&'a ()>, -} - -impl<'a, T> Copy for JSRef<'a, T> {} - -impl<'a, T> Clone for JSRef<'a, T> { - fn clone(&self) -> JSRef<'a, T> { - JSRef { - ptr: self.ptr.clone(), - chain: self.chain, - } +impl<T: Reflectable> Deref for Root<T> { + type Target = T; + fn deref<'a>(&'a self) -> &'a T { + unsafe { &**self.ptr.deref() } } } -impl<'a, 'b, T> PartialEq<JSRef<'b, T>> for JSRef<'a, T> { - fn eq(&self, other: &JSRef<T>) -> bool { +impl<T: Reflectable> PartialEq for Root<T> { + fn eq(&self, other: &Root<T>) -> bool { self.ptr == other.ptr } } -impl<'a, T: Reflectable> JSRef<'a, T> { - /// Returns the inner pointer directly. - pub fn extended_deref(self) -> &'a T { - unsafe { - &**self.ptr - } +impl<T: Reflectable> Drop for Root<T> { + fn drop(&mut self) { + unsafe { (*self.root_list).unroot(self); } } } -impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> { - fn reflector<'b>(&'b self) -> &'b Reflector { - (**self).reflector() - } -} diff --git a/components/script/dom/bindings/proxyhandler.rs b/components/script/dom/bindings/proxyhandler.rs index 4441bdf2335..b7800ddc83e 100644 --- a/components/script/dom/bindings/proxyhandler.rs +++ b/components/script/dom/bindings/proxyhandler.rs @@ -8,18 +8,19 @@ use dom::bindings::conversions::is_dom_proxy; use dom::bindings::utils::delete_property_by_id; -use js::jsapi::{JSContext, jsid, JSPropertyDescriptor, JSObject, JSString}; +use js::jsapi::{JSContext, JSPropertyDescriptor, JSObject, JSString}; use js::jsapi::{JS_GetPropertyDescriptorById, JS_NewStringCopyN}; -use js::jsapi::{JS_DefinePropertyById, JS_NewObjectWithGivenProto}; -use js::jsapi::{JS_ReportErrorFlagsAndNumber, JS_StrictPropertyStub}; -use js::jsapi::{JSREPORT_WARNING, JSREPORT_STRICT, JSREPORT_STRICT_MODE_ERROR}; +use js::jsapi::{JS_DefinePropertyById6, JS_NewObjectWithGivenProto}; +use js::jsapi::{JS_StrictPropertyStub, JSErrNum}; +use js::jsapi::{Handle, HandleObject, HandleId, MutableHandle, RootedObject, ObjectOpResult}; +use js::jsapi::AutoIdVector; +use js::jsapi::GetObjectProto; use js::jsval::ObjectValue; use js::glue::GetProxyExtra; -use js::glue::{GetObjectProto, GetObjectParent, SetProxyExtra, GetProxyHandler}; +use js::glue::{SetProxyExtra, GetProxyHandler}; use js::glue::InvokeGetOwnPropertyDescriptor; -use js::glue::RUST_js_GetErrorMessage; -use js::glue::AutoIdVector; -use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY, JSRESOLVE_QUALIFIED}; +use js::{JSPROP_GETTER, JSPROP_ENUMERATE, JSPROP_READONLY}; +use js::{JSTrue, JSFalse}; use libc; use std::mem; @@ -32,60 +33,78 @@ static JSPROXYSLOT_EXPANDO: u32 = 0; /// Otherwise, walk along the prototype chain to find a property with that /// name. pub unsafe extern fn get_property_descriptor(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; + proxy: HandleObject, + id: HandleId, + desc: MutableHandle<JSPropertyDescriptor>) + -> u8 { + let handler = GetProxyHandler(proxy.get()); + if InvokeGetOwnPropertyDescriptor(handler, cx, proxy, id, desc) == 0 { + return JSFalse; } - if !(*desc).obj.is_null() { - return true; + if !desc.get().obj.is_null() { + return JSTrue; } - //let proto = JS_GetPrototype(proxy); - let proto = GetObjectProto(proxy); - if proto.is_null() { - (*desc).obj = ptr::null_mut(); - return true; + let mut proto = RootedObject::new(cx, ptr::null_mut()); + if GetObjectProto(cx, proxy, proto.handle_mut()) == 0 { + desc.get().obj = ptr::null_mut(); + return JSTrue; } - JS_GetPropertyDescriptorById(cx, proto, id, JSRESOLVE_QUALIFIED, desc) != 0 + JS_GetPropertyDescriptorById(cx, proto.handle(), id, desc) } /// Defines an expando on the given `proxy`. -pub unsafe extern fn define_property(cx: *mut JSContext, proxy: *mut JSObject, - id: jsid, desc: *mut JSPropertyDescriptor) - -> bool { - static JSMSG_GETTER_ONLY: libc::c_uint = 160; - +pub unsafe extern fn define_property(cx: *mut JSContext, proxy: HandleObject, + id: HandleId, desc: Handle<JSPropertyDescriptor>, + result: *mut ObjectOpResult) + -> u8 { //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.get().setter); let setter_stub: *const libc::c_void = mem::transmute(JS_StrictPropertyStub); - 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; + if (desc.get().attrs & JSPROP_GETTER) != 0 && setter == setter_stub { + (*result).code_ = JSErrNum::JSMSG_GETTER_ONLY as u32; + return JSTrue; } - let expando = ensure_expando_object(cx, proxy); - return JS_DefinePropertyById(cx, expando, id, (*desc).value, (*desc).getter, - (*desc).setter, (*desc).attrs) != 0; + let expando = RootedObject::new(cx, ensure_expando_object(cx, proxy)); + JS_DefinePropertyById6(cx, expando.handle(), id, desc, result) } /// Deletes an expando off the given `proxy`. -pub unsafe extern fn delete(cx: *mut JSContext, proxy: *mut JSObject, id: jsid, - bp: *mut bool) -> bool { - let expando = get_expando_object(proxy); - if expando.is_null() { - *bp = true; - return true; +pub unsafe extern fn delete(cx: *mut JSContext, proxy: HandleObject, id: HandleId, + bp: *mut ObjectOpResult) -> u8 { + let expando = RootedObject::new(cx, get_expando_object(proxy)); + if expando.ptr.is_null() { + (*bp).code_ = 0 /* OkCode */; + return JSTrue; } - return delete_property_by_id(cx, expando, id, &mut *bp); + delete_property_by_id(cx, expando.handle(), id, bp) +} + +/// Stub for ownPropertyKeys +pub unsafe extern fn own_property_keys(cx: *mut JSContext, + proxy: HandleObject, + props: *mut AutoIdVector) -> u8 { + // FIXME: implement this + // https://github.com/servo/servo/issues/6390 + JSTrue +} + +/// Controls whether the Extensible bit can be changed +pub unsafe extern fn prevent_extensions(_cx: *mut JSContext, + _proxy: HandleObject, + result: *mut ObjectOpResult) -> u8 { + (*result).code_ = JSErrNum::JSMSG_CANT_PREVENT_EXTENSIONS as u32; + return JSTrue; +} + +/// Reports whether the object is Extensible +pub unsafe extern fn is_extensible(_cx: *mut JSContext, _proxy: HandleObject, + succeeded: *mut u8) -> u8 { + *succeeded = JSTrue; + return JSTrue; } /// Returns the stringification of an object with class `name`. @@ -103,10 +122,10 @@ pub fn object_to_string(cx: *mut JSContext, name: &str) -> *mut JSString { } /// Get the expando object, or null if there is none. -pub fn get_expando_object(obj: *mut JSObject) -> *mut JSObject { +pub fn get_expando_object(obj: HandleObject) -> *mut JSObject { unsafe { - assert!(is_dom_proxy(obj)); - let val = GetProxyExtra(obj, JSPROXYSLOT_EXPANDO); + assert!(is_dom_proxy(obj.get())); + let val = GetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO); if val.is_undefined() { ptr::null_mut() } else { @@ -117,18 +136,16 @@ pub fn get_expando_object(obj: *mut JSObject) -> *mut JSObject { /// Get the expando object, or create it if it doesn't exist yet. /// Fails on JSAPI failure. -pub fn ensure_expando_object(cx: *mut JSContext, obj: *mut JSObject) +pub fn ensure_expando_object(cx: *mut JSContext, obj: HandleObject) -> *mut JSObject { unsafe { - assert!(is_dom_proxy(obj)); + assert!(is_dom_proxy(obj.get())); let mut expando = get_expando_object(obj); if expando.is_null() { - expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), - ptr::null_mut(), - GetObjectParent(obj)); + expando = JS_NewObjectWithGivenProto(cx, ptr::null_mut(), HandleObject::null()); assert!(!expando.is_null()); - SetProxyExtra(obj, JSPROXYSLOT_EXPANDO, ObjectValue(&*expando)); + SetProxyExtra(obj.get(), JSPROXYSLOT_EXPANDO, ObjectValue(&*expando)); } return expando; } @@ -142,18 +159,4 @@ pub fn fill_property_descriptor(desc: &mut JSPropertyDescriptor, desc.attrs = if readonly { JSPROP_READONLY } else { 0 } | JSPROP_ENUMERATE; desc.getter = None; desc.setter = None; - desc.shortid = 0; -} - -/// No-op required hook. -pub unsafe extern fn get_own_property_names(_cx: *mut JSContext, - _obj: *mut JSObject, - _v: *mut AutoIdVector) -> bool { - true -} - -/// No-op required hook. -pub unsafe extern fn enumerate(_cx: *mut JSContext, _obj: *mut JSObject, - _v: *mut AutoIdVector) -> bool { - true } diff --git a/components/script/dom/bindings/refcounted.rs b/components/script/dom/bindings/refcounted.rs index 046ae913b50..ad7f9239eae 100644 --- a/components/script/dom/bindings/refcounted.rs +++ b/components/script/dom/bindings/refcounted.rs @@ -22,11 +22,12 @@ //! is rooted when a hashmap entry is first created, and unrooted when the hashmap entry //! is removed. -use dom::bindings::js::{Temporary, JSRef, Unrooted}; +use dom::bindings::js::Root; use dom::bindings::utils::{Reflector, Reflectable}; +use dom::bindings::trace::trace_reflector; use script_task::{ScriptMsg, ScriptChan}; -use js::jsapi::{JS_AddObjectRoot, JS_RemoveObjectRoot, JSContext}; +use js::jsapi::{JSContext, JSTracer}; use libc; use std::cell::RefCell; @@ -35,6 +36,7 @@ use std::collections::hash_map::Entry::{Vacant, Occupied}; use std::marker::PhantomData; use std::rc::Rc; use std::sync::{Arc, Mutex}; +use core::nonzero::NonZero; thread_local!(pub static LIVE_REFERENCES: Rc<RefCell<Option<LiveDOMReferences>>> = Rc::new(RefCell::new(None))); @@ -63,7 +65,7 @@ impl<T: Reflectable> Trusted<T> { /// Create a new `Trusted<T>` instance from an existing DOM pointer. The DOM object will /// be prevented from being GCed for the duration of the resulting `Trusted<T>` object's /// lifetime. - pub fn new(cx: *mut JSContext, ptr: JSRef<T>, script_chan: Box<ScriptChan + Send>) -> Trusted<T> { + pub fn new(cx: *mut JSContext, ptr: &T, script_chan: Box<ScriptChan + Send>) -> Trusted<T> { LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); @@ -81,14 +83,14 @@ impl<T: Reflectable> Trusted<T> { /// Obtain a usable DOM pointer from a pinned `Trusted<T>` value. Fails if used on /// a different thread than the original value from which this `Trusted<T>` was /// obtained. - pub fn to_temporary(&self) -> Temporary<T> { + pub fn root(&self) -> Root<T> { assert!(LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); self.owner_thread == (&*live_references) as *const _ as *const libc::c_void })); unsafe { - Temporary::from_unrooted(Unrooted::from_raw(self.ptr as *const T)) + Root::new(NonZero::new(self.ptr as *const T)) } } } @@ -151,10 +153,6 @@ impl LiveDOMReferences { refcount.clone() } Vacant(entry) => { - unsafe { - let rootable = (*ptr).reflector().rootable(); - JS_AddObjectRoot(cx, rootable); - } let refcount = Arc::new(Mutex::new(1)); entry.insert(refcount.clone()); refcount @@ -168,7 +166,6 @@ impl LiveDOMReferences { LIVE_REFERENCES.with(|ref r| { let r = r.borrow(); let live_references = r.as_ref().unwrap(); - let reflectable = raw_reflectable as *const Reflector; let mut table = live_references.table.borrow_mut(); match table.entry(raw_reflectable) { Occupied(entry) => { @@ -178,9 +175,6 @@ impl LiveDOMReferences { return; } - unsafe { - JS_RemoveObjectRoot(cx, (*reflectable).rootable()); - } let _ = entry.remove(); } Vacant(_) => { @@ -194,3 +188,16 @@ impl LiveDOMReferences { }) } } + +/// A JSTraceDataOp for tracing reflectors held in LIVE_REFERENCES +pub unsafe extern fn trace_refcounted_objects(tracer: *mut JSTracer, _data: *mut libc::c_void) { + LIVE_REFERENCES.with(|ref r| { + let r = r.borrow(); + let live_references = r.as_ref().unwrap(); + let table = live_references.table.borrow(); + for obj in table.keys() { + let reflectable = &*(*obj as *const Reflector); + trace_reflector(tracer, "LIVE_REFERENCES", reflectable); + } + }); +} diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index bdcdc140028..5697c7eb7dc 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -13,7 +13,7 @@ use js::glue::JS_STRUCTURED_CLONE_VERSION; use js::jsapi::JSContext; use js::jsapi::{JS_WriteStructuredClone, JS_ClearPendingException}; use js::jsapi::JS_ReadStructuredClone; -use js::jsval::{JSVal, UndefinedValue}; +use js::jsapi::{HandleValue, MutableHandleValue}; use libc::size_t; use std::ptr; @@ -26,13 +26,14 @@ pub struct StructuredCloneData { impl StructuredCloneData { /// Writes a structured clone. Returns a `DataClone` error if that fails. - pub fn write(cx: *mut JSContext, message: JSVal) + pub fn write(cx: *mut JSContext, message: HandleValue) -> Fallible<StructuredCloneData> { let mut data = ptr::null_mut(); let mut nbytes = 0; let result = unsafe { JS_WriteStructuredClone(cx, message, &mut data, &mut nbytes, - ptr::null(), ptr::null_mut()) + ptr::null(), ptr::null_mut(), + HandleValue::undefined()) }; if result == 0 { unsafe { JS_ClearPendingException(cx); } @@ -47,15 +48,13 @@ impl StructuredCloneData { /// Reads a structured clone. /// /// Panics if `JS_ReadStructuredClone` fails. - pub fn read(self, global: GlobalRef) -> JSVal { - let mut message = UndefinedValue(); + pub fn read(self, global: GlobalRef, rval: MutableHandleValue) { unsafe { assert!(JS_ReadStructuredClone( - global.get_cx(), self.data as *const u64, self.nbytes, - JS_STRUCTURED_CLONE_VERSION, &mut message, + global.get_cx(), self.data, self.nbytes, + JS_STRUCTURED_CLONE_VERSION, rval, ptr::null(), ptr::null_mut()) != 0); } - message } } diff --git a/components/script/dom/bindings/trace.rs b/components/script/dom/bindings/trace.rs index 6a0f72412de..f401e278498 100644 --- a/components/script/dom/bindings/trace.rs +++ b/components/script/dom/bindings/trace.rs @@ -44,7 +44,8 @@ use euclid::size::Size2D; use html5ever::tree_builder::QuirksMode; use hyper::header::Headers; use hyper::method::Method; -use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSGCTraceKind}; +use js::jsapi::{JSObject, JSTracer, JSGCTraceKind, JS_CallValueTracer, JS_CallObjectTracer, GCTraceKindToAscii, Heap}; +use js::jsapi::JS_CallUnbarrieredObjectTracer; use js::jsval::JSVal; use js::rust::Runtime; use layout_interface::{LayoutRPC, LayoutChan}; @@ -59,7 +60,7 @@ use msg::compositor_msg::ScriptListener; use msg::constellation_msg::ConstellationChan; use net_traits::image::base::Image; use util::str::{LengthOrPercentageOrAuto}; -use std::cell::{Cell, RefCell}; +use std::cell::{Cell, UnsafeCell, RefCell}; use std::collections::{HashMap, HashSet}; use std::collections::hash_state::HashState; use std::ffi::CString; @@ -91,36 +92,46 @@ no_jsmanaged_fields!(EncodingRef); no_jsmanaged_fields!(Reflector); /// Trace a `JSVal`. -pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: JSVal) { - if !val.is_markable() { - return; - } - +pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: &Heap<JSVal>) { unsafe { + if !val.get().is_markable() { + return; + } + let name = CString::new(description).unwrap(); - (*tracer).debugPrinter = None; - (*tracer).debugPrintIndex = !0; - (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void; + (*tracer).debugPrinter_ = None; + (*tracer).debugPrintIndex_ = !0; + (*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void; debug!("tracing value {}", description); - JS_CallTracer(tracer, val.to_gcthing(), val.trace_kind()); + JS_CallValueTracer(tracer, val.ptr.get() as *mut _, + GCTraceKindToAscii(val.get().trace_kind())); } } /// Trace the `JSObject` held by `reflector`. #[allow(unrooted_must_root)] pub fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Reflector) { - trace_object(tracer, description, reflector.get_jsobject()) + unsafe { + let name = CString::new(description).unwrap(); + (*tracer).debugPrinter_ = None; + (*tracer).debugPrintIndex_ = !0; + (*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void; + debug!("tracing reflector {}", description); + JS_CallUnbarrieredObjectTracer(tracer, reflector.rootable(), + GCTraceKindToAscii(JSGCTraceKind::JSTRACE_OBJECT)); + } } /// Trace a `JSObject`. -pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: *mut JSObject) { +pub fn trace_object(tracer: *mut JSTracer, description: &str, obj: &Heap<*mut JSObject>) { unsafe { let name = CString::new(description).unwrap(); - (*tracer).debugPrinter = None; - (*tracer).debugPrintIndex = !0; - (*tracer).debugPrintArg = name.as_ptr() as *const libc::c_void; + (*tracer).debugPrinter_ = None; + (*tracer).debugPrintIndex_ = !0; + (*tracer).debugPrintArg_ = name.as_ptr() as *const libc::c_void; debug!("tracing {}", description); - JS_CallTracer(tracer, obj as *mut libc::c_void, JSGCTraceKind::JSTRACE_OBJECT); + JS_CallObjectTracer(tracer, obj.ptr.get() as *mut _, + GCTraceKindToAscii(JSGCTraceKind::JSTRACE_OBJECT)); } } @@ -168,15 +179,26 @@ impl<T: JSTraceable+Copy> JSTraceable for Cell<T> { } } -impl JSTraceable for *mut JSObject { +impl<T: JSTraceable> JSTraceable for UnsafeCell<T> { fn trace(&self, trc: *mut JSTracer) { - trace_object(trc, "object", *self); + unsafe { (*self.get()).trace(trc) } } } -impl JSTraceable for JSVal { + +impl JSTraceable for Heap<*mut JSObject> { fn trace(&self, trc: *mut JSTracer) { - trace_jsval(trc, "val", *self); + if self.get().is_null() { + return; + } + trace_object(trc, "object", self); + } +} + + +impl JSTraceable for Heap<JSVal> { + fn trace(&self, trc: *mut JSTracer) { + trace_jsval(trc, "val", self); } } @@ -324,115 +346,101 @@ impl JSTraceable for () { } } -/// Holds a set of vectors that need to be rooted -pub struct RootedCollectionSet { - set: Vec<HashSet<*const RootedVec<Void>>> +/// Homemade trait object for JSTraceable things +struct TraceableInfo { + pub ptr: *const libc::c_void, + pub trace: fn(obj: *const libc::c_void, tracer: *mut JSTracer) } -/// TLV Holds a set of vectors that need to be rooted -thread_local!(pub static ROOTED_COLLECTIONS: Rc<RefCell<RootedCollectionSet>> = - Rc::new(RefCell::new(RootedCollectionSet::new()))); - -/// Type of `RootedVec` -pub enum CollectionType { - /// DOM objects - DOMObjects, - /// `JSVal`s - JSVals, - /// `*mut JSObject`s - JSObjects, +/// Holds a set of JSTraceables that need to be rooted +pub struct RootedTraceableSet { + set: Vec<TraceableInfo> } +/// TLV Holds a set of JSTraceables that need to be rooted +thread_local!(pub static ROOTED_TRACEABLES: Rc<RefCell<RootedTraceableSet>> = + Rc::new(RefCell::new(RootedTraceableSet::new()))); -impl RootedCollectionSet { - fn new() -> RootedCollectionSet { - RootedCollectionSet { - set: vec!(HashSet::new(), HashSet::new(), HashSet::new()) +impl RootedTraceableSet { + fn new() -> RootedTraceableSet { + RootedTraceableSet { + set: vec!() } } - fn remove<T: VecRootableType>(collection: &RootedVec<T>) { - ROOTED_COLLECTIONS.with(|ref collections| { - let type_ = VecRootableType::tag(None::<T>); - let mut collections = collections.borrow_mut(); - assert!(collections.set[type_ as usize].remove(&(collection as *const _ as *const _))); + fn remove<T: JSTraceable>(traceable: &T) { + ROOTED_TRACEABLES.with(|ref traceables| { + let mut traceables = traceables.borrow_mut(); + let idx = + match traceables.set.iter() + .rposition(|x| x.ptr == traceable as *const T as *const _) { + Some(idx) => idx, + None => unreachable!(), + }; + traceables.set.remove(idx); }); } - fn add<T: VecRootableType>(collection: &RootedVec<T>) { - ROOTED_COLLECTIONS.with(|ref collections| { - let type_ = VecRootableType::tag(None::<T>); - let mut collections = collections.borrow_mut(); - collections.set[type_ as usize].insert(collection as *const _ as *const _); + fn add<T: JSTraceable>(traceable: &T) { + ROOTED_TRACEABLES.with(|ref traceables| { + fn trace<T: JSTraceable>(obj: *const libc::c_void, tracer: *mut JSTracer) { + let obj: &T = unsafe { &*(obj as *const T) }; + obj.trace(tracer); + } + + let mut traceables = traceables.borrow_mut(); + let info = TraceableInfo { + ptr: traceable as *const T as *const libc::c_void, + trace: trace::<T>, + }; + traceables.set.push(info); }) } unsafe fn trace(&self, tracer: *mut JSTracer) { - fn trace_collection_type<T>(tracer: *mut JSTracer, - collections: &HashSet<*const RootedVec<Void>>) - where T: JSTraceable + VecRootableType - { - for collection in collections { - let collection: *const RootedVec<Void> = *collection; - let collection = collection as *const RootedVec<T>; - unsafe { - let _ = (*collection).trace(tracer); - } - } + for info in self.set.iter() { + (info.trace)(info.ptr, tracer); } - - let dom_collections = - &self.set[CollectionType::DOMObjects as usize] as *const _ as *const HashSet<*const RootedVec<JS<Void>>>; - for dom_collection in (*dom_collections).iter() { - for reflector in (**dom_collection).iter() { - trace_reflector(tracer, "", reflector.reflector()); - } - } - - trace_collection_type::<JSVal>(tracer, &self.set[CollectionType::JSVals as usize]); - trace_collection_type::<*mut JSObject>(tracer, &self.set[CollectionType::JSObjects as usize]); } } - -/// Trait implemented by all types that can be used with RootedVec -pub trait VecRootableType { - /// Return the type tag used to determine how to trace RootedVec - fn tag(_a: Option<Self>) -> CollectionType; -} - -impl<T: Reflectable> VecRootableType for JS<T> { - fn tag(_a: Option<JS<T>>) -> CollectionType { CollectionType::DOMObjects } +/// Roots any JSTraceable thing +/// +/// If you have a valid Reflectable, use Root. +/// If you have GC things like *mut JSObject or JSVal, use jsapi::Rooted. +/// If you have an arbitrary number of Reflectables to root, use RootedVec<JS<T>> +/// If you know what you're doing, use this. +#[jstraceable] +pub struct RootedTraceable<'a, T: 'a + JSTraceable> { + ptr: &'a T } -impl VecRootableType for JSVal { - fn tag(_a: Option<JSVal>) -> CollectionType { CollectionType::JSVals } -} - -impl VecRootableType for *mut JSObject { - fn tag(_a: Option<*mut JSObject>) -> CollectionType { CollectionType::JSObjects } -} - -enum Void {} - -impl VecRootableType for Void { - fn tag(_a: Option<Void>) -> CollectionType { unreachable!() } +impl<'a, T: JSTraceable> RootedTraceable<'a, T> { + /// Root a JSTraceable thing for the life of this RootedTraceable + pub fn new(traceable: &'a T) -> RootedTraceable<'a, T> { + RootedTraceableSet::add(traceable); + RootedTraceable { ptr: traceable } + } } -impl Reflectable for Void { - fn reflector<'a>(&'a self) -> &'a Reflector { unreachable!() } +impl<'a, T: JSTraceable> Drop for RootedTraceable<'a, T> { + fn drop(&mut self) { + RootedTraceableSet::remove(self.ptr); + } } /// A vector of items that are rooted for the lifetime -/// of this struct +/// of this struct. +/// Must be a reflectable #[allow(unrooted_must_root)] #[no_move] -pub struct RootedVec<T: VecRootableType> { +#[jstraceable] +pub struct RootedVec<T: JSTraceable + Reflectable> { v: Vec<T> } -impl<T: VecRootableType> RootedVec<T> { +impl<T: JSTraceable + Reflectable> RootedVec<T> { /// Create a vector of items of type T that is rooted for /// the lifetime of this struct pub fn new() -> RootedVec<T> { @@ -444,39 +452,38 @@ impl<T: VecRootableType> RootedVec<T> { } /// Create a vector of items of type T. This constructor is specific - /// for RootCollection. + /// for RootTraceableSet. pub fn new_with_destination_address(addr: *const libc::c_void) -> RootedVec<T> { unsafe { - RootedCollectionSet::add::<T>(&*(addr as *const _)); + RootedTraceableSet::add::<RootedVec<T>>(&*(addr as *const _)); } RootedVec::<T> { v: vec!() } } } -impl<T: VecRootableType> Drop for RootedVec<T> { +impl<T: JSTraceable + Reflectable> Drop for RootedVec<T> { fn drop(&mut self) { - RootedCollectionSet::remove(self); + RootedTraceableSet::remove(self); } } -impl<T: VecRootableType> Deref for RootedVec<T> { +impl<T: JSTraceable + Reflectable> Deref for RootedVec<T> { type Target = Vec<T>; fn deref(&self) -> &Vec<T> { &self.v } } -impl<T: VecRootableType> DerefMut for RootedVec<T> { +impl<T: JSTraceable + Reflectable> DerefMut for RootedVec<T> { fn deref_mut(&mut self) -> &mut Vec<T> { &mut self.v } } - -/// SM Callback that traces the rooted collections -pub unsafe fn trace_collections(tracer: *mut JSTracer) { - ROOTED_COLLECTIONS.with(|ref collections| { - let collections = collections.borrow(); - collections.trace(tracer); +/// SM Callback that traces the rooted traceables +pub unsafe fn trace_traceables(tracer: *mut JSTracer) { + ROOTED_TRACEABLES.with(|ref traceables| { + let traceables = traceables.borrow(); + traceables.trace(tracer); }); } diff --git a/components/script/dom/bindings/utils.rs b/components/script/dom/bindings/utils.rs index bee42bbe2d3..fa7204f7dc8 100644 --- a/components/script/dom/bindings/utils.rs +++ b/components/script/dom/bindings/utils.rs @@ -6,10 +6,10 @@ use dom::bindings::codegen::PrototypeList; use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH; -use dom::bindings::conversions::{native_from_reflector_jsmanaged, is_dom_class}; +use dom::bindings::conversions::{native_from_handleobject, is_dom_class, jsstring_to_str}; use dom::bindings::error::{Error, ErrorResult, Fallible, throw_type_error}; use dom::bindings::global::GlobalRef; -use dom::bindings::js::{Temporary, Root, Rootable}; +use dom::bindings::js::Root; use dom::bindings::trace::trace_object; use dom::browsercontext; use dom::window; @@ -19,30 +19,39 @@ use util::str::DOMString; use libc; use libc::c_uint; use std::boxed; -use std::cell::Cell; use std::ffi::CString; use std::ptr; +use std::cmp::PartialEq; +use std::default::Default; +use std::cell::UnsafeCell; use js::glue::UnwrapObject; use js::glue::{IsWrapper, RUST_JSID_IS_INT, RUST_JSID_TO_INT}; -use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction}; +use js::jsapi::{JS_AlreadyHasOwnProperty, JS_NewFunction, JSTraceOp}; use js::jsapi::{JS_DefineProperties, JS_ForwardGetPropertyTo}; -use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype, JS_GetStringCharsAndLength}; -use js::jsapi::{JSHandleObject, JSTracer}; +use js::jsapi::{JS_GetClass, JS_LinkConstructorAndPrototype}; +use js::jsapi::{HandleObject, HandleId, HandleValue, MutableHandleValue}; use js::jsapi::JS_GetFunctionObject; use js::jsapi::{JS_HasPropertyById, JS_GetPrototype}; use js::jsapi::{JS_GetProperty, JS_HasProperty, JS_SetProperty}; -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::{JS_DefineFunctions, JS_DefineProperty, JS_DefineProperty1}; +use js::jsapi::{JS_GetReservedSlot, JS_SetReservedSlot}; +use js::jsapi::{JSContext, JSObject, JSClass, JSTracer}; use js::jsapi::{JSFunctionSpec, JSPropertySpec}; use js::jsapi::{JS_NewGlobalObject, JS_InitStandardClasses}; -use js::jsapi::JS_DeletePropertyById2; -use js::jsfriendapi::JS_ObjectToOuterObject; -use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType; +use js::jsapi::{OnNewGlobalHookOption, CompartmentOptions}; +use js::jsapi::{JS_FireOnNewGlobalObject, JSVersion}; +use js::jsapi::JS_DeletePropertyById1; +use js::jsapi::JS_ObjectToOuterObject; +use js::jsapi::JS_NewObjectWithUniqueType; +use js::jsapi::{ObjectOpResult, RootedObject, RootedValue, Heap, MutableHandleObject}; +use js::jsapi::PropertyDefinitionBehavior; +use js::jsapi::JSAutoCompartment; +use js::jsapi::{DOMCallbacks, JSWrapObjectCallbacks}; use js::jsval::JSVal; -use js::jsval::{PrivateValue, ObjectValue, NullValue}; -use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue, UndefinedValue}; -use js::rust::with_compartment; +use js::jsval::{PrivateValue, NullValue}; +use js::jsval::{Int32Value, UInt32Value, DoubleValue, BooleanValue}; +use js::rust::{GCMethods, ToString}; +use js::glue::{WrapperNew, GetCrossCompartmentWrapper}; use js::{JSPROP_ENUMERATE, JSPROP_READONLY, JSPROP_PERMANENT}; use js::JSFUN_CONSTRUCTOR; use js; @@ -145,7 +154,7 @@ unsafe impl Sync for DOMClass {} #[derive(Copy)] pub struct DOMJSClass { /// The actual JSClass. - pub base: js::Class, + pub base: js::jsapi::Class, /// Associated data for DOM object reflectors. pub dom_class: DOMClass } @@ -181,95 +190,93 @@ unsafe impl Sync for 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) -> u8; /// Creates the *interface prototype object* (if a `proto_class` is given) /// and the *interface object* (if a `constructor` is given). /// Fails on JSAPI failure. -pub fn do_create_interface_objects(cx: *mut JSContext, global: *mut JSObject, - receiver: *mut JSObject, - proto_proto: *mut JSObject, +pub fn do_create_interface_objects(cx: *mut JSContext, + receiver: HandleObject, + proto_proto: HandleObject, proto_class: Option<&'static JSClass>, constructor: Option<(NonNullJSNative, &'static str, u32)>, dom_class: *const DOMClass, - members: &'static NativeProperties) - -> *mut JSObject { + members: &'static NativeProperties, + rval: MutableHandleObject) { + if let Some(proto_class) = proto_class { + create_interface_prototype_object(cx, proto_proto, + proto_class, members, rval); + } + unsafe { - let proto = match proto_class { - Some(proto_class) => { - let proto = create_interface_prototype_object(cx, global, proto_proto, - proto_class, members); - JS_SetReservedSlot(proto, DOM_PROTO_INSTANCE_CLASS_SLOT, - PrivateValue(dom_class as *const libc::c_void)); - proto - }, - None => ptr::null_mut() - }; - - if let Some((native, name, nargs)) = constructor { - let s = CString::new(name).unwrap(); - create_interface_object(cx, global, receiver, - native, nargs, proto, - members, s.as_ptr()) + if !rval.get().is_null() { + JS_SetReservedSlot(rval.get(), DOM_PROTO_INSTANCE_CLASS_SLOT, + PrivateValue(dom_class as *const libc::c_void)); } + } - proto + if let Some((native, name, nargs)) = constructor { + let s = CString::new(name).unwrap(); + create_interface_object(cx, receiver, + native, nargs, rval.handle(), + members, s.as_ptr()) } } /// Creates the *interface object*. /// Fails on JSAPI failure. -fn create_interface_object(cx: *mut JSContext, global: *mut JSObject, - receiver: *mut JSObject, +fn create_interface_object(cx: *mut JSContext, + receiver: HandleObject, constructor_native: NonNullJSNative, - ctor_nargs: u32, proto: *mut JSObject, + ctor_nargs: u32, proto: HandleObject, members: &'static NativeProperties, name: *const libc::c_char) { unsafe { let fun = JS_NewFunction(cx, Some(constructor_native), ctor_nargs, - JSFUN_CONSTRUCTOR, global, name); + JSFUN_CONSTRUCTOR, name); assert!(!fun.is_null()); - let constructor = JS_GetFunctionObject(fun); - assert!(!constructor.is_null()); + let constructor = RootedObject::new(cx, JS_GetFunctionObject(fun)); + assert!(!constructor.ptr.is_null()); if let Some(static_methods) = members.static_methods { - define_methods(cx, constructor, static_methods); + define_methods(cx, constructor.handle(), static_methods); } if let Some(static_properties) = members.static_attrs { - define_properties(cx, constructor, static_properties); + define_properties(cx, constructor.handle(), static_properties); } if let Some(constants) = members.consts { - define_constants(cx, constructor, constants); + define_constants(cx, constructor.handle(), constants); } - if !proto.is_null() { - assert!(JS_LinkConstructorAndPrototype(cx, constructor, proto) != 0); + if !proto.get().is_null() { + assert!(JS_LinkConstructorAndPrototype(cx, constructor.handle(), proto) != 0); } let mut already_defined = 0; assert!(JS_AlreadyHasOwnProperty(cx, receiver, name, &mut already_defined) != 0); if already_defined == 0 { - assert!(JS_DefineProperty(cx, receiver, name, - ObjectValue(&*constructor), - None, None, 0) != 0); + assert!(JS_DefineProperty1(cx, receiver, name, + constructor.handle(), + 0, None, None) != 0); } } } /// Defines constants on `obj`. /// Fails on JSAPI failure. -fn define_constants(cx: *mut JSContext, obj: *mut JSObject, +fn define_constants(cx: *mut JSContext, obj: HandleObject, constants: &'static [ConstantSpec]) { for spec in constants.iter() { + let value = RootedValue::new(cx, spec.get_value()); unsafe { assert!(JS_DefineProperty(cx, obj, spec.name.as_ptr() as *const libc::c_char, - spec.get_value(), None, None, + value.handle(), JSPROP_ENUMERATE | JSPROP_READONLY | - JSPROP_PERMANENT) != 0); + JSPROP_PERMANENT, None, None) != 0); } } } @@ -277,17 +284,17 @@ fn define_constants(cx: *mut JSContext, obj: *mut JSObject, /// Defines methods on `obj`. The last entry of `methods` must contain zeroed /// memory. /// Fails on JSAPI failure. -fn define_methods(cx: *mut JSContext, obj: *mut JSObject, +fn define_methods(cx: *mut JSContext, obj: HandleObject, methods: &'static [JSFunctionSpec]) { unsafe { - assert!(JS_DefineFunctions(cx, obj, methods.as_ptr()) != 0); + assert!(JS_DefineFunctions(cx, obj, methods.as_ptr(), PropertyDefinitionBehavior::DefineAllProperties) != 0); } } /// Defines attributes on `obj`. The last entry of `properties` must contain /// zeroed memory. /// Fails on JSAPI failure. -fn define_properties(cx: *mut JSContext, obj: *mut JSObject, +fn define_properties(cx: *mut JSContext, obj: HandleObject, properties: &'static [JSPropertySpec]) { unsafe { assert!(JS_DefineProperties(cx, obj, properties.as_ptr()) != 0); @@ -296,36 +303,32 @@ fn define_properties(cx: *mut JSContext, obj: *mut JSObject, /// Creates the *interface prototype object*. /// Fails on JSAPI failure. -fn create_interface_prototype_object(cx: *mut JSContext, global: *mut JSObject, - parent_proto: *mut JSObject, +fn create_interface_prototype_object(cx: *mut JSContext, global: HandleObject, proto_class: &'static JSClass, - members: &'static NativeProperties) - -> *mut JSObject { + members: &'static NativeProperties, + rval: MutableHandleObject) { unsafe { - let our_proto = JS_NewObjectWithUniqueType(cx, proto_class, - &*parent_proto, &*global); - assert!(!our_proto.is_null()); + rval.set(JS_NewObjectWithUniqueType(cx, proto_class, global)); + assert!(!rval.get().is_null()); if let Some(methods) = members.methods { - define_methods(cx, our_proto, methods); + define_methods(cx, rval.handle(), methods); } if let Some(properties) = members.attrs { - define_properties(cx, our_proto, properties); + define_properties(cx, rval.handle(), properties); } if let Some(constants) = members.consts { - define_constants(cx, our_proto, constants); + define_constants(cx, rval.handle(), constants); } - - return our_proto; } } /// A throwing constructor, for those interfaces that have neither /// `NoInterfaceObject` nor `Constructor`. pub unsafe extern fn throwing_constructor(cx: *mut JSContext, _argc: c_uint, - _vp: *mut JSVal) -> JSBool { + _vp: *mut JSVal) -> u8 { throw_type_error(cx, "Illegal constructor."); return 0; } @@ -351,6 +354,10 @@ pub fn initialize_global(global: *mut JSObject) { pub trait Reflectable { /// Returns the receiver's reflector. fn reflector<'a>(&'a self) -> &'a Reflector; + /// Initializes the Reflector + fn init_reflector(&mut self, _obj: *mut JSObject) { + panic!("Cannot call init on this Reflectable"); + } } /// Create the reflector for a new DOM object and yield ownership to the @@ -358,47 +365,56 @@ pub trait Reflectable { pub fn reflect_dom_object<T: Reflectable> (obj: Box<T>, global: GlobalRef, - wrap_fn: extern "Rust" fn(*mut JSContext, GlobalRef, Box<T>) -> Temporary<T>) - -> Temporary<T> { + wrap_fn: extern "Rust" fn(*mut JSContext, GlobalRef, Box<T>) -> Root<T>) + -> Root<T> { wrap_fn(global.get_cx(), global, obj) } /// A struct to store a reference to the reflector of a DOM object. // Allowing unused_attribute because the lint sometimes doesn't run in order #[allow(raw_pointer_derive, unrooted_must_root, unused_attributes)] -#[derive(PartialEq)] #[must_root] #[servo_lang = "reflector"] // If you're renaming or moving this field, update the path in plugins::reflector as well pub struct Reflector { - object: Cell<*mut JSObject>, + object: UnsafeCell<*mut JSObject>, +} + +#[allow(unrooted_must_root)] +impl PartialEq for Reflector { + fn eq(&self, other: &Reflector) -> bool { + unsafe { *self.object.get() == *other.object.get() } + } } impl Reflector { /// Get the reflector. #[inline] - pub fn get_jsobject(&self) -> *mut JSObject { - self.object.get() + pub fn get_jsobject(&self) -> HandleObject { + HandleObject { ptr: self.object.get() } } /// Initialize the reflector. (May be called only once.) - pub fn set_jsobject(&self, object: *mut JSObject) { - assert!(self.object.get().is_null()); - assert!(!object.is_null()); - self.object.set(object); + pub fn set_jsobject(&mut self, object: *mut JSObject) { + unsafe { + let obj = self.object.get(); + assert!((*obj).is_null()); + assert!(!object.is_null()); + *obj = object; + } } /// Return a pointer to the memory location at which the JS reflector - /// object is stored. Used by Temporary values to root the reflector, as + /// object is stored. Used to root the reflector, as /// required by the JSAPI rooting APIs. - pub unsafe fn rootable(&self) -> *mut *mut JSObject { - self.object.as_unsafe_cell().get() + pub fn rootable(&self) -> *mut *mut JSObject { + self.object.get() } /// Create an uninitialized `Reflector`. pub fn new() -> Reflector { Reflector { - object: Cell::new(ptr::null_mut()), + object: UnsafeCell::new(ptr::null_mut()) } } } @@ -407,33 +423,34 @@ impl Reflector { /// set to true and `*vp` to the value, otherwise `*found` is set to false. /// /// Returns false on JSAPI failure. -pub fn get_property_on_prototype(cx: *mut JSContext, proxy: *mut JSObject, - id: jsid, found: *mut bool, vp: *mut JSVal) +pub fn get_property_on_prototype(cx: *mut JSContext, proxy: HandleObject, + id: HandleId, found: *mut bool, vp: MutableHandleValue) -> bool { unsafe { //let proto = GetObjectProto(proxy); - let proto = JS_GetPrototype(proxy); - if proto.is_null() { + let mut proto = RootedObject::new(cx, ptr::null_mut()); + if JS_GetPrototype(cx, proxy, proto.handle_mut()) == 0 || + proto.ptr.is_null() { *found = false; return true; } let mut has_property = 0; - if JS_HasPropertyById(cx, proto, id, &mut has_property) == 0 { + if JS_HasPropertyById(cx, proto.handle(), id, &mut has_property) == 0 { return false; } *found = has_property != 0; - let no_output = vp.is_null(); + let no_output = vp.ptr.is_null(); if has_property == 0 || no_output { return true; } - JS_ForwardGetPropertyTo(cx, proto, id, proxy, vp) != 0 + JS_ForwardGetPropertyTo(cx, proto.handle(), id, proxy, vp) != 0 } } /// Get an array index from the given `jsid`. Returns `None` if the given /// `jsid` is not an integer. -pub fn get_array_index_from_id(_cx: *mut JSContext, id: jsid) -> Option<u32> { +pub fn get_array_index_from_id(_cx: *mut JSContext, id: HandleId) -> Option<u32> { unsafe { if RUST_JSID_IS_INT(id) != 0 { return Some(RUST_JSID_TO_INT(id) as u32); @@ -460,28 +477,16 @@ pub fn get_array_index_from_id(_cx: *mut JSContext, id: jsid) -> Option<u32> { /// Returns `Err(())` on JSAPI failure (there is a pending exception), and /// `Ok(None)` if there was no matching string. pub fn find_enum_string_index(cx: *mut JSContext, - v: JSVal, + v: HandleValue, values: &[&'static str]) -> Result<Option<usize>, ()> { - unsafe { - let jsstr = JS_ValueToString(cx, v); - if jsstr.is_null() { - return Err(()); - } - - let mut length = 0; - let chars = JS_GetStringCharsAndLength(cx, jsstr, &mut length); - if chars.is_null() { - return Err(()); - } - - Ok(values.iter().position(|value| { - value.len() == length as usize && - (0..length as usize).all(|j| { - value.as_bytes()[j] as u16 == *chars.offset(j as isize) - }) - })) + let jsstr = ToString(cx, v); + if jsstr.is_null() { + return Err(()); } + + let search = jsstring_to_str(cx, jsstr); + Ok(values.iter().position(|value| value == &search)) } /// Returns wether `obj` is a platform object @@ -495,7 +500,7 @@ pub fn is_platform_object(obj: *mut JSObject) -> bool { } // Now for simplicity check for security wrappers before anything else if IsWrapper(obj) == 1 { - let unwrapped_obj = UnwrapObject(obj, /* stopAtOuter = */ 0, ptr::null_mut()); + let unwrapped_obj = UnwrapObject(obj, /* stopAtOuter = */ 0); if unwrapped_obj.is_null() { return false; } @@ -508,53 +513,54 @@ pub fn is_platform_object(obj: *mut JSObject) -> bool { /// Get the property with name `property` from `object`. /// Returns `Err(())` on JSAPI failure (there is a pending exception), and -/// `Ok(None)` if there was no property with the given name. +/// `Ok(false)` if there was no property with the given name. pub fn get_dictionary_property(cx: *mut JSContext, - object: *mut JSObject, - property: &str) -> Result<Option<JSVal>, ()> { - fn has_property(cx: *mut JSContext, object: *mut JSObject, property: &CString, - found: &mut JSBool) -> bool { + object: HandleObject, + property: &str, + rval: MutableHandleValue) + -> Result<bool, ()> { + fn has_property(cx: *mut JSContext, object: HandleObject, property: &CString, + found: &mut u8) -> bool { unsafe { JS_HasProperty(cx, object, property.as_ptr(), found) != 0 } } - fn get_property(cx: *mut JSContext, object: *mut JSObject, property: &CString, - value: &mut JSVal) -> bool { + fn get_property(cx: *mut JSContext, object: HandleObject, property: &CString, + value: MutableHandleValue) -> bool { unsafe { JS_GetProperty(cx, object, property.as_ptr(), value) != 0 } } let property = CString::new(property).unwrap(); - if object.is_null() { - return Ok(None); + if object.get().is_null() { + return Ok(false); } - let mut found: JSBool = 0; + let mut found: u8 = 0; if !has_property(cx, object, &property, &mut found) { return Err(()); } if found == 0 { - return Ok(None); + return Ok(false); } - let mut value = NullValue(); - if !get_property(cx, object, &property, &mut value) { + if !get_property(cx, object, &property, rval) { return Err(()); } - Ok(Some(value)) + Ok(true) } /// Set the property with name `property` from `object`. /// Returns `Err(())` on JSAPI failure, or null object, /// and Ok(()) otherwise pub fn set_dictionary_property(cx: *mut JSContext, - object: *mut JSObject, + object: HandleObject, property: &str, - value: &mut JSVal) -> Result<(), ()> { - if object.is_null() { + value: HandleValue) -> Result<(), ()> { + if object.get().is_null() { return Err(()); } @@ -569,79 +575,99 @@ pub fn set_dictionary_property(cx: *mut JSContext, } /// Returns whether `proxy` has a property `id` on its prototype. -pub fn has_property_on_prototype(cx: *mut JSContext, proxy: *mut JSObject, - id: jsid) -> bool { +pub fn has_property_on_prototype(cx: *mut JSContext, proxy: HandleObject, + id: HandleId) -> bool { // MOZ_ASSERT(js::IsProxy(proxy) && js::GetProxyHandler(proxy) == handler); let mut found = false; - return !get_property_on_prototype(cx, proxy, id, &mut found, ptr::null_mut()) || found; + return !get_property_on_prototype(cx, proxy, id, &mut found, + MutableHandleValue { ptr: ptr::null_mut() }) || found; } /// Create a DOM global object with the given class. -pub fn create_dom_global(cx: *mut JSContext, class: *const JSClass) +pub fn create_dom_global(cx: *mut JSContext, class: *const JSClass, + trace: JSTraceOp) -> *mut JSObject { unsafe { - let obj = JS_NewGlobalObject(cx, class, ptr::null_mut()); - if obj.is_null() { + let mut options = CompartmentOptions::default(); + options.version_ = JSVersion::JSVERSION_LATEST; + options.traceGlobal_ = trace; + + let obj = + RootedObject::new(cx, + JS_NewGlobalObject(cx, class, ptr::null_mut(), + OnNewGlobalHookOption::DontFireOnNewGlobalHook, &options)); + if obj.ptr.is_null() { return ptr::null_mut(); } - with_compartment(cx, obj, || { - JS_InitStandardClasses(cx, obj); - }); - initialize_global(obj); - obj + let _ac = JSAutoCompartment::new(cx, obj.ptr); + JS_InitStandardClasses(cx, obj.handle()); + initialize_global(obj.ptr); + JS_FireOnNewGlobalObject(cx, obj.handle()); + obj.ptr } } /// Drop the resources held by reserved slots of a global object pub unsafe fn finalize_global(obj: *mut JSObject) { + let protolist = get_proto_or_iface_array(obj); + let list = (*protolist).as_mut_ptr(); + for idx in 0..(PrototypeList::ID::Count as isize) { + let entry = list.offset(idx); + let value = *entry; + if <*mut JSObject>::needs_post_barrier(value) { + <*mut JSObject>::relocate(entry); + } + } let _: Box<ProtoOrIfaceArray> = - Box::from_raw(get_proto_or_iface_array(obj)); + Box::from_raw(protolist); } /// Trace the resources held by reserved slots of a global object pub unsafe fn trace_global(tracer: *mut JSTracer, obj: *mut JSObject) { let array = get_proto_or_iface_array(obj); - for &proto in (*array).iter() { + for proto in (&*array).iter() { if !proto.is_null() { - trace_object(tracer, "prototype", proto); + trace_object(tracer, "prototype", &*(proto as *const *mut JSObject as *const Heap<*mut JSObject>)); } } } -/// 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) +unsafe extern fn wrap(cx: *mut JSContext, + existing: HandleObject, + obj: HandleObject) + -> *mut JSObject { + // FIXME terrible idea. need security wrappers + // https://github.com/servo/servo/issues/2382 + WrapperNew(cx, obj, GetCrossCompartmentWrapper()) } -/// Callback to outerize windows before wrapping. -pub unsafe extern fn pre_wrap(cx: *mut JSContext, _scope: *mut JSObject, - obj: *mut JSObject, _flags: c_uint) -> *mut JSObject { +unsafe extern fn pre_wrap(cx: *mut JSContext, _existing: HandleObject, + obj: HandleObject, _object_passed_to_wrap: HandleObject) + -> *mut JSObject { + let _ac = JSAutoCompartment::new(cx, obj.get()); JS_ObjectToOuterObject(cx, obj) } +/// Callback table for use with JS_SetWrapObjectCallbacks +pub static WRAP_CALLBACKS: JSWrapObjectCallbacks = JSWrapObjectCallbacks { + wrap: Some(wrap), + preWrap: Some(pre_wrap), +}; + /// 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<window::Window> = native_from_reflector_jsmanaged(obj).unwrap().root(); - // FIXME(https://github.com/rust-lang/rust/issues/23338) - let win = win.r(); - let context = win.browser_context(); - context.as_ref().unwrap().window_proxy() - } +pub unsafe extern fn outerize_global(_cx: *mut JSContext, obj: HandleObject) -> *mut JSObject { + debug!("outerizing"); + let win: Root<window::Window> = native_from_handleobject(obj).unwrap(); + // FIXME(https://github.com/rust-lang/rust/issues/23338) + let win = win.r(); + let context = win.browser_context(); + context.as_ref().unwrap().window_proxy() } /// Deletes the property `id` from `object`. -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 { - return false; - } - - *bp = value.to_boolean(); - return true; +pub unsafe fn delete_property_by_id(cx: *mut JSContext, object: HandleObject, + id: HandleId, bp: *mut ObjectOpResult) -> u8 { + JS_DeletePropertyById1(cx, object, id, bp) } /// Validate a qualified name. See https://dom.spec.whatwg.org/#validate for details. @@ -659,6 +685,18 @@ pub fn validate_qualified_name(qualified_name: &str) -> ErrorResult { } } +unsafe extern "C" fn instance_class_has_proto_at_depth(clasp: *const js::jsapi::Class, + proto_id: u32, + depth: u32) -> u8 { + let domclass: *const DOMJSClass = clasp as *const _; + let domclass = &*domclass; + (domclass.dom_class.interface_chain[depth as usize] as u32 == proto_id) as u8 +} + +pub const DOM_CALLBACKS: DOMCallbacks = DOMCallbacks { + instanceClassMatchesProto: Some(instance_class_has_proto_at_depth), +}; + /// Validate a namespace and qualified name and extract their parts. /// See https://dom.spec.whatwg.org/#validate-and-extract for details. pub fn validate_and_extract(namespace: Option<DOMString>, qualified_name: &str) |