diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 6ec8db61ebfa..aba62823c318 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -5654,6 +5654,8 @@ MOZ_NEVER_INLINE bool BytecodeEmitter::emitFunction( // NOTE: For a lazy function, this will be applied to any existing function // in FunctionBox::finish(). if (classContentsIfConstructor) { + MOZ_ASSERT(funbox->fieldInitializers.isNothing(), + "FieldInitializers should only be set once"); funbox->fieldInitializers = setupFieldInitializers( classContentsIfConstructor, FieldPlacement::Instance); if (!funbox->fieldInitializers) { diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp index ae1b9ec8115f..d764ca240750 100644 --- a/js/src/frontend/Parser.cpp +++ b/js/src/frontend/Parser.cpp @@ -3214,6 +3214,10 @@ FunctionNode* Parser::standaloneLazyFunction( funbox->initWithEnclosingScope(this->getCompilationInfo().scopeContext, fun->enclosingScope(), fun->flags(), syntaxKind); + if (fun->isClassConstructor()) { + funbox->fieldInitializers = + mozilla::Some(fun->baseScript()->getFieldInitializers()); + } Directives newDirectives = directives; SourceParseContext funpc(this, funbox, &newDirectives); diff --git a/js/src/frontend/SharedContext.cpp b/js/src/frontend/SharedContext.cpp index cb2f432e1af7..7f39a6204a12 100644 --- a/js/src/frontend/SharedContext.cpp +++ b/js/src/frontend/SharedContext.cpp @@ -283,10 +283,6 @@ void FunctionBox::initFromLazyFunction(JSFunction* fun) { BaseScript* lazy = fun->baseScript(); immutableFlags_ = lazy->immutableFlags(); extent = lazy->extent(); - - if (fun->isClassConstructor()) { - fieldInitializers = mozilla::Some(lazy->getFieldInitializers()); - } } void FunctionBox::initWithEnclosingParseContext(ParseContext* enclosing, diff --git a/js/src/jit-test/tests/xdr/classes.js b/js/src/jit-test/tests/xdr/classes.js index 006862ce85ff..3b83e41a48b0 100644 --- a/js/src/jit-test/tests/xdr/classes.js +++ b/js/src/jit-test/tests/xdr/classes.js @@ -9,3 +9,28 @@ evalWithCache(test, { assertEqBytecode : true }); var test = "new class { method() { super.toString(); } }().method()"; evalWithCache(test, { assertEqBytecode : true }); + +// Test class constructor in lazy function +var test = ` + function f(delazify) { + function inner1() { + class Y { + constructor() {} + } + } + + function inner2() { + class Y { + constructor() {} + field1 = ""; + } + } + + if (delazify) { + inner1(); + inner2(); + } + } + f(generation > 0); +`; +evalWithCache(test, {}); diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index c2532ac1241c..bb54b3e0b4b1 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -714,6 +714,8 @@ XDRResult js::PrivateScriptData::XDR(XDRState* xdr, HandleScript script, // Code the field initilizer data. if (funOrMod && funOrMod->is() && funOrMod->as().isClassConstructor()) { + MOZ_ASSERT(scriptEnclosingScope); + uint32_t numFieldInitializers; if (mode == XDR_ENCODE) { numFieldInitializers = data->getFieldInitializers().numFieldInitializers; @@ -1218,7 +1220,9 @@ XDRResult js::XDRLazyScript(XDRState* xdr, HandleScope enclosingScope, } } - bool hasFieldInitializers = fun->isClassConstructor(); + // FieldInitializer data is defined for class constructors, but only once + // their enclosing script has been compiled. + bool hasFieldInitializers = fun->isClassConstructor() && enclosingScope; MOZ_TRY(BaseScript::XDRLazyScriptData(xdr, sourceObject, lazy, hasFieldInitializers)); @@ -4789,8 +4793,10 @@ bool PrivateScriptData::Clone(JSContext* cx, HandleScript src, HandleScript dst, if (!JSScript::createPrivateScriptData(cx, dst, ngcthings)) { return false; } - PrivateScriptData* dstData = dst->data_; + + dstData->fieldInitializers_ = srcData->fieldInitializers_; + { auto array = dstData->gcthings(); for (uint32_t i = 0; i < ngcthings; ++i) { diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h index b9091e35298c..62c89d8ab2af 100644 --- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -1417,6 +1417,8 @@ class alignas(uintptr_t) PrivateScriptData final : public TrailingArray { private: uint32_t ngcthings = 0; + // Note: This is only defined for scripts with an enclosing scope. This + // excludes lazy scripts with lazy parents. js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid(); // End of fields. @@ -1444,6 +1446,8 @@ class alignas(uintptr_t) PrivateScriptData final : public TrailingArray { } void setFieldInitializers(FieldInitializers fieldInitializers) { + MOZ_ASSERT(fieldInitializers_.valid == false, + "Only init FieldInitializers once"); fieldInitializers_ = fieldInitializers; } const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }