📄 jsparse.c
字号:
* 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,
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;
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;
}
/* 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 = 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).
*/
static JSBool
HasFinalReturn(JSParseNode *pn)
{
JSBool ok, hasDefault;
JSParseNode *pn2, *pn3;
switch (pn->pn_type) {
case TOK_LC:
if (!pn->pn_head)
return JS_FALSE;
return HasFinalReturn(PN_LAST(pn));
case TOK_IF:
ok = HasFinalReturn(pn->pn_kid2);
ok &= pn->pn_kid3 && HasFinalReturn(pn->pn_kid3);
return ok;
#if JS_HAS_SWITCH_STATEMENT
case TOK_SWITCH:
ok = JS_TRUE;
hasDefault = JS_FALSE;
for (pn2 = pn->pn_kid2->pn_head; ok && pn2; pn2 = pn2->pn_next) {
if (pn2->pn_type == TOK_DEFAULT)
hasDefault = JS_TRUE;
pn3 = pn2->pn_right;
JS_ASSERT(pn3->pn_type == TOK_LC);
if (pn3->pn_head)
ok &= HasFinalReturn(PN_LAST(pn3));
}
/* If a final switch has no default case, we judge it harshly. */
ok &= hasDefault;
return ok;
#endif /* JS_HAS_SWITCH_STATEMENT */
case TOK_WITH:
return HasFinalReturn(pn->pn_right);
case TOK_RETURN:
return JS_TRUE;
#if JS_HAS_EXCEPTIONS
case TOK_THROW:
return JS_TRUE;
case TOK_TRY:
/* If we have a finally block that returns, we are done. */
if (pn->pn_kid3 && HasFinalReturn(pn->pn_kid3))
return JS_TRUE;
/* Else check the try block and any and all catch statements. */
ok = HasFinalReturn(pn->pn_kid1);
if (pn->pn_kid2)
ok &= HasFinalReturn(pn->pn_kid2);
return ok;
case TOK_CATCH:
/* Check this block's code and iterate over further catch blocks. */
ok = HasFinalReturn(pn->pn_kid3);
for (pn2 = pn->pn_kid2; pn2; pn2 = pn2->pn_kid2)
ok &= HasFinalReturn(pn2->pn_kid3);
return ok;
#endif
default:
return JS_FALSE;
}
}
static JSBool
ReportNoReturnValue(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 JSBool
CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn)
{
return HasFinalReturn(pn) || 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.
*/
JSBool
js_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 and js_EmitFunctionBody. */
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;
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;
JSScopeProperty *sprop;
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,
(JSProperty **)&sprop)) {
return NULL;
}
dupflag = 0;
if (sprop) {
ok = JS_TRUE;
if (pobj == fun->object &&
sprop->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;
}
OBJ_DROP_PROPERTY(cx, pobj, (JSProperty *)sprop);
if (!ok)
return NULL;
sprop = NULL;
}
if (!js_AddNativeProperty(cx, fun->object, (jsid)argAtom,
js_GetArgument, js_SetArgument,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -