summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartok <martok@martoks-place.de>2023-04-08 03:47:06 +0200
committerMartok <martok@martoks-place.de>2023-05-01 17:16:18 +0200
commitf7935ec5b2cfb652b403c1141b585ade25e56135 (patch)
treed2b2e2ce1febf12a38659171e263483052fd64a0
parentb72b80ed589b50546446018f750b8eab3fbbfed6 (diff)
downloaduxp-f7935ec5b2cfb652b403c1141b585ade25e56135.tar.gz
Issue #2142 - Handle fields in derived classes
* Don't name field initializer lambdas Based-on: m-c 1534721, 1551454, 1542448
-rw-r--r--js/src/frontend/BytecodeCompiler.cpp7
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp193
-rw-r--r--js/src/frontend/BytecodeEmitter.h14
-rw-r--r--js/src/frontend/FullParseHandler.h4
-rw-r--r--js/src/frontend/FunctionEmitter.cpp71
-rw-r--r--js/src/frontend/FunctionEmitter.h1
-rw-r--r--js/src/frontend/ObjectEmitter.cpp27
-rw-r--r--js/src/frontend/ObjectEmitter.h33
-rw-r--r--js/src/frontend/ParseNode.h21
-rw-r--r--js/src/frontend/Parser.cpp101
-rw-r--r--js/src/frontend/Parser.h11
-rw-r--r--js/src/jsscript.h67
-rw-r--r--js/src/vm/CommonPropertyNames.h1
13 files changed, 361 insertions, 190 deletions
diff --git a/js/src/frontend/BytecodeCompiler.cpp b/js/src/frontend/BytecodeCompiler.cpp
index 67423eb7ab..7f5b705f92 100644
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -676,8 +676,13 @@ frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const cha
if (lazy->hasBeenCloned())
script->setHasBeenCloned();
+ FieldInitializers fieldInitializers = FieldInitializers::Invalid();
+ if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
+ fieldInitializers = lazy->getFieldInitializers();
+ }
+
BytecodeEmitter bce(/* parent = */ nullptr, &parser, pn->as<FunctionNode>().funbox(), script, lazy,
- pn->pn_pos, BytecodeEmitter::LazyFunction);
+ pn->pn_pos, BytecodeEmitter::LazyFunction, fieldInitializers);
if (!bce.init())
return false;
diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp
index 72ba7c6ed0..02400ce2a3 100644
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -156,7 +156,8 @@ class MOZ_RAII OptionalEmitter
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
- uint32_t lineNum, EmitterMode emitterMode)
+ uint32_t lineNum, EmitterMode emitterMode,
+ FieldInitializers fieldInitializers /* = FieldInitializers::Invalid() */)
: sc(sc),
cx(sc->context),
parent(parent),
@@ -166,6 +167,7 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
main(cx, lineNum),
current(&main),
parser(parser),
+ fieldInitializers_(fieldInitializers),
atomIndices(cx->frontendCollectionPool()),
firstLine(lineNum),
maxFixedSlots(0),
@@ -178,10 +180,6 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
innermostNestableControl(nullptr),
innermostEmitterScope_(nullptr),
innermostTDZCheckCache(nullptr),
- fieldInitializers_(parent
- ? parent->fieldInitializers_
- : lazyScript ? lazyScript->getFieldInitializers()
- : FieldInitializers::Invalid()),
#ifdef DEBUG
unstableEmitterScope(false),
#endif
@@ -202,10 +200,11 @@ BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
- TokenPos bodyPosition, EmitterMode emitterMode)
+ TokenPos bodyPosition, EmitterMode emitterMode,
+ FieldInitializers fieldInitializers)
: BytecodeEmitter(parent, parser, sc, script, lazyScript,
parser->tokenStream.srcCoords.lineNum(bodyPosition.begin),
- emitterMode)
+ emitterMode, fieldInitializers)
{
setScriptStartOffsetIfUnset(bodyPosition.begin);
setFunctionBodyEndPos(bodyPosition.end);
@@ -2321,6 +2320,10 @@ BytecodeEmitter::emitSetThis(BinaryNode* setThisNode)
return false;
}
+ if (!emitInitializeInstanceFields(true)) {
+ return false;
+ }
+
return true;
}
@@ -2399,6 +2402,9 @@ BytecodeEmitter::emitFunctionScript(FunctionNode* funNode)
ListNode* paramsBody = &funNode->body()->as<ListNode>();
FunctionBox* funbox = sc->asFunctionBox();
+ MOZ_ASSERT(fieldInitializers_.valid == (funbox->function()->kind() ==
+ JSFunction::FunctionKind::ClassConstructor));
+
setScriptStartOffsetIfUnset(paramsBody->pn_pos.begin);
// [stack]
@@ -2433,6 +2439,8 @@ BytecodeEmitter::emitFunctionScript(FunctionNode* funNode)
if (!fse.initScript())
return false;
+ script->setFieldInitializers(fieldInitializers_);
+
return true;
}
@@ -5593,11 +5601,14 @@ BytecodeEmitter::emitComprehensionFor(ForNode* forNode)
}
MOZ_NEVER_INLINE bool
-BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
+BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto /* = false */,
+ ListNode* classContentsIfConstructor /* = nullptr */)
{
FunctionBox* funbox = funNode->funbox();
RootedFunction fun(cx, funbox->function());
-
+
+ MOZ_ASSERT((classContentsIfConstructor != nullptr) == (funbox->function()->kind() ==
+ JSFunction::FunctionKind::ClassConstructor));
// [stack]
FunctionEmitter fe(this, funbox, funNode->syntaxKind(),
@@ -5627,6 +5638,9 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
return false;
}
+ if (classContentsIfConstructor) {
+ fun->lazyScript()->setFieldInitializers(setupFieldInitializers(classContentsIfConstructor));
+ }
return true;
}
@@ -5651,8 +5665,13 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
if (!script)
return false;
+ FieldInitializers fieldInitializers = FieldInitializers::Invalid();
+ if (classContentsIfConstructor) {
+ fieldInitializers = setupFieldInitializers(classContentsIfConstructor);
+ }
+
BytecodeEmitter bce2(this, parser, funbox, script, /* lazyScript = */ nullptr,
- funNode->pn_pos, emitterMode);
+ funNode->pn_pos, emitterMode, fieldInitializers);
if (!bce2.init())
return false;
@@ -5660,6 +5679,8 @@ BytecodeEmitter::emitFunction(FunctionNode* funNode, bool needsProto)
if (!bce2.emitFunctionScript(funNode))
return false;
+ // fieldInitializers are copied to the JSScript inside BytecodeEmitter
+
if (funbox->isLikelyConstructorWrapper()) {
script->setLikelyConstructorWrapper();
}
@@ -7932,7 +7953,7 @@ BytecodeEmitter::emitCreateFieldKeys(ListNode* obj)
bool
BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
{
- const FieldInitializers& fieldInitializers = fieldInitializers_;
+ FieldInitializers fieldInitializers = setupFieldInitializers(obj);
MOZ_ASSERT(fieldInitializers.valid);
size_t numFields = fieldInitializers.numFieldInitializers;
@@ -7989,6 +8010,132 @@ BytecodeEmitter::emitCreateFieldInitializers(ListNode* obj)
return true;
}
+const FieldInitializers&
+BytecodeEmitter::findFieldInitializersForCall()
+{
+ for (BytecodeEmitter* current = this; current; current = current->parent) {
+ if (current->sc->isFunctionBox()) {
+ FunctionBox* box = current->sc->asFunctionBox();
+ if (box->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
+ const FieldInitializers& fieldInitializers = current->getFieldInitializers();
+ MOZ_ASSERT(fieldInitializers.valid);
+ return fieldInitializers;
+ }
+ }
+ }
+
+ for (ScopeIter si(innermostScope()); si; si++) {
+ if (si.scope()->is<FunctionScope>()) {
+ JSFunction* fun = si.scope()->as<FunctionScope>().canonicalFunction();
+ if (fun->kind() == JSFunction::FunctionKind::ClassConstructor) {
+ const FieldInitializers& fieldInitializers = fun->isInterpretedLazy()
+ ? fun->lazyScript()->getFieldInitializers()
+ : fun->nonLazyScript()->getFieldInitializers();
+ MOZ_ASSERT(fieldInitializers.valid);
+ return fieldInitializers;
+ }
+ }
+ }
+
+ MOZ_CRASH("Constructor for field initializers not found.");
+}
+
+bool
+BytecodeEmitter::emitCopyInitializersToLocalInitializers()
+{
+ MOZ_ASSERT(sc->asFunctionBox()->isDerivedClassConstructor());
+ if (getFieldInitializers().numFieldInitializers == 0)
+ return true;
+
+ NameOpEmitter noe(this, cx->names().dotLocalInitializers, NameOpEmitter::Kind::Initialize);
+ if (!noe.prepareForRhs()) {
+ // [stack]
+ return false;
+ }
+
+ if (!emitGetName(cx->names().dotInitializers)) {
+ // [stack] .initializers
+ return false;
+ }
+
+ if (!noe.emitAssignment()) {
+ // [stack] .initializers
+ return false;
+ }
+
+ if (!emit1(JSOP_POP)) {
+ // [stack]
+ return false;
+ }
+
+ return true;
+}
+
+bool
+BytecodeEmitter::emitInitializeInstanceFields(bool isSuperCall)
+{
+ const FieldInitializers& fieldInitializers = findFieldInitializersForCall();
+ size_t numFields = fieldInitializers.numFieldInitializers;
+
+ if (numFields == 0) {
+ return true;
+ }
+
+ if (isSuperCall) {
+ if (!emitGetName(cx->names().dotLocalInitializers)) {
+ // [stack] ARRAY
+ return false;
+ }
+ } else {
+ if (!emitGetName(cx->names().dotInitializers)) {
+ // [stack] ARRAY
+ return false;
+ }
+ }
+
+ for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
+ if (fieldIndex < numFields - 1) {
+ // We DUP to keep the array around (it is consumed in the bytecode below)
+ // for next iterations of this loop, except for the last iteration, which
+ // avoids an extra POP at the end of the loop.
+ if (!emit1(JSOP_DUP)) {
+ // [stack] ARRAY ARRAY
+ return false;
+ }
+ }
+
+ if (!emitNumberOp(fieldIndex)) {
+ // [stack] ARRAY? ARRAY INDEX
+ return false;
+ }
+
+ // Don't use CALLELEM here, because the receiver of the call != the receiver
+ // of this getelem. (Specifically, the call receiver is `this`, and the
+ // receiver of this getelem is `.initializers`)
+ if (!emit1(JSOP_GETELEM)) {
+ // [stack] ARRAY? FUNC
+ return false;
+ }
+
+ // This is guaranteed to run after super(), so we don't need TDZ checks.
+ if (!emitGetName(cx->names().dotThis)) {
+ // [stack] ARRAY? FUNC THIS
+ return false;
+ }
+
+ if (!emitCall(JSOP_CALL_IGNORES_RV, 0)) {
+ // [stack] ARRAY? RVAL
+ return false;
+ }
+
+ if (!emit1(JSOP_POP)) {
+ // [stack] ARRAY?
+ return false;
+ }
+ }
+
+ return true;
+}
// Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See
// the comment on emitSwitch.
@@ -8446,20 +8593,6 @@ BytecodeEmitter::emitLexicalInitialization(JSAtom* name)
return true;
}
-class AutoResetFieldInitializers
-{
- BytecodeEmitter* bce;
- FieldInitializers oldFieldInfo;
-
- public:
- AutoResetFieldInitializers(BytecodeEmitter* bce, FieldInitializers newFieldInfo)
- : bce(bce), oldFieldInfo(bce->fieldInitializers_)
- {
- bce->fieldInitializers_ = newFieldInfo;
- }
-
- ~AutoResetFieldInitializers() { bce->fieldInitializers_ = oldFieldInfo; }
-};
// This follows ES6 14.5.14 (ClassDefinitionEvaluation) and ES6 14.5.15
// (BindingClassDeclarationEvaluation).
@@ -8485,15 +8618,11 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
}
}
- // set this->fieldInitializers_
- AutoResetFieldInitializers _innermostClassAutoReset(this, setupFieldInitializers(classMembers));
-
// [stack]
ClassEmitter ce(this);
RootedAtom innerName(cx);
ClassEmitter::Kind kind = ClassEmitter::Kind::Expression;
-
if (names) {
innerName = names->innerBinding()->as<NameNode>().atom();
MOZ_ASSERT(innerName);
@@ -8503,8 +8632,10 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
MOZ_ASSERT(names->outerBinding()->as<NameNode>().atom() == innerName);
kind = ClassEmitter::Kind::Declaration;
}
+ }
- if (!ce.emitScopeForNamedClass(classNode->scopeBindings())) {
+ if (!classNode->isEmptyScope()) {
+ if (!ce.emitScope(classNode->scopeBindings(), classNode->names() != nullptr)) {
// [stack]
return false;
}
@@ -8534,7 +8665,7 @@ BytecodeEmitter::emitClass(ClassNode* classNode)
if (constructor) {
bool needsHomeObject = constructor->funbox()->needsHomeObject();
// HERITAGE is consumed inside emitFunction.
- if (!emitFunction(constructor, isDerived)) {
+ if (!emitFunction(constructor, isDerived, classMembers)) {
// [stack] HOMEOBJ CTOR
return false;
}
diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h
index 42faa78007..8a196a4799 100644
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -181,6 +181,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter
/* field info for enclosing class */
FieldInitializers fieldInitializers_;
+ const FieldInitializers& getFieldInitializers() { return fieldInitializers_; }
#ifdef DEBUG
bool unstableEmitterScope;
@@ -251,13 +252,15 @@ struct MOZ_STACK_CLASS BytecodeEmitter
*/
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
- EmitterMode emitterMode = Normal);
+ EmitterMode emitterMode = Normal,
+ FieldInitializers fieldInitializers = FieldInitializers::Invalid());
// An alternate constructor that uses a TokenPos for the starting
// line and that sets functionBodyEndPos as well.
BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler>* parser, SharedContext* sc,
HandleScript script, Handle<LazyScript*> lazyScript,
- TokenPos bodyPosition, EmitterMode emitterMode = Normal);
+ TokenPos bodyPosition, EmitterMode emitterMode = Normal,
+ FieldInitializers fieldInitializers = FieldInitializers::Invalid());
MOZ_MUST_USE bool init();
@@ -514,7 +517,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
MOZ_MUST_USE bool emitObjectPairOp(ObjectBox* objbox1, ObjectBox* objbox2, JSOp op);
MOZ_MUST_USE bool emitRegExp(uint32_t index);
- MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(FunctionNode* funNode, bool needsProto = false);
+ MOZ_NEVER_INLINE MOZ_MUST_USE bool emitFunction(FunctionNode* funNode,
+ bool needsProto = false,
+ ListNode* classContentsIfConstructor = nullptr);
MOZ_NEVER_INLINE MOZ_MUST_USE bool emitObject(ListNode* objNode);
MOZ_MUST_USE bool replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset);
@@ -527,6 +532,9 @@ struct MOZ_STACK_CLASS BytecodeEmitter
FieldInitializers setupFieldInitializers(ListNode* classMembers);
MOZ_MUST_USE bool emitCreateFieldKeys(ListNode* obj);
MOZ_MUST_USE bool emitCreateFieldInitializers(ListNode* obj);
+ const FieldInitializers& findFieldInitializersForCall();
+ MOZ_MUST_USE bool emitCopyInitializersToLocalInitializers();
+ MOZ_MUST_USE bool emitInitializeInstanceFields(bool isSuperCall);
// To catch accidental misuse, emitUint16Operand/emit3 assert that they are
// not used to unconditionally emit JSOP_GETLOCAL. Variable access should
diff --git a/js/src/frontend/FullParseHandler.h b/js/src/frontend/FullParseHandler.h
index 3f52e88d54..d8ac80e2c6 100644
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -367,7 +367,9 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_AS)
return literal;
}
- ClassNodeType newClass(Node name, Node heritage, Node memberBlock, const TokenPos& pos) {
+ ClassNodeType newClass(Node name, Node heritage, LexicalScopeNodeType memberBlock,
+ const TokenPos& pos)
+ {
return new_<ClassNode>(name, heritage, memberBlock, pos);
}
ListNodeType newClassMemberList(uint32_t begin) {
diff --git a/js/src/frontend/FunctionEmitter.cpp b/js/src/frontend/FunctionEmitter.cpp
index 9ca84427c7..481ad3ecd6 100644
--- a/js/src/frontend/FunctionEmitter.cpp
+++ b/js/src/frontend/FunctionEmitter.cpp
@@ -477,9 +477,16 @@ bool FunctionScriptEmitter::prepareForBody()
}
if (funbox_->function()->kind() == JSFunction::FunctionKind::ClassConstructor) {
- if (!emitInitializeInstanceFields()) {
- // [stack]
- return false;
+ if (funbox_->isDerivedClassConstructor()) {
+ if (!bce_->emitCopyInitializersToLocalInitializers()) {
+ // [stack]
+ return false;
+ }
+ } else {
+ if (!bce_->emitInitializeInstanceFields(false)) {
+ // [stack]
+ return false;
+ }
}
}
@@ -551,64 +558,6 @@ bool FunctionScriptEmitter::emitExtraBodyVarScope()
return true;
}
-bool FunctionScriptEmitter::emitInitializeInstanceFields()
-{
- MOZ_ASSERT(bce_->fieldInitializers_.valid);
- size_t numFields = bce_->fieldInitializers_.numFieldInitializers;
-
- if (numFields == 0) {
- return true;
- }
-
- if (!bce_->emitGetName(bce_->cx->names().dotInitializers)) {
- // [stack] ARRAY
- return false;
- }
-
- for (size_t fieldIndex = 0; fieldIndex < numFields; fieldIndex++) {
- if (fieldIndex < numFields - 1) {
- // We DUP to keep the array around (it is consumed in the bytecode below)
- // for next iterations of this loop, except for the last iteration, which
- // avoids an extra POP at the end of the loop.
- if (!bce_->emit1(JSOP_DUP)) {
- // [stack] ARRAY ARRAY
- return false;
- }
- }
-
- if (!bce_->emitNumberOp(fieldIndex)) {
- // [stack] ARRAY? ARRAY INDEX
- return false;
- }
-
- // Don't use CALLELEM here, because the receiver of the call != the receiver
- // of this getelem. (Specifically, the call receiver is `this`, and the
- // receiver of this getelem is `.initializers`)
- if (!bce_->emit1(JSOP_GETELEM)) {
- // [stack] ARRAY? FUNC
- return false;
- }
-
- // This is guaranteed to run after super(), so we don't need TDZ checks.
- if (!bce_->emitGetName(bce_->cx->names().dotThis)) {
- // [stack] ARRAY? FUNC THIS
- return false;
- }
-
- if (!bce_->emitCall(JSOP_CALL_IGNORES_RV, 0)) {
- // [stack] ARRAY? RVAL
- return false;
- }
-
- if (!bce_->emit1(JSOP_POP)) {
- // [stack] ARRAY?
- return false;
- }
- }
-
- return true;
-}
-
bool FunctionScriptEmitter::emitEndBody()
{
MOZ_ASSERT(state_ == State::Body);
diff --git a/js/src/frontend/FunctionEmitter.h b/js/src/frontend/FunctionEmitter.h
index 0158fed251..ea4dc68126 100644
--- a/js/src/frontend/FunctionEmitter.h
+++ b/js/src/frontend/FunctionEmitter.h
@@ -257,7 +257,6 @@ class MOZ_STACK_CLASS FunctionScriptEmitter {
private:
MOZ_MUST_USE bool emitExtraBodyVarScope();
- MOZ_MUST_USE bool emitInitializeInstanceFields();
};
// Class for emitting function parameters.
diff --git a/js/src/frontend/ObjectEmitter.cpp b/js/src/frontend/ObjectEmitter.cpp
index 5481962381..5dd96d6b32 100644
--- a/js/src/frontend/ObjectEmitter.cpp
+++ b/js/src/frontend/ObjectEmitter.cpp
@@ -531,14 +531,16 @@ ClassEmitter::ClassEmitter(BytecodeEmitter* bce)
isClass_ = true;
}
-bool ClassEmitter::emitScopeForNamedClass(JS::Handle<LexicalScope::Data*> scopeBindings)
+bool ClassEmitter::emitScope(JS::Handle<LexicalScope::Data*> scopeBindings, bool hasName)
{
MOZ_ASSERT(propertyState_ == PropertyState::Start);
MOZ_ASSERT(classState_ == ClassState::Start);
- tdzCacheForInnerName_.emplace(bce_);
- innerNameScope_.emplace(bce_);
- if (!innerNameScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings))
+ if (hasName)
+ tdzCacheForInnerName_.emplace(bce_);
+
+ innerScope_.emplace(bce_);
+ if (!innerScope_->enterLexical(bce_, ScopeKind::Lexical, scopeBindings))
return false;
#ifdef DEBUG
@@ -716,7 +718,7 @@ bool ClassEmitter::emitEnd(Kind kind)
if (name_ != bce_->cx->names().empty) {
MOZ_ASSERT(tdzCacheForInnerName_.isSome());
- MOZ_ASSERT(innerNameScope_.isSome());
+ MOZ_ASSERT(innerScope_.isSome());
if (!bce_->emitLexicalInitialization(name_)) {
// [stack] CTOR
@@ -724,9 +726,9 @@ bool ClassEmitter::emitEnd(Kind kind)
}
// Pop the inner scope.
- if (!innerNameScope_->leave(bce_))
+ if (!innerScope_->leave(bce_))
return false;
- innerNameScope_.reset();
+ innerScope_.reset();
if (kind == Kind::Declaration) {
if (!bce_->emitLexicalInitialization(name_)) {
@@ -742,9 +744,18 @@ bool ClassEmitter::emitEnd(Kind kind)
}
tdzCacheForInnerName_.reset();
- } else {
+ } else if (innerScope_.isSome()) {
+ // [stack] CTOR
+ MOZ_ASSERT(kind == Kind::Expression);
+ MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
+
+ if (!innerScope_->leave(bce_))
+ return false;
+ innerScope_.reset();
+ }else {
// [stack] CTOR
+ MOZ_ASSERT(kind == Kind::Expression);
MOZ_ASSERT(tdzCacheForInnerName_.isNothing());
}
diff --git a/js/src/frontend/ObjectEmitter.h b/js/src/frontend/ObjectEmitter.h
index 247e221761..b389c3e180 100644
--- a/js/src/frontend/ObjectEmitter.h
+++ b/js/src/frontend/ObjectEmitter.h
@@ -464,6 +464,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class {}`
// ClassEmitter ce(this);
+// ce.emitScope(scopeBindings, false);
// ce.emitClass();
//
// ce.emitInitDefaultConstructor(Some(offset_of_class),
@@ -473,6 +474,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class { constructor() { ... } }`
// ClassEmitter ce(this);
+// ce.emitScope(scopeBindings, false);
// ce.emitClass();
//
// emit(function_for_constructor);
@@ -482,7 +484,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class X { constructor() { ... } }`
// ClassEmitter ce(this);
-// ce.emitScopeForNamedClass(scopeBindingForName);
+// ce.emitScope(scopeBindings, true);
// ce.emitClass(atom_of_X);
//
// ce.emitInitDefaultConstructor(Some(offset_of_class),
@@ -492,7 +494,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class X { constructor() { ... } }`
// ClassEmitter ce(this);
-// ce.emitScopeForNamedClass(scopeBindingForName);
+// ce.emitScope(scopeBindings, true);
// ce.emitClass(atom_of_X);
//
// emit(function_for_constructor);
@@ -502,7 +504,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class X extends Y { constructor() { ... } }`
// ClassEmitter ce(this);
-// ce.emitScopeForNamedClass(scopeBindingForName);
+// ce.emitScope(scopeBindings, true);
//
// emit(Y);
// ce.emitDerivedClass(atom_of_X);
@@ -514,7 +516,7 @@ class MOZ_RAII AutoSaveLocalStrictMode
//
// `class X extends Y { constructor() { ... super.f(); ... } }`
// ClassEmitter ce(this);
-// ce.emitScopeForNamedClass(scopeBindingForName);
+// ce.emitScope(scopeBindings, true);
//
// emit(Y);
// ce.emitDerivedClass(atom_of_X);
@@ -629,21 +631,21 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
bool isDerived_ = false;
mozilla::Maybe<TDZCheckCache> tdzCacheForInnerName_;
- mozilla::Maybe<EmitterScope> innerNameScope_;
+ mozilla::Maybe<EmitterScope> innerScope_;
AutoSaveLocalStrictMode strictMode_;
#ifdef DEBUG
// The state of this emitter.
//
// +-------+
- // | Start |-+------------------------------------>+-+
- // +-------+ | ^ |
- // | [named class] | |
- // | emitScopeForNamedClass +-------+ | |
- // +-------------------------->| Scope |-+ |
- // +-------+ |
- // |
- // +-----------------------------------------------+
+ // | Start |-+------------------------>+-+
+ // +-------+ | ^ |
+ // | [has scope] | |
+ // | emitScope +-------+ | |
+ // +-------------->| Scope |-+ |
+ // +-------+ |
+ // |
+ // +-----------------------------------+
// |
// | emitClass +-------+
// +-+----------------->+->| Class |-+
@@ -669,7 +671,7 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
// The initial state.
Start,
- // After calling emitScopeForNamedClass.
+ // After calling emitScope.
Scope,
// After calling emitClass or emitDerivedClass.
@@ -689,8 +691,7 @@ class MOZ_STACK_CLASS ClassEmitter : public PropertyEmitter
public:
explicit ClassEmitter(BytecodeEmitter* bce);
- MOZ_MUST_USE bool emitScopeForNamedClass(
- JS::Handle<LexicalScope::Data*> scopeBindings);
+ MOZ_MUST_USE bool emitScope(JS::Handle<LexicalScope::Data*> scopeBindings, bool hasName);
// @param name
// Name of the class (nullptr if this is anonymous class)
diff --git a/js/src/frontend/ParseNode.h b/js/src/frontend/ParseNode.h
index fffda6a73d..da722a51e7 100644
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -252,10 +252,7 @@ IsTypeofKind(ParseNodeKind kind)
* PNK_CLASS (ClassNode)
* kid1: PNK_CLASSNAMES for class name. can be null for anonymous class.
* kid2: expression after `extends`. null if no expression
- * kid3: either of
- * * PNK_CLASSMEMBERLIST, if anonymous class
- * * PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody,
- * if named class
+ * kid3: PNK_LEXICALSCOPE which contains PNK_CLASSMEMBERLIST as scopeBody
* PNK_CLASSNAMES (ClassNames)
* left: Name node for outer binding, or null if the class is an expression
* that doesn't create an outer binding
@@ -2243,13 +2240,11 @@ class ClassNames : public BinaryNode
class ClassNode : public TernaryNode
{
public:
- ClassNode(ParseNode* names, ParseNode* heritage, ParseNode* membersOrBlock,
+ ClassNode(ParseNode* names, ParseNode* heritage, LexicalScopeNode* memberBlock,
const TokenPos& pos)
- : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, membersOrBlock, pos)
+ : TernaryNode(PNK_CLASS, JSOP_NOP, names, heritage, memberBlock, pos)
{
MOZ_ASSERT_IF(names, names->is<ClassNames>());
- MOZ_ASSERT(membersOrBlock->is<LexicalScopeNode>() ||
- membersOrBlock->isKind(PNK_CLASSMEMBERLIST));
}
static bool test(const ParseNode& node) {
@@ -2265,14 +2260,14 @@ class ClassNode : public TernaryNode
return kid2();
}
ListNode* memberList() const {
- ParseNode* membersOrBlock = kid3();
- if (membersOrBlock->isKind(PNK_CLASSMEMBERLIST))
- return &membersOrBlock->as<ListNode>();
-
- ListNode* list = &membersOrBlock->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
+ ListNode* list = &kid3()->as<LexicalScopeNode>().scopeBody()->as<ListNode>();
MOZ_ASSERT(list->isKind(PNK_CLASSMEMBERLIST));
return list;
}
+ bool isEmptyScope() const {
+ ParseNode* scope = kid3();
+ return scope->as<LexicalScopeNode>().isEmptyScope();
+ }
Handle<LexicalScope::Data*> scopeBindings() const {
ParseNode* scope = kid3();
return scope->as<LexicalScopeNode>().scopeBindings();
diff --git a/js/src/frontend/Parser.cpp b/js/src/frontend/Parser.cpp
index 77fa337fb7..8e09aea626 100644
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -2779,6 +2779,12 @@ Parser<ParseHandler>::functionBody(InHandling inHandling, YieldHandling yieldHan
return null();
}
+ if (kind == FunctionSyntaxKind::DerivedClassConstructor) {
+ if (!noteDeclaredName(context->names().dotLocalInitializers,
+ DeclarationKind::Var, pos()))
+ return null();
+ }
+
return finishLexicalScope(pc->varScope(), body);
}
@@ -3600,7 +3606,11 @@ Parser<FullParseHandler>::standaloneLazyFunction(HandleFunction fun, bool strict
FunctionSyntaxKind syntaxKind = FunctionSyntaxKind::Statement;
if (fun->isClassConstructor()) {
- syntaxKind = FunctionSyntaxKind::ClassConstructor;
+ if (fun->isDerivedClassConstructor()) {
+ syntaxKind = FunctionSyntaxKind::DerivedClassConstructor;
+ } else {
+ syntaxKind = FunctionSyntaxKind::ClassConstructor;
+ }
} else if (fun->isMethod()) {
syntaxKind = FunctionSyntaxKind::Method;
} else if (fun->isGetter()) {
@@ -7468,7 +7478,7 @@ Parser<ParseHandler>::classMember(YieldHandling yieldHandling, DefaultHandling d
numFields++;
- FunctionNodeType initializer = fieldInitializerOpt(yieldHandling, hasHeritage, propName,
+ FunctionNodeType initializer = fieldInitializerOpt(yieldHandling, hasHeritage,
propAtom, numFieldKeys);
if (!initializer)
return false;
@@ -7553,8 +7563,9 @@ Parser<ParseHandler>::classMember(YieldHandling yieldHandling, DefaultHandling d
template <typename ParseHandler>
bool
Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement& classStmt,
- HandlePropertyName className, uint32_t classStartOffset,
- uint32_t classEndOffset, size_t numFields,
+ HandlePropertyName className, bool hasHeritage,
+ uint32_t classStartOffset, uint32_t classEndOffset,
+ size_t numFields,
ListNodeType& classMembers)
{
// Fields cannot re-use the constructor obtained via JSOP_CLASSCONSTRUCTOR or
@@ -7562,7 +7573,7 @@ Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement&
// initializers in the constructor. So, synthesize a new one.
if (classStmt.constructorBox == nullptr && numFields > 0) {
// synthesizeConstructor assigns to classStmt.constructorBox
- FunctionNodeType synthesizedCtor = synthesizeConstructor(className, classStartOffset);
+ FunctionNodeType synthesizedCtor = synthesizeConstructor(className, classStartOffset, hasHeritage);
if (!synthesizedCtor) {
return false;
}
@@ -7600,12 +7611,6 @@ Parser<ParseHandler>::finishClassConstructor(const ParseContext::ClassStatement&
if (numFields > 0) {
ctorbox->function()->lazyScript()->setHasThisBinding();
}
-
- // Field initializers can be retrieved if the class and constructor are
- // being compiled at the same time, but we need to stash the field
- // information if the constructor is being compiled lazily.
- FieldInitializers fieldInfo(numFields);
- ctorbox->function()->lazyScript()->setFieldInitializers(fieldInfo);
}
}
@@ -7724,7 +7729,7 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
pc->innermostScope()->id()))
return null();
if (!noteDeclaredName(context->names().dotInitializers,
- DeclarationKind::Var, namePos))
+ DeclarationKind::Let, namePos))
return null();
}
@@ -7733,8 +7738,8 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
return null();
}
classEndOffset = pos().end;
- if (!finishClassConstructor(classStmt, className, classStartOffset,
- classEndOffset, numFields, classMembers))
+ if (!finishClassConstructor(classStmt, className, hasHeritage,
+ classStartOffset, classEndOffset, numFields, classMembers))
return null();
if (className) {
@@ -7779,9 +7784,10 @@ Parser<ParseHandler>::classDefinition(YieldHandling yieldHandling,
template <class ParseHandler>
typename ParseHandler::FunctionNodeType
-Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t classNameOffset)
+Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t classNameOffset, bool hasHeritage)
{
- FunctionSyntaxKind functionSyntaxKind = FunctionSyntaxKind::ClassConstructor;
+ FunctionSyntaxKind functionSyntaxKind = hasHeritage ? FunctionSyntaxKind::DerivedClassConstructor
+ : FunctionSyntaxKind::ClassConstructor;
// Create the function object.
RootedFunction fun(context, newFunction(className, functionSyntaxKind,
@@ -7820,6 +7826,8 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
funbox->function()->setArgCount(0);
funbox->setStart(tokenStream);
+ pc->functionScope().useAsVarScope(pc);
+
// Push a LexicalScope on to the stack.
ParseContext::Scope lexicalScope(this);
if (!lexicalScope.init(pc))
@@ -7835,10 +7843,55 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
// One might expect a noteUsedName(".initializers") here. See comment in
// GeneralParser<ParseHandler, Unit>::classDefinition on why it's not here.
+ if (hasHeritage) {
+ if (!noteDeclaredName(context->names().dotLocalInitializers,
+ DeclarationKind::Var, synthesizedBodyPos))
+ return null();
+ }
+
bool canSkipLazyClosedOverBindings = handler.canSkipLazyClosedOverBindings();
if (!declareFunctionThis(canSkipLazyClosedOverBindings))
return null();
+ if (hasHeritage) {
+ // {Goanna} Need a different this-NameNode for SuperBase and SetThis or the recycling
+ // by ParseNodeAllocator runs into all sorts of problems because the
+ // same ParseNode gets cleaned up twice.
+ // Parser<ParseHandler>::memberExpr does the same.
+ NameNodeType thisNameBase = newThisName();
+ if (!thisNameBase)
+ return null();
+
+ UnaryNodeType superBase = handler.newSuperBase(thisNameBase, synthesizedBodyPos);
+ if (!superBase)
+ return null();
+
+ ListNodeType arguments = handler.newArguments(synthesizedBodyPos);
+ if (!arguments)
+ return null();
+
+ BinaryNodeType superCall = handler.newSuperCall(superBase, arguments, false);
+ if (!superCall)
+ return null();
+
+ NameNodeType thisName = newThisName();
+ if (!thisName)
+ return null();
+
+ BinaryNodeType setThis = handler.newSetThis(thisName, superCall);
+ if (!setThis)
+ return null();
+
+ if (!noteUsedName(context->names().dotLocalInitializers))
+ return null();
+
+ UnaryNodeType exprStatement = handler.newExprStatement(setThis, synthesizedBodyPos.end);
+ if (!exprStatement)
+ return null();
+
+ handler.addStatementToList(stmtList, exprStatement);
+ }
+
auto initializerBody = finishLexicalScope(lexicalScope, stmtList);
if (!initializerBody)
return null();
@@ -7860,7 +7913,7 @@ Parser<ParseHandler>::synthesizeConstructor(HandleAtom className, uint32_t class
template <class ParseHandler>
typename ParseHandler::FunctionNodeType
Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
- Node propName, HandleAtom propAtom, size_t& numFieldKeys)
+ HandleAtom propAtom, size_t& numFieldKeys)
{
bool hasInitializer = false;
if (!tokenStream.matchToken(&hasInitializer, TOK_ASSIGN))
@@ -7878,9 +7931,9 @@ Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasH
firstTokenPos = TokenPos(endPos, endPos);
}
- // Create the function object.
+ // Create the anonymous function object.
RootedFunction fun(context,
- newFunction(propAtom, FunctionSyntaxKind::Expression,
+ newFunction(nullptr, FunctionSyntaxKind::Expression,
GeneratorKind::NotGenerator,
FunctionAsyncKind::SyncFunction));
if (!fun)
@@ -7981,7 +8034,12 @@ Parser<ParseHandler>::fieldInitializerOpt(YieldHandling yieldHandling, bool hasH
if (!propAssignFieldAccess)
return null();
} else if (propAtom->isIndex(&indexValue)) {
- propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, propName, wholeInitializerPos.end);
+ // {Goanna} Can't reuse propName here, see comment in synthesizeConstructor
+ Node indexNode = handler.newNumber(indexValue, DecimalPoint::NoDecimal, wholeInitializerPos);
+ if (!indexNode)
+ return null();
+
+ propAssignFieldAccess = handler.newPropertyByValue(propAssignThis, indexNode, wholeInitializerPos.end);
if (!propAssignFieldAccess)
return null();
} else {
@@ -9908,6 +9966,9 @@ Parser<ParseHandler>::memberExpr(YieldHandling yieldHandling, TripledotHandling
nextMember = handler.newSetThis(thisName, nextMember);
if (!nextMember)
return null();
+
+ if (!noteUsedName(context->names().dotLocalInitializers))
+ return null();
} else {
nextMember = memberCall(tt, lhs, yieldHandling, possibleError);
if (!nextMember)
diff --git a/js/src/frontend/Parser.h b/js/src/frontend/Parser.h
index f7feefcaab..5a0722afa7 100644
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -1498,14 +1498,15 @@ FOR_EACH_PARSENODE_SUBCLASS(DECLARE_TYPE)
ListNodeType& classMembers, bool* done);
MOZ_MUST_USE bool finishClassConstructor(
const ParseContext::ClassStatement& classStmt,
- HandlePropertyName className, uint32_t classStartOffset,
- uint32_t classEndOffset, size_t numFieldsWithInitializers,
- ListNodeType& classMembers);
+ HandlePropertyName className, bool hasHeritage,
+ uint32_t classStartOffset, uint32_t classEndOffset,
+ size_t numFieldsWithInitializers, ListNodeType& classMembers);
FunctionNodeType fieldInitializerOpt(YieldHandling yieldHandling, bool hasHeritage,
- Node name, HandleAtom atom, size_t& numFieldKeys);
+ HandleAtom atom, size_t& numFieldKeys);
FunctionNodeType synthesizeConstructor(HandleAtom className,
- uint32_t classNameOffset);
+ uint32_t classNameOffset,
+ bool hasHeritage);
bool checkLabelOrIdentifierReference(PropertyName* ident,
uint32_t offset,
diff --git a/js/src/jsscript.h b/js/src/jsscript.h
index fbdd658b79..5534df5806 100644
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -704,6 +704,35 @@ class ScriptSourceObject : public NativeObject
enum GeneratorKind { NotGenerator, LegacyGenerator, StarGenerator };
enum FunctionAsyncKind { SyncFunction, AsyncFunction };
+struct FieldInitializers
+{
+#ifdef DEBUG
+ bool valid;
+#endif
+ // This struct will eventually have a vector of constant values for optimizing
+ // field initializers.
+ size_t numFieldInitializers;
+
+ explicit FieldInitializers(size_t numFieldInitializers)
+ :
+#ifdef DEBUG
+ valid(true),
+#endif
+ numFieldInitializers(numFieldInitializers) {
+ }
+
+ static FieldInitializers Invalid() { return FieldInitializers(); }
+
+ private:
+ FieldInitializers()
+ :
+#ifdef DEBUG
+ valid(false),
+#endif
+ numFieldInitializers(0) {
+ }
+};
+
static inline unsigned
GeneratorKindAsBits(GeneratorKind generatorKind) {
return static_cast<unsigned>(generatorKind);
@@ -855,6 +884,8 @@ class JSScript : public js::gc::TenuredCell
private:
js::SharedScriptData* scriptData_;
+
+ js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid();
public:
uint8_t* data; /* pointer to variable-length data array (see
comment above Create() for details) */
@@ -1454,6 +1485,11 @@ class JSScript : public js::gc::TenuredCell
return functionHasThisBinding_;
}
+ void setFieldInitializers(js::FieldInitializers fieldInitializers) {
+ fieldInitializers_ = fieldInitializers;
+ }
+ const js::FieldInitializers& getFieldInitializers() const { return fieldInitializers_; }
+
/*
* Arguments access (via JSOP_*ARG* opcodes) must access the canonical
* location for the argument. If an arguments object exists AND it's mapped
@@ -1999,35 +2035,6 @@ static_assert(sizeof(JSScript) % js::gc::CellSize == 0,
namespace js {
-struct FieldInitializers
-{
-#ifdef DEBUG
- bool valid;
-#endif
- // This struct will eventually have a vector of constant values for optimizing
- // field initializers.
- size_t numFieldInitializers;
-
- explicit FieldInitializers(size_t numFieldInitializers)
- :
-#ifdef DEBUG
- valid(true),
-#endif
- numFieldInitializers(numFieldInitializers) {
- }
-
- static FieldInitializers Invalid() { return FieldInitializers(); }
-
- private:
- FieldInitializers()
- :
-#ifdef DEBUG
- valid(false),
-#endif
- numFieldInitializers(0) {
- }
-};
-
// Information about a script which may be (or has been) lazily compiled to
// bytecode from its source.
class LazyScript : public gc::TenuredCell
@@ -2330,7 +2337,7 @@ class LazyScript : public gc::TenuredCell
fieldInitializers_ = fieldInitializers;
}
- FieldInitializers getFieldInitializers() const { return fieldInitializers_; }
+ const FieldInitializers& getFieldInitializers() const { return fieldInitializers_; }
const char* filename() const {
return scriptSource()->filename();
diff --git a/js/src/vm/CommonPropertyNames.h b/js/src/vm/CommonPropertyNames.h
index f69dfe3ca9..bc2e4204e3 100644
--- a/js/src/vm/CommonPropertyNames.h
+++ b/js/src/vm/CommonPropertyNames.h
@@ -103,6 +103,7 @@
macro(dotGenerator, dotGenerator, ".generator") \
macro(dotThis, dotThis, ".this") \
macro(dotInitializers, dotInitializers, ".initializers") \
+ macro(dotLocalInitializers, dotLocalInitializers, ".localInitializers") \
macro(dotFieldKeys, dotFieldKeys, ".fieldKeys") \
macro(each, each, "each") \
macro(elementType, elementType, "elementType") \