📄 jsparse.c
字号:
JSMSG_SYNTAX_ERROR); pn = NULL; } else { pn->pn_type = TOK_LC; if (!js_FoldConstants(cx, pn, &tc)) pn = NULL; } } TREE_CONTEXT_FINISH(&tc); JS_UNKEEP_ATOMS(cx->runtime); cx->fp = fp; return pn;}/* * Compile a top-level script. */JS_FRIEND_API(JSBool)js_CompileTokenStream(JSContext *cx, JSObject *chain, JSTokenStream *ts, JSCodeGenerator *cg){ JSStackFrame *fp, frame; uint32 flags; JSParseNode *pn; JSBool ok;#ifdef METER_PARSENODES void *sbrk(ptrdiff_t), *before = sbrk(0);#endif /* * 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; } flags = cx->fp->flags; cx->fp->flags = flags | (JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING); /* Prevent GC activation while compiling. */ JS_KEEP_ATOMS(cx->runtime); pn = Statements(cx, ts, &cg->treeContext); if (!pn) { ok = JS_FALSE; } else if (!js_MatchToken(cx, ts, TOK_EOF)) { js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); ok = JS_FALSE; } else {#ifdef METER_PARSENODES printf("Parser growth: %d (%u nodes, %u max, %u unrecycled)\n", (char *)sbrk(0) - (char *)before, parsenodes, maxparsenodes, parsenodes - recyclednodes); before = sbrk(0);#endif /* * No need to emit code here -- Statements already has, for each * statement in turn. Search for TCF_COMPILING in Statements, below. * That flag is set for every tc == &cg->treeContext, and it implies * that the tc can be downcast to a cg and used to emit code during * parsing, rather than at the end of the parse phase. */ JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); ok = JS_TRUE; }#ifdef METER_PARSENODES printf("Code-gen growth: %d (%u bytecodes, %u srcnotes)\n", (char *)sbrk(0) - (char *)before, CG_OFFSET(cg), cg->noteCount);#endif#ifdef JS_ARENAMETER JS_DumpArenaStats(stdout);#endif JS_UNKEEP_ATOMS(cx->runtime); cx->fp->flags = flags; cx->fp = fp; return ok;}/* * Insist on a final return before control flows out of pn, but don't be too * smart about loops (do {...; return e2;} while(0) at the end of a function * that contains an early return e1 will get a strict-option-only warning). */#define ENDS_IN_OTHER 0#define ENDS_IN_RETURN 1#define ENDS_IN_BREAK 2static intHasFinalReturn(JSParseNode *pn){ uintN rv, rv2, hasDefault; JSParseNode *pn2, *pn3; switch (pn->pn_type) { case TOK_LC: if (!pn->pn_head) return ENDS_IN_OTHER; return HasFinalReturn(PN_LAST(pn)); case TOK_IF: rv = HasFinalReturn(pn->pn_kid2); if (pn->pn_kid3) rv &= HasFinalReturn(pn->pn_kid3); return rv;#if JS_HAS_SWITCH_STATEMENT case TOK_SWITCH: rv = ENDS_IN_RETURN; hasDefault = ENDS_IN_OTHER; for (pn2 = pn->pn_kid2->pn_head; rv && pn2; pn2 = pn2->pn_next) { if (pn2->pn_type == TOK_DEFAULT) hasDefault = ENDS_IN_RETURN; pn3 = pn2->pn_right; JS_ASSERT(pn3->pn_type == TOK_LC); if (pn3->pn_head) { rv2 = HasFinalReturn(PN_LAST(pn3)); if (rv2 == ENDS_IN_OTHER && pn2->pn_next) /* Falling through to next case or default. */; else rv &= rv2; } } /* If a final switch has no default case, we judge it harshly. */ rv &= hasDefault; return rv;#endif /* JS_HAS_SWITCH_STATEMENT */ case TOK_BREAK: return ENDS_IN_BREAK; case TOK_WITH: return HasFinalReturn(pn->pn_right); case TOK_RETURN: return ENDS_IN_RETURN; case TOK_COLON: return HasFinalReturn(pn->pn_expr);#if JS_HAS_EXCEPTIONS case TOK_THROW: return ENDS_IN_RETURN; case TOK_TRY: /* If we have a finally block that returns, we are done. */ if (pn->pn_kid3) { rv = HasFinalReturn(pn->pn_kid3); if (rv == ENDS_IN_RETURN) return rv; } /* Else check the try block and any and all catch statements. */ rv = HasFinalReturn(pn->pn_kid1); if (pn->pn_kid2) rv &= HasFinalReturn(pn->pn_kid2); return rv; case TOK_CATCH: /* Check this block's code and iterate over further catch blocks. */ rv = HasFinalReturn(pn->pn_kid3); for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2) rv &= HasFinalReturn(pn2->pn_kid3); return rv;#endif default: return ENDS_IN_OTHER; }}static JSBoolReportNoReturnValue(JSContext *cx, JSTokenStream *ts){ JSFunction *fun; JSBool ok; fun = cx->fp->fun; if (fun->atom) { char *name = js_GetStringBytes(ATOM_TO_STRING(fun->atom)); ok = js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, name); } else { ok = js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_ANON_NO_RETURN_VALUE); } return ok;}static JSBoolCheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn){ return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportNoReturnValue(cx, ts);}static JSParseNode *FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, JSTreeContext *tc){ JSStackFrame *fp, frame; JSObject *funobj; uintN oldflags; JSParseNode *pn; fp = cx->fp; funobj = fun->object; if (!fp || fp->fun != fun || fp->varobj != funobj || fp->scopeChain != funobj) { memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; cx->fp = &frame; } oldflags = tc->flags; tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); tc->flags |= TCF_IN_FUNCTION; pn = Statements(cx, ts, tc); /* Check for falling off the end of a function that returns a value. */ if (pn && JS_HAS_STRICT_OPTION(cx) && (tc->flags & TCF_RETURN_EXPR)) { if (!CheckFinalReturn(cx, ts, pn)) pn = NULL; } cx->fp = fp; tc->flags = oldflags | (tc->flags & TCF_FUN_FLAGS); return pn;}/* * Compile a JS function body, which might appear as the value of an event * handler attribute in an HTML <INPUT> tag. */JSBooljs_CompileFunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun){ JSArenaPool codePool, notePool; JSCodeGenerator funcg; JSStackFrame *fp, frame; JSObject *funobj; JSParseNode *pn; JSBool ok; JS_InitArenaPool(&codePool, "code", 1024, sizeof(jsbytecode)); JS_InitArenaPool(¬ePool, "note", 1024, sizeof(jssrcnote)); if (!js_InitCodeGenerator(cx, &funcg, &codePool, ¬ePool, ts->filename, ts->lineno, ts->principals)) { return JS_FALSE; } /* Prevent GC activation while compiling. */ JS_KEEP_ATOMS(cx->runtime); /* Push a JSStackFrame for use by FunctionBody. */ fp = cx->fp; funobj = fun->object; JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && fp->scopeChain != funobj)); memset(&frame, 0, sizeof frame); frame.fun = fun; frame.varobj = frame.scopeChain = funobj; frame.down = fp; frame.flags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? JSFRAME_COMPILING | JSFRAME_COMPILE_N_GO : JSFRAME_COMPILING; cx->fp = &frame; /* Ensure that the body looks like a block statement to js_EmitTree. */ CURRENT_TOKEN(ts).type = TOK_LC; pn = FunctionBody(cx, ts, fun, &funcg.treeContext); if (!pn) { ok = JS_FALSE; } else { /* * No need to emit code here -- Statements (via FunctionBody) already * has. See similar comment in js_CompileTokenStream, and bug 108257. */ fun->script = js_NewScriptFromCG(cx, &funcg, fun); if (!fun->script) { ok = JS_FALSE; } else { if (funcg.treeContext.flags & TCF_FUN_HEAVYWEIGHT) fun->flags |= JSFUN_HEAVYWEIGHT; ok = JS_TRUE; } } /* Restore saved state and release code generation arenas. */ cx->fp = fp; JS_UNKEEP_ATOMS(cx->runtime); js_FinishCodeGenerator(cx, &funcg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); return ok;}static JSParseNode *FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda){ JSParseNode *pn, *body; JSOp op, prevop; JSAtom *funAtom, *argAtom; JSFunction *fun; JSObject *parent; JSObject *pobj; JSProperty *prop; uintN dupflag; JSBool ok; JSTreeContext funtc; JSAtomListElement *ale; /* Make a TOK_FUNCTION node. */ pn = NewParseNode(cx, &CURRENT_TOKEN(ts), PN_FUNC, tc); if (!pn) return NULL;#if JS_HAS_GETTER_SETTER op = CURRENT_TOKEN(ts).t_op;#endif /* Scan the optional function name into funAtom. */ if (js_MatchToken(cx, ts, TOK_NAME)) funAtom = CURRENT_TOKEN(ts).t_atom; else funAtom = NULL; /* Find the nearest variable-declaring scope and use it as our parent. */ parent = cx->fp->varobj; fun = js_NewFunction(cx, NULL, NULL, 0, lambda ? JSFUN_LAMBDA : 0, parent, funAtom); if (!fun) return NULL;#if JS_HAS_GETTER_SETTER if (op != JSOP_NOP) fun->flags |= (op == JSOP_GETTER) ? JSPROP_GETTER : JSPROP_SETTER;#endif /* Now parse formal argument list and compute fun->nargs. */ MUST_MATCH_TOKEN(TOK_LP, JSMSG_PAREN_BEFORE_FORMAL); if (!js_MatchToken(cx, ts, TOK_RP)) { do { MUST_MATCH_TOKEN(TOK_NAME, JSMSG_MISSING_FORMAL); argAtom = CURRENT_TOKEN(ts).t_atom; pobj = NULL; if (!js_LookupProperty(cx, fun->object, (jsid)argAtom, &pobj, &prop)) { return NULL; } dupflag = 0; if (prop) { ok = JS_TRUE; if (pobj == fun->object && ((JSScopeProperty *) prop)->getter == js_GetArgument) { const char *name = js_AtomToPrintableString(cx, argAtom); /* * A duplicate parameter name. We force a duplicate node * on the SCOPE_LAST_PROP(scope) list with the same id, * distinguished by the SPROP_IS_DUPLICATE flag, and not * mapped by an entry in scope. */ ok = name && js_ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, name); dupflag = SPROP_IS_DUPLICATE; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -