📄 jsparse.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code, released
* March 31, 1998.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* JS parser.
*
* This is a recursive-descent parser for the JavaScript language specified by
* "The JavaScript 1.5 Language Specification". It uses lexical and semantic
* feedback to disambiguate non-LL(1) structures. It generates trees of nodes
* induced by the recursive parsing (not precise syntax trees, see jsparse.h).
* After tree construction, it rewrites trees to fold constants and evaluate
* compile-time expressions. Finally, it calls js_EmitTree (see jsemit.h) to
* generate bytecode.
*
* This parser attempts no error recovery. The dense JSTokenType enumeration
* was designed with error recovery built on 64-bit first and follow bitsets
* in mind, however.
*/
#include "jsstddef.h"
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "jstypes.h"
#include "jsarena.h" /* Added by JSIFY */
#include "jsutil.h" /* Added by JSIFY */
#include "jsapi.h"
#include "jsatom.h"
#include "jscntxt.h"
#include "jsconfig.h"
#include "jsemit.h"
#include "jsfun.h"
#include "jsinterp.h"
#include "jslock.h"
#include "jsnum.h"
#include "jsobj.h"
#include "jsopcode.h"
#include "jsparse.h"
#include "jsscan.h"
#include "jsscope.h"
#include "jsscript.h"
#include "jsstr.h"
/*
* JS parsers, from lowest to highest precedence.
*
* Each parser takes a context and a token stream, and emits bytecode using
* a code generator.
*/
typedef JSParseNode *
JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);
typedef JSParseNode *
JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc,
JSBool allowCallSyntax);
static JSParser FunctionStmt;
#if JS_HAS_LEXICAL_CLOSURE
static JSParser FunctionExpr;
#endif
static JSParser Statements;
static JSParser Statement;
static JSParser Variables;
static JSParser Expr;
static JSParser AssignExpr;
static JSParser CondExpr;
static JSParser OrExpr;
static JSParser AndExpr;
static JSParser BitOrExpr;
static JSParser BitXorExpr;
static JSParser BitAndExpr;
static JSParser EqExpr;
static JSParser RelExpr;
static JSParser ShiftExpr;
static JSParser AddExpr;
static JSParser MulExpr;
static JSParser UnaryExpr;
static JSMemberParser MemberExpr;
static JSParser PrimaryExpr;
/*
* Insist that the next token be of type tt, or report errno and return null.
* NB: this macro uses cx and ts from its lexical environment.
*/
#define MUST_MATCH_TOKEN(tt, errno) \
JS_BEGIN_MACRO \
if (js_GetToken(cx, ts) != tt) { \
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, errno); \
return NULL; \
} \
JS_END_MACRO
#define CHECK_RECURSION() \
JS_BEGIN_MACRO \
int stackDummy; \
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { \
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, \
JSMSG_OVER_RECURSED); \
return NULL; \
} \
JS_END_MACRO
#ifdef METER_PARSENODES
static uint32 parsenodes = 0;
static uint32 maxparsenodes = 0;
static uint32 recyclednodes = 0;
#endif
static void
RecycleTree(JSParseNode *pn, JSTreeContext *tc)
{
if (!pn)
return;
JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */
pn->pn_next = tc->nodeList;
tc->nodeList = pn;
#ifdef METER_PARSENODES
recyclednodes++;
#endif
}
static JSParseNode *
NewOrRecycledNode(JSContext *cx, JSTreeContext *tc)
{
JSParseNode *pn;
pn = tc->nodeList;
if (!pn) {
JS_ARENA_ALLOCATE_TYPE(pn, JSParseNode, &cx->tempPool);
if (!pn)
JS_ReportOutOfMemory(cx);
} else {
tc->nodeList = pn->pn_next;
/* Recycle immediate descendents only, to save work and working set. */
switch (pn->pn_arity) {
case PN_FUNC:
RecycleTree(pn->pn_body, tc);
break;
case PN_LIST:
if (pn->pn_head) {
/* XXX check for dup recycles in the list */
*pn->pn_tail = tc->nodeList;
tc->nodeList = pn->pn_head;
#ifdef METER_PARSENODES
recyclednodes += pn->pn_count;
#endif
}
break;
case PN_TERNARY:
RecycleTree(pn->pn_kid1, tc);
RecycleTree(pn->pn_kid2, tc);
RecycleTree(pn->pn_kid3, tc);
break;
case PN_BINARY:
RecycleTree(pn->pn_left, tc);
RecycleTree(pn->pn_right, tc);
break;
case PN_UNARY:
RecycleTree(pn->pn_kid, tc);
break;
case PN_NAME:
RecycleTree(pn->pn_expr, tc);
break;
case PN_NULLARY:
break;
}
}
return pn;
}
/*
* Allocate a JSParseNode from cx's temporary arena.
*/
static JSParseNode *
NewParseNode(JSContext *cx, JSToken *tok, JSParseNodeArity arity,
JSTreeContext *tc)
{
JSParseNode *pn;
pn = NewOrRecycledNode(cx, tc);
if (!pn)
return NULL;
pn->pn_type = tok->type;
pn->pn_pos = tok->pos;
pn->pn_op = JSOP_NOP;
pn->pn_arity = arity;
pn->pn_next = NULL;
#ifdef METER_PARSENODES
parsenodes++;
if (parsenodes - recyclednodes > maxparsenodes)
maxparsenodes = parsenodes - recyclednodes;
#endif
return pn;
}
static JSParseNode *
NewBinary(JSContext *cx, JSTokenType tt,
JSOp op, JSParseNode *left, JSParseNode *right,
JSTreeContext *tc)
{
JSParseNode *pn, *pn1, *pn2;
if (!left || !right)
return NULL;
/*
* Flatten a left-associative (left-heavy) tree of a given operator into
* a list, to reduce js_FoldConstants and js_EmitTree recursion.
*/
if (left->pn_type == tt &&
left->pn_op == op &&
(js_CodeSpec[op].format & JOF_LEFTASSOC)) {
if (left->pn_arity != PN_LIST) {
pn1 = left->pn_left, pn2 = left->pn_right;
left->pn_arity = PN_LIST;
PN_INIT_LIST_1(left, pn1);
PN_APPEND(left, pn2);
left->pn_extra = 0;
if (tt == TOK_PLUS) {
if (pn1->pn_type == TOK_STRING)
left->pn_extra |= PNX_STRCAT;
else if (pn1->pn_type != TOK_NUMBER)
left->pn_extra |= PNX_CANTFOLD;
if (pn2->pn_type == TOK_STRING)
left->pn_extra |= PNX_STRCAT;
else if (pn2->pn_type != TOK_NUMBER)
left->pn_extra |= PNX_CANTFOLD;
}
}
PN_APPEND(left, right);
left->pn_pos.end = right->pn_pos.end;
if (tt == TOK_PLUS) {
if (right->pn_type == TOK_STRING)
left->pn_extra |= PNX_STRCAT;
else if (right->pn_type != TOK_NUMBER)
left->pn_extra |= PNX_CANTFOLD;
}
return left;
}
/*
* Fold constant addition immediately, to conserve node space and, what's
* more, so js_FoldConstants never sees mixed addition and concatenation
* operations with more than one leading non-string operand in a PN_LIST
* generated for expressions such as 1 + 2 + "pt" (which should evaluate
* to "3pt", not "12pt").
*/
if (tt == TOK_PLUS &&
left->pn_type == TOK_NUMBER &&
right->pn_type == TOK_NUMBER) {
left->pn_dval += right->pn_dval;
RecycleTree(right, tc);
return left;
}
pn = NewOrRecycledNode(cx, tc);
if (!pn)
return NULL;
pn->pn_type = tt;
pn->pn_pos.begin = left->pn_pos.begin;
pn->pn_pos.end = right->pn_pos.end;
pn->pn_op = op;
pn->pn_arity = PN_BINARY;
pn->pn_left = left;
pn->pn_right = right;
pn->pn_next = NULL;
#ifdef METER_PARSENODES
parsenodes++;
if (parsenodes - recyclednodes > maxparsenodes)
maxparsenodes = parsenodes - recyclednodes;
#endif
return pn;
}
#if JS_HAS_GETTER_SETTER
static JSTokenType
CheckGetterOrSetter(JSContext *cx, JSTokenStream *ts, JSTokenType tt)
{
JSAtom *atom;
JSRuntime *rt;
JSOp op;
const char *name;
JS_ASSERT(CURRENT_TOKEN(ts).type == TOK_NAME);
atom = CURRENT_TOKEN(ts).t_atom;
rt = cx->runtime;
if (atom == rt->atomState.getterAtom)
op = JSOP_GETTER;
else if (atom == rt->atomState.setterAtom)
op = JSOP_SETTER;
else
return TOK_NAME;
if (js_PeekTokenSameLine(cx, ts) != tt)
return TOK_NAME;
(void) js_GetToken(cx, ts);
if (CURRENT_TOKEN(ts).t_op != JSOP_NOP) {
js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
JSMSG_BAD_GETTER_OR_SETTER,
(op == JSOP_GETTER)
? js_getter_str
: js_setter_str);
return TOK_ERROR;
}
CURRENT_TOKEN(ts).t_op = op;
name = js_AtomToPrintableString(cx, atom);
if (!name ||
!js_ReportCompileErrorNumber(cx, ts, NULL,
JSREPORT_WARNING |
JSREPORT_STRICT,
JSMSG_DEPRECATED_USAGE,
name)) {
return TOK_ERROR;
}
return tt;
}
#endif
/*
* Parse a top-level JS script.
*/
JS_FRIEND_API(JSParseNode *)
js_ParseTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts)
{
JSStackFrame *fp, frame;
JSTreeContext tc;
JSParseNode *pn;
/*
* Push a compiler frame if we have no frames, or if the top frame is a
* lightweight function activation, or if its scope chain doesn't match
* the one passed to us.
*/
fp = cx->fp;
if (!fp || !fp->varobj || fp->scopeChain != chain) {
memset(&frame, 0, sizeof frame);
frame.varobj = frame.scopeChain = chain;
if (cx->options & JSOPTION_VAROBJFIX) {
while ((chain = JS_GetParent(cx, chain)) != NULL)
frame.varobj = chain;
}
frame.down = fp;
cx->fp = &frame;
}
/*
* Protect atoms from being collected by a GC activation, which might
* - nest on this thread due to out of memory (the so-called "last ditch"
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -