Backed out changeset 972c44aa9d1f (bug 452598).

This commit is contained in:
Andreas Gal
2009-04-04 10:14:52 -07:00
parent 4af509178e
commit 00a608f948
49 changed files with 2483 additions and 6113 deletions

View File

@@ -88,4 +88,3 @@ BUILTIN2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32,
BUILTIN2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1) BUILTIN2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1)
BUILTIN2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1) BUILTIN2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
BUILTIN1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0) BUILTIN1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0)
BUILTIN4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0)

View File

@@ -533,7 +533,7 @@ static struct {
{ {
/* 0*/ JSOP_SWAP, /* 0*/ JSOP_SWAP,
/* 1*/ JSOP_POP, /* 1*/ JSOP_POP,
/* 2*/ JSOP_NULL, /* 2*/ JSOP_NULLTHIS,
/* 3*/ JSOP_CALL, 0, 0, /* 3*/ JSOP_CALL, 0, 0,
/* 6*/ JSOP_STOP, /* 6*/ JSOP_STOP,
}, },
@@ -815,7 +815,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_LOOKUPSWITCH */ 0, /* JSOP_LOOKUPSWITCH */
0, /* JSOP_STRICTEQ */ 0, /* JSOP_STRICTEQ */
0, /* JSOP_STRICTNE */ 0, /* JSOP_STRICTNE */
0, /* JSOP_SETCALL */ 0, /* JSOP_NULLTHIS */
3, /* JSOP_ITER */ 3, /* JSOP_ITER */
2, /* JSOP_NEXTITER */ 2, /* JSOP_NEXTITER */
0, /* JSOP_ENDITER */ 0, /* JSOP_ENDITER */
@@ -869,14 +869,14 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_DEFFUN */ 0, /* JSOP_DEFFUN */
0, /* JSOP_DEFCONST */ 0, /* JSOP_DEFCONST */
0, /* JSOP_DEFVAR */ 0, /* JSOP_DEFVAR */
0, /* JSOP_LAMBDA */ 0, /* JSOP_ANONFUNOBJ */
0, /* JSOP_CALLEE */ 0, /* JSOP_NAMEDFUNOBJ */
0, /* JSOP_SETLOCALPOP */ 0, /* JSOP_SETLOCALPOP */
0, /* JSOP_PICK */ 0, /* JSOP_IFPRIMTOP */
0, /* JSOP_SETCALL */
0, /* JSOP_TRY */ 0, /* JSOP_TRY */
0, /* JSOP_FINALLY */ 0, /* JSOP_FINALLY */
0, /* JSOP_GETDSLOT */ 0, /* JSOP_OBJTOP */
0, /* JSOP_CALLDSLOT */
0, /* JSOP_ARGSUB */ 0, /* JSOP_ARGSUB */
0, /* JSOP_ARGCNT */ 0, /* JSOP_ARGCNT */
0, /* JSOP_DEFLOCALFUN */ 0, /* JSOP_DEFLOCALFUN */
@@ -926,8 +926,8 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_XMLCOMMENT */ 0, /* JSOP_XMLCOMMENT */
0, /* JSOP_XMLPI */ 0, /* JSOP_XMLPI */
0, /* JSOP_CALLPROP */ 0, /* JSOP_CALLPROP */
0, /* JSOP_GETFUNNS */
0, /* JSOP_GETUPVAR */ 0, /* JSOP_GETUPVAR */
0, /* JSOP_CALLUPVAR */
0, /* JSOP_DELDESC */ 0, /* JSOP_DELDESC */
0, /* JSOP_UINT24 */ 0, /* JSOP_UINT24 */
0, /* JSOP_INDEXBASE */ 0, /* JSOP_INDEXBASE */
@@ -942,17 +942,25 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_TYPEOFEXPR */ 0, /* JSOP_TYPEOFEXPR */
0, /* JSOP_ENTERBLOCK */ 0, /* JSOP_ENTERBLOCK */
0, /* JSOP_LEAVEBLOCK */ 0, /* JSOP_LEAVEBLOCK */
0, /* JSOP_IFPRIMTOP */ 0, /* JSOP_PICK */
0, /* JSOP_PRIMTOP */ 0, /* JSOP_PRIMTOP */
0, /* JSOP_UNUSED203 */
0, /* JSOP_UNUSED204 */
0, /* JSOP_UNUSED205 */
0, /* JSOP_UNUSED206 */
0, /* JSOP_UNUSED207 */
0, /* JSOP_UNUSED208 */
0, /* JSOP_UNUSED209 */
0, /* JSOP_GENERATOR */ 0, /* JSOP_GENERATOR */
0, /* JSOP_YIELD */ 0, /* JSOP_YIELD */
0, /* JSOP_ARRAYPUSH */ 0, /* JSOP_ARRAYPUSH */
0, /* JSOP_GETFUNNS */ 0, /* JSOP_CALLUPVAR */
0, /* JSOP_ENUMCONSTELEM */ 0, /* JSOP_ENUMCONSTELEM */
0, /* JSOP_LEAVEBLOCKEXPR */ 0, /* JSOP_LEAVEBLOCKEXPR */
0, /* JSOP_GETTHISPROP */ 0, /* JSOP_GETTHISPROP */
0, /* JSOP_GETARGPROP */ 0, /* JSOP_GETARGPROP */
0, /* JSOP_GETLOCALPROP */ 0, /* JSOP_GETLOCALPROP */
0, /* JSOP_UNUSED219 */
0, /* JSOP_INDEXBASE1 */ 0, /* JSOP_INDEXBASE1 */
0, /* JSOP_INDEXBASE2 */ 0, /* JSOP_INDEXBASE2 */
0, /* JSOP_INDEXBASE3 */ 0, /* JSOP_INDEXBASE3 */
@@ -965,10 +973,6 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
0, /* JSOP_LENGTH */ 0, /* JSOP_LENGTH */
0, /* JSOP_NEWARRAY */ 0, /* JSOP_NEWARRAY */
0, /* JSOP_HOLE */ 0, /* JSOP_HOLE */
0, /* JSOP_DEFFUN_FC */
0, /* JSOP_DEFLOCALFUN_FC */
0, /* JSOP_LAMBDA_FC */
0, /* JSOP_OBJTOP */
0, /* JSOP_LOOP */ 0, /* JSOP_LOOP */
}; };
#define JSOP_IS_IMACOP(x) (0 \ #define JSOP_IS_IMACOP(x) (0 \

View File

@@ -84,7 +84,7 @@
stop stop
.end .end
.end equality .end
.igroup binary JSOP_BITOR-JSOP_MOD .igroup binary JSOP_BITOR-JSOP_MOD
@@ -175,7 +175,7 @@
stop stop
.end .end
.end binary .end
.igroup add JSOP_ADD .igroup add JSOP_ADD
@@ -266,7 +266,7 @@
stop stop
.end .end
.end add .end
.igroup unary JSOP_NEG-JSOP_POS .igroup unary JSOP_NEG-JSOP_POS
@@ -292,7 +292,7 @@
stop stop
.end .end
.end unary .end
.igroup call JSOP_CALL .igroup call JSOP_CALL
@@ -344,10 +344,9 @@
stop # strobj stop # strobj
.end .end
.end call .end
.igroup apply JSOP_APPLY .igroup apply JSOP_APPLY
.imacro apply0 # apply fun this arr .imacro apply0 # apply fun this arr
pick 3 # fun this arr apply pick 3 # fun this arr apply
pop # fun this arr pop # fun this arr
@@ -567,7 +566,7 @@
.imacro call0 # call fun .imacro call0 # call fun
swap # fun call swap # fun call
pop # fun pop # fun
null # fun this nullthis # fun this
call 0 # call 0 #
stop # stop #
.end # .end #
@@ -628,7 +627,7 @@
stop # stop #
.end # .end #
.end apply .end
.igroup iter JSOP_ITER .igroup iter JSOP_ITER
@@ -666,7 +665,7 @@
stop stop
.end .end
.end iter .end
.igroup nextiter JSOP_NEXTITER .igroup nextiter JSOP_NEXTITER
@@ -692,7 +691,7 @@
stop stop
.end .end
.end nextiter .end
.igroup getelem JSOP_GETELEM .igroup getelem JSOP_GETELEM
@@ -736,7 +735,7 @@
stop stop
.end .end
.end getelem .end
.igroup setelem JSOP_SETELEM .igroup setelem JSOP_SETELEM
@@ -762,7 +761,7 @@
stop stop
.end .end
.end setelem .end
.igroup initelem JSOP_INITELEM .igroup initelem JSOP_INITELEM
@@ -788,4 +787,4 @@
stop stop
.end .end
.end initelem .end

View File

@@ -221,7 +221,7 @@ MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated str
MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression") MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment") MSG_DEF(JSMSG_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment")
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal") MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE, 142, 0, JSEXN_TYPEERR, "bad cloned function scope chain") MSG_DEF(JSMSG_UNUSED142, 142, 0, JSEXN_NONE, "unused142")
MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number") MSG_DEF(JSMSG_SHARPVAR_TOO_BIG, 143, 0, JSEXN_SYNTAXERR, "overlarge sharp variable number")
MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character") MSG_DEF(JSMSG_ILLEGAL_CHARACTER, 144, 0, JSEXN_SYNTAXERR, "illegal character")
MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant") MSG_DEF(JSMSG_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")

View File

@@ -4350,74 +4350,10 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
{ {
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) { if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) {
/* /* Indicate we cannot clone this object. */
* We cannot clone this object, so fail (we used to return funobj, bad return funobj;
* idea, but we changed incompatibly to teach any abusers a lesson!).
*/
jsval v = OBJECT_TO_JSVAL(funobj);
js_ReportIsNotFunction(cx, &v, 0);
return NULL;
} }
return js_CloneFunctionObject(cx, GET_FUNCTION_PRIVATE(cx, funobj), parent);
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
JSObject *clone = js_CloneFunctionObject(cx, fun, parent);
if (!clone)
return NULL;
/*
* A flat closure carries its own environment, so why clone it? In case
* someone wants to mutate its fixed slots or add ad-hoc properties. API
* compatibility suggests we not return funobj and let callers mutate the
* returned object at will.
*
* But it's worse than that: API compatibility according to the test for
* bug 300079 requires we get "upvars" from parent and its ancestors! So
* we do that (grudgingly!). The scope chain ancestors are searched as if
* they were activations, respecting the skip field in each upvar's cookie
* but looking up the property by name instead of frame slot.
*/
if (FUN_FLAT_CLOSURE(fun)) {
JS_ASSERT(funobj->dslots);
JS_ASSERT(JSSLOT_FREE(&js_FunctionClass) == JS_INITIAL_NSLOTS);
uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
nslots += js_FunctionClass.reserveSlots(cx, clone);
if (!js_ReallocSlots(cx, clone, nslots, JS_TRUE))
return NULL;
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
JS_ASSERT(uva->length <= size_t(clone->dslots[-1]));
void *mark = JS_ARENA_MARK(&cx->tempPool);
jsuword *names = js_GetLocalNameArray(cx, fun, &cx->tempPool);
if (!names)
return NULL;
uint32 i = 0, n = uva->length;
for (; i < n; i++) {
JSObject *obj = parent;
for (uintN skip = UPVAR_FRAME_SKIP(uva->vector[i]); skip != 0; --skip) {
if (!obj) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
goto break2;
}
obj = OBJ_GET_PARENT(cx, obj);
}
JSAtom *atom = JS_LOCAL_NAME_TO_ATOM(names[i]);
if (!OBJ_GET_PROPERTY(cx, obj, ATOM_TO_JSID(atom), &clone->dslots[i]))
break;
}
break2:
JS_ARENA_RELEASE(&cx->tempPool, mark);
if (i < n)
return NULL;
}
return clone;
} }
JS_PUBLIC_API(JSObject *) JS_PUBLIC_API(JSObject *)
@@ -4737,8 +4673,8 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
tcflags = JS_OPTIONS_TO_TCFLAGS(cx); tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags, script = js_CompileScript(cx, obj, NULL, principals, tcflags,
chars, length, NULL, filename, lineno); chars, length, NULL, filename, lineno);
LAST_FRAME_CHECKS(cx, script); LAST_FRAME_CHECKS(cx, script);
return script; return script;
} }
@@ -4750,6 +4686,7 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
jschar *chars; jschar *chars;
JSBool result; JSBool result;
JSExceptionState *exnState; JSExceptionState *exnState;
JSParseContext pc;
JSErrorReporter older; JSErrorReporter older;
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
@@ -4763,21 +4700,20 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
*/ */
result = JS_TRUE; result = JS_TRUE;
exnState = JS_SaveExceptionState(cx); exnState = JS_SaveExceptionState(cx);
{ if (js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL, NULL,
JSCompiler jsc(cx); 1)) {
if (jsc.init(chars, length, NULL, NULL, 1)) { older = JS_SetErrorReporter(cx, NULL);
older = JS_SetErrorReporter(cx, NULL); if (!js_ParseScript(cx, obj, &pc) &&
if (!jsc.parse(obj) && (pc.tokenStream.flags & TSF_UNEXPECTED_EOF)) {
(jsc.tokenStream.flags & TSF_UNEXPECTED_EOF)) { /*
/* * We ran into an error. If it was because we ran out of source,
* We ran into an error. If it was because we ran out of * we return false, so our caller will know to try to collect more
* source, we return false so our caller knows to try to * buffered source.
* collect more buffered source. */
*/ result = JS_FALSE;
result = JS_FALSE;
}
JS_SetErrorReporter(cx, older);
} }
JS_SetErrorReporter(cx, older);
js_FinishParseContext(cx, &pc);
} }
JS_free(cx, chars); JS_free(cx, chars);
JS_RestoreExceptionState(cx, exnState); JS_RestoreExceptionState(cx, exnState);
@@ -4804,8 +4740,8 @@ JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
} }
tcflags = JS_OPTIONS_TO_TCFLAGS(cx); tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
script = JSCompiler::compileScript(cx, obj, NULL, NULL, tcflags, script = js_CompileScript(cx, obj, NULL, NULL, tcflags,
NULL, 0, fp, filename, 1); NULL, 0, fp, filename, 1);
if (fp != stdin) if (fp != stdin)
fclose(fp); fclose(fp);
LAST_FRAME_CHECKS(cx, script); LAST_FRAME_CHECKS(cx, script);
@@ -4829,8 +4765,8 @@ JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj,
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
tcflags = JS_OPTIONS_TO_TCFLAGS(cx); tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags, script = js_CompileScript(cx, obj, NULL, principals, tcflags,
NULL, 0, file, filename, 1); NULL, 0, file, filename, 1);
LAST_FRAME_CHECKS(cx, script); LAST_FRAME_CHECKS(cx, script);
return script; return script;
} }
@@ -4966,8 +4902,8 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
} }
} }
if (!JSCompiler::compileFunctionBody(cx, fun, principals, if (!js_CompileFunctionBody(cx, fun, principals, chars, length,
chars, length, filename, lineno)) { filename, lineno)) {
fun = NULL; fun = NULL;
goto out; goto out;
} }
@@ -5167,11 +5103,11 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj,
JSBool ok; JSBool ok;
CHECK_REQUEST(cx); CHECK_REQUEST(cx);
script = JSCompiler::compileScript(cx, obj, NULL, principals, script = js_CompileScript(cx, obj, NULL, principals,
!rval !rval
? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL ? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
: TCF_COMPILE_N_GO, : TCF_COMPILE_N_GO,
chars, length, NULL, filename, lineno); chars, length, NULL, filename, lineno);
if (!script) { if (!script) {
LAST_FRAME_CHECKS(cx, script); LAST_FRAME_CHECKS(cx, script);
return JS_FALSE; return JS_FALSE;

View File

@@ -235,8 +235,8 @@ BOOLEAN_TO_JSVAL(JSBool b)
#define JSFUN_FAST_NATIVE 0x0800 /* JSFastNative needs no JSStackFrame */ #define JSFUN_FAST_NATIVE 0x0800 /* JSFastNative needs no JSStackFrame */
#define JSFUN_FLAGS_MASK 0x0ff8 /* overlay JSFUN_* attributes -- #define JSFUN_FLAGS_MASK 0x0ff8 /* overlay JSFUN_* attributes --
bits 12-15 are used internally to note that bit #15 is used internally
flag interpreted functions */ to flag interpreted functions */
#define JSFUN_STUB_GSOPS 0x1000 /* use JS_PropertyStub getter/setter #define JSFUN_STUB_GSOPS 0x1000 /* use JS_PropertyStub getter/setter
instead of defaulting to class gsops instead of defaulting to class gsops

View File

@@ -3277,7 +3277,7 @@ js_FastNewArrayWithLength(JSContext* cx, JSObject* proto, uint32 i)
JSObject* FASTCALL JSObject* FASTCALL
js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len) js_NewUninitializedArray(JSContext* cx, JSObject* proto, uint32 len)
{ {
JSObject* obj = js_FastNewArrayWithLength(cx, proto, len); JSObject *obj = js_FastNewArrayWithLength(cx, proto, len);
if (!obj || !ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN))) if (!obj || !ResizeSlots(cx, obj, 0, JS_MAX(len, ARRAY_CAPACITY_MIN)))
return NULL; return NULL;
return obj; return obj;

View File

@@ -73,11 +73,8 @@ extern JSClass js_ArrayClass, js_SlowArrayClass;
* *
* Therefore the interpreter (js_Interpret in JSOP_GETPROP and JSOP_CALLPROP) * Therefore the interpreter (js_Interpret in JSOP_GETPROP and JSOP_CALLPROP)
* and js_GetPropertyHelper use this inline function to skip up one link in the * and js_GetPropertyHelper use this inline function to skip up one link in the
* prototype chain when obj is a dense array, in order to find a native object * prototype chain when obj is a dense array, in order to find a likely-native
* (to wit, Array.prototype) in which to probe for cached methods. * object (to wit, Array.prototype) in which to probe for cached methods.
*
* Note that setting aobj.__proto__ for a dense array aobj turns aobj into a
* slow array, avoiding the neede to skip.
* *
* Callers of js_GetProtoIfDenseArray must take care to use the original object * Callers of js_GetProtoIfDenseArray must take care to use the original object
* (obj) for the |this| value of a getter, setter, or method call (bug 476447). * (obj) for the |this| value of a getter, setter, or method call (bug 476447).

View File

@@ -49,15 +49,13 @@
#include "jsprf.h" #include "jsprf.h"
#include "jsapi.h" #include "jsapi.h"
#include "jsatom.h" #include "jsatom.h"
#include "jsbit.h"
#include "jscntxt.h" #include "jscntxt.h"
#include "jsversion.h"
#include "jsgc.h" #include "jsgc.h"
#include "jslock.h" #include "jslock.h"
#include "jsnum.h" #include "jsnum.h"
#include "jsparse.h"
#include "jsscan.h" #include "jsscan.h"
#include "jsstr.h" #include "jsstr.h"
#include "jsversion.h"
/* /*
* ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems. * ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
@@ -935,70 +933,32 @@ js_hash_atom_ptr(const void *key)
return ATOM_HASH(atom); return ATOM_HASH(atom);
} }
#if JS_BITS_PER_WORD == 32
# define TEMP_SIZE_START_LOG2 5
#else
# define TEMP_SIZE_START_LOG2 6
#endif
#define TEMP_SIZE_LIMIT_LOG2 (TEMP_SIZE_START_LOG2 + NUM_TEMP_FREELISTS)
#define TEMP_SIZE_START JS_BIT(TEMP_SIZE_START_LOG2)
#define TEMP_SIZE_LIMIT JS_BIT(TEMP_SIZE_LIMIT_LOG2)
JS_STATIC_ASSERT(TEMP_SIZE_START >= sizeof(JSHashTable));
static void * static void *
js_alloc_temp_space(void *priv, size_t size) js_alloc_temp_space(void *priv, size_t size)
{ {
JSCompiler *jsc = (JSCompiler *) priv; JSContext *cx = (JSContext *) priv;
void *space; void *space;
if (size < TEMP_SIZE_LIMIT) {
int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2;
JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS);
space = jsc->tempFreeList[bin]; JS_ARENA_ALLOCATE(space, &cx->tempPool, size);
if (space) {
jsc->tempFreeList[bin] = *(void **)space;
return space;
}
}
JS_ARENA_ALLOCATE(space, &jsc->context->tempPool, size);
if (!space) if (!space)
js_ReportOutOfScriptQuota(jsc->context); js_ReportOutOfScriptQuota(cx);
return space; return space;
} }
static void static void
js_free_temp_space(void *priv, void *item, size_t size) js_free_temp_space(void *priv, void *item)
{ {
if (size >= TEMP_SIZE_LIMIT)
return;
JSCompiler *jsc = (JSCompiler *) priv;
int bin = JS_CeilingLog2(size) - TEMP_SIZE_START_LOG2;
JS_ASSERT(unsigned(bin) < NUM_TEMP_FREELISTS);
*(void **)item = jsc->tempFreeList[bin];
jsc->tempFreeList[bin] = item;
} }
static JSHashEntry * static JSHashEntry *
js_alloc_temp_entry(void *priv, const void *key) js_alloc_temp_entry(void *priv, const void *key)
{ {
JSCompiler *jsc = (JSCompiler *) priv; JSContext *cx = (JSContext *) priv;
JSAtomListElement *ale; JSAtomListElement *ale;
ale = jsc->aleFreeList; JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
if (ale) {
jsc->aleFreeList = ALE_NEXT(ale);
return &ale->entry;
}
JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &jsc->context->tempPool);
if (!ale) { if (!ale) {
js_ReportOutOfScriptQuota(jsc->context); js_ReportOutOfScriptQuota(cx);
return NULL; return NULL;
} }
return &ale->entry; return &ale->entry;
@@ -1007,11 +967,6 @@ js_alloc_temp_entry(void *priv, const void *key)
static void static void
js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag) js_free_temp_entry(void *priv, JSHashEntry *he, uintN flag)
{ {
JSCompiler *jsc = (JSCompiler *) priv;
JSAtomListElement *ale = (JSAtomListElement *) he;
ALE_SET_NEXT(ale, jsc->aleFreeList);
jsc->aleFreeList = ale;
} }
static JSHashAllocOps temp_alloc_ops = { static JSHashAllocOps temp_alloc_ops = {
@@ -1020,181 +975,67 @@ static JSHashAllocOps temp_alloc_ops = {
}; };
JSAtomListElement * JSAtomListElement *
JSAtomList::rawLookup(JSAtom *atom, JSHashEntry **&hep) js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
{ {
JSAtomListElement *ale;
if (table) {
hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom);
ale = *hep ? (JSAtomListElement *) *hep : NULL;
} else {
JSHashEntry **alep = &list;
hep = NULL;
while ((ale = (JSAtomListElement *)*alep) != NULL) {
if (ALE_ATOM(ale) == atom) {
/* Hit, move atom's element to the front of the list. */
*alep = ale->entry.next;
ale->entry.next = list;
list = &ale->entry;
break;
}
alep = &ale->entry.next;
}
}
return ale;
}
#define ATOM_LIST_HASH_THRESHOLD 12
JSAtomListElement *
JSAtomList::add(JSCompiler *jsc, JSAtom *atom, AddHow how)
{
JS_ASSERT(!set);
JSAtomListElement *ale, *ale2, *next; JSAtomListElement *ale, *ale2, *next;
JSHashEntry **hep; JSHashEntry **hep;
ale = rawLookup(atom, hep); ATOM_LIST_LOOKUP(ale, hep, al, atom);
if (!ale || how != UNIQUE) { if (!ale) {
if (count < ATOM_LIST_HASH_THRESHOLD && !table) { if (al->count < 10) {
/* Few enough for linear search and no hash table yet needed. */ /* Few enough for linear search, no hash table needed. */
ale = (JSAtomListElement *)js_alloc_temp_entry(jsc, atom); JS_ASSERT(!al->table);
ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);
if (!ale) if (!ale)
return NULL; return NULL;
ALE_SET_ATOM(ale, atom); ALE_SET_ATOM(ale, atom);
ale->entry.next = al->list;
if (how == HOIST) { al->list = &ale->entry;
ale->entry.next = NULL;
hep = (JSHashEntry **) &list;
while (*hep)
hep = &(*hep)->next;
*hep = &ale->entry;
} else {
ale->entry.next = list;
list = &ale->entry;
}
} else { } else {
/* /* We want to hash. Have we already made a hash table? */
* We should hash, or else we already are hashing, but count was if (!al->table) {
* reduced by JSAtomList::rawRemove below ATOM_LIST_HASH_THRESHOLD.
* Check whether we should create the table.
*/
if (!table) {
/* No hash table yet, so hep had better be null! */ /* No hash table yet, so hep had better be null! */
JS_ASSERT(!hep); JS_ASSERT(!hep);
table = JS_NewHashTable(count + 1, js_hash_atom_ptr, al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr,
JS_CompareValues, JS_CompareValues, JS_CompareValues, JS_CompareValues,
&temp_alloc_ops, jsc); &temp_alloc_ops, cx);
if (!table) if (!al->table)
return NULL; return NULL;
/* /*
* Set ht->nentries explicitly, because we are moving entries * Set ht->nentries explicitly, because we are moving entries
* from list to ht, not calling JS_HashTable(Raw|)Add. * from al to ht, not calling JS_HashTable(Raw|)Add.
*/ */
table->nentries = count; al->table->nentries = al->count;
/* /* Insert each ale on al->list into the new hash table. */
* Insert each ale on list into the new hash table. Append to for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) {
* the hash chain rather than inserting at the bucket head, to
* preserve order among entries with the same key.
*/
for (ale2 = (JSAtomListElement *)list; ale2; ale2 = next) {
next = ALE_NEXT(ale2); next = ALE_NEXT(ale2);
ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2)); ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2));
hep = JS_HashTableRawLookup(table, ale2->entry.keyHash, hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,
ale2->entry.key); ale2->entry.key);
while (*hep) ale2->entry.next = *hep;
hep = &(*hep)->next;
*hep = &ale2->entry; *hep = &ale2->entry;
ale2->entry.next = NULL;
} }
list = NULL; al->list = NULL;
/* Set hep for insertion of atom's ale, immediately below. */ /* Set hep for insertion of atom's ale, immediately below. */
hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom); hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom);
} }
/* Finally, add an entry for atom into the hash bucket at hep. */ /* Finally, add an entry for atom into the hash bucket at hep. */
ale = (JSAtomListElement *) ale = (JSAtomListElement *)
JS_HashTableRawAdd(table, hep, ATOM_HASH(atom), atom, NULL); JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom,
NULL);
if (!ale) if (!ale)
return NULL; return NULL;
/*
* If hoisting, move ale to the end of its chain after we called
* JS_HashTableRawAdd, since RawAdd may have grown the table and
* then recomputed hep to refer to the pointer to the first entry
* with the given key.
*/
if (how == HOIST && ale->entry.next) {
*hep = ale->entry.next;
ale->entry.next = NULL;
do {
hep = &(*hep)->next;
} while (*hep);
*hep = &ale->entry;
}
} }
ALE_SET_INDEX(ale, count++); ALE_SET_INDEX(ale, al->count++);
} }
return ale; return ale;
} }
void
JSAtomList::rawRemove(JSCompiler *jsc, JSAtomListElement *ale, JSHashEntry **hep)
{
JS_ASSERT(!set);
JS_ASSERT(count != 0);
if (table) {
JS_ASSERT(hep);
JS_HashTableRawRemove(table, hep, &ale->entry);
} else {
JS_ASSERT(!hep);
hep = &list;
while (*hep != &ale->entry) {
JS_ASSERT(*hep);
hep = &(*hep)->next;
}
*hep = ale->entry.next;
js_free_temp_entry(jsc, &ale->entry, HT_FREE_ENTRY);
}
--count;
}
JSAtomListElement *
JSAtomListIterator::operator ()()
{
JSAtomListElement *ale;
JSHashTable *ht;
if (index == uint32(-1))
return NULL;
ale = next;
if (!ale) {
ht = list->table;
if (!ht)
goto done;
do {
if (index == JS_BIT(JS_HASH_BITS - ht->shift))
goto done;
next = (JSAtomListElement *) ht->buckets[index++];
} while (!next);
ale = next;
}
next = ALE_NEXT(ale);
return ale;
done:
index = uint32(-1);
return NULL;
}
static intN static intN
js_map_atom(JSHashEntry *he, intN i, void *arg) js_map_atom(JSHashEntry *he, intN i, void *arg)
{ {
@@ -1240,5 +1081,5 @@ js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
vector[ALE_INDEX(ale)] = ALE_ATOM(ale); vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
} while ((ale = ALE_NEXT(ale)) != NULL); } while ((ale = ALE_NEXT(ale)) != NULL);
} }
al->clear(); ATOM_LIST_INIT(al);
} }

View File

@@ -88,99 +88,51 @@ struct JSAtomListElement {
#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key) #define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key)
#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value)) #define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value))
#define ALE_DEFN(ale) ((JSDefinition *) (ale)->entry.value) #define ALE_JSOP(ale) ((JSOp) JS_PTR_TO_UINT32((ale)->entry.value))
#define ALE_VALUE(ale) ((jsval) (ale)->entry.value) #define ALE_VALUE(ale) ((jsval) (ale)->entry.value)
#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next) #define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next)
#define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom)) #define ALE_SET_ATOM(ale,atom) ((ale)->entry.key = (const void *)(atom))
#define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index)) #define ALE_SET_INDEX(ale,index)((ale)->entry.value = JS_UINT32_TO_PTR(index))
#define ALE_SET_DEFN(ale, dn) ((ale)->entry.value = (void *)(dn)) #define ALE_SET_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op))
#define ALE_SET_VALUE(ale, v) ((ale)->entry.value = (void *)(v)) #define ALE_SET_VALUE(ale, v) ((ale)->entry.value = (void *)(v))
#define ALE_SET_NEXT(ale,nxt) ((ale)->entry.next = (JSHashEntry *)(nxt))
/* struct JSAtomList {
* NB: JSAtomSet must be plain-old-data as it is embedded in the pn_u union in
* JSParseNode. JSAtomList encapsulates all operational uses of a JSAtomSet.
*
* The JSAtomList name is traditional, even though the implementation is a map
* (not to be confused with JSAtomMap). In particular the "ALE" and "ale" short
* names for JSAtomListElement variables roll off the fingers, compared to ASE
* or AME alternatives.
*/
struct JSAtomSet {
JSHashEntry *list; /* literals indexed for mapping */ JSHashEntry *list; /* literals indexed for mapping */
JSHashTable *table; /* hash table if list gets too long */ JSHashTable *table; /* hash table if list gets too long */
jsuint count; /* count of indexed literals */ jsuint count; /* count of indexed literals */
}; };
#ifdef __cplusplus #define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \
(al)->count = 0)
struct JSAtomList : public JSAtomSet #define ATOM_LIST_SEARCH(_ale,_al,_atom) \
{ JS_BEGIN_MACRO \
#ifdef DEBUG JSHashEntry **_hep; \
const JSAtomSet* set; /* asserted null in mutating methods */ ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \
#endif JS_END_MACRO
JSAtomList() { #define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \
list = NULL; table = NULL; count = 0; JS_BEGIN_MACRO \
#ifdef DEBUG if ((_al)->table) { \
set = NULL; _hep = JS_HashTableRawLookup((_al)->table, ATOM_HASH(_atom), \
#endif _atom); \
} _ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \
} else { \
JSAtomList(const JSAtomSet& as) { JSHashEntry **_alep = &(_al)->list; \
list = as.list; table = as.table; count = as.count; _hep = NULL; \
#ifdef DEBUG while ((_ale = (JSAtomListElement *)*_alep) != NULL) { \
set = &as; if (ALE_ATOM(_ale) == (_atom)) { \
#endif /* Hit, move atom's element to the front of the list. */ \
} *_alep = (_ale)->entry.next; \
(_ale)->entry.next = (_al)->list; \
void clear() { JS_ASSERT(!set); list = NULL; table = NULL; count = 0; } (_al)->list = &_ale->entry; \
break; \
JSAtomListElement *lookup(JSAtom *atom) { } \
JSHashEntry **hep; _alep = &_ale->entry.next; \
return rawLookup(atom, hep); } \
} } \
JS_END_MACRO
JSAtomListElement *rawLookup(JSAtom *atom, JSHashEntry **&hep);
enum AddHow { UNIQUE, SHADOW, HOIST };
JSAtomListElement *add(JSCompiler *jsc, JSAtom *atom, AddHow how = UNIQUE);
void remove(JSCompiler *jsc, JSAtom *atom) {
JSHashEntry **hep;
JSAtomListElement *ale = rawLookup(atom, hep);
if (ale)
rawRemove(jsc, ale, hep);
}
void rawRemove(JSCompiler *jsc, JSAtomListElement *ale, JSHashEntry **hep);
};
/*
* Iterate over an atom list. We define a call operator to minimize the syntax
* tax for users. We do not use a more standard pattern using ++ and * because
* (a) it's the wrong pattern for a non-scalar; (b) it's overkill -- one method
* is enough. (This comment is overkill!)
*/
class JSAtomListIterator {
JSAtomList* list;
JSAtomListElement* next;
uint32 index;
public:
JSAtomListIterator(JSAtomList* al) : list(al) { reset(); }
void reset() {
next = (JSAtomListElement *) list->list;
index = 0;
}
JSAtomListElement* operator ()();
};
#endif /* __cplusplus */
struct JSAtomMap { struct JSAtomMap {
JSAtom **vector; /* array of ptrs to indexed atoms */ JSAtom **vector; /* array of ptrs to indexed atoms */
@@ -474,6 +426,12 @@ js_DumpAtoms(JSContext *cx, FILE *fp);
#endif #endif
/*
* Assign atom an index and insert it on al.
*/
extern JSAtomListElement *
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al);
/* /*
* For all unmapped atoms recorded in al, add a mapping from the atom's index * For all unmapped atoms recorded in al, add a mapping from the atom's index
* to its address. map->length must already be set to the number of atoms in * to its address. map->length must already be set to the number of atoms in

View File

@@ -253,10 +253,8 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
return JS_FALSE; return JS_FALSE;
} }
if (slot != sprop->slot) { if (slot != sprop->slot)
js_FreeSlot(cx, obj, slot);
goto slot_changed; goto slot_changed;
}
} }
SCOPE_EXTEND_SHAPE(cx, scope, sprop); SCOPE_EXTEND_SHAPE(cx, scope, sprop);
@@ -276,6 +274,7 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
slot = sprop2->slot; slot = sprop2->slot;
slot_changed: slot_changed:
js_FreeSlot(cx, obj, slot);
JS_UNLOCK_SCOPE(cx, scope); JS_UNLOCK_SCOPE(cx, scope);
return JS_FALSE; return JS_FALSE;
} }
@@ -368,31 +367,6 @@ js_Arguments(JSContext* cx)
return NULL; return NULL;
} }
JSObject* FASTCALL
js_NewNullClosure(JSContext* cx, JSObject* funobj, JSObject* proto, JSObject *parent)
{
JS_ASSERT(HAS_FUNCTION_CLASS(funobj));
JSFunction *fun = (JSFunction*) funobj;
JS_ASSERT(GET_FUNCTION_PRIVATE(cx, funobj) == fun);
JS_ASSERT(JS_ON_TRACE(cx));
JSObject* closure = (JSObject*) js_NewGCThing(cx, GCX_OBJECT, sizeof(JSObject));
if (!closure)
return NULL;
closure->classword = jsuword(&js_FunctionClass);
closure->fslots[JSSLOT_PROTO] = OBJECT_TO_JSVAL(proto);
closure->fslots[JSSLOT_PARENT] = OBJECT_TO_JSVAL(parent);
closure->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
for (unsigned i = JSSLOT_PRIVATE + 1; i != JS_INITIAL_NSLOTS; ++i)
closure->fslots[i] = JSVAL_VOID;
closure->map = js_HoldObjectMap(cx, proto->map);
closure->dslots = NULL;
return closure;
}
#define BUILTIN1 JS_DEFINE_CALLINFO_1 #define BUILTIN1 JS_DEFINE_CALLINFO_1
#define BUILTIN2 JS_DEFINE_CALLINFO_2 #define BUILTIN2 JS_DEFINE_CALLINFO_2
#define BUILTIN3 JS_DEFINE_CALLINFO_3 #define BUILTIN3 JS_DEFINE_CALLINFO_3

View File

@@ -471,27 +471,26 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
#if defined DEBUG && defined XP_UNIX #if defined DEBUG && defined XP_UNIX
# include <stdio.h> # include <stdio.h>
class JSAutoFile { class AutoFile {
public: public:
JSAutoFile() : mFile(NULL) {} AutoFile() : mFp(NULL) {}
~JSAutoFile() { ~AutoFile() {
if (mFile) if (mFp)
fclose(mFile); fclose(mFp);
} }
FILE *open(const char *fname, const char *mode) { FILE *open(const char *fname, const char *mode) {
return mFile = fopen(fname, mode); return mFp = fopen(fname, mode);
} }
operator FILE *() { operator FILE *() {
return mFile; return mFp;
} }
private: private:
FILE *mFile; FILE *mFp;
}; };
#ifdef JS_EVAL_CACHE_METERING
static void static void
DumpEvalCacheMeter(JSContext *cx) DumpEvalCacheMeter(JSContext *cx)
{ {
@@ -505,7 +504,7 @@ DumpEvalCacheMeter(JSContext *cx)
}; };
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter; JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
static JSAutoFile fp; static AutoFile fp;
if (!fp) { if (!fp) {
fp.open("/tmp/evalcache.stats", "w"); fp.open("/tmp/evalcache.stats", "w");
if (!fp) if (!fp)
@@ -528,49 +527,10 @@ DumpEvalCacheMeter(JSContext *cx)
fflush(fp); fflush(fp);
} }
# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx) # define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
#endif #else
#ifdef JS_FUNCTION_METERING
static void
DumpFunctionMeter(JSContext *cx)
{
struct {
const char *name;
ptrdiff_t offset;
} table[] = {
#define frob(x) { #x, offsetof(JSFunctionMeter, x) }
FUNCTION_KIND_METER_LIST(frob)
#undef frob
};
JSFunctionMeter *fm = &cx->runtime->functionMeter;
static JSAutoFile fp;
if (!fp) {
fp.open("/tmp/function.stats", "a");
if (!fp)
return;
}
fprintf(fp, "function meter (%s):\n", cx->runtime->lastScriptFilename);
for (uintN i = 0; i < JS_ARRAY_LENGTH(table); ++i) {
fprintf(fp, "%-11.11s %d\n",
table[i].name, *(int32 *)((uint8 *)fm + table[i].offset));
}
fflush(fp);
}
# define DUMP_FUNCTION_METER(cx) DumpFunctionMeter(cx)
#endif
#endif /* DEBUG && XP_UNIX */
#ifndef DUMP_EVAL_CACHE_METER
# define DUMP_EVAL_CACHE_METER(cx) ((void) 0) # define DUMP_EVAL_CACHE_METER(cx) ((void) 0)
#endif #endif
#ifndef DUMP_FUNCTION_METER
# define DUMP_FUNCTION_METER(cx) ((void) 0)
#endif
void void
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode) js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
{ {
@@ -673,7 +633,6 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
if (last) { if (last) {
js_GC(cx, GC_LAST_CONTEXT); js_GC(cx, GC_LAST_CONTEXT);
DUMP_EVAL_CACHE_METER(cx); DUMP_EVAL_CACHE_METER(cx);
DUMP_FUNCTION_METER(cx);
/* /*
* Free the script filename table if it exists and is empty. Do this * Free the script filename table if it exists and is empty. Do this

View File

@@ -198,8 +198,7 @@ typedef struct InterpStruct InterpStruct;
#endif #endif
#ifdef DEBUG #ifdef DEBUG
# define JS_EVAL_CACHE_METERING 1 # define JS_EVAL_CACHE_METERING 1
# define JS_FUNCTION_METERING 1
#endif #endif
/* Number of potentially reusable scriptsToGC to search for the eval cache. */ /* Number of potentially reusable scriptsToGC to search for the eval cache. */
@@ -210,27 +209,14 @@ typedef struct InterpStruct InterpStruct;
#ifdef JS_EVAL_CACHE_METERING #ifdef JS_EVAL_CACHE_METERING
# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope) # define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope)
# define identity(x) x # define ID(x) x
/* Have to typedef this for LiveConnect C code, which includes us. */ /* Have to typedef this for LiveConnect C code, which includes us. */
typedef struct JSEvalCacheMeter { typedef struct JSEvalCacheMeter {
uint64 EVAL_CACHE_METER_LIST(identity); uint64 EVAL_CACHE_METER_LIST(ID);
} JSEvalCacheMeter; } JSEvalCacheMeter;
# undef identity # undef ID
#endif
#ifdef JS_FUNCTION_METERING
# define FUNCTION_KIND_METER_LIST(_) \
_(allfun), _(heavy), _(nofreeupvar), _(onlyfreevar), \
_(display), _(flat), _(setupvar), _(badfunarg)
# define identity(x) x
typedef struct JSFunctionMeter {
int32 FUNCTION_KIND_METER_LIST(identity);
} JSFunctionMeter;
# undef identity
#endif #endif
struct JSThreadData { struct JSThreadData {
@@ -669,11 +655,6 @@ struct JSRuntime {
#ifdef JS_GCMETER #ifdef JS_GCMETER
JSGCStats gcStats; JSGCStats gcStats;
#endif #endif
#ifdef JS_FUNCTION_METERING
JSFunctionMeter functionMeter;
char lastScriptFilename[1024];
#endif
}; };
/* Common macros to access thread-local caches in JSThread or JSRuntime. */ /* Common macros to access thread-local caches in JSThread or JSRuntime. */
@@ -687,6 +668,7 @@ struct JSRuntime {
#else #else
# define EVAL_CACHE_METER(x) ((void) 0) # define EVAL_CACHE_METER(x) ((void) 0)
#endif #endif
#undef DECLARE_EVAL_CACHE_METER
#ifdef DEBUG #ifdef DEBUG
# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which) # define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
@@ -781,7 +763,7 @@ typedef struct JSLocalRootStack {
* structure */ * structure */
#define JSTVU_SPROP (-3) /* u.sprop roots property tree node */ #define JSTVU_SPROP (-3) /* u.sprop roots property tree node */
#define JSTVU_WEAK_ROOTS (-4) /* u.weakRoots points to saved weak roots */ #define JSTVU_WEAK_ROOTS (-4) /* u.weakRoots points to saved weak roots */
#define JSTVU_COMPILER (-5) /* u.compiler roots JSCompiler* */ #define JSTVU_PARSE_CONTEXT (-5) /* u.parseContext roots JSParseContext* */
#define JSTVU_SCRIPT (-6) /* u.script roots JSScript* */ #define JSTVU_SCRIPT (-6) /* u.script roots JSScript* */
/* /*
@@ -836,8 +818,8 @@ typedef struct JSLocalRootStack {
#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \ #define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \
JS_PUSH_TEMP_ROOT_COMMON(cx, weakRoots_, tvr, JSTVU_WEAK_ROOTS, weakRoots) JS_PUSH_TEMP_ROOT_COMMON(cx, weakRoots_, tvr, JSTVU_WEAK_ROOTS, weakRoots)
#define JS_PUSH_TEMP_ROOT_COMPILER(cx,pc,tvr) \ #define JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx,pc,tvr) \
JS_PUSH_TEMP_ROOT_COMMON(cx, pc, tvr, JSTVU_COMPILER, compiler) JS_PUSH_TEMP_ROOT_COMMON(cx, pc, tvr, JSTVU_PARSE_CONTEXT, parseContext)
#define JS_PUSH_TEMP_ROOT_SCRIPT(cx,script_,tvr) \ #define JS_PUSH_TEMP_ROOT_SCRIPT(cx,script_,tvr) \
JS_PUSH_TEMP_ROOT_COMMON(cx, script_, tvr, JSTVU_SCRIPT, script) JS_PUSH_TEMP_ROOT_COMMON(cx, script_, tvr, JSTVU_SCRIPT, script)
@@ -1073,7 +1055,7 @@ class JSAutoTempValueRooter
} }
jsval value() { return mTvr.u.value; } jsval value() { return mTvr.u.value; }
jsval *addr() { return &mTvr.u.value; } jsval * addr() { return &mTvr.u.value; }
protected: protected:
JSContext *mContext; JSContext *mContext;

View File

@@ -1252,15 +1252,15 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
/* /*
* NB: This function breaks the assumption that the compiler can see all * NB: This function breaks the assumption that the compiler can see all
* calls and properly compute a static level. In order to get around this, * calls and properly compute a static depth. In order to get around this,
* we use a static level that will cause us not to attempt to optimize * we use a static depth that will cause us not to attempt to optimize
* variable references made by this frame. * variable references made by this frame.
*/ */
script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp), script = js_CompileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
TCF_COMPILE_N_GO | TCF_COMPILE_N_GO |
TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE), TCF_PUT_STATIC_DEPTH(JS_DISPLAY_SIZE),
chars, length, NULL, chars, length, NULL,
filename, lineno); filename, lineno);
if (!script) if (!script)
return JS_FALSE; return JS_FALSE;

File diff suppressed because it is too large Load Diff

View File

@@ -46,7 +46,6 @@
#include "jstypes.h" #include "jstypes.h"
#include "jsatom.h" #include "jsatom.h"
#include "jsopcode.h" #include "jsopcode.h"
#include "jsparse.h"
#include "jsscript.h" #include "jsscript.h"
#include "jsprvtd.h" #include "jsprvtd.h"
#include "jspubtd.h" #include "jspubtd.h"
@@ -124,14 +123,13 @@ typedef struct JSStmtInfo JSStmtInfo;
struct JSStmtInfo { struct JSStmtInfo {
uint16 type; /* statement type */ uint16 type; /* statement type */
uint16 flags; /* flags, see below */ uint16 flags; /* flags, see below */
uint32 blockid; /* for simplified dominance computation */
ptrdiff_t update; /* loop update offset (top if none) */ ptrdiff_t update; /* loop update offset (top if none) */
ptrdiff_t breaks; /* offset of last break in loop */ ptrdiff_t breaks; /* offset of last break in loop */
ptrdiff_t continues; /* offset of last continue in loop */ ptrdiff_t continues; /* offset of last continue in loop */
union { union {
JSAtom *label; /* name of LABEL */ JSAtom *label; /* name of LABEL */
JSObject *blockObj; /* block scope object */ JSObject *blockObj; /* block scope object */
}; } u;
JSStmtInfo *down; /* info for enclosing statement */ JSStmtInfo *down; /* info for enclosing statement */
JSStmtInfo *downScope; /* next enclosing lexical scope */ JSStmtInfo *downScope; /* next enclosing lexical scope */
}; };
@@ -152,132 +150,101 @@ struct JSStmtInfo {
#define GOSUBS(stmt) ((stmt).breaks) #define GOSUBS(stmt) ((stmt).breaks)
#define GUARDJUMP(stmt) ((stmt).continues) #define GUARDJUMP(stmt) ((stmt).continues)
#define AT_TOP_LEVEL(tc) \
(!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))
#define SET_STATEMENT_TOP(stmt, top) \ #define SET_STATEMENT_TOP(stmt, top) \
((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1)) ((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
#ifdef JS_SCOPE_DEPTH_METER
# define JS_SCOPE_DEPTH_METERING(code) ((void) (code))
#else
# define JS_SCOPE_DEPTH_METERING(code) ((void) 0)
#endif
struct JSTreeContext { /* tree context for semantic checks */ struct JSTreeContext { /* tree context for semantic checks */
uint16 flags; /* statement state flags, see below */ uint16 flags; /* statement state flags, see below */
uint16 ngvars; /* max. no. of global variables/regexps */ uint16 ngvars; /* max. no. of global variables/regexps */
uint32 bodyid; /* block number of program/function body */
uint32 blockidGen; /* preincremented block number generator */
JSStmtInfo *topStmt; /* top of statement info stack */ JSStmtInfo *topStmt; /* top of statement info stack */
JSStmtInfo *topScopeStmt; /* top lexical scope statement */ JSStmtInfo *topScopeStmt; /* top lexical scope statement */
JSObject *blockChain; /* compile time block scope chain (NB: one JSObject *blockChain; /* compile time block scope chain (NB: one
deeper than the topScopeStmt/downScope deeper than the topScopeStmt/downScope
chain when in head of let block/expr) */ chain when in head of let block/expr) */
JSParseNode *blockNode; /* parse node for a block with let declarations JSParseNode *blockNode; /* parse node for a lexical scope.
(block with its own lexical scope) */ XXX combine with blockChain? */
JSAtomList decls; /* function, const, and var declarations */ JSAtomList decls; /* function, const, and var declarations */
JSCompiler *compiler; /* ptr to common parsing and lexing data */ JSParseContext *parseContext;
union { union {
JSFunction *fun; /* function to store argument and variable JSFunction *fun; /* function to store argument and variable
names when flags & TCF_IN_FUNCTION */ names when flags & TCF_IN_FUNCTION */
JSObject *scopeChain; /* scope chain object for the script */ JSObject *scopeChain; /* scope chain object for the script */
}; } u;
JSAtomList lexdeps; /* unresolved lexical name dependencies */
JSAtomList upvars; /* resolved lexical name dependencies */
JSTreeContext *parent; /* enclosing function or global context */
uintN staticLevel; /* static compilation unit nesting level */
JSFunctionBox *funbox; /* null or box for function we're compiling
if (flags & TCF_IN_FUNCTION) and not in
JSCompiler::compileFunctionBody */
JSFunctionBox *functionList;
#ifdef JS_SCOPE_DEPTH_METER #ifdef JS_SCOPE_DEPTH_METER
uint16 scopeDepth; /* current lexical scope chain depth */ uint16 scopeDepth; /* current lexical scope chain depth */
uint16 maxScopeDepth; /* maximum lexical scope chain depth */ uint16 maxScopeDepth; /* maximum lexical scope chain depth */
#endif #endif
JSTreeContext(JSCompiler *jsc)
: flags(0), ngvars(0), bodyid(0), blockidGen(0),
topStmt(NULL), topScopeStmt(NULL), blockChain(NULL), blockNode(NULL),
compiler(jsc), scopeChain(NULL), parent(NULL), staticLevel(0),
funbox(NULL), functionList(NULL)
{
JS_SCOPE_DEPTH_METERING(scopeDepth = maxScopeDepth = 0);
}
/*
* For functions the tree context is constructed and destructed a second
* time during code generation. To avoid a redundant stats update in such
* cases, we store (uintN) -1 in maxScopeDepth.
*/
~JSTreeContext() {
JS_SCOPE_DEPTH_METERING(maxScopeDepth == (uintN) -1 ||
JS_BASIC_STATS_ACCUM(&compiler
->context
->runtime
->lexicalScopeDepthStats,
maxScopeDepth));
}
uintN blockid() { return topStmt ? topStmt->blockid : bodyid; }
bool atTopLevel() { return !topStmt || (topStmt->flags & SIF_BODY_BLOCK); }
/* Test whether we're in a statement of given type. */
bool inStatement(JSStmtType type);
}; };
#define TCF_IN_FUNCTION 0x01 /* parsing inside function body */
#define TCF_RETURN_EXPR 0x02 /* function has 'return expr;' */
#define TCF_RETURN_VOID 0x04 /* function has 'return;' */
#define TCF_IN_FOR_INIT 0x08 /* parsing init expr of for; exclude 'in' */
#define TCF_NO_SCRIPT_RVAL 0x10 /* API caller does not want result value
from global script */
#define TCF_FUN_USES_NONLOCALS 0x20 /* function refers to non-local names */
#define TCF_FUN_HEAVYWEIGHT 0x40 /* function needs Call object per call */
#define TCF_FUN_IS_GENERATOR 0x80 /* parsed yield statement in function */
#define TCF_HAS_FUNCTION_STMT 0x100 /* block contains a function statement */
#define TCF_GENEXP_LAMBDA 0x200 /* flag lambda from generator expression */
#define TCF_COMPILE_N_GO 0x400 /* compiler-and-go mode of script, can
optimize name references based on scope
chain */
#define TCF_HAS_SHARPS 0x800 /* source contains sharp defs or uses */
/* /*
* Flags to propagate out of the blocks. * Flags to propagate out of the blocks.
*/ */
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID) #define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID)
/*
* TreeContext flags must fit in 16 bits, and all bits are in use now. Widening
* requires changing JSFunctionBox.tcflags too and repacking. Alternative fix
* gets rid of flags, probably starting with TCF_HAS_FUNCTION_STMT.
*/
#define TCF_COMPILING 0x01 /* JSTreeContext is JSCodeGenerator */
#define TCF_IN_FUNCTION 0x02 /* parsing inside function body */
#define TCF_RETURN_EXPR 0x04 /* function has 'return expr;' */
#define TCF_RETURN_VOID 0x08 /* function has 'return;' */
#define TCF_IN_FOR_INIT 0x10 /* parsing init expr of for; exclude 'in' */
#define TCF_FUN_SETS_OUTER_NAME 0x20 /* function set outer name (lexical or free) */
#define TCF_FUN_PARAM_ARGUMENTS 0x40 /* function has parameter named arguments */
#define TCF_FUN_USES_ARGUMENTS 0x80 /* function uses arguments except as a
parameter name */
#define TCF_FUN_HEAVYWEIGHT 0x100 /* function needs Call object per call */
#define TCF_FUN_IS_GENERATOR 0x200 /* parsed yield statement in function */
#define TCF_FUN_IS_FUNARG 0x400 /* function escapes as an argument, return
value, or via the heap */
#define TCF_HAS_FUNCTION_STMT 0x800 /* block contains a function statement */
#define TCF_GENEXP_LAMBDA 0x1000 /* flag lambda from generator expression */
#define TCF_COMPILE_N_GO 0x2000 /* compiler-and-go mode of script, can
optimize name references based on scope
chain */
#define TCF_NO_SCRIPT_RVAL 0x4000 /* API caller does not want result value
from global script */
#define TCF_HAS_SHARPS 0x8000 /* source contains sharp defs or uses */
/* /*
* Flags to propagate from FunctionBody. * Flags to propagate from FunctionBody.
*/ */
#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \ #define TCF_FUN_FLAGS (TCF_FUN_IS_GENERATOR | \
TCF_FUN_USES_ARGUMENTS | \ TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_PARAM_ARGUMENTS | \ TCF_FUN_USES_NONLOCALS | \
TCF_FUN_HEAVYWEIGHT | \
TCF_FUN_IS_GENERATOR | \
TCF_FUN_IS_FUNARG | \
TCF_HAS_SHARPS) TCF_HAS_SHARPS)
/* /*
* Flags field, not stored in JSTreeContext.flags, for passing a static level * Flags field, not stored in JSTreeContext.flags, for passing staticDepth
* into js_CompileScript. * into js_CompileScript.
*/ */
#define TCF_STATIC_LEVEL_MASK 0xffff0000 #define TCF_STATIC_DEPTH_MASK 0xffff0000
#define TCF_GET_STATIC_LEVEL(f) ((uint32)(f) >> 16) #define TCF_GET_STATIC_DEPTH(f) ((uint32)(f) >> 16)
#define TCF_PUT_STATIC_LEVEL(d) ((uint16)(d) << 16) #define TCF_PUT_STATIC_DEPTH(d) ((uint16)(d) << 16)
#ifdef JS_SCOPE_DEPTH_METER
# define JS_SCOPE_DEPTH_METERING(code) ((void) (code))
#else
# define JS_SCOPE_DEPTH_METERING(code) ((void) 0)
#endif
#define TREE_CONTEXT_INIT(tc, pc) \
((tc)->flags = (tc)->ngvars = 0, \
(tc)->topStmt = (tc)->topScopeStmt = NULL, \
(tc)->blockChain = NULL, \
ATOM_LIST_INIT(&(tc)->decls), \
(tc)->blockNode = NULL, \
(tc)->parseContext = (pc), \
(tc)->u.scopeChain = NULL, \
JS_SCOPE_DEPTH_METERING((tc)->scopeDepth = (tc)->maxScopeDepth = 0))
/*
* For functions TREE_CONTEXT_FINISH is called the second time to finish the
* extra tc created during code generation. We skip stats update in such
* cases.
*/
#define TREE_CONTEXT_FINISH(cx, tc) \
JS_SCOPE_DEPTH_METERING( \
(tc)->maxScopeDepth == (uintN) -1 || \
JS_BASIC_STATS_ACCUM(&(cx)->runtime->lexicalScopeDepthStats, \
(tc)->maxScopeDepth))
/* /*
* Span-dependent instructions are jumps whose span (from the jump bytecode to * Span-dependent instructions are jumps whose span (from the jump bytecode to
@@ -347,18 +314,17 @@ struct JSTryNode {
JSTryNode *prev; JSTryNode *prev;
}; };
struct JSCGObjectList { typedef struct JSEmittedObjectList {
uint32 length; /* number of emitted so far objects */ uint32 length; /* number of emitted so far objects */
JSObjectBox *lastbox; /* last emitted object */ JSParsedObjectBox *lastPob; /* last emitted object */
} JSEmittedObjectList;
JSCGObjectList() : length(0), lastbox(NULL) {} extern void
FinishParsedObjects(JSEmittedObjectList *emittedList, JSObjectArray *objectMap);
uintN index(JSObjectBox *objbox); struct JSCodeGenerator {
void finish(JSObjectArray *array); JSTreeContext treeContext; /* base state: statement info stack, etc. */
};
struct JSCodeGenerator : public JSTreeContext
{
JSArenaPool *codePool; /* pointer to thread code arena pool */ JSArenaPool *codePool; /* pointer to thread code arena pool */
JSArenaPool *notePool; /* pointer to thread srcnote arena pool */ JSArenaPool *notePool; /* pointer to thread srcnote arena pool */
void *codeMark; /* low watermark in cg->codePool */ void *codeMark; /* low watermark in cg->codePool */
@@ -397,33 +363,17 @@ struct JSCodeGenerator : public JSTreeContext
uintN emitLevel; /* js_EmitTree recursion level */ uintN emitLevel; /* js_EmitTree recursion level */
JSAtomList constList; /* compile time constants */ JSAtomList constList; /* compile time constants */
JSCGObjectList objectList; /* list of emitted objects */ JSEmittedObjectList objectList; /* list of emitted so far objects */
JSCGObjectList regexpList; /* list of emitted regexp that will be JSEmittedObjectList regexpList; /* list of emitted so far regexp
cloned during execution */ that will be cloned during execution */
uintN staticDepth; /* static frame chain depth */
JSAtomList upvarList; /* map of atoms to upvar indexes */ JSAtomList upvarList; /* map of atoms to upvar indexes */
JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */ JSUpvarArray upvarMap; /* indexed upvar pairs (JS_realloc'ed) */
JSCodeGenerator *parent; /* enclosing function or global context */
/*
* Initialize cg to allocate bytecode space from codePool, source note
* space from notePool, and all other arena-allocated temporaries from
* jsc->context->tempPool.
*/
JSCodeGenerator(JSCompiler *jsc,
JSArenaPool *codePool, JSArenaPool *notePool,
uintN lineno);
/*
* Release cg->codePool, cg->notePool, and compiler->context->tempPool to
* marks set by JSCodeGenerator's ctor. Note that cgs are magic: they own
* the arena pool "tops-of-stack" space above their codeMark, noteMark, and
* tempMark points. This means you cannot alloc from tempPool and save the
* pointer beyond the next JSCodeGenerator destructor call.
*/
~JSCodeGenerator();
}; };
#define CG_TS(cg) TS((cg)->compiler) #define CG_TS(cg) TS((cg)->treeContext.parseContext)
#define CG_BASE(cg) ((cg)->current->base) #define CG_BASE(cg) ((cg)->current->base)
#define CG_LIMIT(cg) ((cg)->current->limit) #define CG_LIMIT(cg) ((cg)->current->limit)
@@ -446,6 +396,25 @@ struct JSCodeGenerator : public JSTreeContext
#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main) #define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main)
#define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog) #define CG_SWITCH_TO_PROLOG(cg) ((cg)->current = &(cg)->prolog)
/*
* Initialize cg to allocate bytecode space from codePool, source note space
* from notePool, and all other arena-allocated temporaries from cx->tempPool.
*/
extern JS_FRIEND_API(void)
js_InitCodeGenerator(JSContext *cx, JSCodeGenerator *cg, JSParseContext *pc,
JSArenaPool *codePool, JSArenaPool *notePool,
uintN lineno);
/*
* Release cg->codePool, cg->notePool, and cx->tempPool to marks set by
* js_InitCodeGenerator. Note that cgs are magic: they own the arena pool
* "tops-of-stack" space above their codeMark, noteMark, and tempMark points.
* This means you cannot alloc from tempPool and save the pointer beyond the
* next JS_FinishCodeGenerator.
*/
extern JS_FRIEND_API(void)
js_FinishCodeGenerator(JSContext *cx, JSCodeGenerator *cg);
/* /*
* Emit one bytecode. * Emit one bytecode.
*/ */
@@ -495,6 +464,13 @@ extern JSBool
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc, js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
ptrdiff_t off); ptrdiff_t off);
/* Test whether we're in a statement of given type. */
extern JSBool
js_InStatement(JSTreeContext *tc, JSStmtType type);
/* Test whether we're in a with statement. */
#define js_InWithStatement(tc) js_InStatement(tc, STMT_WITH)
/* /*
* Push the C-stack-allocated struct at stmt onto the stmtInfo stack. * Push the C-stack-allocated struct at stmt onto the stmtInfo stack.
*/ */
@@ -519,9 +495,9 @@ extern void
js_PopStatement(JSTreeContext *tc); js_PopStatement(JSTreeContext *tc);
/* /*
* Like js_PopStatement(cg), also patch breaks and continues unless the top * Like js_PopStatement(&cg->treeContext), also patch breaks and continues
* statement info record represents a try-catch-finally suite. May fail if a * unless the top statement info record represents a try-catch-finally suite.
* jump offset overflows. * May fail if a jump offset overflows.
*/ */
extern JSBool extern JSBool
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg); js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg);
@@ -608,7 +584,7 @@ typedef enum JSSrcNoteType {
SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or
to an index label in a regular (structuring) to an index label in a regular (structuring)
or a destructuring object initialiser */ or a destructuring object initialiser */
SRC_GENEXP = 1, /* JSOP_LAMBDA from generator expression */ SRC_GENEXP = 1, /* JSOP_ANONFUNOBJ from generator expression */
SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */ SRC_IF_ELSE = 2, /* JSOP_IFEQ bytecode is from an if-then-else */
SRC_FOR_IN = 2, /* JSOP_GOTO to for-in loop condition from SRC_FOR_IN = 2, /* JSOP_GOTO to for-in loop condition from
before loop (same arity as SRC_IF_ELSE) */ before loop (same arity as SRC_IF_ELSE) */

View File

@@ -586,7 +586,7 @@ JSClass js_ArgumentsClass = {
JS_CLASS_TRACE(args_or_call_trace), NULL JS_CLASS_TRACE(args_or_call_trace), NULL
}; };
#define JSSLOT_CALLEE (JSSLOT_PRIVATE + 1) #define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2) #define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
#define CALL_CLASS_FIXED_RESERVED_SLOTS 2 #define CALL_CLASS_FIXED_RESERVED_SLOTS 2
@@ -617,8 +617,8 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
return NULL; return NULL;
JS_SetPrivate(cx, callobj, fp); JS_SetPrivate(cx, callobj, fp);
JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee)); STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION,
STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee)); OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun)));
fp->callobj = callobj; fp->callobj = callobj;
/* /*
@@ -630,19 +630,19 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
return callobj; return callobj;
} }
static JSFunction * JSFunction *
GetCallObjectFunction(JSObject *obj) js_GetCallObjectFunction(JSObject *obj)
{ {
jsval v; jsval v;
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass); JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
v = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE); v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION);
if (JSVAL_IS_VOID(v)) { if (JSVAL_IS_VOID(v)) {
/* Newborn or prototype object. */ /* Newborn or prototype object. */
return NULL; return NULL;
} }
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v)); JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
return GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v)); return (JSFunction *) JSVAL_TO_OBJECT(v);
} }
JS_FRIEND_API(JSBool) JS_FRIEND_API(JSBool)
@@ -678,7 +678,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
} }
fun = fp->fun; fun = fp->fun;
JS_ASSERT(fun == GetCallObjectFunction(callobj)); JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
n = JS_GET_LOCAL_NAME_COUNT(fun); n = JS_GET_LOCAL_NAME_COUNT(fun);
if (n != 0) { if (n != 0) {
JS_LOCK_OBJ(cx, callobj); JS_LOCK_OBJ(cx, callobj);
@@ -718,7 +718,7 @@ call_enumerate(JSContext *cx, JSObject *obj)
JSObject *pobj; JSObject *pobj;
JSProperty *prop; JSProperty *prop;
fun = GetCallObjectFunction(obj); fun = js_GetCallObjectFunction(obj);
n = fun ? JS_GET_LOCAL_NAME_COUNT(fun) : 0; n = fun ? JS_GET_LOCAL_NAME_COUNT(fun) : 0;
if (n == 0) if (n == 0)
return JS_TRUE; return JS_TRUE;
@@ -778,7 +778,7 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
if (STOBJ_GET_CLASS(obj) != &js_CallClass) if (STOBJ_GET_CLASS(obj) != &js_CallClass)
return JS_TRUE; return JS_TRUE;
fun = GetCallObjectFunction(obj); fun = js_GetCallObjectFunction(obj);
fp = (JSStackFrame *) JS_GetPrivate(cx, obj); fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
if (kind == JSCPK_ARGUMENTS) { if (kind == JSCPK_ARGUMENTS) {
@@ -868,39 +868,28 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE); return CallPropertyOp(cx, obj, id, vp, JSCPK_VAR, JS_TRUE);
} }
JSClass js_DeclEnvClass = {
js_Object_str,
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};
static JSBool static JSBool
call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags, call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
JSObject **objp) JSObject **objp)
{ {
jsval callee;
JSFunction *fun; JSFunction *fun;
jsid id; jsid id;
JSLocalKind localKind; JSLocalKind localKind;
JSPropertyOp getter, setter; JSPropertyOp getter, setter;
uintN slot, attrs; uintN slot, attrs;
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
if (!JSVAL_IS_STRING(idval)) if (!JSVAL_IS_STRING(idval))
return JS_TRUE; return JS_TRUE;
callee = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE); fun = js_GetCallObjectFunction(obj);
if (JSVAL_IS_VOID(callee)) if (!fun)
return JS_TRUE; return JS_TRUE;
fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(callee));
if (!js_ValueToStringId(cx, idval, &id)) if (!js_ValueToStringId(cx, idval, &id))
return JS_FALSE; return JS_FALSE;
localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot); localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
if (localKind != JSLOCAL_NONE && localKind != JSLOCAL_UPVAR) { if (localKind != JSLOCAL_NONE) {
JS_ASSERT((uint16) slot == slot); JS_ASSERT((uint16) slot == slot);
/* /*
@@ -943,32 +932,6 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
*objp = obj; *objp = obj;
return JS_TRUE; return JS_TRUE;
} }
/*
* If fun is a named function expression and id matches its name, resolve
* this call object's saved callee function object under that name in obj's
* parent declarative environment object.
*/
if ((fun->flags & JSFUN_LAMBDA) && JSID_TO_ATOM(id) == fun->atom) {
JSObject *parent = STOBJ_GET_PARENT(obj);
if (STOBJ_GET_CLASS(parent) != &js_DeclEnvClass) {
parent = js_NewObjectWithGivenProto(cx, &js_DeclEnvClass, NULL, parent, 0);
if (!parent)
return JS_FALSE;
STOBJ_SET_PARENT(obj, parent);
attrs = JSPROP_PERMANENT | JSPROP_READONLY;
if (!js_DefineNativeProperty(cx, parent, id, callee, NULL, NULL, attrs, 0, 0, NULL))
return JS_FALSE;
}
/* Do not resolve, let normal scope chain search find the name. */
JS_ASSERT(!*objp);
return JS_TRUE;
}
/* Control flow reaches here only if id was not resolved. */
return JS_TRUE; return JS_TRUE;
} }
@@ -992,7 +955,7 @@ call_reserveSlots(JSContext *cx, JSObject *obj)
{ {
JSFunction *fun; JSFunction *fun;
fun = GetCallObjectFunction(obj); fun = js_GetCallObjectFunction(obj);
return JS_GET_LOCAL_NAME_COUNT(fun); return JS_GET_LOCAL_NAME_COUNT(fun);
} }
@@ -1179,6 +1142,15 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
/* /*
* No need to reflect fun.prototype in 'fun.prototype = ... '. * No need to reflect fun.prototype in 'fun.prototype = ... '.
*
* This is not just an optimization, because we must not resolve when
* defining hidden properties during compilation. The setup code for the
* prototype and the lazy properties below eventually calls the property
* hooks for the function object. That in turn calls fun_reserveSlots to
* get the number of the reserved slots which is just the number of
* regular expressions literals in the function. When compiling, that
* number is not yet ready so we must make sure that fun_resolve does
* nothing until the code for the function is generated.
*/ */
if (flags & JSRESOLVE_ASSIGNING) if (flags & JSRESOLVE_ASSIGNING)
return JS_TRUE; return JS_TRUE;
@@ -1264,9 +1236,9 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
JSContext *cx; JSContext *cx;
JSFunction *fun; JSFunction *fun;
uint32 nullAtom; /* flag to indicate if fun->atom is NULL */ uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
uintN nargs, nvars, nupvars, n; uintN nargs, nvars, n;
uint32 localsword; /* word to xdr argument and variable counts */ uint32 localsword; /* word to xdr argument and variable counts */
uint32 flagsword; /* word to xdr upvars count and fun->flags */ uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
JSTempValueRooter tvr; JSTempValueRooter tvr;
JSBool ok; JSBool ok;
@@ -1282,9 +1254,8 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
nullAtom = !fun->atom; nullAtom = !fun->atom;
nargs = fun->nargs; nargs = fun->nargs;
nvars = fun->u.i.nvars; nvars = fun->u.i.nvars;
nupvars = fun->u.i.nupvars;
localsword = (nargs << 16) | nvars; localsword = (nargs << 16) | nvars;
flagsword = (nupvars << 16) | fun->flags; flagsword = fun->flags;
} else { } else {
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL); fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
if (!fun) if (!fun)
@@ -1292,12 +1263,11 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
STOBJ_CLEAR_PARENT(FUN_OBJECT(fun)); STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
STOBJ_CLEAR_PROTO(FUN_OBJECT(fun)); STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
#ifdef __GNUC__ #ifdef __GNUC__
nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */ nvars = nargs = 0; /* quell GCC uninitialized warning */
#endif #endif
} }
/* From here on, control flow must flow through label out. */ /* From here on, control flow must flow through label out. */
MUST_FLOW_THROUGH("out");
JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr); JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
ok = JS_TRUE; ok = JS_TRUE;
@@ -1312,14 +1282,13 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE) {
nargs = localsword >> 16; nargs = localsword >> 16;
nvars = uint16(localsword); nvars = localsword & JS_BITMASK(16);
JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED); JS_ASSERT(flagsword | JSFUN_INTERPRETED);
nupvars = flagsword >> 16; fun->flags = (uint16) flagsword;
fun->flags = uint16(flagsword);
} }
/* do arguments and local vars */ /* do arguments and local vars */
n = nargs + nvars + nupvars; n = nargs + nvars;
if (n != 0) { if (n != 0) {
void *mark; void *mark;
uintN i; uintN i;
@@ -1341,7 +1310,6 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
* names (indexes starting from nargs) bitmap's bit is set when the * names (indexes starting from nargs) bitmap's bit is set when the
* name is declared as const, not as ordinary var. * name is declared as const, not as ordinary var.
* */ * */
MUST_FLOW_THROUGH("release_mark");
bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32); bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool, JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
bitmapLength * sizeof *bitmap); bitmapLength * sizeof *bitmap);
@@ -1397,12 +1365,10 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
if (xdr->mode == JSXDR_DECODE) { if (xdr->mode == JSXDR_DECODE) {
localKind = (i < nargs) localKind = (i < nargs)
? JSLOCAL_ARG ? JSLOCAL_ARG
: (i < nargs + nvars) : bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] & JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
JS_BIT(i & (JS_BITS_PER_UINT32 - 1)) ? JSLOCAL_CONST
? JSLOCAL_CONST : JSLOCAL_VAR;
: JSLOCAL_VAR)
: JSLOCAL_UPVAR;
ok = js_AddLocal(xdr->cx, fun, name, localKind); ok = js_AddLocal(xdr->cx, fun, name, localKind);
if (!ok) if (!ok)
goto release_mark; goto release_mark;
@@ -1540,7 +1506,7 @@ fun_reserveSlots(JSContext *cx, JSObject *obj)
fun = (JSFunction *) JS_GetPrivate(cx, obj); fun = (JSFunction *) JS_GetPrivate(cx, obj);
nslots = 0; nslots = 0;
if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) { if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
if (fun->u.i.nupvars != 0) if (fun->u.i.script->upvarsOffset != 0)
nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length; nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
if (fun->u.i.script->regexpsOffset != 0) if (fun->u.i.script->regexpsOffset != 0)
nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length; nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
@@ -2067,9 +2033,9 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
str = cx->runtime->emptyString; str = cx->runtime->emptyString;
} }
return JSCompiler::compileFunctionBody(cx, fun, principals, return js_CompileFunctionBody(cx, fun, principals,
JSSTRING_CHARS(str), JSSTRING_LENGTH(str), JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
filename, lineno); filename, lineno);
} }
JSObject * JSObject *
@@ -2137,8 +2103,8 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
/* Initialize all function members. */ /* Initialize all function members. */
fun->nargs = nargs; fun->nargs = nargs;
fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRACEABLE); fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED | JSFUN_TRACEABLE);
if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) { if (flags & JSFUN_INTERPRETED) {
JS_ASSERT(!native); JS_ASSERT(!native);
JS_ASSERT(nargs == 0); JS_ASSERT(nargs == 0);
fun->u.i.nvars = 0; fun->u.i.nvars = 0;
@@ -2175,82 +2141,31 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
JSObject * JSObject *
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent) js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
{ {
JSObject *clone;
/* /*
* The cloned function object does not need the extra JSFunction members * The cloned function object does not need the extra fields beyond
* beyond JSObject as it points to fun via the private slot. * JSObject as it points to fun via the private slot.
*/ */
JSObject *clone = js_NewObject(cx, &js_FunctionClass, NULL, parent, clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
sizeof(JSObject)); sizeof(JSObject));
if (!clone) if (!clone)
return NULL; return NULL;
clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun); clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
return clone; return clone;
} }
JSObject *
js_NewFlatClosure(JSContext *cx, JSFunction *fun)
{
JS_ASSERT(FUN_FLAT_CLOSURE(fun));
JSObject *closure = js_CloneFunctionObject(cx, fun, cx->fp->scopeChain);
if (!closure || fun->u.i.script->upvarsOffset == 0)
return closure;
uint32 nslots = JSSLOT_FREE(&js_FunctionClass);
JS_ASSERT(nslots == JS_INITIAL_NSLOTS);
nslots += fun_reserveSlots(cx, closure);
if (!js_ReallocSlots(cx, closure, nslots, JS_TRUE))
return NULL;
JSUpvarArray *uva = JS_SCRIPT_UPVARS(fun->u.i.script);
JS_ASSERT(uva->length <= size_t(closure->dslots[-1]));
JSStackFrame *fp = js_GetTopStackFrame(cx);
for (uint32 i = 0, n = uva->length; i < n; i++) {
JSStackFrame *fp2 = fp;
for (uintN skip = UPVAR_FRAME_SKIP(uva->vector[i]); skip != 0; --skip)
fp2 = fp2->down;
uintN slot = UPVAR_FRAME_SLOT(uva->vector[i]);
jsval *vp;
if (fp2->fun && slot < fp2->fun->nargs) {
vp = fp2->argv;
} else {
if (fp2->fun)
slot -= fp2->fun->nargs;
JS_ASSERT(slot < fp2->script->nslots);
vp = fp2->slots;
}
closure->dslots[i] = vp[slot];
}
return closure;
}
JSFunction * JSFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
uintN nargs, uintN attrs) uintN nargs, uintN attrs)
{ {
JSPropertyOp gsop;
JSFunction *fun; JSFunction *fun;
JSPropertyOp gsop;
if (attrs & JSFUN_STUB_GSOPS) {
/*
* JSFUN_STUB_GSOPS is a request flag only, not stored in fun->flags or
* the defined property's attributes. This allows us to encode another,
* internal flag using the same bit, JSFUN_EXPR_CLOSURE -- see jsfun.h
* for more on this.
*/
attrs &= ~JSFUN_STUB_GSOPS;
gsop = JS_PropertyStub;
} else {
gsop = NULL;
}
fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom); fun = js_NewFunction(cx, NULL, native, nargs, attrs, obj, atom);
if (!fun) if (!fun)
return NULL; return NULL;
gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom), if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
OBJECT_TO_JSVAL(FUN_OBJECT(fun)), OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
gsop, gsop, gsop, gsop,
@@ -2539,18 +2454,11 @@ js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
map->lastdup = NULL; map->lastdup = NULL;
for (i = 0; i != MAX_ARRAY_LOCALS; ++i) { for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
taggedAtom = array[i]; taggedAtom = array[i];
uintN j = i; if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1),
JSLocalKind k = JSLOCAL_ARG; (i < fun->nargs)
if (j >= fun->nargs) { ? JSLOCAL_ARG
j -= fun->nargs; : (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR,
if (j < fun->u.i.nvars) { (i < fun->nargs) ? i : i - fun->nargs)) {
k = (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR;
} else {
j -= fun->u.i.nvars;
k = JSLOCAL_UPVAR;
}
}
if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1), k, j)) {
FreeLocalNameHash(cx, map); FreeLocalNameHash(cx, map);
return JS_FALSE; return JS_FALSE;
} }
@@ -2662,14 +2570,10 @@ get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
constFlag = 0; constFlag = 0;
} else { } else {
JS_ASSERT(entry->localKind == JSLOCAL_VAR || JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
entry->localKind == JSLOCAL_CONST || entry->localKind == JSLOCAL_CONST);
entry->localKind == JSLOCAL_UPVAR); JS_ASSERT(entry->index < args->fun->u.i.nvars);
JS_ASSERT(entry->index < args->fun->u.i.nvars + args->fun->u.i.nupvars); JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars + args->fun->u.i.nupvars); i = args->fun->nargs + entry->index;
i = args->fun->nargs;
if (entry->localKind == JSLOCAL_UPVAR)
i += args->fun->u.i.nvars;
i += entry->index;
constFlag = (entry->localKind == JSLOCAL_CONST); constFlag = (entry->localKind == JSLOCAL_CONST);
} }
args->names[i] = (jsuword) entry->name | constFlag; args->names[i] = (jsuword) entry->name | constFlag;
@@ -2722,7 +2626,7 @@ js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
#if !JS_HAS_DESTRUCTURING #if !JS_HAS_DESTRUCTURING
JS_ASSERT(args.nCopiedArgs == fun->nargs); JS_ASSERT(args.nCopiedArgs == fun->nargs);
#endif #endif
JS_ASSERT(args.nCopiedVars == fun->u.i.nvars + fun->u.i.nupvars); JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
return names; return names;
} }

View File

@@ -89,57 +89,17 @@ struct JSFunction {
JSAtom *atom; /* name for diagnostics and decompiling */ JSAtom *atom; /* name for diagnostics and decompiling */
}; };
/* #define JSFUN_TRACEABLE 0x2000 /* can trace across calls to this native
* The high two bits of fun->flags encode whether the function is native or
* interpreted, and if interpreted, what kind of optimized closure form (if
* any) it might be.
*
* 00 not interpreted
* 01 interpreted, neither flat nor null closure
* 10 interpreted, flat closure
* 11 interpreted, null closure
*
* FUN_FLAT_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset != 0.
* FUN_NULL_CLOSURE implies FUN_INTERPRETED and u.i.script->upvarsOffset == 0.
*
* FUN_INTERPRETED but not FUN_FLAT_CLOSURE and u.i.script->upvarsOffset != 0
* is an Algol-like function expression or nested function, i.e., a function
* that never escapes upward or downward (heapward), and is only ever called.
*
* Finally, FUN_INTERPRETED and u.i.script->upvarsOffset == 0 could be either
* a non-closure (a global function definition, or any function that uses no
* outer names), or a closure of an escaping function that uses outer names
* whose values can't be snapshot (because the outer names could be reassigned
* after the closure is formed, or because assignments could not be analyzed
* due to with or eval).
*
* Such a hard-case function must use JSOP_NAME, etc., and reify outer function
* activations' call objects, etc. if it's not a global function.
*
* NB: JSFUN_EXPR_CLOSURE reuses JSFUN_STUB_GSOPS, which is an API request flag
* bit only, never stored in fun->flags.
*
* If we need more bits in the future, all flags for FUN_INTERPRETED functions
* can move to u.i.script->flags. For now we use function flag bits to minimize
* pointer-chasing.
*/
#define JSFUN_EXPR_CLOSURE 0x1000 /* expression closure: function(x) x*x */
#define JSFUN_TRACEABLE 0x2000 /* can trace across calls to this native
function; use FUN_TRCINFO if set, function; use FUN_TRCINFO if set,
FUN_CLASP if unset */ FUN_CLASP if unset */
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */ #define JSFUN_EXPR_CLOSURE 0x4000 /* expression closure: function(x)x*x */
#define JSFUN_FLAT_CLOSURE 0x8000 /* flag (aka "display") closure */ #define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure #define JSFUN_SCRIPT_OR_FAST_NATIVE (JSFUN_INTERPRETED | JSFUN_FAST_NATIVE)
optimization level -- see above */
#define FUN_OBJECT(fun) (&(fun)->object) #define FUN_OBJECT(fun) (&(fun)->object)
#define FUN_KIND(fun) ((fun)->flags & JSFUN_KINDMASK) #define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED)
#define FUN_SET_KIND(fun,k) ((fun)->flags = ((fun)->flags & ~JSFUN_KINDMASK) | (k)) #define FUN_SLOW_NATIVE(fun) (!((fun)->flags & JSFUN_SCRIPT_OR_FAST_NATIVE))
#define FUN_INTERPRETED(fun) (FUN_KIND(fun) >= JSFUN_INTERPRETED)
#define FUN_FLAT_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_FLAT_CLOSURE)
#define FUN_NULL_CLOSURE(fun)(FUN_KIND(fun) == JSFUN_NULL_CLOSURE)
#define FUN_SLOW_NATIVE(fun) (!FUN_INTERPRETED(fun) && !((fun)->flags & JSFUN_FAST_NATIVE))
#define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL) #define FUN_SCRIPT(fun) (FUN_INTERPRETED(fun) ? (fun)->u.i.script : NULL)
#define FUN_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL) #define FUN_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL)
#define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \ #define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \
@@ -209,11 +169,11 @@ js_TraceFunction(JSTracer *trc, JSFunction *fun);
extern void extern void
js_FinalizeFunction(JSContext *cx, JSFunction *fun); js_FinalizeFunction(JSContext *cx, JSFunction *fun);
extern JS_REQUIRES_STACK JSObject * extern JSObject *
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent); js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent);
extern JS_REQUIRES_STACK JSObject * extern JSBool
js_NewFlatClosure(JSContext *cx, JSFunction *fun); js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);
extern JSFunction * extern JSFunction *
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native, js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,

View File

@@ -3092,8 +3092,8 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
case JSTVU_WEAK_ROOTS: case JSTVU_WEAK_ROOTS:
TraceWeakRoots(trc, tvr->u.weakRoots); TraceWeakRoots(trc, tvr->u.weakRoots);
break; break;
case JSTVU_COMPILER: case JSTVU_PARSE_CONTEXT:
tvr->u.compiler->trace(trc); js_TraceParseContext(trc, tvr->u.parseContext);
break; break;
case JSTVU_SCRIPT: case JSTVU_SCRIPT:
js_TraceScript(trc, tvr->u.script); js_TraceScript(trc, tvr->u.script);

View File

@@ -71,7 +71,7 @@ DefaultAllocTable(void *pool, size_t size)
} }
static void static void
DefaultFreeTable(void *pool, void *item, size_t size) DefaultFreeTable(void *pool, void *item)
{ {
free(item); free(item);
} }
@@ -121,7 +121,7 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash,
nb = n * sizeof(JSHashEntry *); nb = n * sizeof(JSHashEntry *);
ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb); ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb);
if (!ht->buckets) { if (!ht->buckets) {
allocOps->freeTable(allocPriv, ht, nb); allocOps->freeTable(allocPriv, ht);
return NULL; return NULL;
} }
memset(ht->buckets, 0, nb); memset(ht->buckets, 0, nb);
@@ -153,11 +153,11 @@ JS_HashTableDestroy(JSHashTable *ht)
#ifdef DEBUG #ifdef DEBUG
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]); memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
#endif #endif
allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]); allocOps->freeTable(allocPriv, ht->buckets);
#ifdef DEBUG #ifdef DEBUG
memset(ht, 0xDB, sizeof *ht); memset(ht, 0xDB, sizeof *ht);
#endif #endif
allocOps->freeTable(allocPriv, ht, sizeof *ht); allocOps->freeTable(allocPriv, ht);
} }
/* /*
@@ -198,7 +198,9 @@ Resize(JSHashTable *ht, uint32 newshift)
{ {
size_t nb, nentries, i; size_t nb, nentries, i;
JSHashEntry **oldbuckets, *he, *next, **hep; JSHashEntry **oldbuckets, *he, *next, **hep;
#ifdef DEBUG
size_t nold = NBUCKETS(ht); size_t nold = NBUCKETS(ht);
#endif
JS_ASSERT(newshift < JS_HASH_BITS); JS_ASSERT(newshift < JS_HASH_BITS);
@@ -228,20 +230,17 @@ Resize(JSHashTable *ht, uint32 newshift)
hep = BUCKET_HEAD(ht, he->keyHash); hep = BUCKET_HEAD(ht, he->keyHash);
/* /*
* We do not require unique entries, instead appending he to the * Since he comes from the old table, it must be unique and we
* chain starting at hep. * simply add it to the head of bucket chain without chain lookup.
*/ */
while (*hep) he->next = *hep;
hep = &(*hep)->next;
he->next = NULL;
*hep = he; *hep = he;
} }
} }
#ifdef DEBUG #ifdef DEBUG
memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]); memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]);
#endif #endif
ht->allocOps->freeTable(ht->allocPriv, oldbuckets, ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
nold * sizeof oldbuckets[0]);
return JS_TRUE; return JS_TRUE;
} }

View File

@@ -67,7 +67,7 @@ typedef intN (* JSHashEnumerator)(JSHashEntry *he, intN i, void *arg);
typedef struct JSHashAllocOps { typedef struct JSHashAllocOps {
void * (*allocTable)(void *pool, size_t size); void * (*allocTable)(void *pool, size_t size);
void (*freeTable)(void *pool, void *item, size_t size); void (*freeTable)(void *pool, void *item);
JSHashEntry * (*allocEntry)(void *pool, const void *key); JSHashEntry * (*allocEntry)(void *pool, const void *key);
void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag); void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag);
} JSHashAllocOps; } JSHashAllocOps;

View File

@@ -221,7 +221,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
* *
* So here, on first cache fill for this method, we brand * So here, on first cache fill for this method, we brand
* the scope with a new shape and set the SCOPE_BRANDED * the scope with a new shape and set the SCOPE_BRANDED
* flag. Once this scope flag is set, any write that adds * flag. Once this scope flag is set, any write that adds
* or deletes a function-valued plain old property in * or deletes a function-valued plain old property in
* scope->object will result in shape being regenerated. * scope->object will result in shape being regenerated.
*/ */
@@ -701,7 +701,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
if (!js_GetCallObject(cx, fp)) if (!js_GetCallObject(cx, fp))
return NULL; return NULL;
/* We know we must clone everything on blockChain. */ /* We know we must clone everything on blockChain. */
limitBlock = limitClone = NULL; limitBlock = limitClone = NULL;
} else { } else {
/* /*
@@ -715,7 +715,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
limitClone = OBJ_GET_PARENT(cx, limitClone); limitClone = OBJ_GET_PARENT(cx, limitClone);
JS_ASSERT(limitClone); JS_ASSERT(limitClone);
/* /*
* It may seem like we don't know enough about limitClone to be able * It may seem like we don't know enough about limitClone to be able
* to just grab its prototype as we do here, but it's actually okay. * to just grab its prototype as we do here, but it's actually okay.
* *
@@ -743,7 +743,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
/* /*
* Special-case cloning the innermost block; this doesn't have enough in * Special-case cloning the innermost block; this doesn't have enough in
* common with subsequent steps to include in the loop. * common with subsequent steps to include in the loop.
* *
* We pass fp->scopeChain and not null even if we override the parent slot * We pass fp->scopeChain and not null even if we override the parent slot
* later as null triggers useless calculations of slot's value in * later as null triggers useless calculations of slot's value in
* js_NewObject that js_CloneBlockObject calls. * js_NewObject that js_CloneBlockObject calls.
@@ -785,8 +785,8 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
* If we found a limit block belonging to this frame, then we should have * If we found a limit block belonging to this frame, then we should have
* found it in blockChain. * found it in blockChain.
*/ */
JS_ASSERT_IF(limitBlock && JS_ASSERT_IF(limitBlock &&
OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass && OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
OBJ_GET_PRIVATE(cx, limitClone) == fp, OBJ_GET_PRIVATE(cx, limitClone) == fp,
sharedBlock); sharedBlock);
@@ -2485,16 +2485,16 @@ JS_STATIC_ASSERT(!CAN_DO_FAST_INC_DEC(INT_TO_JSVAL(JSVAL_INT_MAX)));
JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH); JS_STATIC_ASSERT(JSOP_NAME_LENGTH == JSOP_CALLNAME_LENGTH);
JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH); JS_STATIC_ASSERT(JSOP_GETGVAR_LENGTH == JSOP_CALLGVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH); JS_STATIC_ASSERT(JSOP_GETUPVAR_LENGTH == JSOP_CALLUPVAR_LENGTH);
JS_STATIC_ASSERT(JSOP_GETDSLOT_LENGTH == JSOP_CALLDSLOT_LENGTH);
JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH); JS_STATIC_ASSERT(JSOP_GETARG_LENGTH == JSOP_CALLARG_LENGTH);
JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH); JS_STATIC_ASSERT(JSOP_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH); JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
/* /*
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but * Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
* remain distinct for the decompiler. * remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.
*/ */
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH); JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);
/* See TRY_BRANCH_AFTER_COND. */ /* See TRY_BRANCH_AFTER_COND. */
JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH); JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
@@ -2665,8 +2665,7 @@ js_Interpret(JSContext *cx)
/* We had better not be entering the interpreter from JIT-compiled code. */ /* We had better not be entering the interpreter from JIT-compiled code. */
TraceRecorder *tr = TRACE_RECORDER(cx); TraceRecorder *tr = TRACE_RECORDER(cx);
SET_TRACE_RECORDER(cx, NULL); SET_TRACE_RECORDER(cx, NULL);
/* If a recorder is pending and we try to re-enter the interpreter, flag
/* If a recorder is pending and we try to re-enter the interpreter, flag
the recorder to be destroyed when we return. */ the recorder to be destroyed when we return. */
if (tr) { if (tr) {
if (tr->wasDeepAborted()) if (tr->wasDeepAborted())
@@ -2787,8 +2786,8 @@ js_Interpret(JSContext *cx)
js_SetVersion(cx, currentVersion); js_SetVersion(cx, currentVersion);
/* Update the static-link display. */ /* Update the static-link display. */
if (script->staticLevel < JS_DISPLAY_SIZE) { if (script->staticDepth < JS_DISPLAY_SIZE) {
JSStackFrame **disp = &cx->display[script->staticLevel]; JSStackFrame **disp = &cx->display[script->staticDepth];
fp->displaySave = *disp; fp->displaySave = *disp;
*disp = fp; *disp = fp;
} }
@@ -3064,8 +3063,8 @@ js_Interpret(JSContext *cx)
JS_ASSERT(!fp->blockChain); JS_ASSERT(!fp->blockChain);
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0)); JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
if (script->staticLevel < JS_DISPLAY_SIZE) if (script->staticDepth < JS_DISPLAY_SIZE)
cx->display[script->staticLevel] = fp->displaySave; cx->display[script->staticDepth] = fp->displaySave;
if (hookData) { if (hookData) {
JSInterpreterHook hook; JSInterpreterHook hook;
@@ -3393,7 +3392,7 @@ js_Interpret(JSContext *cx)
STORE_OPND(-1, lval); STORE_OPND(-1, lval);
STORE_OPND(-2, rval); STORE_OPND(-2, rval);
END_CASE(JSOP_SWAP) END_CASE(JSOP_SWAP)
BEGIN_CASE(JSOP_PICK) BEGIN_CASE(JSOP_PICK)
i = regs.pc[1]; i = regs.pc[1];
JS_ASSERT(regs.sp - (i+1) >= StackBase(fp)); JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
@@ -4846,13 +4845,13 @@ js_Interpret(JSContext *cx)
regs.sp = vp + 1; regs.sp = vp + 1;
CHECK_INTERRUPT_HANDLER(); CHECK_INTERRUPT_HANDLER();
END_CASE(JSOP_NEW) END_CASE(JSOP_NEW)
BEGIN_CASE(JSOP_CALL) BEGIN_CASE(JSOP_CALL)
BEGIN_CASE(JSOP_EVAL) BEGIN_CASE(JSOP_EVAL)
BEGIN_CASE(JSOP_APPLY) BEGIN_CASE(JSOP_APPLY)
argc = GET_ARGC(regs.pc); argc = GET_ARGC(regs.pc);
vp = regs.sp - (argc + 2); vp = regs.sp - (argc + 2);
lval = *vp; lval = *vp;
if (VALUE_IS_FUNCTION(cx, lval)) { if (VALUE_IS_FUNCTION(cx, lval)) {
obj = JSVAL_TO_OBJECT(lval); obj = JSVAL_TO_OBJECT(lval);
@@ -4954,8 +4953,8 @@ js_Interpret(JSContext *cx)
newifp->frame.dormantNext = NULL; newifp->frame.dormantNext = NULL;
newifp->frame.xmlNamespace = NULL; newifp->frame.xmlNamespace = NULL;
newifp->frame.blockChain = NULL; newifp->frame.blockChain = NULL;
if (script->staticLevel < JS_DISPLAY_SIZE) { if (script->staticDepth < JS_DISPLAY_SIZE) {
JSStackFrame **disp = &cx->display[script->staticLevel]; JSStackFrame **disp = &cx->display[script->staticDepth];
newifp->frame.displaySave = *disp; newifp->frame.displaySave = *disp;
*disp = &newifp->frame; *disp = &newifp->frame;
} }
@@ -5406,6 +5405,7 @@ js_Interpret(JSContext *cx)
END_CASE(JSOP_ONE) END_CASE(JSOP_ONE)
BEGIN_CASE(JSOP_NULL) BEGIN_CASE(JSOP_NULL)
BEGIN_CASE(JSOP_NULLTHIS)
PUSH_OPND(JSVAL_NULL); PUSH_OPND(JSVAL_NULL);
END_CASE(JSOP_NULL) END_CASE(JSOP_NULL)
@@ -5647,13 +5647,11 @@ js_Interpret(JSContext *cx)
uva = JS_SCRIPT_UPVARS(script); uva = JS_SCRIPT_UPVARS(script);
JS_ASSERT(index < uva->length); JS_ASSERT(index < uva->length);
skip = UPVAR_FRAME_SKIP(uva->vector[index]); skip = UPVAR_FRAME_SKIP(uva->vector[index]);
fp2 = cx->display[script->staticLevel - skip]; fp2 = cx->display[script->staticDepth - skip];
JS_ASSERT(fp2->script); JS_ASSERT(fp2->fun && fp2->script);
slot = UPVAR_FRAME_SLOT(uva->vector[index]); slot = UPVAR_FRAME_SLOT(uva->vector[index]);
if (!fp2->fun) { if (slot < fp2->fun->nargs) {
vp = fp2->slots + fp2->script->nfixed;
} else if (slot < fp2->fun->nargs) {
vp = fp2->argv; vp = fp2->argv;
} else { } else {
slot -= fp2->fun->nargs; slot -= fp2->fun->nargs;
@@ -5667,22 +5665,6 @@ js_Interpret(JSContext *cx)
} }
END_CASE(JSOP_GETUPVAR) END_CASE(JSOP_GETUPVAR)
BEGIN_CASE(JSOP_GETDSLOT)
BEGIN_CASE(JSOP_CALLDSLOT)
obj = fp->callee;
JS_ASSERT(obj);
JS_ASSERT(obj->dslots);
index = GET_UINT16(regs.pc);
JS_ASSERT(JS_INITIAL_NSLOTS + index < jsatomid(obj->dslots[-1]));
JS_ASSERT_IF(OBJ_SCOPE(obj)->object == obj,
JS_INITIAL_NSLOTS + index < obj->map->freeslot);
PUSH_OPND(obj->dslots[index]);
if (op == JSOP_CALLDSLOT)
PUSH_OPND(JSVAL_NULL);
END_CASE(JSOP_GETDSLOT)
BEGIN_CASE(JSOP_GETGVAR) BEGIN_CASE(JSOP_GETGVAR)
BEGIN_CASE(JSOP_CALLGVAR) BEGIN_CASE(JSOP_CALLGVAR)
slot = GET_SLOTNO(regs.pc); slot = GET_SLOTNO(regs.pc);
@@ -5809,40 +5791,25 @@ js_Interpret(JSContext *cx)
* function body). * function body).
*/ */
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) { if (!fp->blockChain) {
/*
* Even a null closure needs a parent for principals finding.
* FIXME: bug 476950, although debugger users may also demand
* some kind of scope link for debugger-assisted eval-in-frame.
*/
obj2 = fp->scopeChain; obj2 = fp->scopeChain;
} else { } else {
JS_ASSERT(!FUN_FLAT_CLOSURE(fun)); obj2 = js_GetScopeChain(cx, fp);
if (!obj2)
/* goto error;
* Inline js_GetScopeChain a bit to optimize for the case of a
* top-level function.
*/
if (!fp->blockChain) {
obj2 = fp->scopeChain;
} else {
obj2 = js_GetScopeChain(cx, fp);
if (!obj2)
goto error;
}
} }
/* /*
* If static link is not current scope, clone fun's object to link * If static link is not current scope, clone fun's object to link
* to the current scope via parent. We do this to enable sharing of * to the current scope via parent. This clause exists to enable
* compiled functions among multiple equivalent scopes, amortizing * sharing of compiled functions among multiple equivalent scopes,
* the cost of compilation over a number of executions. Examples * splitting the cost of compilation evenly among the scopes and
* include XUL scripts and event handlers shared among Firefox or * amortizing it over a number of executions. Examples include XUL
* other Mozilla app chrome windows, and user-defined JS functions * scripts and event handlers shared among Mozilla chrome windows,
* precompiled and then shared among requests in server-side JS. * and server-side JS user-defined functions shared among requests.
*/ */
obj = FUN_OBJECT(fun);
if (OBJ_GET_PARENT(cx, obj) != obj2) { if (OBJ_GET_PARENT(cx, obj) != obj2) {
obj = js_CloneFunctionObject(cx, fun, obj2); obj = js_CloneFunctionObject(cx, fun, obj2);
if (!obj) if (!obj)
@@ -5856,7 +5823,6 @@ js_Interpret(JSContext *cx)
*/ */
MUST_FLOW_THROUGH("restore_scope"); MUST_FLOW_THROUGH("restore_scope");
fp->scopeChain = obj; fp->scopeChain = obj;
rval = OBJECT_TO_JSVAL(obj); rval = OBJECT_TO_JSVAL(obj);
/* /*
@@ -5951,144 +5917,133 @@ js_Interpret(JSContext *cx)
} }
END_CASE(JSOP_DEFFUN) END_CASE(JSOP_DEFFUN)
BEGIN_CASE(JSOP_DEFFUN_FC)
LOAD_FUNCTION(0);
obj = js_NewFlatClosure(cx, fun);
if (!obj)
goto error;
rval = OBJECT_TO_JSVAL(obj);
attrs = (fp->flags & JSFRAME_EVAL)
? JSPROP_ENUMERATE
: JSPROP_ENUMERATE | JSPROP_PERMANENT;
flags = JSFUN_GSFLAG2ATTR(fun->flags);
if (flags) {
attrs |= flags | JSPROP_SHARED;
rval = JSVAL_VOID;
}
parent = fp->varobj;
JS_ASSERT(parent);
id = ATOM_TO_JSID(fun->atom);
ok = js_CheckRedeclaration(cx, parent, id, attrs, NULL, NULL);
if (ok) {
if (attrs == JSPROP_ENUMERATE) {
JS_ASSERT(fp->flags & JSFRAME_EVAL);
ok = OBJ_SET_PROPERTY(cx, parent, id, &rval);
} else {
JS_ASSERT(attrs & JSPROP_PERMANENT);
ok = OBJ_DEFINE_PROPERTY(cx, parent, id, rval,
(flags & JSPROP_GETTER)
? JS_EXTENSION (JSPropertyOp) obj
: JS_PropertyStub,
(flags & JSPROP_SETTER)
? JS_EXTENSION (JSPropertyOp) obj
: JS_PropertyStub,
attrs,
NULL);
}
}
if (!ok) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
goto error;
}
END_CASE(JSOP_DEFFUN_FC)
BEGIN_CASE(JSOP_DEFLOCALFUN) BEGIN_CASE(JSOP_DEFLOCALFUN)
LOAD_FUNCTION(SLOTNO_LEN);
/* /*
* Define a local function (i.e., one nested at the top level of * Define a local function (i.e., one nested at the top level of
* another function), parented by the current scope chain, stored * another function), parented by the current scope chain, and
* in a local variable slot that the compiler allocated. This is * stored in a local variable slot that the compiler allocated.
* an optimization over JSOP_DEFFUN that avoids requiring a call * This is an optimization over JSOP_DEFFUN that avoids requiring
* object for the outer function's activation. * a call object for the outer function's activation.
*/ */
LOAD_FUNCTION(SLOTNO_LEN); slot = GET_SLOTNO(regs.pc);
JS_ASSERT(FUN_INTERPRETED(fun));
JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) { parent = js_GetScopeChain(cx, fp);
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); if (!parent)
goto error;
obj = FUN_OBJECT(fun);
if (OBJ_GET_PARENT(cx, obj) != parent) {
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
js_AbortRecording(cx, "DEFLOCALFUN for closure");
#endif
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj) if (!obj)
goto error; goto error;
} else {
parent = js_GetScopeChain(cx, fp);
if (!parent)
goto error;
if (OBJ_GET_PARENT(cx, obj) != parent) {
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
js_AbortRecording(cx, "DEFLOCALFUN for closure");
#endif
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj)
goto error;
}
} }
slot = GET_SLOTNO(regs.pc);
TRACE_2(DefLocalFunSetSlot, slot, obj); TRACE_2(DefLocalFunSetSlot, slot, obj);
fp->slots[slot] = OBJECT_TO_JSVAL(obj); fp->slots[slot] = OBJECT_TO_JSVAL(obj);
END_CASE(JSOP_DEFLOCALFUN) END_CASE(JSOP_DEFLOCALFUN)
BEGIN_CASE(JSOP_DEFLOCALFUN_FC) BEGIN_CASE(JSOP_ANONFUNOBJ)
LOAD_FUNCTION(SLOTNO_LEN);
obj = js_NewFlatClosure(cx, fun);
if (!obj)
goto error;
slot = GET_SLOTNO(regs.pc);
TRACE_2(DefLocalFunSetSlot, slot, obj);
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
END_CASE(JSOP_DEFLOCALFUN_FC)
BEGIN_CASE(JSOP_LAMBDA)
/* Load the specified function object literal. */ /* Load the specified function object literal. */
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
obj = FUN_OBJECT(fun);
if (FUN_NULL_CLOSURE(fun)) { /* If re-parenting, push a clone of the function object. */
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain); parent = js_GetScopeChain(cx, fp);
if (!parent)
goto error;
obj = FUN_OBJECT(fun);
if (OBJ_GET_PARENT(cx, obj) != parent) {
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj) if (!obj)
goto error; goto error;
} else {
parent = js_GetScopeChain(cx, fp);
if (!parent)
goto error;
/* If re-parenting, push a clone of the function object. */
if (OBJ_GET_PARENT(cx, obj) != parent) {
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj)
goto error;
}
} }
PUSH_OPND(OBJECT_TO_JSVAL(obj)); PUSH_OPND(OBJECT_TO_JSVAL(obj));
END_CASE(JSOP_LAMBDA) END_CASE(JSOP_ANONFUNOBJ)
BEGIN_CASE(JSOP_LAMBDA_FC) BEGIN_CASE(JSOP_NAMEDFUNOBJ)
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
obj = js_NewFlatClosure(cx, fun); /*
* ECMA ed. 3 FunctionExpression: function Identifier [etc.].
*
* 1. Create a new object as if by the expression new Object().
* 2. Add Result(1) to the front of the scope chain.
*
* Step 2 is achieved by making the new object's parent be the
* current scope chain, and then making the new object the parent
* of the Function object clone.
*/
obj2 = js_GetScopeChain(cx, fp);
if (!obj2)
goto error;
parent = js_NewObject(cx, &js_ObjectClass, NULL, obj2, 0);
if (!parent)
goto error;
/*
* 3. Create a new Function object as specified in section 13.2
* with [parameters and body specified by the function expression
* that was parsed by the compiler into a Function object, and
* saved in the script's atom map].
*
* Protect parent from the GC.
*/
fp->scopeChain = parent;
obj = js_CloneFunctionObject(cx, fun, parent);
if (!obj) if (!obj)
goto error; goto error;
PUSH_OPND(OBJECT_TO_JSVAL(obj)); /*
END_CASE(JSOP_LAMBDA_FC) * Protect obj from any GC hiding below OBJ_DEFINE_PROPERTY. All
* paths from here must flow through the "Restore fp->scopeChain"
* code below the OBJ_DEFINE_PROPERTY call.
*/
MUST_FLOW_THROUGH("restore2");
fp->scopeChain = obj;
rval = OBJECT_TO_JSVAL(obj);
BEGIN_CASE(JSOP_CALLEE) /*
PUSH_OPND(OBJECT_TO_JSVAL(fp->callee)); * 4. Create a property in the object Result(1). The property's
END_CASE(JSOP_CALLEE) * name is [fun->atom, the identifier parsed by the compiler],
* value is Result(3), and attributes are { DontDelete, ReadOnly }.
*/
attrs = JSFUN_GSFLAG2ATTR(fun->flags);
if (attrs) {
attrs |= JSPROP_SHARED;
rval = JSVAL_VOID;
}
ok = OBJ_DEFINE_PROPERTY(cx, parent, ATOM_TO_JSID(fun->atom), rval,
(attrs & JSPROP_GETTER)
? JS_EXTENSION (JSPropertyOp) obj
: JS_PropertyStub,
(attrs & JSPROP_SETTER)
? JS_EXTENSION (JSPropertyOp) obj
: JS_PropertyStub,
attrs |
JSPROP_ENUMERATE | JSPROP_PERMANENT |
JSPROP_READONLY,
NULL);
/* Restore fp->scopeChain now that obj is defined in parent. */
MUST_FLOW_LABEL(restore2)
fp->scopeChain = obj2;
if (!ok) {
cx->weakRoots.newborn[GCX_OBJECT] = NULL;
goto error;
}
/*
* 5. Remove Result(1) from the front of the scope chain [no-op].
* 6. Return Result(3).
*/
PUSH_OPND(OBJECT_TO_JSVAL(obj));
END_CASE(JSOP_NAMEDFUNOBJ)
#if JS_HAS_GETTER_SETTER #if JS_HAS_GETTER_SETTER
BEGIN_CASE(JSOP_GETTER) BEGIN_CASE(JSOP_GETTER)
@@ -6883,7 +6838,7 @@ js_Interpret(JSContext *cx)
#ifdef DEBUG #ifdef DEBUG
JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass); JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain); uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
JS_ASSERT(blockDepth <= StackDepth(script)); JS_ASSERT(blockDepth <= StackDepth(script));
#endif #endif
/* /*
@@ -7024,6 +6979,15 @@ js_Interpret(JSContext *cx)
L_JSOP_DEFXMLNS: L_JSOP_DEFXMLNS:
# endif # endif
L_JSOP_UNUSED203:
L_JSOP_UNUSED204:
L_JSOP_UNUSED205:
L_JSOP_UNUSED206:
L_JSOP_UNUSED207:
L_JSOP_UNUSED208:
L_JSOP_UNUSED209:
L_JSOP_UNUSED219:
#else /* !JS_THREADED_INTERP */ #else /* !JS_THREADED_INTERP */
default: default:
#endif #endif
@@ -7048,7 +7012,7 @@ js_Interpret(JSContext *cx)
// To keep things simple, we hard-code imacro exception handlers here. // To keep things simple, we hard-code imacro exception handlers here.
if (*fp->imacpc == JSOP_NEXTITER) { if (*fp->imacpc == JSOP_NEXTITER) {
// pc may point to JSOP_DUP here due to bug 474854. // pc may point to JSOP_DUP here due to bug 474854.
JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP || *regs.pc == JSOP_TRUE); JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
if (js_ValueIsStopIteration(cx->exception)) { if (js_ValueIsStopIteration(cx->exception)) {
cx->throwing = JS_FALSE; cx->throwing = JS_FALSE;
cx->exception = JSVAL_VOID; cx->exception = JSVAL_VOID;
@@ -7067,9 +7031,9 @@ js_Interpret(JSContext *cx)
JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length); JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
#ifdef JS_TRACER #ifdef JS_TRACER
/* /*
* This abort could be weakened to permit tracing through exceptions that * This abort could be weakened to permit tracing through exceptions that
* are thrown and caught within a loop, with the co-operation of the tracer. * are thrown and caught within a loop, with the co-operation of the tracer.
* For now just bail on any sign of trouble. * For now just bail on any sign of trouble.
*/ */
if (TRACE_RECORDER(cx)) if (TRACE_RECORDER(cx))
@@ -7271,8 +7235,8 @@ js_Interpret(JSContext *cx)
} }
/* Undo the remaining effects committed on entry to js_Interpret. */ /* Undo the remaining effects committed on entry to js_Interpret. */
if (script->staticLevel < JS_DISPLAY_SIZE) if (script->staticDepth < JS_DISPLAY_SIZE)
cx->display[script->staticLevel] = fp->displaySave; cx->display[script->staticDepth] = fp->displaySave;
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave); JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
if (cx->version == currentVersion && currentVersion != originalVersion) if (cx->version == currentVersion && currentVersion != originalVersion)
js_SetVersion(cx, originalVersion); js_SetVersion(cx, originalVersion);

View File

@@ -128,7 +128,7 @@ struct JSStackFrame {
JSStackFrame *dormantNext; /* next dormant frame chain */ JSStackFrame *dormantNext; /* next dormant frame chain */
JSObject *xmlNamespace; /* null or default xml namespace in E4X */ JSObject *xmlNamespace; /* null or default xml namespace in E4X */
JSStackFrame *displaySave; /* previous value of display entry for JSStackFrame *displaySave; /* previous value of display entry for
script->staticLevel */ script->staticDepth */
#ifdef DEBUG #ifdef DEBUG
jsrefcount pcDisabledSave; /* for balanced property cache control */ jsrefcount pcDisabledSave; /* for balanced property cache control */
#endif #endif

View File

@@ -676,7 +676,7 @@ generator_trace(JSTracer *trc, JSObject *obj)
js_TraceStackFrame(trc, &gen->frame); js_TraceStackFrame(trc, &gen->frame);
} }
JS_FRIEND_DATA(JSClass) js_GeneratorClass = { JSClass js_GeneratorClass = {
js_Generator_str, js_Generator_str,
JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS | JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator), JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),

View File

@@ -121,9 +121,9 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp);
#endif #endif
extern JS_FRIEND_API(JSClass) js_GeneratorClass; extern JSClass js_GeneratorClass;
extern JSClass js_IteratorClass; extern JSClass js_IteratorClass;
extern JSClass js_StopIterationClass; extern JSClass js_StopIterationClass;
static inline bool static inline bool
js_ValueIsStopIteration(jsval v) js_ValueIsStopIteration(jsval v)

View File

@@ -136,10 +136,10 @@ struct JSTitle {
#ifdef JS_DEBUG_TITLE_LOCKS #ifdef JS_DEBUG_TITLE_LOCKS
#define JS_SET_OBJ_INFO(obj_, file_, line_) \ #define JS_SET_OBJ_INFO(obj_, file_, line_) \
JS_SET_SCOPE_INFO(OBJ_SCOPE(obj_), file_, line_) JS_SET_SCOPE_INFO(OBJ_SCOPE(obj_), file_, line_)
#define JS_SET_SCOPE_INFO(scope_, file_, line_) \ #define JS_SET_SCOPE_INFO(scope_, file_, line_) \
js_SetScopeInfo(scope_, file_, line_) js_SetScopeInfo(scope_, file_, line_)
#endif #endif
@@ -157,25 +157,25 @@ struct JSTitle {
* are for optimizations above the JSObjectOps layer, under which object locks * are for optimizations above the JSObjectOps layer, under which object locks
* normally hide. * normally hide.
*/ */
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \ #define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
? (void)0 \ ? (void)0 \
: (js_LockObj(cx, obj), \ : (js_LockObj(cx, obj), \
JS_SET_OBJ_INFO(obj,__FILE__,__LINE__))) JS_SET_OBJ_INFO(obj,__FILE__,__LINE__)))
#define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \ #define JS_UNLOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
? (void)0 : js_UnlockObj(cx, obj)) ? (void)0 : js_UnlockObj(cx, obj))
#define JS_LOCK_TITLE(cx,title) \ #define JS_LOCK_TITLE(cx,title) \
((title)->ownercx == (cx) ? (void)0 \ ((title)->ownercx == (cx) ? (void)0 \
: (js_LockTitle(cx, (title)), \ : (js_LockTitle(cx, (title)), \
JS_SET_TITLE_INFO(title,__FILE__,__LINE__))) JS_SET_TITLE_INFO(title,__FILE__,__LINE__)))
#define JS_UNLOCK_TITLE(cx,title) ((title)->ownercx == (cx) ? (void)0 \ #define JS_UNLOCK_TITLE(cx,title) ((title)->ownercx == (cx) ? (void)0 \
: js_UnlockTitle(cx, title)) : js_UnlockTitle(cx, title))
#define JS_LOCK_SCOPE(cx,scope) JS_LOCK_TITLE(cx,&(scope)->title) #define JS_LOCK_SCOPE(cx,scope) JS_LOCK_TITLE(cx,&(scope)->title)
#define JS_UNLOCK_SCOPE(cx,scope) JS_UNLOCK_TITLE(cx,&(scope)->title) #define JS_UNLOCK_SCOPE(cx,scope) JS_UNLOCK_TITLE(cx,&(scope)->title)
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \ #define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
js_TransferTitle(cx, &scope->title, &newscope->title) js_TransferTitle(cx, &scope->title, &newscope->title)

View File

