📄 jsparse.c
字号:
SPROP_INVALID_SLOT,
JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_SHARED,
SPROP_HAS_SHORTID | dupflag,
fun->nargs)) {
return NULL;
}
fun->nargs++;
} while (js_MatchToken(cx, ts, TOK_COMMA));
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_FORMAL);
}
MUST_MATCH_TOKEN(TOK_LC, JSMSG_CURLY_BEFORE_BODY);
pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin;
TREE_CONTEXT_INIT(&funtc);
body = FunctionBody(cx, ts, fun, &funtc);
if (!body)
return NULL;
MUST_MATCH_TOKEN(TOK_RC, JSMSG_CURLY_AFTER_BODY);
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
#if JS_HAS_LEXICAL_CLOSURE
/*
* If we collected flags that indicate nested heavyweight functions, or
* this function contains heavyweight-making statements (references to
* __parent__ or __proto__; use of with, eval, import, or export; and
* assignment to arguments), flag the function as heavyweight (requiring
* a call object per invocation).
*/
if (funtc.flags & TCF_FUN_HEAVYWEIGHT) {
fun->flags |= JSFUN_HEAVYWEIGHT;
tc->flags |= TCF_FUN_HEAVYWEIGHT;
} else {
/*
* If this function is a named statement function not at top-level
* (i.e. a JSOP_CLOSURE), or if it refers to unqualified names that
* are not local args or vars (TCF_FUN_USES_NONLOCALS), then our
* enclosing function, if any, must be heavyweight.
*/
if ((!lambda && funAtom && tc->topStmt) ||
(funtc.flags & TCF_FUN_USES_NONLOCALS)) {
tc->flags |= TCF_FUN_HEAVYWEIGHT;
}
}
#endif
/*
* Record names for function statements in tc->decls so we know when to
* avoid optimizing variable references that might name a function.
*/
if (!lambda && funAtom) {
ATOM_LIST_SEARCH(ale, &tc->decls, funAtom);
if (ale) {
prevop = ALE_JSOP(ale);
if (JS_HAS_STRICT_OPTION(cx) || prevop == JSOP_DEFCONST) {
const char *name = js_AtomToPrintableString(cx, funAtom);
if (!name ||
!js_ReportCompileErrorNumber(cx, ts, NULL,
(prevop != JSOP_DEFCONST)
? JSREPORT_WARNING |
JSREPORT_STRICT
: JSREPORT_ERROR,
JSMSG_REDECLARED_VAR,
(prevop == JSOP_DEFFUN ||
prevop == JSOP_CLOSURE)
? js_function_str
: (prevop == JSOP_DEFCONST)
? js_const_str
: js_var_str,
name)) {
return NULL;
}
}
if (tc->topStmt && prevop == JSOP_DEFVAR)
tc->flags |= TCF_FUN_CLOSURE_VS_VAR;
} else {
ale = js_IndexAtom(cx, funAtom, &tc->decls);
if (!ale)
return NULL;
}
ALE_SET_JSOP(ale, tc->topStmt ? JSOP_CLOSURE : JSOP_DEFFUN);
#if JS_HAS_LEXICAL_CLOSURE
/*
* A function nested at top level inside another's body needs only a
* local variable to bind its name to its value, and not an activation
* object property (it might also need the activation property, if the
* outer function contains with statements, e.g., but the stack slot
* wins when jsemit.c's LookupArgOrVar can optimize a JSOP_NAME into a
* JSOP_GETVAR bytecode).
*/
if (!tc->topStmt && (tc->flags & TCF_IN_FUNCTION)) {
JSStackFrame *fp;
JSObject *varobj;
/*
* Define a property on the outer function so that LookupArgOrVar
* can properly optimize accesses.
*
* XXX Here and in Variables, we use the function object's scope,
* XXX arguably polluting it, when we could use a compiler-private
* XXX scope structure. Tradition!
*/
fp = cx->fp;
varobj = fp->varobj;
JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass);
JS_ASSERT(fp->fun == (JSFunction *) JS_GetPrivate(cx, varobj));
if (!js_DefineNativeProperty(cx, varobj, (jsid)funAtom,
OBJECT_TO_JSVAL(fun->object),
js_GetLocalVariable,
js_SetLocalVariable,
JSPROP_ENUMERATE,
SPROP_HAS_SHORTID, fp->fun->nvars,
NULL)) {
return NULL;
}
fp->fun->nvars++;
}
#endif
}
#if JS_HAS_LEXICAL_CLOSURE
if (lambda || !funAtom) {
/*
* ECMA ed. 3 standard: function expression, possibly anonymous (even
* if at top-level, an unnamed function is an expression statement, not
* a function declaration).
*/
op = fun->atom ? JSOP_NAMEDFUNOBJ : JSOP_ANONFUNOBJ;
} else if (tc->topStmt) {
/*
* ECMA ed. 3 extension: a function expression statement not at the
* top level, e.g., in a compound statement such as the "then" part
* of an "if" statement, binds a closure only if control reaches that
* sub-statement.
*/
op = JSOP_CLOSURE;
} else
#endif
op = JSOP_NOP;
/*
* Pending a better automatic GC root management scheme (see Mozilla bug
* 40757, http://bugzilla.mozilla.org/show_bug.cgi?id=40757), we need to
* atomize here to protect against a GC activation.
*/
pn->pn_funAtom = js_AtomizeObject(cx, fun->object, 0);
if (!pn->pn_funAtom)
return NULL;
pn->pn_op = op;
pn->pn_body = body;
pn->pn_flags = funtc.flags & TCF_FUN_FLAGS;
pn->pn_tryCount = funtc.tryCount;
TREE_CONTEXT_FINISH(&funtc);
return pn;
}
static JSParseNode *
FunctionStmt(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
return FunctionDef(cx, ts, tc, JS_FALSE);
}
#if JS_HAS_LEXICAL_CLOSURE
static JSParseNode *
FunctionExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
return FunctionDef(cx, ts, tc, JS_TRUE);
}
#endif
/*
* Parse the statements in a block, creating a TOK_LC node that lists the
* statements' trees. If called from block-parsing code, the caller must
* match { before and } after.
*/
static JSParseNode *
Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
JSParseNode *pn, *pn2;
JSTokenType tt;
CHECK_RECURSION();
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_LIST, tc);
if (!pn)
return NULL;
PN_INIT_LIST(pn);
ts->flags |= TSF_REGEXP;
while ((tt = js_PeekToken(cx, ts)) > TOK_EOF && tt != TOK_RC) {
ts->flags &= ~TSF_REGEXP;
pn2 = Statement(cx, ts, tc);
if (!pn2)
return NULL;
ts->flags |= TSF_REGEXP;
/* If compiling top-level statements, emit as we go to save space. */
if (!tc->topStmt && (tc->flags & TCF_COMPILING)) {
if (cx->fp->fun &&
JS_HAS_STRICT_OPTION(cx) &&
(tc->flags & TCF_RETURN_EXPR)) {
/*
* Check pn2 for lack of a final return statement if it is the
* last statement in the block.
*/
tt = js_PeekToken(cx, ts);
if ((tt == TOK_EOF || tt == TOK_RC) &&
!CheckFinalReturn(cx, ts, pn2)) {
tt = TOK_ERROR;
break;
}
/*
* Clear TCF_RETURN_EXPR so FunctionBody doesn't try to
* CheckFinalReturn again.
*/
tc->flags &= ~TCF_RETURN_EXPR;
}
if (!js_FoldConstants(cx, pn2, tc) ||
!js_AllocTryNotes(cx, (JSCodeGenerator *)tc) ||
!js_EmitTree(cx, (JSCodeGenerator *)tc, pn2)) {
tt = TOK_ERROR;
break;
}
RecycleTree(pn2, tc);
} else {
PN_APPEND(pn, pn2);
}
}
ts->flags &= ~TSF_REGEXP;
if (tt == TOK_ERROR)
return NULL;
pn->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
return pn;
}
static JSParseNode *
Condition(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
JSParseNode *pn, *pn2;
MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_COND);
pn = Expr(cx, ts, tc);
if (!pn)
return NULL;
MUST_MATCH_TOKEN(TOK_RP, JSMSG_PAREN_AFTER_COND);
/*
* Check for (a = b) and "correct" it to (a == b) iff b's operator has
* greater precedence than ==.
* XXX not ECMA, but documented in several books -- now a strict warning.
*/
if (pn->pn_type == TOK_ASSIGN &&
pn->pn_op == JSOP_NOP &&
pn->pn_right->pn_type > TOK_EQOP)
{
JSBool rewrite = !JSVERSION_IS_ECMA(cx->version);
if (!js_ReportCompileErrorNumber(cx, ts, NULL,
JSREPORT_WARNING | JSREPORT_STRICT,
JSMSG_EQUAL_AS_ASSIGN,
rewrite
? "\nAssuming equality test"
: "")) {
return NULL;
}
if (rewrite) {
pn->pn_type = TOK_EQOP;
pn->pn_op = (JSOp)cx->jsop_eq;
pn2 = pn->pn_left;
switch (pn2->pn_op) {
case JSOP_SETNAME:
pn2->pn_op = JSOP_NAME;
break;
case JSOP_SETPROP:
pn2->pn_op = JSOP_GETPROP;
break;
case JSOP_SETELEM:
pn2->pn_op = JSOP_GETELEM;
break;
default:
JS_ASSERT(0);
}
}
}
return pn;
}
static JSBool
MatchLabel(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
{
JSAtom *label;
#if JS_HAS_LABEL_STATEMENT
JSTokenType tt;
tt = js_PeekTokenSameLine(cx, ts);
if (tt == TOK_ERROR)
return JS_FALSE;
if (tt == TOK_NAME) {
(void) js_GetToken(cx, ts);
label = CURRENT_TOKEN(ts).t_atom;
} else {
label = NULL;
}
#else
label = NULL;
#endif
pn->pn_atom = label;
return JS_TRUE;
}
#if JS_HAS_EXPORT_IMPORT
static JSParseNode *
ImportExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc)
{
JSParseNode *pn, *pn2, *pn3;
JSTokenType tt;
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NO_IMPORT_NAME);
pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
if (!pn)
return NULL;
pn->pn_op = JSOP_NAME;
pn->pn_atom = CURRENT_TOKEN(ts).t_atom;
pn->pn_expr = NULL;
pn->pn_slot = -1;
pn->pn_attrs = 0;
ts->flags |= TSF_REGEXP;
while ((tt = js_GetToken(cx, ts)) == TOK_DOT || tt == TOK_LB) {
ts->flags &= ~TSF_REGEXP;
if (pn->pn_op == JSOP_IMPORTALL)
goto bad_import;
if (tt == TOK_DOT) {
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_NAME, tc);
if (!pn2)
return NULL;
if (js_MatchToken(cx, ts, TOK_STAR)) {
pn2->pn_op = JSOP_IMPORTALL;
pn2->pn_atom = NULL;
} else {
MUST_MATCH_TOKEN(TOK_NAME, JSMSG_NAME_AFTER_DOT);
pn2->pn_op = JSOP_GETPROP;
pn2->pn_atom = CURRENT_TOKEN(ts).t_atom;
pn2->pn_slot = -1;
pn2->pn_attrs = 0;
}
pn2->pn_expr = pn;
pn2->pn_pos.begin = pn->pn_pos.begin;
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
} else {
/* Make a TOK_LB node. */
pn2 = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_BINARY, tc);
if (!pn2)
return NULL;
pn3 = Expr(cx, ts, tc);
if (!pn3)
return NULL;
MUST_MATCH_TOKEN(TOK_RB, JSMSG_BRACKET_IN_INDEX);
pn2->pn_pos.begin = pn->pn_pos.begin;
pn2->pn_pos.end = CURRENT_TOKEN(ts).pos.end;
pn2->pn_op = JSOP_GETELEM;
pn2->pn_left = pn;
pn2->pn_right = pn3;
}
pn = pn2;
ts->flags |= TSF_REGEXP;
}
ts->flags &= ~TSF_REGEXP;
if (tt == TOK_ERROR)
return NULL;
js_UngetToken(ts);
switch (pn->pn_op) {
case JSOP_GETPROP:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -