Bug 928336. Make defining unforgeable properties on objects faster by just copying them from an unforgeable holder object. r=peterv

This commit is contained in:
Boris Zbarsky 2015-03-03 21:01:58 -05:00
parent 0b079cb32c
commit fc656d4547
5 changed files with 204 additions and 98 deletions

View File

@ -222,7 +222,7 @@ UnwrapDOMObjectToISupports(JSObject* aObject)
return nullptr; return nullptr;
} }
return UnwrapDOMObject<nsISupports>(aObject); return UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObject);
} }
inline bool inline bool
@ -3036,8 +3036,10 @@ struct CreateGlobalOptions<nsGlobalWindow>
nsresult nsresult
RegisterDOMNames(); RegisterDOMNames();
// The return value is whatever the ProtoHandleGetter we used
// returned. This should be the DOM prototype for the global.
template <class T, ProtoHandleGetter GetProto> template <class T, ProtoHandleGetter GetProto>
bool JS::Handle<JSObject*>
CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache, CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
const JSClass* aClass, JS::CompartmentOptions& aOptions, const JSClass* aClass, JS::CompartmentOptions& aOptions,
JSPrincipals* aPrincipal, bool aInitStandardClasses, JSPrincipals* aPrincipal, bool aInitStandardClasses,
@ -3049,7 +3051,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
JS::DontFireOnNewGlobalHook, aOptions)); JS::DontFireOnNewGlobalHook, aOptions));
if (!aGlobal) { if (!aGlobal) {
NS_WARNING("Failed to create global"); NS_WARNING("Failed to create global");
return false; return JS::NullPtr();
} }
JSAutoCompartment ac(aCx, aGlobal); JSAutoCompartment ac(aCx, aGlobal);
@ -3064,7 +3066,7 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
CreateGlobalOptions<T>::ProtoAndIfaceCacheKind); CreateGlobalOptions<T>::ProtoAndIfaceCacheKind);
if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) { if (!CreateGlobalOptions<T>::PostCreateGlobal(aCx, aGlobal)) {
return false; return JS::NullPtr();
} }
} }
@ -3072,16 +3074,16 @@ CreateGlobal(JSContext* aCx, T* aNative, nsWrapperCache* aCache,
!CreateGlobalOptions<T>::ForceInitStandardClassesToFalse && !CreateGlobalOptions<T>::ForceInitStandardClassesToFalse &&
!JS_InitStandardClasses(aCx, aGlobal)) { !JS_InitStandardClasses(aCx, aGlobal)) {
NS_WARNING("Failed to init standard classes"); NS_WARNING("Failed to init standard classes");
return false; return JS::NullPtr();
} }
JS::Handle<JSObject*> proto = GetProto(aCx, aGlobal); JS::Handle<JSObject*> proto = GetProto(aCx, aGlobal);
if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) { if (!proto || !JS_SplicePrototype(aCx, aGlobal, proto)) {
NS_WARNING("Failed to set proto"); NS_WARNING("Failed to set proto");
return false; return JS::NullPtr();
} }
return true; return proto;
} }
/* /*

View File

@ -12,7 +12,7 @@ import textwrap
import functools import functools
from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary from WebIDL import BuiltinTypes, IDLBuiltinType, IDLNullValue, IDLSequenceType, IDLType, IDLAttribute, IDLInterfaceMember, IDLUndefinedValue, IDLEmptySequenceValue, IDLDictionary
from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor from Configuration import NoSuchDescriptorError, getTypesFromDescriptor, getTypesFromDictionary, getTypesFromCallback, getAllTypes, Descriptor, MemberIsUnforgeable
AUTOGENERATED_WARNING_COMMENT = \ AUTOGENERATED_WARNING_COMMENT = \
"/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n" "/* THIS FILE IS AUTOGENERATED - DO NOT EDIT */\n\n"
@ -552,22 +552,6 @@ def PrototypeIDAndDepth(descriptor):
return (prototypeID, depth) return (prototypeID, depth)
def MemberIsUnforgeable(member, descriptor):
# Note: "or" and "and" return either their LHS or RHS, not
# necessarily booleans. Make sure to return a boolean from this
# method, because callers will compare its return value to
# booleans.
return bool((member.isAttr() or member.isMethod()) and
not member.isStatic() and
(member.isUnforgeable() or
descriptor.interface.getExtendedAttribute("Unforgeable")))
def HasUnforgeableMembers(descriptor):
return any(MemberIsUnforgeable(m, descriptor) for m in
descriptor.interface.members)
def InterfacePrototypeObjectProtoGetter(descriptor): def InterfacePrototypeObjectProtoGetter(descriptor):
""" """
Returns a tuple with two elements: Returns a tuple with two elements:
@ -611,6 +595,8 @@ class CGPrototypeJSClass(CGThing):
def define(self): def define(self):
prototypeID, depth = PrototypeIDAndDepth(self.descriptor) prototypeID, depth = PrototypeIDAndDepth(self.descriptor)
slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE" slotCount = "DOM_INTERFACE_PROTO_SLOTS_BASE"
if self.descriptor.hasUnforgeableMembers:
slotCount += " + 1 /* slot for the JSObject holding the unforgeable properties */"
(protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor) (protoGetter, _) = InterfacePrototypeObjectProtoGetter(self.descriptor)
type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype" type = "eGlobalInterfacePrototype" if self.descriptor.isGlobal() else "eInterfacePrototype"
return fill( return fill(
@ -1524,8 +1510,9 @@ class CGAddPropertyHook(CGAbstractClassHook):
def generate_code(self): def generate_code(self):
assert self.descriptor.wrapperCache assert self.descriptor.wrapperCache
return dedent(""" return dedent("""
// We don't want to preserve if we don't have a wrapper. // We don't want to preserve if we don't have a wrapper, and we
if (self->GetWrapperPreserveColor()) { // obviously can't preserve if we're not initialized.
if (self && self->GetWrapperPreserveColor()) {
PreserveWrapper(self); PreserveWrapper(self);
} }
return true; return true;
@ -2634,6 +2621,55 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
else: else:
prefCache = None prefCache = None
if self.descriptor.hasUnforgeableMembers:
assert needInterfacePrototypeObject
# We want to use the same JSClass and prototype as the object we'll
# end up defining the unforgeable properties on in the end, so that
# we can use JS_InitializePropertiesFromCompatibleNativeObject to do
# a fast copy. In the case of proxies that's null, because the
# expando object is a vanilla object, but in the case of other DOM
# objects it's whatever our class is.
#
# Also, for a global we can't use the global's class; just use
# nullpr and when we do the copy off the holder we'll take a slower
# path. This also means that we don't need to worry about matching
# the prototype.
if self.descriptor.proxy or self.descriptor.isGlobal():
holderClass = "nullptr"
holderProto = "nullptr"
else:
holderClass = "Class.ToJSClass()"
holderProto = "*protoCache"
failureCode = dedent(
"""
*protoCache = nullptr;
if (interfaceCache) {
*interfaceCache = nullptr;
}
return;
""")
createUnforgeableHolder = CGGeneric(fill(
"""
JS::Rooted<JSObject*> unforgeableHolder(aCx);
{
JS::Rooted<JSObject*> holderProto(aCx, ${holderProto});
unforgeableHolder = JS_NewObjectWithoutMetadata(aCx, ${holderClass}, holderProto);
if (!unforgeableHolder) {
$*{failureCode}
}
}
""",
holderProto=holderProto,
holderClass=holderClass,
failureCode=failureCode))
defineUnforgeables = InitUnforgeablePropertiesOnHolder(self.descriptor,
self.properties,
failureCode)
createUnforgeableHolder = CGList(
[createUnforgeableHolder, defineUnforgeables])
else:
createUnforgeableHolder = None
getParentProto = fill( getParentProto = fill(
""" """
JS::${type}<JSObject*> parentProto(${getParentProto}); JS::${type}<JSObject*> parentProto(${getParentProto});
@ -2699,10 +2735,12 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
call = fill( call = fill(
""" """
JS::Heap<JSObject*>* protoCache = ${protoCache};
JS::Heap<JSObject*>* interfaceCache = ${interfaceCache};
dom::CreateInterfaceObjects(aCx, aGlobal, parentProto, dom::CreateInterfaceObjects(aCx, aGlobal, parentProto,
${protoClass}, ${protoCache}, ${protoClass}, protoCache,
constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors}, constructorProto, ${interfaceClass}, ${constructHookHolder}, ${constructArgs}, ${namedConstructors},
${interfaceCache}, interfaceCache,
${properties}, ${properties},
${chromeProperties}, ${chromeProperties},
${name}, aDefineOnGlobal); ${name}, aDefineOnGlobal);
@ -2718,9 +2756,21 @@ class CGCreateInterfaceObjectsMethod(CGAbstractMethod):
chromeProperties=chromeProperties, chromeProperties=chromeProperties,
name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr") name='"' + self.descriptor.interface.identifier.name + '"' if needInterfaceObject else "nullptr")
if self.descriptor.hasUnforgeableMembers:
assert needInterfacePrototypeObject
setUnforgeableHolder = CGGeneric(fill(
"""
if (*protoCache) {
js::SetReservedSlot(*protoCache, DOM_INTERFACE_PROTO_SLOTS_BASE,
JS::ObjectValue(*unforgeableHolder));
}
""",
name=self.descriptor.name))
else:
setUnforgeableHolder = None
return CGList( return CGList(
[CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds, [CGGeneric(getParentProto), CGGeneric(getConstructorProto), initIds,
prefCache, CGGeneric(call)], prefCache, CGGeneric(call), createUnforgeableHolder, setUnforgeableHolder],
"\n").define() "\n").define()
@ -3023,15 +3073,71 @@ def CreateBindingJSObject(descriptor, properties):
return objDecl + create return objDecl + create
def InitUnforgeableProperties(descriptor, properties, wrapperCache): def InitUnforgeablePropertiesOnHolder(descriptor, properties, failureCode):
""" """
properties is a PropertyArrays instance Define the unforgeable properties on the unforgeable holder for
the interface represented by descriptor.
properties is a PropertyArrays instance.
""" """
unforgeableAttrs = properties.unforgeableAttrs assert (properties.unforgeableAttrs.hasNonChromeOnly() or
if not unforgeableAttrs.hasNonChromeOnly() and not unforgeableAttrs.hasChromeOnly(): properties.unforgeableAttrs.hasChromeOnly() or
properties.unforgeableMethods.hasNonChromeOnly() or
properties.unforgeableMethods.hasChromeOnly)
unforgeables = []
defineUnforgeableAttrs = fill(
"""
if (!DefineUnforgeableAttributes(aCx, unforgeableHolder, %s)) {
$*{failureCode}
}
""",
failureCode=failureCode)
defineUnforgeableMethods = fill(
"""
if (!DefineUnforgeableMethods(aCx, unforgeableHolder, %s)) {
$*{failureCode}
}
""",
failureCode=failureCode)
unforgeableMembers = [
(defineUnforgeableAttrs, properties.unforgeableAttrs),
(defineUnforgeableMethods, properties.unforgeableMethods)
]
for (template, array) in unforgeableMembers:
if array.hasNonChromeOnly():
unforgeables.append(CGGeneric(template % array.variableName(False)))
if array.hasChromeOnly():
unforgeables.append(
CGIfWrapper(CGGeneric(template % array.variableName(True)),
"nsContentUtils::ThreadsafeIsCallerChrome()"))
if descriptor.interface.getExtendedAttribute("Unforgeable"):
# We do our undefined toJSON here, not as a regular property
# because we don't have a concept of value props anywhere in IDL.
unforgeables.append(CGGeneric(fill(
"""
if (!JS_DefineProperty(aCx, unforgeableHolder, "toJSON", JS::UndefinedHandleValue,
JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
$*{failureCode}
}
""",
failureCode=failureCode)))
return CGWrapper(CGList(unforgeables), pre="\n")
def CopyUnforgeablePropertiesToInstance(descriptor, wrapperCache):
"""
Copy the unforgeable properties from the unforgeable holder for
this interface to the instance object we have.
"""
if not descriptor.hasUnforgeableMembers:
return "" return ""
unforgeables = [ copyCode = [
CGGeneric(dedent( CGGeneric(dedent(
""" """
// Important: do unforgeable property setup after we have handed // Important: do unforgeable property setup after we have handed
@ -3053,7 +3159,7 @@ def InitUnforgeableProperties(descriptor, properties, wrapperCache):
# For proxies, we want to define on the expando object, not directly on the # For proxies, we want to define on the expando object, not directly on the
# reflector, so we can make sure we don't get confused by named getters. # reflector, so we can make sure we don't get confused by named getters.
if descriptor.proxy: if descriptor.proxy:
unforgeables.append(CGGeneric(fill( copyCode.append(CGGeneric(fill(
""" """
JS::Rooted<JSObject*> expando(aCx, JS::Rooted<JSObject*> expando(aCx,
DOMProxyHandler::EnsureExpandoObject(aCx, aReflector)); DOMProxyHandler::EnsureExpandoObject(aCx, aReflector));
@ -3067,52 +3173,30 @@ def InitUnforgeableProperties(descriptor, properties, wrapperCache):
else: else:
obj = "aReflector" obj = "aReflector"
defineUnforgeableAttrs = fill( # We can't do the fast copy for globals, because we can't allocate the
# unforgeable holder for those with the right JSClass. Luckily, there
# aren't too many globals being created.
if descriptor.isGlobal():
copyFunc = "JS_CopyPropertiesFrom"
else:
copyFunc = "JS_InitializePropertiesFromCompatibleNativeObject"
copyCode.append(CGGeneric(fill(
""" """
if (!DefineUnforgeableAttributes(aCx, ${obj}, %s)) { // XXXbz Once we allow subclassing, we'll want to make sure that
// this uses the canonical proto, not whatever random passed-in
// proto we end up using for the object.
JS::Rooted<JSObject*> unforgeableHolder(aCx,
&js::GetReservedSlot(proto, DOM_INTERFACE_PROTO_SLOTS_BASE).toObject());
if (!${copyFunc}(aCx, ${obj}, unforgeableHolder)) {
$*{cleanup} $*{cleanup}
return false; return false;
} }
""", """,
copyFunc=copyFunc,
obj=obj, obj=obj,
cleanup=cleanup) cleanup=cleanup)))
defineUnforgeableMethods = fill(
"""
if (!DefineUnforgeableMethods(aCx, ${obj}, %s)) {
$*{cleanup}
return false;
}
""",
obj=obj,
cleanup=cleanup)
unforgeableMembers = [ return CGWrapper(CGList(copyCode), pre="\n").define()
(defineUnforgeableAttrs, properties.unforgeableAttrs),
(defineUnforgeableMethods, properties.unforgeableMethods)
]
for (template, array) in unforgeableMembers:
if array.hasNonChromeOnly():
unforgeables.append(CGGeneric(template % array.variableName(False)))
if array.hasChromeOnly():
unforgeables.append(
CGIfWrapper(CGGeneric(template % array.variableName(True)),
"nsContentUtils::ThreadsafeIsCallerChrome()"))
if descriptor.interface.getExtendedAttribute("Unforgeable"):
# We do our undefined toJSON here, not as a regular property
# because we don't have a concept of value props anywhere.
unforgeables.append(CGGeneric(fill(
"""
if (!JS_DefineProperty(aCx, ${obj}, "toJSON", JS::UndefinedHandleValue,
JSPROP_READONLY | JSPROP_ENUMERATE | JSPROP_PERMANENT)) {
$*{cleanup}
return false;
}
""",
obj=obj,
cleanup=cleanup)))
return CGWrapper(CGList(unforgeables), pre="\n").define()
def AssertInheritanceChain(descriptor): def AssertInheritanceChain(descriptor):
@ -3174,16 +3258,6 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
self.properties = properties self.properties = properties
def definition_body(self): def definition_body(self):
# For proxies, we have to SetWrapper() before we init unforgeables. But
# for non-proxies we'd rather do it the other way around, so our
# unforgeables don't force preservation of the wrapper.
setWrapper = "aCache->SetWrapper(aReflector);\n"
if self.descriptor.proxy:
setWrapperProxy = setWrapper
setWrapperNonProxy = ""
else:
setWrapperProxy = ""
setWrapperNonProxy = setWrapper
return fill( return fill(
""" """
$*{assertion} $*{assertion}
@ -3212,18 +3286,15 @@ class CGWrapWithCacheMethod(CGAbstractMethod):
$*{createObject} $*{createObject}
$*{setWrapperProxy} aCache->SetWrapper(aReflector);
$*{unforgeable} $*{unforgeable}
$*{setWrapperNonProxy}
$*{slots} $*{slots}
creator.InitializationSucceeded(); creator.InitializationSucceeded();
return true; return true;
""", """,
assertion=AssertInheritanceChain(self.descriptor), assertion=AssertInheritanceChain(self.descriptor),
createObject=CreateBindingJSObject(self.descriptor, self.properties), createObject=CreateBindingJSObject(self.descriptor, self.properties),
setWrapperProxy=setWrapperProxy, unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, True),
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, True),
setWrapperNonProxy=setWrapperNonProxy,
slots=InitMemberSlots(self.descriptor, True)) slots=InitMemberSlots(self.descriptor, True))
@ -3280,7 +3351,7 @@ class CGWrapNonWrapperCacheMethod(CGAbstractMethod):
""", """,
assertions=AssertInheritanceChain(self.descriptor), assertions=AssertInheritanceChain(self.descriptor),
createObject=CreateBindingJSObject(self.descriptor, self.properties), createObject=CreateBindingJSObject(self.descriptor, self.properties),
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, False), unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, False),
slots=InitMemberSlots(self.descriptor, False)) slots=InitMemberSlots(self.descriptor, False))
@ -3321,13 +3392,23 @@ class CGWrapGlobalMethod(CGAbstractMethod):
else: else:
fireOnNewGlobal = "" fireOnNewGlobal = ""
if self.descriptor.hasUnforgeableMembers:
declareProto = "JS::Handle<JSObject*> proto =\n"
assertProto = (
"MOZ_ASSERT(proto &&\n"
" IsDOMIfaceAndProtoClass(js::GetObjectClass(proto)));\n")
else:
declareProto = ""
assertProto = ""
return fill( return fill(
""" """
$*{assertions} $*{assertions}
MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache), MOZ_ASSERT(ToSupportsIsOnPrimaryInheritanceChain(aObject, aCache),
"nsISupports must be on our primary inheritance chain"); "nsISupports must be on our primary inheritance chain");
CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx, $*{declareProto}
CreateGlobal<${nativeType}, GetProtoObjectHandle>(aCx,
aObject, aObject,
aCache, aCache,
Class.ToJSClass(), Class.ToJSClass(),
@ -3338,6 +3419,7 @@ class CGWrapGlobalMethod(CGAbstractMethod):
if (!aReflector) { if (!aReflector) {
return false; return false;
} }
$*{assertProto}
// aReflector is a new global, so has a new compartment. Enter it // aReflector is a new global, so has a new compartment. Enter it
// before doing anything with it. // before doing anything with it.
@ -3355,9 +3437,11 @@ class CGWrapGlobalMethod(CGAbstractMethod):
""", """,
assertions=AssertInheritanceChain(self.descriptor), assertions=AssertInheritanceChain(self.descriptor),
nativeType=self.descriptor.nativeType, nativeType=self.descriptor.nativeType,
declareProto=declareProto,
assertProto=assertProto,
properties=properties, properties=properties,
chromeProperties=chromeProperties, chromeProperties=chromeProperties,
unforgeable=InitUnforgeableProperties(self.descriptor, self.properties, True), unforgeable=CopyUnforgeablePropertiesToInstance(self.descriptor, True),
slots=InitMemberSlots(self.descriptor, True), slots=InitMemberSlots(self.descriptor, True),
fireOnNewGlobal=fireOnNewGlobal) fireOnNewGlobal=fireOnNewGlobal)
@ -10178,7 +10262,7 @@ class CGDOMJSProxyHandler_defineProperty(ClassMethod):
namedSetter = self.descriptor.operations['NamedSetter'] namedSetter = self.descriptor.operations['NamedSetter']
if namedSetter: if namedSetter:
if HasUnforgeableMembers(self.descriptor): if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a named setter on an interface " raise TypeError("Can't handle a named setter on an interface "
"that has unforgeables. Figure out how that " "that has unforgeables. Figure out how that "
"should work!") "should work!")
@ -10235,7 +10319,7 @@ class CGDOMJSProxyHandler_delete(ClassMethod):
assert type in ("Named", "Indexed") assert type in ("Named", "Indexed")
deleter = self.descriptor.operations[type + 'Deleter'] deleter = self.descriptor.operations[type + 'Deleter']
if deleter: if deleter:
if HasUnforgeableMembers(self.descriptor): if self.descriptor.hasUnforgeableMembers:
raise TypeError("Can't handle a deleter on an interface " raise TypeError("Can't handle a deleter on an interface "
"that has unforgeables. Figure out how " "that has unforgeables. Figure out how "
"that should work!") "that should work!")
@ -10591,7 +10675,7 @@ class CGDOMJSProxyHandler_setCustom(ClassMethod):
if self.descriptor.operations['NamedCreator'] is not namedSetter: if self.descriptor.operations['NamedCreator'] is not namedSetter:
raise ValueError("In interface " + self.descriptor.name + ": " + raise ValueError("In interface " + self.descriptor.name + ": " +
"Can't cope with named setter that is not also a named creator") "Can't cope with named setter that is not also a named creator")
if HasUnforgeableMembers(self.descriptor): if self.descriptor.hasUnforgeableMembers:
raise ValueError("In interface " + self.descriptor.name + ": " + raise ValueError("In interface " + self.descriptor.name + ": " +
"Can't cope with [OverrideBuiltins] and unforgeable members") "Can't cope with [OverrideBuiltins] and unforgeable members")

View File

@ -294,6 +294,18 @@ def methodReturnsJSObject(method):
return False return False
def MemberIsUnforgeable(member, descriptor):
# Note: "or" and "and" return either their LHS or RHS, not
# necessarily booleans. Make sure to return a boolean from this
# method, because callers will compare its return value to
# booleans.
return bool((member.isAttr() or member.isMethod()) and
not member.isStatic() and
(member.isUnforgeable() or
descriptor.interface.getExtendedAttribute("Unforgeable")))
class Descriptor(DescriptorProvider): class Descriptor(DescriptorProvider):
""" """
Represents a single descriptor for an interface. See Bindings.conf. Represents a single descriptor for an interface. See Bindings.conf.
@ -370,6 +382,9 @@ class Descriptor(DescriptorProvider):
self.concrete = (not self.interface.isExternal() and self.concrete = (not self.interface.isExternal() and
not self.interface.isCallback() and not self.interface.isCallback() and
desc.get('concrete', True)) desc.get('concrete', True))
self.hasUnforgeableMembers = (self.concrete and
any(MemberIsUnforgeable(m, self) for m in
self.interface.members))
self.operations = { self.operations = {
'IndexedGetter': None, 'IndexedGetter': None,
'IndexedSetter': None, 'IndexedSetter': None,

View File

@ -26,7 +26,8 @@
#define DOM_INTERFACE_SLOTS_BASE 0 #define DOM_INTERFACE_SLOTS_BASE 0
// Interface prototype objects store a number of reserved slots equal to // Interface prototype objects store a number of reserved slots equal to
// DOM_INTERFACE_PROTO_SLOTS_BASE. // DOM_INTERFACE_PROTO_SLOTS_BASE or DOM_INTERFACE_PROTO_SLOTS_BASE + 1 if a
// slot for the unforgeable holder is needed.
#define DOM_INTERFACE_PROTO_SLOTS_BASE 0 #define DOM_INTERFACE_PROTO_SLOTS_BASE 0
#endif /* mozilla_dom_DOMSlots_h */ #endif /* mozilla_dom_DOMSlots_h */

View File

@ -619,10 +619,14 @@ CycleCollectedJSRuntime::NoteGCThingXPCOMChildren(const js::Class* aClasp,
const DOMJSClass* domClass = GetDOMClass(aObj); const DOMJSClass* domClass = GetDOMClass(aObj);
if (domClass) { if (domClass) {
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)"); NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(aCb, "UnwrapDOMObject(obj)");
// It's possible that our object is an unforgeable holder object, in
// which case it doesn't actually have a C++ DOM object associated with
// it. Use UnwrapPossiblyNotInitializedDOMObject, which produces null in
// that case, since NoteXPCOMChild/NoteNativeChild are null-safe.
if (domClass->mDOMObjectIsISupports) { if (domClass->mDOMObjectIsISupports) {
aCb.NoteXPCOMChild(UnwrapDOMObject<nsISupports>(aObj)); aCb.NoteXPCOMChild(UnwrapPossiblyNotInitializedDOMObject<nsISupports>(aObj));
} else if (domClass->mParticipant) { } else if (domClass->mParticipant) {
aCb.NoteNativeChild(UnwrapDOMObject<void>(aObj), aCb.NoteNativeChild(UnwrapPossiblyNotInitializedDOMObject<void>(aObj),
domClass->mParticipant); domClass->mParticipant);
} }
} }