@@ -1375,7 +1375,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
tcflags = TCF_COMPILE_N_GO; tcflags = TCF_COMPILE_N_GO;
if (caller) { if (caller) {
tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1); tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
principals = JS_EvalFramePrincipals(cx, fp, caller); principals = JS_EvalFramePrincipals(cx, fp, caller);
file = js_ComputeFilename(cx, caller, principals, &line); file = js_ComputeFilename(cx, caller, principals, &line);
} else { } else {
@@ -1402,7 +1402,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
script->principals->subsume(script->principals, principals)))) { script->principals->subsume(script->principals, principals)))) {
/* /*
* Get the prior (cache-filling) eval's saved caller function. * Get the prior (cache-filling) eval's saved caller function.
* See JSCompiler::compileScript in jsparse.cpp. * See js_CompileScript in jsparse.cpp.
*/ */
JSFunction *fun; JSFunction *fun;
JS_GET_SCRIPT_FUNCTION(script, 0, fun); JS_GET_SCRIPT_FUNCTION(script, 0, fun);
@@ -1410,7 +1410,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
if (fun == caller->fun) { if (fun == caller->fun) {
/* /*
* Get the source string passed for safekeeping in the * Get the source string passed for safekeeping in the
* atom map by the prior eval to JSCompiler::compileScript. * atom map by the prior eval to js_CompileScript.
*/ */
JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]); JSString *src = ATOM_TO_STRING(script->atomMap.vector[0]);
@@ -1454,9 +1454,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
} }
if (!script) { if (!script) {
script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags, script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
JSSTRING_CHARS(str), JSSTRING_LENGTH(str), JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
NULL, file, line, str); NULL, file, line, str);
if (!script) { if (!script) {
ok = JS_FALSE; ok = JS_FALSE;
goto out; goto out;

View File

@@ -151,41 +151,37 @@ struct JSObjectMap {
#define JS_INITIAL_NSLOTS 5 #define JS_INITIAL_NSLOTS 5
/* /*
* JSObject struct, with members sized to fit in 32 bytes on 32-bit targets, * When JSObject.dslots is not null, JSObject.dslots[-1] records the number of
* 64 bytes on 64-bit systems. The JSFunction struct is an extension of this * available slots.
* struct allocated from a larger GC size-class.
*
* The classword member stores the JSClass pointer for this object, with the
* least two bits encoding whether this object is a "delegate" or a "system"
* object.
*
* An object is a delegate if it is on another object's prototype (linked by
* JSSLOT_PROTO) or scope (JSSLOT_PARENT) chain, and therefore the delegate
* might be asked implicitly to get or set a property on behalf of another
* object. Delegates may be accessed directly too, as may any object, but only
* those objects linked after the head of any prototype or scope chain are
* flagged as delegates. This definition helps to optimize shape-based property
* cache invalidation (see Purge{Scope,Proto}Chain in jsobj.cpp).
*
* The meaning of the system object bit is defined by the API client. It is
* set in JS_NewSystemObject and is queried by JS_IsSystemObject (jsdbgapi.h),
* but it has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM
* and JS_FlagScriptFilenamePrefix (also exported via jsdbgapi.h) are intended
* to be complementary to this bit, but it is up to the API client to implement
* any such association.
*
* Both these classword tag bits are initially zero; they may be set or queried
* using the STOBJ_(IS|SET)_(DELEGATE|SYSTEM) macros.
*
* The dslots member is null or a pointer into a dynamically allocated vector
* of jsvals for reserved and dynamic slots. If dslots is not null, dslots[-1]
* records the number of available slots.
*/ */
struct JSObject { struct JSObject {
JSObjectMap *map; /* propery map, see jsscope.h */ JSObjectMap *map;
jsuword classword; /* classword, see above */
jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */ /*
jsval *dslots; /* dynamically allocated slots */ * Stores the JSClass* for this object, with the two lowest bits encoding
* whether this object is a delegate or a system object.
*
* A delegate is an object linked on another object's prototype
* (JSSLOT_PROTO) or scope (JSSLOT_PARENT) chain, which might be implicitly
* asked to get or set a property on behalf of another object. Delegates
* may be accessed directly too, as might any object, but only those
* objects linked after the head of a prototype or scope chain are
* delegates. This definition helps to optimize shape-based property cache
* purging (see Purge{Scope,Proto}Chain in jsobj.cpp).
*
* The meaning of the system object bit is defined by the API client. It is
* set in JS_NewSystemObject and is queried by JS_IsSystemObject, but it
* has no intrinsic meaning to SpiderMonkey. Further, JSFILENAME_SYSTEM and
* JS_FlagScriptFilenamePrefix are intended to be complementary to this
* bit, but it is up to the API client to implement any such association.
*
* Both bits are initially zero and may be set or queried using the
* STOBJ_(IS|SET)_(DELEGATE|SYSTEM) macros.
*/
jsuword classword;
jsval fslots[JS_INITIAL_NSLOTS];
jsval *dslots; /* dynamically allocated slots */
}; };
#define JSSLOT_PROTO 0 #define JSSLOT_PROTO 0

View File

@@ -266,49 +266,35 @@ js_DumpScript(JSContext *cx, JSScript *script)
const char * const char *
ToDisassemblySource(JSContext *cx, jsval v) ToDisassemblySource(JSContext *cx, jsval v)
{ {
if (!JSVAL_IS_PRIMITIVE(v)) { JSObject *obj;
JSObject *obj = JSVAL_TO_OBJECT(v); JSScopeProperty *sprop;
JSClass *clasp = OBJ_GET_CLASS(cx, obj); char *source;
const char *bytes;
JSString *str;
if (clasp == &js_BlockClass) { if (!JSVAL_IS_PRIMITIVE(v)) {
char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj)); obj = JSVAL_TO_OBJECT(v);
for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProp; if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
sprop; source = JS_sprintf_append(NULL, "depth %d {",
OBJ_BLOCK_DEPTH(cx, obj));
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
sprop = sprop->parent) { sprop = sprop->parent) {
const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id)); bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
if (!bytes) if (!bytes)
return NULL; return NULL;
source = JS_sprintf_append(source, "%s: %d%s", source = JS_sprintf_append(source, "%s: %d%s",
bytes, sprop->shortid, bytes, sprop->shortid,
sprop->parent ? ", " : ""); sprop->parent ? ", " : "");
} }
source = JS_sprintf_append(source, "}"); source = JS_sprintf_append(source, "}");
if (!source) if (!source)
return NULL; return NULL;
str = JS_NewString(cx, source, strlen(source));
JSString *str = JS_NewString(cx, source, strlen(source));
if (!str) if (!str)
return NULL; return NULL;
return js_GetStringBytes(cx, str); return js_GetStringBytes(cx, str);
} }
if (clasp == &js_FunctionClass) {
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, obj);
JSString *str = JS_DecompileFunction(cx, fun, JS_DONT_PRETTY_PRINT);
if (!str)
return NULL;
return js_GetStringBytes(cx, str);
}
if (clasp == &js_RegExpClass) {
JSAutoTempValueRooter tvr(cx);
if (!js_regexp_toString(cx, obj, tvr.addr()))
return NULL;
return js_GetStringBytes(cx, JSVAL_TO_STRING(tvr.value()));
}
} }
return js_ValueToPrintableSource(cx, v); return js_ValueToPrintableSource(cx, v);
} }
@@ -2802,14 +2788,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break; break;
} }
case JSOP_GETUPVAR:
case JSOP_CALLUPVAR: case JSOP_CALLUPVAR:
case JSOP_GETDSLOT: case JSOP_GETUPVAR:
case JSOP_CALLDSLOT: JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
if (!jp->fun) {
JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN); if (!jp->fun)
JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun); JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun);
}
if (!jp->localNames) if (!jp->localNames)
jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool); jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
@@ -2961,7 +2945,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
: SprintCString(&ss->sprinter, js_yield_str); : SprintCString(&ss->sprinter, js_yield_str);
break; break;
} }
#if JS_HAS_GENERATOR_EXPRS #if JS_HAS_GENERATOR_EXPRS
LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN); LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
/* FALL THROUGH */ /* FALL THROUGH */
@@ -3008,6 +2991,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
break; break;
--pos; --pos;
} }
JS_ASSERT_IF(saveop == JSOP_ARRAYPUSH,
jp->script->nfixed + pos == GET_UINT16(pc));
#if JS_HAS_GENERATOR_EXPRS #if JS_HAS_GENERATOR_EXPRS
if (saveop == JSOP_YIELD) { if (saveop == JSOP_YIELD) {
@@ -3035,9 +3020,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* Array comprehension: retract the sprinter to the beginning * Array comprehension: retract the sprinter to the beginning
* of the array initialiser and decompile "[<rval> for ...]". * of the array initialiser and decompile "[<rval> for ...]".
*/ */
JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT); LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
start = ss->offsets[pos]; start = ss->offsets[pos];
LOCAL_ASSERT(ss->sprinter.base[start] == '[' || LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
ss->sprinter.base[start] == '#'); ss->sprinter.base[start] == '#');
@@ -3053,7 +3036,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
todo = -2; todo = -2;
break; break;
} }
#endif /* JS_HAS_GENERATORS */ #endif
case JSOP_THROWING: case JSOP_THROWING:
todo = -2; todo = -2;
@@ -3105,12 +3088,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
JS_ASSERT(pc[cond] == JSOP_NEXTITER); JS_ASSERT(pc[cond] == JSOP_NEXTITER);
DECOMPILE_CODE(pc + oplen, next - oplen); DECOMPILE_CODE(pc + oplen, next - oplen);
lval = POP_STR(); lval = POP_STR();
LOCAL_ASSERT(ss->top >= 2);
if (ss->inArrayInit || ss->inGenExp) { if (ss->inArrayInit || ss->inGenExp) {
(void) PopOff(ss, JSOP_NOP); (void) PopOff(ss, JSOP_NOP);
rval = TOP_STR(); rval = TOP_STR();
if (ss->top >= 2 && ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) { LOCAL_ASSERT(ss->top >= 2);
if (ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP; ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
if (Sprint(&ss->sprinter, " %s (%s in %s)", if (Sprint(&ss->sprinter, " %s (%s in %s)",
foreach ? js_for_each_str : js_for_str, foreach ? js_for_each_str : js_for_str,
@@ -3126,6 +3108,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
*/ */
todo = ss->offsets[ss->top - 1]; todo = ss->offsets[ss->top - 1];
} else { } else {
LOCAL_ASSERT(ss->opcodes[ss->top - 2] == JSOP_ENTERBLOCK);
todo = Sprint(&ss->sprinter, " %s (%s in %s)", todo = Sprint(&ss->sprinter, " %s (%s in %s)",
foreach ? js_for_each_str : js_for_str, foreach ? js_for_each_str : js_for_str,
lval, rval); lval, rval);
@@ -3147,7 +3130,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
jp->indent -= 4; jp->indent -= 4;
js_printf(jp, "\t}\n"); js_printf(jp, "\t}\n");
} }
pc += tail; pc += tail;
LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX); LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
len = js_CodeSpec[*pc].length; len = js_CodeSpec[*pc].length;
@@ -3969,39 +3951,26 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
todo = STR2OFF(&ss->sprinter, rval); todo = STR2OFF(&ss->sprinter, rval);
break; break;
case JSOP_LAMBDA: case JSOP_ANONFUNOBJ:
case JSOP_LAMBDA_FC:
#if JS_HAS_GENERATOR_EXPRS #if JS_HAS_GENERATOR_EXPRS
sn = js_GetSrcNote(jp->script, pc); sn = js_GetSrcNote(jp->script, pc);
if (sn && SN_TYPE(sn) == SRC_GENEXP) { if (sn && SN_TYPE(sn) == SRC_GENEXP) {
void *mark;
jsuword *innerLocalNames, *outerLocalNames;
JSScript *inner, *outer; JSScript *inner, *outer;
void *mark;
SprintStack ss2; SprintStack ss2;
JSFunction *outerfun;
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
inner = fun->u.i.script;
/* /*
* All allocation when decompiling is LIFO, using malloc * All allocation when decompiling is LIFO, using malloc
* or, more commonly, arena-allocating from cx->tempPool. * or, more commonly, arena-allocating from cx->tempPool.
* Therefore after InitSprintStack succeeds, we must * After InitSprintStack succeeds, we must release to mark
* release to mark before returning. * before returning.
*/ */
mark = JS_ARENA_MARK(&cx->tempPool); mark = JS_ARENA_MARK(&cx->tempPool);
uintN n = JS_GET_LOCAL_NAME_COUNT(fun); if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner)))
if (n == 0) {
innerLocalNames = NULL;
} else {
innerLocalNames = js_GetLocalNameArray(cx, fun, &cx->tempPool);
if (!innerLocalNames)
return NULL;
}
inner = fun->u.i.script;
if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner))) {
JS_ARENA_RELEASE(&cx->tempPool, mark);
return NULL; return NULL;
}
ss2.inGenExp = JS_TRUE; ss2.inGenExp = JS_TRUE;
/* /*
@@ -4012,27 +3981,20 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* string pushed on ss2. * string pushed on ss2.
*/ */
outer = jp->script; outer = jp->script;
outerfun = jp->fun;
outerLocalNames = jp->localNames;
LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length); LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
jp->script = inner; jp->script = inner;
jp->fun = fun; if (!Decompile(&ss2, inner->code, inner->length, JSOP_NOP)) {
jp->localNames = innerLocalNames;
ok = Decompile(&ss2, inner->code, inner->length, JSOP_NOP) != NULL;
jp->script = outer;
jp->fun = outerfun;
jp->localNames = outerLocalNames;
if (!ok) {
JS_ARENA_RELEASE(&cx->tempPool, mark); JS_ARENA_RELEASE(&cx->tempPool, mark);
return NULL; return NULL;
} }
jp->script = outer;
/* /*
* Advance over this op and its global |this| push, and * Advance over this op and its global |this| push, and
* arrange to advance over the call to this lambda. * arrange to advance over the call to this lambda.
*/ */
pc += len; pc += len;
LOCAL_ASSERT(*pc == JSOP_NULL); LOCAL_ASSERT(*pc == JSOP_NULL || *pc == JSOP_NULLTHIS);
pc += JSOP_NULL_LENGTH; pc += JSOP_NULL_LENGTH;
LOCAL_ASSERT(*pc == JSOP_CALL); LOCAL_ASSERT(*pc == JSOP_CALL);
LOCAL_ASSERT(GET_ARGC(pc) == 0); LOCAL_ASSERT(GET_ARGC(pc) == 0);
@@ -4073,7 +4035,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* anonymous function, so it doesn't get decompiled as * anonymous function, so it doesn't get decompiled as
* a generator function in a getter or setter context. * a generator function in a getter or setter context.
* The precedence level is the same for JSOP_NAME and * The precedence level is the same for JSOP_NAME and
* JSOP_LAMBDA. * JSOP_ANONFUNOBJ.
*/ */
LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec == LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
js_CodeSpec[saveop].prec); js_CodeSpec[saveop].prec);
@@ -4096,6 +4058,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
#endif /* JS_HAS_GENERATOR_EXPRS */ #endif /* JS_HAS_GENERATOR_EXPRS */
/* FALL THROUGH */ /* FALL THROUGH */
case JSOP_NAMEDFUNOBJ:
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
{ {
uintN indent = JS_DONT_PRETTY_PRINT; uintN indent = JS_DONT_PRETTY_PRINT;
@@ -4104,7 +4067,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
* Always parenthesize expression closures. We can't force * Always parenthesize expression closures. We can't force
* saveop to a low-precedence op to arrange for auto-magic * saveop to a low-precedence op to arrange for auto-magic
* parenthesization without confusing getter/setter code * parenthesization without confusing getter/setter code
* that checks for JSOP_LAMBDA. * that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ.
*/ */
if (!(fun->flags & JSFUN_EXPR_CLOSURE)) if (!(fun->flags & JSFUN_EXPR_CLOSURE))
indent |= JS_IN_GROUP_CONTEXT; indent |= JS_IN_GROUP_CONTEXT;
@@ -4116,11 +4079,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
todo = SprintString(&ss->sprinter, str); todo = SprintString(&ss->sprinter, str);
break; break;
case JSOP_CALLEE:
JS_ASSERT(jp->fun && jp->fun->atom);
todo = SprintString(&ss->sprinter, ATOM_TO_STRING(jp->fun->atom));
break;
case JSOP_OBJECT: case JSOP_OBJECT:
LOAD_OBJECT(0); LOAD_OBJECT(0);
LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass); LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
@@ -4336,7 +4294,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
} }
case JSOP_DEFFUN: case JSOP_DEFFUN:
case JSOP_DEFFUN_FC:
LOAD_FUNCTION(0); LOAD_FUNCTION(0);
todo = -2; todo = -2;
goto do_function; goto do_function;
@@ -4508,8 +4465,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
!ATOM_IS_STRING(atom) || !ATOM_IS_STRING(atom) ||
!ATOM_IS_IDENTIFIER(atom) || !ATOM_IS_IDENTIFIER(atom) ||
ATOM_IS_KEYWORD(atom) || ATOM_IS_KEYWORD(atom) ||
(ss->opcodes[ss->top+1] != JSOP_LAMBDA && (ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ &&
ss->opcodes[ss->top+1] != JSOP_LAMBDA_FC)) { ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) {
todo = Sprint(&ss->sprinter, "%s%s%s %s: %s", todo = Sprint(&ss->sprinter, "%s%s%s %s: %s",
lval, lval,
maybeComma, maybeComma,

View File

@@ -65,13 +65,13 @@ typedef enum JSOp {
*/ */
#define JOF_BYTE 0 /* single bytecode, no immediates */ #define JOF_BYTE 0 /* single bytecode, no immediates */
#define JOF_JUMP 1 /* signed 16-bit jump offset immediate */ #define JOF_JUMP 1 /* signed 16-bit jump offset immediate */
#define JOF_ATOM 2 /* unsigned 16-bit constant index */ #define JOF_ATOM 2 /* unsigned 16-bit constant pool index */
#define JOF_UINT16 3 /* unsigned 16-bit immediate operand */ #define JOF_UINT16 3 /* unsigned 16-bit immediate operand */
#define JOF_TABLESWITCH 4 /* table switch */ #define JOF_TABLESWITCH 4 /* table switch */
#define JOF_LOOKUPSWITCH 5 /* lookup switch */ #define JOF_LOOKUPSWITCH 5 /* lookup switch */
#define JOF_QARG 6 /* quickened get/set function argument ops */ #define JOF_QARG 6 /* quickened get/set function argument ops */
#define JOF_LOCAL 7 /* var or block-local variable */ #define JOF_LOCAL 7 /* var or block-local variable */
#define JOF_SLOTATOM 8 /* uint16 slot + constant index */ #define JOF_SLOTATOM 8 /* uint16 slot index + constant pool index */
#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */ #define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */
#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */ #define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */
#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */ #define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */
@@ -79,11 +79,10 @@ typedef enum JSOp {
#define JOF_UINT8 13 /* uint8 immediate, e.g. top 8 bits of 24-bit #define JOF_UINT8 13 /* uint8 immediate, e.g. top 8 bits of 24-bit
atom index */ atom index */
#define JOF_INT32 14 /* int32 immediate operand */ #define JOF_INT32 14 /* int32 immediate operand */
#define JOF_OBJECT 15 /* unsigned 16-bit object index */ #define JOF_OBJECT 15 /* unsigned 16-bit object pool index */
#define JOF_SLOTOBJECT 16 /* uint16 slot index + object index */ #define JOF_SLOTOBJECT 16 /* uint16 slot index + object pool index */
#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */ #define JOF_REGEXP 17 /* unsigned 16-bit regexp pool index */
#define JOF_INT8 18 /* int8 immediate operand */ #define JOF_INT8 18 /* int8 immediate operand */
#define JOF_ATOMOBJECT 19 /* uint16 constant index + object index */
#define JOF_TYPEMASK 0x001f /* mask for above immediate types */ #define JOF_TYPEMASK 0x001f /* mask for above immediate types */
#define JOF_NAME (1U<<5) /* name operation */ #define JOF_NAME (1U<<5) /* name operation */

View File

@@ -84,7 +84,7 @@
* 17 new JSOP_NEW * 17 new JSOP_NEW
* 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc. * 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc.
* 19 x, null, JSOP_NAME, JSOP_NULL, etc.; * 19 x, null, JSOP_NAME, JSOP_NULL, etc.;
* function (...) ... and JSOP_LAMBDA * function (...) ... and JSOP_ANONFUNOBJ, JSOP_NAMEDFUNOBJ
* *
* The push-numeric-constant operators, JSOP_ZERO, JSOP_DOUBLE, etc., have * The push-numeric-constant operators, JSOP_ZERO, JSOP_DOUBLE, etc., have
* lower precedence than the member operators emitted for the . operator, to * lower precedence than the member operators emitted for the . operator, to
@@ -197,11 +197,8 @@ OPDEF(JSOP_LOOKUPSWITCH, 71, "lookupswitch", NULL, -1, 1, 0, 0, JOF_LOOKUP
OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) OPDEF(JSOP_STRICTEQ, 72, "stricteq", "===", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC) OPDEF(JSOP_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
/* /* Variant of JSOP_NULL for default (global) |this| parameter pushing. */
* Host object extension: given 'o.item(i) = j', the left-hand side compiles OPDEF(JSOP_NULLTHIS, 74, "nullthis", "nullthis", 1, 0, 1, 19, JOF_BYTE)
* JSOP_SETCALL, rather than JSOP_CALL.
*/
OPDEF(JSOP_SETCALL, 74, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET)
/* /*
* JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits * JSOP_ITER sets up a for-in or for-each-in loop using the JSITER_* flag bits
@@ -328,11 +325,11 @@ OPDEF(JSOP_DEFFUN, 125,"deffun", NULL, 3, 0, 0, 0, JOF_OBJECT
OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) OPDEF(JSOP_DEFCONST, 126,"defconst", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING) OPDEF(JSOP_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
/* Push a closure for a named or anonymous function expression. */ /* Auto-clone (if needed due to re-parenting) and push an anonymous function. */
OPDEF(JSOP_LAMBDA, 128, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT) OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT)
/* Used for named function expression self-naming, if lightweight. */ /* ECMA ed. 3 named function expression. */
OPDEF(JSOP_CALLEE, 129, "callee", NULL, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT)
/* /*
* Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately * Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
@@ -340,23 +337,28 @@ OPDEF(JSOP_CALLEE, 129, "callee", NULL, 1, 0, 1, 19, JOF_BYTE)
*/ */
OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET) OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET)
/* Pick an element from the stack. */ /* Jump to target if top of stack value is of primitive type. */
OPDEF(JSOP_PICK, 131, "pick", NULL, 2, 0, 0, 0, JOF_UINT8) OPDEF(JSOP_IFPRIMTOP, 131, "ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING)
/*
* Host object extension: given 'o.item(i) = j', the left-hand side compiles
* JSOP_SETCALL, rather than JSOP_CALL.
*/
OPDEF(JSOP_SETCALL, 132, "setcall", NULL, 3, -1, 2, 18, JOF_UINT16|JOF_SET)
/* /*
* Exception handling no-op, for more economical byte-coding than SRC_TRYFIN * Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
* srcnote-annotated JSOP_NOPs and to simply stack balance handling. * srcnote-annotated JSOP_NOPs and to simply stack balance handling.
*/ */
OPDEF(JSOP_TRY, 132,"try", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_FINALLY, 133,"finally", NULL, 1, 0, 2, 0, JOF_BYTE) OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
/* /*
* Get a dynamic slot from an object known to have at least one greater than * Ensure that the value on the top of the stack is an object. The one
* the slot index number of values at obj->dslots. The CALL variant computes * argument is an error message, defined in js.msg, that takes one parameter
* the callee and this-object in preparation for a JSOP_CALL. * (the decompilation of the primitive value).
*/ */
OPDEF(JSOP_GETDSLOT, 134,"getdslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) OPDEF(JSOP_OBJTOP, 135,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_CALLDSLOT, 135,"calldslot", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
/* /*
* Bytecodes that avoid making an arguments object in most cases: * Bytecodes that avoid making an arguments object in most cases:
@@ -432,12 +434,12 @@ OPDEF(JSOP_XMLCDATA, 181,"xmlcdata", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM) OPDEF(JSOP_XMLCOMMENT, 182,"xmlcomment", NULL, 3, 0, 1, 19, JOF_ATOM)
OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM) OPDEF(JSOP_XMLPI, 183,"xmlpi", NULL, 3, 1, 1, 19, JOF_ATOM)
OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP) OPDEF(JSOP_CALLPROP, 184,"callprop", NULL, 3, 1, 2, 18, JOF_ATOM|JOF_PROP|JOF_CALLOP)
OPDEF(JSOP_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
/* /*
* Get a display (free) variable from the closure's reserved slots. * Get a display (free) variable from the closure's reserved slots.
*/ */
OPDEF(JSOP_GETUPVAR, 185,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME) OPDEF(JSOP_GETUPVAR, 186,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
OPDEF(JSOP_CALLUPVAR, 186,"callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL) OPDEF(JSOP_DELDESC, 187,"deldesc", NULL, 1, 2, 1, 15, JOF_BYTE|JOF_ELEM|JOF_DEL)
@@ -489,65 +491,76 @@ OPDEF(JSOP_TYPEOFEXPR, 198,"typeofexpr", NULL, 1, 1, 1, 15, JOF_BYTE|J
OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT) OPDEF(JSOP_ENTERBLOCK, 199,"enterblock", NULL, 3, 0, -1, 0, JOF_OBJECT)
OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16) OPDEF(JSOP_LEAVEBLOCK, 200,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16)
/* Jump to target if top of stack value is of primitive type. */ /*
OPDEF(JSOP_IFPRIMTOP, 201,"ifprimtop", NULL, 3, 1, 1, 0, JOF_JUMP|JOF_DETECTING) * Pick an element from the stack.
*/
OPDEF(JSOP_PICK, 201,"pick", NULL, 2, 0, 0, 0, JOF_UINT8)
/* Throws a TypeError if the value at the top of the stack is not primitive. */ /* Throws a TypeError if the value at the top of the stack is not primitive. */
OPDEF(JSOP_PRIMTOP, 202,"primtop", NULL, 1, 1, 1, 0, JOF_BYTE) OPDEF(JSOP_PRIMTOP, 202, "primtop", NULL, 1, 1, 1, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED203, 203,"unused203", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED204, 204,"unused204", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED205, 205,"unused205", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED206, 206,"unused206", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED207, 207,"unused207", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED208, 208,"unused208", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_UNUSED209, 209,"unused209", NULL, 1, 0, 0, 0, JOF_BYTE)
/* /*
* Generator and array comprehension support. * Generator and array comprehension support.
*/ */
OPDEF(JSOP_GENERATOR, 203,"generator", NULL, 1, 0, 0, 0, JOF_BYTE) OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
OPDEF(JSOP_YIELD, 204,"yield", NULL, 1, 1, 1, 1, JOF_BYTE) OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
OPDEF(JSOP_ARRAYPUSH, 205,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL) OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
/* /*
* Get the built-in function::foo namespace and push it. * In the forthcoming great opcode reorg, this should go next to JSOP_GETUPVAR.
*/ */
OPDEF(JSOP_GETFUNNS, 206,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE) OPDEF(JSOP_CALLUPVAR, 213, "callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
/* /*
* Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...). * Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
*/ */
OPDEF(JSOP_ENUMCONSTELEM, 207,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET) OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
/* /*
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals, * Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
* which must be moved down when the block pops. * which must be moved down when the block pops.
*/ */
OPDEF(JSOP_LEAVEBLOCKEXPR,208,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16) OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16)
/* /*
* Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches. * Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches.
*/ */
OPDEF(JSOP_GETTHISPROP, 209,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP) OPDEF(JSOP_GETTHISPROP, 216,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP)
OPDEF(JSOP_GETARGPROP, 210,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) OPDEF(JSOP_GETARGPROP, 217,"getargprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
OPDEF(JSOP_GETLOCALPROP, 211,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP) OPDEF(JSOP_GETLOCALPROP, 218,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
OPDEF(JSOP_UNUSED219, 219,"unused219", NULL, 1, 0, 0, 0, JOF_BYTE)
/* /*
* Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after * Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after
* the opcode that they prefix. * the opcode that they prefix.
*/ */
OPDEF(JSOP_INDEXBASE1, 212,"atombase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) OPDEF(JSOP_INDEXBASE1, 220,"atombase1", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE2, 213,"atombase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) OPDEF(JSOP_INDEXBASE2, 221,"atombase2", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_INDEXBASE3, 214,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE) OPDEF(JSOP_INDEXBASE3, 222,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
OPDEF(JSOP_CALLGVAR, 215, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLGVAR, 223, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLLOCAL, 216, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLLOCAL, 224, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLARG, 217, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP) OPDEF(JSOP_CALLARG, 225, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
OPDEF(JSOP_CALLBUILTIN, 218, "callbuiltin", NULL, 3, 0, 2, 0, JOF_UINT16) OPDEF(JSOP_CALLBUILTIN, 226, "callbuiltin", NULL, 3, 0, 2, 0, JOF_UINT16)
/* /*
* Opcodes to hold 8-bit and 32-bit immediate integer operands. * Opcodes to hold 8-bit and 32-bit immediate integer operands.
*/ */
OPDEF(JSOP_INT8, 219, "int8", NULL, 2, 0, 1, 16, JOF_INT8) OPDEF(JSOP_INT8, 227, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
OPDEF(JSOP_INT32, 220, "int32", NULL, 5, 0, 1, 16, JOF_INT32) OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
/* /*
* Get the value of the 'length' property from a stacked object. * Get the value of the 'length' property from a stacked object.
*/ */
OPDEF(JSOP_LENGTH, 221, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP) OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
/* /*
* Construct a new dense array whose contents are the values provided on the * Construct a new dense array whose contents are the values provided on the
@@ -556,27 +569,13 @@ OPDEF(JSOP_LENGTH, 221, "length", NULL, 1, 1, 1, 18, JOF_BYTE|J
* bottommost value is the first value in the array; the array length is a * bottommost value is the first value in the array; the array length is a
* 24-bit immediate operand to the instruction. * 24-bit immediate operand to the instruction.
*/ */
OPDEF(JSOP_NEWARRAY, 222, "newarray", NULL, 4, -1, 1, 19, JOF_UINT24) OPDEF(JSOP_NEWARRAY, 230, "newarray", NULL, 4, -1, 1, 19, JOF_UINT24)
/* /*
* Push a JSVAL_HOLE value onto the stack, representing an omitted property in * Push a JSVAL_HOLE value onto the stack, representing an omitted property in
* an array literal (e.g. property 0 in the array [, 1]). This opcode is used * an array literal (e.g. property 0 in the array [, 1]). This opcode is used
* with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes. * with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes.
*/ */
OPDEF(JSOP_HOLE, 223, "hole", NULL, 1, 0, 1, 0, JOF_BYTE) OPDEF(JSOP_HOLE, 231, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
/* OPDEF(JSOP_LOOP, 232, "loop", NULL, 1, 0, 0, 0, JOF_BYTE)
* Variants of JSOP_{DEF{,LOCAL}FUN,LAMBDA} optimized for the flat closure case.
*/
OPDEF(JSOP_DEFFUN_FC, 224,"deffun_fc", NULL, 3, 0, 0, 0, JOF_OBJECT|JOF_DECLARING)
OPDEF(JSOP_DEFLOCALFUN_FC,225,"deflocalfun_fc",NULL, 5, 0, 0, 0, JOF_SLOTOBJECT|JOF_DECLARING)
OPDEF(JSOP_LAMBDA_FC, 226,"lambda_fc", NULL, 3, 0, 1, 19, JOF_OBJECT)
/*
* Ensure that the value on the top of the stack is an object. The one
* argument is an error message, defined in js.msg, that takes one parameter
* (the decompilation of the primitive value).
*/
OPDEF(JSOP_OBJTOP, 227,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
OPDEF(JSOP_LOOP, 228, "loop", NULL, 1, 0, 0, 0, JOF_BYTE)

File diff suppressed because it is too large Load Diff

View File

@@ -46,7 +46,6 @@
#include "jsversion.h" #include "jsversion.h"
#include "jsprvtd.h" #include "jsprvtd.h"
#include "jspubtd.h" #include "jspubtd.h"
#include "jsatom.h"
#include "jsscan.h" #include "jsscan.h"
JS_BEGIN_EXTERN_C JS_BEGIN_EXTERN_C
@@ -62,25 +61,13 @@ JS_BEGIN_EXTERN_C
* Label Variant Members * Label Variant Members
* ----- ------- ------- * ----- ------- -------
* <Definitions> * <Definitions>
* TOK_FUNCTION name pn_funbox: ptr to JSFunctionBox holding function * TOK_FUNCTION func pn_funpob: JSParsedObjectBox holding function
* object containing arg and var properties. We * object containing arg and var properties. We
* create the function object at parse (not emit) * create the function object at parse (not emit)
* time to specialize arg and var bytecodes early. * time to specialize arg and var bytecodes early.
* pn_body: TOK_UPVARS if the function's source body * pn_body: TOK_LC node for function body statements
* depends on outer names, else TOK_ARGSBODY * pn_flags: TCF_FUN_* flags (see jsemit.h) collected
* if formal parameters, else TOK_LC node for * while parsing the function's body
* function body statements
* pn_cookie: static level and var index for function
* pn_dflags: PND_* definition/use flags (see below)
* pn_blockid: block id number
* TOK_ARGSBODY list list of formal parameters followed by TOK_LC node
* for function body statements as final element
* pn_count: 1 + number of formal parameters
* TOK_UPVARS nameset pn_names: lexical dependencies (JSDefinitions)
* defined in enclosing scopes, or ultimately not
* defined (free variables, either global property
* references or reference errors).
* pn_argsbody: TOK_ARGSBODY or TOK_LC node
* *
* <Statements> * <Statements>
* TOK_LC list pn_head: list of pn_count statements * TOK_LC list pn_head: list of pn_count statements
@@ -99,7 +86,7 @@ JS_BEGIN_EXTERN_C
* TOK_FOR binary pn_left: either * TOK_FOR binary pn_left: either
* for/in loop: a binary TOK_IN node with * for/in loop: a binary TOK_IN node with
* pn_left: TOK_VAR or TOK_NAME to left of 'in' * pn_left: TOK_VAR or TOK_NAME to left of 'in'
* if TOK_VAR, its pn_xflags may have PNX_POPVAR * if TOK_VAR, its pn_extra may have PNX_POPVAR
* and PNX_FORINVAR bits set * and PNX_FORINVAR bits set
* pn_right: object expr to right of 'in' * pn_right: object expr to right of 'in'
* for(;;) loop: a ternary TOK_RESERVED node with * for(;;) loop: a ternary TOK_RESERVED node with
@@ -121,15 +108,10 @@ JS_BEGIN_EXTERN_C
* TOK_BREAK name pn_atom: label or null * TOK_BREAK name pn_atom: label or null
* TOK_CONTINUE name pn_atom: label or null * TOK_CONTINUE name pn_atom: label or null
* TOK_WITH binary pn_left: head expr, pn_right: body * TOK_WITH binary pn_left: head expr, pn_right: body
* TOK_VAR list pn_head: list of TOK_NAME or TOK_ASSIGN nodes * TOK_VAR list pn_head: list of pn_count TOK_NAME nodes
* each name node has * each name node has
* pn_used: false
* pn_atom: variable name * pn_atom: variable name
* pn_expr: initializer or null * pn_expr: initializer or null
* each assignment node has
* pn_left: TOK_NAME with pn_used true and
* pn_lexdef (NOT pn_expr) set
* pn_right: initializer
* TOK_RETURN unary pn_kid: return expr or null * TOK_RETURN unary pn_kid: return expr or null
* TOK_SEMI unary pn_kid: expr or null statement * TOK_SEMI unary pn_kid: expr or null statement
* TOK_COLON name pn_atom: label, pn_expr: labeled statement * TOK_COLON name pn_atom: label, pn_expr: labeled statement
@@ -154,12 +136,12 @@ JS_BEGIN_EXTERN_C
* TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr * TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr
* pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH * pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH
* TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr * TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr
* pn_xflags: if a left-associated binary TOK_PLUS * pn_extra: if a left-associated binary TOK_PLUS
* tree has been flattened into a list (see above * tree has been flattened into a list (see above
* under <Expressions>), pn_xflags will contain * under <Expressions>), pn_extra will contain
* PNX_STRCAT if at least one list element is a * PNX_STRCAT if at least one list element is a
* string literal (TOK_STRING); if such a list has * string literal (TOK_STRING); if such a list has
* any non-string, non-number term, pn_xflags will * any non-string, non-number term, pn_extra will
* contain PNX_CANTFOLD. * contain PNX_CANTFOLD.
* pn_ * pn_
* TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB * TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB
@@ -182,12 +164,12 @@ JS_BEGIN_EXTERN_C
* call is a MEMBER expr naming a callable object * call is a MEMBER expr naming a callable object
* TOK_RB list pn_head: list of pn_count array element exprs * TOK_RB list pn_head: list of pn_count array element exprs
* [,,] holes are represented by TOK_COMMA nodes * [,,] holes are represented by TOK_COMMA nodes
* pn_xflags: PN_ENDCOMMA if extra comma at end * pn_extra: PN_ENDCOMMA if extra comma at end
* TOK_RC list pn_head: list of pn_count TOK_COLON nodes where * TOK_RC list pn_head: list of pn_count TOK_COLON nodes where
* each has pn_left: property id, pn_right: value * each has pn_left: property id, pn_right: value
* var {x} = object destructuring shorthand shares * var {x} = object destructuring shorthand shares
* PN_NAME node for x on left and right of TOK_COLON * PN_NAME node for x on left and right of TOK_COLON
* node in TOK_RC's list, has PNX_DESTRUCT flag * node in TOK_RC's list, has PNX_SHORTHAND flag
* TOK_DEFSHARP unary pn_num: jsint value of n in #n= * TOK_DEFSHARP unary pn_num: jsint value of n in #n=
* pn_kid: primary function, paren, name, object or * pn_kid: primary function, paren, name, object or
* array literal expressions * array literal expressions
@@ -197,11 +179,7 @@ JS_BEGIN_EXTERN_C
* TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or * TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or
* JSOP_REGEXP * JSOP_REGEXP
* TOK_REGEXP If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR * TOK_REGEXP If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR
* with pn_cookie telling (staticLevel, slot) (see * with pn_slot >= 0 and pn_const telling const-ness
* jsscript.h's UPVAR macros) and pn_dflags telling
* const-ness and static analysis results
* TOK_NAME name If pn_used, TOK_NAME uses the lexdef member instead
* of the expr member it overlays
* TOK_NUMBER dval pn_dval: double value of numeric literal * TOK_NUMBER dval pn_dval: double value of numeric literal
* TOK_PRIMARY nullary pn_op: JSOp bytecode * TOK_PRIMARY nullary pn_op: JSOp bytecode
* *
@@ -264,49 +242,44 @@ JS_BEGIN_EXTERN_C
* Label Variant Members * Label Variant Members
* ----- ------- ------- * ----- ------- -------
* TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR * TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
* pn_objbox: block object in JSObjectBox holder * pn_pob: block object
* pn_expr: block body * pn_expr: block body
* TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements * TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements
* if pn_count is 2, first element is #n=[...] * if pn_count is 2, first element is #n=[...]
* last element is block enclosing for loop(s) * last element is block enclosing for loop(s)
* and optionally if-guarded TOK_ARRAYPUSH * and optionally if-guarded TOK_ARRAYPUSH
* pn_extra: stack slot, used during code gen
* TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP * TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
* pn_kid: array comprehension expression * pn_kid: array comprehension expression
*/ */
typedef enum JSParseNodeArity { typedef enum JSParseNodeArity {
PN_NULLARY, /* 0 kids, only pn_atom/pn_dval/etc. */ PN_FUNC = -3,
PN_UNARY, /* one kid, plus a couple of scalars */ PN_LIST = -2,
PN_BINARY, /* two kids, plus a couple of scalars */ PN_TERNARY = 3,
PN_TERNARY, /* three kids */ PN_BINARY = 2,
PN_FUNC, /* function definition node */ PN_UNARY = 1,
PN_LIST, /* generic singly linked list */ PN_NAME = -1,
PN_NAME, /* name use or definition node */ PN_NULLARY = 0
PN_NAMESET /* JSAtomList + JSParseNode ptr */
} JSParseNodeArity; } JSParseNodeArity;
struct JSDefinition;
struct JSParseNode { struct JSParseNode {
uint32 pn_type:16, /* TOK_* type, see jsscan.h */ uint16 pn_type;
pn_op:8, /* see JSOp enum and jsopcode.tbl */ uint8 pn_op;
pn_arity:6, /* see JSParseNodeArity enum */ int8 pn_arity;
pn_used:1, /* name node is on a use-chain */ JSTokenPos pn_pos;
pn_defn:1; /* this node is a JSDefinition */ ptrdiff_t pn_offset; /* first generated bytecode offset */
#define PN_OP(pn) ((JSOp)(pn)->pn_op)
#define PN_TYPE(pn) ((JSTokenType)(pn)->pn_type)
JSTokenPos pn_pos; /* two 16-bit pairs here, for 64 bits */
int32 pn_offset; /* first generated bytecode offset */
JSParseNode *pn_next; /* intrinsic link in parent PN_LIST */
JSParseNode *pn_link; /* def/use link (alignment freebie) */
union { union {
struct { /* TOK_FUNCTION node */
JSParsedObjectBox *funpob; /* function object */
JSParseNode *body; /* TOK_LC list of statements */
uint16 flags; /* accumulated tree context flags */
uint32 index; /* emitter's index */
} func;
struct { /* list of next-linked nodes */ struct { /* list of next-linked nodes */
JSParseNode *head; /* first node in list */ JSParseNode *head; /* first node in list */
JSParseNode **tail; /* ptr to ptr to last node in list */ JSParseNode **tail; /* ptr to ptr to last node in list */
uint32 count; /* number of nodes in list */ uint32 count; /* number of nodes in list */
uint32 xflags:12, /* extra flags, see below */ uint32 extra; /* extra flags, see below */
blockid:20; /* see name variant below */
} list; } list;
struct { /* ternary: if, for(;;), ?: */ struct { /* ternary: if, for(;;), ?: */
JSParseNode *kid1; /* condition, discriminant, etc. */ JSParseNode *kid1; /* condition, discriminant, etc. */
@@ -325,44 +298,36 @@ struct JSParseNode {
JSBool hidden; /* hidden genexp-induced JSOP_YIELD */ JSBool hidden; /* hidden genexp-induced JSOP_YIELD */
} unary; } unary;
struct { /* name, labeled statement, etc. */ struct { /* name, labeled statement, etc. */
union { JSAtom *atom; /* name or label atom, null if slot */
JSAtom *atom; /* lexical name or label atom */ JSParseNode *expr; /* object or initializer */
JSFunctionBox *funbox; /* function object */ jsint slot; /* -1 or arg or local var slot */
JSObjectBox *objbox; /* block or regexp object */ JSBool isconst; /* true for const names */
};
union {
JSParseNode *expr; /* function body, var initializer, or
base object of TOK_DOT */
JSDefinition *lexdef; /* lexical definition for this use */
};
uint32 cookie; /* upvar cookie with absolute frame
level (not relative skip), possibly
in current frame */
uint32 dflags:12, /* definition/use flags, see below */
blockid:20; /* block number, for subset dominance
computation */
} name; } name;
struct { /* lexical dependencies + sub-tree */ struct { /* lexical scope. */
JSAtomSet names; /* set of names with JSDefinitions */ JSParsedObjectBox *pob; /* block object */
JSParseNode *tree; /* sub-tree containing name uses */ JSParseNode *expr; /* object or initializer */
} nameset; jsint slot; /* -1 or arg or local var slot */
struct { /* PN_NULLARY variant for E4X */ } lexical;
struct {
JSAtom *atom; /* first atom in pair */ JSAtom *atom; /* first atom in pair */
JSAtom *atom2; /* second atom in pair or null */ JSAtom *atom2; /* second atom in pair or null */
} apair; } apair;
struct { /* object literal */
JSParsedObjectBox *pob;
} object;
jsdouble dval; /* aligned numeric literal value */ jsdouble dval; /* aligned numeric literal value */
} pn_u; } pn_u;
JSParseNode *pn_next; /* to align dval and pn_u on RISCs */
};
#define pn_funbox pn_u.name.funbox #define pn_funpob pn_u.func.funpob
#define pn_body pn_u.name.expr #define pn_body pn_u.func.body
#define pn_cookie pn_u.name.cookie #define pn_flags pn_u.func.flags
#define pn_dflags pn_u.name.dflags #define pn_index pn_u.func.index
#define pn_blockid pn_u.name.blockid
#define pn_index pn_u.name.blockid /* reuse as object table index */
#define pn_head pn_u.list.head #define pn_head pn_u.list.head
#define pn_tail pn_u.list.tail #define pn_tail pn_u.list.tail
#define pn_count pn_u.list.count #define pn_count pn_u.list.count
#define pn_xflags pn_u.list.xflags #define pn_extra pn_u.list.extra
#define pn_kid1 pn_u.ternary.kid1 #define pn_kid1 pn_u.ternary.kid1
#define pn_kid2 pn_u.ternary.kid2 #define pn_kid2 pn_u.ternary.kid2
#define pn_kid3 pn_u.ternary.kid3 #define pn_kid3 pn_u.ternary.kid3
@@ -374,50 +339,14 @@ struct JSParseNode {
#define pn_num pn_u.unary.num #define pn_num pn_u.unary.num
#define pn_hidden pn_u.unary.hidden #define pn_hidden pn_u.unary.hidden
#define pn_atom pn_u.name.atom #define pn_atom pn_u.name.atom
#define pn_objbox pn_u.name.objbox
#define pn_expr pn_u.name.expr #define pn_expr pn_u.name.expr
#define pn_lexdef pn_u.name.lexdef #define pn_slot pn_u.name.slot
#define pn_names pn_u.nameset.names #define pn_const pn_u.name.isconst
#define pn_tree pn_u.nameset.tree
#define pn_dval pn_u.dval #define pn_dval pn_u.dval
#define pn_atom2 pn_u.apair.atom2 #define pn_atom2 pn_u.apair.atom2
#define pn_pob pn_u.object.pob
/* /* PN_LIST pn_extra flags. */
* The pn_expr and lexdef members are arms of an unsafe union. Unless you
* know exactly what you're doing, use only the following methods to access
* them. For less overhead and assertions for protection, use pn->expr()
* and pn->lexdef(). Otherwise, use pn->maybeExpr() and pn->maybeLexDef().
*/
JSParseNode *expr() const {
JS_ASSERT(!pn_used);
JS_ASSERT(pn_arity == PN_NAME || pn_arity == PN_FUNC);
return pn_expr;
}
JSDefinition *lexdef() const {
JS_ASSERT(pn_used);
JS_ASSERT(pn_arity == PN_NAME);
return pn_lexdef;
}
JSParseNode *maybeExpr() { return pn_used ? NULL : expr(); }
JSDefinition *maybeLexDef() { return pn_used ? lexdef() : NULL; }
/* PN_FUNC and PN_NAME pn_dflags bits. */
#define PND_LET 0x01 /* let (block-scoped) binding */
#define PND_CONST 0x02 /* const binding (orthogonal to let) */
#define PND_INITIALIZED 0x04 /* initialized declaration */
#define PND_ASSIGNED 0x08 /* set if ever LHS of assignment */
#define PND_TOPLEVEL 0x10 /* function at top of body or prog */
#define PND_BLOCKCHILD 0x20 /* use or def is direct block child */
#define PND_FORWARD 0x40 /* forward referenced definition */
#define PND_PLACEHOLDER 0x80 /* placeholder definition for lexdep */
#define PND_FUNARG 0x100 /* downward or upward funarg usage */
#define PND_BOUND 0x200 /* bound to a stack or global slot */
#define PND_GVAR 0x400 /* gvar binding, can't close over
because it could be deleted */
/* PN_LIST pn_xflags bits. */
#define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */ #define PNX_STRCAT 0x01 /* TOK_PLUS list has string term */
#define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */ #define PNX_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */
#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */ #define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */
@@ -429,435 +358,151 @@ struct JSParseNode {
#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */ #define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */
#define PNX_FUNCDEFS 0x100 /* contains top-level function #define PNX_FUNCDEFS 0x100 /* contains top-level function
statements */ statements */
#define PNX_DESTRUCT 0x200 /* destructuring special cases: #define PNX_SHORTHAND 0x200 /* shorthand syntax used, at present
1. shorthand syntax used, at present object destructuring ({x,y}) only */
object destructuring ({x,y}) only; #define PNX_DESTRARGS 0x400 /* the first child is node defining
2. the first child of function body destructuring arguments */
is code evaluating destructuring
arguments */
uintN frameLevel() const {
JS_ASSERT(pn_arity == PN_FUNC || pn_arity == PN_NAME);
return UPVAR_FRAME_SKIP(pn_cookie);
}
uintN frameSlot() const {
JS_ASSERT(pn_arity == PN_FUNC || pn_arity == PN_NAME);
return UPVAR_FRAME_SLOT(pn_cookie);
}
bool test(uintN flag) const {
JS_ASSERT(pn_arity == PN_FUNC || pn_arity == PN_NAME);
return !!(pn_dflags & flag);
}
bool isLet() const { return test(PND_LET); }
bool isConst() const { return test(PND_CONST); }
bool isInitialized() const { return test(PND_INITIALIZED); }
bool isTopLevel() const { return test(PND_TOPLEVEL); }
bool isBlockChild() const { return test(PND_BLOCKCHILD); }
bool isForward() const { return test(PND_FORWARD); }
bool isPlaceholder() const { return test(PND_PLACEHOLDER); }
/* Defined below, see after struct JSDefinition. */
bool isAssigned() const;
bool isFunArg() const;
void become(JSParseNode *pn2);
void clear();
/* True if pn is a parsenode representing a literal constant. */
bool isLiteral() const {
return PN_TYPE(this) == TOK_NUMBER ||
PN_TYPE(this) == TOK_STRING ||
(PN_TYPE(this) == TOK_PRIMARY && PN_OP(this) != JSOP_THIS);
}
/*
* Compute a pointer to the last element in a singly-linked list. NB: list
* must be non-empty for correct PN_LAST usage -- this is asserted!
*/
JSParseNode *last() const {
JS_ASSERT(pn_arity == PN_LIST);
JS_ASSERT(pn_count != 0);
return (JSParseNode *)((char *)pn_tail - offsetof(JSParseNode, pn_next));
}
void makeEmpty() {
JS_ASSERT(pn_arity == PN_LIST);
pn_head = NULL;
pn_tail = &pn_head;
pn_count = 0;
pn_xflags = 0;
pn_blockid = 0;
}
void initList(JSParseNode *pn) {
JS_ASSERT(pn_arity == PN_LIST);
pn_head = pn;
pn_tail = &pn->pn_next;
pn_count = 1;
pn_xflags = 0;
pn_blockid = 0;
}
void append(JSParseNode *pn) {
JS_ASSERT(pn_arity == PN_LIST);
*pn_tail = pn;
pn_tail = &pn->pn_next;
pn_count++;
}
};
/* /*
* JSDefinition is a degenerate subtype of the PN_FUNC and PN_NAME variants of * Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off
* JSParseNode, allocated only for function, var, const, and let declarations * any kids in pn2->pn_u, by clearing pn2.
* that define truly lexical bindings. This means that a child of a TOK_VAR
* list may be a JSDefinition instead of a JSParseNode. The pn_defn bit is set
* for all JSDefinitions, clear otherwise.
*
* Note that not all var declarations are definitions: JS allows multiple var
* declarations in a function or script, but only the first creates the hoisted
* binding. JS programmers do redeclare variables for good refactoring reasons,
* for example:
*
* function foo() {
* ...
* for (var i ...) ...;
* ...
* for (var i ...) ...;
* ...
* }
*
* Not all definitions bind lexical variables, alas. In global and eval code
* var may re-declare a pre-existing property having any attributes, with or
* without JSPROP_PERMANENT. In eval code, indeed, ECMA-262 Editions 1 through
* 3 require function and var to bind deletable bindings. Global vars thus are
* properties of the global object, so they can be aliased even if they can't
* be deleted.
*
* Only bindings within function code may be treated as lexical, of course with
* the caveat that hoisting means use before initialization is allowed. We deal
* with use before declaration in one pass as follows (error checking elided):
*
* for (each use of unqualified name x in parse order) {
* if (this use of x is a declaration) {
* if (x in tc->decls) { // redeclaring
* pn = allocate a PN_NAME JSParseNode;
* } else { // defining
* dn = lookup x in tc->lexdeps;
* if (dn) { // use before def
* remove x from tc->lexdeps;
* } else { // def before use
* dn = allocate a PN_NAME JSDefinition;
* map x to dn via tc->decls;
* }
* pn = dn;
* }
* insert pn into its parent TOK_VAR list;
* } else {
* pn = allocate a JSParseNode for this reference to x;
* dn = lookup x in tc's lexical scope chain;
* if (!dn) {
* dn = lookup x in tc->lexdeps;
* if (!dn) {
* dn = pre-allocate a JSDefinition for x;
* map x to dn in tc->lexdeps;
* }
* }
* append pn to dn's use chain;
* }
* }
*
* See jsemit.h for JSTreeContext and its top*Stmt, decls, and lexdeps members.
*
* Notes:
*
* 0. To avoid bloating JSParseNode, we steal a bit from pn_arity for pn_defn
* and set it on a JSParseNode instead of allocating a JSDefinition.
*
* 1. Due to hoisting, a definition cannot be eliminated even if its "Variable
* statement" (ECMA-262 12.2) can be proven to be dead code. RecycleTree in
* jsparse.cpp will not recycle a node whose pn_defn bit is set.
*
* 2. "lookup x in tc's lexical scope chain" gives up on def/use chaining if a
* with statement is found along the the scope chain, which includes tc,
* tc->parent, etc. Thus we eagerly connect an inner function's use of an
* outer's var x if the var x was parsed before the inner function.
*
* 3. A use may be eliminated as dead by the constant folder, which therefore
* must remove the dead name node from its singly-linked use chain, which
* would mean hashing to find the definition node and searching to update
* the pn_link pointing at the use to be removed. This is costly, so as for
* dead definitions, we do not recycle dead pn_used nodes.
*
* At the end of parsing a function body or global or eval program, tc->lexdeps
* holds the lexical dependencies of the parsed unit. The name to def/use chain
* mappings are then merged into the parent tc->lexdeps.
*
* Thus if a later var x is parsed in the outer function satisfying an earlier
* inner function's use of x, we will remove dn from tc->lexdeps and re-use it
* as the new definition node in the outer function's parse tree.
*
* When the compiler unwinds from the outermost tc, tc->lexdeps contains the
* definition nodes with use chains for all free variables. These are either
* global variables or reference errors.
*
* We analyze whether a binding is initialized, whether the bound names is ever
* assigned apart from its initializer, and if the bound name definition or use
* is in a direct child of a block. These PND_* flags allow a subset dominance
* computation telling whether an initialized var dominates its uses. An inner
* function using only such outer vars (and formal parameters) can be optimized
* into a flat closure. See JSOP_{GET,CALL}DSLOT.
*
* Another important subset dominance relation: ... { var x = ...; ... x ... }
* where x is not assigned after initialization and not used outside the block.
* This style is common in the absence of 'let'. Even though the var x is not
* at top level, we can tell its initialization dominates all uses cheaply,
* because the above one-pass algorithm sees the definition before any uses,
* and because all uses are contained in the same block as the definition.
*
* We also analyze function uses to flag upward/downward funargs, optimizing
* Algol-like (not passed as funargs, only ever called) lightweight functions
* using cx->display. See JSOP_{GET,CALL}UPVAR.
*
* This means that closure optimizations may be frustrated by with, eval, or
* assignment to an outer var. Such hard cases require heavyweight functions
* and JSOP_NAME, etc.
*/ */
#define dn_uses pn_link #define PN_MOVE_NODE(pn, pn2) \
JS_BEGIN_MACRO \
(pn)->pn_type = (pn2)->pn_type; \
(pn)->pn_op = (pn2)->pn_op; \
(pn)->pn_arity = (pn2)->pn_arity; \
(pn)->pn_u = (pn2)->pn_u; \
PN_CLEAR_NODE(pn2); \
JS_END_MACRO
struct JSDefinition : public JSParseNode #define PN_CLEAR_NODE(pn) \
{ JS_BEGIN_MACRO \
/* (pn)->pn_type = TOK_EOF; \
* We store definition pointers in PN_NAMESET JSAtomLists in the AST, but (pn)->pn_op = JSOP_NOP; \
* due to redefinition these nodes may become uses of other definitions. (pn)->pn_arity = PN_NULLARY; \
* This is unusual, so we simply chase the pn_lexdef link to find the final JS_END_MACRO
* definition node. See methods called from JSCompiler::analyzeFunctions.
*
* FIXME: MakeAssignment mutates for want of a parent link...
*/
JSDefinition *resolve() {
JSParseNode *pn = this;
while (!pn->pn_defn) {
if (pn->pn_type == TOK_ASSIGN) {
pn = pn->pn_left;
continue;
}
pn = pn->lexdef();
}
return (JSDefinition *) pn;
}
bool test(uintN flag) const { /* True if pn is a parsenode representing a literal constant. */
JS_ASSERT(pn_defn); #define PN_IS_CONSTANT(pn) \
if (pn_dflags & flag) ((pn)->pn_type == TOK_NUMBER || \
return true; (pn)->pn_type == TOK_STRING || \
#ifdef DEBUG ((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS))
for (JSParseNode *pn = dn_uses; pn; pn = pn->pn_link) {
JS_ASSERT(!pn->pn_defn);
JS_ASSERT(!(pn->pn_dflags & flag));
}
#endif
return false;
}
bool isAssigned() const { #define PN_OP(pn) ((JSOp)(pn)->pn_op)
return test(PND_ASSIGNED); #define PN_TYPE(pn) ((JSTokenType)(pn)->pn_type)
}
bool isFunArg() const {
return test(PND_FUNARG);
}
bool isFreeVar() const {
JS_ASSERT(pn_defn);
return pn_cookie == FREE_UPVAR_COOKIE || test(PND_GVAR);
}
// Grr, windows.h or something under it #defines CONST...
#ifdef CONST
# undef CONST
#endif
enum Kind { VAR, CONST, LET, FUNCTION, ARG, UNKNOWN };
bool isBindingForm() { return int(kind()) <= int(LET); }
static const char *kindString(Kind kind);
Kind kind() {
if (PN_TYPE(this) == TOK_FUNCTION)
return FUNCTION;
JS_ASSERT(PN_TYPE(this) == TOK_NAME);
if (PN_OP(this) == JSOP_NOP)
return UNKNOWN;
if (PN_OP(this) == JSOP_GETARG)
return ARG;
if (isConst())
return CONST;
if (isLet())
return LET;
return VAR;
}
};
/* /*
* These two are overridden by JSDefinition and we cannot afford virtual * Compute a pointer to the last JSParseNode element in a singly-linked list.
* methods -- so we use the mighty 'if' statement! * NB: list must be non-empty for correct PN_LAST usage!
*/ */
inline bool #define PN_LAST(list) \
JSParseNode::isAssigned() const ((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next)))
{
#ifdef DEBUG
if (pn_defn)
return ((JSDefinition *)this)->isAssigned();
#endif
return test(PND_ASSIGNED);
}
inline bool #define PN_INIT_LIST(list) \
JSParseNode::isFunArg() const JS_BEGIN_MACRO \
{ (list)->pn_head = NULL; \
#ifdef DEBUG (list)->pn_tail = &(list)->pn_head; \
if (pn_defn) (list)->pn_count = (list)->pn_extra = 0; \
return ((JSDefinition *)this)->isFunArg(); JS_END_MACRO
#endif
return test(PND_FUNARG);
}
struct JSObjectBox { #define PN_INIT_LIST_1(list, pn) \
JSObjectBox *traceLink; JS_BEGIN_MACRO \
JSObjectBox *emitLink; (list)->pn_head = (pn); \
(list)->pn_tail = &(pn)->pn_next; \
(list)->pn_count = 1; \
(list)->pn_extra = 0; \
JS_END_MACRO
#define PN_APPEND(list, pn) \
JS_BEGIN_MACRO \
*(list)->pn_tail = (pn); \
(list)->pn_tail = &(pn)->pn_next; \
(list)->pn_count++; \
JS_END_MACRO
struct JSParsedObjectBox {
JSParsedObjectBox *traceLink;
JSParsedObjectBox *emitLink;
JSObject *object; JSObject *object;
}; };
struct JSFunctionBox : public JSObjectBox struct JSParseContext {
{
JSParseNode *node;
JSFunctionBox *siblings;
JSFunctionBox *kids;
JSFunctionBox *parent;
uint16 level;
uint16 tcflags;
};
struct JSFunctionBoxQueue {
JSFunctionBox **vector;
size_t head, tail, length;
JSFunctionBoxQueue(uint32 count)
: vector(new JSFunctionBox*[count]), head(0), tail(0), length(count) { }
~JSFunctionBoxQueue() { delete[] vector; }
void push(JSFunctionBox *funbox) {
if (head == length)
head = 0;
vector[head++] = funbox;
}
JSFunctionBox *pull() {
if (tail == head)
return NULL;
if (tail == length)
tail = 0;
JS_ASSERT(tail != head);
return vector[tail++];
}
};
#define NUM_TEMP_FREELISTS 6U /* 32 to 2048 byte size classes (32 bit) */
struct JSCompiler {
JSContext *context;
JSAtomListElement *aleFreeList;
void *tempFreeList[NUM_TEMP_FREELISTS];
JSTokenStream tokenStream; JSTokenStream tokenStream;
void *tempPoolMark; /* initial JSContext.tempPool mark */ void *tempPoolMark; /* initial JSContext.tempPool mark */
JSPrincipals *principals; /* principals associated with source */ JSPrincipals *principals; /* principals associated with source */
JSStackFrame *callerFrame; /* scripted caller frame for eval and dbgapi */ JSStackFrame *callerFrame; /* scripted caller frame for eval and
JSParseNode *nodeList; /* list of recyclable parse-node structs */ debug scripts */
uint32 functionCount; /* number of functions in current unit */ JSParseNode *nodeList; /* list of recyclable parse-node
JSObjectBox *traceListHead; /* list of parsed object for GC tracing */ structs */
JSParsedObjectBox *traceListHead; /* list of parsed object for GC
tracing */
JSTempValueRooter tempRoot; /* root to trace traceListHead */ JSTempValueRooter tempRoot; /* root to trace traceListHead */
JSCompiler(JSContext *cx, JSPrincipals *prin = NULL, JSStackFrame *cfp = NULL)
: context(cx), aleFreeList(NULL), principals(NULL), callerFrame(cfp),
nodeList(NULL), functionCount(0), traceListHead(NULL)
{
memset(tempFreeList, 0, sizeof tempFreeList);
setPrincipals(prin);
JS_ASSERT_IF(cfp, cfp->script);
}
~JSCompiler();
/*
* Initialize a compiler. Parameters are passed on to js_InitTokenStream.
* The compiler owns the arena pool "tops-of-stack" space above the current
* JSContext.tempPool mark. This means you cannot allocate from tempPool
* and save the pointer beyond the next JSCompiler destructor invocation.
*/
bool init(const jschar *base, size_t length,
FILE *fp, const char *filename, uintN lineno);
void setPrincipals(JSPrincipals *prin);
/*
* Parse a top-level JS script.
*/
JSParseNode *parse(JSObject *chain);
#if JS_HAS_XML_SUPPORT
JSParseNode *parseXMLText(JSObject *chain, bool allowList);
#endif
/*
* Allocate a new parsed object or function container from cx->tempPool.
*/
JSObjectBox *newObjectBox(JSObject *obj);
JSFunctionBox *newFunctionBox(JSObject *obj, JSParseNode *fn, JSTreeContext *tc);
/*
* Create a new function object given tree context (tc), optional name
* (atom may be null) and lambda flag (JSFUN_LAMBDA or 0).
*/
JSFunction *newFunction(JSTreeContext *tc, JSAtom *atom, uintN lambda);
/*
* Analyze the tree of functions nested within a single compilation unit,
* starting at funbox, recursively walking its kids, then following its
* siblings, their kids, etc.
*/
void analyzeFunctions(JSFunctionBox *funbox, uint16& tcflags);
void markFunArgs(JSFunctionBox *funbox, uintN tcflags);
void setFunctionKinds(JSFunctionBox *funbox, uint16& tcflags);
void trace(JSTracer *trc);
static bool
compileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno);
static JSScript *
compileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
JSPrincipals *principals, uint32 tcflags,
const jschar *chars, size_t length,
FILE *file, const char *filename, uintN lineno,
JSString *source = NULL);
}; };
/* /*
* Convenience macro to access JSCompiler.tokenStream as a pointer. * Convenience macro to access JSParseContext.tokenStream as a pointer.
*/ */
#define TS(jsc) (&(jsc)->tokenStream) #define TS(pc) (&(pc)->tokenStream)
/*
* Parse a top-level JS script.
*/
extern JSParseNode *
js_ParseScript(JSContext *cx, JSObject *chain, JSParseContext *pc);
extern JSScript *
js_CompileScript(JSContext *cx, JSObject *scopeChain, JSStackFrame *callerFrame,
JSPrincipals *principals, uint32 tcflags,
const jschar *chars, size_t length,
FILE *file, const char *filename, uintN lineno,
JSString *source = NULL);
extern JSBool
js_CompileFunctionBody(JSContext *cx, JSFunction *fun, JSPrincipals *principals,
const jschar *chars, size_t length,
const char *filename, uintN lineno);
extern JSBool extern JSBool
js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc, js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc,
bool inCond = false); bool inCond = false);
#if JS_HAS_XML_SUPPORT
JS_FRIEND_API(JSParseNode *)
js_ParseXMLText(JSContext *cx, JSObject *chain, JSParseContext *pc,
JSBool allowList);
#endif
/*
* Initialize a parse context. All parameters after pc are passed to
* js_InitTokenStream.
*
* The parse context owns the arena pool "tops-of-stack" space above the
* current JSContext.tempPool mark. This means you cannot allocate from
* tempPool and save the pointer beyond the next js_FinishParseContext.
*/
extern JSBool
js_InitParseContext(JSContext *cx, JSParseContext *pc, JSPrincipals *principals,
JSStackFrame *callerFrame,
const jschar *base, size_t length, FILE *fp,
const char *filename, uintN lineno);
extern void
js_FinishParseContext(JSContext *cx, JSParseContext *pc);
extern void
js_InitCompilePrincipals(JSContext *cx, JSParseContext *pc,
JSPrincipals *principals);
/*
* Allocate a new parseed object node from cx->tempPool.
*/
extern JSParsedObjectBox *
js_NewParsedObjectBox(JSContext *cx, JSParseContext *pc, JSObject *obj);
extern void
js_TraceParseContext(JSTracer *trc, JSParseContext *pc);
JS_END_EXTERN_C JS_END_EXTERN_C
#endif /* jsparse_h___ */ #endif /* jsparse_h___ */

View File

@@ -94,9 +94,8 @@ typedef struct JSCodeGenerator JSCodeGenerator;
typedef struct JSGCThing JSGCThing; typedef struct JSGCThing JSGCThing;
typedef struct JSGenerator JSGenerator; typedef struct JSGenerator JSGenerator;
typedef struct JSNativeEnumerator JSNativeEnumerator; typedef struct JSNativeEnumerator JSNativeEnumerator;
typedef struct JSCompiler JSCompiler; typedef struct JSParseContext JSParseContext;
typedef struct JSFunctionBox JSFunctionBox; typedef struct JSParsedObjectBox JSParsedObjectBox;
typedef struct JSObjectBox JSObjectBox;
typedef struct JSParseNode JSParseNode; typedef struct JSParseNode JSParseNode;
typedef struct JSPropCacheEntry JSPropCacheEntry; typedef struct JSPropCacheEntry JSPropCacheEntry;
typedef struct JSProperty JSProperty; typedef struct JSProperty JSProperty;
@@ -244,7 +243,7 @@ typedef union JSTempValueUnion {
JSTempValueTrace trace; JSTempValueTrace trace;
JSScopeProperty *sprop; JSScopeProperty *sprop;
JSWeakRoots *weakRoots; JSWeakRoots *weakRoots;
JSCompiler *compiler; JSParseContext *parseContext;
JSScript *script; JSScript *script;
jsval *array; jsval *array;
} JSTempValueUnion; } JSTempValueUnion;

View File

@@ -137,10 +137,6 @@ typedef enum JSTokenType {
TOK_SEQ = 82, /* synthetic sequence of statements, TOK_SEQ = 82, /* synthetic sequence of statements,
not a block */ not a block */
TOK_FORHEAD = 83, /* head of for(;;)-style loop */ TOK_FORHEAD = 83, /* head of for(;;)-style loop */
TOK_ARGSBODY = 84, /* formal args in list + body at end */
TOK_UPVARS = 85, /* lexical dependencies as JSAtomList
of definitions paired with a parse
tree full of uses of those names */
TOK_RESERVED, /* reserved keywords */ TOK_RESERVED, /* reserved keywords */
TOK_LIMIT /* domain size */ TOK_LIMIT /* domain size */
} JSTokenType; } JSTokenType;
@@ -194,45 +190,11 @@ js_AppendJSString(JSStringBuffer *sb, JSString *str);
struct JSTokenPtr { struct JSTokenPtr {
uint16 index; /* index of char in physical line */ uint16 index; /* index of char in physical line */
uint16 lineno; /* physical line number */ uint16 lineno; /* physical line number */
bool operator <(const JSTokenPtr& bptr) {
return lineno < bptr.lineno ||
(lineno == bptr.lineno && index < bptr.index);
}
bool operator <=(const JSTokenPtr& bptr) {
return lineno < bptr.lineno ||
(lineno == bptr.lineno && index <= bptr.index);
}
bool operator >(const JSTokenPtr& bptr) {
return !(*this <= bptr);
}
bool operator >=(const JSTokenPtr& bptr) {
return !(*this < bptr);
}
}; };
struct JSTokenPos { struct JSTokenPos {
JSTokenPtr begin; /* first character and line of token */ JSTokenPtr begin; /* first character and line of token */
JSTokenPtr end; /* index 1 past last char, last line */ JSTokenPtr end; /* index 1 past last char, last line */
bool operator <(const JSTokenPos& bpos) {
return begin < bpos.begin;
}
bool operator <=(const JSTokenPos& bpos) {
return begin <= bpos.begin;
}
bool operator >(const JSTokenPos& bpos) {
return !(*this <= bpos);
}
bool operator >=(const JSTokenPos& bpos) {
return !(*this < bpos);
}
}; };
struct JSToken { struct JSToken {
@@ -336,9 +298,6 @@ struct JSTokenStream {
/* Ignore keywords and return TOK_NAME instead to the parser. */ /* Ignore keywords and return TOK_NAME instead to the parser. */
#define TSF_KEYWORD_IS_NAME 0x4000 #define TSF_KEYWORD_IS_NAME 0x4000
/* Parsing a destructuring object or array initialiser pattern. */
#define TSF_DESTRUCTURING 0x8000
/* Unicode separators that are treated as line terminators, in addition to \n, \r */ /* Unicode separators that are treated as line terminators, in addition to \n, \r */
#define LINE_SEPARATOR 0x2028 #define LINE_SEPARATOR 0x2028
#define PARA_SEPARATOR 0x2029 #define PARA_SEPARATOR 0x2029

View File

@@ -263,9 +263,9 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
* jsparse.c to optimize based on identity of run- and compile-time scope. * jsparse.c to optimize based on identity of run- and compile-time scope.
*/ */
tcflags = 0; tcflags = 0;
script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags, script = js_CompileScript(cx, scopeobj, NULL, principals, tcflags,
STRING_CHARS(str), JSSTRING_LENGTH(str), JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
NULL, file, line); NULL, file, line);
if (!script) if (!script)
return JS_FALSE; return JS_FALSE;
@@ -457,7 +457,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
version = (uint32)script->version | (script->nfixed << 16); version = (uint32)script->version | (script->nfixed << 16);
lineno = (uint32)script->lineno; lineno = (uint32)script->lineno;
nslots = (uint32)script->nslots; nslots = (uint32)script->nslots;
nslots = (uint32)((script->staticLevel << 16) | script->nslots); nslots = (uint32)((script->staticDepth << 16) | script->nslots);
natoms = (uint32)script->atomMap.length; natoms = (uint32)script->atomMap.length;
/* Count the srcnotes, keeping notes pointing at the first one. */ /* Count the srcnotes, keeping notes pointing at the first one. */
@@ -582,7 +582,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
} }
script->lineno = (uintN)lineno; script->lineno = (uintN)lineno;
script->nslots = (uint16)nslots; script->nslots = (uint16)nslots;
script->staticLevel = (uint16)(nslots >> 16); script->staticDepth = (uint16)(nslots >> 16);
} }
for (i = 0; i != natoms; ++i) { for (i = 0; i != natoms; ++i) {
@@ -983,7 +983,7 @@ js_alloc_table_space(void *priv, size_t size)
} }
static void static void
js_free_table_space(void *priv, void *item, size_t size) js_free_table_space(void *priv, void *item)
{ {
free(item); free(item);
} }
@@ -1145,14 +1145,6 @@ SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 flags)
sfp->flags |= flags; sfp->flags |= flags;
} }
#ifdef JS_FUNCTION_METERING
size_t len = strlen(sfe->filename);
if (len >= sizeof rt->lastScriptFilename)
len = sizeof rt->lastScriptFilename - 1;
memcpy(rt->lastScriptFilename, sfe->filename, len);
rt->lastScriptFilename[len] = '\0';
#endif
return sfe; return sfe;
} }
@@ -1479,14 +1471,14 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
script->main += prologLength; script->main += prologLength;
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode)); memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode)); memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
nfixed = (cg->flags & TCF_IN_FUNCTION) nfixed = (cg->treeContext.flags & TCF_IN_FUNCTION)
? cg->fun->u.i.nvars ? cg->treeContext.u.fun->u.i.nvars
: cg->ngvars + cg->regexpList.length; : cg->treeContext.ngvars + cg->regexpList.length;
JS_ASSERT(nfixed < SLOTNO_LIMIT); JS_ASSERT(nfixed < SLOTNO_LIMIT);
script->nfixed = (uint16) nfixed; script->nfixed = (uint16) nfixed;
js_InitAtomMap(cx, &script->atomMap, &cg->atomList); js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
filename = cg->compiler->tokenStream.filename; filename = cg->treeContext.parseContext->tokenStream.filename;
if (filename) { if (filename) {
script->filename = js_SaveScriptFilename(cx, filename); script->filename = js_SaveScriptFilename(cx, filename);
if (!script->filename) if (!script->filename)
@@ -1499,8 +1491,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
goto bad; goto bad;
} }
script->nslots = script->nfixed + cg->maxStackDepth; script->nslots = script->nfixed + cg->maxStackDepth;
script->staticLevel = cg->staticLevel; script->staticDepth = cg->staticDepth;
script->principals = cg->compiler->principals; script->principals = cg->treeContext.parseContext->principals;
if (script->principals) if (script->principals)
JSPRINCIPALS_HOLD(cx, script->principals); JSPRINCIPALS_HOLD(cx, script->principals);
@@ -1509,17 +1501,17 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
if (cg->ntrynotes != 0) if (cg->ntrynotes != 0)
js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script)); js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script));
if (cg->objectList.length != 0) if (cg->objectList.length != 0)
cg->objectList.finish(JS_SCRIPT_OBJECTS(script)); FinishParsedObjects(&cg->objectList, JS_SCRIPT_OBJECTS(script));
if (cg->regexpList.length != 0) if (cg->regexpList.length != 0)
cg->regexpList.finish(JS_SCRIPT_REGEXPS(script)); FinishParsedObjects(&cg->regexpList, JS_SCRIPT_REGEXPS(script));
if (cg->flags & TCF_NO_SCRIPT_RVAL) if (cg->treeContext.flags & TCF_NO_SCRIPT_RVAL)
script->flags |= JSSF_NO_SCRIPT_RVAL; script->flags |= JSSF_NO_SCRIPT_RVAL;
if (cg->upvarList.count != 0) { if (cg->upvarList.count != 0) {
JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length); JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector, memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector,
cg->upvarList.count * sizeof(uint32)); cg->upvarList.count * sizeof(uint32));
cg->upvarList.clear(); ATOM_LIST_INIT(&cg->upvarList);
JS_free(cx, cg->upvarMap.vector); JS_free(cx, cg->upvarMap.vector);
cg->upvarMap.vector = NULL; cg->upvarMap.vector = NULL;
} }
@@ -1529,8 +1521,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
* so that the debugger has a valid FUN_SCRIPT(fun). * so that the debugger has a valid FUN_SCRIPT(fun).
*/ */
fun = NULL; fun = NULL;
if (cg->flags & TCF_IN_FUNCTION) { if (cg->treeContext.flags & TCF_IN_FUNCTION) {
fun = cg->fun; fun = cg->treeContext.u.fun;
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun)); JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
JS_ASSERT_IF(script->upvarsOffset != 0, JS_ASSERT_IF(script->upvarsOffset != 0,
JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars); JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars);
@@ -1540,7 +1532,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
#ifdef CHECK_SCRIPT_OWNER #ifdef CHECK_SCRIPT_OWNER
script->owner = NULL; script->owner = NULL;
#endif #endif
if (cg->flags & TCF_FUN_HEAVYWEIGHT) if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
fun->flags |= JSFUN_HEAVYWEIGHT; fun->flags |= JSFUN_HEAVYWEIGHT;
} }

