📄 jsparse.c
字号:
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sw=4 et tw=78: * * ***** 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. */#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 "jsarray.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"#if JS_HAS_XML_SUPPORT#include "jsxml.h"#endif#if JS_HAS_DESTRUCTURING#include "jsdhash.h"#endif/* * JS parsers, from lowest to highest precedence. * * Each parser takes a context, a token stream, and a tree context struct. * Each returns a parse node tree or null on error. */typedef JSParseNode *JSParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc);typedef JSParseNode *JSMemberParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool allowCallSyntax);typedef JSParseNode *JSPrimaryParser(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt, JSBool afterDot);static JSParser FunctionStmt;static JSParser FunctionExpr;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 JSPrimaryParser 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, JSREPORT_TS | 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, JSREPORT_TS | JSREPORT_ERROR, \ JSMSG_OVER_RECURSED); \ return NULL; \ } \ JS_END_MACRO#ifdef METER_PARSENODESstatic uint32 parsenodes = 0;static uint32 maxparsenodes = 0;static uint32 recyclednodes = 0;#endifstatic JSParseNode *RecycleTree(JSParseNode *pn, JSTreeContext *tc){ JSParseNode *next; if (!pn) return NULL; JS_ASSERT(pn != tc->nodeList); /* catch back-to-back dup recycles */ next = pn->pn_next; pn->pn_next = tc->nodeList; tc->nodeList = pn;#ifdef METER_PARSENODES recyclednodes++;#endif return next;}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; } }#ifdef METER_PARSENODES if (pn) { parsenodes++; if (parsenodes - recyclednodes > maxparsenodes) maxparsenodes = parsenodes - recyclednodes; }#endif return pn;}/* * Allocate a JSParseNode from cx's temporary arena. */static JSParseNode *NewParseNode(JSContext *cx, JSTokenStream *ts, JSParseNodeArity arity, JSTreeContext *tc){ JSParseNode *pn; JSToken *tp; pn = NewOrRecycledNode(cx, tc); if (!pn) return NULL; tp = &CURRENT_TOKEN(ts); pn->pn_type = tp->type; pn->pn_pos = tp->pos; pn->pn_op = JSOP_NOP; pn->pn_arity = arity; pn->pn_next = NULL; pn->pn_ts = ts; pn->pn_source = NULL; 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); 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; left->pn_pos.end = right->pn_pos.end; 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; pn->pn_ts = NULL; pn->pn_source = NULL; return pn;}#if JS_HAS_GETTER_SETTERstatic JSTokenTypeCheckGetterOrSetter(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, JSREPORT_TS | 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; if (JS_HAS_STRICT_OPTION(cx)) { name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DEPRECATED_USAGE, name)) { return TOK_ERROR; } } return tt;}#endifstatic voidMaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, JSStackFrame *newfp){ /* * Always push a new frame if the current frame is special, so that * Variables gets the correct variables object: the one from the special * frame's caller. */ if (oldfp && oldfp->varobj && oldfp->scopeChain == chain && !(oldfp->flags & JSFRAME_SPECIAL)) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -