Auto merge of #17056 - jdm:heapdict, r=emilio

Make dictionaries and unions containing GC values safer

Problems:
* the Heap::new constructor is memory-unsafe with any value other than Undefined/Null
* this means that moving dictionaries containing Heap values (ie. any/object) is memory-unsafe
* unions containing GC pointers are similarly unsafe

Solutions:
- dictionaries containing GC pointers are now wrapped in RootedTraceableBox (even inside other dictionaries)
- instead of using Heap::new, dictionaries containing GC pointers are now initialized to a default value (by deriving Default) and mutated one field at a time
- dictionaries containing GC pointers are marked #[must_root]
- FromJSVal for dictionaries containing GC pointers now returns RootedTraceableBox<Dictionary>
- unions wrap their variants that require rooting in RootedTraceableBox

Rather than attempting to derive Default for all dictionaries, we only do so for the dictionaries that require it. Because some dictionaries that require it inherit from dictionaries that do not, we need to write manual implementations for those parent dictionaries. This is a better option than having to figure out a default value for types like `Root<T>`, which would be required for deriving Default for all dictionaries.

I would still like to come up with an automated test for this, but I figured I would get eyes on this first.

---
- [X] `./mach build -d` does not report any errors
- [X] `./mach test-tidy` does not report any errors
- [X] These changes fix #16952
- [ ] There are tests for these changes

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/servo/17056)
<!-- Reviewable:end -->
This commit is contained in:
bors-servo 2017-09-25 16:07:47 -05:00 committed by GitHub
commit 532dee36c1
11 changed files with 194 additions and 69 deletions

View file

