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