📄 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_CLOSUREstatic JSParser FunctionExpr;#endifstatic 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_PARSENODESstatic uint32 parsenodes = 0;static uint32 maxparsenodes = 0;static uint32 recyclednodes = 0;#endifstatic voidRecycleTree(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_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, 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" * GC attempted within js_AllocGCThing), or * - run for any reason on another thread if this thread is suspended on * an object lock before it finishes generating bytecode into a script * protected from the GC by a root or a stack frame reference. */ JS_KEEP_ATOMS(cx->runtime); TREE_CONTEXT_INIT(&tc); pn = Statements(cx, ts, &tc); if (pn) { if (!js_MatchToken(cx, ts, TOK_EOF)) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -