blob: 22b276125ca94abbbb9d40071b1c1e9286daca96 [file] [log] [blame]
/* -*- 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 &notes() 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 */