|
@@ -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
|