View File

@@ -86,7 +86,6 @@ typedef struct JSUpvarArray {
uint32 length; /* count of indexed upvar cookies */ uint32 length; /* count of indexed upvar cookies */
} JSUpvarArray; } JSUpvarArray;
#define FREE_UPVAR_COOKIE 0xffffffff
#define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot)) #define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot))
#define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16) #define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16)
#define UPVAR_FRAME_SLOT(cookie) ((uint16)(cookie)) #define UPVAR_FRAME_SLOT(cookie) ((uint16)(cookie))
@@ -119,7 +118,7 @@ struct JSScript {
const char *filename; /* source filename or null */ const char *filename; /* source filename or null */
uint32 lineno; /* base line number of script */ uint32 lineno; /* base line number of script */
uint16 nslots; /* vars plus maximum stack depth */ uint16 nslots; /* vars plus maximum stack depth */
uint16 staticLevel;/* static level for display maintenance */ uint16 staticDepth;/* static depth for display maintenance */
JSPrincipals *principals;/* principals for this script */ JSPrincipals *principals;/* principals for this script */
union { union {
JSObject *object; /* optional Script-class object wrapper */ JSObject *object; /* optional Script-class object wrapper */

View File

@@ -3123,7 +3123,7 @@ js_CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) { for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
GlobalState &state = tm->globalStates[i]; GlobalState &state = tm->globalStates[i];
if (state.globalShape == uint32(-1)) { if (state.globalShape == (uint32) -1) {
state.globalObj = globalObj; state.globalObj = globalObj;
state.globalShape = globalShape; state.globalShape = globalShape;
JS_ASSERT(state.globalSlots); JS_ASSERT(state.globalSlots);
@@ -3315,8 +3315,8 @@ js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
newifp->frame.regs->sp = newsp + script->nfixed; newifp->frame.regs->sp = newsp + script->nfixed;
newifp->frame.imacpc = NULL; newifp->frame.imacpc = NULL;
newifp->frame.slots = newsp; newifp->frame.slots = newsp;
if (script->staticLevel < JS_DISPLAY_SIZE) { if (script->staticDepth < JS_DISPLAY_SIZE) {
JSStackFrame **disp = &cx->display[script->staticLevel]; JSStackFrame **disp = &cx->display[script->staticDepth];
newifp->frame.displaySave = *disp; newifp->frame.displaySave = *disp;
*disp = &newifp->frame; *disp = &newifp->frame;
} }
@@ -4252,7 +4252,7 @@ LeaveTree(InterpState& state, VMSideExit* lr)
#ifdef DEBUG #ifdef DEBUG
// Verify that our state restoration worked. // Verify that our state restoration worked.
for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) { for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) {
JS_ASSERT_IF(fp->callee, JSVAL_IS_OBJECT(fp->argv[-1])); JS_ASSERT(!fp->callee || JSVAL_IS_OBJECT(fp->argv[-1]));
JS_ASSERT_IF(fp->callee && fp->thisp != JSVAL_TO_OBJECT(fp->argv[-1]), JS_ASSERT_IF(fp->callee && fp->thisp != JSVAL_TO_OBJECT(fp->argv[-1]),
!(fp->flags & JSFRAME_COMPUTED_THIS) && !fp->thisp); !(fp->flags & JSFRAME_COMPUTED_THIS) && !fp->thisp);
} }
@@ -4394,7 +4394,9 @@ TraceRecorder::monitorRecording(JSContext* cx, TraceRecorder* tr, JSOp op)
tr->pendingTraceableNative = NULL; tr->pendingTraceableNative = NULL;
debug_only_v(js_Disassemble1(cx, cx->fp->script, cx->fp->regs->pc, debug_only_v(js_Disassemble1(cx, cx->fp->script, cx->fp->regs->pc,
cx->fp->imacpc ? 0 : cx->fp->regs->pc - cx->fp->script->code, (cx->fp->imacpc)
? 0
: cx->fp->regs->pc - cx->fp->script->code,
!cx->fp->imacpc, stdout);) !cx->fp->imacpc, stdout);)
/* If op is not a break or a return from a loop, continue recording and follow the /* If op is not a break or a return from a loop, continue recording and follow the
@@ -6095,20 +6097,15 @@ TraceRecorder::stobj_get_fslot(LIns* obj_ins, unsigned slot)
return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval)); return lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, fslots) + slot * sizeof(jsval));
} }
LIns*
TraceRecorder::stobj_get_dslot(LIns* obj_ins, unsigned index, LIns*& dslots_ins)
{
if (!dslots_ins)
dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
return lir->insLoad(LIR_ldp, dslots_ins, index * sizeof(jsval));
}
LIns* LIns*
TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins) TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins)
{ {
if (slot < JS_INITIAL_NSLOTS) if (slot < JS_INITIAL_NSLOTS)
return stobj_get_fslot(obj_ins, slot); return stobj_get_fslot(obj_ins, slot);
return stobj_get_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins);
if (!dslots_ins)
dslots_ins = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
return lir->insLoad(LIR_ldp, dslots_ins, (slot - JS_INITIAL_NSLOTS) * sizeof(jsval));
} }
bool bool
@@ -6123,7 +6120,7 @@ TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns*& dslots_i
bool bool
TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop, TraceRecorder::native_get(LIns* obj_ins, LIns* pobj_ins, JSScopeProperty* sprop,
LIns*& dslots_ins, LIns*& v_ins) LIns*& dslots_ins, LIns*& v_ins)
{ {
if (!SPROP_HAS_STUB_GETTER(sprop)) if (!SPROP_HAS_STUB_GETTER(sprop))
return false; return false;
@@ -6274,17 +6271,13 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
idx_ins, idx_ins,
stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)), stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
NULL); NULL);
/* If dslots is NULL, stay on trace (and read value as undefined). */ /* If dslots is NULL, stay on trace (and read value as undefined). */
LIns* br2 = lir->insBranch(LIR_jt, lir->ins_eq0(dslots_ins), NULL); LIns* br2 = lir->insBranch(LIR_jt, lir->ins_eq0(dslots_ins), NULL);
/* If not idx < capacity, stay on trace (and read value as undefined). */ /* If not idx < capacity, stay on trace (and read value as undefined). */
LIns* br3 = lir->insBranch(LIR_jf, LIns* br3 = lir->insBranch(LIR_jf,
lir->ins2(LIR_ult, lir->ins2(LIR_ult,
idx_ins, idx_ins,
lir->insLoad(LIR_ldp, lir->insLoad(LIR_ldp, dslots_ins, 0 - (int)sizeof(jsval))),
dslots_ins,
-(int)sizeof(jsval))),
NULL); NULL);
lir->insGuard(LIR_x, lir->insImm(1), snapshot(exitType)); lir->insGuard(LIR_x, lir->insImm(1), snapshot(exitType));
LIns* label = lir->ins0(LIR_label); LIns* label = lir->ins0(LIR_label);
@@ -6835,17 +6828,7 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins)
} }
bool bool
TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins) TraceRecorder::newArray(JSObject *ctor, uint32 argc, jsval *argv, jsval *rval)
{
JSObject* proto;
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &proto))
return false;
proto_ins = INS_CONSTPTR(proto);
return true;
}
bool
TraceRecorder::newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* rval)
{ {
LIns *proto_ins, *arr_ins; LIns *proto_ins, *arr_ins;
if (!getClassPrototype(ctor, proto_ins)) if (!getClassPrototype(ctor, proto_ins))
@@ -7748,67 +7731,12 @@ TraceRecorder::record_JSOP_CALLNAME()
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_GETUPVAR() TraceRecorder::record_JSOP_GETUPVAR()
{ {
uintN index = GET_UINT16(cx->fp->regs->pc); return false;
JSScript *script = cx->fp->script;
JSUpvarArray* uva = JS_SCRIPT_UPVARS(script);
JS_ASSERT(index < uva->length);
uintN skip = UPVAR_FRAME_SKIP(uva->vector[index]);
if (skip > callDepth)
ABORT_TRACE("upvar out of reach");
JSStackFrame* fp2 = cx->display[script->staticLevel - skip];
JS_ASSERT(fp2->script);
uintN slot = UPVAR_FRAME_SLOT(uva->vector[index]);
jsval* vp;
if (!fp2->fun) {
vp = fp2->slots + fp2->script->nfixed;
} else if (slot < fp2->fun->nargs) {
vp = fp2->argv;
} else {
slot -= fp2->fun->nargs;
JS_ASSERT(slot < fp2->script->nslots);
vp = fp2->slots;
}
stack(0, get(&vp[slot]));
return true;
} }
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_CALLUPVAR() TraceRecorder::record_JSOP_CALLUPVAR()
{ {
if (record_JSOP_GETUPVAR()) {
stack(1, INS_CONSTPTR(NULL));
return true;
}
return false;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_GETDSLOT()
{
JSObject* callee = cx->fp->callee;
LIns* callee_ins = (callDepth == 0) ? get(&cx->fp->argv[-2]) : INS_CONSTPTR(callee);
unsigned index = GET_UINT16(cx->fp->regs->pc);
LIns* dslots_ins = NULL;
LIns* v_ins = stobj_get_dslot(callee_ins, index, dslots_ins);
unbox_jsval(callee->dslots[index], v_ins);
stack(0, v_ins);
return true;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_CALLDSLOT()
{
if (record_JSOP_GETDSLOT()) {
stack(1, INS_CONSTPTR(NULL));
return true;
}
return false; return false;
} }
@@ -8522,12 +8450,13 @@ JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NEWINIT() TraceRecorder::record_JSOP_NEWINIT()
{ {
JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc)); JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc));
LIns *proto_ins; JSObject* obj;
if (!getClassPrototype(key, proto_ins)) const CallInfo *ci;
return false;
LIns* args[] = { proto_ins, cx_ins }; if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &obj))
const CallInfo *ci = (key == JSProto_Array) ? &js_FastNewArray_ci : &js_Object_tn_ci; return false;
ci = (key == JSProto_Array) ? &js_FastNewArray_ci : &js_Object_tn_ci;
LIns* args[] = { INS_CONSTPTR(obj), cx_ins };
LIns* v_ins = lir->insCall(ci, args); LIns* v_ins = lir->insCall(ci, args);
guard(false, lir->ins_eq0(v_ins), OOM_EXIT); guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
stack(0, v_ins); stack(0, v_ins);
@@ -8895,12 +8824,6 @@ TraceRecorder::record_JSOP_DEFFUN()
return false; return false;
} }
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_DEFFUN_FC()
{
return false;
}
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_DEFCONST() TraceRecorder::record_JSOP_DEFCONST()
{ {
@@ -8913,46 +8836,38 @@ TraceRecorder::record_JSOP_DEFVAR()
return false; return false;
} }
jsatomid /*
TraceRecorder::getFullIndex(ptrdiff_t pcoff) * XXX could hoist out to jsinterp.h and share with jsinterp.cpp, but
{ * XXX jsopcode.cpp has different definitions of same-named macros.
jsatomid index = GET_INDEX(cx->fp->regs->pc + pcoff); */
index += atoms - cx->fp->script->atomMap.vector; #define GET_FULL_INDEX(PCOFF) \
return index; (atoms - script->atomMap.vector + GET_INDEX(regs.pc + PCOFF))
}
#define LOAD_FUNCTION(PCOFF) \
JS_GET_SCRIPT_FUNCTION(script, GET_FULL_INDEX(PCOFF), fun)
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_LAMBDA() TraceRecorder::record_JSOP_ANONFUNOBJ()
{ {
JSFunction* fun; JSFunction* fun;
JS_GET_SCRIPT_FUNCTION(cx->fp->script, getFullIndex(), fun); JSFrameRegs& regs = *cx->fp->regs;
JSScript* script = cx->fp->script;
LOAD_FUNCTION(0); // needs script, regs, fun
if (FUN_NULL_CLOSURE(fun) && OBJ_GET_PARENT(cx, FUN_OBJECT(fun)) == globalObj) { JSObject* obj = FUN_OBJECT(fun);
LIns *proto_ins; if (OBJ_GET_PARENT(cx, obj) != cx->fp->scopeChain)
if (!getClassPrototype(JSProto_Function, proto_ins)) ABORT_TRACE("can't trace with activation object on scopeChain");
return false;
LIns* args[] = { INS_CONSTPTR(globalObj), proto_ins, INS_CONSTPTR(fun), cx_ins }; stack(0, INS_CONSTPTR(obj));
LIns* x = lir->insCall(&js_NewNullClosure_ci, args);
stack(0, x);
return true;
}
return false;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_LAMBDA_FC()
{
return false;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_CALLEE()
{
stack(0, INS_CONSTPTR(cx->fp->callee));
return true; return true;
} }
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NAMEDFUNOBJ()
{
return false;
}
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_SETLOCALPOP() TraceRecorder::record_JSOP_SETLOCALPOP()
{ {
@@ -9021,30 +8936,12 @@ TraceRecorder::record_JSOP_ARGCNT()
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj) TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj)
{ {
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, obj); var(slot, INS_CONSTPTR(obj));
if (FUN_NULL_CLOSURE(fun) && OBJ_GET_PARENT(cx, FUN_OBJECT(fun)) == globalObj) {
LIns *proto_ins;
if (!getClassPrototype(JSProto_Function, proto_ins))
return false;
LIns* args[] = { INS_CONSTPTR(globalObj), proto_ins, INS_CONSTPTR(fun), cx_ins };
LIns* x = lir->insCall(&js_NewNullClosure_ci, args);
var(slot, x);
return true;
}
return false;
}
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_DEFLOCALFUN()
{
return true; return true;
} }
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_DEFLOCALFUN_FC() TraceRecorder::record_JSOP_DEFLOCALFUN()
{ {
return true; return true;
} }
@@ -9566,8 +9463,10 @@ TraceRecorder::record_JSOP_TYPEOFEXPR()
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_ENTERBLOCK() TraceRecorder::record_JSOP_ENTERBLOCK()
{ {
JSScript* script = cx->fp->script;
JSFrameRegs& regs = *cx->fp->regs;
JSObject* obj; JSObject* obj;
JS_GET_SCRIPT_OBJECT(cx->fp->script, getFullIndex(0), obj); JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(0), obj);
LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID)); LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++) for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++)
@@ -9838,6 +9737,13 @@ TraceRecorder::record_JSOP_CALLBUILTIN()
return true; return true;
} }
JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NULLTHIS()
{
stack(0, INS_CONSTPTR(NULL));
return true;
}
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_INT8() TraceRecorder::record_JSOP_INT8()
{ {
@@ -9909,13 +9815,14 @@ TraceRecorder::record_JSOP_LENGTH()
JS_REQUIRES_STACK bool JS_REQUIRES_STACK bool
TraceRecorder::record_JSOP_NEWARRAY() TraceRecorder::record_JSOP_NEWARRAY()
{ {
LIns *proto_ins; JSObject* proto;
if (!getClassPrototype(JSProto_Array, proto_ins)) const CallInfo* ci = &js_NewUninitializedArray_ci;
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(JSProto_Array), &proto))
return false; return false;
uint32 len = GET_UINT24(cx->fp->regs->pc); uint32 len = GET_UINT24(cx->fp->regs->pc);
LIns* args[] = { lir->insImm(len), proto_ins, cx_ins }; LIns* args[] = { lir->insImm(len), INS_CONSTPTR(proto), cx_ins };
LIns* v_ins = lir->insCall(&js_NewUninitializedArray_ci, args); LIns* v_ins = lir->insCall(ci, args);
guard(false, lir->ins_eq0(v_ins), OOM_EXIT); guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
LIns* dslots_ins = NULL; LIns* dslots_ins = NULL;
@@ -9988,3 +9895,12 @@ js_DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, ui
JS_NOT_REACHED("JSOP_UNUSED" # n); \ JS_NOT_REACHED("JSOP_UNUSED" # n); \
return false; \ return false; \
} }
UNUSED(203)
UNUSED(204)
UNUSED(205)
UNUSED(206)
UNUSED(207)
UNUSED(208)
UNUSED(209)
UNUSED(219)

View File

@@ -512,8 +512,6 @@ class TraceRecorder : public avmplus::GCObject {
nanojit::LIns* v_ins, const char *name); nanojit::LIns* v_ins, const char *name);
nanojit::LIns* stobj_get_fslot(nanojit::LIns* obj_ins, unsigned slot); nanojit::LIns* stobj_get_fslot(nanojit::LIns* obj_ins, unsigned slot);
nanojit::LIns* stobj_get_dslot(nanojit::LIns* obj_ins, unsigned index,
nanojit::LIns*& dslots_ins);
nanojit::LIns* stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot, nanojit::LIns* stobj_get_slot(nanojit::LIns* obj_ins, unsigned slot,
nanojit::LIns*& dslots_ins); nanojit::LIns*& dslots_ins);
bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop, bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop,
@@ -543,7 +541,6 @@ class TraceRecorder : public avmplus::GCObject {
void clearFrameSlotsFromCache(); void clearFrameSlotsFromCache();
JS_REQUIRES_STACK bool guardCallee(jsval& callee); JS_REQUIRES_STACK bool guardCallee(jsval& callee);
JS_REQUIRES_STACK bool getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins); JS_REQUIRES_STACK bool getClassPrototype(JSObject* ctor, nanojit::LIns*& proto_ins);
JS_REQUIRES_STACK bool getClassPrototype(JSProtoKey key, nanojit::LIns*& proto_ins);
JS_REQUIRES_STACK bool newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* vp); JS_REQUIRES_STACK bool newArray(JSObject* ctor, uint32 argc, jsval* argv, jsval* vp);
JS_REQUIRES_STACK bool newString(JSObject* ctor, jsval& arg, jsval* rval); JS_REQUIRES_STACK bool newString(JSObject* ctor, jsval& arg, jsval* rval);
JS_REQUIRES_STACK bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc, JS_REQUIRES_STACK bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc,
@@ -562,8 +559,6 @@ class TraceRecorder : public avmplus::GCObject {
bool hasMethod(JSObject* obj, jsid id); bool hasMethod(JSObject* obj, jsid id);
JS_REQUIRES_STACK bool hasIteratorMethod(JSObject* obj); JS_REQUIRES_STACK bool hasIteratorMethod(JSObject* obj);
jsatomid getFullIndex(ptrdiff_t pcoff = 0);
public: public:
JS_REQUIRES_STACK JS_REQUIRES_STACK
TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*, TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
@@ -613,10 +608,9 @@ public:
#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder) #define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder)
#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr)) #define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
#define JSOP_IN_RANGE(op,lo,hi) (uintN((op) - (lo)) <= uintN((hi) - (lo))) #define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
#define JSOP_IS_BINARY(op) JSOP_IN_RANGE(op, JSOP_BITOR, JSOP_MOD) #define JSOP_IS_UNARY(op) ((uintN)((op) - JSOP_NEG) <= (uintN)(JSOP_POS - JSOP_NEG))
#define JSOP_IS_UNARY(op) JSOP_IN_RANGE(op, JSOP_NEG, JSOP_POS) #define JSOP_IS_EQUALITY(op) ((uintN)((op) - JSOP_EQ) <= (uintN)(JSOP_NE - JSOP_EQ))
#define JSOP_IS_EQUALITY(op) JSOP_IN_RANGE(op, JSOP_EQ, JSOP_NE)
#define TRACE_ARGS_(x,args) \ #define TRACE_ARGS_(x,args) \
JS_BEGIN_MACRO \ JS_BEGIN_MACRO \

View File

@@ -204,7 +204,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
* before deserialization of bytecode. If the saved version does not match * before deserialization of bytecode. If the saved version does not match
* the current version, abort deserialization and invalidate the file. * the current version, abort deserialization and invalidate the file.
*/ */
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 43) #define JSXDR_BYTECODE_VERSION (0xb973c0de - 42)
/* /*
* Library-private functions. * Library-private functions.

View File

@@ -1261,10 +1261,9 @@ static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/"; static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
static JSObject * static JSObject *
ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn, ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
JSXMLArray *inScopeNSes, JSBool isAttributeName) JSXMLArray *inScopeNSes, JSBool isAttributeName)
{ {
JSContext *cx = jsc->context;
JSString *str, *uri, *prefix, *localName; JSString *str, *uri, *prefix, *localName;
size_t length, offset; size_t length, offset;
const jschar *start, *limit, *colon; const jschar *start, *limit, *colon;
@@ -1314,15 +1313,15 @@ ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn,
} }
if (!uri) { if (!uri) {
js_ReportCompileErrorNumber(jsc->context, &jsc->tokenStream, pn, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
JSREPORT_ERROR, JSREPORT_ERROR,
JSMSG_BAD_XML_NAMESPACE, JSMSG_BAD_XML_NAMESPACE,
js_ValueToPrintableString(jsc->context, js_ValueToPrintableString(cx,
STRING_TO_JSVAL(prefix))); STRING_TO_JSVAL(prefix)));
return NULL; return NULL;
} }
localName = js_NewStringCopyN(jsc->context, colon + 1, length - (offset + 1)); localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
if (!localName) if (!localName)
return NULL; return NULL;
} else { } else {
@@ -1347,12 +1346,12 @@ ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn,
break; break;
} }
} }
prefix = IS_EMPTY(uri) ? jsc->context->runtime->emptyString : NULL; prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
} }
localName = str; localName = str;
} }
return NewXMLQName(jsc->context, uri, prefix, localName); return NewXMLQName(cx, uri, prefix, localName);
} }
static JSString * static JSString *
@@ -1382,10 +1381,9 @@ ChompXMLWhitespace(JSContext *cx, JSString *str)
} }
static JSXML * static JSXML *
ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn, ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
JSXMLArray *inScopeNSes, uintN flags) JSXMLArray *inScopeNSes, uintN flags)
{ {
JSContext *cx = jsc->context;
JSXML *xml, *kid, *attr, *attrj; JSXML *xml, *kid, *attr, *attrj;
JSString *str; JSString *str;
uint32 length, n, i, j; uint32 length, n, i, j;
@@ -1396,7 +1394,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
int stackDummy; int stackDummy;
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) { if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
JSMSG_OVER_RECURSED); JSMSG_OVER_RECURSED);
return NULL; return NULL;
} }
@@ -1415,7 +1413,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
case TOK_XMLELEM: case TOK_XMLELEM:
length = inScopeNSes->length; length = inScopeNSes->length;
pn2 = pn->pn_head; pn2 = pn->pn_head;
xml = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
if (!xml) if (!xml)
goto fail; goto fail;
@@ -1440,7 +1438,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
continue; continue;
} }
kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
if (kid == PN2X_SKIP_CHILD) { if (kid == PN2X_SKIP_CHILD) {
--n; --n;
continue; continue;
@@ -1491,7 +1489,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
continue; continue;
} }
kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags); kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
if (kid == PN2X_SKIP_CHILD) { if (kid == PN2X_SKIP_CHILD) {
--n; --n;
continue; continue;
@@ -1535,7 +1533,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
/* Enforce "Well-formedness constraint: Unique Att Spec". */ /* Enforce "Well-formedness constraint: Unique Att Spec". */
for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) { for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
if (pn3->pn_atom == pn2->pn_atom) { if (pn3->pn_atom == pn2->pn_atom) {
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
JSREPORT_ERROR, JSREPORT_ERROR,
JSMSG_DUPLICATE_XML_ATTR, JSMSG_DUPLICATE_XML_ATTR,
js_ValueToPrintableString(cx, js_ValueToPrintableString(cx,
@@ -1620,7 +1618,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
/* Second pass: process tag name and attributes, using namespaces. */ /* Second pass: process tag name and attributes, using namespaces. */
pn2 = pn->pn_head; pn2 = pn->pn_head;
qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_FALSE); qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
if (!qn) if (!qn)
goto fail; goto fail;
xml->name = qn; xml->name = qn;
@@ -1631,7 +1629,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
goto fail; goto fail;
for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) { for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_TRUE); qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
if (!qn) { if (!qn) {
xml->xml_attrs.length = i; xml->xml_attrs.length = i;
goto fail; goto fail;
@@ -1646,7 +1644,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
attrjqn = attrj->name; attrjqn = attrj->name;
if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) && if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) { js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
JSREPORT_ERROR, JSREPORT_ERROR,
JSMSG_DUPLICATE_XML_ATTR, JSMSG_DUPLICATE_XML_ATTR,
js_ValueToPrintableString(cx, js_ValueToPrintableString(cx,
@@ -1687,7 +1685,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
xml_class = JSXML_CLASS_COMMENT; xml_class = JSXML_CLASS_COMMENT;
} else if (pn->pn_type == TOK_XMLPI) { } else if (pn->pn_type == TOK_XMLPI) {
if (IS_XML(str)) { if (IS_XML(str)) {
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
JSREPORT_ERROR, JSREPORT_ERROR,
JSMSG_RESERVED_ID, JSMSG_RESERVED_ID,
js_ValueToPrintableString(cx, js_ValueToPrintableString(cx,
@@ -1698,7 +1696,7 @@ ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS) if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
goto skip_child; goto skip_child;
qn = ParseNodeToQName(jsc, pn, inScopeNSes, JS_FALSE); qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
if (!qn) if (!qn)
goto fail; goto fail;
@@ -1736,7 +1734,7 @@ skip_child:
#undef PN2X_SKIP_CHILD #undef PN2X_SKIP_CHILD
syntax: syntax:
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR, js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
JSMSG_BAD_XML_MARKUP); JSMSG_BAD_XML_MARKUP);
fail: fail:
js_LeaveLocalRootScope(cx); js_LeaveLocalRootScope(cx);
@@ -1830,6 +1828,7 @@ ParseXMLSource(JSContext *cx, JSString *src)
jschar *chars; jschar *chars;
const jschar *srcp, *endp; const jschar *srcp, *endp;
JSXML *xml; JSXML *xml;
JSParseContext pc;
const char *filename; const char *filename;
uintN lineno; uintN lineno;
JSStackFrame *fp; JSStackFrame *fp;
@@ -1892,19 +1891,20 @@ ParseXMLSource(JSContext *cx, JSString *src)
} }
} }
{ if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
JSCompiler jsc(cx); filename, lineno))
if (jsc.init(chars, length, NULL, filename, lineno)) { goto out;
pn = jsc.parseXMLText(js_GetTopStackFrame(cx)->scopeChain, false); pn = js_ParseXMLText(cx, js_GetTopStackFrame(cx)->scopeChain, &pc,
if (pn && XMLArrayInit(cx, &nsarray, 1)) { JS_FALSE);
if (GetXMLSettingFlags(cx, &flags)) if (pn && XMLArrayInit(cx, &nsarray, 1)) {
xml = ParseNodeToXML(&jsc, pn, &nsarray, flags); if (GetXMLSettingFlags(cx, &flags))
xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
XMLArrayFinish(cx, &nsarray); XMLArrayFinish(cx, &nsarray);
}
}
} }
js_FinishParseContext(cx, &pc);
out:
JS_free(cx, chars); JS_free(cx, chars);
return xml; return xml;
@@ -7433,24 +7433,24 @@ js_FinalizeXML(JSContext *cx, JSXML *xml)
} }
JSObject * JSObject *
js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn) js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn)
{ {
jsval nsval; jsval nsval;
JSObject *ns; JSObject *ns;
JSXMLArray nsarray; JSXMLArray nsarray;
JSXML *xml; JSXML *xml;
if (!js_GetDefaultXMLNamespace(jsc->context, &nsval)) if (!js_GetDefaultXMLNamespace(cx, &nsval))
return NULL; return NULL;
JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval)); JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
ns = JSVAL_TO_OBJECT(nsval); ns = JSVAL_TO_OBJECT(nsval);
if (!XMLArrayInit(jsc->context, &nsarray, 1)) if (!XMLArrayInit(cx, &nsarray, 1))
return NULL; return NULL;
XMLARRAY_APPEND(jsc->context, &nsarray, ns); XMLARRAY_APPEND(cx, &nsarray, ns);
xml = ParseNodeToXML(jsc, pn, &nsarray, XSF_PRECOMPILED_ROOT); xml = ParseNodeToXML(cx, pc, pn, &nsarray, XSF_PRECOMPILED_ROOT);
XMLArrayFinish(jsc->context, &nsarray); XMLArrayFinish(cx, &nsarray);
if (!xml) if (!xml)
return NULL; return NULL;

