blob: 43295c55a352fefe42a0cd4ec9844dec818937d5 [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_FullParseHandler_h
#define frontend_FullParseHandler_h
#include "mozilla/PodOperations.h"
#include "ParseNode.h"
#include "SharedContext.h"
namespace js {
namespace frontend {
template <typename ParseHandler>
class Parser;
class SyntaxParseHandler;
// Parse handler used when generating a full parse tree for all code which the
// parser encounters.
class FullParseHandler
{
ParseNodeAllocator allocator;
TokenStream &tokenStream;
bool foldConstants;
ParseNode *allocParseNode(size_t size) {
JS_ASSERT(size == sizeof(ParseNode));
return static_cast<ParseNode *>(allocator.allocNode());
}
ParseNode *cloneNode(const ParseNode &other) {
ParseNode *node = allocParseNode(sizeof(ParseNode));
if (!node)
return NULL;
mozilla::PodAssign(node, &other);
return node;
}
/*
* If this is a full parse to construct the bytecode for a function that
* was previously lazily parsed, that lazy function and the current index
* into its inner functions. We do not want to reparse the inner functions.
*/
LazyScript * const lazyOuterFunction_;
size_t lazyInnerFunctionIndex;
const TokenPos &pos() {
return tokenStream.currentToken().pos;
}
public:
/*
* If non-NULL, points to a syntax parser which can be used for inner
* functions. Cleared if language features not handled by the syntax parser
* are encountered, in which case all future activity will use the full
* parser.
*/
Parser<SyntaxParseHandler> *syntaxParser;
/* new_ methods for creating parse nodes. These report OOM on context. */
JS_DECLARE_NEW_METHODS(new_, allocParseNode, inline)
typedef ParseNode *Node;
typedef Definition *DefinitionNode;
FullParseHandler(JSContext *cx, TokenStream &tokenStream, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser, LazyScript *lazyOuterFunction)
: allocator(cx),
tokenStream(tokenStream),
foldConstants(foldConstants),
lazyOuterFunction_(lazyOuterFunction),
lazyInnerFunctionIndex(0),
syntaxParser(syntaxParser)
{}
static ParseNode *null() { return NULL; }
ParseNode *freeTree(ParseNode *pn) { return allocator.freeTree(pn); }
void prepareNodeForMutation(ParseNode *pn) { return allocator.prepareNodeForMutation(pn); }
const Token &currentToken() { return tokenStream.currentToken(); }
ParseNode *newName(PropertyName *name, InBlockBool inBlock, uint32_t blockid,
const TokenPos &pos)
{
return new_<NameNode>(PNK_NAME, JSOP_NAME, name, inBlock, blockid, pos);
}
Definition *newPlaceholder(JSAtom *atom, InBlockBool inBlock, uint32_t blockid,
const TokenPos &pos)
{
Definition *dn =
(Definition *) new_<NameNode>(PNK_NAME, JSOP_NOP, atom, inBlock, blockid, pos);
if (!dn)
return NULL;
dn->setDefn(true);
dn->pn_dflags |= PND_PLACEHOLDER;
return dn;
}
ParseNode *newIdentifier(JSAtom *atom, const TokenPos &pos) {
return new_<NullaryNode>(PNK_NAME, JSOP_NOP, pos, atom);
}
ParseNode *newNumber(double value, DecimalPoint decimalPoint, const TokenPos &pos) {
ParseNode *pn = new_<NullaryNode>(PNK_NUMBER, pos);
if (!pn)
return NULL;
pn->initNumber(value, decimalPoint);
return pn;
}
ParseNode *newBooleanLiteral(bool cond, const TokenPos &pos) {
return new_<BooleanLiteral>(cond, pos);
}
ParseNode *newStringLiteral(JSAtom *atom, const TokenPos &pos) {
return new_<NullaryNode>(PNK_STRING, JSOP_STRING, pos, atom);
}
ParseNode *newThisLiteral(const TokenPos &pos) {
return new_<ThisLiteral>(pos);
}
ParseNode *newNullLiteral(const TokenPos &pos) {
return new_<NullLiteral>(pos);
}
// The Boxer object here is any object that can allocate ObjectBoxes.
// Specifically, a Boxer has a .newObjectBox(T) method that accepts a
// Rooted<RegExpObject*> argument and returns an ObjectBox*.
template <class Boxer>
ParseNode *newRegExp(HandleObject reobj, const TokenPos &pos, Boxer &boxer) {
ObjectBox *objbox = boxer.newObjectBox(reobj);
if (!objbox)
return null();
return new_<RegExpLiteral>(objbox, pos);
}
ParseNode *newConditional(ParseNode *cond, ParseNode *thenExpr, ParseNode *elseExpr) {
return new_<ConditionalExpression>(cond, thenExpr, elseExpr);
}
ParseNode *newElision() {
return new_<NullaryNode>(PNK_ELISION, pos());
}
void markAsSetCall(ParseNode *pn) {
pn->pn_xflags |= PNX_SETCALL;
}
ParseNode *newDelete(uint32_t begin, ParseNode *expr) {
if (expr->getKind() == PNK_NAME) {
expr->pn_dflags |= PND_DEOPTIMIZED;
expr->setOp(JSOP_DELNAME);
}
return newUnary(PNK_DELETE, JSOP_NOP, begin, expr);
}
ParseNode *newUnary(ParseNodeKind kind, JSOp op, uint32_t begin, ParseNode *kid) {
TokenPos pos(begin, kid ? kid->pn_pos.end : begin + 1);
return new_<UnaryNode>(kind, op, pos, kid);
}
ParseNode *newBinary(ParseNodeKind kind, JSOp op = JSOP_NOP) {
return new_<BinaryNode>(kind, op, pos(), (ParseNode *) NULL, (ParseNode *) NULL);
}
ParseNode *newBinary(ParseNodeKind kind, ParseNode *left,
JSOp op = JSOP_NOP) {
return new_<BinaryNode>(kind, op, left->pn_pos, left, (ParseNode *) NULL);
}
ParseNode *newBinary(ParseNodeKind kind, ParseNode *left, ParseNode *right,
JSOp op = JSOP_NOP) {
TokenPos pos(left->pn_pos.begin, right->pn_pos.end);
return new_<BinaryNode>(kind, op, pos, left, right);
}
ParseNode *newBinaryOrAppend(ParseNodeKind kind, ParseNode *left, ParseNode *right,
ParseContext<FullParseHandler> *pc, JSOp op = JSOP_NOP)
{
return ParseNode::newBinaryOrAppend(kind, op, left, right, this, pc, foldConstants);
}
ParseNode *newTernary(ParseNodeKind kind,
ParseNode *first, ParseNode *second, ParseNode *third,
JSOp op = JSOP_NOP)
{
return new_<TernaryNode>(kind, op, first, second, third);
}
ParseNode *newStatementList(unsigned blockid, const TokenPos &pos) {
ParseNode *pn = new_<ListNode>(PNK_STATEMENTLIST, pos);
if (pn)
pn->pn_blockid = blockid;
return pn;
}
template <typename PC>
void addStatementToList(ParseNode *list, ParseNode *stmt, PC *pc) {
JS_ASSERT(list->isKind(PNK_STATEMENTLIST));
if (stmt->isKind(PNK_FUNCTION)) {
if (pc->atBodyLevel()) {
// PNX_FUNCDEFS notifies the emitter that the block contains
// body-level function definitions that should be processed
// before the rest of nodes.
list->pn_xflags |= PNX_FUNCDEFS;
} else {
// General deoptimization was done in Parser::functionDef.
JS_ASSERT_IF(pc->sc->isFunctionBox(),
pc->sc->asFunctionBox()->hasExtensibleScope());
}
}
list->append(stmt);
}
ParseNode *newEmptyStatement(const TokenPos &pos) {
return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, pos, (ParseNode *) NULL);
}
ParseNode *newExprStatement(ParseNode *expr, uint32_t end) {
JS_ASSERT(expr->pn_pos.end <= end);
return new_<UnaryNode>(PNK_SEMI, JSOP_NOP, TokenPos(expr->pn_pos.begin, end), expr);
}
ParseNode *newIfStatement(uint32_t begin, ParseNode *cond, ParseNode *thenBranch,
ParseNode *elseBranch)
{
ParseNode *pn = new_<TernaryNode>(PNK_IF, JSOP_NOP, cond, thenBranch, elseBranch);
if (!pn)
return null();
pn->pn_pos.begin = begin;
return pn;
}
ParseNode *newDoWhileStatement(ParseNode *body, ParseNode *cond, const TokenPos &pos) {
return new_<BinaryNode>(PNK_DOWHILE, JSOP_NOP, pos, body, cond);
}
ParseNode *newWhileStatement(uint32_t begin, ParseNode *cond, ParseNode *body) {
TokenPos pos(begin, body->pn_pos.end);
return new_<BinaryNode>(PNK_WHILE, JSOP_NOP, pos, cond, body);
}
ParseNode *newForStatement(uint32_t begin, ParseNode *forHead, ParseNode *body,
unsigned iflags)
{
/* A FOR node is binary, left is loop control and right is the body. */
JSOp op = forHead->isKind(PNK_FORIN) ? JSOP_ITER : JSOP_NOP;
BinaryNode *pn = new_<BinaryNode>(PNK_FOR, op, TokenPos(begin, body->pn_pos.end),
forHead, body);
if (!pn)
return null();
pn->pn_iflags = iflags;
return pn;
}
ParseNode *newForHead(bool isForInOrOf, ParseNode *pn1, ParseNode *pn2, ParseNode *pn3,
const TokenPos &pos)
{
ParseNodeKind kind = isForInOrOf ? PNK_FORIN : PNK_FORHEAD;
return new_<TernaryNode>(kind, JSOP_NOP, pn1, pn2, pn3, pos);
}
ParseNode *newSwitchStatement(uint32_t begin, ParseNode *discriminant, ParseNode *caseList) {
TokenPos pos(begin, caseList->pn_pos.end);
return new_<BinaryNode>(PNK_SWITCH, JSOP_NOP, pos, discriminant, caseList);
}
ParseNode *newCaseOrDefault(uint32_t begin, ParseNode *expr, ParseNode *body) {
TokenPos pos(begin, body->pn_pos.end);
return new_<BinaryNode>(expr ? PNK_CASE : PNK_DEFAULT, JSOP_NOP, pos, expr, body);
}
ParseNode *newContinueStatement(PropertyName *label, const TokenPos &pos) {
return new_<ContinueStatement>(label, pos);
}
ParseNode *newBreakStatement(PropertyName *label, const TokenPos &pos) {
return new_<BreakStatement>(label, pos);
}
ParseNode *newReturnStatement(ParseNode *expr, const TokenPos &pos) {
JS_ASSERT_IF(expr, pos.encloses(expr->pn_pos));
return new_<UnaryNode>(PNK_RETURN, JSOP_RETURN, pos, expr);
}
ParseNode *newWithStatement(uint32_t begin, ParseNode *expr, ParseNode *body) {
return new_<BinaryNode>(PNK_WITH, JSOP_NOP, TokenPos(begin, body->pn_pos.end), expr, body);
}
ParseNode *newLabeledStatement(PropertyName *label, ParseNode *stmt, uint32_t begin) {
return new_<LabeledStatement>(label, stmt, begin);
}
ParseNode *newThrowStatement(ParseNode *expr, const TokenPos &pos) {
JS_ASSERT(pos.encloses(expr->pn_pos));
return new_<UnaryNode>(PNK_THROW, JSOP_THROW, pos, expr);
}
ParseNode *newTryStatement(uint32_t begin, ParseNode *body, ParseNode *catchList,
ParseNode *finallyBlock) {
TokenPos pos(begin, (finallyBlock ? finallyBlock : catchList)->pn_pos.end);
return new_<TernaryNode>(PNK_TRY, JSOP_NOP, body, catchList, finallyBlock, pos);
}
ParseNode *newDebuggerStatement(const TokenPos &pos) {
return new_<DebuggerStatement>(pos);
}
ParseNode *newPropertyAccess(ParseNode *pn, PropertyName *name, uint32_t end) {
return new_<PropertyAccess>(pn, name, pn->pn_pos.begin, end);
}
private:
/*
* Examine the RHS of a MemberExpression obj[pn], to optimize expressions
* like obj["name"] or obj["0"].
*
* If pn is a number or string constant that's not an index, store the
* corresponding PropertyName in *namep. Otherwise store NULL in *namep.
*
* Also, if pn is a string constant whose value is the ToString of an
* index, morph it to a number.
*
* Return false on OOM.
*/
bool foldConstantIndex(ParseNode *pn, PropertyName **namep) {
if (pn->isKind(PNK_STRING)) {
JSAtom *atom = pn->pn_atom;
uint32_t index;
if (atom->isIndex(&index)) {
pn->setKind(PNK_NUMBER);
pn->setOp(JSOP_DOUBLE);
pn->pn_dval = index;
} else {
*namep = atom->asPropertyName();
return true;
}
} else if (pn->isKind(PNK_NUMBER)) {
double number = pn->pn_dval;
if (number != ToUint32(number)) {
JSContext *cx = tokenStream.getContext();
JSAtom *atom = ToAtom<NoGC>(cx, DoubleValue(number));
if (!atom)
return false;
*namep = atom->asPropertyName();
return true;
}
}
*namep = NULL;
return true;
}
public:
ParseNode *newPropertyByValue(ParseNode *lhs, ParseNode *index, uint32_t end) {
if (foldConstants) {
PropertyName *name;
if (!foldConstantIndex(index, &name))
return null();
if (name)
return newPropertyAccess(lhs, name, end);
}
return new_<PropertyByValue>(lhs, index, lhs->pn_pos.begin, end);
}
inline bool addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody);
inline void setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr);
inline void setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *pn);
inline ParseNode *newFunctionDefinition();
void setFunctionBody(ParseNode *pn, ParseNode *kid) {
pn->pn_body = kid;
}
void setFunctionBox(ParseNode *pn, FunctionBox *funbox) {
pn->pn_funbox = funbox;
}
void addFunctionArgument(ParseNode *pn, ParseNode *argpn) {
pn->pn_body->append(argpn);
}
inline ParseNode *newLexicalScope(ObjectBox *blockbox);
bool isOperationWithoutParens(ParseNode *pn, ParseNodeKind kind) {
return pn->isKind(kind) && !pn->isInParens();
}
inline bool finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op);
void setBeginPosition(ParseNode *pn, ParseNode *oth) {
setBeginPosition(pn, oth->pn_pos.begin);
}
void setBeginPosition(ParseNode *pn, uint32_t begin) {
pn->pn_pos.begin = begin;
JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
}
void setEndPosition(ParseNode *pn, ParseNode *oth) {
setEndPosition(pn, oth->pn_pos.end);
}
void setEndPosition(ParseNode *pn, uint32_t end) {
pn->pn_pos.end = end;
JS_ASSERT(pn->pn_pos.begin <= pn->pn_pos.end);
}
void setPosition(ParseNode *pn, const TokenPos &pos) {
pn->pn_pos = pos;
}
TokenPos getPosition(ParseNode *pn) {
return pn->pn_pos;
}
ParseNode *newList(ParseNodeKind kind, ParseNode *kid = NULL, JSOp op = JSOP_NOP) {
ParseNode *pn = ListNode::create(kind, this);
if (!pn)
return NULL;
pn->setOp(op);
pn->makeEmpty();
if (kid) {
pn->pn_pos.begin = kid->pn_pos.begin;
pn->append(kid);
}
return pn;
}
void addList(ParseNode *pn, ParseNode *kid) {
pn->append(kid);
}
void setOp(ParseNode *pn, JSOp op) {
pn->setOp(op);
}
void setBlockId(ParseNode *pn, unsigned blockid) {
pn->pn_blockid = blockid;
}
void setFlag(ParseNode *pn, unsigned flag) {
pn->pn_dflags |= flag;
}
void setListFlag(ParseNode *pn, unsigned flag) {
JS_ASSERT(pn->isArity(PN_LIST));
pn->pn_xflags |= flag;
}
ParseNode *setInParens(ParseNode *pn) {
pn->setInParens(true);
return pn;
}
void setPrologue(ParseNode *pn) {
pn->pn_prologue = true;
}
bool isConstant(ParseNode *pn) {
return pn->isConstant();
}
PropertyName *isName(ParseNode *pn) {
return pn->isKind(PNK_NAME) ? pn->pn_atom->asPropertyName() : NULL;
}
bool isCall(ParseNode *pn) {
return pn->isKind(PNK_CALL);
}
PropertyName *isGetProp(ParseNode *pn) {
return pn->isOp(JSOP_GETPROP) ? pn->pn_atom->asPropertyName() : NULL;
}
JSAtom *isStringExprStatement(ParseNode *pn, TokenPos *pos) {
if (JSAtom *atom = pn->isStringExprStatement()) {
*pos = pn->pn_kid->pn_pos;
return atom;
}
return NULL;
}
inline ParseNode *makeAssignment(ParseNode *pn, ParseNode *rhs);
static Definition *getDefinitionNode(Definition *dn) {
return dn;
}
static Definition::Kind getDefinitionKind(Definition *dn) {
return dn->kind();
}
void linkUseToDef(ParseNode *pn, Definition *dn)
{
JS_ASSERT(!pn->isUsed());
JS_ASSERT(!pn->isDefn());
JS_ASSERT(pn != dn->dn_uses);
JS_ASSERT(dn->isDefn());
pn->pn_link = dn->dn_uses;
dn->dn_uses = pn;
dn->pn_dflags |= pn->pn_dflags & PND_USE2DEF_FLAGS;
pn->setUsed(true);
pn->pn_lexdef = dn;
}
Definition *resolve(Definition *dn) {
return dn->resolve();
}
void deoptimizeUsesWithin(Definition *dn, const TokenPos &pos)
{
for (ParseNode *pnu = dn->dn_uses; pnu; pnu = pnu->pn_link) {
JS_ASSERT(pnu->isUsed());
JS_ASSERT(!pnu->isDefn());
if (pnu->pn_pos.begin >= pos.begin && pnu->pn_pos.end <= pos.end)
pnu->pn_dflags |= PND_DEOPTIMIZED;
}
}
bool dependencyCovered(ParseNode *pn, unsigned blockid, bool functionScope) {
return pn->pn_blockid >= blockid;
}
static uintptr_t definitionToBits(Definition *dn) {
return uintptr_t(dn);
}
static Definition *definitionFromBits(uintptr_t bits) {
return (Definition *) bits;
}
static Definition *nullDefinition() {
return NULL;
}
void disableSyntaxParser() {
syntaxParser = NULL;
}
LazyScript *lazyOuterFunction() {
return lazyOuterFunction_;
}
JSFunction *nextLazyInnerFunction() {
JS_ASSERT(lazyInnerFunctionIndex < lazyOuterFunction()->numInnerFunctions());
return lazyOuterFunction()->innerFunctions()[lazyInnerFunctionIndex++];
}
};
inline bool
FullParseHandler::addCatchBlock(ParseNode *catchList, ParseNode *letBlock,
ParseNode *catchName, ParseNode *catchGuard, ParseNode *catchBody)
{
ParseNode *catchpn = newTernary(PNK_CATCH, catchName, catchGuard, catchBody);
if (!catchpn)
return false;
catchList->append(letBlock);
letBlock->pn_expr = catchpn;
return true;
}
inline void
FullParseHandler::setLeaveBlockResult(ParseNode *block, ParseNode *kid, bool leaveBlockExpr)
{
JS_ASSERT(block->isOp(JSOP_LEAVEBLOCK));
if (leaveBlockExpr)
block->setOp(JSOP_LEAVEBLOCKEXPR);
block->pn_expr = kid;
}
inline void
FullParseHandler::setLastFunctionArgumentDefault(ParseNode *funcpn, ParseNode *defaultValue)
{
ParseNode *arg = funcpn->pn_body->last();
arg->pn_dflags |= PND_DEFAULT;
arg->pn_expr = defaultValue;
}
inline ParseNode *
FullParseHandler::newFunctionDefinition()
{
ParseNode *pn = CodeNode::create(PNK_FUNCTION, this);
if (!pn)
return NULL;
pn->pn_body = NULL;
pn->pn_funbox = NULL;
pn->pn_cookie.makeFree();
pn->pn_dflags = 0;
return pn;
}
inline ParseNode *
FullParseHandler::newLexicalScope(ObjectBox *blockbox)
{
ParseNode *pn = LexicalScopeNode::create(PNK_LEXICALSCOPE, this);
if (!pn)
return NULL;
pn->setOp(JSOP_LEAVEBLOCK);
pn->pn_objbox = blockbox;
pn->pn_cookie.makeFree();
pn->pn_dflags = 0;
return pn;
}
inline bool
FullParseHandler::finishInitializerAssignment(ParseNode *pn, ParseNode *init, JSOp op)
{
if (pn->isUsed()) {
pn = makeAssignment(pn, init);
if (!pn)
return false;
} else {
pn->pn_expr = init;
}
pn->setOp((pn->pn_dflags & PND_BOUND)
? JSOP_SETLOCAL
: (op == JSOP_DEFCONST)
? JSOP_SETCONST
: JSOP_SETNAME);
pn->markAsAssigned();
/* The declarator's position must include the initializer. */
pn->pn_pos.end = init->pn_pos.end;
return true;
}
inline ParseNode *
FullParseHandler::makeAssignment(ParseNode *pn, ParseNode *rhs)
{
ParseNode *lhs = cloneNode(*pn);
if (!lhs)
return NULL;
if (pn->isUsed()) {
Definition *dn = pn->pn_lexdef;
ParseNode **pnup = &dn->dn_uses;
while (*pnup != pn)
pnup = &(*pnup)->pn_link;
*pnup = lhs;
lhs->pn_link = pn->pn_link;
pn->pn_link = NULL;
}
pn->setKind(PNK_ASSIGN);
pn->setOp(JSOP_NOP);
pn->setArity(PN_BINARY);
pn->setInParens(false);
pn->setUsed(false);
pn->setDefn(false);
pn->pn_left = lhs;
pn->pn_right = rhs;
pn->pn_pos.end = rhs->pn_pos.end;
return lhs;
}
} // frontend
} // js
#endif /* frontend_FullParseHandler_h */