diff --git a/js/src/jsapi.c b/js/src/jsapi.c index 5c93d81333b6..02a1b59ec3b8 100644 --- a/js/src/jsapi.c +++ b/js/src/jsapi.c @@ -4437,15 +4437,13 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj, const jschar *chars, size_t length, const char *filename, uintN lineno) { - JSParseContext pc; + uint32 tcflags; JSScript *script; CHECK_REQUEST(cx); - if (!js_InitParseContext(cx, &pc, chars, length, NULL, filename, lineno)) - return NULL; - js_InitCompilePrincipals(cx, &pc, principals); - script = js_CompileScript(cx, obj, &pc); - js_FinishParseContext(cx, &pc); + tcflags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? TCF_COMPILE_N_GO : 0; + script = js_CompileScript(cx, obj, principals, tcflags, + chars, length, NULL, filename, lineno); LAST_FRAME_CHECKS(cx, script); return script; } @@ -4471,7 +4469,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj, */ result = JS_TRUE; exnState = JS_SaveExceptionState(cx); - if (js_InitParseContext(cx, &pc, chars, length, NULL, NULL, 1)) { + if (js_InitParseContext(cx, &pc, NULL, chars, length, NULL, NULL, 1)) { older = JS_SetErrorReporter(cx, NULL); if (!js_ParseScript(cx, obj, &pc) && (pc.tokenStream.flags & TSF_UNEXPECTED_EOF)) { @@ -4494,7 +4492,7 @@ JS_PUBLIC_API(JSScript *) JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) { FILE *fp; - JSParseContext pc; + uint32 tcflags; JSScript *script; CHECK_REQUEST(cx); @@ -4509,12 +4507,9 @@ JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename) } } - if (!js_InitParseContext(cx, &pc, NULL, 0, fp, filename, 1)) { - script = NULL; - } else { - script = js_CompileScript(cx, obj, &pc); - js_FinishParseContext(cx, &pc); - } + tcflags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? TCF_COMPILE_N_GO : 0; + script = js_CompileScript(cx, obj, NULL, tcflags, + NULL, 0, fp, filename, 1); if (fp != stdin) fclose(fp); LAST_FRAME_CHECKS(cx, script); @@ -4533,15 +4528,13 @@ JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj, const char *filename, FILE *file, JSPrincipals *principals) { - JSParseContext pc; + uint32 tcflags; JSScript *script; CHECK_REQUEST(cx); - if (!js_InitParseContext(cx, &pc, NULL, 0, file, filename, 1)) - return NULL; - js_InitCompilePrincipals(cx, &pc, principals); - script = js_CompileScript(cx, obj, &pc); - js_FinishParseContext(cx, &pc); + tcflags = JS_HAS_COMPILE_N_GO_OPTION(cx) ? TCF_COMPILE_N_GO : 0; + script = js_CompileScript(cx, obj, principals, tcflags, + NULL, 0, file, filename, 1); LAST_FRAME_CHECKS(cx, script); return script; } @@ -4641,8 +4634,6 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, JSFunction *fun; JSAtom *funAtom, *argAtom; uintN i; - JSParseContext pc; - JSBool ok; CHECK_REQUEST(cx); if (!name) { @@ -4669,13 +4660,8 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj, } } - ok = js_InitParseContext(cx, &pc, chars, length, NULL, filename, lineno); - if (ok) { - js_InitCompilePrincipals(cx, &pc, principals); - ok = js_CompileFunctionBody(cx, &pc, fun); - js_FinishParseContext(cx, &pc); - } - if (!ok) { + if (!js_CompileFunctionBody(cx, fun, principals, chars, length, + filename, lineno)) { fun = NULL; goto out; } @@ -4856,16 +4842,12 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj, const char *filename, uintN lineno, jsval *rval) { - uint32 options; JSScript *script; JSBool ok; CHECK_REQUEST(cx); - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, obj, principals, chars, length, - filename, lineno); - cx->options = options; + script = js_CompileScript(cx, obj, principals, TCF_COMPILE_N_GO, + chars, length, NULL, filename, lineno); if (!script) return JS_FALSE; ok = js_Execute(cx, obj, script, NULL, 0, rval); diff --git a/js/src/jsdbgapi.c b/js/src/jsdbgapi.c index 0026bd070c31..47a05436f56c 100644 --- a/js/src/jsdbgapi.c +++ b/js/src/jsdbgapi.c @@ -50,12 +50,14 @@ #include "jscntxt.h" #include "jsconfig.h" #include "jsdbgapi.h" +#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" #include "jslock.h" #include "jsobj.h" #include "jsopcode.h" +#include "jsparse.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" @@ -1202,7 +1204,7 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, jsval *rval) { JSObject *scobj; - uint32 flags, options; + uint32 flags; JSScript *script; JSBool ok; @@ -1212,17 +1214,14 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp, /* * XXX Hack around ancient compiler API to propagate the JSFRAME_SPECIAL - * flags to the code generator (see js_EmitTree's TOK_SEMI case). + * flags to the code generator. */ flags = fp->flags; fp->flags |= JSFRAME_DEBUGGER | JSFRAME_EVAL; - options = cx->options; - cx->options = options | JSOPTION_COMPILE_N_GO; - script = JS_CompileUCScriptForPrincipals(cx, scobj, - JS_StackFramePrincipals(cx, fp), - chars, length, filename, lineno); + script = js_CompileScript(cx, scobj, JS_StackFramePrincipals(cx, fp), + TCF_COMPILE_N_GO, chars, length, NULL, + filename, lineno); fp->flags = flags; - cx->options = options; if (!script) return JS_FALSE; diff --git a/js/src/jsemit.c b/js/src/jsemit.c index 02f7427ed581..75206b3c6d29 100644 --- a/js/src/jsemit.c +++ b/js/src/jsemit.c @@ -1608,7 +1608,6 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, jsval *vp) { JSBool ok; - JSStackFrame *fp; JSStmtInfo *stmt; jsint slot; JSAtomListElement *ale; @@ -1617,20 +1616,14 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, uintN attrs; /* - * fp chases cg down the stack, but only until we reach the outermost cg. + * Chase down the cg stack, but only until we reach the outermost cg. * This enables propagating consts from top-level into switch cases in a - * function compiled along with the top-level script. All stack frames - * with matching code generators should be flagged with JSFRAME_COMPILING; - * we check sanity here. + * function compiled along with the top-level script. */ *vp = JSVAL_VOID; - ok = JS_TRUE; - fp = cx->fp; do { - JS_ASSERT(fp->flags & JSFRAME_COMPILING); - - obj = fp->varobj; - if (obj == fp->scopeChain) { + if ((cg->treeContext.flags & TCF_IN_FUNCTION) || + cx->fp->varobj == cx->fp->scopeChain) { /* XXX this will need revising when 'let const' is added. */ stmt = js_LexicalLookup(&cg->treeContext, atom, &slot, 0); if (stmt) @@ -1649,16 +1642,18 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, * with object or catch variable; nor can prop's value be changed, * nor can prop be deleted. */ - if (OBJ_GET_CLASS(cx, obj) == &js_FunctionClass) { - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, obj)); - if (js_LookupLocal(cx, fp->fun, atom, NULL) != JSLOCAL_NONE) + if (cg->treeContext.flags & TCF_IN_FUNCTION) { + if (js_LookupLocal(cx, cg->treeContext.fun, atom, NULL) != + JSLOCAL_NONE) { break; - } - - ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, &prop); - if (ok) { - if (pobj == obj && - (fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO))) { + } + } else if (cg->treeContext.flags & TCF_COMPILE_N_GO) { + obj = cx->fp->varobj; + ok = OBJ_LOOKUP_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &pobj, + &prop); + if (!ok) + return JS_FALSE; + if (pobj == obj) { /* * We're compiling code that will be executed immediately, * not re-executed against a different scope chain and/or @@ -1672,13 +1667,14 @@ js_LookupCompileTimeConstant(JSContext *cx, JSCodeGenerator *cg, JSAtom *atom, } if (prop) OBJ_DROP_PROPERTY(cx, pobj, prop); + if (!ok) + return JS_FALSE; + if (prop) + break; } - if (!ok || prop) - break; } - fp = fp->down; } while ((cg = cg->parent) != NULL); - return ok; + return JS_TRUE; } /* @@ -1838,7 +1834,6 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, jsint slot; JSOp op; JSStackFrame *fp; - JSClass *clasp; JSLocalKind localKind; uintN index; JSAtomListElement *ale; @@ -1884,18 +1879,6 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, return JS_TRUE; } - /* - * A Script object can be used to split an eval into a compile step done - * at construction time, and an execute step done separately, possibly in - * a different scope altogether. We therefore cannot do any name-to-slot - * optimizations, but must lookup names at runtime. Note that script_exec - * ensures that its caller's frame has a Call object, so arg and var name - * lookups will succeed. - */ - fp = cx->fp; - if (fp->flags & JSFRAME_SCRIPT_OBJECT) - return JS_TRUE; - /* * We can't optimize if var and closure (a local function not in a larger * expression and not at top-level within another's body) collide. @@ -1904,14 +1887,30 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, if (tc->flags & TCF_FUN_CLOSURE_VS_VAR) return JS_TRUE; - /* - * We can't optimize if we are in an eval called inside a with statement. - */ - if (fp->scopeChain != fp->varobj) - return JS_TRUE; + if (!(tc->flags & TCF_IN_FUNCTION) && + !((cx->fp->flags & JSFRAME_SPECIAL) && cx->fp->fun)) { + /* + * We are compiling a script or eval and eval is not inside a function + * frame. + * + * We can't optimize if we are in an eval called inside a with + * statement. + */ + fp = cx->fp; + if (fp->scopeChain != fp->varobj) + return JS_TRUE; + + /* + * A Script object can be used to split an eval into a compile step + * done at construction time, and an execute step done separately, + * possibly in a different scope altogether. We therefore cannot do + * any name-to-slot optimizations, but must lookup names at runtime. + * Note that script_exec ensures that its caller's frame has a Call + * object, so arg and var name lookups will succeed. + */ + if (fp->flags & JSFRAME_SCRIPT_OBJECT) + return JS_TRUE; - clasp = OBJ_GET_CLASS(cx, fp->varobj); - if (clasp != &js_FunctionClass && clasp != &js_CallClass) { /* * We cannot optimize the name access when compiling with an eval or * debugger frame. @@ -1977,14 +1976,13 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, return JS_TRUE; } - if (clasp == &js_FunctionClass) { + if (tc->flags & TCF_IN_FUNCTION) { /* * We are compiling a function body and may be able to optimize name * to stack slot. Look for an argument or variable in the function and * rewrite pn_op and update pn accordingly. */ - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->varobj)); - localKind = js_LookupLocal(cx, fp->fun, atom, &index); + localKind = js_LookupLocal(cx, tc->fun, atom, &index); if (localKind != JSLOCAL_NONE) { op = PN_OP(pn); if (localKind == JSLOCAL_ARG) { @@ -2021,6 +2019,7 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, pn->pn_slot = index; return JS_TRUE; } + tc->flags |= TCF_FUN_USES_NONLOCALS; } /* @@ -2038,7 +2037,6 @@ BindNameToSlot(JSContext *cx, JSTreeContext *tc, JSParseNode *pn, pn->pn_op = JSOP_ARGUMENTS; return JS_TRUE; } - tc->flags |= TCF_FUN_USES_NONLOCALS; return JS_TRUE; } @@ -3171,39 +3169,6 @@ js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body) js_Emit1(cx, cg, JSOP_STOP) >= 0; } -JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun) -{ - JSStackFrame *fp, frame; - JSObject *funobj; - JSBool ok; - - fp = cx->fp; - funobj = fun->object; - JS_ASSERT(!fp || (fp->fun != fun && fp->varobj != funobj && - fp->scopeChain != funobj)); - memset(&frame, 0, sizeof frame); - frame.callee = funobj; - 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; - ok = js_EmitFunctionBytecode(cx, cg, body); - cx->fp = fp; - if (!ok) - return JS_FALSE; - - if (!js_NewScriptFromCG(cx, cg, fun)) - return JS_FALSE; - - JS_ASSERT(FUN_INTERPRETED(fun)); - return JS_TRUE; -} - /* A macro for inlining at the top of js_EmitTree (whence it came). */ #define UPDATE_LINE_NUMBER_NOTES(cx, cg, pn) \ JS_BEGIN_MACRO \ @@ -3998,10 +3963,13 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) cg->codePool, cg->notePool, pn->pn_pos.begin.lineno); cg2->treeContext.flags = (uint16) (pn->pn_flags | TCF_IN_FUNCTION); - cg2->parent = cg; fun = GET_FUNCTION_PRIVATE(cx, pn->pn_funpob->object); - if (!js_EmitFunctionBody(cx, cg2, pn->pn_body, fun)) + cg2->treeContext.fun = fun; + cg2->parent = cg; + if (!js_EmitFunctionBytecode(cx, cg2, pn->pn_body) || + !js_NewScriptFromCG(cx, cg2)) { return JS_FALSE; + } /* * We need an activation object if an inner peeks out, or if such @@ -4055,14 +4023,9 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) JSFunction *parentFun; JSLocalKind localKind; - if (cg->treeContext.flags & TCF_IN_FUNCTION) { - JS_ASSERT(OBJ_GET_CLASS(cx, OBJ_GET_PARENT(cx, fun->object)) == - &js_FunctionClass); - parentFun = - GET_FUNCTION_PRIVATE(cx, OBJ_GET_PARENT(cx, fun->object)); - } else { - parentFun = cx->fp->fun; - } + parentFun = (cg->treeContext.flags & TCF_IN_FUNCTION) + ? cg->treeContext.fun + : cx->fp->fun; localKind = js_LookupLocal(cx, parentFun, fun->atom, &slot); if (localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST) op = JSOP_DEFLOCALFUN; @@ -5217,9 +5180,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * expression statement as the script's result, despite the fact * that it appears useless to the compiler. */ - useful = wantval = !cx->fp->fun || - !FUN_INTERPRETED(cx->fp->fun) || - (cx->fp->flags & JSFRAME_SPECIAL); + useful = wantval = !(cg->treeContext.flags & TCF_IN_FUNCTION); if (!useful) { if (!CheckSideEffects(cx, &cg->treeContext, pn2, &useful)) return JS_FALSE; @@ -6267,7 +6228,7 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn) * select JSOP_REGEXP. */ JS_ASSERT(pn->pn_op == JSOP_REGEXP); - if (cx->fp->flags & (JSFRAME_EVAL | JSFRAME_COMPILE_N_GO)) { + if (cg->treeContext.flags & TCF_COMPILE_N_GO) { ok = EmitObjectOp(cx, pn->pn_pob, JSOP_OBJECT, cg); } else { ok = EmitIndexOp(cx, JSOP_REGEXP, diff --git a/js/src/jsemit.h b/js/src/jsemit.h index 9efc3df1efb4..8c4fe3fd5676 100644 --- a/js/src/jsemit.h +++ b/js/src/jsemit.h @@ -171,6 +171,8 @@ struct JSTreeContext { /* tree context for semantic checks */ XXX combine with blockChain? */ JSAtomList decls; /* function, const, and var declarations */ JSParseContext *parseContext; + JSFunction *fun; /* function to store argument and variable + names when flags & TCF_IN_FUNCTION */ }; #define TCF_COMPILING 0x01 /* generating bytecode; this tc is a cg */ @@ -187,6 +189,9 @@ struct JSTreeContext { /* tree context for semantic checks */ #define TCF_HAS_DEFXMLNS 0x200 /* default xml namespace = ...; parsed */ #define TCF_HAS_FUNCTION_STMT 0x400 /* block contains a function statement */ #define TCF_GENEXP_LAMBDA 0x800 /* flag lambda from generator expression */ +#define TCF_COMPILE_N_GO 0x1000 /* compiler-and-go mode of script, can + optimize name references based on scope + chain */ #define TREE_CONTEXT_INIT(tc, pc) \ ((tc)->flags = (tc)->ngvars = 0, \ @@ -195,7 +200,8 @@ struct JSTreeContext { /* tree context for semantic checks */ (tc)->blockChain = NULL, \ ATOM_LIST_INIT(&(tc)->decls), \ (tc)->blockNode = NULL, \ - (tc)->parseContext = (pc)) + (tc)->parseContext = (pc), \ + (tc)->fun = NULL) #define TREE_CONTEXT_FINISH(tc) \ ((void)0) @@ -502,14 +508,6 @@ js_EmitTree(JSContext *cx, JSCodeGenerator *cg, JSParseNode *pn); extern JSBool js_EmitFunctionBytecode(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body); -/* - * Emit code into cg for the tree rooted at body, then create a persistent - * script for fun from cg. - */ -extern JSBool -js_EmitFunctionBody(JSContext *cx, JSCodeGenerator *cg, JSParseNode *body, - JSFunction *fun); - /* * Source notes generated along with bytecode for decompiling and debugging. * A source note is a uint8 with 5 bits of type and 3 of offset from the pc of diff --git a/js/src/jsfun.c b/js/src/jsfun.c index 7b656d5ffe62..8eda9f5d36b5 100644 --- a/js/src/jsfun.c +++ b/js/src/jsfun.c @@ -1701,7 +1701,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) const char *filename; JSBool ok; JSString *str, *arg; - JSParseContext pc; + JSTokenStream ts; JSPrincipals *principals; jschar *collected_args, *cp; void *mark; @@ -1839,14 +1839,14 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) } /* Initialize a tokenstream that reads from the given string. */ - if (!js_InitTokenStream(cx, &pc.tokenStream, collected_args, - args_length, NULL, filename, lineno)) { + if (!js_InitTokenStream(cx, &ts, collected_args, args_length, + NULL, filename, lineno)) { JS_ARENA_RELEASE(&cx->tempPool, mark); return JS_FALSE; } /* The argument string may be empty or contain no tokens. */ - tt = js_GetToken(cx, &pc.tokenStream); + tt = js_GetToken(cx, &ts); if (tt != TOK_EOF) { for (;;) { /* @@ -1857,10 +1857,11 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) goto after_args; /* - * Get the atom corresponding to the name from the tokenstream; - * we're assured at this point that it's a valid identifier. + * Get the atom corresponding to the name from the token + * stream; we're assured at this point that it's a valid + * identifier. */ - atom = CURRENT_TOKEN(&pc.tokenStream).t_atom; + atom = CURRENT_TOKEN(&ts).t_atom; /* Check for a duplicate parameter name. */ if (js_LookupLocal(cx, fun, atom, NULL) != JSLOCAL_NONE) { @@ -1868,7 +1869,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) name = js_AtomToPrintableString(cx, atom); ok = name && - js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, + js_ReportCompileErrorNumber(cx, &ts, NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, @@ -1883,18 +1884,18 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) * Get the next token. Stop on end of stream. Otherwise * insist on a comma, get another name, and iterate. */ - tt = js_GetToken(cx, &pc.tokenStream); + tt = js_GetToken(cx, &ts); if (tt == TOK_EOF) break; if (tt != TOK_COMMA) goto after_args; - tt = js_GetToken(cx, &pc.tokenStream); + tt = js_GetToken(cx, &ts); } } state = OK; after_args: - if (state == BAD_FORMAL && !(pc.tokenStream.flags & TSF_ERROR)) { + if (state == BAD_FORMAL && !(ts.flags & TSF_ERROR)) { /* * Report "malformed formal parameter" iff no illegal char or * similar scanner error was already reported. @@ -1902,7 +1903,7 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_FORMAL); } - js_CloseTokenStream(cx, &pc.tokenStream); + js_CloseTokenStream(cx, &ts); JS_ARENA_RELEASE(&cx->tempPool, mark); if (state != OK) return JS_FALSE; @@ -1917,14 +1918,9 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) str = cx->runtime->emptyString; } - ok = js_InitParseContext(cx, &pc, JSSTRING_CHARS(str), JSSTRING_LENGTH(str), - NULL, filename, lineno); - if (ok) { - js_InitCompilePrincipals(cx, &pc, principals); - ok = js_CompileFunctionBody(cx, &pc, fun); - js_FinishParseContext(cx, &pc); - } - return ok; + return js_CompileFunctionBody(cx, fun, principals, + JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + filename, lineno); } JSObject * diff --git a/js/src/jsinterp.h b/js/src/jsinterp.h index 9758dc6d55d3..6bb8346fccb8 100644 --- a/js/src/jsinterp.h +++ b/js/src/jsinterp.h @@ -99,21 +99,19 @@ typedef struct JSInlineFrame { is currently assigning to a property */ #define JSFRAME_DEBUGGER 0x08 /* frame for JS_EvaluateInStackFrame */ #define JSFRAME_EVAL 0x10 /* frame for obj_eval */ -#define JSFRAME_SPECIAL 0x18 /* special evaluation frame flags */ -#define JSFRAME_COMPILING 0x20 /* frame is being used by compiler */ -#define JSFRAME_COMPILE_N_GO 0x40 /* compiler-and-go mode, can optimize name - references based on scope chain */ -#define JSFRAME_SCRIPT_OBJECT 0x80 /* compiling source for a Script object */ -#define JSFRAME_YIELDING 0x100 /* js_Interpret dispatched JSOP_YIELD */ -#define JSFRAME_FILTERING 0x200 /* XML filtering predicate expression */ -#define JSFRAME_ITERATOR 0x400 /* trying to get an iterator for for-in */ -#define JSFRAME_POP_BLOCKS 0x800 /* scope chain contains blocks to pop */ -#define JSFRAME_GENERATOR 0x1000 /* frame belongs to generator-iterator */ -#define JSFRAME_ROOTED_ARGV 0x2000 /* frame.argv is rooted by the caller */ +#define JSFRAME_SCRIPT_OBJECT 0x20 /* compiling source for a Script object */ +#define JSFRAME_YIELDING 0x40 /* js_Interpret dispatched JSOP_YIELD */ +#define JSFRAME_FILTERING 0x80 /* XML filtering predicate expression */ +#define JSFRAME_ITERATOR 0x100 /* trying to get an iterator for for-in */ +#define JSFRAME_POP_BLOCKS 0x200 /* scope chain contains blocks to pop */ +#define JSFRAME_GENERATOR 0x400 /* frame belongs to generator-iterator */ +#define JSFRAME_ROOTED_ARGV 0x800 /* frame.argv is rooted by the caller */ #define JSFRAME_OVERRIDE_SHIFT 24 /* override bit-set params; see jsfun.c */ #define JSFRAME_OVERRIDE_BITS 8 +#define JSFRAME_SPECIAL (JSFRAME_DEBUGGER | JSFRAME_EVAL) + extern JS_FRIEND_API(jsval *) js_AllocStack(JSContext *cx, uintN nslots, void **markp); diff --git a/js/src/jsobj.c b/js/src/jsobj.c index 7b6c321ef733..1c3a5d4208ea 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -57,6 +57,7 @@ #include "jsbool.h" #include "jscntxt.h" #include "jsconfig.h" +#include "jsemit.h" #include "jsfun.h" #include "jsgc.h" #include "jsinterp.h" @@ -64,7 +65,7 @@ #include "jsnum.h" #include "jsobj.h" #include "jsopcode.h" -#include "jsscan.h" +#include "jsparse.h" #include "jsscope.h" #include "jsscript.h" #include "jsstr.h" @@ -1431,10 +1432,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) fp->flags |= JSFRAME_EVAL; } while ((fp = fp->down) != caller); - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); + script = js_CompileScript(cx, scopeobj, principals, TCF_COMPILE_N_GO, + JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + NULL, file, line); if (!script) { ok = JS_FALSE; goto out; diff --git a/js/src/jsparse.c b/js/src/jsparse.c index d54aed4a298a..ed2aaa0db71a 100644 --- a/js/src/jsparse.c +++ b/js/src/jsparse.c @@ -156,8 +156,8 @@ static uint32 maxparsenodes = 0; static uint32 recyclednodes = 0; #endif -JS_FRIEND_API(JSBool) -js_InitParseContext(JSContext *cx, JSParseContext *pc, +JSBool +js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals, const jschar *base, size_t length, FILE *fp, const char *filename, uintN lineno) { @@ -166,7 +166,9 @@ js_InitParseContext(JSContext *cx, JSParseContext *pc, JS_ARENA_RELEASE(&cx->tempPool, pc->tempPoolMark); return JS_FALSE; } - pc->principals = NULL; + if (principals) + JSPRINCIPALS_HOLD(cx, principals); + pc->principals = principals; pc->nodeList = NULL; pc->traceListHead = NULL; @@ -176,7 +178,7 @@ js_InitParseContext(JSContext *cx, JSParseContext *pc, return JS_TRUE; } -JS_FRIEND_API(void) +void js_FinishParseContext(JSContext *cx, JSParseContext *pc) { if (pc->principals) @@ -492,8 +494,7 @@ MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, * 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); + newfp->flags = oldfp->flags & (JSFRAME_SPECIAL | JSFRAME_SCRIPT_OBJECT); while (oldfp->flags & JSFRAME_SPECIAL) { oldfp = oldfp->down; if (!oldfp) @@ -512,7 +513,7 @@ MaybeSetupFrame(JSContext *cx, JSObject *chain, JSStackFrame *oldfp, /* * Parse a top-level JS script. */ -JS_FRIEND_API(JSParseNode *) +JSParseNode * js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc) { JSStackFrame *fp, frame; @@ -557,11 +558,13 @@ js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc) /* * Compile a top-level script. */ -JS_FRIEND_API(JSScript *) -js_CompileScript(JSContext *cx, JSObject *chain, JSParseContext *pc) +JSScript * +js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals, + uint32 tcflags, const jschar *chars, size_t length, + FILE *file, const char *filename, uintN lineno) { + JSParseContext pc; JSStackFrame *fp, frame; - uint32 flags; JSArenaPool codePool, notePool; JSCodeGenerator cg; JSParseNode *pn; @@ -570,33 +573,39 @@ js_CompileScript(JSContext *cx, JSObject *chain, JSParseContext *pc) void *sbrk(ptrdiff_t), *before = sbrk(0); #endif + JS_ASSERT(!(tcflags & ~TCF_COMPILE_N_GO)); + + if (!js_InitParseContext(cx, &pc, principals, chars, length, file, + filename, lineno)) { + return NULL; + } + /* + * From this point the control must flow through the label out. + * * 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); + MaybeSetupFrame(cx, obj, fp, &frame); JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), &cx->scriptStackQuota); JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), &cx->scriptStackQuota); - js_InitCodeGenerator(cx, &cg, pc, &codePool, ¬ePool, TS(pc)->lineno); + js_InitCodeGenerator(cx, &cg, &pc, &codePool, ¬ePool, + pc.tokenStream.lineno); /* From this point the control must flow via the label out. */ - pn = Statements(cx, TS(pc), &cg.treeContext); + cg.treeContext.flags |= tcflags; + pn = Statements(cx, &pc.tokenStream, &cg.treeContext); if (!pn) { script = NULL; goto out; } - if (!js_MatchToken(cx, TS(pc), TOK_EOF)) { - js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR, + if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { + js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); script = NULL; goto out; @@ -632,15 +641,14 @@ js_CompileScript(JSContext *cx, JSObject *chain, JSParseContext *pc) #ifdef JS_ARENAMETER JS_DumpArenaStats(stdout); #endif - script = js_NewScriptFromCG(cx, &cg, NULL); + script = js_NewScriptFromCG(cx, &cg); out: js_FinishCodeGenerator(cx, &cg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); - - cx->fp->flags = flags; cx->fp = fp; + js_FinishParseContext(cx, &pc); return script; } @@ -772,61 +780,44 @@ HasFinalReturn(JSParseNode *pn) } static JSBool -ReportBadReturn(JSContext *cx, JSTokenStream *ts, uintN flags, uintN errnum, +ReportBadReturn(JSContext *cx, JSTreeContext *tc, uintN flags, uintN errnum, uintN anonerrnum) { - JSFunction *fun; const char *name; - fun = cx->fp->fun; - if (fun->atom) { - name = js_AtomToPrintableString(cx, fun->atom); + JS_ASSERT(tc->flags & TCF_IN_FUNCTION); + if (tc->fun->atom) { + name = js_AtomToPrintableString(cx, tc->fun->atom); } else { errnum = anonerrnum; name = NULL; } - return js_ReportCompileErrorNumber(cx, ts, NULL, flags, errnum, name); + return js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, flags, + errnum, name); } static JSBool -CheckFinalReturn(JSContext *cx, JSTokenStream *ts, JSParseNode *pn) +CheckFinalReturn(JSContext *cx, JSTreeContext *tc, JSParseNode *pn) { + JS_ASSERT(tc->flags & TCF_IN_FUNCTION); return HasFinalReturn(pn) == ENDS_IN_RETURN || - ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, + ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE); } static JSParseNode * -FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, - JSTreeContext *tc) +FunctionBody(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) { - JSStackFrame *fp, frame; - JSObject *funobj; JSStmtInfo stmtInfo; uintN oldflags, firstLine; JSParseNode *pn; - JS_ASSERT(FUN_INTERPRETED(fun)); - fp = cx->fp; - funobj = fun->object; - if (!fp || fp->fun != fun || fp->varobj != funobj || - fp->scopeChain != funobj) { - memset(&frame, 0, sizeof frame); - frame.callee = funobj; - frame.fun = fun; - frame.varobj = frame.scopeChain = funobj; - frame.down = fp; - if (fp) - frame.flags = fp->flags & JSFRAME_COMPILE_N_GO; - cx->fp = &frame; - } - + JS_ASSERT(tc->flags & TCF_IN_FUNCTION); 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 @@ -845,7 +836,7 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, pn = NULL; } else { if (tc->flags & TCF_FUN_IS_GENERATOR) { - ReportBadReturn(cx, ts, JSREPORT_ERROR, + ReportBadReturn(cx, tc, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN); pn = NULL; @@ -865,28 +856,13 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, /* 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)) + if (!CheckFinalReturn(cx, tc, 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) { + 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; } @@ -896,34 +872,29 @@ FunctionBody(JSContext *cx, JSTokenStream *ts, JSFunction *fun, * handler attribute in an HTML tag. */ JSBool -js_CompileFunctionBody(JSContext *cx, JSParseContext *pc, JSFunction *fun) +js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno) { + JSParseContext pc; JSArenaPool codePool, notePool; JSCodeGenerator funcg; - JSStackFrame *fp, frame; - JSObject *funobj; JSParseNode *pn; + if (!js_InitParseContext(cx, &pc, principals, chars, length, NULL, + filename, lineno)) { + return JS_FALSE; + } + + /* No early return from this point until js_FinishParseContext call. */ JS_INIT_ARENA_POOL(&codePool, "code", 1024, sizeof(jsbytecode), &cx->scriptStackQuota); JS_INIT_ARENA_POOL(¬ePool, "note", 1024, sizeof(jssrcnote), &cx->scriptStackQuota); - js_InitCodeGenerator(cx, &funcg, pc, &codePool, ¬ePool, TS(pc)->lineno); - - /* 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.callee = funobj; - 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; + js_InitCodeGenerator(cx, &funcg, &pc, &codePool, ¬ePool, + pc.tokenStream.lineno); + funcg.treeContext.flags |= TCF_IN_FUNCTION; + funcg.treeContext.fun = fun; /* * Farble the body so that it looks like a block statement to js_EmitTree, @@ -936,24 +907,27 @@ js_CompileFunctionBody(JSContext *cx, JSParseContext *pc, JSFunction *fun) * Therefore we must fold constants, allocate try notes, and generate code * for this function, including a stop opcode at the end. */ - CURRENT_TOKEN(TS(pc)).type = TOK_LC; - pn = FunctionBody(cx, TS(pc), fun, &funcg.treeContext); + CURRENT_TOKEN(&pc.tokenStream).type = TOK_LC; + pn = FunctionBody(cx, &pc.tokenStream, &funcg.treeContext); if (pn) { - if (!js_MatchToken(cx, TS(pc), TOK_EOF)) { - js_ReportCompileErrorNumber(cx, TS(pc), NULL, JSREPORT_ERROR, - JSMSG_SYNTAX_ERROR); + if (!js_MatchToken(cx, &pc.tokenStream, TOK_EOF)) { + js_ReportCompileErrorNumber(cx, &pc.tokenStream, NULL, + JSREPORT_ERROR, JSMSG_SYNTAX_ERROR); pn = NULL; } else { - if (!js_NewScriptFromCG(cx, &funcg, fun)) + if (!js_FoldConstants(cx, pn, &funcg.treeContext) || + !js_EmitFunctionBytecode(cx, &funcg, pn) || + !js_NewScriptFromCG(cx, &funcg)) { pn = NULL; + } } } /* Restore saved state and release code generation arenas. */ - cx->fp = fp; js_FinishCodeGenerator(cx, &funcg); JS_FinishArenaPool(&codePool); JS_FinishArenaPool(¬ePool); + js_FinishParseContext(cx, &pc); return pn != NULL; } @@ -971,18 +945,9 @@ typedef JSBool struct BindData { JSParseNode *pn; /* error source coordinate */ - JSObject *obj; /* the variable object */ JSOp op; /* prolog bytecode or nop */ Binder binder; /* binder, discriminates u */ union { - struct { - JSFunction *fun; /* must come first! see next */ - } arg; - struct { - JSFunction *fun; /* this overlays u.arg.fun */ - JSClass *clasp; - JSLocalKind kind; - } var; struct { jsuint index; uintN overflow; @@ -991,17 +956,18 @@ struct BindData { }; static JSBool -BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) +BindArg(JSContext *cx, JSAtom *atom, JSTreeContext *tc) { const char *name; /* * Check for a duplicate parameter name, a "feature" required by ECMA-262. */ - if (js_LookupLocal(cx, data->u.arg.fun, atom, NULL) != JSLOCAL_NONE) { + JS_ASSERT(tc->flags & TCF_IN_FUNCTION); + if (js_LookupLocal(cx, tc->fun, atom, NULL) != JSLOCAL_NONE) { name = js_AtomToPrintableString(cx, atom); if (!name || - !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, + !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), NULL, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_DUPLICATE_FORMAL, name)) { @@ -1009,19 +975,14 @@ BindArg(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) } } - return js_AddLocal(cx, data->u.arg.fun, atom, JSLOCAL_ARG); + return js_AddLocal(cx, tc->fun, atom, JSLOCAL_ARG); } static JSBool -BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) +BindLocalVariable(JSContext *cx, JSFunction *fun, JSAtom *atom, + JSLocalKind localKind) { - /* - * Can't increase fun->nvars in an active frame when kind is JSFL_NONE. - */ - if (data->u.var.kind == JSLOCAL_NONE) - return JS_TRUE; - JS_ASSERT(data->u.var.kind == JSLOCAL_VAR || - data->u.var.kind == JSLOCAL_CONST); + JS_ASSERT(localKind == JSLOCAL_VAR || localKind == JSLOCAL_CONST); /* * Don't bind a variable with the hidden name 'arguments', per ECMA-262. @@ -1032,7 +993,7 @@ BindLocalVariable(JSContext *cx, BindData *data, JSAtom *atom) if (atom == cx->runtime->atomState.argumentsAtom) return JS_TRUE; - return js_AddLocal(cx, data->u.var.fun, atom, data->u.var.kind); + return js_AddLocal(cx, fun, atom, localKind); } #if JS_HAS_DESTRUCTURING @@ -1050,6 +1011,7 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, JSAtomListElement *ale; const char *name; + JS_ASSERT(tc->flags & TCF_IN_FUNCTION); ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (!ale) { ale = js_IndexAtom(cx, atom, &tc->decls); @@ -1058,7 +1020,7 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, ALE_SET_JSOP(ale, data->op); } - if (js_LookupLocal(cx, data->u.var.fun, atom, NULL) != JSLOCAL_NONE) { + if (js_LookupLocal(cx, tc->fun, atom, NULL) != JSLOCAL_NONE) { name = js_AtomToPrintableString(cx, atom); if (!name || !js_ReportCompileErrorNumber(cx, TS(tc->parseContext), data->pn, @@ -1068,13 +1030,25 @@ BindDestructuringArg(JSContext *cx, BindData *data, JSAtom *atom, return JS_FALSE; } } else { - if (!BindLocalVariable(cx, data, atom)) + if (!BindLocalVariable(cx, tc->fun, atom, JSLOCAL_VAR)) return JS_FALSE; } return JS_TRUE; } #endif /* JS_HAS_DESTRUCTURING */ +static JSFunction * +NewCompilerFunction(JSContext *cx, JSTreeContext *tc, JSAtom *atom, + JSBool lambda) +{ + JSObject *parent; + + parent = (tc->flags & TCF_IN_FUNCTION) ? tc->fun->object : cx->fp->varobj; + return js_NewFunction(cx, NULL, NULL, 0, + JSFUN_INTERPRETED | (lambda ? JSFUN_LAMBDA : 0), + parent, atom); +} + static JSParseNode * FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSBool lambda) @@ -1084,8 +1058,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, JSTokenType tt; JSAtom *funAtom; JSParsedObjectBox *funpob; - JSStackFrame *fp; - JSObject *varobj; JSAtomListElement *ale; JSFunction *fun; JSTreeContext funtc; @@ -1117,10 +1089,6 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, js_UngetToken(ts); } - /* Find the nearest variable-declaring scope and use it as our parent. */ - fp = cx->fp; - varobj = fp->varobj; - /* * Record names for function statements in tc->decls so we know when to * avoid optimizing variable references that might name a function. @@ -1175,19 +1143,15 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * variable even if the parameter with the given name already * exists. */ - JS_ASSERT(OBJ_GET_CLASS(cx, varobj) == &js_FunctionClass); - JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, varobj)); - localKind = js_LookupLocal(cx, fp->fun, funAtom, NULL); + localKind = js_LookupLocal(cx, tc->fun, funAtom, NULL); if (localKind == JSLOCAL_NONE || localKind == JSLOCAL_ARG) { - if (!js_AddLocal(cx, fp->fun, funAtom, JSLOCAL_VAR)) + if (!js_AddLocal(cx, tc->fun, funAtom, JSLOCAL_VAR)) return NULL; } } } - fun = js_NewFunction(cx, NULL, NULL, 0, - JSFUN_INTERPRETED | (lambda ? JSFUN_LAMBDA : 0), - varobj, funAtom); + fun = NewCompilerFunction(cx, tc, funAtom, lambda); if (!fun) return NULL; @@ -1206,18 +1170,12 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, /* Initialize early for possible flags mutation via DestructuringExpr. */ TREE_CONTEXT_INIT(&funtc, tc->parseContext); + funtc.flags |= TCF_IN_FUNCTION; + funtc.fun = fun; /* 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)) { - BindData data; - - data.pn = NULL; - data.obj = fun->object; - data.op = JSOP_NOP; - data.binder = BindArg; - data.u.arg.fun = fun; - do { tt = js_GetToken(cx, ts); switch (tt) { @@ -1225,6 +1183,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, case TOK_LB: case TOK_LC: { + BindData data; JSParseNode *lhs, *rhs; jsint slot; @@ -1234,20 +1193,13 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * anonymous positional parameter, so here we must tweak our * binder and its data. */ + data.pn = NULL; data.op = JSOP_DEFVAR; data.binder = BindDestructuringArg; - data.u.var.clasp = &js_FunctionClass; - data.u.var.kind = JSLOCAL_VAR; lhs = DestructuringExpr(cx, &data, &funtc, tt); if (!lhs) return NULL; - /* - * Restore the formal parameter binder in case there are more - * non-destructuring formals in the parameter list. - */ - data.binder = BindArg; - /* * Adjust fun->nargs to count the single anonymous positional * parameter that is to be destructured. @@ -1285,7 +1237,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #endif /* JS_HAS_DESTRUCTURING */ case TOK_NAME: - if (!data.binder(cx, &data, CURRENT_TOKEN(ts).t_atom, tc)) + if (!BindArg(cx, CURRENT_TOKEN(ts).t_atom, &funtc)) return NULL; break; @@ -1312,7 +1264,7 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, #endif pn->pn_pos.begin = CURRENT_TOKEN(ts).pos.begin; - body = FunctionBody(cx, ts, fun, &funtc); + body = FunctionBody(cx, ts, &funtc); if (!body) return NULL; @@ -1381,8 +1333,8 @@ FunctionDef(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * * The TCF_FUN_USES_NONLOCALS flag is set only by the code generator, * so it won't be set here. Assert that it's not. We have to check - * it later, in js_EmitTree, after js_EmitFunctionBody has traversed - * the function's body + * it later, in js_EmitTree, after js_EmitFunctionBytecode has + * traversed the function's body */ JS_ASSERT(!(funtc.flags & TCF_FUN_USES_NONLOCALS)); if (!lambda && funAtom && !AT_TOP_LEVEL(tc)) @@ -1479,16 +1431,14 @@ Statements(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) /* 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)) { + if (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)) { + !CheckFinalReturn(cx, tc, pn2)) { tt = TOK_ERROR; break; } @@ -1658,7 +1608,7 @@ BindLet(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) JSScopeProperty *sprop; JSAtomListElement *ale; - blockObj = data->obj; + blockObj = tc->blockChain; sprop = SCOPE_GET_PROPERTY(OBJ_SCOPE(blockObj), ATOM_TO_JSID(atom)); ATOM_LIST_SEARCH(ale, &tc->decls, atom); if (sprop || (ale && ALE_JSOP(ale) == JSOP_DEFCONST)) { @@ -1703,8 +1653,6 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) JSAtomListElement *ale; JSOp op, prevop; const char *name; - JSFunction *fun; - JSObject *obj; JSLocalKind localKind; stmt = js_LexicalLookup(tc, atom, NULL, 0); @@ -1744,9 +1692,7 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) } ALE_SET_JSOP(ale, op); - fun = data->u.var.fun; - obj = data->obj; - if (!fun || OBJ_GET_CLASS(cx, obj) != &js_FunctionClass) { + if (!(tc->flags & TCF_IN_FUNCTION)) { /* * Don't lookup global variables or variables in an active frame at * compile time. @@ -1754,19 +1700,19 @@ BindVarOrConst(JSContext *cx, BindData *data, JSAtom *atom, JSTreeContext *tc) return JS_TRUE; } - localKind = js_LookupLocal(cx, fun, atom, NULL); + localKind = js_LookupLocal(cx, tc->fun, atom, NULL); if (localKind == JSLOCAL_NONE) { /* * Property not found in current variable scope: we have not seen this * variable before. Define a new local variable by adding a property * to the function's scope, allocating one slot in the function's vars - * frame. Global variables and any locals declared in with statement - * bodies are handled at runtime, by script prolog JSOP_DEFVAR opcodes - * generated for slot-less vars. + * frame. Any locals declared in with statement bodies are handled at + * runtime, by script prolog JSOP_DEFVAR opcodes generated for + * slot-less vars. */ - if (cx->fp->scopeChain == obj && - !js_InWithStatement(tc) && - !BindLocalVariable(cx, data, atom)) { + localKind = (data->op == JSOP_DEFCONST) ? JSLOCAL_CONST : JSLOCAL_VAR; + if (!js_InWithStatement(tc) && + !BindLocalVariable(cx, tc->fun, atom, localKind)) { return JS_FALSE; } } else if (localKind == JSLOCAL_ARG) { @@ -1821,10 +1767,13 @@ BindDestructuringVar(JSContext *cx, BindData *data, JSParseNode *pn, * point, we can't select the optimal final opcode, yet we must preserve * the CONST bit and convey "set", not "get". */ - pn->pn_op = (data->op == JSOP_DEFCONST) - ? JSOP_SETCONST - : JSOP_SETNAME; - pn->pn_const = (data->u.var.kind == JSLOCAL_CONST); + if (data->op == JSOP_DEFCONST) { + pn->pn_op = JSOP_SETCONST; + pn->pn_const = JS_TRUE; + } else { + pn->pn_op = JSOP_SETNAME; + pn->pn_const = JS_FALSE; + } return JS_TRUE; } @@ -2284,7 +2233,7 @@ ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if ((~tc->flags & (TCF_RETURN_EXPR | TCF_FUN_IS_GENERATOR)) == 0) { /* As in Python (see PEP-255), disallow return v; in generators. */ - ReportBadReturn(cx, ts, JSREPORT_ERROR, + ReportBadReturn(cx, tc, JSREPORT_ERROR, JSMSG_BAD_GENERATOR_RETURN, JSMSG_BAD_ANON_GENERATOR_RETURN); return NULL; @@ -2292,7 +2241,7 @@ ReturnOrYield(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (JS_HAS_STRICT_OPTION(cx) && (~tc->flags & (TCF_RETURN_EXPR | TCF_RETURN_VOID)) == 0 && - !ReportBadReturn(cx, ts, JSREPORT_WARNING | JSREPORT_STRICT, + !ReportBadReturn(cx, tc, JSREPORT_WARNING | JSREPORT_STRICT, JSMSG_NO_RETURN_VALUE, JSMSG_ANON_NO_RETURN_VALUE)) { return NULL; @@ -2997,7 +2946,6 @@ Statement(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) * an intentional change that anticipates ECMA Ed. 4. */ data.pn = NULL; - data.obj = tc->blockChain; data.op = JSOP_NOP; data.binder = BindLet; data.u.let.index = 0; @@ -3531,7 +3479,6 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) data.pn = NULL; data.op = let ? JSOP_NOP : CURRENT_TOKEN(ts).t_op; - data.binder = let ? BindLet : BindVarOrConst; pn = NewParseNode(cx, ts, PN_LIST, tc); if (!pn) return NULL; @@ -3550,25 +3497,11 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) fp = cx->fp; if (let) { JS_ASSERT(tc->blockChain == scopeStmt->u.blockObj); - data.obj = tc->blockChain; - data.u.let.index = OBJ_BLOCK_COUNT(cx, data.obj); + data.binder = BindLet; + data.u.let.index = OBJ_BLOCK_COUNT(cx, tc->blockChain); data.u.let.overflow = JSMSG_TOO_MANY_FUN_VARS; } else { - data.obj = fp->varobj; - data.u.var.fun = fp->fun; - data.u.var.clasp = OBJ_GET_CLASS(cx, data.obj); - if (data.u.var.fun && data.u.var.clasp == &js_FunctionClass) { - /* We are compiling code inside a function */ - data.u.var.kind = (data.op == JSOP_DEFCONST) - ? JSLOCAL_CONST - : JSLOCAL_VAR; - } else { - /* - * We are compiling global code or code from an eval inside a - * function - */ - data.u.var.kind = JSLOCAL_NONE; - } + data.binder = BindVarOrConst; } do { @@ -3621,7 +3554,7 @@ Variables(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc) pn2->pn_atom = atom; pn2->pn_slot = -1; if (!let) - pn2->pn_const = (data.u.var.kind == JSLOCAL_CONST); + pn2->pn_const = (data.op == JSOP_DEFCONST); PN_APPEND(pn, pn2); if (js_MatchToken(cx, ts, TOK_ASSIGN)) { @@ -4170,7 +4103,6 @@ ComprehensionTail(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, pnp = &pn->pn_expr; data.pn = NULL; - data.obj = tc->blockChain; data.op = JSOP_NOP; data.binder = BindLet; data.u.let.index = 0; @@ -4327,8 +4259,7 @@ GeneratorExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, * Make the generator function and flag it as interpreted ASAP (see the * comment in FunctionBody). */ - fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_LAMBDA | JSFUN_INTERPRETED, - cx->fp->varobj, NULL); + fun = NewCompilerFunction(cx, tc, NULL, JS_TRUE); if (!fun) return NULL; @@ -5754,7 +5685,7 @@ PrimaryExpr(JSContext *cx, JSTokenStream *ts, JSTreeContext *tc, if (pn->pn_atom == cx->runtime->atomState.parentAtom || pn->pn_atom == cx->runtime->atomState.protoAtom) { tc->flags |= TCF_FUN_HEAVYWEIGHT; - } else { + } else if (!(tc->flags & TCF_IN_FUNCTION)) { JSAtomListElement *ale; JSStackFrame *fp; JSBool loopy; diff --git a/js/src/jsparse.h b/js/src/jsparse.h index f140ce6960de..4aea3b6ea6e7 100644 --- a/js/src/jsparse.h +++ b/js/src/jsparse.h @@ -451,14 +451,18 @@ struct JSParseContext { /* * Parse a top-level JS script. */ -extern JS_FRIEND_API(JSParseNode *) +extern JSParseNode * js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc); -extern JS_FRIEND_API(JSScript *) -js_CompileScript(JSContext *cx, JSObject *chain, JSParseContext *pc); +extern JSScript * +js_CompileScript(JSContext *cx, JSObject *obj, JSPrincipals *principals, + uint32 tcflags, const jschar *chars, size_t length, + FILE *file, const char *filename, uintN lineno); extern JSBool -js_CompileFunctionBody(JSContext *cx, JSParseContext *pc, JSFunction *fun); +js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals, + const jschar *chars, size_t length, + const char *filename, uintN lineno); extern JSBool js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc); @@ -477,12 +481,12 @@ js_ParseXMLText(JSContext *cx, JSObject *chain, JSParseContext *pc, * current JSContext.tempPool mark. This means you cannot allocate from * tempPool and save the pointer beyond the next js_FinishParseContext. */ -extern JS_FRIEND_API(JSBool) -js_InitParseContext(JSContext *cx, JSParseContext *pc, +extern JSBool +js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals, const jschar *base, size_t length, FILE *fp, const char *filename, uintN lineno); -extern JS_FRIEND_API(void) +extern void js_FinishParseContext(JSContext *cx, JSParseContext *pc); extern void diff --git a/js/src/jsscript.c b/js/src/jsscript.c index 55b5172d45fd..acf0af14db2a 100644 --- a/js/src/jsscript.c +++ b/js/src/jsscript.c @@ -198,6 +198,7 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, const char *file; uintN line; JSPrincipals *principals; + uint32 tcflags; jsint execDepth; /* Make sure obj is a Script object. */ @@ -250,17 +251,17 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, /* * Compile the new script using the caller's scope chain, a la eval(). - * Unlike jsobj.c:obj_eval, however, we do not set JSFRAME_EVAL in fp's - * flags, because compilation is here separated from execution, and the + * Unlike jsobj.c:obj_eval, however, we do not pass TCF_COMPILE_N_GO in + * tcflags, because compilation is here separated from execution, and the * run-time scope chain may not match the compile-time. JSFRAME_EVAL is * tested in jsemit.c and jsscan.c to optimize based on identity of run- * and compile-time scope. */ fp->flags |= JSFRAME_SCRIPT_OBJECT; - script = JS_CompileUCScriptForPrincipals(cx, scopeobj, principals, - JSSTRING_CHARS(str), - JSSTRING_LENGTH(str), - file, line); + tcflags = 0; + script = js_CompileScript(cx, scopeobj, principals, tcflags, + JSSTRING_CHARS(str), JSSTRING_LENGTH(str), + NULL, file, line); if (!script) return JS_FALSE; @@ -1387,12 +1388,13 @@ js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, return script; } -JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) +JSScript * +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg) { uint32 mainLength, prologLength, nsrcnotes; JSScript *script; const char *filename; + JSFunction *fun; /* The counts of indexed things must be checked during code generation. */ JS_ASSERT(cg->atomList.count <= INDEX_LIMIT); @@ -1441,7 +1443,9 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun) * We initialize fun->u.script to be the script constructed above * so that the debugger has a valid FUN_SCRIPT(fun). */ - if (fun) { + fun = NULL; + if (cg->treeContext.flags & TCF_IN_FUNCTION) { + fun = cg->treeContext.fun; JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); js_FreezeLocalNames(cx, fun); fun->u.i.script = script; diff --git a/js/src/jsscript.h b/js/src/jsscript.h index f928afd73921..41c6902f4051 100644 --- a/js/src/jsscript.h +++ b/js/src/jsscript.h @@ -212,8 +212,8 @@ extern JSScript * js_NewScript(JSContext *cx, uint32 length, uint32 nsrcnotes, uint32 natoms, uint32 nobjects, uint32 nregexps, uint32 ntrynotes); -extern JS_FRIEND_API(JSScript *) -js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg, JSFunction *fun); +extern JSScript * +js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg); /* * New-script-hook calling is factored from js_NewScriptFromCG so that it diff --git a/js/src/jsxml.c b/js/src/jsxml.c index 349b77c19efe..8f446fc8a0cd 100644 --- a/js/src/jsxml.c +++ b/js/src/jsxml.c @@ -2018,7 +2018,8 @@ ParseXMLSource(JSContext *cx, JSString *src) } } - if (!js_InitParseContext(cx, &pc, chars, length, NULL, filename, lineno)) + if (!js_InitParseContext(cx, &pc, NULL, chars, length, NULL, + filename, lineno)) goto out; pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE); if (pn && XMLArrayInit(cx, &nsarray, 1)) {