View File

@@ -160,7 +160,7 @@ extern void
js_FinalizeXML(JSContext *cx, JSXML *xml); js_FinalizeXML(JSContext *cx, JSXML *xml);
extern JSObject * extern JSObject *
js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn); js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn);
extern JSObject * extern JSObject *
js_NewXMLObject(JSContext *cx, JSXMLClass xml_class); js_NewXMLObject(JSContext *cx, JSXMLClass xml_class);

View File

@@ -62,7 +62,6 @@
#include "jsemit.h" #include "jsemit.h"
#include "jsfun.h" #include "jsfun.h"
#include "jsgc.h" #include "jsgc.h"
#include "jsiter.h"
#include "jslock.h" #include "jslock.h"
#include "jsnum.h" #include "jsnum.h"
#include "jsobj.h" #include "jsobj.h"
@@ -1349,31 +1348,22 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp)
static JSScript * static JSScript *
ValueToScript(JSContext *cx, jsval v) ValueToScript(JSContext *cx, jsval v)
{ {
JSScript *script = NULL; JSScript *script;
JSFunction *fun; JSFunction *fun;
if (!JSVAL_IS_PRIMITIVE(v)) { if (!JSVAL_IS_PRIMITIVE(v) &&
JSObject *obj = JSVAL_TO_OBJECT(v); JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
JSClass *clasp = JS_GET_CLASS(cx, obj); script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
} else {
if (clasp == &js_ScriptClass) {
script = (JSScript *) JS_GetPrivate(cx, obj);
} else if (clasp == &js_GeneratorClass) {
JSGenerator *gen = (JSGenerator *) JS_GetPrivate(cx, obj);
fun = gen->frame.fun;
script = FUN_SCRIPT(fun);
}
}
if (!script) {
fun = JS_ValueToFunction(cx, v); fun = JS_ValueToFunction(cx, v);
if (!fun) if (!fun)
return NULL; return NULL;
script = FUN_SCRIPT(fun); script = FUN_SCRIPT(fun);
if (!script) { }
JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_SCRIPTS_ONLY); if (!script) {
} JS_ReportErrorNumber(cx, my_GetErrorMessage, NULL,
JSSMSG_SCRIPTS_ONLY);
} }
return script; return script;
@@ -1716,7 +1706,7 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
return JS_FALSE; return JS_FALSE;
if (VALUE_IS_FUNCTION(cx, argv[i])) { if (VALUE_IS_FUNCTION(cx, argv[i])) {
JSFunction *fun = JS_ValueToFunction(cx, argv[i]); JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
if (fun && (fun->flags & ~7U)) { if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
uint16 flags = fun->flags; uint16 flags = fun->flags;
fputs("flags:", stdout); fputs("flags:", stdout);
@@ -1731,14 +1721,9 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
SHOW_FLAG(THISP_NUMBER); SHOW_FLAG(THISP_NUMBER);
SHOW_FLAG(THISP_BOOLEAN); SHOW_FLAG(THISP_BOOLEAN);
SHOW_FLAG(EXPR_CLOSURE); SHOW_FLAG(EXPR_CLOSURE);
SHOW_FLAG(TRACEABLE); SHOW_FLAG(INTERPRETED);
#undef SHOW_FLAG #undef SHOW_FLAG
if (FUN_NULL_CLOSURE(fun))
fputs(" NULL_CLOSURE", stdout);
else if (FUN_FLAT_CLOSURE(fun))
fputs(" FLAT_CLOSURE", stdout);
putchar('\n'); putchar('\n');
} }
} }
@@ -2266,16 +2251,13 @@ Intern(JSContext *cx, uintN argc, jsval *vp)
static JSBool static JSBool
Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{ {
JSFunction *fun;
JSObject *funobj, *parent, *clone; JSObject *funobj, *parent, *clone;
if (VALUE_IS_FUNCTION(cx, argv[0])) { fun = JS_ValueToFunction(cx, argv[0]);
funobj = JSVAL_TO_OBJECT(argv[0]); if (!fun)
} else { return JS_FALSE;
JSFunction *fun = JS_ValueToFunction(cx, argv[0]); funobj = JS_GetFunctionObject(fun);
if (!fun)
return JS_FALSE;
funobj = JS_GetFunctionObject(fun);
}
if (argc > 1) { if (argc > 1) {
if (!JS_ValueToObject(cx, argv[1], &parent)) if (!JS_ValueToObject(cx, argv[1], &parent))
return JS_FALSE; return JS_FALSE;

View File

@@ -4490,36 +4490,36 @@ test(testString);
function testToStringBeforeValueOf() function testToStringBeforeValueOf()
{ {
var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } }; var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } };
var a = []; var a = [];
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
a.push(String(o)); a.push(String(o));
return a.join(","); return a.join(",");
} }
testToStringBeforeValueOf.expected = "s,s,s,s,s,s,s,s,s,s"; testToStringBeforeValueOf.expected = "s,s,s,s,s,s,s,s,s,s";
testToStringBeforeValueOf.jitstats = { testToStringBeforeValueOf.jitstats = {
recorderStarted: 1, recorderStarted: 1,
sideExitIntoInterpreter: 1 sideExitIntoInterpreter: 1
}; };
test(testToStringBeforeValueOf); test(testToStringBeforeValueOf);
function testNullToString() function testNullToString()
{ {
var a = []; var a = [];
for (var i = 0; i < 10; i++) for (var i = 0; i < 10; i++)
a.push(String(null)); a.push(String(null));
for (i = 0; i < 10; i++) { for (i = 0; i < 10; i++) {
var t = typeof a[i]; var t = typeof a[i];
if (t != "string") if (t != "string")
a.push(t); a.push(t);
} }
return a.join(","); return a.join(",");
} }
testNullToString.expected = "null,null,null,null,null,null,null,null,null,null"; testNullToString.expected = "null,null,null,null,null,null,null,null,null,null";
testNullToString.jitstats = { testNullToString.jitstats = {
recorderStarted: 2, recorderStarted: 2,
sideExitIntoInterpreter: 2, sideExitIntoInterpreter: 2,
recorderAborted: 0 recorderAborted: 0
}; };
test(testNullToString); test(testNullToString);
@@ -4532,162 +4532,12 @@ function testAddNull()
} }
testAddNull.expected = "null,"; testAddNull.expected = "null,";
testAddNull.jitstats = { testAddNull.jitstats = {
recorderStarted: 1, recorderStarted: 1,
sideExitIntoInterpreter: 1, sideExitIntoInterpreter: 1,
recorderAborted: 0 recorderAborted: 0
}; };
test(testAddNull); test(testAddNull);
function testClosures()
{
function MyObject(id) {
var thisObject = this;
this.id = id;
this.toString = str;
function str() {
return "" + this.id + thisObject.id;
}
}
var a = [];
for (var i = 0; i < 5; i++)
a.push(new MyObject(i));
return a.toString();
}
testClosures.expected = "00,11,22,33,44";
test(testClosures);
function testLambdaInitedVar() {
var jQuery = function (a, b) {
return jQuery && jQuery.length;
}
return jQuery();
}
testLambdaInitedVar.expected = 2;
test(testLambdaInitedVar);
function testNestedEscapingLambdas()
{
try {
return (function() {
var a = [], r = [];
function setTimeout(f, t) {
a.push(f);
}
function runTimeouts() {
for (var i = 0; i < a.length; i++)
a[i]();
}
var $foo = "#nothiddendiv";
setTimeout(function(){
r.push($foo);
setTimeout(function(){
r.push($foo);
}, 100);
}, 100);
runTimeouts();
return r.join("");
})();
} catch (e) {
return e;
}
}
testNestedEscapingLambdas.expected = "#nothiddendiv#nothiddendiv";
test(testNestedEscapingLambdas);
function testPropagatedFunArgs()
{
var win = this;
var res = [], q = [];
function addEventListener(name, func, flag) {
q.push(func);
}
var pageInfo, obs;
addEventListener("load", handleLoad, true);
var observer = {
observe: function(win, topic, data) {
// obs.removeObserver(observer, "page-info-dialog-loaded");
handlePageInfo();
}
};
function handleLoad() {
pageInfo = { toString: function() { return "pageInfo"; } };
obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } };
obs.addObserver(observer, "page-info-dialog-loaded", false);
}
function handlePageInfo() {
res.push(pageInfo);
function $(aId) { res.push(pageInfo); };
var feedTab = $("feedTab");
}
q[0]();
return res.join(',');
}
testPropagatedFunArgs.expected = "pageInfo,pageInfo";
test(testPropagatedFunArgs);
// Second testPropagatedFunArgs test -- this is a crash-test.
(function () {
var escapee;
function testPropagatedFunArgs()
{
const magic = 42;
var win = this;
var res = [], q = [];
function addEventListener(name, func, flag) {
q.push(func);
}
var pageInfo = "pageInfo", obs;
addEventListener("load", handleLoad, true);
var observer = {
observe: function(win, topic, data) {
// obs.removeObserver(observer, "page-info-dialog-loaded");
handlePageInfo();
}
};
function handleLoad() {
//pageInfo = { toString: function() { return "pageInfo"; } };
obs = { addObserver: function (obs, topic, data) { obs.observe(win, topic, data); } };
obs.addObserver(observer, "page-info-dialog-loaded", false);
}
function handlePageInfo() {
res.push(pageInfo);
function $(aId) {
function notSafe() {
return magic;
}
print(notSafe());
res.push(pageInfo);
};
var feedTab = $("feedTab");
}
escapee = q[0];
return res.join(',');
}
testPropagatedFunArgs();
escapee();
})();
function testStringLengthNoTinyId() function testStringLengthNoTinyId()
{ {
var x = "unset"; var x = "unset";

View File

@@ -56,7 +56,7 @@ function test()
var f; var f;
f = function() { (function x() { }); return x; } f = function() { (function x() { }); return x; }
expect = 'function() { return x; }'; expect = 'function() { (function x() { }); return x; }';
actual = f + ''; actual = f + '';
compareSource(expect, actual, summary); compareSource(expect, actual, summary);

View File

@@ -53,7 +53,7 @@ function test()
printBugNumber(BUGNUMBER); printBugNumber(BUGNUMBER);
printStatus (summary); printStatus (summary);
expect = '1'; expect = '2';
// ------- Comment #74 From Jesse Ruderman // ------- Comment #74 From Jesse Ruderman

View File

@@ -53,7 +53,7 @@ function test()
printBugNumber(BUGNUMBER); printBugNumber(BUGNUMBER);
printStatus (summary); printStatus (summary);
expect = '1'; expect = '2';
// ------- Comment #77 From Brendan Eich // ------- Comment #77 From Brendan Eich

View File

@@ -57,11 +57,7 @@ function test()
// Does not require -j: // Does not require -j:
// ===== // =====
try { ({ set x x () { for(x in function(){}){}} })
eval("({ set x x () { for(x in function(){}){}} })");
} catch (e)
{
}
// Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710 // Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710
// ===== // =====

View File

@@ -35,6 +35,7 @@ js1_5/Regress/regress-312588.js
js1_5/Regress/regress-314401.js js1_5/Regress/regress-314401.js
js1_5/Regress/regress-329530.js js1_5/Regress/regress-329530.js
js1_5/Regress/regress-3649-n.js js1_5/Regress/regress-3649-n.js
js1_5/Regress/regress-451322.js
js1_6/extensions/regress-455464-04.js js1_6/extensions/regress-455464-04.js
js1_6/extensions/regress-456826.js js1_6/extensions/regress-456826.js
js1_7/extensions/regress-455982-01.js js1_7/extensions/regress-455982-01.js