michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #ifndef frontend_BytecodeEmitter_h michael@0: #define frontend_BytecodeEmitter_h michael@0: michael@0: /* michael@0: * JS bytecode generation. michael@0: */ michael@0: michael@0: #include "jscntxt.h" michael@0: #include "jsopcode.h" michael@0: #include "jsscript.h" michael@0: michael@0: #include "frontend/ParseMaps.h" michael@0: #include "frontend/SourceNotes.h" michael@0: michael@0: namespace js { michael@0: namespace frontend { michael@0: michael@0: class FullParseHandler; michael@0: class ObjectBox; michael@0: class ParseNode; michael@0: template class Parser; michael@0: class SharedContext; michael@0: class TokenStream; michael@0: michael@0: class CGConstList { michael@0: Vector list; michael@0: public: michael@0: CGConstList(ExclusiveContext *cx) : list(cx) {} michael@0: bool append(Value v) { JS_ASSERT_IF(v.isString(), v.toString()->isAtom()); return list.append(v); } michael@0: size_t length() const { return list.length(); } michael@0: void finish(ConstArray *array); michael@0: }; michael@0: michael@0: struct CGObjectList { michael@0: uint32_t length; /* number of emitted so far objects */ michael@0: ObjectBox *lastbox; /* last emitted object */ michael@0: michael@0: CGObjectList() : length(0), lastbox(nullptr) {} michael@0: michael@0: unsigned add(ObjectBox *objbox); michael@0: unsigned indexOf(JSObject *obj); michael@0: void finish(ObjectArray *array); michael@0: ObjectBox* find(uint32_t index); michael@0: }; michael@0: michael@0: struct CGTryNoteList { michael@0: Vector list; michael@0: CGTryNoteList(ExclusiveContext *cx) : list(cx) {} michael@0: michael@0: bool append(JSTryNoteKind kind, uint32_t stackDepth, size_t start, size_t end); michael@0: size_t length() const { return list.length(); } michael@0: void finish(TryNoteArray *array); michael@0: }; michael@0: michael@0: struct CGBlockScopeList { michael@0: Vector list; michael@0: CGBlockScopeList(ExclusiveContext *cx) : list(cx) {} michael@0: michael@0: bool append(uint32_t scopeObject, uint32_t offset, uint32_t parent); michael@0: uint32_t findEnclosingScope(uint32_t index); michael@0: void recordEnd(uint32_t index, uint32_t offset); michael@0: size_t length() const { return list.length(); } michael@0: void finish(BlockScopeArray *array); michael@0: }; michael@0: michael@0: struct StmtInfoBCE; michael@0: michael@0: // Use zero inline elements because these go on the stack and affect how many michael@0: // nested functions are possible. michael@0: typedef Vector BytecodeVector; michael@0: typedef Vector SrcNotesVector; michael@0: michael@0: struct BytecodeEmitter michael@0: { michael@0: typedef StmtInfoBCE StmtInfo; michael@0: michael@0: SharedContext *const sc; /* context shared between parsing and bytecode generation */ michael@0: michael@0: BytecodeEmitter *const parent; /* enclosing function or global context */ michael@0: michael@0: Rooted script; /* the JSScript we're ultimately producing */ michael@0: michael@0: struct EmitSection { michael@0: BytecodeVector code; /* bytecode */ michael@0: SrcNotesVector notes; /* source notes, see below */ michael@0: ptrdiff_t lastNoteOffset; /* code offset for last source note */ michael@0: uint32_t currentLine; /* line number for tree-based srcnote gen */ michael@0: uint32_t lastColumn; /* zero-based column index on currentLine of michael@0: last SRC_COLSPAN-annotated opcode */ michael@0: michael@0: EmitSection(ExclusiveContext *cx, uint32_t lineNum) michael@0: : code(cx), notes(cx), lastNoteOffset(0), currentLine(lineNum), lastColumn(0) michael@0: {} michael@0: }; michael@0: EmitSection prolog, main, *current; michael@0: michael@0: /* the parser */ michael@0: Parser *const parser; michael@0: michael@0: HandleScript evalCaller; /* scripted caller info for eval and dbgapi */ michael@0: michael@0: StmtInfoBCE *topStmt; /* top of statement info stack */ michael@0: StmtInfoBCE *topScopeStmt; /* top lexical scope statement */ michael@0: Rooted staticScope; michael@0: /* compile time scope chain */ michael@0: michael@0: OwnedAtomIndexMapPtr atomIndices; /* literals indexed for mapping */ michael@0: unsigned firstLine; /* first line, for JSScript::initFromEmitter */ michael@0: michael@0: int32_t stackDepth; /* current stack depth in script frame */ michael@0: uint32_t maxStackDepth; /* maximum stack depth so far */ michael@0: michael@0: uint32_t arrayCompDepth; /* stack depth of array in comprehension */ michael@0: michael@0: unsigned emitLevel; /* js::frontend::EmitTree recursion level */ michael@0: michael@0: CGConstList constList; /* constants to be included with the script */ michael@0: michael@0: CGObjectList objectList; /* list of emitted objects */ michael@0: CGObjectList regexpList; /* list of emitted regexp that will be michael@0: cloned during execution */ michael@0: CGTryNoteList tryNoteList; /* list of emitted try notes */ michael@0: CGBlockScopeList blockScopeList;/* list of emitted block scope notes */ michael@0: michael@0: uint16_t typesetCount; /* Number of JOF_TYPESET opcodes generated */ michael@0: michael@0: bool hasSingletons:1; /* script contains singleton initializer JSOP_OBJECT */ michael@0: michael@0: bool emittingForInit:1; /* true while emitting init expr of for; exclude 'in' */ michael@0: michael@0: bool emittingRunOnceLambda:1; /* true while emitting a lambda which is only michael@0: expected to run once. */ michael@0: bool lazyRunOnceLambda:1; /* true while lazily emitting a script for michael@0: * a lambda which is only expected to run once. */ michael@0: michael@0: bool isRunOnceLambda(); michael@0: michael@0: bool insideEval:1; /* True if compiling an eval-expression or a function michael@0: nested inside an eval. */ michael@0: michael@0: const bool hasGlobalScope:1; /* frontend::CompileScript's scope chain is the michael@0: global object */ michael@0: michael@0: enum EmitterMode { michael@0: Normal, michael@0: michael@0: /* michael@0: * Emit JSOP_GETINTRINSIC instead of JSOP_NAME and assert that michael@0: * JSOP_NAME and JSOP_*GNAME don't ever get emitted. See the comment michael@0: * for the field |selfHostingMode| in Parser.h for details. michael@0: */ michael@0: SelfHosting, michael@0: michael@0: /* michael@0: * Check the static scope chain of the root function for resolving free michael@0: * variable accesses in the script. michael@0: */ michael@0: LazyFunction michael@0: }; michael@0: michael@0: const EmitterMode emitterMode; michael@0: michael@0: /* michael@0: * Note that BytecodeEmitters are magic: they own the arena "top-of-stack" michael@0: * space above their tempMark points. This means that you cannot alloc from michael@0: * tempLifoAlloc and save the pointer beyond the next BytecodeEmitter michael@0: * destruction. michael@0: */ michael@0: BytecodeEmitter(BytecodeEmitter *parent, Parser *parser, SharedContext *sc, michael@0: HandleScript script, bool insideEval, HandleScript evalCaller, michael@0: bool hasGlobalScope, uint32_t lineNum, EmitterMode emitterMode = Normal); michael@0: bool init(); michael@0: michael@0: bool isAliasedName(ParseNode *pn); michael@0: michael@0: MOZ_ALWAYS_INLINE michael@0: bool makeAtomIndex(JSAtom *atom, jsatomid *indexp) { michael@0: AtomIndexAddPtr p = atomIndices->lookupForAdd(atom); michael@0: if (p) { michael@0: *indexp = p.value(); michael@0: return true; michael@0: } michael@0: michael@0: jsatomid index = atomIndices->count(); michael@0: if (!atomIndices->add(p, atom, index)) michael@0: return false; michael@0: michael@0: *indexp = index; michael@0: return true; michael@0: } michael@0: michael@0: bool isInLoop(); michael@0: bool checkSingletonContext(); michael@0: michael@0: bool needsImplicitThis(); michael@0: michael@0: void tellDebuggerAboutCompiledScript(ExclusiveContext *cx); michael@0: michael@0: inline TokenStream *tokenStream(); michael@0: michael@0: BytecodeVector &code() const { return current->code; } michael@0: jsbytecode *code(ptrdiff_t offset) const { return current->code.begin() + offset; } michael@0: ptrdiff_t offset() const { return current->code.end() - current->code.begin(); } michael@0: ptrdiff_t prologOffset() const { return prolog.code.end() - prolog.code.begin(); } michael@0: void switchToMain() { current = &main; } michael@0: void switchToProlog() { current = &prolog; } michael@0: michael@0: SrcNotesVector ¬es() const { return current->notes; } michael@0: ptrdiff_t lastNoteOffset() const { return current->lastNoteOffset; } michael@0: unsigned currentLine() const { return current->currentLine; } michael@0: unsigned lastColumn() const { return current->lastColumn; } michael@0: michael@0: bool reportError(ParseNode *pn, unsigned errorNumber, ...); michael@0: bool reportStrictWarning(ParseNode *pn, unsigned errorNumber, ...); michael@0: bool reportStrictModeError(ParseNode *pn, unsigned errorNumber, ...); michael@0: }; michael@0: michael@0: /* michael@0: * Emit one bytecode. michael@0: */ michael@0: ptrdiff_t michael@0: Emit1(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op); michael@0: michael@0: /* michael@0: * Emit two bytecodes, an opcode (op) with a byte of immediate operand (op1). michael@0: */ michael@0: ptrdiff_t michael@0: Emit2(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1); michael@0: michael@0: /* michael@0: * Emit three bytecodes, an opcode with two bytes of immediate operands. michael@0: */ michael@0: ptrdiff_t michael@0: Emit3(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, jsbytecode op1, jsbytecode op2); michael@0: michael@0: /* michael@0: * Emit (1 + extra) bytecodes, for N bytes of op and its immediate operand. michael@0: */ michael@0: ptrdiff_t michael@0: EmitN(ExclusiveContext *cx, BytecodeEmitter *bce, JSOp op, size_t extra); michael@0: michael@0: /* michael@0: * Emit code into bce for the tree rooted at pn. michael@0: */ michael@0: bool michael@0: EmitTree(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *pn); michael@0: michael@0: /* michael@0: * Emit function code using bce for the tree rooted at body. michael@0: */ michael@0: bool michael@0: EmitFunctionScript(ExclusiveContext *cx, BytecodeEmitter *bce, ParseNode *body); michael@0: michael@0: /* michael@0: * Append a new source note of the given type (and therefore size) to bce's michael@0: * notes dynamic array, updating bce->noteCount. Return the new note's index michael@0: * within the array pointed at by bce->current->notes. Return -1 if out of michael@0: * memory. michael@0: */ michael@0: int michael@0: NewSrcNote(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type); michael@0: michael@0: int michael@0: NewSrcNote2(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset); michael@0: michael@0: int michael@0: NewSrcNote3(ExclusiveContext *cx, BytecodeEmitter *bce, SrcNoteType type, ptrdiff_t offset1, michael@0: ptrdiff_t offset2); michael@0: michael@0: /* NB: this function can add at most one extra extended delta note. */ michael@0: bool michael@0: AddToSrcNoteDelta(ExclusiveContext *cx, BytecodeEmitter *bce, jssrcnote *sn, ptrdiff_t delta); michael@0: michael@0: bool michael@0: FinishTakingSrcNotes(ExclusiveContext *cx, BytecodeEmitter *bce, uint32_t *out); michael@0: michael@0: void michael@0: CopySrcNotes(BytecodeEmitter *bce, jssrcnote *destination, uint32_t nsrcnotes); michael@0: michael@0: } /* namespace frontend */ michael@0: } /* namespace js */ michael@0: michael@0: #endif /* frontend_BytecodeEmitter_h */