diff options
author | Martok <martok@martoks-place.de> | 2023-04-08 03:47:06 +0200 |
---|---|---|
committer | Martok <martok@martoks-place.de> | 2023-05-01 17:16:18 +0200 |
commit | f7935ec5b2cfb652b403c1141b585ade25e56135 (patch) | |
tree | d2b2e2ce1febf12a38659171e263483052fd64a0 /js/src | |
parent | b72b80ed589b50546446018f750b8eab3fbbfed6 (diff) | |
download | uxp-f7935ec5b2cfb652b403c1141b585ade25e56135.tar.gz |
Issue #2142 - Handle fields in derived classes
* Don't name field initializer lambdas
Based-on: m-c 1534721, 1551454, 1542448
Diffstat (limited to 'js/src')
-rw-r--r-- | js/src/frontend/BytecodeCompiler.cpp | 7 | ||||
-rw-r--r-- | js/src/frontend/BytecodeEmitter.cpp | 193 | ||||
-rw-r--r-- | js/src/frontend/BytecodeEmitter.h | 14 | ||||
-rw-r--r-- | js/src/frontend/FullParseHandler.h | 4 | ||||
-rw-r--r-- | js/src/frontend/FunctionEmitter.cpp | 71 | ||||
-rw-r--r-- | js/src/frontend/FunctionEmitter.h | 1 | ||||
-rw-r--r-- | js/src/frontend/ObjectEmitter.cpp | 27 | ||||
-rw-r--r-- | js/src/frontend/ObjectEmitter.h | 33 | ||||
-rw-r--r-- | js/src/frontend/ParseNode.h | 21 | ||||
-rw-r--r-- | js/src/frontend/Parser.cpp | 101 | ||||
-rw-r--r-- | js/src/frontend/Parser.h | 11 | ||||
-rw-r--r-- | js/src/jsscript.h | 67 | ||||
-rw-r--r-- | js/src/vm/CommonPropertyNames.h | 1 |
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") \ |