Be more conservative about safety of dictionary and union values.

Mark dictionaries containing GC values as must_root, and wrap them in
RootedTraceableBox in automatically-generated APIs. To accommodate
union variants that are now flagged as unsafe, add RootedTraceableBox
to union variants that need to be rooted, rather than wrapping the
entire union value.
This commit is contained in:
Josh Matthews 2017-05-26 12:29:31 -04:00
parent e481e8934a
commit da65698c5c
5 changed files with 47 additions and 17 deletions

View file

@ -723,9 +723,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >")
if isMember != "Dictionary" and type_needs_tracing(type):
declType = CGTemplatedType("RootedTraceableBox", declType)
templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(value)) => value,\n"
" Ok(ConversionResult::Failure(error)) => {\n"
@ -1427,6 +1424,8 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
nullable = returnType.nullable()
dictName = returnType.inner.name if nullable else returnType.name
result = CGGeneric(dictName)
if type_needs_tracing(returnType):
result = CGWrapper(result, pre="RootedTraceableBox<", post=">")
if nullable:
result = CGWrapper(result, pre="Option<", post=">")
return result
@ -2262,6 +2261,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
'dom::bindings::str::ByteString',
'dom::bindings::str::DOMString',
'dom::bindings::str::USVString',
'dom::bindings::trace::RootedTraceableBox',
'dom::types::*',
'js::error::throw_type_error',
'js::jsapi::HandleValue',
@ -4132,15 +4132,23 @@ class CGUnionStruct(CGThing):
self.type = type
self.descriptorProvider = descriptorProvider
def membersNeedTracing(self):
for t in self.type.flatMemberTypes:
if type_needs_tracing(t):
return True
return False
def define(self):
templateVars = map(lambda t: getUnionTypeTemplateVars(t, self.descriptorProvider),
templateVars = map(lambda t: (getUnionTypeTemplateVars(t, self.descriptorProvider),
type_needs_tracing(t)),
self.type.flatMemberTypes)
enumValues = [
" %s(%s)," % (v["name"], v["typeName"]) for v in templateVars
" %s(%s)," % (v["name"], "RootedTraceableBox<%s>" % v["typeName"] if trace else v["typeName"])
for (v, trace) in templateVars
]
enumConversions = [
" %s::%s(ref inner) => inner.to_jsval(cx, rval),"
% (self.type, v["name"]) for v in templateVars
% (self.type, v["name"]) for (v, _) in templateVars
]
return ("""\
#[derive(JSTraceable)]
@ -4167,6 +4175,12 @@ class CGUnionConversionStruct(CGThing):
self.type = type
self.descriptorProvider = descriptorProvider
def membersNeedTracing(self):
for t in self.type.flatMemberTypes:
if type_needs_tracing(t):
return True
return False
def from_jsval(self):
memberTypes = self.type.flatMemberTypes
names = []
@ -4310,7 +4324,10 @@ class CGUnionConversionStruct(CGThing):
def try_method(self, t):
templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider)
returnType = "Result<Option<%s>, ()>" % templateVars["typeName"]
actualType = templateVars["typeName"]
if type_needs_tracing(t):
actualType = "RootedTraceableBox<%s>" % actualType
returnType = "Result<Option<%s>, ()>" % actualType
jsConversion = templateVars["jsConversion"]
# Any code to convert to Object is unused, since we're already converting
@ -6022,13 +6039,17 @@ class CGDictionary(CGThing):
(self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
for m in self.memberInfo]
mustRoot = "#[must_root]\n" if self.membersNeedTracing() else ""
return (string.Template(
"#[derive(JSTraceable)]\n"
"${mustRoot}" +
"pub struct ${selfName} {\n" +
"${inheritance}" +
"\n".join(memberDecls) + "\n" +
"}").substitute({"selfName": self.makeClassName(d),
"inheritance": inheritance}))
"inheritance": inheritance,
"mustRoot": mustRoot}))
def impl(self):
d = self.dictionary
@ -6120,6 +6141,12 @@ class CGDictionary(CGThing):
"insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
})
def membersNeedTracing(self):
for member, _ in self.memberInfo:
if type_needs_tracing(member.type):
return True
return False
@staticmethod
def makeDictionaryName(dictionary):
return dictionary.identifier.name

View file

@ -12,7 +12,7 @@ use dom::bindings::codegen::Bindings::IterableIteratorBinding::IterableKeyOrValu
use dom::bindings::error::Fallible;
use dom::bindings::js::{JS, Root};
use dom::bindings::reflector::{DomObject, Reflector, reflect_dom_object};
use dom::bindings::trace::JSTraceable;
use dom::bindings::trace::{JSTraceable, RootedTraceableBox};
use dom::globalscope::GlobalScope;
use dom_struct::dom_struct;
use js::conversions::ToJSValConvertible;
@ -115,7 +115,7 @@ fn dict_return(cx: *mut JSContext,
result: MutableHandleObject,
done: bool,
value: HandleValue) -> Fallible<()> {
let mut dict = unsafe { IterableKeyOrValueResult::empty(cx) };
let mut dict = RootedTraceableBox::new(unsafe { IterableKeyOrValueResult::empty(cx) });
dict.done = done;
dict.value.set(value.get());
rooted!(in(cx) let mut dict_value = UndefinedValue());
@ -130,7 +130,7 @@ fn key_and_value_return(cx: *mut JSContext,
result: MutableHandleObject,
key: HandleValue,
value: HandleValue) -> Fallible<()> {
let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) };
let mut dict = RootedTraceableBox::new(unsafe { IterableKeyAndValueResult::empty(cx) });
dict.done = false;
dict.value = Some(vec![Heap::new(key.get()), Heap::new(value.get())]);
rooted!(in(cx) let mut dict_value = UndefinedValue());

View file

@ -745,6 +745,7 @@ impl<'a, T: JSTraceable + 'static> Drop for RootedTraceable<'a, T> {
/// If you have GC things like *mut JSObject or JSVal, use rooted!.
/// If you have an arbitrary number of DomObjects to root, use rooted_vec!.
/// If you know what you're doing, use this.
#[allow_unrooted_interior]
pub struct RootedTraceableBox<T: 'static + JSTraceable> {
ptr: *mut T,
}