aboutsummaryrefslogtreecommitdiffstats
path: root/src/components/script/dom/bindings
diff options
context:
space:
mode:
Diffstat (limited to 'src/components/script/dom/bindings')
-rw-r--r--src/components/script/dom/bindings/codegen/Bindings.conf107
-rw-r--r--src/components/script/dom/bindings/codegen/CodegenRust.py239
-rw-r--r--src/components/script/dom/bindings/codegen/Configuration.py13
-rw-r--r--src/components/script/dom/bindings/conversions.rs20
-rw-r--r--src/components/script/dom/bindings/js.rs465
-rw-r--r--src/components/script/dom/bindings/trace.rs24
-rw-r--r--src/components/script/dom/bindings/utils.rs50
7 files changed, 660 insertions, 258 deletions
diff --git a/src/components/script/dom/bindings/codegen/Bindings.conf b/src/components/script/dom/bindings/codegen/Bindings.conf
index 053fabae1b2..c41856ccf86 100644
--- a/src/components/script/dom/bindings/codegen/Bindings.conf
+++ b/src/components/script/dom/bindings/codegen/Bindings.conf
@@ -9,8 +9,6 @@
# The configuration table maps each interface name to a |descriptor|.
#
# Valid fields for all descriptors:
-# * needsAbstract: a list of members that require a JS<>-wrapped version of
-# self to be passed to the native code.
# * createGlobal: True for global objects.
# * outerObjectHook: string to use in place of default value for outerObject and thisObject
# JS class hooks
@@ -26,123 +24,44 @@ DOMInterfaces = {
'ClientRect': {},
'ClientRectList': {},
'Console': {},
-'Document': {
- 'needsAbstract': [
- 'adoptNode',
- 'anchors',
- 'applets',
- 'body',
- 'children',
- 'createComment',
- 'createDocumentFragment',
- 'createElement',
- 'createElementNS',
- 'createProcessingInstruction',
- 'createTextNode',
- 'embeds',
- 'forms',
- 'getElementsByClassName',
- 'getElementsByTagName',
- 'getElementsByTagNameNS',
- 'images',
- 'importNode',
- 'links',
- 'location',
- 'plugins',
- 'scripts',
- 'title',
- ],
-},
+'Document': {},
'DOMException': {},
'DOMImplementation': {},
'DOMParser': {},
-'Element': {
- 'needsAbstract': [
- 'attributes',
- 'children',
- 'className',
- 'getAttribute',
- 'getAttributeNS',
- 'getBoundingClientRect',
- 'getClientRects',
- 'getElementsByClassName',
- 'getElementsByTagName',
- 'getElementsByTagNameNS',
- 'hasAttribute',
- 'hasAttributeNS',
- 'id',
- 'innerHTML',
- 'outerHTML',
- 'removeAttribute',
- 'removeAttributeNS',
- 'setAttribute',
- 'setAttributeNS',
- ]
-},
+'Element': {},
'Event': {},
'EventListener': {
'nativeType': 'EventListenerBinding::EventListener',
},
-'EventTarget': {
- 'needsAbstract': ['dispatchEvent']
-},
+'EventTarget': {},
'FormData': {},
'HTMLCollection': {},
'Location': {},
'MouseEvent': {},
'Navigator': {},
-'Node': {
- 'needsAbstract': [
- 'appendChild',
- 'childNodes',
- 'cloneNode',
- 'compareDocumentPosition',
- 'contains',
- 'insertBefore',
- 'isEqualNode',
- 'namespaceURI',
- 'nodeName',
- 'nodeValue',
- 'normalize',
- 'removeChild',
- 'replaceChild',
- 'textContent',
- ]
-},
-
+'Node': {},
'NodeList': {},
-
'UIEvent': {},
'ValidityState': {},
'Window': {
'createGlobal': True,
'outerObjectHook': 'Some(bindings::utils::outerize_global)',
- 'needsAbstract': [
- 'console',
- 'location',
- 'navigator',
- 'self',
- 'window',
- ],
},
'XMLHttpRequest': {},
'XMLHttpRequestEventTarget': {},
'XMLHttpRequestUpload': {},
+#FIXME(jdm): This should be 'register': False, but then we don't generate enum types
'TestBinding': {},
}
# FIXME: This should be renamed: https://github.com/mozilla/servo/issues/1625
-def addHTMLElement(element, concrete=None, needsAbstract=[]):
- DOMInterfaces[element] = {
- 'nativeType': 'JS<%s>' % element,
- 'concreteType': concrete if concrete else element,
- 'needsAbstract': needsAbstract
- }
+def addHTMLElement(element):
+ DOMInterfaces[element] = {}
addHTMLElement('Comment')
-addHTMLElement('DocumentFragment', concrete='DocumentFragment', needsAbstract=['children'])
+addHTMLElement('DocumentFragment')
addHTMLElement('DocumentType')
addHTMLElement('Text')
addHTMLElement('ProcessingInstruction')
@@ -158,12 +77,12 @@ addHTMLElement('HTMLBRElement')
addHTMLElement('HTMLCanvasElement')
addHTMLElement('HTMLDataElement')
addHTMLElement('HTMLDivElement')
-addHTMLElement('HTMLDataListElement', needsAbstract=['options'])
+addHTMLElement('HTMLDataListElement')
addHTMLElement('HTMLDirectoryElement')
addHTMLElement('HTMLDListElement')
addHTMLElement('HTMLElement')
addHTMLElement('HTMLEmbedElement')
-addHTMLElement('HTMLFieldSetElement', needsAbstract=['elements'])
+addHTMLElement('HTMLFieldSetElement')
addHTMLElement('HTMLFontElement')
addHTMLElement('HTMLFormElement')
addHTMLElement('HTMLFrameElement')
@@ -172,8 +91,8 @@ addHTMLElement('HTMLHeadElement')
addHTMLElement('HTMLHeadingElement')
addHTMLElement('HTMLHtmlElement')
addHTMLElement('HTMLHRElement')
-addHTMLElement('HTMLIFrameElement', needsAbstract=['sandbox'])
-addHTMLElement('HTMLImageElement', needsAbstract=['alt', 'src', 'useMap', 'isMap', 'width', 'height', 'name', 'align', 'hspace', 'vspace', 'longDesc', 'border'])
+addHTMLElement('HTMLIFrameElement')
+addHTMLElement('HTMLImageElement')
addHTMLElement('HTMLInputElement')
addHTMLElement('HTMLLabelElement')
addHTMLElement('HTMLLegendElement')
@@ -195,7 +114,7 @@ addHTMLElement('HTMLParamElement')
addHTMLElement('HTMLPreElement')
addHTMLElement('HTMLProgressElement')
addHTMLElement('HTMLQuoteElement')
-addHTMLElement('HTMLScriptElement', needsAbstract=['src'])
+addHTMLElement('HTMLScriptElement')
addHTMLElement('HTMLSelectElement')
addHTMLElement('HTMLSourceElement')
addHTMLElement('HTMLSpanElement')
diff --git a/src/components/script/dom/bindings/codegen/CodegenRust.py b/src/components/script/dom/bindings/codegen/CodegenRust.py
index cb3fc862c64..55bfa9eac91 100644
--- a/src/components/script/dom/bindings/codegen/CodegenRust.py
+++ b/src/components/script/dom/bindings/codegen/CodegenRust.py
@@ -394,6 +394,9 @@ def typeIsSequenceOrHasSequenceMember(type):
type.flatMemberTypes)
return False
+def typeNeedsRooting(type, descriptorProvider):
+ return type.isGeckoInterface() and descriptorProvider.getDescriptor(type.name).needsRooting
+
def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
isDefinitelyObject=False,
isMember=False,
@@ -481,6 +484,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
if exceptionCode is None:
exceptionCode = "return 0;"
+ needsRooting = typeNeedsRooting(type, descriptorProvider)
+
def handleOptional(template, declType, isOptional):
if isOptional:
template = "Some(%s)" % template
@@ -489,7 +494,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
else:
initialValue = None
- return (template, declType, isOptional, initialValue)
+ return (template, declType, isOptional, initialValue, needsRooting)
# Unfortunately, .capitalize() on a string will lowercase things inside the
# string, which we do not want.
@@ -551,7 +556,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
templateBody += (
"} else {\n" +
CGIndenter(onFailureNotAnObject(failureCode)).define() +
- "}")
+ "}\n")
if type.nullable():
templateBody = handleDefaultNull(templateBody, "None")
else:
@@ -601,6 +606,8 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
failureCode)
return handleOptional(template, declType, isOptional)
+ descriptorType = descriptor.memberType if isMember else descriptor.nativeType
+
templateBody = ""
if descriptor.interface.isConsequential():
raise TypeError("Consequential interface %s being used as an "
@@ -616,11 +623,14 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
descriptor,
"(${val}).to_object()"))
- declType = CGGeneric(descriptor.nativeType)
+ declType = CGGeneric(descriptorType)
if type.nullable():
templateBody = "Some(%s)" % templateBody
declType = CGWrapper(declType, pre="Option<", post=">")
+ if isMember:
+ templateBody += ".root()"
+
templateBody = wrapObjectTemplate(templateBody, isDefinitelyObject,
type, failureCode)
@@ -745,7 +755,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
"} else {\n"
" ${declName} = NULL;\n"
"}" % haveCallable,
- CGGeneric("JSObject*"), isOptional, None)
+ CGGeneric("JSObject*"), isOptional, None, needsRooting)
if type.isAny():
assert not isEnforceRange and not isClamp
@@ -789,7 +799,7 @@ def getJSToNativeConversionTemplate(type, descriptorProvider, failureCode=None,
assert not isOptional
# This one only happens for return values, and its easy: Just
# ignore the jsval.
- return ("", None, False, None)
+ return ("", None, False, None, False)
if not type.isPrimitive():
raise TypeError("Need conversion for argument type '%s'" % str(type))
@@ -842,7 +852,7 @@ def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
replace ${argc} and ${index}, where ${index} is the index of this
argument (0-based) and ${argc} is the total number of arguments.
"""
- (templateBody, declType, dealWithOptional, initialValue) = templateTuple
+ (templateBody, declType, dealWithOptional, initialValue, needsRooting) = templateTuple
if dealWithOptional and argcAndIndex is None:
raise TypeError("Have to deal with optional things, but don't know how")
@@ -888,6 +898,12 @@ def instantiateJSToNativeConversionTemplate(templateTuple, replacements,
# Add an empty CGGeneric to get an extra newline after the argument
# conversion.
result.append(CGGeneric(""))
+
+ if needsRooting:
+ rootBody = "let ${declName} = ${declName}.root();"
+ result.append(CGGeneric(string.Template(rootBody).substitute(replacements)))
+ result.append(CGGeneric(""))
+
return result;
def convertConstIDLValueToJSVal(value):
@@ -985,6 +1001,13 @@ def typeNeedsCx(type, retVal=False):
return True
return type.isCallback() or type.isAny() or type.isObject()
+def typeRetValNeedsRooting(type):
+ if type is None:
+ return False
+ if type.nullable():
+ type = type.inner
+ return type.isGeckoInterface() and not type.isCallback()
+
def memberIsCreator(member):
return member.getExtendedAttribute("Creator") is not None
@@ -1016,7 +1039,7 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
if returnType.isGeckoInterface():
descriptor = descriptorProvider.getDescriptor(
returnType.unroll().inner.identifier.name)
- result = CGGeneric(descriptor.nativeType)
+ result = CGGeneric(descriptor.returnType)
if returnType.nullable():
result = CGWrapper(result, pre="Option<", post=">")
return result
@@ -1365,7 +1388,7 @@ class CGImports(CGWrapper):
"""
Generates the appropriate import/use statements.
"""
- def __init__(self, child, imports):
+ def __init__(self, child, descriptors, imports):
"""
Adds a set of imports.
"""
@@ -1385,6 +1408,10 @@ class CGImports(CGWrapper):
'dead_code',
]
+ for d in descriptors:
+ name = d.interface.identifier.name
+ imports.append('dom::%s::%sMethods' % (name.lower(), name))
+
statements = ['#![allow(%s)]' % ','.join(ignored_warnings)]
statements.extend('use %s;' % i for i in sorted(imports))
@@ -1763,7 +1790,7 @@ class CGAbstractMethod(CGThing):
def _returnType(self):
return (" -> %s" % self.returnType) if self.returnType != "void" else ""
def _unsafe_open(self):
- return "\n unsafe {" if self.unsafe else ""
+ return "\n unsafe {\n" if self.unsafe else ""
def _unsafe_close(self):
return "\n }\n" if self.unsafe else ""
@@ -1783,7 +1810,7 @@ def CreateBindingJSObject(descriptor, parent=None):
if descriptor.proxy:
assert not descriptor.createGlobal
handler = """
- let js_info = aScope.get().page().js_info();
+ let js_info = aScope.deref().page().js_info();
let handler = js_info.get_ref().dom_static.proxy_handlers.deref().get(&(PrototypeList::id::%s as uint));
""" % descriptor.name
create += handler + """ let obj = NewProxyObject(aCx, *handler,
@@ -1809,7 +1836,7 @@ class CGWrapMethod(CGAbstractMethod):
def __init__(self, descriptor):
assert descriptor.interface.hasInterfacePrototypeObject()
if not descriptor.createGlobal:
- args = [Argument('*JSContext', 'aCx'), Argument('&JS<Window>', 'aScope'),
+ args = [Argument('*JSContext', 'aCx'), Argument('&JSRef<Window>', 'aScope'),
Argument("~" + descriptor.concreteType, 'aObject', mutable=True)]
else:
args = [Argument('*JSContext', 'aCx'),
@@ -2210,6 +2237,9 @@ class CGCallGenerator(CGThing):
self.cgRoot.append(CGGeneric("}"))
self.cgRoot.append(CGGeneric("result = result_fallible.unwrap();"))
+ if typeRetValNeedsRooting(returnType):
+ self.cgRoot.append(CGGeneric("let result = result.root();"))
+
def define(self):
return self.cgRoot.define()
@@ -2277,7 +2307,12 @@ class CGPerSignatureCall(CGThing):
def getArgc(self):
return "argc"
def getArguments(self):
- return [(a, "arg" + str(i)) for (i, a) in enumerate(self.arguments)]
+ def process(arg, i):
+ argVal = "arg" + str(i)
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ return [(a, process(a, i)) for (i, a) in enumerate(self.arguments)]
def isFallible(self):
return not 'infallible' in self.extendedAttributes
@@ -2447,17 +2482,10 @@ class CGSpecializedMethod(CGAbstractExternMethod):
def definition_body(self):
name = self.method.identifier.name
- nativeName = MakeNativeName(name)
- extraPre = ''
- argsPre = []
- if name in self.descriptor.needsAbstract:
- abstractName = re.sub(r'<\w+>', '', self.descriptor.nativeType)
- extraPre = ' let mut abstract_this = %s::from_raw(this);\n' % abstractName
- argsPre = ['&mut abstract_this']
- return CGWrapper(CGMethodCall(argsPre, nativeName, self.method.isStatic(),
+ return CGWrapper(CGMethodCall([], MakeNativeName(name), self.method.isStatic(),
self.descriptor, self.method),
- pre=extraPre +
- " let this = &mut *this;\n").define()
+ pre=" let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n").define()
class CGGenericGetter(CGAbstractBindingMethod):
"""
@@ -2480,10 +2508,8 @@ class CGGenericGetter(CGAbstractBindingMethod):
def generate_code(self):
return CGIndenter(CGGeneric(
- "return with_gc_disabled(cx, || {\n"
- " let info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, &*vp));\n"
- " CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *libc::c_void, &*vp)\n"
- "});\n"))
+ "let info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, &*vp));\n"
+ "return CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *libc::c_void, &*vp);\n"))
class CGSpecializedGetter(CGAbstractExternMethod):
"""
@@ -2502,21 +2528,15 @@ class CGSpecializedGetter(CGAbstractExternMethod):
def definition_body(self):
name = self.attr.identifier.name
nativeName = MakeNativeName(name)
- extraPre = ''
- argsPre = []
infallible = ('infallible' in
self.descriptor.getExtendedAttributes(self.attr,
getter=True))
- if name in self.descriptor.needsAbstract:
- abstractName = re.sub(r'<\w+>', '', self.descriptor.nativeType)
- extraPre = ' let mut abstract_this = %s::from_raw(this);\n' % abstractName
- argsPre = ['&mut abstract_this']
if self.attr.type.nullable() or not infallible:
nativeName = "Get" + nativeName
- return CGWrapper(CGIndenter(CGGetterCall(argsPre, self.attr.type, nativeName,
+ return CGWrapper(CGIndenter(CGGetterCall([], self.attr.type, nativeName,
self.descriptor, self.attr)),
- pre=extraPre +
- " let this = &mut *this;\n").define()
+ pre=" let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n").define()
class CGGenericSetter(CGAbstractBindingMethod):
"""
@@ -2541,10 +2561,7 @@ class CGGenericSetter(CGAbstractBindingMethod):
"let undef = UndefinedValue();\n"
"let argv: *JSVal = if argc != 0 { JS_ARGV(cx, vp as *JSVal) } else { &undef as *JSVal };\n"
"let info: *JSJitInfo = RUST_FUNCTION_VALUE_TO_JITINFO(JS_CALLEE(cx, vp as *JSVal));\n"
- "let ok = with_gc_disabled(cx, || {\n"
- " CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *libc::c_void, argv)\n"
- "});\n"
- "if ok == 0 {\n"
+ "if CallJitPropertyOp(info, cx, obj, this.unsafe_get() as *libc::c_void, argv) == 0 {\n"
" return 0;\n"
"}\n"
"*vp = UndefinedValue();\n"
@@ -2566,17 +2583,11 @@ class CGSpecializedSetter(CGAbstractExternMethod):
def definition_body(self):
name = self.attr.identifier.name
- nativeName = "Set" + MakeNativeName(name)
- argsPre = []
- extraPre = ''
- if name in self.descriptor.needsAbstract:
- abstractName = re.sub(r'<\w+>', '', self.descriptor.nativeType)
- extraPre = ' let mut abstract_this = %s::from_raw(this);\n' % abstractName
- argsPre = ['&mut abstract_this']
- return CGWrapper(CGIndenter(CGSetterCall(argsPre, self.attr.type, nativeName,
+ return CGWrapper(CGIndenter(CGSetterCall([], self.attr.type,
+ "Set" + MakeNativeName(name),
self.descriptor, self.attr)),
- pre=extraPre +
- " let this = &mut *this;\n").define()
+ pre=" let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n").define()
class CGMemberJITInfo(CGThing):
@@ -2744,7 +2755,7 @@ def getUnionTypeTemplateVars(type, descriptorProvider):
name = type.name
typeName = "/*" + type.name + "*/"
- (template, _, _, _) = getJSToNativeConversionTemplate(
+ (template, _, _, _, _) = getJSToNativeConversionTemplate(
type, descriptorProvider, failureCode="return Ok(None);",
exceptionCode='return Err(());',
isDefinitelyObject=True, isOptional=False)
@@ -3411,7 +3422,12 @@ class CGProxySpecialOperation(CGPerSignatureCall):
self.cgRoot.prepend(CGGeneric("let mut found = false;"))
def getArguments(self):
- args = [(a, a.identifier.name) for a in self.arguments]
+ def process(arg):
+ argVal = arg.identifier.name
+ if arg.type.isGeckoInterface() and not arg.type.unroll().inner.isCallback():
+ argVal += ".root_ref()"
+ return argVal
+ args = [(a, process(a)) for a in self.arguments]
if self.idlNode.isGetter():
args.append((FakeArgument(BuiltinTypes[IDLBuiltinType.Types.boolean],
self.idlNode),
@@ -3461,14 +3477,14 @@ class CGProxyNamedSetter(CGProxySpecialOperation):
class CGProxyUnwrap(CGAbstractMethod):
def __init__(self, descriptor):
args = [Argument('*JSObject', 'obj')]
- CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*' + descriptor.concreteType, args, alwaysInline=True)
+ CGAbstractMethod.__init__(self, descriptor, "UnwrapProxy", '*mut ' + descriptor.concreteType, args, alwaysInline=True)
def definition_body(self):
return """ /*if (xpc::WrapperFactory::IsXrayWrapper(obj)) {
obj = js::UnwrapObject(obj);
}*/
//MOZ_ASSERT(IsProxy(obj));
- let box_: *%s = cast::transmute(GetProxyPrivate(obj).to_private());
+ let box_: *mut %s = cast::transmute(GetProxyPrivate(obj).to_private());
return box_;""" % (self.descriptor.concreteType)
class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
@@ -3493,9 +3509,11 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
templateValues = {'jsvalRef': '(*desc).value', 'successCode': fillDescriptor}
get = ("if index.is_some() {\n" +
" let index = index.unwrap();\n" +
- " let this: *%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define() + "\n" +
- "}\n") % (self.descriptor.concreteType)
+ "}\n")
if indexedSetter or self.descriptor.operations['NamedSetter']:
setOrIndexedGet += "if set != 0 {\n"
@@ -3538,9 +3556,11 @@ class CGDOMJSProxyHandler_getOwnPropertyDescriptor(CGAbstractExternMethod):
namedGet = ("\n" +
"if set == 0 && RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
" let name = Some(jsid_to_str(cx, id));\n" +
- " let this: *%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() + "\n" +
- "}\n") % (self.descriptor.concreteType)
+ "}\n")
else:
namedGet = ""
@@ -3581,10 +3601,12 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
set += ("let index = GetArrayIndexFromId(cx, id);\n" +
"if index.is_some() {\n" +
" let index = index.unwrap();\n" +
- " let this: *mut %s = UnwrapProxy(proxy) as *mut %s;\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyIndexedSetter(self.descriptor)).define() +
" return 1;\n" +
- "}\n") % (self.descriptor.concreteType, self.descriptor.concreteType)
+ "}\n")
elif self.descriptor.operations['IndexedGetter']:
set += ("if GetArrayIndexFromId(cx, id).is_some() {\n" +
" return 0;\n" +
@@ -3597,20 +3619,24 @@ class CGDOMJSProxyHandler_defineProperty(CGAbstractExternMethod):
raise TypeError("Can't handle creator that's different from the setter")
set += ("if RUST_JSID_IS_STRING(id) != 0 {\n" +
" let name = Some(jsid_to_str(cx, id));\n" +
- " let this: *%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyNamedSetter(self.descriptor)).define() + "\n" +
- "}\n") % (self.descriptor.concreteType)
+ "}\n")
elif self.descriptor.operations['NamedGetter']:
set += ("if RUST_JSID_IS_STRING(id) {\n" +
" let name = Some(jsid_to_str(cx, id));\n" +
- " let this: %%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyNamedGetter(self.descriptor)).define() +
" if (found) {\n"
" return 0;\n" +
" //return ThrowErrorMessage(cx, MSG_NO_PROPERTY_SETTER, \"%s\");\n" +
" }\n" +
" return 1;\n"
- "}\n") % (self.descriptor.concreteType, self.descriptor.name)
+ "}\n") % (self.descriptor.name)
return set + """return proxyhandler::defineProperty_(%s);""" % ", ".join(a.name for a in self.args)
def definition_body(self):
@@ -3628,11 +3654,13 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
indexed = ("let index = GetArrayIndexFromId(cx, id);\n" +
"if index.is_some() {\n" +
" let index = index.unwrap();\n" +
- " let this: *%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyIndexedGetter(self.descriptor)).define() + "\n" +
" *bp = found as JSBool;\n" +
" return 1;\n" +
- "}\n\n") % (self.descriptor.concreteType)
+ "}\n\n")
else:
indexed = ""
@@ -3640,12 +3668,14 @@ class CGDOMJSProxyHandler_hasOwn(CGAbstractExternMethod):
if namedGetter:
named = ("if RUST_JSID_IS_STRING(id) != 0 && !HasPropertyOnPrototype(cx, proxy, id) {\n" +
" let name = Some(jsid_to_str(cx, id));\n" +
- " let this: *%s = UnwrapProxy(proxy);\n" +
+ " let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyNamedGetter(self.descriptor)).define() + "\n" +
" *bp = found as JSBool;\n"
" return 1;\n"
"}\n" +
- "\n") % (self.descriptor.concreteType)
+ "\n")
else:
named = ""
@@ -3693,6 +3723,8 @@ if expando.is_not_null() {
"if index.is_some() {\n" +
" let index = index.unwrap();\n" +
" let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyIndexedGetter(self.descriptor, templateValues)).define())
getIndexedOrExpando += """
// Even if we don't have this index, we don't forward the
@@ -3709,6 +3741,8 @@ if expando.is_not_null() {
getNamed = ("if (JSID_IS_STRING(id)) {\n" +
" let name = Some(jsid_to_str(cx, id));\n" +
" let this = UnwrapProxy(proxy);\n" +
+ " let this = JS::from_raw(this);\n" +
+ " let mut this = this.root();\n" +
CGIndenter(CGProxyNamedGetter(self.descriptor, templateValues)).define() +
"}\n") % (self.descriptor.concreteType)
else:
@@ -3825,11 +3859,11 @@ class CGClassConstructHook(CGAbstractExternMethod):
def generate_code(self):
preamble = """
- let global = global_object_for_js_object(JS_CALLEE(cx, &*vp).to_object());
- let obj = global.reflector().get_jsobject();
+ let global = global_object_for_js_object(JS_CALLEE(cx, &*vp).to_object()).root();
+ let obj = global.deref().reflector().get_jsobject();
"""
nativeName = MakeNativeName(self._ctor.identifier.name)
- callGenerator = CGMethodCall(["&global"], nativeName, True,
+ callGenerator = CGMethodCall(["&global.root_ref()"], nativeName, True,
self.descriptor, self._ctor)
return preamble + callGenerator.define();
@@ -4030,7 +4064,7 @@ class CGDictionary(CGThing):
def struct(self):
d = self.dictionary
if d.parent:
- inheritance = " pub parent: %s::%s,\n" % (self.makeModuleName(d.parent),
+ inheritance = " pub parent: %s::%s<'a, 'b>,\n" % (self.makeModuleName(d.parent),
self.makeClassName(d.parent))
else:
inheritance = ""
@@ -4039,7 +4073,7 @@ class CGDictionary(CGThing):
for m in self.memberInfo]
return (string.Template(
- "pub struct ${selfName} {\n" +
+ "pub struct ${selfName}<'a, 'b> {\n" +
"${inheritance}" +
"\n".join(memberDecls) + "\n" +
"}").substitute( { "selfName": self.makeClassName(d),
@@ -4065,7 +4099,7 @@ class CGDictionary(CGThing):
memberInits = CGList([memberInit(m) for m in self.memberInfo])
return string.Template(
- "impl ${selfName} {\n"
+ "impl<'a, 'b> ${selfName}<'a, 'b> {\n"
" pub fn new(cx: *JSContext, val: JSVal) -> Result<${selfName}, ()> {\n"
" let object = if val.is_null_or_undefined() {\n"
" ptr::null()\n"
@@ -4104,14 +4138,14 @@ class CGDictionary(CGThing):
def getMemberType(self, memberInfo):
(member, (templateBody, declType,
- dealWithOptional, initialValue)) = memberInfo
+ dealWithOptional, initialValue, _)) = memberInfo
if dealWithOptional:
declType = CGWrapper(declType, pre="Optional< ", post=" >")
return declType.define()
def getMemberConversion(self, memberInfo):
(member, (templateBody, declType,
- dealWithOptional, initialValue)) = memberInfo
+ dealWithOptional, initialValue, _)) = memberInfo
replacements = { "val": "value.unwrap()" }
if member.defaultValue:
replacements["haveValue"] = "value.is_some()"
@@ -4238,7 +4272,7 @@ class CGBindingRoot(CGThing):
# Add imports
#XXXjdm This should only import the namespace for the current binding,
# not every binding ever.
- curr = CGImports(curr, [
+ curr = CGImports(curr, descriptors, [
'js',
'js::{JS_ARGV, JS_CALLEE, JS_THIS_OBJECT}',
'js::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_IS_DOMJSCLASS}',
@@ -4266,7 +4300,9 @@ class CGBindingRoot(CGThing):
'js::glue::{RUST_JS_NumberValue, RUST_JSID_IS_STRING}',
'dom::types::*',
'dom::bindings',
- 'dom::bindings::js::JS',
+ 'dom::bindings::js::{JS, JSRef, Root, RootedReference, Temporary}',
+ 'dom::bindings::js::{OptionalRootable, OptionalRootedRootable, ResultRootable}',
+ 'dom::bindings::js::{OptionalRootedReference, OptionalOptionalRootedRootable}',
'dom::bindings::utils::{CreateDOMGlobal, CreateInterfaceObjects2}',
'dom::bindings::utils::{ConstantSpec, cx_for_dom_object, Default}',
'dom::bindings::utils::{dom_object_slot, DOM_OBJECT_SLOT, DOMClass}',
@@ -4279,8 +4315,7 @@ class CGBindingRoot(CGThing):
'dom::bindings::utils::{Reflectable}',
'dom::bindings::utils::{squirrel_away_unique}',
'dom::bindings::utils::{ThrowingConstructor, unwrap, unwrap_jsmanaged}',
- 'dom::bindings::utils::{VoidVal, with_gc_disabled}',
- 'dom::bindings::utils::{with_gc_enabled}',
+ 'dom::bindings::utils::VoidVal',
'dom::bindings::utils::get_dictionary_property',
'dom::bindings::trace::JSTraceable',
'dom::bindings::callback::{CallbackContainer,CallbackInterface}',
@@ -4549,7 +4584,7 @@ class CGNativeMember(ClassMethod):
else:
typeDecl = "%s"
descriptor = self.descriptorProvider.getDescriptor(iface.identifier.name)
- return (typeDecl % descriptor.nativeType,
+ return (typeDecl % descriptor.argumentType,
False, False)
if type.isSpiderMonkeyInterface():
@@ -5057,11 +5092,8 @@ class CallbackMethod(CallbackMember):
replacements["argc"] = "0"
return string.Template("${getCallable}"
"let ok = unsafe {\n"
- " //JS_AllowGC(cx); // It's unsafe to enable GC at arbitrary points during Rust execution; leave it disabled\n"
- " let ok = JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
- " ${argc}, ${argv}, &rval);\n"
- " //JS_InhibitGC(cx);\n"
- " ok\n"
+ " JS_CallFunctionValue(cx, ${thisObj}, callable,\n"
+ " ${argc}, ${argv}, &rval)\n"
"};\n"
"if ok == 0 {\n"
" return${errorReturn};\n"
@@ -5207,9 +5239,9 @@ class GlobalGenRoots():
@staticmethod
def RegisterBindings(config):
# TODO - Generate the methods we want
- return CGImports(CGRegisterProtos(config), [
+ return CGImports(CGRegisterProtos(config), [], [
'dom::bindings::codegen',
- 'dom::bindings::js::JS',
+ 'dom::bindings::js::{JS, JSRef}',
'dom::window::Window',
'script_task::JSPageInfo',
])
@@ -5241,8 +5273,9 @@ class GlobalGenRoots():
descriptors = config.getDescriptors(register=True, hasInterfaceObject=True)
allprotos = [CGGeneric("#![allow(unused_imports)]\n"),
CGGeneric("use dom::types::*;\n"),
- CGGeneric("use dom::bindings::js::JS;\n"),
+ CGGeneric("use dom::bindings::js::{JS, JSRef, Temporary};\n"),
CGGeneric("use dom::bindings::trace::JSTraceable;\n"),
+ CGGeneric("use dom::bindings::utils::Reflectable;\n"),
CGGeneric("use serialize::{Encodable, Encoder};\n"),
CGGeneric("use js::jsapi::JSTracer;\n\n")]
for descriptor in descriptors:
@@ -5269,22 +5302,34 @@ class GlobalGenRoots():
cast = [CGGeneric(string.Template('''pub trait ${castTraitName} {
#[inline(always)]
- fn from<T: ${fromBound}>(derived: &JS<T>) -> JS<Self> {
- unsafe { derived.clone().transmute() }
+ fn to_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a JSRef<'b, T>) -> Option<&'a JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute()) },
+ false => None
+ }
}
#[inline(always)]
- fn to<T: ${toBound}>(base: &JS<T>) -> Option<JS<Self>> {
- match base.get().${checkFn}() {
- true => unsafe { Some(base.clone().transmute()) },
+ fn to_mut_ref<'a, 'b, T: ${toBound}+Reflectable>(base: &'a mut JSRef<'b, T>) -> Option<&'a mut JSRef<'b, Self>> {
+ match base.deref().${checkFn}() {
+ true => unsafe { Some(base.transmute_mut()) },
false => None
}
}
#[inline(always)]
- unsafe fn to_unchecked<T: ${toBound}>(base: &JS<T>) -> JS<Self> {
- assert!(base.get().${checkFn}());
- base.clone().transmute()
+ fn from_ref<'a, 'b, T: ${fromBound}>(derived: &'a JSRef<'b, T>) -> &'a JSRef<'b, Self> {
+ unsafe { derived.transmute() }
+ }
+
+ #[inline(always)]
+ fn from_mut_ref<'a, 'b, T: ${fromBound}>(derived: &'a mut JSRef<'b, T>) -> &'a mut JSRef<'b, Self> {
+ unsafe { derived.transmute_mut() }
+ }
+
+ #[inline(always)]
+ fn from_unrooted<T: ${fromBound}+Reflectable>(derived: Temporary<T>) -> Temporary<Self> {
+ unsafe { derived.transmute() }
}
}
''').substitute({'checkFn': 'is_' + name.lower(),
@@ -5313,7 +5358,7 @@ class GlobalGenRoots():
curr = UnionTypes(config.getDescriptors())
- curr = CGImports(curr, [
+ curr = CGImports(curr, [], [
'dom::bindings::utils::unwrap_jsmanaged',
'dom::bindings::codegen::PrototypeList',
'dom::bindings::conversions::{FromJSValConvertible, ToJSValConvertible}',
diff --git a/src/components/script/dom/bindings/codegen/Configuration.py b/src/components/script/dom/bindings/codegen/Configuration.py
index a1ccb460ee2..1272867f542 100644
--- a/src/components/script/dom/bindings/codegen/Configuration.py
+++ b/src/components/script/dom/bindings/codegen/Configuration.py
@@ -128,14 +128,19 @@ class Descriptor(DescriptorProvider):
# Read the desc, and fill in the relevant defaults.
ifaceName = self.interface.identifier.name
+
+ # Callback types do not use JS smart pointers, so we should not use the
+ # built-in rooting mechanisms for them.
if self.interface.isCallback():
- nativeTypeDefault = "nsIDOM" + ifaceName
+ self.needsRooting = False
else:
- nativeTypeDefault = 'JS<%s>' % ifaceName
+ self.needsRooting = True
- self.nativeType = desc.get('nativeType', nativeTypeDefault)
+ self.returnType = "Temporary<%s>" % ifaceName
+ self.argumentType = "JSRef<%s>" % ifaceName
+ self.memberType = "Root<'a, 'b, %s>" % ifaceName
+ self.nativeType = desc.get('nativeType', 'JS<%s>' % ifaceName)
self.concreteType = desc.get('concreteType', ifaceName)
- self.needsAbstract = desc.get('needsAbstract', [])
self.createGlobal = desc.get('createGlobal', False)
self.register = desc.get('register', True)
self.outerObjectHook = desc.get('outerObjectHook', 'None')
diff --git a/src/components/script/dom/bindings/conversions.rs b/src/components/script/dom/bindings/conversions.rs
index 6721f3f5dde..02d6983cf3b 100644
--- a/src/components/script/dom/bindings/conversions.rs
+++ b/src/components/script/dom/bindings/conversions.rs
@@ -2,9 +2,9 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::js::JS;
+use dom::bindings::js::{JS, JSRef, Root};
use dom::bindings::str::ByteString;
-use dom::bindings::utils::Reflectable;
+use dom::bindings::utils::{Reflectable, Reflector};
use dom::bindings::utils::jsstring_to_str;
use dom::bindings::utils::unwrap_jsmanaged;
use servo_util::str::DOMString;
@@ -293,9 +293,9 @@ impl FromJSValConvertible<()> for ByteString {
}
}
-impl<T: Reflectable> ToJSValConvertible for JS<T> {
+impl ToJSValConvertible for Reflector {
fn to_jsval(&self, cx: *JSContext) -> JSVal {
- let obj = self.reflector().get_jsobject();
+ let obj = self.get_jsobject();
assert!(obj.is_not_null());
let mut value = ObjectValue(unsafe { &*obj });
if unsafe { JS_WrapValue(cx, &mut value as *mut JSVal as *JSVal) } == 0 {
@@ -316,6 +316,18 @@ impl<T: Reflectable+IDLInterface> FromJSValConvertible<()> for JS<T> {
}
}
+impl<'a, 'b, T: Reflectable> ToJSValConvertible for Root<'a, 'b, T> {
+ fn to_jsval(&self, cx: *JSContext) -> JSVal {
+ self.reflector().to_jsval(cx)
+ }
+}
+
+impl<'a, T: Reflectable> ToJSValConvertible for JSRef<'a, T> {
+ fn to_jsval(&self, cx: *JSContext) -> JSVal {
+ self.reflector().to_jsval(cx)
+ }
+}
+
impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
fn to_jsval(&self, cx: *JSContext) -> JSVal {
match self {
diff --git a/src/components/script/dom/bindings/js.rs b/src/components/script/dom/bindings/js.rs
index 10aec5424bc..da84c85eeb7 100644
--- a/src/components/script/dom/bindings/js.rs
+++ b/src/components/script/dom/bindings/js.rs
@@ -2,14 +2,115 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-use dom::bindings::utils::{Reflector, Reflectable};
-use dom::window::Window;
-use js::jsapi::JSContext;
+/// The DOM is made up of Rust types whose lifetime is entirely controlled by the whims of
+/// the SpiderMonkey garbage collector. The types in this module are designed to ensure
+/// that any interactions with said Rust types only occur on values that will remain alive
+/// the entire time.
+///
+/// Here is a brief overview of the important types:
+/// - JSRef<T>: a freely-copyable reference to a rooted value.
+/// - JS<T>: a pointer to JS-owned memory that can automatically be traced by the GC when
+/// encountered as a field of a Rust structure.
+/// - Temporary<T>: a value that will remain rooted for the duration of its lifetime.
+///
+/// The rule of thumb is as follows:
+/// - 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 types contain JS<T> fields and derive the Encodable 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. This returns a Root<T>, which causes
+/// the JS-owned value to be uncollectable for the duration of the Root type's lifetime.
+/// A JSRef<T> can be obtained from a Root<T> either by dereferencing the Root<T> (`*rooted`)
+/// or explicitly calling the `root_ref` 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 OptionalRootedRootable: 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
+/// - OptionalSettable: allows assigning Option values of JSRef/Temporary to fields of Option<JS<T>>
+/// - RootedReference: makes obtaining an Option<JSRef<T>> from an Option<Root<T>> easy
+
+use dom::bindings::utils::{Reflector, Reflectable, cx_for_dom_object};
+use dom::node::Node;
+use js::jsapi::{JSObject, JS_AddObjectRoot, JS_RemoveObjectRoot};
use layout_interface::TrustedNodeAddress;
+use script_task::StackRoots;
use std::cast;
use std::cell::RefCell;
+use std::local_data;
+
+/// 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 (ie. JS<T> types) safely via the
+/// `JS<T>::assign` method or `OptionalSettable::assign` (for Option<JS<T>> fields).
+pub struct Temporary<T> {
+ inner: JS<T>,
+}
+
+impl<T> Eq for Temporary<T> {
+ fn eq(&self, other: &Temporary<T>) -> bool {
+ self.inner == other.inner
+ }
+}
+
+#[unsafe_destructor]
+impl<T: Reflectable> Drop for Temporary<T> {
+ fn drop(&mut self) {
+ let cx = cx_for_dom_object(&self.inner);
+ unsafe {
+ JS_RemoveObjectRoot(cx, self.inner.reflector().rootable());
+ }
+ }
+}
+
+impl<T: Reflectable> Temporary<T> {
+ /// Create a new Temporary value from a JS-owned value.
+ pub fn new(inner: JS<T>) -> Temporary<T> {
+ let cx = cx_for_dom_object(&inner);
+ unsafe {
+ JS_AddObjectRoot(cx, inner.reflector().rootable());
+ }
+ Temporary {
+ inner: inner,
+ }
+ }
+
+ /// Create a new Temporary value from a rooted value.
+ pub fn from_rooted<'a>(root: &JSRef<'a, T>) -> Temporary<T> {
+ Temporary::new(root.unrooted())
+ }
+
+ /// Create a stack-bounded root for this value.
+ pub fn root<'a, 'b>(self) -> Root<'a, 'b, T> {
+ local_data::get(StackRoots, |opt| {
+ let collection = opt.unwrap();
+ unsafe {
+ (**collection).new_root(&self.inner)
+ }
+ })
+ }
+
+ unsafe fn inner(&self) -> JS<T> {
+ self.inner.clone()
+ }
+
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute<To>(self) -> Temporary<To> {
+ cast::transmute(self)
+ }
+}
+/// A rooted, JS-owned value. Must only be used as a field in other JS-owned types.
pub struct JS<T> {
ptr: RefCell<*mut T>
}
@@ -29,13 +130,18 @@ impl <T> Clone for JS<T> {
}
}
-impl<T: Reflectable> JS<T> {
- pub fn new(obj: ~T,
- window: &JS<Window>,
- wrap_fn: extern "Rust" fn(*JSContext, &JS<Window>, ~T) -> JS<T>) -> JS<T> {
- wrap_fn(window.get().get_cx(), window, obj)
+impl JS<Node> {
+ /// Create a new JS-owned value wrapped from an address known to be a Node pointer.
+ pub unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> JS<Node> {
+ let TrustedNodeAddress(addr) = inner;
+ JS {
+ ptr: RefCell::new(addr as *mut Node)
+ }
}
+}
+impl<T: Reflectable> JS<T> {
+ /// Create a new JS-owned value wrapped from a raw Rust pointer.
pub unsafe fn from_raw(raw: *mut T) -> JS<T> {
JS {
ptr: RefCell::new(raw)
@@ -43,45 +149,47 @@ impl<T: Reflectable> JS<T> {
}
- pub unsafe fn from_trusted_node_address(inner: TrustedNodeAddress) -> JS<T> {
- let TrustedNodeAddress(addr) = inner;
- JS {
- ptr: RefCell::new(addr as *mut T)
- }
+ /// Root this JS-owned value to prevent its collection as garbage.
+ pub fn root<'a, 'b>(&self) -> Root<'a, 'b, T> {
+ local_data::get(StackRoots, |opt| {
+ let collection = opt.unwrap();
+ unsafe {
+ (**collection).new_root(self)
+ }
+ })
}
}
+//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 {
- self.get().reflector()
- }
-
- fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
- self.get_mut().mut_reflector()
- }
-}
-
-impl<T> JS<T> {
- pub fn get<'a>(&'a self) -> &'a T {
- let borrowed = self.ptr.borrow();
unsafe {
- &**borrowed
+ (*self.unsafe_get()).reflector()
}
}
- pub fn get_mut<'a>(&'a mut self) -> &'a mut T {
- let mut borrowed = self.ptr.borrow_mut();
+ fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
unsafe {
- &mut **borrowed
+ (*self.unsafe_get()).mut_reflector()
}
}
+}
+impl<T: Reflectable> JS<T> {
/// Returns an unsafe pointer to the interior of this JS object without touching the borrow
/// flags. This is the only method that be safely accessed from layout. (The fact that this
/// is unsafe is what necessitates the layout wrappers.)
pub unsafe fn unsafe_get(&self) -> *mut T {
cast::transmute_copy(&self.ptr)
}
+
+ /// 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 = unsafe { val.inner() };
+ }
}
impl<From, To> JS<From> {
@@ -94,3 +202,304 @@ impl<From, To> JS<From> {
cast::transmute_copy(self)
}
}
+
+
+/// Get an Option<JSRef<T>> out of an Option<Root<T>>
+pub trait RootedReference<T> {
+ fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>>;
+}
+
+impl<'a, 'b, T: Reflectable> RootedReference<T> for Option<Root<'a, 'b, T>> {
+ fn root_ref<'a>(&'a self) -> Option<JSRef<'a, T>> {
+ self.as_ref().map(|root| root.root_ref())
+ }
+}
+
+/// Get an Option<Option<JSRef<T>>> out of an Option<Option<Root<T>>>
+pub trait OptionalRootedReference<T> {
+ fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>>;
+}
+
+impl<'a, 'b, T: Reflectable> OptionalRootedReference<T> for Option<Option<Root<'a, 'b, T>>> {
+ fn root_ref<'a>(&'a self) -> Option<Option<JSRef<'a, T>>> {
+ self.as_ref().map(|inner| inner.root_ref())
+ }
+}
+
+/// 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.
+/*definitely not public*/ trait Assignable<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> Assignable<T> for JSRef<'a, T> {
+ unsafe fn get_js(&self) -> JS<T> {
+ self.unrooted()
+ }
+}
+
+impl<T: Reflectable> Assignable<T> for Temporary<T> {
+ unsafe fn get_js(&self) -> JS<T> {
+ self.inner()
+ }
+}
+
+/// Assign an optional rootable value (either of JS<T> or Temporary<T>) to an optional
+/// field of a DOM type (ie. Option<JS<T>>)
+pub trait OptionalSettable<T> {
+ fn assign(&mut self, val: Option<T>);
+}
+
+impl<T: Assignable<U>, U: Reflectable> OptionalSettable<T> for Option<JS<U>> {
+ fn assign(&mut self, val: Option<T>) {
+ *self = val.map(|val| unsafe { val.get_js() });
+ }
+}
+
+/// Root a rootable Option type (used for Option<Temporary<T>>)
+pub trait OptionalRootable<T> {
+ fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>>;
+}
+
+impl<T: Reflectable> OptionalRootable<T> for Option<Temporary<T>> {
+ fn root<'a, 'b>(self) -> Option<Root<'a, 'b, T>> {
+ self.map(|inner| inner.root())
+ }
+}
+
+/// Return an unrooted type for storing in optional DOM fields
+pub trait OptionalUnrootable<T> {
+ fn unrooted(&self) -> Option<JS<T>>;
+}
+
+impl<'a, T: Reflectable> OptionalUnrootable<T> for Option<JSRef<'a, T>> {
+ fn unrooted(&self) -> Option<JS<T>> {
+ self.as_ref().map(|inner| inner.unrooted())
+ }
+}
+
+/// Root a rootable Option type (used for Option<JS<T>>)
+pub trait OptionalRootedRootable<T> {
+ fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>>;
+}
+
+impl<T: Reflectable> OptionalRootedRootable<T> for Option<JS<T>> {
+ fn root<'a, 'b>(&self) -> Option<Root<'a, 'b, T>> {
+ self.as_ref().map(|inner| inner.root())
+ }
+}
+
+/// Root a rootable Option<Option> type (used for Option<Option<JS<T>>>)
+pub trait OptionalOptionalRootedRootable<T> {
+ fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, T>>>;
+}
+
+impl<T: Reflectable> OptionalOptionalRootedRootable<T> for Option<Option<JS<T>>> {
+ fn root<'a, 'b>(&self) -> Option<Option<Root<'a, 'b, 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> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U>;
+}
+
+impl<T: Reflectable, U> ResultRootable<T, U> for Result<Temporary<T>, U> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
+ self.map(|inner| inner.root())
+ }
+}
+
+impl<T: Reflectable, U> ResultRootable<T, U> for Result<JS<T>, U> {
+ fn root<'a, 'b>(self) -> Result<Root<'a, 'b, T>, U> {
+ self.map(|inner| inner.root())
+ }
+}
+
+/// 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> {
+ fn push_unrooted(&mut self, val: &T);
+ fn insert_unrooted(&mut self, index: uint, 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: uint, val: &T) {
+ self.insert(index, unsafe { val.get_js() });
+ }
+}
+
+/// An opaque, LIFO rooting mechanism.
+pub struct RootCollection {
+ roots: RefCell<Vec<*JSObject>>,
+}
+
+impl RootCollection {
+ /// Create an empty collection of roots
+ pub fn new() -> RootCollection {
+ RootCollection {
+ roots: RefCell::new(vec!()),
+ }
+ }
+
+ /// Create a new stack-bounded root that will not outlive this collection
+ fn new_root<'a, 'b, T: Reflectable>(&'a self, unrooted: &JS<T>) -> Root<'a, 'b, T> {
+ Root::new(self, unrooted)
+ }
+
+ /// Track a stack-based root to ensure LIFO root ordering
+ fn root<'a, 'b, T: Reflectable>(&self, untracked: &Root<'a, 'b, T>) {
+ let mut roots = self.roots.borrow_mut();
+ roots.push(untracked.js_ptr);
+ debug!(" rooting {:?}", untracked.js_ptr);
+ }
+
+ /// Stop tracking a stack-based root, asserting if LIFO root ordering has been violated
+ fn unroot<'a, 'b, T: Reflectable>(&self, rooted: &Root<'a, 'b, T>) {
+ let mut roots = self.roots.borrow_mut();
+ debug!("unrooting {:?} (expecting {:?}", roots.last().unwrap(), rooted.js_ptr);
+ assert!(*roots.last().unwrap() == rooted.js_ptr);
+ roots.pop().unwrap();
+ }
+}
+
+/// A rooted JS value. 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. Roots cannot outlive the associated RootCollection object.
+/// Attempts to transfer ownership of a Root via moving will trigger dynamic unrooting
+/// failures due to incorrect ordering.
+pub struct Root<'a, 'b, T> {
+ /// List that ensures correct dynamic root ordering
+ root_list: &'a RootCollection,
+ /// Reference to rooted value that must not outlive this container
+ jsref: JSRef<'b, T>,
+ /// Pointer to underlying Rust data
+ ptr: RefCell<*mut T>,
+ /// On-stack JS pointer to assuage conservative stack scanner
+ js_ptr: *JSObject,
+}
+
+impl<'a, 'b, T: Reflectable> Root<'a, 'b, T> {
+ /// Create a new stack-bounded root for the provided JS-owned value.
+ /// It cannot not outlive its associated RootCollection, and it contains a JSRef
+ /// which cannot outlive this new Root.
+ fn new(roots: &'a RootCollection, unrooted: &JS<T>) -> Root<'a, 'b, T> {
+ let root = Root {
+ root_list: roots,
+ jsref: JSRef {
+ ptr: unrooted.ptr.clone(),
+ chain: unsafe { cast::transmute_region(&()) },
+ },
+ ptr: unrooted.ptr.clone(),
+ js_ptr: unrooted.reflector().get_jsobject(),
+ };
+ roots.root(&root);
+ root
+ }
+
+ /// Obtain a safe reference to the wrapped JS owned-value that cannot outlive
+ /// the lifetime of this root.
+ pub fn root_ref<'b>(&'b self) -> JSRef<'b,T> {
+ self.jsref.clone()
+ }
+}
+
+#[unsafe_destructor]
+impl<'a, 'b, T: Reflectable> Drop for Root<'a, 'b, T> {
+ fn drop(&mut self) {
+ self.root_list.unroot(self);
+ }
+}
+
+impl<'a, 'b, T: Reflectable> Deref<JSRef<'b, T>> for Root<'a, 'b, T> {
+ fn deref<'c>(&'c self) -> &'c JSRef<'b, T> {
+ &self.jsref
+ }
+}
+
+impl<'a, 'b, T: Reflectable> DerefMut<JSRef<'b, T>> for Root<'a, 'b, T> {
+ fn deref_mut<'c>(&'c mut self) -> &'c mut JSRef<'b, T> {
+ &mut self.jsref
+ }
+}
+
+impl<'a, T: Reflectable> Deref<T> for JSRef<'a, T> {
+ fn deref<'b>(&'b self) -> &'b T {
+ let borrow = self.ptr.borrow();
+ unsafe {
+ &**borrow
+ }
+ }
+}
+
+impl<'a, T: Reflectable> DerefMut<T> for JSRef<'a, T> {
+ fn deref_mut<'b>(&'b mut self) -> &'b mut T {
+ let mut borrowed = self.ptr.borrow_mut();
+ unsafe {
+ &mut **borrowed
+ }
+ }
+}
+
+/// Encapsulates a reference to something that is guaranteed to be alive. This is freely copyable.
+pub struct JSRef<'a, T> {
+ ptr: RefCell<*mut T>,
+ chain: &'a (),
+}
+
+impl<'a, T> Clone for JSRef<'a, T> {
+ fn clone(&self) -> JSRef<'a, T> {
+ JSRef {
+ ptr: self.ptr.clone(),
+ chain: self.chain
+ }
+ }
+}
+
+impl<'a, T> Eq for JSRef<'a, T> {
+ fn eq(&self, other: &JSRef<T>) -> bool {
+ self.ptr == other.ptr
+ }
+}
+
+impl<'a,T> JSRef<'a,T> {
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute<'b, To>(&'b self) -> &'b JSRef<'a, To> {
+ cast::transmute(self)
+ }
+
+ //XXXjdm It would be lovely if this could be private.
+ pub unsafe fn transmute_mut<'b, To>(&'b mut self) -> &'b mut JSRef<'a, To> {
+ cast::transmute(self)
+ }
+
+ pub fn unrooted(&self) -> JS<T> {
+ JS {
+ ptr: self.ptr.clone()
+ }
+ }
+}
+
+impl<'a, T: Reflectable> Reflectable for JSRef<'a, T> {
+ fn reflector<'a>(&'a self) -> &'a Reflector {
+ self.deref().reflector()
+ }
+
+ fn mut_reflector<'a>(&'a mut self) -> &'a mut Reflector {
+ self.deref_mut().mut_reflector()
+ }
+}
diff --git a/src/components/script/dom/bindings/trace.rs b/src/components/script/dom/bindings/trace.rs
index 554c67e8dc6..cbbeddb8594 100644
--- a/src/components/script/dom/bindings/trace.rs
+++ b/src/components/script/dom/bindings/trace.rs
@@ -6,6 +6,7 @@ use dom::bindings::js::JS;
use dom::bindings::utils::{Reflectable, Reflector};
use js::jsapi::{JSObject, JSTracer, JS_CallTracer, JSTRACE_OBJECT};
+use js::jsval::JSVal;
use libc;
use std::cast;
@@ -42,6 +43,22 @@ pub trait JSTraceable {
fn trace(&self, trc: *mut JSTracer);
}
+pub fn trace_jsval(tracer: *mut JSTracer, description: &str, val: JSVal) {
+ if !val.is_gcthing() {
+ return;
+ }
+
+ unsafe {
+ description.to_c_str().with_ref(|name| {
+ (*tracer).debugPrinter = ptr::null();
+ (*tracer).debugPrintIndex = -1;
+ (*tracer).debugPrintArg = name as *libc::c_void;
+ debug!("tracing value {:s}", description);
+ JS_CallTracer(tracer as *JSTracer, val.to_gcthing(), val.trace_kind());
+ });
+ }
+}
+
pub fn trace_reflector(tracer: *mut JSTracer, description: &str, reflector: &Reflector) {
trace_object(tracer, description, reflector.get_jsobject())
}
@@ -132,3 +149,10 @@ impl<S: Encoder<E>, E> Encodable<S, E> for Traceable<*JSObject> {
Ok(())
}
}
+
+impl<S: Encoder<E>, E> Encodable<S, E> for Traceable<JSVal> {
+ fn encode(&self, s: &mut S) -> Result<(), E> {
+ trace_jsval(get_jstracer(s), "val", **self);
+ Ok(())
+ }
+}
diff --git a/src/components/script/dom/bindings/utils.rs b/src/components/script/dom/bindings/utils.rs
index 417d4c434c2..a99189ae161 100644
--- a/src/components/script/dom/bindings/utils.rs
+++ b/src/components/script/dom/bindings/utils.rs
@@ -5,7 +5,7 @@
use dom::bindings::codegen::PrototypeList;
use dom::bindings::codegen::PrototypeList::MAX_PROTO_CHAIN_LENGTH;
use dom::bindings::conversions::{FromJSValConvertible, IDLInterface};
-use dom::bindings::js::JS;
+use dom::bindings::js::{JS, JSRef, Temporary, Root};
use dom::bindings::trace::Untraceable;
use dom::browsercontext;
use dom::window;
@@ -37,7 +37,6 @@ use js::jsapi::{JSContext, JSObject, JSBool, jsid, JSClass, JSNative};
use js::jsapi::{JSFunctionSpec, JSPropertySpec};
use js::jsapi::{JS_NewGlobalObject, JS_InitStandardClasses};
use js::jsapi::{JSString};
-use js::jsapi::{JS_AllowGC, JS_InhibitGC};
use js::jsfriendapi::bindgen::JS_NewObjectWithUniqueType;
use js::jsval::JSVal;
use js::jsval::{PrivateValue, ObjectValue, NullValue, ObjectOrNullValue};
@@ -390,10 +389,10 @@ pub trait Reflectable {
pub fn reflect_dom_object<T: Reflectable>
(obj: ~T,
- window: &JS<window::Window>,
- wrap_fn: extern "Rust" fn(*JSContext, &JS<window::Window>, ~T) -> JS<T>)
- -> JS<T> {
- JS::new(obj, window, wrap_fn)
+ window: &JSRef<window::Window>,
+ wrap_fn: extern "Rust" fn(*JSContext, &JSRef<window::Window>, ~T) -> JS<T>)
+ -> Temporary<T> {
+ Temporary::new(wrap_fn(window.deref().get_cx(), window, obj))
}
#[deriving(Eq)]
@@ -413,6 +412,13 @@ impl Reflector {
self.object = 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 required by the JSAPI rooting
+ /// APIs.
+ pub fn rootable<'a>(&'a self) -> &'a *JSObject {
+ &self.object
+ }
+
pub fn new() -> Reflector {
Reflector {
object: ptr::null(),
@@ -605,11 +611,13 @@ pub extern fn outerize_global(_cx: *JSContext, obj: JSHandleObject) -> *JSObject
unsafe {
debug!("outerizing");
let obj = *obj.unnamed;
- let win: JS<window::Window> =
+ let win: Root<window::Window> =
unwrap_jsmanaged(obj,
IDLInterface::get_prototype_id(None::<window::Window>),
- IDLInterface::get_prototype_depth(None::<window::Window>)).unwrap();
- win.get().browser_context.get_ref().window_proxy()
+ IDLInterface::get_prototype_depth(None::<window::Window>))
+ .unwrap()
+ .root();
+ win.deref().browser_context.get_ref().window_proxy()
}
}
@@ -625,8 +633,8 @@ pub fn global_object_for_js_object(obj: *JSObject) -> JS<window::Window> {
}
fn cx_for_dom_reflector(obj: *JSObject) -> *JSContext {
- let win = global_object_for_js_object(obj);
- let js_info = win.get().page().js_info();
+ let win = global_object_for_js_object(obj).root();
+ let js_info = win.deref().page().js_info();
match *js_info {
Some(ref info) => info.js_context.deref().deref().ptr,
None => fail!("no JS context for DOM global")
@@ -637,26 +645,6 @@ pub fn cx_for_dom_object<T: Reflectable>(obj: &T) -> *JSContext {
cx_for_dom_reflector(obj.reflector().get_jsobject())
}
-/// Execute arbitrary code with the JS GC enabled, then disable it afterwards.
-pub fn with_gc_enabled<R>(cx: *JSContext, f: || -> R) -> R {
- unsafe {
- JS_AllowGC(cx);
- let rv = f();
- JS_InhibitGC(cx);
- rv
- }
-}
-
-/// Execute arbitrary code with the JS GC disabled, then enable it afterwards.
-pub fn with_gc_disabled<R>(cx: *JSContext, f: || -> R) -> R {
- unsafe {
- JS_InhibitGC(cx);
- let rv = f();
- JS_AllowGC(cx);
- rv
- }
-}
-
/// Check if an element name is valid. See http://www.w3.org/TR/xml/#NT-Name
/// for details.
#[deriving(Eq)]