📄 jsparse.c
字号:
return; } memset(newfp, 0, sizeof *newfp); /* Default to sharing the same variables object and scope chain. */ newfp->varobj = newfp->scopeChain = chain; if (cx->options & JSOPTION_VAROBJFIX) { while ((chain = JS_GetParent(cx, chain)) != NULL) newfp->varobj = chain; } newfp->down = oldfp; if (oldfp) { /* * In the case of eval and debugger frames, we need to dig down and find * the real variables objects and function that our new stack frame is * going to use. */ newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_COMPILE_N_GO | JSFRAME_SCRIPT_OBJECT); while (oldfp->flags & JSFRAME_SPECIAL) { oldfp = oldfp->down; if (!oldfp) break; } if (oldfp && (newfp->flags & JSFRAME_SPECIAL)) { newfp->varobj = oldfp->varobj; newfp->vars = oldfp->vars; newfp->fun = oldfp->fun; } } cx->fp = newfp;}/* * 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; MaybeSetupFrame(cx, chain, 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_NewGCThing), 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, JSREPORT_TS | JSREPORT_ERROR, 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; MaybeSetupFrame(cx, chain, 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, JSREPORT_TS | 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 bytecode 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. * * Nowadays the threaded interpreter needs a stop instruction, so we * do have to emit that here. */ JS_ASSERT(cg->treeContext.flags & TCF_COMPILING); ok = js_Emit1(cx, cg, JSOP_STOP) >= 0; }#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. Try to be a bit * smart about loops: do {...; return e2;} while(0) at the end of a function * that contains an early return e1 will get a strict warning. Similarly for * iloops: while (true){...} is treated as though ... returns. */#define ENDS_IN_OTHER 0#define ENDS_IN_RETURN 1#define ENDS_IN_BREAK 2static intHasFinalReturn(JSParseNode *pn){ JSParseNode *pn2, *pn3; uintN rv, rv2, hasDefault; switch (pn->pn_type) { case TOK_LC: if (!pn->pn_head) return ENDS_IN_OTHER; return HasFinalReturn(PN_LAST(pn)); case TOK_IF: if (!pn->pn_kid3) return ENDS_IN_OTHER; return HasFinalReturn(pn->pn_kid2) & HasFinalReturn(pn->pn_kid3); case TOK_WHILE: pn2 = pn->pn_left; if (pn2->pn_type == TOK_PRIMARY && pn2->pn_op == JSOP_TRUE) return ENDS_IN_RETURN; if (pn2->pn_type == TOK_NUMBER && pn2->pn_dval) return ENDS_IN_RETURN; return ENDS_IN_OTHER; case TOK_DO: pn2 = pn->pn_right; if (pn2->pn_type == TOK_PRIMARY) { if (pn2->pn_op == JSOP_FALSE) return HasFinalReturn(pn->pn_left); if (pn2->pn_op == JSOP_TRUE) return ENDS_IN_RETURN; } if (pn2->pn_type == TOK_NUMBER) { if (pn2->pn_dval == 0) return HasFinalReturn(pn->pn_left); return ENDS_IN_RETURN; } return ENDS_IN_OTHER; case TOK_FOR: pn2 = pn->pn_left; if (pn2->pn_arity == PN_TERNARY && !pn2->pn_kid2) return ENDS_IN_RETURN; return ENDS_IN_OTHER; case TOK_SWITCH: rv = ENDS_IN_RETURN; hasDefault = ENDS_IN_OTHER; pn2 = pn->pn_right; if (pn2->pn_type == TOK_LEXICALSCOPE) pn2 = pn2->pn_expr; for (pn2 = pn2->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; 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: case TOK_LEXICALSCOPE: return HasFinalReturn(pn->pn_expr); 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) { JS_ASSERT(pn->pn_kid2->pn_arity == PN_LIST); for (pn2 = pn->pn_kid2->pn_head; pn2; pn2 = pn2->pn_next) rv &= HasFinalReturn(pn2); } return rv; case TOK_CATCH: /* Check this catch block's body. */ return HasFinalReturn(pn->pn_kid3); case TOK_LET: /* Non-binary let statements are let declarations. */ if (pn->pn_arity != PN_BINARY) return ENDS_IN_OTHER; return HasFinalReturn(pn->pn_right); default: return ENDS_IN_OTHER; }}static JSBoolReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, uintN anonerrnum){ JSFunction *fun; const char *name; fun = cx->fp->fun; if (fun->atom) { name = js_AtomToPrintableString(cx, fun->atom); } else { errnum = anonerrnum; name = NULL; } return js_ReportCompileErrorNumber(cx, ts, JSREPORT_TS | flags, errnum, name);}static JSBoolCheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn){ return HasFinalReturn(pn) == ENDS_IN_RETURN || ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE);}static JSParseNode *FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, JSTreeContext *tc){ JSStackFrame *fp, frame; JSObject *funobj; JSStmtInfo stmtInfo; uintN oldflags, firstLine; 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; if (fp) frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; cx->fp = &frame; } /* * Set interpreted early so js_EmitTree can test it to decide whether to * eliminate useless expressions. */ fun->flags |= JSFUN_INTERPRETED; js_PushStatement(tc, &stmtInfo, STMT_BLOCK, -1); stmtInfo.flags = SIF_BODY_BLOCK; oldflags = tc->flags; tc->flags &= ~(TCF_RETURN_EXPR | TCF_RETURN_VOID); tc->flags |= TCF_IN_FUNCTION; /* * Save the body's first line, and store it in pn->pn_pos.begin.lineno * later, because we may have not peeked in ts yet, so Statements won't * acquire a valid pn->pn_pos.begin from the current token. */ firstLine = ts->lineno; pn = Statements(cx, ts, tc); js_PopStatement(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; } /* * If we have a parse tree in pn and a code generator in tc, emit this * function's code. We must do this here, not in js_CompileFunctionBody, * in order to detect TCF_IN_FUNCTION among tc->flags. */ if (pn) { pn->pn_pos.begin.lineno = firstLine; if ((tc->flags & TCF_COMPILING)) { JSCodeGenerator *cg = (JSCodeGenerator *) tc; if (!js_FoldConstants(cx, pn, tc) || !js_EmitFunctionBytecode(cx, cg, pn)) { pn = NULL; } } } cx->fp = fp; tc->flags = oldflags | (tc->flags & (TCF_FUN_FLAGS | TCF_HAS_DEFXMLNS)); return pn;}/* * Compile a JS function body, which might appear as the value of an event * handler attribute in an HTML <INPUT> tag. */JSBool
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -