blob: 1c6e0e93344e68cb866819b6ed84edccf5b231cd [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_Parser_h
#define frontend_Parser_h
/*
* JS parser definitions.
*/
#include "jsprvtd.h"
#include "jspubtd.h"
#include "frontend/BytecodeCompiler.h"
#include "frontend/FullParseHandler.h"
#include "frontend/ParseMaps.h"
#include "frontend/ParseNode.h"
#include "frontend/SharedContext.h"
#include "frontend/SyntaxParseHandler.h"
namespace js {
namespace frontend {
struct StmtInfoPC : public StmtInfoBase {
StmtInfoPC *down; /* info for enclosing statement */
StmtInfoPC *downScope; /* next enclosing lexical scope */
uint32_t blockid; /* for simplified dominance computation */
StmtInfoPC(JSContext *cx) : StmtInfoBase(cx) {}
};
typedef HashSet<JSAtom *> FuncStmtSet;
class SharedContext;
typedef Vector<Definition *, 16> DeclVector;
typedef Vector<JSFunction *, 4> FunctionVector;
struct GenericParseContext
{
// Enclosing function or global context.
GenericParseContext *parent;
// Context shared between parsing and bytecode generation.
SharedContext *sc;
// The following flags are set when a particular code feature is detected
// in a function.
// Function has 'return <expr>;'
bool funHasReturnExpr:1;
// Function has 'return;'
bool funHasReturnVoid:1;
// The following flags are set when parsing enters a particular region of
// source code, and cleared when that region is exited.
// true while parsing init expr of for; exclude 'in'
bool parsingForInit:1;
// true while we are within a with-statement in the current ParseContext
// chain (which stops at the top-level or an eval()
bool parsingWith:1;
inline GenericParseContext(GenericParseContext *parent, SharedContext *sc);
};
/*
* The struct ParseContext stores information about the current parsing context,
* which is part of the parser state (see the field Parser::pc). The current
* parsing context is either the global context, or the function currently being
* parsed. When the parser encounters a function definition, it creates a new
* ParseContext, makes it the new current context, and sets its parent to the
* context in which it encountered the definition.
*/
template <typename ParseHandler>
struct ParseContext : public GenericParseContext
{
typedef StmtInfoPC StmtInfo;
typedef typename ParseHandler::Node Node;
typedef typename ParseHandler::DefinitionNode DefinitionNode;
uint32_t bodyid; /* block number of program/function body */
uint32_t blockidGen; /* preincremented block number generator */
StmtInfoPC *topStmt; /* top of statement info stack */
StmtInfoPC *topScopeStmt; /* top lexical scope statement */
Rooted<StaticBlockObject *> blockChain;
/* compile time block scope chain */
const unsigned staticLevel; /* static compilation unit nesting level */
uint32_t parenDepth; /* nesting depth of parens that might turn out
to be generator expressions */
uint32_t yieldCount; /* number of |yield| tokens encountered at
non-zero depth in current paren tree */
Node blockNode; /* parse node for a block with let declarations
(block with its own lexical scope) */
private:
AtomDecls<ParseHandler> decls_; /* function, const, and var declarations */
DeclVector args_; /* argument definitions */
DeclVector vars_; /* var/const definitions */
public:
const AtomDecls<ParseHandler> &decls() const {
return decls_;
}
uint32_t numArgs() const {
JS_ASSERT(sc->isFunctionBox());
return args_.length();
}
uint32_t numVars() const {
JS_ASSERT(sc->isFunctionBox());
return vars_.length();
}
/*
* This function adds a definition to the lexical scope represented by this
* ParseContext.
*
* Pre-conditions:
* + The caller must have already taken care of name collisions:
* - For non-let definitions, this means 'name' isn't in 'decls'.
* - For let definitions, this means 'name' isn't already a name in the
* current block.
* + The given 'pn' is either a placeholder (created by a previous unbound
* use) or an un-bound un-linked name node.
* + The given 'kind' is one of ARG, CONST, VAR, or LET. In particular,
* NAMED_LAMBDA is handled in an ad hoc special case manner (see
* LeaveFunction) that we should consider rewriting.
*
* Post-conditions:
* + pc->decls().lookupFirst(name) == pn
* + The given name 'pn' has been converted in-place into a
* non-placeholder definition.
* + If this is a function scope (sc->inFunction), 'pn' is bound to a
* particular local/argument slot.
* + PND_CONST is set for Definition::COSNT
* + Pre-existing uses of pre-existing placeholders have been linked to
* 'pn' if they are in the scope of 'pn'.
* + Pre-existing placeholders in the scope of 'pn' have been removed.
*/
bool define(JSContext *cx, HandlePropertyName name, Node pn, Definition::Kind);
/*
* Let definitions may shadow same-named definitions in enclosing scopes.
* To represesent this, 'decls' is not a plain map, but actually:
* decls :: name -> stack of definitions
* New bindings are pushed onto the stack, name lookup always refers to the
* top of the stack, and leaving a block scope calls popLetDecl for each
* name in the block's scope.
*/
void popLetDecl(JSAtom *atom);
/* See the sad story in defineArg. */
void prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl);
/* See the sad story in MakeDefIntoUse. */
void updateDecl(JSAtom *atom, Node newDecl);
/*
* After a function body has been parsed, the parser generates the
* function's "bindings". Bindings are a data-structure, ultimately stored
* in the compiled JSScript, that serve three purposes:
* - After parsing, the ParseContext is destroyed and 'decls' along with
* it. Mostly, the emitter just uses the binding information stored in
* the use/def nodes, but the emitter occasionally needs 'bindings' for
* various scope-related queries.
* - Bindings provide the initial js::Shape to use when creating a dynamic
* scope object (js::CallObject) for the function. This shape is used
* during dynamic name lookup.
* - Sometimes a script's bindings are accessed at runtime to retrieve the
* contents of the lexical scope (e.g., from the debugger).
*/
bool generateFunctionBindings(JSContext *cx, InternalHandle<Bindings*> bindings) const;
public:
uint32_t yieldOffset; /* offset of a yield expression that might
be an error if we turn out to be inside
a generator expression. Zero means
there isn't one. */
private:
ParseContext **parserPC; /* this points to the Parser's active pc
and holds either |this| or one of
|this|'s descendents */
// Value for parserPC to restore at the end. Use 'parent' instead for
// information about the parse chain, this may be NULL if parent != NULL.
ParseContext<ParseHandler> *oldpc;
public:
OwnedAtomDefnMapPtr lexdeps; /* unresolved lexical name dependencies */
FuncStmtSet *funcStmts; /* Set of (non-top-level) function statements
that will alias any top-level bindings with
the same name. */
// All inner functions in this context. Only filled in when parsing syntax.
FunctionVector innerFunctions;
// Set when parsing a declaration-like destructuring pattern. This flag
// causes PrimaryExpr to create PN_NAME parse nodes for variable references
// which are not hooked into any definition's use chain, added to any tree
// context's AtomList, etc. etc. CheckDestructuring will do that work
// later.
//
// The comments atop CheckDestructuring explain the distinction between
// assignment-like and declaration-like destructuring patterns, and why
// they need to be treated differently.
bool inDeclDestructuring:1;
// True if we are in a function, saw a "use strict" directive, and weren't
// strict before.
bool funBecameStrict:1;
inline ParseContext(Parser<ParseHandler> *prs, GenericParseContext *parent,
SharedContext *sc, unsigned staticLevel, uint32_t bodyid);
inline ~ParseContext();
inline bool init();
InBlockBool inBlock() const { return InBlockBool(!topStmt || topStmt->type == STMT_BLOCK); }
unsigned blockid();
// True if we are at the topmost level of a entire script or function body.
// For example, while parsing this code we would encounter f1 and f2 at
// body level, but we would not encounter f3 or f4 at body level:
//
// function f1() { function f2() { } }
// if (cond) { function f3() { if (cond) { function f4() { } } } }
//
bool atBodyLevel();
inline bool useAsmOrInsideUseAsm() const {
return sc->isFunctionBox() && sc->asFunctionBox()->useAsmOrInsideUseAsm();
}
};
template <typename ParseHandler>
bool
GenerateBlockId(ParseContext<ParseHandler> *pc, uint32_t &blockid);
template <typename ParseHandler>
struct BindData;
class CompExprTransplanter;
template <typename ParseHandler>
class GenexpGuard;
enum LetContext { LetExpresion, LetStatement };
enum VarContext { HoistVars, DontHoistVars };
enum FunctionType { Getter, Setter, Normal };
template <typename ParseHandler>
struct Parser : private AutoGCRooter, public StrictModeGetter
{
JSContext *const context; /* FIXME Bug 551291: use AutoGCRooter::context? */
TokenStream tokenStream;
LifoAlloc::Mark tempPoolMark;
/* list of parsed objects for GC tracing */
ObjectBox *traceListHead;
/* innermost parse context (stack-allocated) */
ParseContext<ParseHandler> *pc;
SourceCompressionToken *sct; /* compression token for aborting */
/* Root atoms and objects allocated for the parsed tree. */
AutoKeepAtoms keepAtoms;
/* Perform constant-folding; must be true when interfacing with the emitter. */
const bool foldConstants:1;
private:
/* Script can optimize name references based on scope chain. */
const bool compileAndGo:1;
/*
* In self-hosting mode, scripts emit JSOP_CALLINTRINSIC instead of
* JSOP_NAME or JSOP_GNAME to access unbound variables. JSOP_CALLINTRINSIC
* does a name lookup in a special object that contains properties
* installed during global initialization and that properties from
* self-hosted scripts get copied into lazily upon first access in a
* global.
* As that object is inaccessible to client code, the lookups are
* guaranteed to return the original objects, ensuring safe implementation
* of self-hosted builtins.
* Additionally, the special syntax _CallFunction(receiver, ...args, fun)
* is supported, for which bytecode is emitted that invokes |fun| with
* |receiver| as the this-object and ...args as the arguments..
*/
const bool selfHostingMode:1;
/*
* Not all language constructs can be handled during syntax parsing. If it
* is not known whether the parse succeeds or fails, this bit is set and
* the parse will return false.
*/
bool abortedSyntaxParse;
typedef typename ParseHandler::Node Node;
typedef typename ParseHandler::DefinitionNode DefinitionNode;
public:
/* State specific to the kind of parse being performed. */
ParseHandler handler;
private:
bool reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
unsigned errorNumber, va_list args);
public:
bool report(ParseReportKind kind, bool strict, Node pn, unsigned errorNumber, ...);
bool reportWithOffset(ParseReportKind kind, bool strict, uint32_t offset, unsigned errorNumber,
...);
Parser(JSContext *cx, const CompileOptions &options,
const jschar *chars, size_t length, bool foldConstants,
Parser<SyntaxParseHandler> *syntaxParser,
LazyScript *lazyOuterFunction);
~Parser();
#if defined(STARBOARD)
friend void MarkParser(JSTracer *trc, AutoGCRooter *parser);
#else
friend void js::frontend::MarkParser(JSTracer *trc, AutoGCRooter *parser);
#endif
const char *getFilename() const { return tokenStream.getFilename(); }
JSVersion versionNumber() const { return tokenStream.versionNumber(); }
/*
* Parse a top-level JS script.
*/
Node parse(JSObject *chain);
/*
* Allocate a new parsed object or function container from
* cx->tempLifoAlloc.
*/
ObjectBox *newObjectBox(JSObject *obj);
ModuleBox *newModuleBox(Module *module, ParseContext<ParseHandler> *pc);
FunctionBox *newFunctionBox(JSFunction *fun, ParseContext<ParseHandler> *pc, bool strict);
/*
* Create a new function object given parse context (pc) and a name (which
* is optional if this is a function expression).
*/
JSFunction *newFunction(GenericParseContext *pc, HandleAtom atom, FunctionSyntaxKind kind);
void trace(JSTracer *trc);
bool hadAbortedSyntaxParse() {
return abortedSyntaxParse;
}
void clearAbortedSyntaxParse() {
abortedSyntaxParse = false;
}
private:
Parser *thisForCtor() { return this; }
Node stringLiteral();
inline Node newName(PropertyName *name);
inline bool abortIfSyntaxParser();
public:
/* Public entry points for parsing. */
Node statement(bool canHaveDirectives = false);
bool maybeParseDirective(Node pn, bool *cont);
// Parse a function, given only its body. Used for the Function constructor.
Node standaloneFunctionBody(HandleFunction fun, const AutoNameVector &formals, HandleScript script,
Node fn, FunctionBox **funbox, bool strict,
bool *becameStrict = NULL);
// Parse a function, given only its arguments and body. Used for lazily
// parsed functions.
Node standaloneLazyFunction(HandleFunction fun, unsigned staticLevel, bool strict);
/*
* Parse a function body. Pass StatementListBody if the body is a list of
* statements; pass ExpressionBody if the body is a single expression.
*/
enum FunctionBodyType { StatementListBody, ExpressionBody };
Node functionBody(FunctionSyntaxKind kind, FunctionBodyType type);
bool functionArgsAndBodyGeneric(Node pn, HandleFunction fun,
HandlePropertyName funName, FunctionType type,
FunctionSyntaxKind kind, bool strict, bool *becameStrict);
virtual bool strictMode() { return pc->sc->strict; }
private:
/*
* JS parsers, from lowest to highest precedence.
*
* Each parser must be called during the dynamic scope of a ParseContext
* object, pointed to by this->pc.
*
* Each returns a parse node tree or null on error.
*
* Parsers whose name has a '1' suffix leave the TokenStream state
* pointing to the token one past the end of the parsed fragment. For a
* number of the parsers this is convenient and avoids a lot of
* unnecessary ungetting and regetting of tokens.
*
* Some parsers have two versions: an always-inlined version (with an 'i'
* suffix) and a never-inlined version (with an 'n' suffix).
*/
Node moduleDecl();
Node functionStmt();
Node functionExpr();
Node statements();
Node blockStatement();
Node ifStatement();
Node doWhileStatement();
Node whileStatement();
Node forStatement();
Node switchStatement();
Node continueStatement();
Node breakStatement();
Node returnStatementOrYieldExpression();
Node withStatement();
Node labeledStatement();
Node throwStatement();
Node tryStatement();
Node debuggerStatement();
#if JS_HAS_BLOCK_SCOPE
Node letStatement();
#endif
Node expressionStatement();
Node variables(ParseNodeKind kind, bool *psimple = NULL,
StaticBlockObject *blockObj = NULL,
VarContext varContext = HoistVars);
Node expr();
Node assignExpr();
Node assignExprWithoutYield(unsigned err);
Node condExpr1();
Node orExpr1();
Node unaryExpr();
Node memberExpr(TokenKind tt, bool allowCallSyntax);
Node primaryExpr(TokenKind tt);
Node parenExpr(bool *genexp = NULL);
/*
* Additional JS parsers.
*/
bool functionArguments(FunctionSyntaxKind kind, Node *list, Node funcpn, bool &hasRest);
Node functionDef(HandlePropertyName name, const TokenStream::Position &start,
size_t startOffset, FunctionType type, FunctionSyntaxKind kind);
bool functionArgsAndBody(Node pn, HandleFunction fun, HandlePropertyName funName,
size_t startOffset, FunctionType type, FunctionSyntaxKind kind,
bool strict, bool *becameStrict = NULL);
Node unaryOpExpr(ParseNodeKind kind, JSOp op, uint32_t begin);
Node condition();
Node comprehensionTail(Node kid, unsigned blockid, bool isGenexp,
ParseContext<ParseHandler> *outerpc,
ParseNodeKind kind = PNK_SEMI, JSOp op = JSOP_NOP);
bool arrayInitializerComprehensionTail(Node pn);
Node generatorExpr(Node kid);
bool argumentList(Node listNode);
Node bracketedExpr();
Node letBlock(LetContext letContext);
Node destructuringExpr(BindData<ParseHandler> *data, TokenKind tt);
Node identifierName();
bool allowsForEachIn() {
#if !JS_HAS_FOR_EACH_IN
return false;
#else
return versionNumber() >= JSVERSION_1_6;
#endif
}
bool setAssignmentLhsOps(Node pn, JSOp op);
bool matchInOrOf(bool *isForOfp);
bool checkFunctionArguments();
bool makeDefIntoUse(Definition *dn, Node pn, JSAtom *atom);
bool checkFunctionDefinition(HandlePropertyName funName, Node *pn, FunctionSyntaxKind kind,
bool *pbodyProcessed);
bool finishFunctionDefinition(Node pn, FunctionBox *funbox, Node prelude, Node body);
bool addFreeVariablesFromLazyFunction(JSFunction *fun, ParseContext<ParseHandler> *pc);
bool isValidForStatementLHS(Node pn1, JSVersion version,
bool forDecl, bool forEach, bool forOf);
bool setLvalKid(Node pn, Node kid, const char *name);
bool setIncOpKid(Node pn, Node kid, TokenKind tt, bool preorder);
bool checkStrictAssignment(Node lhs);
bool checkStrictBinding(HandlePropertyName name, Node pn);
bool defineArg(Node funcpn, HandlePropertyName name,
bool disallowDuplicateArgs = false, Node *duplicatedArg = NULL);
Node pushLexicalScope(StmtInfoPC *stmt);
Node pushLexicalScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
Node pushLetScope(Handle<StaticBlockObject*> blockObj, StmtInfoPC *stmt);
bool noteNameUse(HandlePropertyName name, Node pn);
Node newRegExp();
Node newBindingNode(PropertyName *name, bool functionScope, VarContext varContext = HoistVars);
bool checkDestructuring(BindData<ParseHandler> *data, Node left, bool toplevel = true);
bool bindDestructuringVar(BindData<ParseHandler> *data, Node pn);
bool bindDestructuringLHS(Node pn);
bool makeSetCall(Node pn, unsigned msg);
Node cloneLeftHandSide(Node opn);
Node cloneParseTree(Node opn);
Node newNumber(const Token &tok) {
return handler.newNumber(tok.number(), tok.decimalPoint(), tok.pos);
}
static bool
bindDestructuringArg(JSContext *cx, BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
static bool
bindLet(JSContext *cx, BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
static bool
bindVarOrConst(JSContext *cx, BindData<ParseHandler> *data,
HandlePropertyName name, Parser<ParseHandler> *parser);
static Node null() { return ParseHandler::null(); }
bool reportRedeclaration(Node pn, bool isConst, JSAtom *atom);
bool reportBadReturn(Node pn, ParseReportKind kind, unsigned errnum, unsigned anonerrnum);
bool checkFinalReturn(Node pn);
DefinitionNode getOrCreateLexicalDependency(ParseContext<ParseHandler> *pc, JSAtom *atom);
bool leaveFunction(Node fn, HandlePropertyName funName,
ParseContext<ParseHandler> *outerpc,
FunctionSyntaxKind kind = Expression);
TokenPos pos() const { return tokenStream.currentToken().pos; }
friend class CompExprTransplanter;
friend class GenexpGuard<ParseHandler>;
friend struct BindData<ParseHandler>;
};
/* Declare some required template specializations. */
template <>
ParseNode *
Parser<FullParseHandler>::expr();
template <>
SyntaxParseHandler::Node
Parser<SyntaxParseHandler>::expr();
template <>
bool
Parser<FullParseHandler>::setAssignmentLhsOps(ParseNode *pn, JSOp op);
template <>
bool
Parser<SyntaxParseHandler>::setAssignmentLhsOps(Node pn, JSOp op);
} /* namespace frontend */
} /* namespace js */
/*
* Convenience macro to access Parser.tokenStream as a pointer.
*/
#define TS(p) (&(p)->tokenStream)
#endif /* frontend_Parser_h */