Bug 403878: No compiler pseudo-frames when compiling functions. r,a=brendan

This commit is contained in:
igor@mir2.org 2007-11-29 06:49:42 -08:00
parent 2dce87330b
commit 22c2a33a75
12 changed files with 275 additions and 401 deletions

View File

@ -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);

View File

@ -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;

View File

@ -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,

View File

@ -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

View File

@ -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 *

View File

@ -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);

View File

@ -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;

View File

@ -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(&notePool, "note", 1024, sizeof(jssrcnote),
&cx->scriptStackQuota);
js_InitCodeGenerator(cx, &cg, pc, &codePool, &notePool, TS(pc)->lineno);
js_InitCodeGenerator(cx, &cg, &pc, &codePool, &notePool,
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(&notePool);
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 <INPUT> 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(&notePool, "note", 1024, sizeof(jssrcnote),
&cx->scriptStackQuota);
js_InitCodeGenerator(cx, &funcg, pc, &codePool, &notePool, 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, &notePool,
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(&notePool);
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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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)) {