summaryrefslogtreecommitdiff
path: root/js/src/frontend/BytecodeEmitter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/frontend/BytecodeEmitter.cpp')
-rw-r--r--js/src/frontend/BytecodeEmitter.cpp193
1 files changed, 162 insertions, 31 deletions
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;
}