Bug 1451826 - Part 10: Convert BCE to using BCEParserHandle. (r=Waldo)
authorEric Faust <efaustbmo@gmail.com>
Fri, 27 Apr 2018 15:02:31 -0700
changeset 472249 e4ce5e759df5b08bfc1bf5f5f5f4892cf46b0dad
parent 472248 60bdf6086590f974614e0b1d202ff4849f5a2c45
child 472250 abedb29f91fd6b736ed109dcefad1951450c7271
push id1728
push userjlund@mozilla.com
push dateMon, 18 Jun 2018 21:12:27 +0000
treeherdermozilla-release@c296fde26f5f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersWaldo
bugs1451826
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1451826 - Part 10: Convert BCE to using BCEParserHandle. (r=Waldo)
js/src/frontend/BCEParserHandle.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
--- a/js/src/frontend/BCEParserHandle.h
+++ b/js/src/frontend/BCEParserHandle.h
@@ -9,17 +9,17 @@
 
 #include "frontend/ErrorReporter.h"
 #include "frontend/FullParseHandler.h"
 #include "frontend/Parser.h"
 
 namespace js {
 namespace frontend {
 
-class BCEParserHandle {
+struct BCEParserHandle {
     virtual ErrorReporter& errorReporter() = 0;
     virtual const ErrorReporter& errorReporter() const = 0;
 
     virtual const JS::ReadOnlyCompileOptions& options() = 0;
 
     virtual FullParseHandler& astGenerator() = 0;
     virtual ObjectBox* newObjectBox(JSObject *obj) = 0;
 };
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2135,29 +2135,29 @@ class ForOfLoopControl : public LoopCont
             if (!bce->emit1(JSOP_POP))                    //
                 return false;
         }
 
         return true;
     }
 };
 
+
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
-                                 const EitherParser& parser, SharedContext* sc,
-                                 HandleScript script, Handle<LazyScript*> lazyScript,
+                                 SharedContext* sc, HandleScript script,
+                                 Handle<LazyScript*> lazyScript,
                                  uint32_t lineNum, EmitterMode emitterMode)
   : sc(sc),
     cx(sc->context),
     parent(parent),
     script(cx, script),
     lazyScript(cx, lazyScript),
     prologue(cx, lineNum),
     main(cx, lineNum),
     current(&main),
-    parser(parser),
     atomIndices(cx->frontendCollectionPool()),
     firstLine(lineNum),
     maxFixedSlots(0),
     maxStackDepth(0),
     stackDepth(0),
     emitLevel(0),
     bodyScopeIndex(UINT32_MAX),
     varEmitterScope(nullptr),
