Frank-Rainer Grahl 9 months ago
parent
commit
d906834562

+ 992 - 0
mozilla-release/patches/1432794-60a1.patch

@@ -0,0 +1,992 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1517335060 0
+# Node ID 1b4d5be7203199c65559ef7e35893587afa9da67
+# Parent  0e9810133811ad52f9689bebdc397157ec7718fa
+Bug 1432794 - Skip prototype and constructor intialization for off-thread parsing r=jandem
+
+diff --git a/js/public/Value.h b/js/public/Value.h
+--- a/js/public/Value.h
++++ b/js/public/Value.h
+@@ -216,16 +216,19 @@ typedef enum JSWhyMagic
+     JS_ION_BAILOUT,
+ 
+     /** optimized out slot */
+     JS_OPTIMIZED_OUT,
+ 
+     /** uninitialized lexical bindings that produce ReferenceError on touch. */
+     JS_UNINITIALIZED_LEXICAL,
+ 
++    /** standard constructors are not created for off-thread parsing. */
++    JS_OFF_THREAD_CONSTRUCTOR,
++
+     /** for local use */
+     JS_GENERIC_MAGIC,
+ 
+     JS_WHY_MAGIC_COUNT
+ } JSWhyMagic;
+ 
+ namespace js {
+ static inline JS::Value PoisonedObjectValue(uintptr_t poison);
+diff --git a/js/src/builtin/ModuleObject.cpp b/js/src/builtin/ModuleObject.cpp
+--- a/js/src/builtin/ModuleObject.cpp
++++ b/js/src/builtin/ModuleObject.cpp
+@@ -140,17 +140,20 @@ GlobalObject::initImportEntryProto(JSCon
+ /* static */ ImportEntryObject*
+ ImportEntryObject::create(JSContext* cx,
+                           HandleAtom moduleRequest,
+                           HandleAtom importName,
+                           HandleAtom localName,
+                           uint32_t lineNumber,
+                           uint32_t columnNumber)
+ {
+-    RootedObject proto(cx, cx->global()->getImportEntryPrototype());
++    RootedObject proto(cx, GlobalObject::getOrCreateImportEntryPrototype(cx, cx->global()));
++    if (!proto)
++        return nullptr;
++
+     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
+     if (!obj)
+         return nullptr;
+ 
+     RootedImportEntryObject self(cx, &obj->as<ImportEntryObject>());
+     self->initReservedSlot(ModuleRequestSlot, StringValue(moduleRequest));
+     self->initReservedSlot(ImportNameSlot, StringValue(importName));
+     self->initReservedSlot(LocalNameSlot, StringValue(localName));
+@@ -226,17 +229,20 @@ ExportEntryObject::create(JSContext* cx,
+                           HandleAtom maybeImportName,
+                           HandleAtom maybeLocalName,
+                           uint32_t lineNumber,
+                           uint32_t columnNumber)
+ {
+     // Line and column numbers are optional for export entries since direct
+     // entries are checked at parse time.
+ 
+-    RootedObject proto(cx, cx->global()->getExportEntryPrototype());
++    RootedObject proto(cx, GlobalObject::getOrCreateExportEntryPrototype(cx, cx->global()));
++    if (!proto)
++        return nullptr;
++
+     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
+     if (!obj)
+         return nullptr;
+ 
+     RootedExportEntryObject self(cx, &obj->as<ExportEntryObject>());
+     self->initReservedSlot(ExportNameSlot, StringOrNullValue(maybeExportName));
+     self->initReservedSlot(ModuleRequestSlot, StringOrNullValue(maybeModuleRequest));
+     self->initReservedSlot(ImportNameSlot, StringOrNullValue(maybeImportName));
+@@ -292,17 +298,20 @@ GlobalObject::initRequestedModuleProto(J
+ }
+ 
+ /* static */ RequestedModuleObject*
+ RequestedModuleObject::create(JSContext* cx,
+                               HandleAtom moduleSpecifier,
+                               uint32_t lineNumber,
+                               uint32_t columnNumber)
+ {
+-    RootedObject proto(cx, cx->global()->getRequestedModulePrototype());
++    RootedObject proto(cx, GlobalObject::getOrCreateRequestedModulePrototype(cx, cx->global()));
++    if (!proto)
++        return nullptr;
++
+     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
+     if (!obj)
+         return nullptr;
+ 
+     RootedRequestedModuleObject self(cx, &obj->as<RequestedModuleObject>());
+     self->initReservedSlot(ModuleSpecifierSlot, StringValue(moduleSpecifier));
+     self->initReservedSlot(LineNumberSlot, NumberValue(lineNumber));
+     self->initReservedSlot(ColumnNumberSlot, NumberValue(columnNumber));
+@@ -753,17 +762,20 @@ DEFINE_ARRAY_SLOT_ACCESSOR(ModuleObject,
+ ModuleObject::isInstance(HandleValue value)
+ {
+     return value.isObject() && value.toObject().is<ModuleObject>();
+ }
+ 
+ /* static */ ModuleObject*
+ ModuleObject::create(JSContext* cx)
+ {
+-    RootedObject proto(cx, cx->global()->getModulePrototype());
++    RootedObject proto(cx, GlobalObject::getOrCreateModulePrototype(cx, cx->global()));
++    if (!proto)
++        return nullptr;
++
+     RootedObject obj(cx, NewObjectWithGivenProto(cx, &class_, proto));
+     if (!obj)
+         return nullptr;
+ 
+     RootedModuleObject self(cx, &obj->as<ModuleObject>());
+ 
+     Zone* zone = cx->zone();
+     IndirectBindingMap* bindings = zone->new_<IndirectBindingMap>();
+diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
+--- a/js/src/frontend/Parser.cpp
++++ b/js/src/frontend/Parser.cpp
+@@ -3384,21 +3384,17 @@ GeneralParser<ParseHandler, CharT>::func
+ 
+         return funcNode;
+     }
+ 
+     RootedObject proto(context);
+     if (generatorKind == GeneratorKind::Generator ||
+         asyncKind == FunctionAsyncKind::AsyncFunction)
+     {
+-        // If we are off thread, the generator meta-objects have
+-        // already been created by js::StartOffThreadParseTask, so cx will not
+-        // be necessary.
+-        JSContext* cx = context->helperThread() ? nullptr : context;
+-        proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, context->global());
++        proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(context, context->global());
+         if (!proto)
+             return null();
+     }
+     RootedFunction fun(context, newFunction(funName, kind, generatorKind, asyncKind, proto));
+     if (!fun)
+         return null();
+ 
+     // Speculatively parse using the directives of the parent parsing context.
+diff --git a/js/src/jit-test/tests/parser/bug-1431353-2.js b/js/src/jit-test/tests/parser/bug-1431353-2.js
+new file mode 100644
+--- /dev/null
++++ b/js/src/jit-test/tests/parser/bug-1431353-2.js
+@@ -0,0 +1,56 @@
++// Test off-thread parsing correctly fixes up prototypes of special objects when
++// merging back to the target compartment.
++
++if (helperThreadCount() === 0)
++    quit();
++
++function execOffThread(source)
++{
++    offThreadCompileScript(source);
++    return runOffThreadScript();
++}
++
++function parseModuleOffThread(source)
++{
++    offThreadCompileModule(source);
++    return finishOffThreadModule();
++}
++
++let a = { x: 1 };
++let b = execOffThread("undefined, { x: 1 }")
++let c = execOffThread("undefined, { x: 1 }")
++
++assertEq(Object.getPrototypeOf(a), Object.prototype);
++assertEq(Object.getPrototypeOf(b), Object.prototype);
++assertEq(Object.getPrototypeOf(c), Object.prototype);
++
++a = () => 1;
++b = execOffThread("() => 1")
++c = execOffThread("() => 1")
++
++assertEq(Object.getPrototypeOf(a), Function.prototype);
++assertEq(Object.getPrototypeOf(b), Function.prototype);
++assertEq(Object.getPrototypeOf(c), Function.prototype);
++
++a = [1, 2, 3];
++b = execOffThread("[1, 2, 3]")
++c = execOffThread("[1, 2, 3]")
++
++assertEq(Object.getPrototypeOf(a), Array.prototype);
++assertEq(Object.getPrototypeOf(b), Array.prototype);
++assertEq(Object.getPrototypeOf(c), Array.prototype);
++
++a = /a/;
++b = execOffThread("/a/")
++c = execOffThread("/a/")
++
++assertEq(Object.getPrototypeOf(a), RegExp.prototype);
++assertEq(Object.getPrototypeOf(b), RegExp.prototype);
++assertEq(Object.getPrototypeOf(c), RegExp.prototype);
++
++a = parseModule("");
++b = parseModuleOffThread("");
++c = parseModuleOffThread("");
++
++assertEq(Object.getPrototypeOf(b), Object.getPrototypeOf(a));
++assertEq(Object.getPrototypeOf(c), Object.getPrototypeOf(a));
+diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp
+--- a/js/src/jsfun.cpp
++++ b/js/src/jsfun.cpp
+@@ -633,21 +633,17 @@ js::XDRInterpretedFunction(XDRState<mode
+     if ((firstword & HasAtom) && !XDRAtom(xdr, &atom))
+         return false;
+     if (!xdr->codeUint32(&flagsword))
+         return false;
+ 
+     if (mode == XDR_DECODE) {
+         RootedObject proto(cx);
+         if (firstword & HasGeneratorProto) {
+-            // If we are off thread, the generator meta-objects have
+-            // already been created by js::StartOffThreadParseTask, so
+-            // JSContext* will not be necessary.
+-            JSContext* context = cx->helperThread() ? nullptr : cx;
+-            proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(context, cx->global());
++            proto = GlobalObject::getOrCreateGeneratorFunctionPrototype(cx, cx->global());
+             if (!proto)
+                 return false;
+         }
+ 
+         gc::AllocKind allocKind = gc::AllocKind::FUNCTION;
+         if (uint16_t(flagsword) & JSFunction::EXTENDED)
+             allocKind = gc::AllocKind::FUNCTION_EXTENDED;
+         fun = NewFunctionWithProto(cx, nullptr, 0, JSFunction::INTERPRETED,
+diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
+--- a/js/src/jsgc.cpp
++++ b/js/src/jsgc.cpp
+@@ -8021,17 +8021,34 @@ GCRuntime::mergeCompartments(JSCompartme
+     // type information generations are in sync.
+ 
+     for (auto script = source->zone()->cellIter<JSScript>(); !script.done(); script.next()) {
+         MOZ_ASSERT(script->compartment() == source);
+         script->compartment_ = target;
+         script->setTypesGeneration(target->zone()->types.generation);
+     }
+ 
++    GlobalObject* global = target->maybeGlobal();
++    MOZ_ASSERT(global);
++
+     for (auto group = source->zone()->cellIter<ObjectGroup>(); !group.done(); group.next()) {
++        // Replace placeholder object prototypes with the correct prototype in
++        // the target compartment.
++        TaggedProto proto(group->proto());
++        if (proto.isObject()) {
++            JSObject* obj = proto.toObject();
++            if (GlobalObject::isOffThreadPrototypePlaceholder(obj)) {
++                JSObject* targetProto = global->getPrototypeForOffThreadPlaceholder(obj);
++                MOZ_ASSERT(targetProto->isDelegate());
++                group->setProtoUnchecked(TaggedProto(targetProto));
++                if (targetProto->isNewGroupUnknown())
++                    group->markUnknown(cx);
++            }
++        }
++
+         group->setGeneration(target->zone()->types.generation);
+         group->compartment_ = target;
+ 
+         // Remove any unboxed layouts from the list in the off thread
+         // compartment. These do not need to be reinserted in the target
+         // compartment's list, as the list is not required to be complete.
+         if (UnboxedLayout* layout = group->maybeUnboxedLayoutDontCheckGeneration())
+             layout->detachFromCompartment();
+diff --git a/js/src/jsscript.cpp b/js/src/jsscript.cpp
+--- a/js/src/jsscript.cpp
++++ b/js/src/jsscript.cpp
+@@ -536,26 +536,25 @@ js::XDRScript(XDRState<mode>* xdr, Handl
+              * ScriptSourceObject. Most CompileOptions fields aren't used by
+              * ScriptSourceObject, and those that are (element; elementAttributeName)
+              * aren't preserved by XDR. So this can be simple.
+              */
+             if (!ss->initFromOptions(cx, *options))
+                 return false;
+ 
+             sourceObject = ScriptSourceObject::create(cx, ss);
++            if (!sourceObject)
++                return false;
++
+             if (xdr->hasScriptSourceObjectOut()) {
+                 // When the ScriptSourceObjectOut is provided by ParseTask, it
+                 // is stored in a location which is traced by the GC.
+                 *xdr->scriptSourceObjectOut() = sourceObject;
+-            } else {
+-                if (!sourceObject ||
+-                    !ScriptSourceObject::initFromOptions(cx, sourceObject, *options))
+-                {
+-                    return false;
+-                }
++            } else if (!ScriptSourceObject::initFromOptions(cx, sourceObject, *options)) {
++                return false;
+             }
+         }
+ 
+         script = JSScript::Create(cx, *options, sourceObject, 0, 0, 0, 0);
+         if (!script)
+             return false;
+ 
+         // Set the script in its function now so that inner scripts to be
+diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
+--- a/js/src/vm/GlobalObject.cpp
++++ b/js/src/vm/GlobalObject.cpp
+@@ -116,16 +116,20 @@ GlobalObject::skipDeselectedConstructor(
+         return false;
+     }
+ }
+ 
+ /* static*/ bool
+ GlobalObject::resolveConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
+ {
+     MOZ_ASSERT(!global->isStandardClassResolved(key));
++
++    if (global->zone()->group()->createdForHelperThread())
++        return resolveOffThreadConstructor(cx, global, key);
++
+     MOZ_ASSERT(!cx->helperThread());
+ 
+     // Prohibit collection of allocation metadata. Metadata builders shouldn't
+     // need to observe lazily-constructed prototype objects coming into
+     // existence. And assertions start to fail when the builder itself attempts
+     // an allocation that re-entrantly tries to create the same prototype.
+     AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
+ 
+@@ -269,16 +273,115 @@ GlobalObject::resolveConstructor(JSConte
+         global->setConstructor(key, ObjectValue(*ctor));
+         if (proto)
+             global->setPrototype(key, ObjectValue(*proto));
+     }
+ 
+     return true;
+ }
+ 
++/* static */ JSObject*
++GlobalObject::createObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot, ObjectInitOp init)
++{
++    if (global->zone()->group()->createdForHelperThread())
++        return createOffThreadObject(cx, global, slot);
++
++    MOZ_ASSERT(!cx->helperThread());
++    if (!init(cx, global))
++        return nullptr;
++
++    return &global->getSlot(slot).toObject();
++}
++
++const Class GlobalObject::OffThreadPlaceholderObject::class_ = {
++    "off-thread-prototype-placeholder",
++    JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_RESERVED_SLOTS(1)
++};
++
++/* static */ GlobalObject::OffThreadPlaceholderObject*
++GlobalObject::OffThreadPlaceholderObject::New(JSContext* cx, unsigned slot)
++{
++    Rooted<OffThreadPlaceholderObject*> placeholder(cx);
++    placeholder =
++        NewObjectWithGivenTaggedProto<OffThreadPlaceholderObject>(cx, AsTaggedProto(nullptr));
++    if (!placeholder)
++        return nullptr;
++
++    placeholder->setReservedSlot(SlotIndexSlot, Int32Value(slot));
++    return placeholder;
++}
++
++inline int32_t
++GlobalObject::OffThreadPlaceholderObject::getSlotIndex() const
++{
++    return getReservedSlot(SlotIndexSlot).toInt32();
++}
++
++/* static */ bool
++GlobalObject::resolveOffThreadConstructor(JSContext* cx,
++                                          Handle<GlobalObject*> global,
++                                          JSProtoKey key)
++{
++    // Don't resolve constructors for off-thread parse globals. Instead create a
++    // placeholder object for the prototype which we can use to find the real
++    // prototype when the off-thread compartment is merged back into the target
++    // compartment.
++
++    MOZ_ASSERT(global->zone()->group()->createdForHelperThread());
++    MOZ_ASSERT(key == JSProto_Object ||
++               key == JSProto_Function ||
++               key == JSProto_Array ||
++               key == JSProto_RegExp);
++
++    Rooted<OffThreadPlaceholderObject*> placeholder(cx);
++    placeholder = OffThreadPlaceholderObject::New(cx, prototypeSlot(key));
++    if (!placeholder)
++        return false;
++
++    if (key == JSProto_Object &&
++        !JSObject::setFlags(cx, placeholder, BaseShape::IMMUTABLE_PROTOTYPE))
++    {
++        return false;
++    }
++
++    global->setPrototype(key, ObjectValue(*placeholder));
++    global->setConstructor(key, MagicValue(JS_OFF_THREAD_CONSTRUCTOR));
++    return true;
++}
++
++/* static */ JSObject*
++GlobalObject::createOffThreadObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot)
++{
++    // Don't create prototype objects for off-thread parse globals. Instead
++    // create a placeholder object which we can use to find the real prototype
++    // when the off-thread compartment is merged back into the target
++    // compartment.
++
++    MOZ_ASSERT(global->zone()->group()->createdForHelperThread());
++    MOZ_ASSERT(slot == GENERATOR_FUNCTION_PROTO ||
++               slot == MODULE_PROTO ||
++               slot == IMPORT_ENTRY_PROTO ||
++               slot == EXPORT_ENTRY_PROTO ||
++               slot == REQUESTED_MODULE_PROTO);
++
++    auto placeholder = OffThreadPlaceholderObject::New(cx, slot);
++    if (!placeholder)
++        return nullptr;
++
++    global->setSlot(slot, ObjectValue(*placeholder));
++    return placeholder;
++}
++
++JSObject*
++GlobalObject::getPrototypeForOffThreadPlaceholder(JSObject* obj)
++{
++    auto placeholder = &obj->as<OffThreadPlaceholderObject>();
++    return &getSlot(placeholder->getSlotIndex()).toObject();
++}
++
+ /* static */ bool
+ GlobalObject::initBuiltinConstructor(JSContext* cx, Handle<GlobalObject*> global,
+                                      JSProtoKey key, HandleObject ctor, HandleObject proto)
+ {
+     MOZ_ASSERT(!global->empty()); // reserved slots already allocated
+     MOZ_ASSERT(key != JSProto_Null);
+     MOZ_ASSERT(ctor);
+     MOZ_ASSERT(proto);
+@@ -896,13 +999,13 @@ GlobalObject::addIntrinsicValue(JSContex
+ 
+     holder->setSlot(shape->slot(), value);
+     return true;
+ }
+ 
+ /* static */ bool
+ GlobalObject::ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global)
+ {
+-    return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto) &&
+-           getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO, initImportEntryProto) &&
+-           getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO, initExportEntryProto) &&
+-           getOrCreateObject(cx, global, REQUESTED_MODULE_PROTO, initRequestedModuleProto);
++    return getOrCreateModulePrototype(cx, global) &&
++           getOrCreateImportEntryPrototype(cx, global) &&
++           getOrCreateExportEntryPrototype(cx, global) &&
++           getOrCreateRequestedModulePrototype(cx, global);
+ }
+diff --git a/js/src/vm/GlobalObject.h b/js/src/vm/GlobalObject.h
+--- a/js/src/vm/GlobalObject.h
++++ b/js/src/vm/GlobalObject.h
+@@ -116,28 +116,37 @@ class GlobalObject : public NativeObject
+     /*
+      * The slot count must be in the public API for JSCLASS_GLOBAL_FLAGS, and
+      * we won't expose GlobalObject, so just assert that the two values are
+      * synchronized.
+      */
+     static_assert(JSCLASS_GLOBAL_SLOT_COUNT == RESERVED_SLOTS,
+                   "global object slot counts are inconsistent");
+ 
++    static unsigned constructorSlot(JSProtoKey key) {
++        MOZ_ASSERT(key <= JSProto_LIMIT);
++        return APPLICATION_SLOTS + key;
++    }
++
++    static unsigned prototypeSlot(JSProtoKey key) {
++        MOZ_ASSERT(key <= JSProto_LIMIT);
++        return APPLICATION_SLOTS + JSProto_LIMIT + key;
++    }
++
+   public:
+     LexicalEnvironmentObject& lexicalEnvironment() const;
+     GlobalScope& emptyGlobalScope() const;
+ 
+     void setOriginalEval(JSObject* evalobj) {
+         MOZ_ASSERT(getSlotRef(EVAL).isUndefined());
+         setSlot(EVAL, ObjectValue(*evalobj));
+     }
+ 
+     Value getConstructor(JSProtoKey key) const {
+-        MOZ_ASSERT(key <= JSProto_LIMIT);
+-        return getSlot(APPLICATION_SLOTS + key);
++        return getSlot(constructorSlot(key));
+     }
+     static bool skipDeselectedConstructor(JSContext* cx, JSProtoKey key);
+     static bool initBuiltinConstructor(JSContext* cx, Handle<GlobalObject*> global,
+                                        JSProtoKey key, HandleObject ctor, HandleObject proto);
+ 
+     static bool maybeResolveGlobalThis(JSContext* cx, Handle<GlobalObject*> global, bool* resolved);
+ 
+   private:
+@@ -169,28 +178,25 @@ class GlobalObject : public NativeObject
+     JSObject* maybeGetPrototype(JSProtoKey protoKey) const {
+         MOZ_ASSERT(JSProto_Null < protoKey);
+         MOZ_ASSERT(protoKey < JSProto_LIMIT);
+         const Value& v = getPrototype(protoKey);
+         return v.isObject() ? &v.toObject() : nullptr;
+     }
+ 
+     void setConstructor(JSProtoKey key, const Value& v) {
+-        MOZ_ASSERT(key <= JSProto_LIMIT);
+-        setSlot(APPLICATION_SLOTS + key, v);
++        setSlot(constructorSlot(key), v);
+     }
+ 
+     Value getPrototype(JSProtoKey key) const {
+-        MOZ_ASSERT(key <= JSProto_LIMIT);
+-        return getSlot(APPLICATION_SLOTS + JSProto_LIMIT + key);
++        return getSlot(prototypeSlot(key));
+     }
+ 
+     void setPrototype(JSProtoKey key, const Value& value) {
+-        MOZ_ASSERT(key <= JSProto_LIMIT);
+-        setSlot(APPLICATION_SLOTS + JSProto_LIMIT + key, value);
++        setSlot(prototypeSlot(key), value);
+     }
+ 
+     bool classIsInitialized(JSProtoKey key) const {
+         bool inited = !getConstructor(key).isUndefined();
+         MOZ_ASSERT(inited == !getPrototype(key).isUndefined());
+         return inited;
+     }
+ 
+@@ -207,19 +213,21 @@ class GlobalObject : public NativeObject
+      * getConstructor(key) reserved slot to indicate that they've been
+      * initialized.
+      *
+      * Note: A few builtin objects, like JSON and Math, are not constructors,
+      * so getConstructor is a bit of a misnomer.
+      */
+     bool isStandardClassResolved(JSProtoKey key) const {
+         // If the constructor is undefined, then it hasn't been initialized.
+-        MOZ_ASSERT(getConstructor(key).isUndefined() ||
+-                   getConstructor(key).isObject());
+-        return !getConstructor(key).isUndefined();
++        Value value = getConstructor(key);
++        MOZ_ASSERT(value.isUndefined() ||
++                   value.isObject() ||
++                   value.isMagic(JS_OFF_THREAD_CONSTRUCTOR));
++        return !value.isUndefined();
+     }
+ 
+     /*
+      * Using a Handle<GlobalObject*> as a Handle<Object*> is always safe as
+      * GlobalObject derives JSObject. However, with C++'s semantics, Handle<T>
+      * is not related to Handle<S>, independent of how S and T are related.
+      * Further, Handle stores an indirect pointer and, again because of C++'s
+      * semantics, T** is not related to S**, independent of how S and T are
+@@ -509,58 +517,34 @@ class GlobalObject : public NativeObject
+ 
+     static JSObject*
+     getOrCreateRelativeTimeFormatPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+         return getOrCreateObject(cx, global, RELATIVE_TIME_FORMAT_PROTO, initIntlObject);
+     }
+ 
+     static bool ensureModulePrototypesCreated(JSContext *cx, Handle<GlobalObject*> global);
+ 
+-    JSObject* maybeGetModulePrototype() {
+-        Value value = getSlot(MODULE_PROTO);
+-        return value.isUndefined() ? nullptr : &value.toObject();
+-    }
+-
+-    JSObject* maybeGetImportEntryPrototype() {
+-        Value value = getSlot(IMPORT_ENTRY_PROTO);
+-        return value.isUndefined() ? nullptr : &value.toObject();
+-    }
+-
+-    JSObject* maybeGetExportEntryPrototype() {
+-        Value value = getSlot(EXPORT_ENTRY_PROTO);
+-        return value.isUndefined() ? nullptr : &value.toObject();
+-    }
+-
+-    JSObject* maybeGetRequestedModulePrototype() {
+-        Value value = getSlot(REQUESTED_MODULE_PROTO);
+-        return value.isUndefined() ? nullptr : &value.toObject();
++    static JSObject*
++    getOrCreateModulePrototype(JSContext* cx, Handle<GlobalObject*> global) {
++        return getOrCreateObject(cx, global, MODULE_PROTO, initModuleProto);
+     }
+ 
+-    JSObject* getModulePrototype() {
+-        JSObject* proto = maybeGetModulePrototype();
+-        MOZ_ASSERT(proto);
+-        return proto;
++    static JSObject*
++    getOrCreateImportEntryPrototype(JSContext* cx, Handle<GlobalObject*> global) {
++        return getOrCreateObject(cx, global, IMPORT_ENTRY_PROTO, initImportEntryProto);
+     }
+ 
+-    JSObject* getImportEntryPrototype() {
+-        JSObject* proto = maybeGetImportEntryPrototype();
+-        MOZ_ASSERT(proto);
+-        return proto;
++    static JSObject*
++    getOrCreateExportEntryPrototype(JSContext* cx, Handle<GlobalObject*> global) {
++        return getOrCreateObject(cx, global, EXPORT_ENTRY_PROTO, initExportEntryProto);
+     }
+ 
+-    JSObject* getExportEntryPrototype() {
+-        JSObject* proto = maybeGetExportEntryPrototype();
+-        MOZ_ASSERT(proto);
+-        return proto;
+-    }
+-
+-    JSObject* getRequestedModulePrototype() {
+-        JSObject* proto = maybeGetRequestedModulePrototype();
+-        MOZ_ASSERT(proto);
+-        return proto;
++    static JSObject*
++    getOrCreateRequestedModulePrototype(JSContext* cx, Handle<GlobalObject*> global) {
++        return getOrCreateObject(cx, global, REQUESTED_MODULE_PROTO, initRequestedModuleProto);
+     }
+ 
+     static JSFunction*
+     getOrCreateTypedArrayConstructor(JSContext* cx, Handle<GlobalObject*> global) {
+         if (!ensureConstructor(cx, global, JSProto_TypedArray))
+             return nullptr;
+         return &global->getConstructor(JSProto_TypedArray).toObject().as<JSFunction>();
+     }
+@@ -577,21 +561,23 @@ class GlobalObject : public NativeObject
+ 
+     static JSObject*
+     getOrCreateObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot,
+                       ObjectInitOp init)
+     {
+         Value v = global->getSlotRef(slot);
+         if (v.isObject())
+             return &v.toObject();
+-        if (!init(cx, global))
+-            return nullptr;
+-        return &global->getSlot(slot).toObject();
++
++        return createObject(cx, global, slot, init);
+     }
+ 
++    static JSObject*
++    createObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot, ObjectInitOp init);
++
+   public:
+     static NativeObject*
+     getOrCreateIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+         return MaybeNativeObject(getOrCreateObject(cx, global, ITERATOR_PROTO, initIteratorProto));
+     }
+ 
+     static NativeObject*
+     getOrCreateArrayIteratorPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+@@ -607,20 +593,19 @@ class GlobalObject : public NativeObject
+ 
+     static NativeObject*
+     getOrCreateGeneratorObjectPrototype(JSContext* cx, Handle<GlobalObject*> global)
+     {
+         return MaybeNativeObject(getOrCreateObject(cx, global, GENERATOR_OBJECT_PROTO,
+                                                    initGenerators));
+     }
+ 
+-    static NativeObject*
++    static JSObject*
+     getOrCreateGeneratorFunctionPrototype(JSContext* cx, Handle<GlobalObject*> global) {
+-        return MaybeNativeObject(getOrCreateObject(cx, global, GENERATOR_FUNCTION_PROTO,
+-                                                   initGenerators));
++        return getOrCreateObject(cx, global, GENERATOR_FUNCTION_PROTO, initGenerators);
+     }
+ 
+     static JSObject*
+     getOrCreateGeneratorFunction(JSContext* cx, Handle<GlobalObject*> global) {
+         return getOrCreateObject(cx, global, GENERATOR_FUNCTION, initGenerators);
+     }
+ 
+     static NativeObject*
+@@ -861,20 +846,36 @@ class GlobalObject : public NativeObject
+     JSFunction* moduleResolveHook() {
+         Value value = getSlotRef(MODULE_RESOLVE_HOOK);
+         if (value.isUndefined())
+             return nullptr;
+ 
+         return &value.toObject().as<JSFunction>();
+     }
+ 
+-    // Returns either this global's generator function prototype, or null if
+-    // that object was never created.  Dodgy; for use only in also-dodgy
+-    // GlobalHelperThreadState::mergeParseTaskCompartment().
+-    JSObject* getGeneratorFunctionPrototype();
++    // A class used in place of a prototype during off-thread parsing.
++    struct OffThreadPlaceholderObject : public NativeObject
++    {
++        static const int32_t SlotIndexSlot = 0;
++        static const Class class_;
++        static OffThreadPlaceholderObject* New(JSContext* cx, unsigned slot);
++        inline int32_t getSlotIndex() const;
++    };
++
++    static bool isOffThreadPrototypePlaceholder(JSObject* obj) {
++        return obj->is<OffThreadPlaceholderObject>();
++    }
++
++    JSObject* getPrototypeForOffThreadPlaceholder(JSObject* placeholder);
++
++  private:
++     static bool resolveOffThreadConstructor(JSContext* cx, Handle<GlobalObject*> global,
++                                            JSProtoKey key);
++     static JSObject* createOffThreadObject(JSContext* cx, Handle<GlobalObject*> global,
++                                            unsigned slot);
+ };
+ 
+ /*
+  * Unless otherwise specified, define ctor.prototype = proto as non-enumerable,
+  * non-configurable, and non-writable; and define proto.constructor = ctor as
+  * non-enumerable but configurable and writable.
+  */
+ extern bool
+diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp
+--- a/js/src/vm/HelperThreads.cpp
++++ b/js/src/vm/HelperThreads.cpp
+@@ -471,16 +471,17 @@ ParseTask::~ParseTask()
+         js_delete(errors[i]);
+ }
+ 
+ void
+ ParseTask::trace(JSTracer* trc)
+ {
+     if (parseGlobal->runtimeFromAnyThread() != trc->runtime())
+         return;
++
+     Zone* zone = MaybeForwarded(parseGlobal)->zoneFromAnyThread();
+     if (zone->usedByHelperThread()) {
+         MOZ_ASSERT(!zone->isCollecting());
+         return;
+     }
+ 
+     TraceManuallyBarrieredEdge(trc, &parseGlobal, "ParseTask::parseGlobal");
+     scripts.trace(trc);
+@@ -706,79 +707,64 @@ EnsureParserCreatedClasses(JSContext* cx
+         return false; // needed by function*() {}
+ 
+     if (kind == ParseTaskKind::Module && !GlobalObject::ensureModulePrototypesCreated(cx, global))
+         return false;
+ 
+     return true;
+ }
+ 
+-class AutoClearUsedByHelperThread
++class MOZ_RAII AutoSetCreatedForHelperThread
+ {
+     ZoneGroup* group;
+ 
+   public:
+-    explicit AutoClearUsedByHelperThread(JSObject* global)
++    explicit AutoSetCreatedForHelperThread(JSObject* global)
+       : group(global->zone()->group())
+-    {}
++    {
++        group->setCreatedForHelperThread();
++    }
+ 
+     void forget() {
+         group = nullptr;
+     }
+ 
+-    ~AutoClearUsedByHelperThread() {
++    ~AutoSetCreatedForHelperThread() {
+         if (group)
+             group->clearUsedByHelperThread();
+     }
+ };
+ 
+ static JSObject*
+ CreateGlobalForOffThreadParse(JSContext* cx, ParseTaskKind kind,
+-                              Maybe<AutoClearUsedByHelperThread>& clearUseGuard,
+                               const gc::AutoSuppressGC& nogc)
+ {
+     JSCompartment* currentCompartment = cx->compartment();
+ 
+     JS::CompartmentOptions compartmentOptions(currentCompartment->creationOptions(),
+                                               currentCompartment->behaviors());
+ 
+     auto& creationOptions = compartmentOptions.creationOptions();
+ 
+     creationOptions.setInvisibleToDebugger(true)
+                    .setMergeable(true)
+                    .setNewZoneInNewZoneGroup();
+ 
+     // Don't falsely inherit the host's global trace hook.
+     creationOptions.setTrace(nullptr);
+ 
+-    JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
+-                                          JS::FireOnNewGlobalHook, compartmentOptions);
+-    if (!global)
++    JSObject* obj = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
++                                       JS::DontFireOnNewGlobalHook, compartmentOptions);
++    if (!obj)
+         return nullptr;
+ 
++    Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
++
+     JS_SetCompartmentPrincipals(global->compartment(), currentCompartment->principals());
+ 
+-    // Mark this zone group as created for a helper thread. This prevents it
+-    // from being collected until clearUsedByHelperThread() is called.
+-    ZoneGroup* group = global->zone()->group();
+-    group->setCreatedForHelperThread();
+-    clearUseGuard.emplace(global);
+-
+-    // Initialize all classes required for parsing while still on the active
+-    // thread, for both the target and the new global so that prototype
+-    // pointers can be changed infallibly after parsing finishes.
+-    if (!EnsureParserCreatedClasses(cx, kind))
+-        return nullptr;
+-
+-    {
+-        AutoCompartment ac(cx, global);
+-        if (!EnsureParserCreatedClasses(cx, kind))
+-            return nullptr;
+-    }
+-
+     return global;
+ }
+ 
+ static bool
+ QueueOffThreadParseTask(JSContext* cx, ParseTask* task)
+ {
+     AutoLockHelperThreadState lock;
+ 
+@@ -805,35 +791,38 @@ StartOffThreadParseTask(JSContext* cx, c
+                         ParseTaskKind kind, TaskFunctor& taskFunctor)
+ {
+     // Suppress GC so that calls below do not trigger a new incremental GC
+     // which could require barriers on the atoms compartment.
+     gc::AutoSuppressGC nogc(cx);
+     gc::AutoAssertNoNurseryAlloc noNurseryAlloc;
+     AutoSuppressAllocationMetadataBuilder suppressMetadata(cx);
+ 
+-    Maybe<AutoClearUsedByHelperThread> clearUseGuard;
+-    JSObject* global = CreateGlobalForOffThreadParse(cx, kind, clearUseGuard, nogc);
++    JSObject* global = CreateGlobalForOffThreadParse(cx, kind, nogc);
+     if (!global)
+         return false;
+ 
++    // Mark the global's zone group as created for a helper thread. This
++    // prevents it from being collected until clearUsedByHelperThread() is
++    // called after parsing is complete. If this function exits due to error
++    // this state is cleared automatically.
++    AutoSetCreatedForHelperThread createdForHelper(global);
++
+     ScopedJSDeletePtr<ParseTask> task(taskFunctor(global));
+-    if (!task)
++    if (!task || !task->init(cx, options))
+         return false;
+ 
+-    if (!task->init(cx, options) || !QueueOffThreadParseTask(cx, task))
++    if (!QueueOffThreadParseTask(cx, task))
+         return false;
+ 
+     task.forget();
+-    clearUseGuard->forget();
+-
++    createdForHelper.forget();
+     return true;
+ }
+ 
+-
+ bool
+ js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
+                               const char16_t* chars, size_t length,
+                               JS::OffThreadCompileCallback callback, void* callbackData)
+ {
+     auto functor = [&](JSObject* global) -> ScriptParseTask* {
+         return cx->new_<ScriptParseTask>(cx, global, chars, length,
+                                          callback, callbackData);
+@@ -1711,84 +1700,28 @@ GlobalHelperThreadState::finishModulePar
+ 
+ void
+ GlobalHelperThreadState::cancelParseTask(JSRuntime* rt, ParseTaskKind kind, void* token)
+ {
+     ScopedJSDeletePtr<ParseTask> parseTask(removeFinishedParseTask(kind, token));
+     LeaveParseTaskZone(rt, parseTask);
+ }
+ 
+-JSObject*
+-GlobalObject::getGeneratorFunctionPrototype()
+-{
+-    const Value& v = getReservedSlot(GENERATOR_FUNCTION_PROTO);
+-    return v.isObject() ? &v.toObject() : nullptr;
+-}
+-
+ void
+ GlobalHelperThreadState::mergeParseTaskCompartment(JSContext* cx, ParseTask* parseTask,
+                                                    Handle<GlobalObject*> global,
+                                                    JSCompartment* dest)
+ {
+     // After we call LeaveParseTaskZone() it's not safe to GC until we have
+     // finished merging the contents of the parse task's compartment into the
+     // destination compartment.
+     JS::AutoAssertNoGC nogc(cx);
+ 
+     LeaveParseTaskZone(cx->runtime(), parseTask);
+ 
+-    {
+-        AutoCompartment ac(cx, parseTask->parseGlobal);
+-
+-        // Generator functions don't have Function.prototype as prototype but a
+-        // different function object, so the IdentifyStandardPrototype trick
+-        // below won't work.  Just special-case it.
+-        GlobalObject* parseGlobal = &parseTask->parseGlobal->as<GlobalObject>();
+-        JSObject* parseTaskGenFunctionProto = parseGlobal->getGeneratorFunctionPrototype();
+-
+-        // Module objects don't have standard prototypes either.
+-        JSObject* moduleProto = parseGlobal->maybeGetModulePrototype();
+-        JSObject* importEntryProto = parseGlobal->maybeGetImportEntryPrototype();
+-        JSObject* exportEntryProto = parseGlobal->maybeGetExportEntryPrototype();
+-
+-        // Point the prototypes of any objects in the script's compartment to refer
+-        // to the corresponding prototype in the new compartment. This will briefly
+-        // create cross compartment pointers, which will be fixed by the
+-        // MergeCompartments call below.
+-        Zone* parseZone = parseTask->parseGlobal->zone();
+-        for (auto group = parseZone->cellIter<ObjectGroup>(); !group.done(); group.next()) {
+-            TaggedProto proto(group->proto());
+-            if (!proto.isObject())
+-                continue;
+-
+-            JSObject* protoObj = proto.toObject();
+-
+-            JSObject* newProto;
+-            JSProtoKey key = JS::IdentifyStandardPrototype(protoObj);
+-            if (key != JSProto_Null) {
+-                MOZ_ASSERT(key == JSProto_Object || key == JSProto_Array ||
+-                           key == JSProto_Function || key == JSProto_RegExp);
+-                newProto = global->maybeGetPrototype(key);
+-                MOZ_ASSERT(newProto);
+-            } else if (protoObj == parseTaskGenFunctionProto) {
+-                newProto = global->getGeneratorFunctionPrototype();
+-            } else if (protoObj == moduleProto) {
+-                newProto = global->getModulePrototype();
+-            } else if (protoObj == importEntryProto) {
+-                newProto = global->getImportEntryPrototype();
+-            } else if (protoObj == exportEntryProto) {
+-                newProto = global->getExportEntryPrototype();
+-            } else {
+-                continue;
+-            }
+-
+-            group->setProtoUnchecked(TaggedProto(newProto));
+-        }
+-    }
+-
+     // Move the parsed script and all its contents into the desired compartment.
+     gc::MergeCompartments(parseTask->parseGlobal->compartment(), dest);
+ }
+ 
+ void
+ HelperThread::destroy()
+ {
+     if (thread.isSome()) {
+diff --git a/js/src/vm/HelperThreads.h b/js/src/vm/HelperThreads.h
+--- a/js/src/vm/HelperThreads.h
++++ b/js/src/vm/HelperThreads.h
+@@ -634,17 +634,17 @@ struct ParseTask
+     OwningCompileOptions options;
+ 
+     mozilla::Variant<const JS::TranscodeRange,
+                      JS::TwoByteChars,
+                      JS::TranscodeSources*> data;
+ 
+     LifoAlloc alloc;
+ 
+-    // Rooted pointer to the global object to use while parsing.
++    // The global object to use while parsing.
+     JSObject* parseGlobal;
+ 
+     // Callback invoked off thread when the parse finishes.
+     JS::OffThreadCompileCallback callback;
+     void* callbackData;
+ 
+     // Holds the final scripts between the invocation of the callback and the
+     // point where FinishOffThreadScript is called, which will destroy the

+ 45 - 0
mozilla-release/patches/1435295-60a1.patch

@@ -0,0 +1,45 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1517918001 0
+# Node ID d8ad38aef8866ba2ac70862c78bbb67ef8349890
+# Parent  a56303fc4753e5224624d12ebff18b3adb57c552
+Bug 1435295 - Don't attempt to mark object group unknown unnecessarily when merging compartments r=jandem
+
+diff --git a/js/src/jit-test/tests/gc/bug-1435295.js b/js/src/jit-test/tests/gc/bug-1435295.js
+new file mode 100644
+--- /dev/null
++++ b/js/src/jit-test/tests/gc/bug-1435295.js
+@@ -0,0 +1,11 @@
++if (helperThreadCount() === 0)
++    quit();
++if (!('oomTest' in this))
++    quit();
++
++oomTest(new Function(`function execOffThread(source) {
++    offThreadCompileModule(source);
++    return finishOffThreadModule();
++}
++b = execOffThread("[1, 2, 3]")
++`));
+diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
+--- a/js/src/jsgc.cpp
++++ b/js/src/jsgc.cpp
+@@ -8027,17 +8027,17 @@ GCRuntime::mergeCompartments(JSCompartme
+         // the target compartment.
+         TaggedProto proto(group->proto());
+         if (proto.isObject()) {
+             JSObject* obj = proto.toObject();
+             if (GlobalObject::isOffThreadPrototypePlaceholder(obj)) {
+                 JSObject* targetProto = global->getPrototypeForOffThreadPlaceholder(obj);
+                 MOZ_ASSERT(targetProto->isDelegate());
+                 group->setProtoUnchecked(TaggedProto(targetProto));
+-                if (targetProto->isNewGroupUnknown())
++                if (targetProto->isNewGroupUnknown() && !group->unknownProperties())
+                     group->markUnknown(cx);
+             }
+         }
+ 
+         group->setGeneration(target->zone()->types.generation);
+         group->compartment_ = target;
+ 
+         // Remove any unboxed layouts from the list in the off thread

+ 60 - 0
mozilla-release/patches/1449033-62a1.patch

@@ -0,0 +1,60 @@
+# HG changeset patch
+# User Jon Coppeard <jcoppeard@mozilla.com>
+# Date 1526217648 -3600
+# Node ID aafd8f9a6ed406363f0c8f18e12f4415b64a7565
+# Parent  57f84f6a6f9dbbd2df4eab15382859646bf7164f
+Bug 1449033 - Set new group unknown flag on placeholder prototypes where necessary r=jandem
+
+diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
+--- a/js/src/jsgc.cpp
++++ b/js/src/jsgc.cpp
+@@ -8075,19 +8075,21 @@ GCRuntime::mergeCompartments(JSCompartme
+         // Replace placeholder object prototypes with the correct prototype in
+         // the target compartment.
+         TaggedProto proto(group->proto());
+         if (proto.isObject()) {
+             JSObject* obj = proto.toObject();
+             if (GlobalObject::isOffThreadPrototypePlaceholder(obj)) {
+                 JSObject* targetProto = global->getPrototypeForOffThreadPlaceholder(obj);
+                 MOZ_ASSERT(targetProto->isDelegate());
++                MOZ_ASSERT_IF(targetProto->staticPrototypeIsImmutable(),
++                              obj->staticPrototypeIsImmutable());
++                MOZ_ASSERT_IF(targetProto->isNewGroupUnknown(),
++                              obj->isNewGroupUnknown());
+                 group->setProtoUnchecked(TaggedProto(targetProto));
+-                if (targetProto->isNewGroupUnknown() && !group->unknownProperties())
+-                    group->markUnknown(cx);
+             }
+         }
+ 
+         group->setGeneration(target->zone()->types.generation);
+         group->compartment_ = target;
+ 
+         // Remove any unboxed layouts from the list in the off thread
+         // compartment. These do not need to be reinserted in the target
+diff --git a/js/src/vm/GlobalObject.cpp b/js/src/vm/GlobalObject.cpp
+--- a/js/src/vm/GlobalObject.cpp
++++ b/js/src/vm/GlobalObject.cpp
+@@ -336,16 +336,22 @@ GlobalObject::resolveOffThreadConstructo
+         return false;
+ 
+     if (key == JSProto_Object &&
+         !JSObject::setFlags(cx, placeholder, BaseShape::IMMUTABLE_PROTOTYPE))
+     {
+         return false;
+     }
+ 
++    if ((key == JSProto_Object || key == JSProto_Function || key == JSProto_Array) &&
++        !JSObject::setNewGroupUnknown(cx, placeholder->getClass(), placeholder))
++    {
++        return false;
++    }
++
+     global->setPrototype(key, ObjectValue(*placeholder));
+     global->setConstructor(key, MagicValue(JS_OFF_THREAD_CONSTRUCTOR));
+     return true;
+ }
+ 
+ /* static */ JSObject*
+ GlobalObject::createOffThreadObject(JSContext* cx, Handle<GlobalObject*> global, unsigned slot)
+ {

+ 37 - 0
mozilla-release/patches/1460385-62a1.patch

@@ -0,0 +1,37 @@
+# HG changeset patch
+# User Andrew McCreight <continuation@gmail.com>
+# Date 1525894786 25200
+#      Wed May 09 12:39:46 2018 -0700
+# Node ID b830e5f05a78fad337911b36930a322bb82a81f1
+# Parent  5b6f2518b404214fc58b4e1fa37603e2c5e69d19
+Bug 1460385 - Don't trace propid in TraceCycleCollectorChildren. r=sfink
+
+The propid is a jsid, and a jsid can never be anything the CC cares
+about, so there's no reason to trace it. In at least one profile of
+Google Inbox, tracing shape propids is taking the majority of time in
+the CC and causing very long pauses.
+
+MozReview-Commit-ID: HcjnawBHLx1
+
+diff --git a/js/src/gc/Tracer.cpp b/js/src/gc/Tracer.cpp
+--- a/js/src/gc/Tracer.cpp
++++ b/js/src/gc/Tracer.cpp
+@@ -189,17 +189,17 @@ JS::TraceIncomingCCWs(JSTracer* trc, con
+ // simplicity and performance of FireFox's embedding of this engine.
+ void
+ gc::TraceCycleCollectorChildren(JS::CallbackTracer* trc, Shape* shape)
+ {
+     do {
+         MOZ_ASSERT(shape->base());
+         shape->base()->assertConsistency();
+ 
+-        TraceEdge(trc, &shape->propidRef(), "propid");
++        // Don't trace the propid because the CC doesn't care about jsid.
+ 
+         if (shape->hasGetterObject()) {
+             JSObject* tmp = shape->getterObject();
+             DoCallback(trc, &tmp, "getter");
+             MOZ_ASSERT(tmp == shape->getterObject());
+         }
+ 
+         if (shape->hasSetterObject()) {

+ 166 - 0
mozilla-release/patches/1460636-62a1.patch

@@ -0,0 +1,166 @@
+# HG changeset patch
+# User Andrew McCreight <continuation@gmail.com>
+# Date 1526063938 25200
+# Node ID 39c076bf5d77fa66c37d37e6fd6e94a68c493756
+# Parent  b444c4e40ce57f26172cd3941010eaad3686aa05
+Bug 1460636 - Don't trace jsids on ObjectGroup in the cycle collector. r=jonco,sfink
+
+For some reason, the CC spends a lot of time tracing jsids on
+ObjectGroups when an addon is installed. This patch avoids that by
+adding a canSkipJsids flag to JSTracer, and using it in
+ObjectGroup::traceChildren. If this is true, then the tracer is free
+to not report every jsid. This flag is set to true for the two CC
+tracers.
+
+MozReview-Commit-ID: CWFqQEr0SxV
+
+diff --git a/js/public/TracingAPI.h b/js/public/TracingAPI.h
+--- a/js/public/TracingAPI.h
++++ b/js/public/TracingAPI.h
+@@ -82,16 +82,17 @@ class JS_PUBLIC_API(JSTracer)
+         Callback
+     };
+     bool isMarkingTracer() const { return tag_ == TracerKindTag::Marking || tag_ == TracerKindTag::WeakMarking; }
+     bool isWeakMarkingTracer() const { return tag_ == TracerKindTag::WeakMarking; }
+     bool isTenuringTracer() const { return tag_ == TracerKindTag::Tenuring; }
+     bool isCallbackTracer() const { return tag_ == TracerKindTag::Callback; }
+     inline JS::CallbackTracer* asCallbackTracer();
+     bool traceWeakEdges() const { return traceWeakEdges_; }
++    bool canSkipJsids() const { return canSkipJsids_; }
+ #ifdef DEBUG
+     bool checkEdges() { return checkEdges_; }
+ #endif
+ 
+     // Get the current GC number. Only call this method if |isMarkingTracer()|
+     // is true.
+     uint32_t gcNumberForMarking() const;
+ 
+@@ -100,16 +101,17 @@ class JS_PUBLIC_API(JSTracer)
+              WeakMapTraceKind weakTraceKind = TraceWeakMapValues)
+       : runtime_(rt)
+       , weakMapAction_(weakTraceKind)
+ #ifdef DEBUG
+       , checkEdges_(true)
+ #endif
+       , tag_(tag)
+       , traceWeakEdges_(true)
++      , canSkipJsids_(false)
+     {}
+ 
+ #ifdef DEBUG
+     // Set whether to check edges are valid in debug builds.
+     void setCheckEdges(bool check) {
+         checkEdges_ = check;
+     }
+ #endif
+@@ -119,16 +121,17 @@ class JS_PUBLIC_API(JSTracer)
+     WeakMapTraceKind weakMapAction_;
+ #ifdef DEBUG
+     bool checkEdges_;
+ #endif
+ 
+   protected:
+     TracerKindTag tag_;
+     bool traceWeakEdges_;
++    bool canSkipJsids_;
+ };
+ 
+ namespace JS {
+ 
+ class AutoTracingName;
+ class AutoTracingIndex;
+ class AutoTracingCallback;
+ 
+@@ -250,16 +253,22 @@ class JS_PUBLIC_API(CallbackTracer) : pu
+     void dispatchToOnEdge(js::Scope** scopep) { onScopeEdge(scopep); }
+     void dispatchToOnEdge(js::RegExpShared** sharedp) { onRegExpSharedEdge(sharedp); }
+ 
+   protected:
+     void setTraceWeakEdges(bool value) {
+         traceWeakEdges_ = value;
+     }
+ 
++    // If this is set to false, then the tracer will skip some jsids
++    // to improve performance. This is needed for the cycle collector.
++    void setCanSkipJsids(bool value) {
++        canSkipJsids_ = value;
++    }
++
+   private:
+     friend class AutoTracingName;
+     const char* contextName_;
+ 
+     friend class AutoTracingIndex;
+     size_t contextIndex_;
+ 
+     friend class AutoTracingDetails;
+diff --git a/js/src/gc/Marking.cpp b/js/src/gc/Marking.cpp
+--- a/js/src/gc/Marking.cpp
++++ b/js/src/gc/Marking.cpp
+@@ -1435,20 +1435,23 @@ js::GCMarker::eagerlyMarkChildren(Scope*
+         for (uint32_t i = 0; i < length; i++)
+             traverseEdge(scope, static_cast<JSString*>(names[i].name()));
+     }
+ }
+ 
+ void
+ js::ObjectGroup::traceChildren(JSTracer* trc)
+ {
+-    unsigned count = getPropertyCount();
+-    for (unsigned i = 0; i < count; i++) {
+-        if (ObjectGroup::Property* prop = getProperty(i))
+-            TraceEdge(trc, &prop->id, "group_property");
++
++    if (!trc->canSkipJsids()) {
++        unsigned count = getPropertyCount();
++        for (unsigned i = 0; i < count; i++) {
++            if (ObjectGroup::Property* prop = getProperty(i))
++                TraceEdge(trc, &prop->id, "group_property");
++        }
+     }
+ 
+     if (proto().isObject())
+         TraceEdge(trc, &proto(), "group_proto");
+ 
+     if (trc->isMarkingTracer())
+         compartment()->mark();
+ 
+diff --git a/xpcom/base/CycleCollectedJSRuntime.cpp b/xpcom/base/CycleCollectedJSRuntime.cpp
+--- a/xpcom/base/CycleCollectedJSRuntime.cpp
++++ b/xpcom/base/CycleCollectedJSRuntime.cpp
+@@ -134,16 +134,17 @@ public:
+ 
+ struct NoteWeakMapChildrenTracer : public JS::CallbackTracer
+ {
+   NoteWeakMapChildrenTracer(JSRuntime* aRt,
+                             nsCycleCollectionNoteRootCallback& aCb)
+     : JS::CallbackTracer(aRt), mCb(aCb), mTracedAny(false), mMap(nullptr),
+       mKey(nullptr), mKeyDelegate(nullptr)
+   {
++    setCanSkipJsids(true);
+   }
+   void onChild(const JS::GCCellPtr& aThing) override;
+   nsCycleCollectionNoteRootCallback& mCb;
+   bool mTracedAny;
+   JSObject* mMap;
+   JS::GCCellPtr mKey;
+   JSObject* mKeyDelegate;
+ };
+@@ -390,16 +391,17 @@ JSZoneParticipant::TraverseNative(void* 
+   return NS_OK;
+ }
+ 
+ struct TraversalTracer : public JS::CallbackTracer
+ {
+   TraversalTracer(JSRuntime* aRt, nsCycleCollectionTraversalCallback& aCb)
+     : JS::CallbackTracer(aRt, DoNotTraceWeakMaps), mCb(aCb)
+   {
++    setCanSkipJsids(true);
+   }
+   void onChild(const JS::GCCellPtr& aThing) override;
+   nsCycleCollectionTraversalCallback& mCb;
+ };
+ 
+ void
+ TraversalTracer::onChild(const JS::GCCellPtr& aThing)
+ {

+ 5 - 0
mozilla-release/patches/series

@@ -809,6 +809,7 @@ servo-19902-60a1.patch
 1434270-60a1.patch
 1426146-1-60a1.patch
 1426146-2no3-60a1.patch
+1432794-60a1.patch
 1431533-5aa-60a1.patch
 1356331-1-60a1.patch
 1356331-2-60a1.patch
@@ -1063,6 +1064,7 @@ servo-19955-60a1.patch
 1434600-1-60a1.patch
 1434600-2-60a1.patch
 1434600-3-60a1.patch
+1435295-60a1.patch
 1434723-1-60a1.patch
 1434723-2-60a1.patch
 1434723-3-60a1.patch
@@ -2817,6 +2819,7 @@ NOBUG-20180505-lint-61a1.patch
 1456118-62a1.patch
 1454149-62a1.patch
 1460258-62a1.patch
+1460385-62a1.patch
 1459979-62a1.patch
 1460451-62a1.patch
 1374699-1-62a1.patch
@@ -2829,6 +2832,7 @@ NOBUG-20180505-lint-61a1.patch
 1460373-62a1.patch
 1460367-62a1.patch
 1460407-62a1.patch
+1460636-62a1.patch
 1457359-62a1.patch
 1454640-4-62a1.patch
 1441914-1-62a1.patch
@@ -2847,6 +2851,7 @@ NOBUG-20180505-lint-61a1.patch
 1454667-62a1.patch
 1432410-62a1.patch
 1453990-62a1.patch
+1449033-62a1.patch
 1459761-1-62a1.patch
 1430438-2-62a1.patch
 1459862-62a1.patch