Bug 1101561 - Move ensuring classes needed by parsing into a single method, and assert all needed prototypes are created as delegates. r=jandem
--- a/js/src/jsarray.cpp
+++ b/js/src/jsarray.cpp
@@ -3242,16 +3242,17 @@ CreateArrayPrototype(JSContext* cx, JSPr
return nullptr;
AutoSetNewObjectMetadata metadata(cx);
RootedArrayObject arrayProto(cx, ArrayObject::createArray(cx, gc::AllocKind::OBJECT4,
gc::TenuredHeap, shape, group, 0,
metadata));
if (!arrayProto ||
!JSObject::setSingleton(cx, arrayProto) ||
+ !arrayProto->setDelegate(cx) ||
!AddLengthProperty(cx, arrayProto))
{
return nullptr;
}
/*
* The default 'new' group of Array.prototype is required by type inference
* to have unknown properties, to simplify handling of e.g. heterogenous
--- a/js/src/vm/GeneratorObject.cpp
+++ b/js/src/vm/GeneratorObject.cpp
@@ -286,30 +286,32 @@ NewSingletonObjectWithFunctionPrototype(
return NewObjectWithGivenProto<PlainObject>(cx, proto, SingletonObject);
}
/* static */ bool
GlobalObject::initGeneratorClasses(JSContext* cx, Handle<GlobalObject*> global)
{
if (global->getSlot(LEGACY_GENERATOR_OBJECT_PROTO).isUndefined()) {
RootedObject proto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
- if (!proto || !DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
+ if (!proto || !proto->setDelegate(cx))
+ return false;
+ if (!DefinePropertiesAndFunctions(cx, proto, nullptr, legacy_generator_methods))
return false;
global->setReservedSlot(LEGACY_GENERATOR_OBJECT_PROTO, ObjectValue(*proto));
}
if (global->getSlot(STAR_GENERATOR_OBJECT_PROTO).isUndefined()) {
RootedObject genObjectProto(cx, NewSingletonObjectWithObjectPrototype(cx, global));
- if (!genObjectProto)
+ if (!genObjectProto || !genObjectProto->setDelegate(cx))
return false;
if (!DefinePropertiesAndFunctions(cx, genObjectProto, nullptr, star_generator_methods))
return false;
RootedObject genFunctionProto(cx, NewSingletonObjectWithFunctionPrototype(cx, global));
- if (!genFunctionProto)
+ if (!genFunctionProto || !genFunctionProto->setDelegate(cx))
return false;
if (!LinkConstructorAndPrototype(cx, genFunctionProto, genObjectProto))
return false;
RootedValue function(cx, global->getConstructor(JSProto_Function));
if (!function.toObjectOrNull())
return false;
RootedObject proto(cx, &function.toObject());
--- a/js/src/vm/HelperThreads.cpp
+++ b/js/src/vm/HelperThreads.cpp
@@ -308,16 +308,55 @@ js::OffThreadParsingMustWaitForGC(JSRunt
// atoms compartment, to avoid triggering barriers. (Outside the atoms
// compartment, the compilation will use a new zone that is never
// collected.) If an atoms-zone GC is in progress, hold off on executing the
// parse task until the atoms-zone GC completes (see
// EnqueuePendingParseTasksAfterGC).
return rt->activeGCInAtomsZone();
}
+static bool
+EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
+{
+ if (!GlobalObject::ensureConstructor(cx, global, key))
+ return false;
+
+ MOZ_ASSERT(global->getPrototype(key).toObject().isDelegate(),
+ "standard class prototype wasn't a delegate from birth");
+ return true;
+}
+
+// Initialize all classes potentially created during parsing for use in parser
+// data structures, template objects, &c.
+static bool
+EnsureParserCreatedClasses(JSContext* cx)
+{
+ Handle<GlobalObject*> global = cx->global();
+
+ if (!EnsureConstructor(cx, global, JSProto_Object))
+ return false; // needed by object literals
+
+ if (!EnsureConstructor(cx, global, JSProto_Array))
+ return false; // needed by array literals
+
+ if (!EnsureConstructor(cx, global, JSProto_Function))
+ return false; // needed by functions
+
+ if (!EnsureConstructor(cx, global, JSProto_RegExp))
+ return false; // needed by regular expression literals
+
+ if (!EnsureConstructor(cx, global, JSProto_Iterator))
+ return false; // needed by ???
+
+ if (!EnsureConstructor(cx, global, JSProto_GeneratorFunction))
+ return false; // needed by function*() {} and generator comprehensions
+
+ return true;
+}
+
bool
js::StartOffThreadParseScript(JSContext* cx, const ReadOnlyCompileOptions& options,
const char16_t* chars, size_t length,
JS::OffThreadCompileCallback callback, void* callbackData)
{
// Suppress GC so that calls below do not trigger a new incremental GC
// which could require barriers on the atoms compartment.
gc::AutoSuppressGC suppress(cx);
@@ -332,37 +371,25 @@ js::StartOffThreadParseScript(JSContext*
JSObject* global = JS_NewGlobalObject(cx, &parseTaskGlobalClass, nullptr,
JS::FireOnNewGlobalHook, compartmentOptions);
if (!global)
return false;
JS_SetCompartmentPrincipals(global->compartment(), cx->compartment()->principals());
- RootedObject obj(cx);
-
- // Initialize all classes needed for parsing while we are still on the main
- // thread. Do this for both the target and the new global so that prototype
+ // Initialize all classes required for parsing while still on the main
+ // thread, for both the target and the new global so that prototype
// pointers can be changed infallibly after parsing finishes.
- if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
- {
+ if (!EnsureParserCreatedClasses(cx))
return false;
- }
{
AutoCompartment ac(cx, global);
- if (!GetBuiltinConstructor(cx, JSProto_Function, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_Array, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_RegExp, &obj) ||
- !GetBuiltinConstructor(cx, JSProto_Iterator, &obj))
- {
+ if (!EnsureParserCreatedClasses(cx))
return false;
- }
}
ScopedJSDeletePtr<ExclusiveContext> helpercx(
cx->new_<ExclusiveContext>(cx->runtime(), (PerThreadData*) nullptr,
ExclusiveContext::Context_Exclusive));
if (!helpercx)
return false;
@@ -879,25 +906,16 @@ static void
LeaveParseTaskZone(JSRuntime* rt, ParseTask* task)
{
// Mark the zone as no longer in use by an ExclusiveContext, and available
// to be collected by the GC.
task->cx->leaveCompartment(task->cx->compartment());
rt->clearUsedByExclusiveThread(task->cx->zone());
}
-static bool
-EnsureConstructor(JSContext* cx, Handle<GlobalObject*> global, JSProtoKey key)
-{
- if (!GlobalObject::ensureConstructor(cx, global, key))
- return false;
-
- return global->getPrototype(key).toObject().setDelegate(cx);
-}
-
JSScript*
GlobalHelperThreadState::finishParseTask(JSContext* maybecx, JSRuntime* rt, void* token)
{
ScopedJSDeletePtr<ParseTask> parseTask;
// The token is a ParseTask* which should be in the finished list.
// Find and remove its entry.
{
@@ -919,23 +937,17 @@ GlobalHelperThreadState::finishParseTask
}
JSContext* cx = maybecx;
MOZ_ASSERT(cx->compartment());
// Make sure we have all the constructors we need for the prototype
// remapping below, since we can't GC while that's happening.
Rooted<GlobalObject*> global(cx, &cx->global()->as<GlobalObject>());
- if (!EnsureConstructor(cx, global, JSProto_Object) ||
- !EnsureConstructor(cx, global, JSProto_Array) ||
- !EnsureConstructor(cx, global, JSProto_Function) ||
- !EnsureConstructor(cx, global, JSProto_RegExp) ||
- !EnsureConstructor(cx, global, JSProto_Iterator) ||
- !EnsureConstructor(cx, global, JSProto_GeneratorFunction))
- {
+ if (!EnsureParserCreatedClasses(cx)) {
LeaveParseTaskZone(rt, parseTask);
return nullptr;
}
mergeParseTaskCompartment(rt, parseTask, global, cx->compartment());
if (!parseTask->finish(cx))
return nullptr;