@@ -2176,22 +2176,36 @@ BytecodeEmitter::BytecodeEmitter(Bytecod
     emitterMode(emitterMode),
     scriptStartOffsetSet(false),
     functionBodyEndPosSet(false)
 {
     MOZ_ASSERT_IF(emitterMode == LazyFunction, lazyScript);
 }
 
 BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
+                                 BCEParserHandle* handle, SharedContext* sc,
+                                 HandleScript script, Handle<LazyScript*> lazyScript,
+                                 uint32_t lineNum, EmitterMode emitterMode)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode)
+{
+    parser = handle;
+}
+
+BytecodeEmitter::BytecodeEmitter(BytecodeEmitter* parent,
                                  const EitherParser& parser, SharedContext* sc,
                                  HandleScript script, Handle<LazyScript*> lazyScript,
-                                 TokenPos bodyPosition, EmitterMode emitterMode)
-    : BytecodeEmitter(parent, parser, sc, script, lazyScript,
-                      parser.errorReporter().lineAt(bodyPosition.begin),
-                      emitterMode)
+                                 uint32_t lineNum, EmitterMode emitterMode)
+    : BytecodeEmitter(parent, sc, script, lazyScript, lineNum, emitterMode)
+{
+    ep_.emplace(parser);
+    this->parser = ep_.ptr();
+}
+
+void
+BytecodeEmitter::initFromBodyPosition(TokenPos bodyPosition)
 {
     setScriptStartOffsetIfUnset(bodyPosition);
     setFunctionBodyEndPos(bodyPosition);
 }
 
 bool
 BytecodeEmitter::init()
 {
@@ -2523,17 +2537,17 @@ LengthOfSetLine(unsigned line)
 {
     return 1 /* SRC_SETLINE */ + (line > SN_4BYTE_OFFSET_MASK ? 4 : 1);
 }
 
 /* Updates line number notes, not column notes. */
 bool
 BytecodeEmitter::updateLineNumberNotes(uint32_t offset)
 {
-    ErrorReporter* er = &parser.errorReporter();
+    ErrorReporter* er = &parser->errorReporter();
     bool onThisLine;
     if (!er->isOnThisLine(offset, currentLine(), &onThisLine)) {
         er->reportErrorNoOffset(JSMSG_OUT_OF_MEMORY);
         return false;
     }
 
     if (!onThisLine) {
         unsigned line = er->lineAt(offset);
@@ -2567,17 +2581,17 @@ BytecodeEmitter::updateLineNumberNotes(u
 
 /* Updates the line number and column number information in the source notes. */
 bool
 BytecodeEmitter::updateSourceCoordNotes(uint32_t offset)
 {
     if (!updateLineNumberNotes(offset))
         return false;
 
-    uint32_t columnIndex = parser.errorReporter().columnAt(offset);
+    uint32_t columnIndex = parser->errorReporter().columnAt(offset);
     ptrdiff_t colspan = ptrdiff_t(columnIndex) - ptrdiff_t(current->lastColumn);
     if (colspan != 0) {
         // If the column span is so large that we can't store it, then just
         // discard this information. This can happen with minimized or otherwise
         // machine-generated code. Even gigantic column numbers are still
         // valuable if you have a source map to relate them to something real;
         // but it's better to fail soft here.
         if (!SN_REPRESENTABLE_COLSPAN(colspan))
@@ -3543,31 +3557,31 @@ void
 BytecodeEmitter::reportError(ParseNode* pn, unsigned errorNumber, ...)
 {
     MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
     uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
     va_list args;
     va_start(args, errorNumber);
 
-    parser.errorReporter().errorAtVA(offset, errorNumber, &args);
+    parser->errorReporter().errorAtVA(offset, errorNumber, &args);
 
     va_end(args);
 }
 
 bool
 BytecodeEmitter::reportExtraWarning(ParseNode* pn, unsigned errorNumber, ...)
 {
     MOZ_ASSERT_IF(!pn, this->scriptStartOffsetSet);
     uint32_t offset = pn ? pn->pn_pos.begin : this->scriptStartOffset;
 
     va_list args;
     va_start(args, errorNumber);
 
-    bool result = parser.errorReporter().reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args);
+    bool result = parser->errorReporter().reportExtraWarningErrorNumberVA(nullptr, offset, errorNumber, &args);
 
     va_end(args);
     return result;
 }
 
 bool
 BytecodeEmitter::emitNewInit(JSProtoKey key)
 {
@@ -3599,17 +3613,17 @@ BytecodeEmitter::iteratorResultShape(uns
 
     Rooted<jsid> value_id(cx, NameToId(cx->names().value));
     Rooted<jsid> done_id(cx, NameToId(cx->names().done));
     if (!NativeDefineDataProperty(cx, obj, value_id, UndefinedHandleValue, JSPROP_ENUMERATE))
         return false;
     if (!NativeDefineDataProperty(cx, obj, done_id, UndefinedHandleValue, JSPROP_ENUMERATE))
         return false;
 
-    ObjectBox* objbox = parser.newObjectBox(obj);
+    ObjectBox* objbox = parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
     *shape = objectList.add(objbox);
 
     return true;
 }
 
@@ -4808,17 +4822,17 @@ BytecodeEmitter::emitSetThis(ParseNode* 
     }
 
     return emitSetOrInitializeNameAtLocation(name, lexicalLoc, emitRhs, true);
 }
 
 bool
 BytecodeEmitter::emitScript(ParseNode* body)
 {
-    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser.errorReporter(), body);
+    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), body);
 
     setScriptStartOffsetIfUnset(body->pn_pos);
 
     TDZCheckCache tdzCache(this);
     EmitterScope emitterScope(this);
     if (sc->isGlobalContext()) {
         switchToPrologue();
         if (!emitterScope.enterGlobal(this, sc->asGlobalContext()))
@@ -4877,17 +4891,17 @@ BytecodeEmitter::emitScript(ParseNode* b
 
     return true;
 }
 
 bool
 BytecodeEmitter::emitFunctionScript(ParseNode* body)
 {
     FunctionBox* funbox = sc->asFunctionBox();
-    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser.errorReporter(), funbox);
+    AutoFrontendTraceLog traceLog(cx, TraceLogger_BytecodeEmission, parser->errorReporter(), funbox);
 
     setScriptStartOffsetIfUnset(body->pn_pos);
 
     // The ordering of these EmitterScopes is important. The named lambda
     // scope needs to enclose the function scope needs to enclose the extra
     // var scope.
 
     Maybe<EmitterScope> namedLambdaEmitterScope;
@@ -6524,42 +6538,42 @@ BytecodeEmitter::emitSingletonInitialise
         (pn->getKind() == ParseNodeKind::Object) ? SingletonObject : TenuredObject;
 
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value, nullptr, 0, newKind))
         return false;
 
     MOZ_ASSERT_IF(newKind == SingletonObject, value.toObject().isSingleton());
 
-    ObjectBox* objbox = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox = parser->newObjectBox(&value.toObject());
     if (!objbox)
         return false;
 
     return emitObjectOp(objbox, JSOP_OBJECT);
 }
 
 bool
 BytecodeEmitter::emitCallSiteObject(ParseNode* pn)
 {
     RootedValue value(cx);
     if (!pn->getConstantValue(cx, ParseNode::AllowObjects, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox1 = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox1 = parser->newObjectBox(&value.toObject());
     if (!objbox1)
         return false;
 
     if (!pn->as<CallSiteNode>().getRawArrayValue(cx, &value))
         return false;
 
     MOZ_ASSERT(value.isObject());
 
-    ObjectBox* objbox2 = parser.newObjectBox(&value.toObject());
+    ObjectBox* objbox2 = parser->newObjectBox(&value.toObject());
     if (!objbox2)
         return false;
 
     return emitObjectPairOp(objbox1, objbox2, JSOP_CALLSITEOBJ);
 }
 
 /* See the SRC_FOR source note offsetBias comments later in this file. */
 JS_STATIC_ASSERT(JSOP_NOP_LENGTH == 1);
@@ -7064,30 +7078,30 @@ BytecodeEmitter::emitInitializeForInOrOf
                "must have a per-iteration value for initializing");
 
     ParseNode* target = forHead->pn_kid1;
     MOZ_ASSERT(!forHead->pn_kid2);
 
     // If the for-in/of loop didn't have a variable declaration, per-loop
     // initialization is just assigning the iteration value to a target
     // expression.
-    if (!parser.astGenerator().isDeclarationList(target))
+    if (!parser->astGenerator().isDeclarationList(target))
         return emitAssignment(target, ParseNodeKind::Assign, nullptr); // ... ITERVAL
 
     // Otherwise, per-loop initialization is (possibly) declaration
     // initialization.  If the declaration is a lexical declaration, it must be
     // initialized.  If the declaration is a variable declaration, an
     // assignment to that name (which does *not* necessarily assign to the
     // variable!) must be generated.
 
     if (!updateSourceCoordNotes(target->pn_pos.begin))
         return false;
 
     MOZ_ASSERT(target->isForLoopDeclaration());
-    target = parser.astGenerator().singleBindingFromDeclaration(target);
+    target = parser->astGenerator().singleBindingFromDeclaration(target);
 
     if (target->isKind(ParseNodeKind::Name)) {
         auto emitSwapScopeAndRhs = [](BytecodeEmitter* bce, const NameLocation&,
                                       bool emittedBindOp)
         {
             if (emittedBindOp) {
                 // Per-iteration initialization in for-in/of loops computes the
                 // iteration value *before* initializing.  Thus the
@@ -7317,18 +7331,18 @@ BytecodeEmitter::emitForIn(ParseNode* fo
 
     ParseNode* forInHead = forInLoop->pn_left;
     MOZ_ASSERT(forInHead->isKind(ParseNodeKind::ForIn));
     MOZ_ASSERT(forInHead->isArity(PN_TERNARY));
 
     // Annex B: Evaluate the var-initializer expression if present.
     // |for (var i = initializer in expr) { ... }|
     ParseNode* forInTarget = forInHead->pn_kid1;
-    if (parser.astGenerator().isDeclarationList(forInTarget)) {
-        ParseNode* decl = parser.astGenerator().singleBindingFromDeclaration(forInTarget);
+    if (parser->astGenerator().isDeclarationList(forInTarget)) {
+        ParseNode* decl = parser->astGenerator().singleBindingFromDeclaration(forInTarget);
         if (decl->isKind(ParseNodeKind::Name)) {
             if (ParseNode* initializer = decl->expr()) {
                 MOZ_ASSERT(forInTarget->isKind(ParseNodeKind::Var),
                            "for-in initializers are only permitted for |var| declarations");
 
                 if (!updateSourceCoordNotes(decl->pn_pos.begin))
                     return false;
 
@@ -7589,17 +7603,17 @@ BytecodeEmitter::emitCStyleFor(ParseNode
         if (!updateSourceCoordNotes(update->pn_pos.begin))
             return false;
         if (!emitTree(update, ValueUsage::IgnoreValue))
             return false;
         if (!emit1(JSOP_POP))
             return false;
 
         /* Restore the absolute line number for source note readers. */
-        uint32_t lineNum = parser.errorReporter().lineAt(pn->pn_pos.end);
+        uint32_t lineNum = parser->errorReporter().lineAt(pn->pn_pos.end);
         if (currentLine() != lineNum) {
             if (!newSrcNote2(SRC_SETLINE, ptrdiff_t(lineNum)))
                 return false;
             current->currentLine = lineNum;
             current->lastColumn = 0;
         }
     }
 
@@ -7745,18 +7759,18 @@ BytecodeEmitter::emitFunction(ParseNode*
             if (emittingRunOnceLambda)
                 fun->lazyScript()->setTreatAsRunOnce();
         } else {
             MOZ_ASSERT_IF(outersc->strict(), funbox->strictScript);
 
             // Inherit most things (principals, version, etc) from the
             // parent.  Use default values for the rest.
             Rooted<JSScript*> parent(cx, script);
-            MOZ_ASSERT(parent->mutedErrors() == parser.options().mutedErrors());
-            const TransitiveCompileOptions& transitiveOptions = parser.options();
+            MOZ_ASSERT(parent->mutedErrors() == parser->options().mutedErrors());
+            const TransitiveCompileOptions& transitiveOptions = parser->options();
             CompileOptions options(cx, transitiveOptions);
 
             Rooted<JSObject*> sourceObject(cx, script->sourceObject());
             Rooted<JSScript*> script(cx, JSScript::Create(cx, options, sourceObject,
                                                           funbox->bufStart, funbox->bufEnd,
                                                           funbox->toStringStart,
                                                           funbox->toStringEnd));
             if (!script)
@@ -8031,18 +8045,18 @@ BytecodeEmitter::emitWhile(ParseNode* pn
     // If we have a single-line while, like "while (x) ;", we want to
     // emit the line note before the initial goto, so that the
     // debugger sees a single entry point.  This way, if there is a
     // breakpoint on the line, it will only fire once; and "next"ing
     // will skip the whole loop.  However, for the multi-line case we
     // want to emit the line note after the initial goto, so that
     // "cont" stops on each iteration -- but without a stop before the
     // first iteration.
-    if (parser.errorReporter().lineAt(pn->pn_pos.begin) ==
-        parser.errorReporter().lineAt(pn->pn_pos.end))
+    if (parser->errorReporter().lineAt(pn->pn_pos.begin) ==
+        parser->errorReporter().lineAt(pn->pn_pos.end))
     {
         if (!updateSourceCoordNotes(pn->pn_pos.begin))
             return false;
     }
 
     JumpTarget top{ -1 };
     if (!emitJumpTarget(&top))
         return false;
@@ -9121,17 +9135,17 @@ BytecodeEmitter::emitCallee(ParseNode* c
         } else {
             if (!emitTree(callee))
                 return false;
         }
         *callop = false;
         break;
       case ParseNodeKind::SuperBase:
         MOZ_ASSERT(call->isKind(ParseNodeKind::SuperCall));
-        MOZ_ASSERT(parser.astGenerator().isSuperBase(callee));
+        MOZ_ASSERT(parser->astGenerator().isSuperBase(callee));
         if (!emit1(JSOP_SUPERFUN))
             return false;
         break;
       default:
         if (!emitTree(callee))
             return false;
         *callop = false;             /* trigger JSOP_UNDEFINED after */
         break;
@@ -9336,17 +9350,17 @@ BytecodeEmitter::emitCallOrNew(ParseNode
             return false;
         checkTypeSet(pn->getOp());
     }
     if (pn->isOp(JSOP_EVAL) ||
         pn->isOp(JSOP_STRICTEVAL) ||
         pn->isOp(JSOP_SPREADEVAL) ||
         pn->isOp(JSOP_STRICTSPREADEVAL))
     {
-        uint32_t lineNum = parser.errorReporter().lineAt(pn->pn_pos.begin);
+        uint32_t lineNum = parser->errorReporter().lineAt(pn->pn_pos.begin);
         if (!emitUint32Operand(JSOP_LINENO, lineNum))
             return false;
     }
 
     return true;
 }
 
 static const JSOp ParseNodeKindToJSOp[] = {
@@ -9776,17 +9790,17 @@ BytecodeEmitter::emitObject(ParseNode* p
     }
 
     return true;
 }
 
 bool
 BytecodeEmitter::replaceNewInitWithNewObject(JSObject* obj, ptrdiff_t offset)
 {
-    ObjectBox* objbox = parser.newObjectBox(obj);
+    ObjectBox* objbox = parser->newObjectBox(obj);
     if (!objbox)
         return false;
 
     static_assert(JSOP_NEWINIT_LENGTH == JSOP_NEWOBJECT_LENGTH,
                   "newinit and newobject must have equal length to edit in-place");
 
     uint32_t index = objectList.add(objbox);
     jsbytecode* code = this->code(offset);
@@ -9825,17 +9839,17 @@ BytecodeEmitter::emitArrayLiteral(ParseN
                 // use ObjectGroup::getOrFixupCopyOnWriteObject to make sure the
                 // group for the template is accurate. We don't do this here as we
                 // want to use ObjectGroup::allocationSiteGroup, which requires a
                 // finished script.
                 JSObject* obj = &value.toObject();
                 MOZ_ASSERT(obj->is<ArrayObject>() &&
                            obj->as<ArrayObject>().denseElementsAreCopyOnWrite());
 
-                ObjectBox* objbox = parser.newObjectBox(obj);
+                ObjectBox* objbox = parser->newObjectBox(obj);
                 if (!objbox)
                     return false;
 
                 return emitObjectOp(objbox, JSOP_NEWARRAY_COPYONWRITE);
             }
         }
     }
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -7,16 +7,17 @@
 /* JS bytecode generation. */
 
 #ifndef frontend_BytecodeEmitter_h
 #define frontend_BytecodeEmitter_h
 
 #include "mozilla/Attributes.h"
 
 #include "ds/InlineTable.h"
+#include "frontend/BCEParserHandle.h"
 #include "frontend/EitherParser.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SourceNotes.h"
 #include "vm/BytecodeUtil.h"
 #include "vm/Interpreter.h"
 #include "vm/Iteration.h"
 #include "vm/JSContext.h"
 #include "vm/JSScript.h"
@@ -215,17 +216,20 @@ struct MOZ_STACK_CLASS BytecodeEmitter
 
         EmitSection(JSContext* cx, uint32_t lineNum)
           : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0),
             lastTarget{ -1 - ptrdiff_t(JSOP_JUMPTARGET_LENGTH) }
         {}
     };
     EmitSection prologue, main, *current;
 
-    EitherParser parser;
+    // Private storage for parser wrapper. DO NOT REFERENCE INTERNALLY. May not be initialized.
+    // Use |parser| instead.
+    mozilla::Maybe<EitherParser> ep_;
+    BCEParserHandle *parser;
 
     PooledMapPtr<AtomIndexMap> atomIndices; /* literals indexed for mapping */
     unsigned        firstLine;      /* first line, for JSScript::initFromEmitter */
 
     uint32_t        maxFixedSlots;  /* maximum number of fixed frame slots so far */
     uint32_t        maxStackDepth;  /* maximum number of expression stack slots so far */
 
     int32_t         stackDepth;     /* current stack depth in script frame */
@@ -290,33 +294,62 @@ struct MOZ_STACK_CLASS BytecodeEmitter
     bool functionBodyEndPosSet;
 
     /*
      * Note that BytecodeEmitters are magic: they own the arena "top-of-stack"
      * space above their tempMark points. This means that you cannot alloc from
      * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter
      * destruction.
      */
-    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser,
-                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
-                    uint32_t lineNum, EmitterMode emitterMode = Normal);
+  private:
+    // Internal constructor, for delegation use only.
+    BytecodeEmitter(BytecodeEmitter* parent, SharedContext* sc, HandleScript script,
+                    Handle<LazyScript*> lazyScript, uint32_t lineNum, EmitterMode emitterMode);
+
+    void initFromBodyPosition(TokenPos bodyPosition);
+
+  public:
+
+    BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+                    EmitterMode emitterMode = Normal);
+
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, uint32_t lineNum,
+                    EmitterMode emitterMode = Normal);
 
     template<typename CharT>
     BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     uint32_t lineNum, EmitterMode emitterMode = Normal)
       : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
                         lineNum, emitterMode)
     {}
 
     // An alternate constructor that uses a TokenPos for the starting
     // line and that sets functionBodyEndPos as well.
-    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser,
-                    SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
-                    TokenPos bodyPosition, EmitterMode emitterMode = Normal);
+    BytecodeEmitter(BytecodeEmitter* parent, BCEParserHandle* parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
+                    EmitterMode emitterMode = Normal)
+        : BytecodeEmitter(parent, parser, sc, script, lazyScript,
+                          parser->errorReporter().lineAt(bodyPosition.begin),
+                          emitterMode)
+    {
+        initFromBodyPosition(bodyPosition);
+    }
+
+    BytecodeEmitter(BytecodeEmitter* parent, const EitherParser& parser, SharedContext* sc,
+                    HandleScript script, Handle<LazyScript*> lazyScript, TokenPos bodyPosition,
+                    EmitterMode emitterMode = Normal)
+        : BytecodeEmitter(parent, parser, sc, script, lazyScript,
+                          parser.errorReporter().lineAt(bodyPosition.begin),
+                          emitterMode)
+    {
+        initFromBodyPosition(bodyPosition);
+    }
 
     template<typename CharT>
     BytecodeEmitter(BytecodeEmitter* parent, Parser<FullParseHandler, CharT>* parser,
                     SharedContext* sc, HandleScript script, Handle<LazyScript*> lazyScript,
                     TokenPos bodyPosition, EmitterMode emitterMode = Normal)
       : BytecodeEmitter(parent, EitherParser(parser), sc, script, lazyScript,
                         bodyPosition, emitterMode)
     {}