@ -723,9 +723,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if type.nullable(): if type.nullable():
declType = CGWrapper(declType, pre="Option<", post=" >") 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" templateBody = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(value)) => value,\n" " Ok(ConversionResult::Success(value)) => value,\n"
" Ok(ConversionResult::Failure(error)) => {\n" " Ok(ConversionResult::Failure(error)) => {\n"
@ -1048,12 +1045,12 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if defaultValue is None: if defaultValue is None:
default = None default = None
elif isinstance(defaultValue, IDLNullValue): elif isinstance(defaultValue, IDLNullValue):
default = "Heap::new(NullValue())" default = "NullValue()"
elif isinstance(defaultValue, IDLUndefinedValue): elif isinstance(defaultValue, IDLUndefinedValue):
default = "Heap::new(UndefinedValue())" default = "UndefinedValue()"
else: else:
raise TypeError("Can't handle non-null, non-undefined default value here") raise TypeError("Can't handle non-null, non-undefined default value here")
return handleOptional("Heap::new(${val}.get())", declType, default) return handleOptional("${val}.get()", declType, default)
declType = CGGeneric("HandleValue") declType = CGGeneric("HandleValue")
@ -1080,8 +1077,6 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
if isMember in ("Dictionary", "Union"): if isMember in ("Dictionary", "Union"):
declType = CGGeneric("Heap<*mut JSObject>") declType = CGGeneric("Heap<*mut JSObject>")
templateBody = "Heap::new(%s)" % templateBody
default = "Heap::new(%s)" % default
else: else:
# TODO: Need to root somehow # TODO: Need to root somehow
# https://github.com/servo/servo/issues/6382 # https://github.com/servo/servo/issues/6382
@ -1099,9 +1094,8 @@ def getJSToNativeConversionInfo(type, descriptorProvider, failureCode=None,
declType = CGGeneric(typeName) declType = CGGeneric(typeName)
empty = "%s::empty(cx)" % typeName empty = "%s::empty(cx)" % typeName
if isMember != "Dictionary" and type_needs_tracing(type): if type_needs_tracing(type):
declType = CGTemplatedType("RootedTraceableBox", declType) declType = CGTemplatedType("RootedTraceableBox", declType)
empty = "RootedTraceableBox::new(%s)" % empty
template = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n" template = ("match FromJSValConvertible::from_jsval(cx, ${val}, ()) {\n"
" Ok(ConversionResult::Success(dictionary)) => dictionary,\n" " Ok(ConversionResult::Success(dictionary)) => dictionary,\n"
@ -1427,6 +1421,8 @@ def getRetvalDeclarationForType(returnType, descriptorProvider):
nullable = returnType.nullable() nullable = returnType.nullable()
dictName = returnType.inner.name if nullable else returnType.name dictName = returnType.inner.name if nullable else returnType.name
result = CGGeneric(dictName) result = CGGeneric(dictName)
if type_needs_tracing(returnType):
result = CGWrapper(result, pre="RootedTraceableBox<", post=">")
if nullable: if nullable:
result = CGWrapper(result, pre="Option<", post=">") result = CGWrapper(result, pre="Option<", post=">")
return result return result
@ -2262,6 +2258,7 @@ def UnionTypes(descriptors, dictionaries, callbacks, typedefs, config):
'dom::bindings::str::ByteString', 'dom::bindings::str::ByteString',
'dom::bindings::str::DOMString', 'dom::bindings::str::DOMString',
'dom::bindings::str::USVString', 'dom::bindings::str::USVString',
'dom::bindings::trace::RootedTraceableBox',
'dom::types::*', 'dom::types::*',
'js::error::throw_type_error', 'js::error::throw_type_error',
'js::jsapi::HandleValue', 'js::jsapi::HandleValue',
@ -4014,27 +4011,36 @@ pub enum %s {
pairs = ",\n ".join(['("%s", super::%s::%s)' % (val, ident, getEnumValueName(val)) for val in enum.values()]) pairs = ",\n ".join(['("%s", super::%s::%s)' % (val, ident, getEnumValueName(val)) for val in enum.values()])
inner = """\ inner = string.Template("""\
use dom::bindings::conversions::ToJSValConvertible; use dom::bindings::conversions::ToJSValConvertible;
use js::jsapi::{JSContext, MutableHandleValue}; use js::jsapi::{JSContext, MutableHandleValue};
use js::jsval::JSVal; use js::jsval::JSVal;
pub const pairs: &'static [(&'static str, super::%s)] = &[ pub const pairs: &'static [(&'static str, super::${ident})] = &[
%s, ${pairs},
]; ];
impl super::%s { impl super::${ident} {
pub fn as_str(&self) -> &'static str { pub fn as_str(&self) -> &'static str {
pairs[*self as usize].0 pairs[*self as usize].0
} }
} }
impl ToJSValConvertible for super::%s { impl Default for super::${ident} {
fn default() -> super::${ident} {
pairs[0].1
}
}
impl ToJSValConvertible for super::${ident} {
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) { unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
pairs[*self as usize].0.to_jsval(cx, rval); pairs[*self as usize].0.to_jsval(cx, rval);
} }
} }
""" % (ident, pairs, ident, ident) """).substitute({
'ident': ident,
'pairs': pairs
})
self.cgRoot = CGList([ self.cgRoot = CGList([
CGGeneric(decl), CGGeneric(decl),
CGNamespace.build([ident + "Values"], CGNamespace.build([ident + "Values"],
@ -4132,15 +4138,23 @@ class CGUnionStruct(CGThing):
self.type = type self.type = type
self.descriptorProvider = descriptorProvider self.descriptorProvider = descriptorProvider
def membersNeedTracing(self):
for t in self.type.flatMemberTypes:
if type_needs_tracing(t):
return True
return False
def define(self): 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) self.type.flatMemberTypes)
enumValues = [ 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 = [ enumConversions = [
" %s::%s(ref inner) => inner.to_jsval(cx, rval)," " %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 ("""\ return ("""\
#[derive(JSTraceable)] #[derive(JSTraceable)]
@ -4167,6 +4181,12 @@ class CGUnionConversionStruct(CGThing):
self.type = type self.type = type
self.descriptorProvider = descriptorProvider 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): def from_jsval(self):
memberTypes = self.type.flatMemberTypes memberTypes = self.type.flatMemberTypes
names = [] names = []
@ -4310,13 +4330,20 @@ class CGUnionConversionStruct(CGThing):
def try_method(self, t): def try_method(self, t):
templateVars = getUnionTypeTemplateVars(t, self.descriptorProvider) 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"] jsConversion = templateVars["jsConversion"]
# Any code to convert to Object is unused, since we're already converting
# from an Object value.
if t.name == 'Object':
return CGGeneric('')
return CGWrapper( return CGWrapper(
CGIndenter(jsConversion, 4), CGIndenter(jsConversion, 4),
# TryConvertToObject is unused, but not generating it while generating others is tricky. pre="unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n"
pre="#[allow(dead_code)] unsafe fn TryConvertTo%s(cx: *mut JSContext, value: HandleValue) -> %s {\n"
% (t.name, returnType), % (t.name, returnType),
post="\n}") post="\n}")
@ -5682,6 +5709,7 @@ def generate_imports(config, cgthings, descriptors, callbacks=None, dictionaries
'dom::bindings::iterable::Iterable', 'dom::bindings::iterable::Iterable',
'dom::bindings::iterable::IteratorType', 'dom::bindings::iterable::IteratorType',
'dom::bindings::js::JS', 'dom::bindings::js::JS',
'dom::bindings::js::OptionalHeapSetter',
'dom::bindings::js::Root', 'dom::bindings::js::Root',
'dom::bindings::js::RootedReference', 'dom::bindings::js::RootedReference',
'dom::bindings::namespace::NamespaceObjectClass', 'dom::bindings::namespace::NamespaceObjectClass',
@ -6018,18 +6046,27 @@ class CGDictionary(CGThing):
(self.makeMemberName(m[0].identifier.name), self.getMemberType(m)) (self.makeMemberName(m[0].identifier.name), self.getMemberType(m))
for m in self.memberInfo] for m in self.memberInfo]
derive = ["JSTraceable"]
mustRoot = ""
if self.membersNeedTracing():
mustRoot = "#[must_root]\n"
derive += ["Default"]
return (string.Template( return (string.Template(
"#[derive(JSTraceable)]\n" "#[derive(${derive})]\n"
"${mustRoot}" +
"pub struct ${selfName} {\n" + "pub struct ${selfName} {\n" +
"${inheritance}" + "${inheritance}" +
"\n".join(memberDecls) + "\n" + "\n".join(memberDecls) + "\n" +
"}").substitute({"selfName": self.makeClassName(d), "}").substitute({"selfName": self.makeClassName(d),
"inheritance": inheritance})) "inheritance": inheritance,
"mustRoot": mustRoot,
"derive": ', '.join(derive)}))
def impl(self): def impl(self):
d = self.dictionary d = self.dictionary
if d.parent: if d.parent:
initParent = ("parent: {\n" initParent = ("{\n"
" match try!(%s::%s::new(cx, val)) {\n" " match try!(%s::%s::new(cx, val)) {\n"
" ConversionResult::Success(v) => v,\n" " ConversionResult::Success(v) => v,\n"
" ConversionResult::Failure(error) => {\n" " ConversionResult::Failure(error) => {\n"
@ -6037,16 +6074,20 @@ class CGDictionary(CGThing):
" return Err(());\n" " return Err(());\n"
" }\n" " }\n"
" }\n" " }\n"
"},\n" % (self.makeModuleName(d.parent), "}" % (self.makeModuleName(d.parent),
self.makeClassName(d.parent))) self.makeClassName(d.parent)))
else: else:
initParent = "" initParent = ""
def memberInit(memberInfo): def memberInit(memberInfo, isInitial):
member, _ = memberInfo member, _ = memberInfo
name = self.makeMemberName(member.identifier.name) name = self.makeMemberName(member.identifier.name)
conversion = self.getMemberConversion(memberInfo, member.type) conversion = self.getMemberConversion(memberInfo, member.type)
return CGGeneric("%s: %s,\n" % (name, conversion.define())) if isInitial:
return CGGeneric("%s: %s,\n" % (name, conversion.define()))
if member.type.isAny() or member.type.isObject():
return CGGeneric("dictionary.%s.set(%s);\n" % (name, conversion.define()))
return CGGeneric("dictionary.%s = %s;\n" % (name, conversion.define()))
def varInsert(varName, dictionaryName): def varInsert(varName, dictionaryName):
insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n" insertion = ("rooted!(in(cx) let mut %s_js = UndefinedValue());\n"
@ -6066,19 +6107,32 @@ class CGDictionary(CGThing):
(name, name, varInsert(name, member.identifier.name).define())) (name, name, varInsert(name, member.identifier.name).define()))
return CGGeneric("%s\n" % insertion.define()) return CGGeneric("%s\n" % insertion.define())
memberInits = CGList([memberInit(m) for m in self.memberInfo])
memberInserts = CGList([memberInsert(m) for m in self.memberInfo]) memberInserts = CGList([memberInsert(m) for m in self.memberInfo])
selfName = self.makeClassName(d)
if self.membersNeedTracing():
actualType = "RootedTraceableBox<%s>" % selfName
preInitial = "let mut dictionary = RootedTraceableBox::new(%s::default());\n" % selfName
initParent = initParent = ("dictionary.parent = %s;\n" % initParent) if initParent else ""
memberInits = CGList([memberInit(m, False) for m in self.memberInfo])
postInitial = ""
else:
actualType = selfName
preInitial = "let dictionary = %s {\n" % selfName
postInitial = "};\n"
initParent = ("parent: %s,\n" % initParent) if initParent else ""
memberInits = CGList([memberInit(m, True) for m in self.memberInfo])
return string.Template( return string.Template(
"impl ${selfName} {\n" "impl ${selfName} {\n"
" pub unsafe fn empty(cx: *mut JSContext) -> ${selfName} {\n" " pub unsafe fn empty(cx: *mut JSContext) -> ${actualType} {\n"
" match ${selfName}::new(cx, HandleValue::null()) {\n" " match ${selfName}::new(cx, HandleValue::null()) {\n"
" Ok(ConversionResult::Success(v)) => v,\n" " Ok(ConversionResult::Success(v)) => v,\n"
" _ => unreachable!(),\n" " _ => unreachable!(),\n"
" }\n" " }\n"
" }\n" " }\n"
" pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n" " pub unsafe fn new(cx: *mut JSContext, val: HandleValue) \n"
" -> Result<ConversionResult<${selfName}>, ()> {\n" " -> Result<ConversionResult<${actualType}>, ()> {\n"
" let object = if val.get().is_null_or_undefined() {\n" " let object = if val.get().is_null_or_undefined() {\n"
" ptr::null_mut()\n" " ptr::null_mut()\n"
" } else if val.get().is_object() {\n" " } else if val.get().is_object() {\n"
@ -6088,17 +6142,18 @@ class CGDictionary(CGThing):
" return Err(());\n" " return Err(());\n"
" };\n" " };\n"
" rooted!(in(cx) let object = object);\n" " rooted!(in(cx) let object = object);\n"
" Ok(ConversionResult::Success(${selfName} {\n" "${preInitial}"
"${initParent}" "${initParent}"
"${initMembers}" "${initMembers}"
" }))\n" "${postInitial}"
" Ok(ConversionResult::Success(dictionary))\n"
" }\n" " }\n"
"}\n" "}\n"
"\n" "\n"
"impl FromJSValConvertible for ${selfName} {\n" "impl FromJSValConvertible for ${actualType} {\n"
" type Config = ();\n" " type Config = ();\n"
" unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n" " unsafe fn from_jsval(cx: *mut JSContext, value: HandleValue, _option: ())\n"
" -> Result<ConversionResult<${selfName}>, ()> {\n" " -> Result<ConversionResult<${actualType}>, ()> {\n"
" ${selfName}::new(cx, value)\n" " ${selfName}::new(cx, value)\n"
" }\n" " }\n"
"}\n" "}\n"
@ -6110,12 +6165,21 @@ class CGDictionary(CGThing):
" rval.set(ObjectOrNullValue(obj.get()))\n" " rval.set(ObjectOrNullValue(obj.get()))\n"
" }\n" " }\n"
"}\n").substitute({ "}\n").substitute({
"selfName": self.makeClassName(d), "selfName": selfName,
"actualType": actualType,
"initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(), "initParent": CGIndenter(CGGeneric(initParent), indentLevel=12).define(),
"initMembers": CGIndenter(memberInits, indentLevel=12).define(), "initMembers": CGIndenter(memberInits, indentLevel=12).define(),
"insertMembers": CGIndenter(memberInserts, indentLevel=8).define(), "insertMembers": CGIndenter(memberInserts, indentLevel=8).define(),
"preInitial": CGIndenter(CGGeneric(preInitial), indentLevel=12).define(),
"postInitial": CGIndenter(CGGeneric(postInitial), indentLevel=12).define(),
}) })
def membersNeedTracing(self):
for member, _ in self.memberInfo:
if type_needs_tracing(member.type):
return True
return False
@staticmethod @staticmethod
def makeDictionaryName(dictionary): def makeDictionaryName(dictionary):
return dictionary.identifier.name return dictionary.identifier.name
@ -6638,7 +6702,7 @@ class CallbackMember(CGNativeMember):
replacements["argCount"] = self.argCountStr replacements["argCount"] = self.argCountStr
replacements["argvDecl"] = string.Template( replacements["argvDecl"] = string.Template(
"rooted_vec!(let mut argv);\n" "rooted_vec!(let mut argv);\n"
"argv.extend((0..${argCount}).map(|_| Heap::new(UndefinedValue())));\n" "argv.extend((0..${argCount}).map(|_| Heap::default()));\n"
).substitute(replacements) ).substitute(replacements)
else: else:
# Avoid weird 0-sized arrays # Avoid weird 0-sized arrays
@ -6713,7 +6777,11 @@ class CallbackMember(CGNativeMember):
conversion = wrapForType( conversion = wrapForType(
"argv_root.handle_mut()", result=argval, "argv_root.handle_mut()", result=argval,
successCode="argv[%s] = Heap::new(argv_root.get());" % jsvalIndex, successCode=("{\n" +
"let arg = &mut argv[%s];\n" +
"*arg = Heap::default();\n" +
"arg.set(argv_root.get());\n" +
"}") % jsvalIndex,
pre="rooted!(in(cx) let mut argv_root = UndefinedValue());") pre="rooted!(in(cx) let mut argv_root = UndefinedValue());")
if arg.variadic: if arg.variadic:
conversion = string.Template( conversion = string.Template(
@ -6729,7 +6797,7 @@ class CallbackMember(CGNativeMember):
" // This is our current trailing argument; reduce argc\n" " // This is our current trailing argument; reduce argc\n"
" argc -= 1;\n" " argc -= 1;\n"
"} else {\n" "} else {\n"
" argv[%d] = Heap::new(UndefinedValue());\n" " argv[%d] = Heap::default();\n"
"}" % (i + 1, i)) "}" % (i + 1, i))
return conversion return conversion

View file

@ -116,19 +116,11 @@ impl <T: DomObject + IDLInterface> FromJSValConvertible for Root<T> {
} }
} }
impl <T: FromJSValConvertible + JSTraceable> FromJSValConvertible for RootedTraceableBox<T> { impl<T: ToJSValConvertible + JSTraceable> ToJSValConvertible for RootedTraceableBox<T> {
type Config = T::Config; #[inline]
unsafe fn to_jsval(&self, cx: *mut JSContext, rval: MutableHandleValue) {
unsafe fn from_jsval(cx: *mut JSContext, let value = &**self;
value: HandleValue, value.to_jsval(cx, rval);
config: Self::Config)
-> Result<ConversionResult<Self>, ()> {
T::from_jsval(cx, value, config).map(|result| {
match result {
ConversionResult::Success(v) => ConversionResult::Success(RootedTraceableBox::new(v)),
ConversionResult::Failure(e) => ConversionResult::Failure(e),
}
})
} }
} }

View file

@ -132,7 +132,10 @@ fn key_and_value_return(cx: *mut JSContext,
value: HandleValue) -> Fallible<()> { value: HandleValue) -> Fallible<()> {
let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) }; let mut dict = unsafe { IterableKeyAndValueResult::empty(cx) };
dict.done = false; dict.done = false;
dict.value = Some(vec![Heap::new(key.get()), Heap::new(value.get())]); let values = vec![Heap::default(), Heap::default()];
values[0].set(key.get());
values[1].set(value.get());
dict.value = Some(values);
rooted!(in(cx) let mut dict_value = UndefinedValue()); rooted!(in(cx) let mut dict_value = UndefinedValue());
unsafe { unsafe {
dict.to_jsval(cx, dict_value.handle_mut()); dict.to_jsval(cx, dict_value.handle_mut());

View file

@ -31,7 +31,8 @@ use dom::bindings::trace::JSTraceable;
use dom::bindings::trace::trace_reflector; use dom::bindings::trace::trace_reflector;
use dom::node::Node; use dom::node::Node;
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use js::jsapi::{JSObject, JSTracer}; use js::jsapi::{JSObject, JSTracer, Heap};
use js::rust::GCMethods;
use mitochondria::OnceCell; use mitochondria::OnceCell;
use script_layout_interface::TrustedNodeAddress; use script_layout_interface::TrustedNodeAddress;
use script_thread::STACK_ROOTS; use script_thread::STACK_ROOTS;
@ -654,3 +655,29 @@ unsafe impl<T: DomObject> JSTraceable for Root<T> {
// Already traced. // Already traced.
} }
} }
/// Helper trait for safer manipulations of Option<Heap<T>> values.
pub trait OptionalHeapSetter {
type Value;
/// Update this optional heap value with a new value.
fn set(&mut self, v: Option<Self::Value>);
}
impl<T: GCMethods + Copy> OptionalHeapSetter for Option<Heap<T>> where Heap<T>: Default {
type Value = T;
fn set(&mut self, v: Option<T>) {
let v = match v {
None => {
*self = None;
return;
}
Some(v) => v,
};
if self.is_none() {
*self = Some(Heap::default());
}
self.as_ref().unwrap().set(v);
}
}

View file

@ -6,6 +6,7 @@
use heapsize::HeapSizeOf; use heapsize::HeapSizeOf;
use num_traits::Float; use num_traits::Float;
use std::default::Default;
use std::ops::Deref; use std::ops::Deref;
/// Encapsulates the IDL restricted float type. /// Encapsulates the IDL restricted float type.
@ -45,3 +46,9 @@ impl<T: Float + HeapSizeOf> HeapSizeOf for Finite<T> {
(**self).heap_size_of_children() (**self).heap_size_of_children()
} }
} }
impl<T: Float + Default> Default for Finite<T> {
fn default() -> Finite<T> {
Finite::wrap(T::default())
}
}

View file

@ -9,6 +9,7 @@ use html5ever::{LocalName, Namespace};
use servo_atoms::Atom; use servo_atoms::Atom;
use std::ascii::AsciiExt; use std::ascii::AsciiExt;
use std::borrow::{Borrow, Cow, ToOwned}; use std::borrow::{Borrow, Cow, ToOwned};
use std::default::Default;
use std::fmt; use std::fmt;
use std::hash::{Hash, Hasher}; use std::hash::{Hash, Hasher};
use std::marker::PhantomData; use std::marker::PhantomData;
@ -18,7 +19,7 @@ use std::str;
use std::str::{Bytes, FromStr}; use std::str::{Bytes, FromStr};
/// Encapsulates the IDL `ByteString` type. /// Encapsulates the IDL `ByteString` type.
#[derive(Clone, Debug, Eq, HeapSizeOf, JSTraceable, PartialEq)] #[derive(Clone, Debug, Default, Eq, HeapSizeOf, JSTraceable, PartialEq)]
pub struct ByteString(Vec<u8>); pub struct ByteString(Vec<u8>);
impl ByteString { impl ByteString {
@ -77,7 +78,7 @@ impl ops::Deref for ByteString {
/// A string that is constructed from a UCS-2 buffer by replacing invalid code /// A string that is constructed from a UCS-2 buffer by replacing invalid code
/// points with the replacement character. /// points with the replacement character.
#[derive(Clone, HeapSizeOf)] #[derive(Clone, Default, HeapSizeOf)]
pub struct USVString(pub String); pub struct USVString(pub String);

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 GC things like *mut JSObject or JSVal, use rooted!.
/// If you have an arbitrary number of DomObjects to root, use rooted_vec!. /// If you have an arbitrary number of DomObjects to root, use rooted_vec!.
/// If you know what you're doing, use this. /// If you know what you're doing, use this.
#[allow_unrooted_interior]
pub struct RootedTraceableBox<T: 'static + JSTraceable> { pub struct RootedTraceableBox<T: 'static + JSTraceable> {
ptr: *mut T, ptr: *mut T,
} }
@ -768,6 +769,12 @@ impl<T: JSTraceable + 'static> RootedTraceableBox<T> {
} }
} }
impl<T: JSTraceable + Default> Default for RootedTraceableBox<T> {
fn default() -> RootedTraceableBox<T> {
RootedTraceableBox::new(T::default())
}
}
impl<T: JSTraceable> Deref for RootedTraceableBox<T> { impl<T: JSTraceable> Deref for RootedTraceableBox<T> {
type Target = T; type Target = T;
fn deref(&self) -> &T { fn deref(&self) -> &T {

View file

@ -522,3 +522,12 @@ fn inner_invoke(window: Option<&Window>,
// Step 3. // Step 3.
found found
} }
impl Default for EventBinding::EventInit {
fn default() -> EventBinding::EventInit {
EventBinding::EventInit {
bubbles: false,
cancelable: false,
}
}
}

View file

@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use dom::bindings::codegen::Bindings::EventBinding::EventMethods; use dom::bindings::codegen::Bindings::EventBinding::{self, EventMethods};
use dom::bindings::codegen::Bindings::ExtendableEventBinding; use dom::bindings::codegen::Bindings::ExtendableEventBinding;
use dom::bindings::error::{Error, ErrorResult, Fallible}; use dom::bindings::error::{Error, ErrorResult, Fallible};
use dom::bindings::inheritance::Castable; use dom::bindings::inheritance::Castable;
@ -67,3 +67,11 @@ impl ExtendableEvent {
self.event.IsTrusted() self.event.IsTrusted()
} }
} }
impl Default for ExtendableEventBinding::ExtendableEventInit {
fn default() -> ExtendableEventBinding::ExtendableEventInit {
ExtendableEventBinding::ExtendableEventInit {
parent: EventBinding::EventInit::default(),
}
}
}

View file

@ -13,6 +13,7 @@ use dom::bindings::js::{MutNullableJS, Root};
use dom::bindings::refcounted::Trusted; use dom::bindings::refcounted::Trusted;
use dom::bindings::reflector::{DomObject, reflect_dom_object}; use dom::bindings::reflector::{DomObject, reflect_dom_object};
use dom::bindings::str::DOMString; use dom::bindings::str::DOMString;
use dom::bindings::trace::RootedTraceableBox;
use dom::blob::Blob; use dom::blob::Blob;
use dom::domexception::{DOMErrorName, DOMException}; use dom::domexception::{DOMErrorName, DOMException};
use dom::event::{Event, EventBubbles, EventCancelable}; use dom::event::{Event, EventBubbles, EventCancelable};
@ -338,7 +339,9 @@ impl FileReaderMethods for FileReader {
FileReaderResult::String(ref string) => FileReaderResult::String(ref string) =>
StringOrObject::String(string.clone()), StringOrObject::String(string.clone()),
FileReaderResult::ArrayBuffer(ref arr_buffer) => { FileReaderResult::ArrayBuffer(ref arr_buffer) => {
StringOrObject::Object(Heap::new((*arr_buffer.ptr.get()).to_object())) let result = RootedTraceableBox::new(Heap::default());
result.set((*arr_buffer.ptr.get()).to_object());
StringOrObject::Object(result)
} }
}) })
} }

View file

@ -338,14 +338,14 @@ impl TestBindingMethods for TestBinding {
Some(ByteStringOrLong::ByteString(ByteString::new(vec!()))) Some(ByteStringOrLong::ByteString(ByteString::new(vec!())))
} }
fn ReceiveNullableSequence(&self) -> Option<Vec<i32>> { Some(vec![1]) } fn ReceiveNullableSequence(&self) -> Option<Vec<i32>> { Some(vec![1]) }
fn ReceiveTestDictionaryWithSuccessOnKeyword(&self) -> TestDictionary { fn ReceiveTestDictionaryWithSuccessOnKeyword(&self) -> RootedTraceableBox<TestDictionary> {
TestDictionary { RootedTraceableBox::new(TestDictionary {
anyValue: Heap::new(NullValue()), anyValue: Heap::default(),
booleanValue: None, booleanValue: None,
byteValue: None, byteValue: None,
dict: TestDictionaryDefaults { dict: RootedTraceableBox::new(TestDictionaryDefaults {
UnrestrictedDoubleValue: 0.0, UnrestrictedDoubleValue: 0.0,
anyValue: Heap::new(NullValue()), anyValue: Heap::default(),
booleanValue: false, booleanValue: false,
bytestringValue: ByteString::new(vec![]), bytestringValue: ByteString::new(vec![]),
byteValue: 0, byteValue: 0,
@ -361,7 +361,7 @@ impl TestBindingMethods for TestBinding {
nullableFloatValue: None, nullableFloatValue: None,
nullableLongLongValue: None, nullableLongLongValue: None,
nullableLongValue: None, nullableLongValue: None,
nullableObjectValue: Heap::new(ptr::null_mut()), nullableObjectValue: Heap::default(),
nullableOctetValue: None, nullableOctetValue: None,
nullableShortValue: None, nullableShortValue: None,
nullableStringValue: None, nullableStringValue: None,
@ -379,7 +379,7 @@ impl TestBindingMethods for TestBinding {
unsignedLongValue: 0, unsignedLongValue: 0,
unsignedShortValue: 0, unsignedShortValue: 0,
usvstringValue: USVString("".to_owned()), usvstringValue: USVString("".to_owned()),
}, }),
doubleValue: None, doubleValue: None,
enumValue: None, enumValue: None,
floatValue: None, floatValue: None,
@ -401,7 +401,7 @@ impl TestBindingMethods for TestBinding {
usvstringValue: None, usvstringValue: None,
nonRequiredNullable: None, nonRequiredNullable: None,
nonRequiredNullable2: Some(None), // null nonRequiredNullable2: Some(None), // null
} })
} }
fn DictMatchesPassedValues(&self, arg: RootedTraceableBox<TestDictionary>) -> bool { fn DictMatchesPassedValues(&self, arg: RootedTraceableBox<TestDictionary>) -> bool {
@ -436,9 +436,9 @@ impl TestBindingMethods for TestBinding {
fn PassUnion6(&self, _: UnsignedLongOrBoolean) {} fn PassUnion6(&self, _: UnsignedLongOrBoolean) {}
fn PassUnion7(&self, _: StringSequenceOrUnsignedLong) {} fn PassUnion7(&self, _: StringSequenceOrUnsignedLong) {}
fn PassUnion8(&self, _: ByteStringSequenceOrLong) {} fn PassUnion8(&self, _: ByteStringSequenceOrLong) {}
fn PassUnion9(&self, _: RootedTraceableBox<UnionTypes::TestDictionaryOrLong>) {} fn PassUnion9(&self, _: UnionTypes::TestDictionaryOrLong) {}
#[allow(unsafe_code)] #[allow(unsafe_code)]
unsafe fn PassUnion10(&self, _: *mut JSContext, _: RootedTraceableBox<UnionTypes::StringOrObject>) {} unsafe fn PassUnion10(&self, _: *mut JSContext, _: UnionTypes::StringOrObject) {}
fn PassUnionWithTypedef(&self, _: DocumentOrTestTypedef) {} fn PassUnionWithTypedef(&self, _: DocumentOrTestTypedef) {}
fn PassUnionWithTypedef2(&self, _: LongSequenceOrTestTypedef) {} fn PassUnionWithTypedef2(&self, _: LongSequenceOrTestTypedef) {}
#[allow(unsafe_code)] #[allow(unsafe_code)]