mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
upvar2, aka the big one (452598, r=mrbkap).
This commit is contained in:
parent
a2d4d5a779
commit
5f4d9ff474
@ -88,3 +88,4 @@ BUILTIN2(extern, STRING, js_TypeOfBoolean, CONTEXT, INT32,
|
||||
BUILTIN2(extern, DOUBLE, js_BooleanOrUndefinedToNumber, CONTEXT, INT32, 1, 1)
|
||||
BUILTIN2(extern, STRING, js_BooleanOrUndefinedToString, CONTEXT, INT32, 1, 1)
|
||||
BUILTIN1(extern, OBJECT, js_Arguments, CONTEXT, 0, 0)
|
||||
BUILTIN4(extern, OBJECT, js_NewNullClosure, CONTEXT, OBJECT, OBJECT, OBJECT, 0, 0)
|
||||
|
@ -533,7 +533,7 @@ static struct {
|
||||
{
|
||||
/* 0*/ JSOP_SWAP,
|
||||
/* 1*/ JSOP_POP,
|
||||
/* 2*/ JSOP_NULLTHIS,
|
||||
/* 2*/ JSOP_NULL,
|
||||
/* 3*/ JSOP_CALL, 0, 0,
|
||||
/* 6*/ JSOP_STOP,
|
||||
},
|
||||
@ -815,7 +815,7 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_LOOKUPSWITCH */
|
||||
0, /* JSOP_STRICTEQ */
|
||||
0, /* JSOP_STRICTNE */
|
||||
0, /* JSOP_NULLTHIS */
|
||||
0, /* JSOP_SETCALL */
|
||||
3, /* JSOP_ITER */
|
||||
2, /* JSOP_NEXTITER */
|
||||
0, /* JSOP_ENDITER */
|
||||
@ -869,14 +869,14 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_DEFFUN */
|
||||
0, /* JSOP_DEFCONST */
|
||||
0, /* JSOP_DEFVAR */
|
||||
0, /* JSOP_ANONFUNOBJ */
|
||||
0, /* JSOP_NAMEDFUNOBJ */
|
||||
0, /* JSOP_LAMBDA */
|
||||
0, /* JSOP_CALLEE */
|
||||
0, /* JSOP_SETLOCALPOP */
|
||||
0, /* JSOP_IFPRIMTOP */
|
||||
0, /* JSOP_SETCALL */
|
||||
0, /* JSOP_PICK */
|
||||
0, /* JSOP_TRY */
|
||||
0, /* JSOP_FINALLY */
|
||||
0, /* JSOP_OBJTOP */
|
||||
0, /* JSOP_GETDSLOT */
|
||||
0, /* JSOP_CALLDSLOT */
|
||||
0, /* JSOP_ARGSUB */
|
||||
0, /* JSOP_ARGCNT */
|
||||
0, /* JSOP_DEFLOCALFUN */
|
||||
@ -926,8 +926,8 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_XMLCOMMENT */
|
||||
0, /* JSOP_XMLPI */
|
||||
0, /* JSOP_CALLPROP */
|
||||
0, /* JSOP_GETFUNNS */
|
||||
0, /* JSOP_GETUPVAR */
|
||||
0, /* JSOP_CALLUPVAR */
|
||||
0, /* JSOP_DELDESC */
|
||||
0, /* JSOP_UINT24 */
|
||||
0, /* JSOP_INDEXBASE */
|
||||
@ -942,25 +942,17 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_TYPEOFEXPR */
|
||||
0, /* JSOP_ENTERBLOCK */
|
||||
0, /* JSOP_LEAVEBLOCK */
|
||||
0, /* JSOP_PICK */
|
||||
0, /* JSOP_IFPRIMTOP */
|
||||
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_YIELD */
|
||||
0, /* JSOP_ARRAYPUSH */
|
||||
0, /* JSOP_CALLUPVAR */
|
||||
0, /* JSOP_GETFUNNS */
|
||||
0, /* JSOP_ENUMCONSTELEM */
|
||||
0, /* JSOP_LEAVEBLOCKEXPR */
|
||||
0, /* JSOP_GETTHISPROP */
|
||||
0, /* JSOP_GETARGPROP */
|
||||
0, /* JSOP_GETLOCALPROP */
|
||||
0, /* JSOP_UNUSED219 */
|
||||
0, /* JSOP_INDEXBASE1 */
|
||||
0, /* JSOP_INDEXBASE2 */
|
||||
0, /* JSOP_INDEXBASE3 */
|
||||
@ -973,6 +965,10 @@ uint8 js_opcode2extra[JSOP_LIMIT] = {
|
||||
0, /* JSOP_LENGTH */
|
||||
0, /* JSOP_NEWARRAY */
|
||||
0, /* JSOP_HOLE */
|
||||
0, /* JSOP_DEFFUN_FC */
|
||||
0, /* JSOP_DEFLOCALFUN_FC */
|
||||
0, /* JSOP_LAMBDA_FC */
|
||||
0, /* JSOP_OBJTOP */
|
||||
0, /* JSOP_LOOP */
|
||||
};
|
||||
#define JSOP_IS_IMACOP(x) (0 \
|
||||
|
@ -84,7 +84,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end equality
|
||||
|
||||
.igroup binary JSOP_BITOR-JSOP_MOD
|
||||
|
||||
@ -175,7 +175,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end binary
|
||||
|
||||
.igroup add JSOP_ADD
|
||||
|
||||
@ -266,7 +266,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end add
|
||||
|
||||
.igroup unary JSOP_NEG-JSOP_POS
|
||||
|
||||
@ -292,7 +292,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end unary
|
||||
|
||||
.igroup call JSOP_CALL
|
||||
|
||||
@ -344,9 +344,10 @@
|
||||
stop # strobj
|
||||
.end
|
||||
|
||||
.end
|
||||
.end call
|
||||
|
||||
.igroup apply JSOP_APPLY
|
||||
|
||||
.imacro apply0 # apply fun this arr
|
||||
pick 3 # fun this arr apply
|
||||
pop # fun this arr
|
||||
@ -566,7 +567,7 @@
|
||||
.imacro call0 # call fun
|
||||
swap # fun call
|
||||
pop # fun
|
||||
nullthis # fun this
|
||||
null # fun this
|
||||
call 0 #
|
||||
stop #
|
||||
.end #
|
||||
@ -627,7 +628,7 @@
|
||||
stop #
|
||||
.end #
|
||||
|
||||
.end
|
||||
.end apply
|
||||
|
||||
.igroup iter JSOP_ITER
|
||||
|
||||
@ -665,7 +666,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end iter
|
||||
|
||||
.igroup nextiter JSOP_NEXTITER
|
||||
|
||||
@ -691,7 +692,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end nextiter
|
||||
|
||||
.igroup getelem JSOP_GETELEM
|
||||
|
||||
@ -735,7 +736,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end getelem
|
||||
|
||||
.igroup setelem JSOP_SETELEM
|
||||
|
||||
@ -761,7 +762,7 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end setelem
|
||||
|
||||
.igroup initelem JSOP_INITELEM
|
||||
|
||||
@ -787,4 +788,4 @@
|
||||
stop
|
||||
.end
|
||||
|
||||
.end
|
||||
.end initelem
|
||||
|
@ -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_UNTERMINATED_COMMENT, 140, 0, JSEXN_SYNTAXERR, "unterminated comment")
|
||||
MSG_DEF(JSMSG_UNTERMINATED_REGEXP, 141, 0, JSEXN_SYNTAXERR, "unterminated regular expression literal")
|
||||
MSG_DEF(JSMSG_UNUSED142, 142, 0, JSEXN_NONE, "unused142")
|
||||
MSG_DEF(JSMSG_BAD_CLONE_FUNOBJ_SCOPE, 142, 0, JSEXN_TYPEERR, "bad cloned function scope chain")
|
||||
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_BAD_OCTAL, 145, 1, JSEXN_SYNTAXERR, "{0} is not a legal ECMA-262 octal constant")
|
||||
|
124
js/src/jsapi.cpp
124
js/src/jsapi.cpp
@ -4350,10 +4350,74 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
if (OBJ_GET_CLASS(cx, funobj) != &js_FunctionClass) {
|
||||
/* Indicate we cannot clone this object. */
|
||||
return funobj;
|
||||
/*
|
||||
* We cannot clone this object, so fail (we used to return funobj, bad
|
||||
* 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 *)
|
||||
@ -4673,8 +4737,8 @@ JS_CompileUCScriptForPrincipals(JSContext *cx, JSObject *obj,
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
|
||||
script = js_CompileScript(cx, obj, NULL, principals, tcflags,
|
||||
chars, length, NULL, filename, lineno);
|
||||
script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags,
|
||||
chars, length, NULL, filename, lineno);
|
||||
LAST_FRAME_CHECKS(cx, script);
|
||||
return script;
|
||||
}
|
||||
@ -4686,7 +4750,6 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
|
||||
jschar *chars;
|
||||
JSBool result;
|
||||
JSExceptionState *exnState;
|
||||
JSParseContext pc;
|
||||
JSErrorReporter older;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
@ -4700,20 +4763,21 @@ JS_BufferIsCompilableUnit(JSContext *cx, JSObject *obj,
|
||||
*/
|
||||
result = JS_TRUE;
|
||||
exnState = JS_SaveExceptionState(cx);
|
||||
if (js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL, NULL,
|
||||
1)) {
|
||||
older = JS_SetErrorReporter(cx, NULL);
|
||||
if (!js_ParseScript(cx, obj, &pc) &&
|
||||
(pc.tokenStream.flags & TSF_UNEXPECTED_EOF)) {
|
||||
/*
|
||||
* We ran into an error. If it was because we ran out of source,
|
||||
* we return false, so our caller will know to try to collect more
|
||||
* buffered source.
|
||||
*/
|
||||
result = JS_FALSE;
|
||||
{
|
||||
JSCompiler jsc(cx);
|
||||
if (jsc.init(chars, length, NULL, NULL, 1)) {
|
||||
older = JS_SetErrorReporter(cx, NULL);
|
||||
if (!jsc.parse(obj) &&
|
||||
(jsc.tokenStream.flags & TSF_UNEXPECTED_EOF)) {
|
||||
/*
|
||||
* We ran into an error. If it was because we ran out of
|
||||
* source, we return false so our caller knows to try to
|
||||
* collect more buffered source.
|
||||
*/
|
||||
result = JS_FALSE;
|
||||
}
|
||||
JS_SetErrorReporter(cx, older);
|
||||
}
|
||||
JS_SetErrorReporter(cx, older);
|
||||
js_FinishParseContext(cx, &pc);
|
||||
}
|
||||
JS_free(cx, chars);
|
||||
JS_RestoreExceptionState(cx, exnState);
|
||||
@ -4740,8 +4804,8 @@ JS_CompileFile(JSContext *cx, JSObject *obj, const char *filename)
|
||||
}
|
||||
|
||||
tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
|
||||
script = js_CompileScript(cx, obj, NULL, NULL, tcflags,
|
||||
NULL, 0, fp, filename, 1);
|
||||
script = JSCompiler::compileScript(cx, obj, NULL, NULL, tcflags,
|
||||
NULL, 0, fp, filename, 1);
|
||||
if (fp != stdin)
|
||||
fclose(fp);
|
||||
LAST_FRAME_CHECKS(cx, script);
|
||||
@ -4765,8 +4829,8 @@ JS_CompileFileHandleForPrincipals(JSContext *cx, JSObject *obj,
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
tcflags = JS_OPTIONS_TO_TCFLAGS(cx);
|
||||
script = js_CompileScript(cx, obj, NULL, principals, tcflags,
|
||||
NULL, 0, file, filename, 1);
|
||||
script = JSCompiler::compileScript(cx, obj, NULL, principals, tcflags,
|
||||
NULL, 0, file, filename, 1);
|
||||
LAST_FRAME_CHECKS(cx, script);
|
||||
return script;
|
||||
}
|
||||
@ -4902,8 +4966,8 @@ JS_CompileUCFunctionForPrincipals(JSContext *cx, JSObject *obj,
|
||||
}
|
||||
}
|
||||
|
||||
if (!js_CompileFunctionBody(cx, fun, principals, chars, length,
|
||||
filename, lineno)) {
|
||||
if (!JSCompiler::compileFunctionBody(cx, fun, principals,
|
||||
chars, length, filename, lineno)) {
|
||||
fun = NULL;
|
||||
goto out;
|
||||
}
|
||||
@ -5103,11 +5167,11 @@ JS_EvaluateUCScriptForPrincipals(JSContext *cx, JSObject *obj,
|
||||
JSBool ok;
|
||||
|
||||
CHECK_REQUEST(cx);
|
||||
script = js_CompileScript(cx, obj, NULL, principals,
|
||||
!rval
|
||||
? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
|
||||
: TCF_COMPILE_N_GO,
|
||||
chars, length, NULL, filename, lineno);
|
||||
script = JSCompiler::compileScript(cx, obj, NULL, principals,
|
||||
!rval
|
||||
? TCF_COMPILE_N_GO | TCF_NO_SCRIPT_RVAL
|
||||
: TCF_COMPILE_N_GO,
|
||||
chars, length, NULL, filename, lineno);
|
||||
if (!script) {
|
||||
LAST_FRAME_CHECKS(cx, script);
|
||||
return JS_FALSE;
|
||||
|
@ -235,8 +235,8 @@ BOOLEAN_TO_JSVAL(JSBool b)
|
||||
#define JSFUN_FAST_NATIVE 0x0800 /* JSFastNative needs no JSStackFrame */
|
||||
|
||||
#define JSFUN_FLAGS_MASK 0x0ff8 /* overlay JSFUN_* attributes --
|
||||
note that bit #15 is used internally
|
||||
to flag interpreted functions */
|
||||
bits 12-15 are used internally to
|
||||
flag interpreted functions */
|
||||
|
||||
#define JSFUN_STUB_GSOPS 0x1000 /* use JS_PropertyStub getter/setter
|
||||
instead of defaulting to class gsops
|
||||
|
@ -3277,7 +3277,7 @@ js_FastNewArrayWithLength(JSContext* cx, JSObject* proto, uint32 i)
|
||||
JSObject* FASTCALL
|
||||
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)))
|
||||
return NULL;
|
||||
return obj;
|
||||
|
@ -73,8 +73,11 @@ extern JSClass js_ArrayClass, js_SlowArrayClass;
|
||||
*
|
||||
* 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
|
||||
* prototype chain when obj is a dense array, in order to find a likely-native
|
||||
* object (to wit, Array.prototype) in which to probe for cached methods.
|
||||
* prototype chain when obj is a dense array, in order to find a native 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
|
||||
* (obj) for the |this| value of a getter, setter, or method call (bug 476447).
|
||||
|
@ -49,13 +49,15 @@
|
||||
#include "jsprf.h"
|
||||
#include "jsapi.h"
|
||||
#include "jsatom.h"
|
||||
#include "jsbit.h"
|
||||
#include "jscntxt.h"
|
||||
#include "jsversion.h"
|
||||
#include "jsgc.h"
|
||||
#include "jslock.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsscan.h"
|
||||
#include "jsstr.h"
|
||||
#include "jsversion.h"
|
||||
|
||||
/*
|
||||
* ATOM_HASH assumes that JSHashNumber is 32-bit even on 64-bit systems.
|
||||
@ -933,32 +935,70 @@ js_hash_atom_ptr(const void *key)
|
||||
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 *
|
||||
js_alloc_temp_space(void *priv, size_t size)
|
||||
{
|
||||
JSContext *cx = (JSContext *) priv;
|
||||
void *space;
|
||||
JSCompiler *jsc = (JSCompiler *) priv;
|
||||
|
||||
JS_ARENA_ALLOCATE(space, &cx->tempPool, size);
|
||||
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];
|
||||
if (space) {
|
||||
jsc->tempFreeList[bin] = *(void **)space;
|
||||
return space;
|
||||
}
|
||||
}
|
||||
|
||||
JS_ARENA_ALLOCATE(space, &jsc->context->tempPool, size);
|
||||
if (!space)
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
js_ReportOutOfScriptQuota(jsc->context);
|
||||
return space;
|
||||
}
|
||||
|
||||
static void
|
||||
js_free_temp_space(void *priv, void *item)
|
||||
js_free_temp_space(void *priv, void *item, size_t size)
|
||||
{
|
||||
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 *
|
||||
js_alloc_temp_entry(void *priv, const void *key)
|
||||
{
|
||||
JSContext *cx = (JSContext *) priv;
|
||||
JSCompiler *jsc = (JSCompiler *) priv;
|
||||
JSAtomListElement *ale;
|
||||
|
||||
JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &cx->tempPool);
|
||||
ale = jsc->aleFreeList;
|
||||
if (ale) {
|
||||
jsc->aleFreeList = ALE_NEXT(ale);
|
||||
return &ale->entry;
|
||||
}
|
||||
|
||||
JS_ARENA_ALLOCATE_TYPE(ale, JSAtomListElement, &jsc->context->tempPool);
|
||||
if (!ale) {
|
||||
js_ReportOutOfScriptQuota(cx);
|
||||
js_ReportOutOfScriptQuota(jsc->context);
|
||||
return NULL;
|
||||
}
|
||||
return &ale->entry;
|
||||
@ -967,6 +1007,11 @@ js_alloc_temp_entry(void *priv, const void *key)
|
||||
static void
|
||||
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 = {
|
||||
@ -975,67 +1020,181 @@ static JSHashAllocOps temp_alloc_ops = {
|
||||
};
|
||||
|
||||
JSAtomListElement *
|
||||
js_IndexAtom(JSContext *cx, JSAtom *atom, JSAtomList *al)
|
||||
JSAtomList::rawLookup(JSAtom *atom, JSHashEntry **&hep)
|
||||
{
|
||||
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;
|
||||
JSHashEntry **hep;
|
||||
|
||||
ATOM_LIST_LOOKUP(ale, hep, al, atom);
|
||||
if (!ale) {
|
||||
if (al->count < 10) {
|
||||
/* Few enough for linear search, no hash table needed. */
|
||||
JS_ASSERT(!al->table);
|
||||
ale = (JSAtomListElement *)js_alloc_temp_entry(cx, atom);
|
||||
ale = rawLookup(atom, hep);
|
||||
if (!ale || how != UNIQUE) {
|
||||
if (count < ATOM_LIST_HASH_THRESHOLD && !table) {
|
||||
/* Few enough for linear search and no hash table yet needed. */
|
||||
ale = (JSAtomListElement *)js_alloc_temp_entry(jsc, atom);
|
||||
if (!ale)
|
||||
return NULL;
|
||||
ALE_SET_ATOM(ale, atom);
|
||||
ale->entry.next = al->list;
|
||||
al->list = &ale->entry;
|
||||
|
||||
if (how == HOIST) {
|
||||
ale->entry.next = NULL;
|
||||
hep = (JSHashEntry **) &list;
|
||||
while (*hep)
|
||||
hep = &(*hep)->next;
|
||||
*hep = &ale->entry;
|
||||
} else {
|
||||
ale->entry.next = list;
|
||||
list = &ale->entry;
|
||||
}
|
||||
} else {
|
||||
/* We want to hash. Have we already made a hash table? */
|
||||
if (!al->table) {
|
||||
/*
|
||||
* We should hash, or else we already are hashing, but count was
|
||||
* 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! */
|
||||
JS_ASSERT(!hep);
|
||||
al->table = JS_NewHashTable(al->count + 1, js_hash_atom_ptr,
|
||||
JS_CompareValues, JS_CompareValues,
|
||||
&temp_alloc_ops, cx);
|
||||
if (!al->table)
|
||||
table = JS_NewHashTable(count + 1, js_hash_atom_ptr,
|
||||
JS_CompareValues, JS_CompareValues,
|
||||
&temp_alloc_ops, jsc);
|
||||
if (!table)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* Set ht->nentries explicitly, because we are moving entries
|
||||
* from al to ht, not calling JS_HashTable(Raw|)Add.
|
||||
* from list to ht, not calling JS_HashTable(Raw|)Add.
|
||||
*/
|
||||
al->table->nentries = al->count;
|
||||
table->nentries = count;
|
||||
|
||||
/* Insert each ale on al->list into the new hash table. */
|
||||
for (ale2 = (JSAtomListElement *)al->list; ale2; ale2 = next) {
|
||||
/*
|
||||
* Insert each ale on list into the new hash table. Append to
|
||||
* 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);
|
||||
ale2->entry.keyHash = ATOM_HASH(ALE_ATOM(ale2));
|
||||
hep = JS_HashTableRawLookup(al->table, ale2->entry.keyHash,
|
||||
hep = JS_HashTableRawLookup(table, ale2->entry.keyHash,
|
||||
ale2->entry.key);
|
||||
ale2->entry.next = *hep;
|
||||
while (*hep)
|
||||
hep = &(*hep)->next;
|
||||
*hep = &ale2->entry;
|
||||
ale2->entry.next = NULL;
|
||||
}
|
||||
al->list = NULL;
|
||||
list = NULL;
|
||||
|
||||
/* Set hep for insertion of atom's ale, immediately below. */
|
||||
hep = JS_HashTableRawLookup(al->table, ATOM_HASH(atom), atom);
|
||||
hep = JS_HashTableRawLookup(table, ATOM_HASH(atom), atom);
|
||||
}
|
||||
|
||||
/* Finally, add an entry for atom into the hash bucket at hep. */
|
||||
ale = (JSAtomListElement *)
|
||||
JS_HashTableRawAdd(al->table, hep, ATOM_HASH(atom), atom,
|
||||
NULL);
|
||||
JS_HashTableRawAdd(table, hep, ATOM_HASH(atom), atom, NULL);
|
||||
if (!ale)
|
||||
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, al->count++);
|
||||
ALE_SET_INDEX(ale, count++);
|
||||
}
|
||||
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
|
||||
js_map_atom(JSHashEntry *he, intN i, void *arg)
|
||||
{
|
||||
@ -1081,5 +1240,5 @@ js_InitAtomMap(JSContext *cx, JSAtomMap *map, JSAtomList *al)
|
||||
vector[ALE_INDEX(ale)] = ALE_ATOM(ale);
|
||||
} while ((ale = ALE_NEXT(ale)) != NULL);
|
||||
}
|
||||
ATOM_LIST_INIT(al);
|
||||
al->clear();
|
||||
}
|
||||
|
116
js/src/jsatom.h
116
js/src/jsatom.h
@ -88,51 +88,99 @@ struct JSAtomListElement {
|
||||
|
||||
#define ALE_ATOM(ale) ((JSAtom *) (ale)->entry.key)
|
||||
#define ALE_INDEX(ale) ((jsatomid) JS_PTR_TO_UINT32((ale)->entry.value))
|
||||
#define ALE_JSOP(ale) ((JSOp) JS_PTR_TO_UINT32((ale)->entry.value))
|
||||
#define ALE_DEFN(ale) ((JSDefinition *) (ale)->entry.value)
|
||||
#define ALE_VALUE(ale) ((jsval) (ale)->entry.value)
|
||||
#define ALE_NEXT(ale) ((JSAtomListElement *) (ale)->entry.next)
|
||||
|
||||
#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_JSOP(ale,op) ((ale)->entry.value = JS_UINT32_TO_PTR(op))
|
||||
#define ALE_SET_DEFN(ale, dn) ((ale)->entry.value = (void *)(dn))
|
||||
#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 */
|
||||
JSHashTable *table; /* hash table if list gets too long */
|
||||
jsuint count; /* count of indexed literals */
|
||||
};
|
||||
|
||||
#define ATOM_LIST_INIT(al) ((al)->list = NULL, (al)->table = NULL, \
|
||||
(al)->count = 0)
|
||||
#ifdef __cplusplus
|
||||
|
||||
#define ATOM_LIST_SEARCH(_ale,_al,_atom) \
|
||||
JS_BEGIN_MACRO \
|
||||
JSHashEntry **_hep; \
|
||||
ATOM_LIST_LOOKUP(_ale, _hep, _al, _atom); \
|
||||
JS_END_MACRO
|
||||
struct JSAtomList : public JSAtomSet
|
||||
{
|
||||
#ifdef DEBUG
|
||||
const JSAtomSet* set; /* asserted null in mutating methods */
|
||||
#endif
|
||||
|
||||
#define ATOM_LIST_LOOKUP(_ale,_hep,_al,_atom) \
|
||||
JS_BEGIN_MACRO \
|
||||
if ((_al)->table) { \
|
||||
_hep = JS_HashTableRawLookup((_al)->table, ATOM_HASH(_atom), \
|
||||
_atom); \
|
||||
_ale = *_hep ? (JSAtomListElement *) *_hep : NULL; \
|
||||
} else { \
|
||||
JSHashEntry **_alep = &(_al)->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 = (_al)->list; \
|
||||
(_al)->list = &_ale->entry; \
|
||||
break; \
|
||||
} \
|
||||
_alep = &_ale->entry.next; \
|
||||
} \
|
||||
} \
|
||||
JS_END_MACRO
|
||||
JSAtomList() {
|
||||
list = NULL; table = NULL; count = 0;
|
||||
#ifdef DEBUG
|
||||
set = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
JSAtomList(const JSAtomSet& as) {
|
||||
list = as.list; table = as.table; count = as.count;
|
||||
#ifdef DEBUG
|
||||
set = &as;
|
||||
#endif
|
||||
}
|
||||
|
||||
void clear() { JS_ASSERT(!set); list = NULL; table = NULL; count = 0; }
|
||||
|
||||
JSAtomListElement *lookup(JSAtom *atom) {
|
||||
JSHashEntry **hep;
|
||||
return rawLookup(atom, hep);
|
||||
}
|
||||
|
||||
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 {
|
||||
JSAtom **vector; /* array of ptrs to indexed atoms */
|
||||
@ -426,12 +474,6 @@ js_DumpAtoms(JSContext *cx, FILE *fp);
|
||||
|
||||
#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
|
||||
* to its address. map->length must already be set to the number of atoms in
|
||||
|
@ -253,8 +253,10 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
||||
return JS_FALSE;
|
||||
}
|
||||
|
||||
if (slot != sprop->slot)
|
||||
if (slot != sprop->slot) {
|
||||
js_FreeSlot(cx, obj, slot);
|
||||
goto slot_changed;
|
||||
}
|
||||
}
|
||||
|
||||
SCOPE_EXTEND_SHAPE(cx, scope, sprop);
|
||||
@ -274,7 +276,6 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
||||
slot = sprop2->slot;
|
||||
|
||||
slot_changed:
|
||||
js_FreeSlot(cx, obj, slot);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -367,6 +368,31 @@ js_Arguments(JSContext* cx)
|
||||
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 BUILTIN2 JS_DEFINE_CALLINFO_2
|
||||
#define BUILTIN3 JS_DEFINE_CALLINFO_3
|
||||
|
@ -471,26 +471,27 @@ js_NewContext(JSRuntime *rt, size_t stackChunkSize)
|
||||
#if defined DEBUG && defined XP_UNIX
|
||||
# include <stdio.h>
|
||||
|
||||
class AutoFile {
|
||||
class JSAutoFile {
|
||||
public:
|
||||
AutoFile() : mFp(NULL) {}
|
||||
JSAutoFile() : mFile(NULL) {}
|
||||
|
||||
~AutoFile() {
|
||||
if (mFp)
|
||||
fclose(mFp);
|
||||
~JSAutoFile() {
|
||||
if (mFile)
|
||||
fclose(mFile);
|
||||
}
|
||||
|
||||
FILE *open(const char *fname, const char *mode) {
|
||||
return mFp = fopen(fname, mode);
|
||||
return mFile = fopen(fname, mode);
|
||||
}
|
||||
operator FILE *() {
|
||||
return mFp;
|
||||
return mFile;
|
||||
}
|
||||
|
||||
private:
|
||||
FILE *mFp;
|
||||
FILE *mFile;
|
||||
};
|
||||
|
||||
#ifdef JS_EVAL_CACHE_METERING
|
||||
static void
|
||||
DumpEvalCacheMeter(JSContext *cx)
|
||||
{
|
||||
@ -504,7 +505,7 @@ DumpEvalCacheMeter(JSContext *cx)
|
||||
};
|
||||
JSEvalCacheMeter *ecm = &JS_THREAD_DATA(cx)->evalCacheMeter;
|
||||
|
||||
static AutoFile fp;
|
||||
static JSAutoFile fp;
|
||||
if (!fp) {
|
||||
fp.open("/tmp/evalcache.stats", "w");
|
||||
if (!fp)
|
||||
@ -527,10 +528,49 @@ DumpEvalCacheMeter(JSContext *cx)
|
||||
fflush(fp);
|
||||
}
|
||||
# define DUMP_EVAL_CACHE_METER(cx) DumpEvalCacheMeter(cx)
|
||||
#else
|
||||
#endif
|
||||
|
||||
#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)
|
||||
#endif
|
||||
|
||||
#ifndef DUMP_FUNCTION_METER
|
||||
# define DUMP_FUNCTION_METER(cx) ((void) 0)
|
||||
#endif
|
||||
|
||||
void
|
||||
js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
{
|
||||
@ -633,6 +673,7 @@ js_DestroyContext(JSContext *cx, JSDestroyContextMode mode)
|
||||
if (last) {
|
||||
js_GC(cx, GC_LAST_CONTEXT);
|
||||
DUMP_EVAL_CACHE_METER(cx);
|
||||
DUMP_FUNCTION_METER(cx);
|
||||
|
||||
/*
|
||||
* Free the script filename table if it exists and is empty. Do this
|
||||
|
@ -198,7 +198,8 @@ typedef struct InterpStruct InterpStruct;
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
# define JS_EVAL_CACHE_METERING 1
|
||||
# define JS_EVAL_CACHE_METERING 1
|
||||
# define JS_FUNCTION_METERING 1
|
||||
#endif
|
||||
|
||||
/* Number of potentially reusable scriptsToGC to search for the eval cache. */
|
||||
@ -209,14 +210,27 @@ typedef struct InterpStruct InterpStruct;
|
||||
|
||||
#ifdef JS_EVAL_CACHE_METERING
|
||||
# define EVAL_CACHE_METER_LIST(_) _(probe), _(hit), _(step), _(noscope)
|
||||
# define ID(x) x
|
||||
# define identity(x) x
|
||||
|
||||
/* Have to typedef this for LiveConnect C code, which includes us. */
|
||||
typedef struct JSEvalCacheMeter {
|
||||
uint64 EVAL_CACHE_METER_LIST(ID);
|
||||
uint64 EVAL_CACHE_METER_LIST(identity);
|
||||
} JSEvalCacheMeter;
|
||||
|
||||
# undef ID
|
||||
# undef identity
|
||||
#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
|
||||
|
||||
struct JSThreadData {
|
||||
@ -655,6 +669,11 @@ struct JSRuntime {
|
||||
#ifdef JS_GCMETER
|
||||
JSGCStats gcStats;
|
||||
#endif
|
||||
|
||||
#ifdef JS_FUNCTION_METERING
|
||||
JSFunctionMeter functionMeter;
|
||||
char lastScriptFilename[1024];
|
||||
#endif
|
||||
};
|
||||
|
||||
/* Common macros to access thread-local caches in JSThread or JSRuntime. */
|
||||
@ -668,7 +687,6 @@ struct JSRuntime {
|
||||
#else
|
||||
# define EVAL_CACHE_METER(x) ((void) 0)
|
||||
#endif
|
||||
#undef DECLARE_EVAL_CACHE_METER
|
||||
|
||||
#ifdef DEBUG
|
||||
# define JS_RUNTIME_METER(rt, which) JS_ATOMIC_INCREMENT(&(rt)->which)
|
||||
@ -763,7 +781,7 @@ typedef struct JSLocalRootStack {
|
||||
* structure */
|
||||
#define JSTVU_SPROP (-3) /* u.sprop roots property tree node */
|
||||
#define JSTVU_WEAK_ROOTS (-4) /* u.weakRoots points to saved weak roots */
|
||||
#define JSTVU_PARSE_CONTEXT (-5) /* u.parseContext roots JSParseContext* */
|
||||
#define JSTVU_COMPILER (-5) /* u.compiler roots JSCompiler* */
|
||||
#define JSTVU_SCRIPT (-6) /* u.script roots JSScript* */
|
||||
|
||||
/*
|
||||
@ -818,8 +836,8 @@ typedef struct JSLocalRootStack {
|
||||
#define JS_PUSH_TEMP_ROOT_WEAK_COPY(cx,weakRoots_,tvr) \
|
||||
JS_PUSH_TEMP_ROOT_COMMON(cx, weakRoots_, tvr, JSTVU_WEAK_ROOTS, weakRoots)
|
||||
|
||||
#define JS_PUSH_TEMP_ROOT_PARSE_CONTEXT(cx,pc,tvr) \
|
||||
JS_PUSH_TEMP_ROOT_COMMON(cx, pc, tvr, JSTVU_PARSE_CONTEXT, parseContext)
|
||||
#define JS_PUSH_TEMP_ROOT_COMPILER(cx,pc,tvr) \
|
||||
JS_PUSH_TEMP_ROOT_COMMON(cx, pc, tvr, JSTVU_COMPILER, compiler)
|
||||
|
||||
#define JS_PUSH_TEMP_ROOT_SCRIPT(cx,script_,tvr) \
|
||||
JS_PUSH_TEMP_ROOT_COMMON(cx, script_, tvr, JSTVU_SCRIPT, script)
|
||||
@ -1055,7 +1073,7 @@ class JSAutoTempValueRooter
|
||||
}
|
||||
|
||||
jsval value() { return mTvr.u.value; }
|
||||
jsval * addr() { return &mTvr.u.value; }
|
||||
jsval *addr() { return &mTvr.u.value; }
|
||||
|
||||
protected:
|
||||
JSContext *mContext;
|
||||
|
@ -1252,15 +1252,15 @@ JS_EvaluateUCInStackFrame(JSContext *cx, JSStackFrame *fp,
|
||||
|
||||
/*
|
||||
* NB: This function breaks the assumption that the compiler can see all
|
||||
* calls and properly compute a static depth. In order to get around this,
|
||||
* we use a static depth that will cause us not to attempt to optimize
|
||||
* calls and properly compute a static level. In order to get around this,
|
||||
* we use a static level that will cause us not to attempt to optimize
|
||||
* variable references made by this frame.
|
||||
*/
|
||||
script = js_CompileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
|
||||
TCF_COMPILE_N_GO |
|
||||
TCF_PUT_STATIC_DEPTH(JS_DISPLAY_SIZE),
|
||||
chars, length, NULL,
|
||||
filename, lineno);
|
||||
script = JSCompiler::compileScript(cx, scobj, fp, JS_StackFramePrincipals(cx, fp),
|
||||
TCF_COMPILE_N_GO |
|
||||
TCF_PUT_STATIC_LEVEL(JS_DISPLAY_SIZE),
|
||||
chars, length, NULL,
|
||||
filename, lineno);
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
|
1373
js/src/jsemit.cpp
1373
js/src/jsemit.cpp
File diff suppressed because it is too large
Load Diff
278
js/src/jsemit.h
278
js/src/jsemit.h
@ -46,6 +46,7 @@
|
||||
#include "jstypes.h"
|
||||
#include "jsatom.h"
|
||||
#include "jsopcode.h"
|
||||
#include "jsparse.h"
|
||||
#include "jsscript.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
@ -123,13 +124,14 @@ typedef struct JSStmtInfo JSStmtInfo;
|
||||
struct JSStmtInfo {
|
||||
uint16 type; /* statement type */
|
||||
uint16 flags; /* flags, see below */
|
||||
uint32 blockid; /* for simplified dominance computation */
|
||||
ptrdiff_t update; /* loop update offset (top if none) */
|
||||
ptrdiff_t breaks; /* offset of last break in loop */
|
||||
ptrdiff_t continues; /* offset of last continue in loop */
|
||||
union {
|
||||
JSAtom *label; /* name of LABEL */
|
||||
JSObject *blockObj; /* block scope object */
|
||||
} u;
|
||||
};
|
||||
JSStmtInfo *down; /* info for enclosing statement */
|
||||
JSStmtInfo *downScope; /* next enclosing lexical scope */
|
||||
};
|
||||
@ -150,101 +152,132 @@ struct JSStmtInfo {
|
||||
#define GOSUBS(stmt) ((stmt).breaks)
|
||||
#define GUARDJUMP(stmt) ((stmt).continues)
|
||||
|
||||
#define AT_TOP_LEVEL(tc) \
|
||||
(!(tc)->topStmt || ((tc)->topStmt->flags & SIF_BODY_BLOCK))
|
||||
|
||||
#define SET_STATEMENT_TOP(stmt, top) \
|
||||
((stmt)->update = (top), (stmt)->breaks = (stmt)->continues = (-1))
|
||||
|
||||
struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint16 flags; /* statement state flags, see below */
|
||||
uint16 ngvars; /* max. no. of global variables/regexps */
|
||||
JSStmtInfo *topStmt; /* top of statement info stack */
|
||||
JSStmtInfo *topScopeStmt; /* top lexical scope statement */
|
||||
JSObject *blockChain; /* compile time block scope chain (NB: one
|
||||
deeper than the topScopeStmt/downScope
|
||||
chain when in head of let block/expr) */
|
||||
JSParseNode *blockNode; /* parse node for a lexical scope.
|
||||
XXX combine with blockChain? */
|
||||
JSAtomList decls; /* function, const, and var declarations */
|
||||
JSParseContext *parseContext;
|
||||
|
||||
union {
|
||||
|
||||
JSFunction *fun; /* function to store argument and variable
|
||||
names when flags & TCF_IN_FUNCTION */
|
||||
JSObject *scopeChain; /* scope chain object for the script */
|
||||
} u;
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
uint16 scopeDepth; /* current lexical scope chain depth */
|
||||
uint16 maxScopeDepth; /* maximum lexical scope chain depth */
|
||||
#endif
|
||||
};
|
||||
|
||||
#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.
|
||||
*/
|
||||
#define TCF_RETURN_FLAGS (TCF_RETURN_EXPR | TCF_RETURN_VOID)
|
||||
|
||||
/*
|
||||
* Flags to propagate from FunctionBody.
|
||||
*/
|
||||
#define TCF_FUN_FLAGS (TCF_FUN_IS_GENERATOR | \
|
||||
TCF_FUN_HEAVYWEIGHT | \
|
||||
TCF_FUN_USES_NONLOCALS | \
|
||||
TCF_HAS_SHARPS)
|
||||
|
||||
/*
|
||||
* Flags field, not stored in JSTreeContext.flags, for passing staticDepth
|
||||
* into js_CompileScript.
|
||||
*/
|
||||
#define TCF_STATIC_DEPTH_MASK 0xffff0000
|
||||
#define TCF_GET_STATIC_DEPTH(f) ((uint32)(f) >> 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))
|
||||
struct JSTreeContext { /* tree context for semantic checks */
|
||||
uint16 flags; /* statement state flags, see below */
|
||||
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 *topScopeStmt; /* top lexical scope statement */
|
||||
JSObject *blockChain; /* compile time block scope chain (NB: one
|
||||
deeper than the topScopeStmt/downScope
|
||||
chain when in head of let block/expr) */
|
||||
JSParseNode *blockNode; /* parse node for a block with let declarations
|
||||
(block with its own lexical scope) */
|
||||
JSAtomList decls; /* function, const, and var declarations */
|
||||
JSCompiler *compiler; /* ptr to common parsing and lexing data */
|
||||
|
||||
union {
|
||||
JSFunction *fun; /* function to store argument and variable
|
||||
names when flags & TCF_IN_FUNCTION */
|
||||
JSObject *scopeChain; /* scope chain object for the script */
|
||||
};
|
||||
|
||||
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
|
||||
uint16 scopeDepth; /* current lexical scope chain depth */
|
||||
uint16 maxScopeDepth; /* maximum lexical scope chain depth */
|
||||
#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);
|
||||
};
|
||||
|
||||
/*
|
||||
* 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.
|
||||
* Flags to propagate out of the blocks.
|
||||
*/
|
||||
#define TREE_CONTEXT_FINISH(cx, tc) \
|
||||
JS_SCOPE_DEPTH_METERING( \
|
||||
(tc)->maxScopeDepth == (uintN) -1 || \
|
||||
JS_BASIC_STATS_ACCUM(&(cx)->runtime->lexicalScopeDepthStats, \
|
||||
(tc)->maxScopeDepth))
|
||||
#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.
|
||||
*/
|
||||
#define TCF_FUN_FLAGS (TCF_FUN_SETS_OUTER_NAME | \
|
||||
TCF_FUN_USES_ARGUMENTS | \
|
||||
TCF_FUN_PARAM_ARGUMENTS | \
|
||||
TCF_FUN_HEAVYWEIGHT | \
|
||||
TCF_FUN_IS_GENERATOR | \
|
||||
TCF_FUN_IS_FUNARG | \
|
||||
TCF_HAS_SHARPS)
|
||||
|
||||
/*
|
||||
* Flags field, not stored in JSTreeContext.flags, for passing a static level
|
||||
* into js_CompileScript.
|
||||
*/
|
||||
#define TCF_STATIC_LEVEL_MASK 0xffff0000
|
||||
#define TCF_GET_STATIC_LEVEL(f) ((uint32)(f) >> 16)
|
||||
#define TCF_PUT_STATIC_LEVEL(d) ((uint16)(d) << 16)
|
||||
|
||||
/*
|
||||
* Span-dependent instructions are jumps whose span (from the jump bytecode to
|
||||
@ -314,17 +347,18 @@ struct JSTryNode {
|
||||
JSTryNode *prev;
|
||||
};
|
||||
|
||||
typedef struct JSEmittedObjectList {
|
||||
struct JSCGObjectList {
|
||||
uint32 length; /* number of emitted so far objects */
|
||||
JSParsedObjectBox *lastPob; /* last emitted object */
|
||||
} JSEmittedObjectList;
|
||||
JSObjectBox *lastbox; /* last emitted object */
|
||||
|
||||
extern void
|
||||
FinishParsedObjects(JSEmittedObjectList *emittedList, JSObjectArray *objectMap);
|
||||
JSCGObjectList() : length(0), lastbox(NULL) {}
|
||||
|
||||
struct JSCodeGenerator {
|
||||
JSTreeContext treeContext; /* base state: statement info stack, etc. */
|
||||
uintN index(JSObjectBox *objbox);
|
||||
void finish(JSObjectArray *array);
|
||||
};
|
||||
|
||||
struct JSCodeGenerator : public JSTreeContext
|
||||
{
|
||||
JSArenaPool *codePool; /* pointer to thread code arena pool */
|
||||
JSArenaPool *notePool; /* pointer to thread srcnote arena pool */
|
||||
void *codeMark; /* low watermark in cg->codePool */
|
||||
@ -363,17 +397,33 @@ struct JSCodeGenerator {
|
||||
uintN emitLevel; /* js_EmitTree recursion level */
|
||||
JSAtomList constList; /* compile time constants */
|
||||
|
||||
JSEmittedObjectList objectList; /* list of emitted so far objects */
|
||||
JSEmittedObjectList regexpList; /* list of emitted so far regexp
|
||||
that will be cloned during execution */
|
||||
JSCGObjectList objectList; /* list of emitted objects */
|
||||
JSCGObjectList regexpList; /* list of emitted regexp that will be
|
||||
cloned during execution */
|
||||
|
||||
uintN staticDepth; /* static frame chain depth */
|
||||
JSAtomList upvarList; /* map of atoms to upvar indexes */
|
||||
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)->treeContext.parseContext)
|
||||
#define CG_TS(cg) TS((cg)->compiler)
|
||||
|
||||
#define CG_BASE(cg) ((cg)->current->base)
|
||||
#define CG_LIMIT(cg) ((cg)->current->limit)
|
||||
@ -396,25 +446,6 @@ struct JSCodeGenerator {
|
||||
#define CG_SWITCH_TO_MAIN(cg) ((cg)->current = &(cg)->main)
|
||||
#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.
|
||||
*/
|
||||
@ -464,13 +495,6 @@ extern JSBool
|
||||
js_SetJumpOffset(JSContext *cx, JSCodeGenerator *cg, jsbytecode *pc,
|
||||
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.
|
||||
*/
|
||||
@ -495,9 +519,9 @@ extern void
|
||||
js_PopStatement(JSTreeContext *tc);
|
||||
|
||||
/*
|
||||
* Like js_PopStatement(&cg->treeContext), also patch breaks and continues
|
||||
* unless the top statement info record represents a try-catch-finally suite.
|
||||
* May fail if a jump offset overflows.
|
||||
* Like js_PopStatement(cg), also patch breaks and continues unless the top
|
||||
* statement info record represents a try-catch-finally suite. May fail if a
|
||||
* jump offset overflows.
|
||||
*/
|
||||
extern JSBool
|
||||
js_PopStatementCG(JSContext *cx, JSCodeGenerator *cg);
|
||||
@ -584,7 +608,7 @@ typedef enum JSSrcNoteType {
|
||||
SRC_INITPROP = 1, /* disjoint meaning applied to JSOP_INITELEM or
|
||||
to an index label in a regular (structuring)
|
||||
or a destructuring object initialiser */
|
||||
SRC_GENEXP = 1, /* JSOP_ANONFUNOBJ from generator expression */
|
||||
SRC_GENEXP = 1, /* JSOP_LAMBDA from generator expression */
|
||||
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
|
||||
before loop (same arity as SRC_IF_ELSE) */
|
||||
|
214
js/src/jsfun.cpp
214
js/src/jsfun.cpp
@ -586,7 +586,7 @@ JSClass js_ArgumentsClass = {
|
||||
JS_CLASS_TRACE(args_or_call_trace), NULL
|
||||
};
|
||||
|
||||
#define JSSLOT_SCRIPTED_FUNCTION (JSSLOT_PRIVATE + 1)
|
||||
#define JSSLOT_CALLEE (JSSLOT_PRIVATE + 1)
|
||||
#define JSSLOT_CALL_ARGUMENTS (JSSLOT_PRIVATE + 2)
|
||||
#define CALL_CLASS_FIXED_RESERVED_SLOTS 2
|
||||
|
||||
@ -617,8 +617,8 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
return NULL;
|
||||
|
||||
JS_SetPrivate(cx, callobj, fp);
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_SCRIPTED_FUNCTION,
|
||||
OBJECT_TO_JSVAL(FUN_OBJECT(fp->fun)));
|
||||
JS_ASSERT(fp->fun == GET_FUNCTION_PRIVATE(cx, fp->callee));
|
||||
STOBJ_SET_SLOT(callobj, JSSLOT_CALLEE, OBJECT_TO_JSVAL(fp->callee));
|
||||
fp->callobj = callobj;
|
||||
|
||||
/*
|
||||
@ -630,19 +630,19 @@ js_GetCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
return callobj;
|
||||
}
|
||||
|
||||
JSFunction *
|
||||
js_GetCallObjectFunction(JSObject *obj)
|
||||
static JSFunction *
|
||||
GetCallObjectFunction(JSObject *obj)
|
||||
{
|
||||
jsval v;
|
||||
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
|
||||
v = STOBJ_GET_SLOT(obj, JSSLOT_SCRIPTED_FUNCTION);
|
||||
v = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE);
|
||||
if (JSVAL_IS_VOID(v)) {
|
||||
/* Newborn or prototype object. */
|
||||
return NULL;
|
||||
}
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(v));
|
||||
return (JSFunction *) JSVAL_TO_OBJECT(v);
|
||||
return GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(v));
|
||||
}
|
||||
|
||||
JS_FRIEND_API(JSBool)
|
||||
@ -678,7 +678,7 @@ js_PutCallObject(JSContext *cx, JSStackFrame *fp)
|
||||
}
|
||||
|
||||
fun = fp->fun;
|
||||
JS_ASSERT(fun == js_GetCallObjectFunction(callobj));
|
||||
JS_ASSERT(fun == GetCallObjectFunction(callobj));
|
||||
n = JS_GET_LOCAL_NAME_COUNT(fun);
|
||||
if (n != 0) {
|
||||
JS_LOCK_OBJ(cx, callobj);
|
||||
@ -718,7 +718,7 @@ call_enumerate(JSContext *cx, JSObject *obj)
|
||||
JSObject *pobj;
|
||||
JSProperty *prop;
|
||||
|
||||
fun = js_GetCallObjectFunction(obj);
|
||||
fun = GetCallObjectFunction(obj);
|
||||
n = fun ? JS_GET_LOCAL_NAME_COUNT(fun) : 0;
|
||||
if (n == 0)
|
||||
return JS_TRUE;
|
||||
@ -778,7 +778,7 @@ CallPropertyOp(JSContext *cx, JSObject *obj, jsid id, jsval *vp,
|
||||
if (STOBJ_GET_CLASS(obj) != &js_CallClass)
|
||||
return JS_TRUE;
|
||||
|
||||
fun = js_GetCallObjectFunction(obj);
|
||||
fun = GetCallObjectFunction(obj);
|
||||
fp = (JSStackFrame *) JS_GetPrivate(cx, obj);
|
||||
|
||||
if (kind == JSCPK_ARGUMENTS) {
|
||||
@ -868,28 +868,39 @@ SetCallVar(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
|
||||
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
|
||||
call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
|
||||
JSObject **objp)
|
||||
{
|
||||
jsval callee;
|
||||
JSFunction *fun;
|
||||
jsid id;
|
||||
JSLocalKind localKind;
|
||||
JSPropertyOp getter, setter;
|
||||
uintN slot, attrs;
|
||||
|
||||
JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_CallClass);
|
||||
if (!JSVAL_IS_STRING(idval))
|
||||
return JS_TRUE;
|
||||
|
||||
fun = js_GetCallObjectFunction(obj);
|
||||
if (!fun)
|
||||
callee = STOBJ_GET_SLOT(obj, JSSLOT_CALLEE);
|
||||
if (JSVAL_IS_VOID(callee))
|
||||
return JS_TRUE;
|
||||
fun = GET_FUNCTION_PRIVATE(cx, JSVAL_TO_OBJECT(callee));
|
||||
|
||||
if (!js_ValueToStringId(cx, idval, &id))
|
||||
return JS_FALSE;
|
||||
|
||||
localKind = js_LookupLocal(cx, fun, JSID_TO_ATOM(id), &slot);
|
||||
if (localKind != JSLOCAL_NONE) {
|
||||
if (localKind != JSLOCAL_NONE && localKind != JSLOCAL_UPVAR) {
|
||||
JS_ASSERT((uint16) slot == slot);
|
||||
|
||||
/*
|
||||
@ -932,6 +943,32 @@ call_resolve(JSContext *cx, JSObject *obj, jsval idval, uintN flags,
|
||||
*objp = obj;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -955,7 +992,7 @@ call_reserveSlots(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
JSFunction *fun;
|
||||
|
||||
fun = js_GetCallObjectFunction(obj);
|
||||
fun = GetCallObjectFunction(obj);
|
||||
return JS_GET_LOCAL_NAME_COUNT(fun);
|
||||
}
|
||||
|
||||
@ -1142,15 +1179,6 @@ fun_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
|
||||
|
||||
/*
|
||||
* 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)
|
||||
return JS_TRUE;
|
||||
@ -1236,9 +1264,9 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
JSContext *cx;
|
||||
JSFunction *fun;
|
||||
uint32 nullAtom; /* flag to indicate if fun->atom is NULL */
|
||||
uintN nargs, nvars, n;
|
||||
uintN nargs, nvars, nupvars, n;
|
||||
uint32 localsword; /* word to xdr argument and variable counts */
|
||||
uint32 flagsword; /* originally only flags was JS_XDRUint8'd */
|
||||
uint32 flagsword; /* word to xdr upvars count and fun->flags */
|
||||
JSTempValueRooter tvr;
|
||||
JSBool ok;
|
||||
|
||||
@ -1254,8 +1282,9 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
nullAtom = !fun->atom;
|
||||
nargs = fun->nargs;
|
||||
nvars = fun->u.i.nvars;
|
||||
nupvars = fun->u.i.nupvars;
|
||||
localsword = (nargs << 16) | nvars;
|
||||
flagsword = fun->flags;
|
||||
flagsword = (nupvars << 16) | fun->flags;
|
||||
} else {
|
||||
fun = js_NewFunction(cx, NULL, NULL, 0, JSFUN_INTERPRETED, NULL, NULL);
|
||||
if (!fun)
|
||||
@ -1263,11 +1292,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
STOBJ_CLEAR_PARENT(FUN_OBJECT(fun));
|
||||
STOBJ_CLEAR_PROTO(FUN_OBJECT(fun));
|
||||
#ifdef __GNUC__
|
||||
nvars = nargs = 0; /* quell GCC uninitialized warning */
|
||||
nvars = nargs = nupvars = 0; /* quell GCC uninitialized warning */
|
||||
#endif
|
||||
}
|
||||
|
||||
/* From here on, control flow must flow through label out. */
|
||||
MUST_FLOW_THROUGH("out");
|
||||
JS_PUSH_TEMP_ROOT_OBJECT(cx, FUN_OBJECT(fun), &tvr);
|
||||
ok = JS_TRUE;
|
||||
|
||||
@ -1282,13 +1312,14 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
nargs = localsword >> 16;
|
||||
nvars = localsword & JS_BITMASK(16);
|
||||
JS_ASSERT(flagsword | JSFUN_INTERPRETED);
|
||||
fun->flags = (uint16) flagsword;
|
||||
nvars = uint16(localsword);
|
||||
JS_ASSERT((flagsword & JSFUN_KINDMASK) >= JSFUN_INTERPRETED);
|
||||
nupvars = flagsword >> 16;
|
||||
fun->flags = uint16(flagsword);
|
||||
}
|
||||
|
||||
/* do arguments and local vars */
|
||||
n = nargs + nvars;
|
||||
n = nargs + nvars + nupvars;
|
||||
if (n != 0) {
|
||||
void *mark;
|
||||
uintN i;
|
||||
@ -1310,6 +1341,7 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
* names (indexes starting from nargs) bitmap's bit is set when the
|
||||
* name is declared as const, not as ordinary var.
|
||||
* */
|
||||
MUST_FLOW_THROUGH("release_mark");
|
||||
bitmapLength = JS_HOWMANY(n, JS_BITS_PER_UINT32);
|
||||
JS_ARENA_ALLOCATE_CAST(bitmap, uint32 *, &xdr->cx->tempPool,
|
||||
bitmapLength * sizeof *bitmap);
|
||||
@ -1365,10 +1397,12 @@ fun_xdrObject(JSXDRState *xdr, JSObject **objp)
|
||||
if (xdr->mode == JSXDR_DECODE) {
|
||||
localKind = (i < nargs)
|
||||
? JSLOCAL_ARG
|
||||
: bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? JSLOCAL_CONST
|
||||
: JSLOCAL_VAR;
|
||||
: (i < nargs + nvars)
|
||||
? (bitmap[i >> JS_BITS_PER_UINT32_LOG2] &
|
||||
JS_BIT(i & (JS_BITS_PER_UINT32 - 1))
|
||||
? JSLOCAL_CONST
|
||||
: JSLOCAL_VAR)
|
||||
: JSLOCAL_UPVAR;
|
||||
ok = js_AddLocal(xdr->cx, fun, name, localKind);
|
||||
if (!ok)
|
||||
goto release_mark;
|
||||
@ -1506,7 +1540,7 @@ fun_reserveSlots(JSContext *cx, JSObject *obj)
|
||||
fun = (JSFunction *) JS_GetPrivate(cx, obj);
|
||||
nslots = 0;
|
||||
if (fun && FUN_INTERPRETED(fun) && fun->u.i.script) {
|
||||
if (fun->u.i.script->upvarsOffset != 0)
|
||||
if (fun->u.i.nupvars != 0)
|
||||
nslots = JS_SCRIPT_UPVARS(fun->u.i.script)->length;
|
||||
if (fun->u.i.script->regexpsOffset != 0)
|
||||
nslots += JS_SCRIPT_REGEXPS(fun->u.i.script)->length;
|
||||
@ -2033,9 +2067,9 @@ Function(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
str = cx->runtime->emptyString;
|
||||
}
|
||||
|
||||
return js_CompileFunctionBody(cx, fun, principals,
|
||||
JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
filename, lineno);
|
||||
return JSCompiler::compileFunctionBody(cx, fun, principals,
|
||||
JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
filename, lineno);
|
||||
}
|
||||
|
||||
JSObject *
|
||||
@ -2103,8 +2137,8 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
||||
|
||||
/* Initialize all function members. */
|
||||
fun->nargs = nargs;
|
||||
fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_INTERPRETED | JSFUN_TRACEABLE);
|
||||
if (flags & JSFUN_INTERPRETED) {
|
||||
fun->flags = flags & (JSFUN_FLAGS_MASK | JSFUN_KINDMASK | JSFUN_TRACEABLE);
|
||||
if ((flags & JSFUN_KINDMASK) >= JSFUN_INTERPRETED) {
|
||||
JS_ASSERT(!native);
|
||||
JS_ASSERT(nargs == 0);
|
||||
fun->u.i.nvars = 0;
|
||||
@ -2141,31 +2175,82 @@ js_NewFunction(JSContext *cx, JSObject *funobj, JSNative native, uintN nargs,
|
||||
JSObject *
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent)
|
||||
{
|
||||
JSObject *clone;
|
||||
|
||||
/*
|
||||
* The cloned function object does not need the extra fields beyond
|
||||
* JSObject as it points to fun via the private slot.
|
||||
* The cloned function object does not need the extra JSFunction members
|
||||
* beyond JSObject as it points to fun via the private slot.
|
||||
*/
|
||||
clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
|
||||
sizeof(JSObject));
|
||||
JSObject *clone = js_NewObject(cx, &js_FunctionClass, NULL, parent,
|
||||
sizeof(JSObject));
|
||||
if (!clone)
|
||||
return NULL;
|
||||
clone->fslots[JSSLOT_PRIVATE] = PRIVATE_TO_JSVAL(fun);
|
||||
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 *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
uintN nargs, uintN attrs)
|
||||
{
|
||||
JSFunction *fun;
|
||||
JSPropertyOp gsop;
|
||||
JSFunction *fun;
|
||||
|
||||
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);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
gsop = (attrs & JSFUN_STUB_GSOPS) ? JS_PropertyStub : NULL;
|
||||
if (!OBJ_DEFINE_PROPERTY(cx, obj, ATOM_TO_JSID(atom),
|
||||
OBJECT_TO_JSVAL(FUN_OBJECT(fun)),
|
||||
gsop, gsop,
|
||||
@ -2454,11 +2539,18 @@ js_AddLocal(JSContext *cx, JSFunction *fun, JSAtom *atom, JSLocalKind kind)
|
||||
map->lastdup = NULL;
|
||||
for (i = 0; i != MAX_ARRAY_LOCALS; ++i) {
|
||||
taggedAtom = array[i];
|
||||
if (!HashLocalName(cx, map, (JSAtom *) (taggedAtom & ~1),
|
||||
(i < fun->nargs)
|
||||
? JSLOCAL_ARG
|
||||
: (taggedAtom & 1) ? JSLOCAL_CONST : JSLOCAL_VAR,
|
||||
(i < fun->nargs) ? i : i - fun->nargs)) {
|
||||
uintN j = i;
|
||||
JSLocalKind k = JSLOCAL_ARG;
|
||||
if (j >= fun->nargs) {
|
||||
j -= fun->nargs;
|
||||
if (j < fun->u.i.nvars) {
|
||||
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);
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -2570,10 +2662,14 @@ get_local_names_enumerator(JSDHashTable *table, JSDHashEntryHdr *hdr,
|
||||
constFlag = 0;
|
||||
} else {
|
||||
JS_ASSERT(entry->localKind == JSLOCAL_VAR ||
|
||||
entry->localKind == JSLOCAL_CONST);
|
||||
JS_ASSERT(entry->index < args->fun->u.i.nvars);
|
||||
JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars);
|
||||
i = args->fun->nargs + entry->index;
|
||||
entry->localKind == JSLOCAL_CONST ||
|
||||
entry->localKind == JSLOCAL_UPVAR);
|
||||
JS_ASSERT(entry->index < args->fun->u.i.nvars + args->fun->u.i.nupvars);
|
||||
JS_ASSERT(args->nCopiedVars++ < args->fun->u.i.nvars + args->fun->u.i.nupvars);
|
||||
i = args->fun->nargs;
|
||||
if (entry->localKind == JSLOCAL_UPVAR)
|
||||
i += args->fun->u.i.nvars;
|
||||
i += entry->index;
|
||||
constFlag = (entry->localKind == JSLOCAL_CONST);
|
||||
}
|
||||
args->names[i] = (jsuword) entry->name | constFlag;
|
||||
@ -2626,7 +2722,7 @@ js_GetLocalNameArray(JSContext *cx, JSFunction *fun, JSArenaPool *pool)
|
||||
#if !JS_HAS_DESTRUCTURING
|
||||
JS_ASSERT(args.nCopiedArgs == fun->nargs);
|
||||
#endif
|
||||
JS_ASSERT(args.nCopiedVars == fun->u.i.nvars);
|
||||
JS_ASSERT(args.nCopiedVars == fun->u.i.nvars + fun->u.i.nupvars);
|
||||
|
||||
return names;
|
||||
}
|
||||
|
@ -89,17 +89,57 @@ struct JSFunction {
|
||||
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,
|
||||
FUN_CLASP if unset */
|
||||
#define JSFUN_EXPR_CLOSURE 0x4000 /* expression closure: function(x)x*x */
|
||||
#define JSFUN_INTERPRETED 0x8000 /* use u.i if set, u.n if unset */
|
||||
|
||||
#define JSFUN_SCRIPT_OR_FAST_NATIVE (JSFUN_INTERPRETED | JSFUN_FAST_NATIVE)
|
||||
#define JSFUN_INTERPRETED 0x4000 /* use u.i if kind >= this value else u.n */
|
||||
#define JSFUN_FLAT_CLOSURE 0x8000 /* flag (aka "display") closure */
|
||||
#define JSFUN_NULL_CLOSURE 0xc000 /* null closure entrains no scope chain */
|
||||
#define JSFUN_KINDMASK 0xc000 /* encode interp vs. native and closure
|
||||
optimization level -- see above */
|
||||
|
||||
#define FUN_OBJECT(fun) (&(fun)->object)
|
||||
#define FUN_INTERPRETED(fun) ((fun)->flags & JSFUN_INTERPRETED)
|
||||
#define FUN_SLOW_NATIVE(fun) (!((fun)->flags & JSFUN_SCRIPT_OR_FAST_NATIVE))
|
||||
#define FUN_KIND(fun) ((fun)->flags & JSFUN_KINDMASK)
|
||||
#define FUN_SET_KIND(fun,k) ((fun)->flags = ((fun)->flags & ~JSFUN_KINDMASK) | (k))
|
||||
#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_NATIVE(fun) (FUN_SLOW_NATIVE(fun) ? (fun)->u.n.native : NULL)
|
||||
#define FUN_FAST_NATIVE(fun) (((fun)->flags & JSFUN_FAST_NATIVE) \
|
||||
@ -169,11 +209,11 @@ js_TraceFunction(JSTracer *trc, JSFunction *fun);
|
||||
extern void
|
||||
js_FinalizeFunction(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSObject *
|
||||
extern JS_REQUIRES_STACK JSObject *
|
||||
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent);
|
||||
|
||||
extern JSBool
|
||||
js_LinkFunctionObject(JSContext *cx, JSFunction *fun, JSObject *object);
|
||||
extern JS_REQUIRES_STACK JSObject *
|
||||
js_NewFlatClosure(JSContext *cx, JSFunction *fun);
|
||||
|
||||
extern JSFunction *
|
||||
js_DefineFunction(JSContext *cx, JSObject *obj, JSAtom *atom, JSNative native,
|
||||
|
@ -3092,8 +3092,8 @@ js_TraceContext(JSTracer *trc, JSContext *acx)
|
||||
case JSTVU_WEAK_ROOTS:
|
||||
TraceWeakRoots(trc, tvr->u.weakRoots);
|
||||
break;
|
||||
case JSTVU_PARSE_CONTEXT:
|
||||
js_TraceParseContext(trc, tvr->u.parseContext);
|
||||
case JSTVU_COMPILER:
|
||||
tvr->u.compiler->trace(trc);
|
||||
break;
|
||||
case JSTVU_SCRIPT:
|
||||
js_TraceScript(trc, tvr->u.script);
|
||||
|
@ -71,7 +71,7 @@ DefaultAllocTable(void *pool, size_t size)
|
||||
}
|
||||
|
||||
static void
|
||||
DefaultFreeTable(void *pool, void *item)
|
||||
DefaultFreeTable(void *pool, void *item, size_t size)
|
||||
{
|
||||
free(item);
|
||||
}
|
||||
@ -121,7 +121,7 @@ JS_NewHashTable(uint32 n, JSHashFunction keyHash,
|
||||
nb = n * sizeof(JSHashEntry *);
|
||||
ht->buckets = (JSHashEntry**) allocOps->allocTable(allocPriv, nb);
|
||||
if (!ht->buckets) {
|
||||
allocOps->freeTable(allocPriv, ht);
|
||||
allocOps->freeTable(allocPriv, ht, nb);
|
||||
return NULL;
|
||||
}
|
||||
memset(ht->buckets, 0, nb);
|
||||
@ -153,11 +153,11 @@ JS_HashTableDestroy(JSHashTable *ht)
|
||||
#ifdef DEBUG
|
||||
memset(ht->buckets, 0xDB, n * sizeof ht->buckets[0]);
|
||||
#endif
|
||||
allocOps->freeTable(allocPriv, ht->buckets);
|
||||
allocOps->freeTable(allocPriv, ht->buckets, n * sizeof ht->buckets[0]);
|
||||
#ifdef DEBUG
|
||||
memset(ht, 0xDB, sizeof *ht);
|
||||
#endif
|
||||
allocOps->freeTable(allocPriv, ht);
|
||||
allocOps->freeTable(allocPriv, ht, sizeof *ht);
|
||||
}
|
||||
|
||||
/*
|
||||
@ -198,9 +198,7 @@ Resize(JSHashTable *ht, uint32 newshift)
|
||||
{
|
||||
size_t nb, nentries, i;
|
||||
JSHashEntry **oldbuckets, *he, *next, **hep;
|
||||
#ifdef DEBUG
|
||||
size_t nold = NBUCKETS(ht);
|
||||
#endif
|
||||
|
||||
JS_ASSERT(newshift < JS_HASH_BITS);
|
||||
|
||||
@ -230,17 +228,20 @@ Resize(JSHashTable *ht, uint32 newshift)
|
||||
hep = BUCKET_HEAD(ht, he->keyHash);
|
||||
|
||||
/*
|
||||
* Since he comes from the old table, it must be unique and we
|
||||
* simply add it to the head of bucket chain without chain lookup.
|
||||
* We do not require unique entries, instead appending he to the
|
||||
* chain starting at hep.
|
||||
*/
|
||||
he->next = *hep;
|
||||
while (*hep)
|
||||
hep = &(*hep)->next;
|
||||
he->next = NULL;
|
||||
*hep = he;
|
||||
}
|
||||
}
|
||||
#ifdef DEBUG
|
||||
memset(oldbuckets, 0xDB, nold * sizeof oldbuckets[0]);
|
||||
#endif
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets);
|
||||
ht->allocOps->freeTable(ht->allocPriv, oldbuckets,
|
||||
nold * sizeof oldbuckets[0]);
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@ typedef intN (* JSHashEnumerator)(JSHashEntry *he, intN i, void *arg);
|
||||
|
||||
typedef struct JSHashAllocOps {
|
||||
void * (*allocTable)(void *pool, size_t size);
|
||||
void (*freeTable)(void *pool, void *item);
|
||||
void (*freeTable)(void *pool, void *item, size_t size);
|
||||
JSHashEntry * (*allocEntry)(void *pool, const void *key);
|
||||
void (*freeEntry)(void *pool, JSHashEntry *he, uintN flag);
|
||||
} JSHashAllocOps;
|
||||
|
@ -221,7 +221,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
|
||||
*
|
||||
* So here, on first cache fill for this method, we brand
|
||||
* 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
|
||||
* scope->object will result in shape being regenerated.
|
||||
*/
|
||||
@ -701,7 +701,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
if (!js_GetCallObject(cx, fp))
|
||||
return NULL;
|
||||
|
||||
/* We know we must clone everything on blockChain. */
|
||||
/* We know we must clone everything on blockChain. */
|
||||
limitBlock = limitClone = NULL;
|
||||
} else {
|
||||
/*
|
||||
@ -715,7 +715,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
limitClone = OBJ_GET_PARENT(cx, limitClone);
|
||||
JS_ASSERT(limitClone);
|
||||
|
||||
/*
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
@ -743,7 +743,7 @@ js_GetScopeChain(JSContext *cx, JSStackFrame *fp)
|
||||
/*
|
||||
* Special-case cloning the innermost block; this doesn't have enough in
|
||||
* common with subsequent steps to include in the loop.
|
||||
*
|
||||
*
|
||||
* 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
|
||||
* 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
|
||||
* found it in blockChain.
|
||||
*/
|
||||
JS_ASSERT_IF(limitBlock &&
|
||||
OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
|
||||
JS_ASSERT_IF(limitBlock &&
|
||||
OBJ_GET_CLASS(cx, limitBlock) == &js_BlockClass &&
|
||||
OBJ_GET_PRIVATE(cx, limitClone) == fp,
|
||||
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_GETGVAR_LENGTH == JSOP_CALLGVAR_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_GETLOCAL_LENGTH == JSOP_CALLLOCAL_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_XMLNAME_LENGTH == JSOP_CALLXMLNAME_LENGTH);
|
||||
|
||||
/*
|
||||
* Same for JSOP_SETNAME and JSOP_SETPROP, which differ only slightly but
|
||||
* remain distinct for the decompiler. Ditto for JSOP_NULL{,THIS}.
|
||||
* remain distinct for the decompiler.
|
||||
*/
|
||||
JS_STATIC_ASSERT(JSOP_SETNAME_LENGTH == JSOP_SETPROP_LENGTH);
|
||||
JS_STATIC_ASSERT(JSOP_NULL_LENGTH == JSOP_NULLTHIS_LENGTH);
|
||||
|
||||
/* See TRY_BRANCH_AFTER_COND. */
|
||||
JS_STATIC_ASSERT(JSOP_IFNE_LENGTH == JSOP_IFEQ_LENGTH);
|
||||
@ -2665,7 +2665,8 @@ js_Interpret(JSContext *cx)
|
||||
/* We had better not be entering the interpreter from JIT-compiled code. */
|
||||
TraceRecorder *tr = TRACE_RECORDER(cx);
|
||||
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. */
|
||||
if (tr) {
|
||||
if (tr->wasDeepAborted())
|
||||
@ -2786,8 +2787,8 @@ js_Interpret(JSContext *cx)
|
||||
js_SetVersion(cx, currentVersion);
|
||||
|
||||
/* Update the static-link display. */
|
||||
if (script->staticDepth < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticDepth];
|
||||
if (script->staticLevel < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticLevel];
|
||||
fp->displaySave = *disp;
|
||||
*disp = fp;
|
||||
}
|
||||
@ -3063,8 +3064,8 @@ js_Interpret(JSContext *cx)
|
||||
JS_ASSERT(!fp->blockChain);
|
||||
JS_ASSERT(!js_IsActiveWithOrBlock(cx, fp->scopeChain, 0));
|
||||
|
||||
if (script->staticDepth < JS_DISPLAY_SIZE)
|
||||
cx->display[script->staticDepth] = fp->displaySave;
|
||||
if (script->staticLevel < JS_DISPLAY_SIZE)
|
||||
cx->display[script->staticLevel] = fp->displaySave;
|
||||
|
||||
if (hookData) {
|
||||
JSInterpreterHook hook;
|
||||
@ -3392,7 +3393,7 @@ js_Interpret(JSContext *cx)
|
||||
STORE_OPND(-1, lval);
|
||||
STORE_OPND(-2, rval);
|
||||
END_CASE(JSOP_SWAP)
|
||||
|
||||
|
||||
BEGIN_CASE(JSOP_PICK)
|
||||
i = regs.pc[1];
|
||||
JS_ASSERT(regs.sp - (i+1) >= StackBase(fp));
|
||||
@ -4845,13 +4846,13 @@ js_Interpret(JSContext *cx)
|
||||
regs.sp = vp + 1;
|
||||
CHECK_INTERRUPT_HANDLER();
|
||||
END_CASE(JSOP_NEW)
|
||||
|
||||
|
||||
BEGIN_CASE(JSOP_CALL)
|
||||
BEGIN_CASE(JSOP_EVAL)
|
||||
BEGIN_CASE(JSOP_APPLY)
|
||||
argc = GET_ARGC(regs.pc);
|
||||
vp = regs.sp - (argc + 2);
|
||||
|
||||
|
||||
lval = *vp;
|
||||
if (VALUE_IS_FUNCTION(cx, lval)) {
|
||||
obj = JSVAL_TO_OBJECT(lval);
|
||||
@ -4953,8 +4954,8 @@ js_Interpret(JSContext *cx)
|
||||
newifp->frame.dormantNext = NULL;
|
||||
newifp->frame.xmlNamespace = NULL;
|
||||
newifp->frame.blockChain = NULL;
|
||||
if (script->staticDepth < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticDepth];
|
||||
if (script->staticLevel < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticLevel];
|
||||
newifp->frame.displaySave = *disp;
|
||||
*disp = &newifp->frame;
|
||||
}
|
||||
@ -5405,7 +5406,6 @@ js_Interpret(JSContext *cx)
|
||||
END_CASE(JSOP_ONE)
|
||||
|
||||
BEGIN_CASE(JSOP_NULL)
|
||||
BEGIN_CASE(JSOP_NULLTHIS)
|
||||
PUSH_OPND(JSVAL_NULL);
|
||||
END_CASE(JSOP_NULL)
|
||||
|
||||
@ -5647,11 +5647,13 @@ js_Interpret(JSContext *cx)
|
||||
uva = JS_SCRIPT_UPVARS(script);
|
||||
JS_ASSERT(index < uva->length);
|
||||
skip = UPVAR_FRAME_SKIP(uva->vector[index]);
|
||||
fp2 = cx->display[script->staticDepth - skip];
|
||||
JS_ASSERT(fp2->fun && fp2->script);
|
||||
fp2 = cx->display[script->staticLevel - skip];
|
||||
JS_ASSERT(fp2->script);
|
||||
|
||||
slot = UPVAR_FRAME_SLOT(uva->vector[index]);
|
||||
if (slot < fp2->fun->nargs) {
|
||||
if (!fp2->fun) {
|
||||
vp = fp2->slots + fp2->script->nfixed;
|
||||
} else if (slot < fp2->fun->nargs) {
|
||||
vp = fp2->argv;
|
||||
} else {
|
||||
slot -= fp2->fun->nargs;
|
||||
@ -5665,6 +5667,22 @@ js_Interpret(JSContext *cx)
|
||||
}
|
||||
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_CALLGVAR)
|
||||
slot = GET_SLOTNO(regs.pc);
|
||||
@ -5791,25 +5809,40 @@ js_Interpret(JSContext *cx)
|
||||
* function body).
|
||||
*/
|
||||
LOAD_FUNCTION(0);
|
||||
obj = FUN_OBJECT(fun);
|
||||
|
||||
if (!fp->blockChain) {
|
||||
if (FUN_NULL_CLOSURE(fun)) {
|
||||
/*
|
||||
* 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;
|
||||
} else {
|
||||
obj2 = js_GetScopeChain(cx, fp);
|
||||
if (!obj2)
|
||||
goto error;
|
||||
JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
|
||||
|
||||
/*
|
||||
* 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
|
||||
* to the current scope via parent. This clause exists to enable
|
||||
* sharing of compiled functions among multiple equivalent scopes,
|
||||
* splitting the cost of compilation evenly among the scopes and
|
||||
* amortizing it over a number of executions. Examples include XUL
|
||||
* scripts and event handlers shared among Mozilla chrome windows,
|
||||
* and server-side JS user-defined functions shared among requests.
|
||||
* to the current scope via parent. We do this to enable sharing of
|
||||
* compiled functions among multiple equivalent scopes, amortizing
|
||||
* the cost of compilation over a number of executions. Examples
|
||||
* include XUL scripts and event handlers shared among Firefox or
|
||||
* other Mozilla app chrome windows, and user-defined JS functions
|
||||
* precompiled and then shared among requests in server-side JS.
|
||||
*/
|
||||
obj = FUN_OBJECT(fun);
|
||||
if (OBJ_GET_PARENT(cx, obj) != obj2) {
|
||||
obj = js_CloneFunctionObject(cx, fun, obj2);
|
||||
if (!obj)
|
||||
@ -5823,6 +5856,7 @@ js_Interpret(JSContext *cx)
|
||||
*/
|
||||
MUST_FLOW_THROUGH("restore_scope");
|
||||
fp->scopeChain = obj;
|
||||
|
||||
rval = OBJECT_TO_JSVAL(obj);
|
||||
|
||||
/*
|
||||
@ -5917,133 +5951,144 @@ js_Interpret(JSContext *cx)
|
||||
}
|
||||
END_CASE(JSOP_DEFFUN)
|
||||
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN)
|
||||
LOAD_FUNCTION(SLOTNO_LEN);
|
||||
BEGIN_CASE(JSOP_DEFFUN_FC)
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
/*
|
||||
* Define a local function (i.e., one nested at the top level of
|
||||
* another function), parented by the current scope chain, and
|
||||
* stored in a local variable slot that the compiler allocated.
|
||||
* This is an optimization over JSOP_DEFFUN that avoids requiring
|
||||
* a call object for the outer function's activation.
|
||||
*/
|
||||
slot = GET_SLOTNO(regs.pc);
|
||||
|
||||
parent = js_GetScopeChain(cx, fp);
|
||||
if (!parent)
|
||||
obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
rval = OBJECT_TO_JSVAL(obj);
|
||||
|
||||
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)
|
||||
goto error;
|
||||
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)
|
||||
/*
|
||||
* Define a local function (i.e., one nested at the top level of
|
||||
* another function), parented by the current scope chain, stored
|
||||
* in a local variable slot that the compiler allocated. This is
|
||||
* an optimization over JSOP_DEFFUN that avoids requiring a call
|
||||
* object for the outer function's activation.
|
||||
*/
|
||||
LOAD_FUNCTION(SLOTNO_LEN);
|
||||
JS_ASSERT(FUN_INTERPRETED(fun));
|
||||
JS_ASSERT(!FUN_FLAT_CLOSURE(fun));
|
||||
obj = FUN_OBJECT(fun);
|
||||
|
||||
if (FUN_NULL_CLOSURE(fun)) {
|
||||
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
|
||||
if (!obj)
|
||||
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);
|
||||
|
||||
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
|
||||
END_CASE(JSOP_DEFLOCALFUN)
|
||||
|
||||
BEGIN_CASE(JSOP_ANONFUNOBJ)
|
||||
/* Load the specified function object literal. */
|
||||
LOAD_FUNCTION(0);
|
||||
BEGIN_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
LOAD_FUNCTION(SLOTNO_LEN);
|
||||
|
||||
/* If re-parenting, push a clone of the function object. */
|
||||
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)
|
||||
goto error;
|
||||
}
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
END_CASE(JSOP_ANONFUNOBJ)
|
||||
|
||||
BEGIN_CASE(JSOP_NAMEDFUNOBJ)
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
/*
|
||||
* 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);
|
||||
obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
/*
|
||||
* 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);
|
||||
slot = GET_SLOTNO(regs.pc);
|
||||
TRACE_2(DefLocalFunSetSlot, slot, obj);
|
||||
|
||||
/*
|
||||
* 4. Create a property in the object Result(1). The property's
|
||||
* 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);
|
||||
fp->slots[slot] = OBJECT_TO_JSVAL(obj);
|
||||
END_CASE(JSOP_DEFLOCALFUN_FC)
|
||||
|
||||
/* 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;
|
||||
BEGIN_CASE(JSOP_LAMBDA)
|
||||
/* Load the specified function object literal. */
|
||||
LOAD_FUNCTION(0);
|
||||
obj = FUN_OBJECT(fun);
|
||||
|
||||
if (FUN_NULL_CLOSURE(fun)) {
|
||||
obj = js_CloneFunctionObject(cx, fun, fp->scopeChain);
|
||||
if (!obj)
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
END_CASE(JSOP_LAMBDA)
|
||||
|
||||
BEGIN_CASE(JSOP_LAMBDA_FC)
|
||||
LOAD_FUNCTION(0);
|
||||
|
||||
obj = js_NewFlatClosure(cx, fun);
|
||||
if (!obj)
|
||||
goto error;
|
||||
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(obj));
|
||||
END_CASE(JSOP_LAMBDA_FC)
|
||||
|
||||
BEGIN_CASE(JSOP_CALLEE)
|
||||
PUSH_OPND(OBJECT_TO_JSVAL(fp->callee));
|
||||
END_CASE(JSOP_CALLEE)
|
||||
|
||||
#if JS_HAS_GETTER_SETTER
|
||||
BEGIN_CASE(JSOP_GETTER)
|
||||
@ -6838,7 +6883,7 @@ js_Interpret(JSContext *cx)
|
||||
#ifdef DEBUG
|
||||
JS_ASSERT(OBJ_GET_CLASS(cx, fp->blockChain) == &js_BlockClass);
|
||||
uintN blockDepth = OBJ_BLOCK_DEPTH(cx, fp->blockChain);
|
||||
|
||||
|
||||
JS_ASSERT(blockDepth <= StackDepth(script));
|
||||
#endif
|
||||
/*
|
||||
@ -6979,15 +7024,6 @@ js_Interpret(JSContext *cx)
|
||||
L_JSOP_DEFXMLNS:
|
||||
# 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 */
|
||||
default:
|
||||
#endif
|
||||
@ -7012,7 +7048,7 @@ js_Interpret(JSContext *cx)
|
||||
// To keep things simple, we hard-code imacro exception handlers here.
|
||||
if (*fp->imacpc == JSOP_NEXTITER) {
|
||||
// pc may point to JSOP_DUP here due to bug 474854.
|
||||
JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP);
|
||||
JS_ASSERT(*regs.pc == JSOP_CALL || *regs.pc == JSOP_DUP || *regs.pc == JSOP_TRUE);
|
||||
if (js_ValueIsStopIteration(cx->exception)) {
|
||||
cx->throwing = JS_FALSE;
|
||||
cx->exception = JSVAL_VOID;
|
||||
@ -7031,9 +7067,9 @@ js_Interpret(JSContext *cx)
|
||||
JS_ASSERT((size_t)((fp->imacpc ? fp->imacpc : regs.pc) - script->code) < script->length);
|
||||
|
||||
#ifdef JS_TRACER
|
||||
/*
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (TRACE_RECORDER(cx))
|
||||
@ -7235,8 +7271,8 @@ js_Interpret(JSContext *cx)
|
||||
}
|
||||
|
||||
/* Undo the remaining effects committed on entry to js_Interpret. */
|
||||
if (script->staticDepth < JS_DISPLAY_SIZE)
|
||||
cx->display[script->staticDepth] = fp->displaySave;
|
||||
if (script->staticLevel < JS_DISPLAY_SIZE)
|
||||
cx->display[script->staticLevel] = fp->displaySave;
|
||||
JS_ASSERT(JS_PROPERTY_CACHE(cx).disabled == fp->pcDisabledSave);
|
||||
if (cx->version == currentVersion && currentVersion != originalVersion)
|
||||
js_SetVersion(cx, originalVersion);
|
||||
|
@ -128,7 +128,7 @@ struct JSStackFrame {
|
||||
JSStackFrame *dormantNext; /* next dormant frame chain */
|
||||
JSObject *xmlNamespace; /* null or default xml namespace in E4X */
|
||||
JSStackFrame *displaySave; /* previous value of display entry for
|
||||
script->staticDepth */
|
||||
script->staticLevel */
|
||||
#ifdef DEBUG
|
||||
jsrefcount pcDisabledSave; /* for balanced property cache control */
|
||||
#endif
|
||||
|
@ -676,7 +676,7 @@ generator_trace(JSTracer *trc, JSObject *obj)
|
||||
js_TraceStackFrame(trc, &gen->frame);
|
||||
}
|
||||
|
||||
JSClass js_GeneratorClass = {
|
||||
JS_FRIEND_DATA(JSClass) js_GeneratorClass = {
|
||||
js_Generator_str,
|
||||
JSCLASS_HAS_PRIVATE | JSCLASS_IS_ANONYMOUS |
|
||||
JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Generator),
|
||||
|
@ -121,9 +121,9 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp);
|
||||
|
||||
#endif
|
||||
|
||||
extern JSClass js_GeneratorClass;
|
||||
extern JSClass js_IteratorClass;
|
||||
extern JSClass js_StopIterationClass;
|
||||
extern JS_FRIEND_API(JSClass) js_GeneratorClass;
|
||||
extern JSClass js_IteratorClass;
|
||||
extern JSClass js_StopIterationClass;
|
||||
|
||||
static inline bool
|
||||
js_ValueIsStopIteration(jsval v)
|
||||
|
@ -136,10 +136,10 @@ struct JSTitle {
|
||||
|
||||
#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_)
|
||||
|
||||
#define JS_SET_SCOPE_INFO(scope_, file_, line_) \
|
||||
#define JS_SET_SCOPE_INFO(scope_, file_, line_) \
|
||||
js_SetScopeInfo(scope_, file_, line_)
|
||||
|
||||
#endif
|
||||
@ -157,25 +157,25 @@ struct JSTitle {
|
||||
* are for optimizations above the JSObjectOps layer, under which object locks
|
||||
* normally hide.
|
||||
*/
|
||||
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
|
||||
? (void)0 \
|
||||
: (js_LockObj(cx, obj), \
|
||||
#define JS_LOCK_OBJ(cx,obj) ((OBJ_SCOPE(obj)->title.ownercx == (cx)) \
|
||||
? (void)0 \
|
||||
: (js_LockObj(cx, obj), \
|
||||
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))
|
||||
|
||||
#define JS_LOCK_TITLE(cx,title) \
|
||||
((title)->ownercx == (cx) ? (void)0 \
|
||||
: (js_LockTitle(cx, (title)), \
|
||||
#define JS_LOCK_TITLE(cx,title) \
|
||||
((title)->ownercx == (cx) ? (void)0 \
|
||||
: (js_LockTitle(cx, (title)), \
|
||||
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))
|
||||
|
||||
#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_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
|
||||
#define JS_TRANSFER_SCOPE_LOCK(cx, scope, newscope) \
|
||||
js_TransferTitle(cx, &scope->title, &newscope->title)
|
||||
|
||||
|
||||
|
@ -1375,7 +1375,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
|
||||
tcflags = TCF_COMPILE_N_GO;
|
||||
if (caller) {
|
||||
tcflags |= TCF_PUT_STATIC_DEPTH(caller->script->staticDepth + 1);
|
||||
tcflags |= TCF_PUT_STATIC_LEVEL(caller->script->staticLevel + 1);
|
||||
principals = JS_EvalFramePrincipals(cx, fp, caller);
|
||||
file = js_ComputeFilename(cx, caller, principals, &line);
|
||||
} else {
|
||||
@ -1402,7 +1402,7 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
script->principals->subsume(script->principals, principals)))) {
|
||||
/*
|
||||
* Get the prior (cache-filling) eval's saved caller function.
|
||||
* See js_CompileScript in jsparse.cpp.
|
||||
* See JSCompiler::compileScript in jsparse.cpp.
|
||||
*/
|
||||
JSFunction *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) {
|
||||
/*
|
||||
* Get the source string passed for safekeeping in the
|
||||
* atom map by the prior eval to js_CompileScript.
|
||||
* atom map by the prior eval to JSCompiler::compileScript.
|
||||
*/
|
||||
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) {
|
||||
script = js_CompileScript(cx, scopeobj, caller, principals, tcflags,
|
||||
JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
NULL, file, line, str);
|
||||
script = JSCompiler::compileScript(cx, scopeobj, caller, principals, tcflags,
|
||||
JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
NULL, file, line, str);
|
||||
if (!script) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
|
@ -151,37 +151,41 @@ struct JSObjectMap {
|
||||
#define JS_INITIAL_NSLOTS 5
|
||||
|
||||
/*
|
||||
* When JSObject.dslots is not null, JSObject.dslots[-1] records the number of
|
||||
* available slots.
|
||||
* JSObject struct, with members sized to fit in 32 bytes on 32-bit targets,
|
||||
* 64 bytes on 64-bit systems. The JSFunction struct is an extension of this
|
||||
* 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 {
|
||||
JSObjectMap *map;
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
JSObjectMap *map; /* propery map, see jsscope.h */
|
||||
jsuword classword; /* classword, see above */
|
||||
jsval fslots[JS_INITIAL_NSLOTS]; /* small number of fixed slots */
|
||||
jsval *dslots; /* dynamically allocated slots */
|
||||
};
|
||||
|
||||
#define JSSLOT_PROTO 0
|
||||
|
@ -266,35 +266,49 @@ js_DumpScript(JSContext *cx, JSScript *script)
|
||||
const char *
|
||||
ToDisassemblySource(JSContext *cx, jsval v)
|
||||
{
|
||||
JSObject *obj;
|
||||
JSScopeProperty *sprop;
|
||||
char *source;
|
||||
const char *bytes;
|
||||
JSString *str;
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
obj = JSVAL_TO_OBJECT(v);
|
||||
if (OBJ_GET_CLASS(cx, obj) == &js_BlockClass) {
|
||||
source = JS_sprintf_append(NULL, "depth %d {",
|
||||
OBJ_BLOCK_DEPTH(cx, obj));
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
|
||||
JSObject *obj = JSVAL_TO_OBJECT(v);
|
||||
JSClass *clasp = OBJ_GET_CLASS(cx, obj);
|
||||
|
||||
if (clasp == &js_BlockClass) {
|
||||
char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
|
||||
for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProp;
|
||||
sprop;
|
||||
sprop = sprop->parent) {
|
||||
bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
|
||||
const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
|
||||
if (!bytes)
|
||||
return NULL;
|
||||
source = JS_sprintf_append(source, "%s: %d%s",
|
||||
bytes, sprop->shortid,
|
||||
sprop->parent ? ", " : "");
|
||||
}
|
||||
|
||||
source = JS_sprintf_append(source, "}");
|
||||
if (!source)
|
||||
return NULL;
|
||||
str = JS_NewString(cx, source, strlen(source));
|
||||
|
||||
JSString *str = JS_NewString(cx, source, strlen(source));
|
||||
if (!str)
|
||||
return NULL;
|
||||
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);
|
||||
}
|
||||
|
||||
@ -2788,12 +2802,14 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
break;
|
||||
}
|
||||
|
||||
case JSOP_CALLUPVAR:
|
||||
case JSOP_GETUPVAR:
|
||||
JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
|
||||
|
||||
if (!jp->fun)
|
||||
case JSOP_CALLUPVAR:
|
||||
case JSOP_GETDSLOT:
|
||||
case JSOP_CALLDSLOT:
|
||||
if (!jp->fun) {
|
||||
JS_ASSERT(jp->script->flags & JSSF_SAVED_CALLER_FUN);
|
||||
JS_GET_SCRIPT_FUNCTION(jp->script, 0, jp->fun);
|
||||
}
|
||||
|
||||
if (!jp->localNames)
|
||||
jp->localNames = js_GetLocalNameArray(cx, jp->fun, &jp->pool);
|
||||
@ -2945,6 +2961,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
: SprintCString(&ss->sprinter, js_yield_str);
|
||||
break;
|
||||
}
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
LOCAL_ASSERT(SN_TYPE(sn) == SRC_HIDDEN);
|
||||
/* FALL THROUGH */
|
||||
@ -2991,8 +3008,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
break;
|
||||
--pos;
|
||||
}
|
||||
JS_ASSERT_IF(saveop == JSOP_ARRAYPUSH,
|
||||
jp->script->nfixed + pos == GET_UINT16(pc));
|
||||
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
if (saveop == JSOP_YIELD) {
|
||||
@ -3020,7 +3035,9 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
* Array comprehension: retract the sprinter to the beginning
|
||||
* of the array initialiser and decompile "[<rval> for ...]".
|
||||
*/
|
||||
JS_ASSERT(jp->script->nfixed + pos == GET_UINT16(pc));
|
||||
LOCAL_ASSERT(ss->opcodes[pos] == JSOP_NEWINIT);
|
||||
|
||||
start = ss->offsets[pos];
|
||||
LOCAL_ASSERT(ss->sprinter.base[start] == '[' ||
|
||||
ss->sprinter.base[start] == '#');
|
||||
@ -3036,7 +3053,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
todo = -2;
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
#endif /* JS_HAS_GENERATORS */
|
||||
|
||||
case JSOP_THROWING:
|
||||
todo = -2;
|
||||
@ -3088,11 +3105,12 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
JS_ASSERT(pc[cond] == JSOP_NEXTITER);
|
||||
DECOMPILE_CODE(pc + oplen, next - oplen);
|
||||
lval = POP_STR();
|
||||
LOCAL_ASSERT(ss->top >= 2);
|
||||
|
||||
if (ss->inArrayInit || ss->inGenExp) {
|
||||
(void) PopOff(ss, JSOP_NOP);
|
||||
rval = TOP_STR();
|
||||
LOCAL_ASSERT(ss->top >= 2);
|
||||
if (ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
|
||||
if (ss->top >= 2 && ss->opcodes[ss->top - 2] == JSOP_FORLOCAL) {
|
||||
ss->sprinter.offset = ss->offsets[ss->top - 1] - PAREN_SLOP;
|
||||
if (Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
@ -3108,7 +3126,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
*/
|
||||
todo = ss->offsets[ss->top - 1];
|
||||
} else {
|
||||
LOCAL_ASSERT(ss->opcodes[ss->top - 2] == JSOP_ENTERBLOCK);
|
||||
todo = Sprint(&ss->sprinter, " %s (%s in %s)",
|
||||
foreach ? js_for_each_str : js_for_str,
|
||||
lval, rval);
|
||||
@ -3130,6 +3147,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
jp->indent -= 4;
|
||||
js_printf(jp, "\t}\n");
|
||||
}
|
||||
|
||||
pc += tail;
|
||||
LOCAL_ASSERT(*pc == JSOP_IFNE || *pc == JSOP_IFNEX);
|
||||
len = js_CodeSpec[*pc].length;
|
||||
@ -3951,26 +3969,39 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
todo = STR2OFF(&ss->sprinter, rval);
|
||||
break;
|
||||
|
||||
case JSOP_ANONFUNOBJ:
|
||||
case JSOP_LAMBDA:
|
||||
case JSOP_LAMBDA_FC:
|
||||
#if JS_HAS_GENERATOR_EXPRS
|
||||
sn = js_GetSrcNote(jp->script, pc);
|
||||
if (sn && SN_TYPE(sn) == SRC_GENEXP) {
|
||||
JSScript *inner, *outer;
|
||||
void *mark;
|
||||
jsuword *innerLocalNames, *outerLocalNames;
|
||||
JSScript *inner, *outer;
|
||||
SprintStack ss2;
|
||||
JSFunction *outerfun;
|
||||
|
||||
LOAD_FUNCTION(0);
|
||||
inner = fun->u.i.script;
|
||||
|
||||
/*
|
||||
* All allocation when decompiling is LIFO, using malloc
|
||||
* or, more commonly, arena-allocating from cx->tempPool.
|
||||
* After InitSprintStack succeeds, we must release to mark
|
||||
* before returning.
|
||||
* Therefore after InitSprintStack succeeds, we must
|
||||
* release to mark before returning.
|
||||
*/
|
||||
mark = JS_ARENA_MARK(&cx->tempPool);
|
||||
if (!InitSprintStack(cx, &ss2, jp, StackDepth(inner)))
|
||||
uintN n = JS_GET_LOCAL_NAME_COUNT(fun);
|
||||
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;
|
||||
}
|
||||
ss2.inGenExp = JS_TRUE;
|
||||
|
||||
/*
|
||||
@ -3981,20 +4012,27 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
* string pushed on ss2.
|
||||
*/
|
||||
outer = jp->script;
|
||||
outerfun = jp->fun;
|
||||
outerLocalNames = jp->localNames;
|
||||
LOCAL_ASSERT(JS_UPTRDIFF(pc, outer->code) <= outer->length);
|
||||
jp->script = inner;
|
||||
if (!Decompile(&ss2, inner->code, inner->length, JSOP_NOP)) {
|
||||
jp->fun = fun;
|
||||
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);
|
||||
return NULL;
|
||||
}
|
||||
jp->script = outer;
|
||||
|
||||
/*
|
||||
* Advance over this op and its global |this| push, and
|
||||
* arrange to advance over the call to this lambda.
|
||||
*/
|
||||
pc += len;
|
||||
LOCAL_ASSERT(*pc == JSOP_NULL || *pc == JSOP_NULLTHIS);
|
||||
LOCAL_ASSERT(*pc == JSOP_NULL);
|
||||
pc += JSOP_NULL_LENGTH;
|
||||
LOCAL_ASSERT(*pc == JSOP_CALL);
|
||||
LOCAL_ASSERT(GET_ARGC(pc) == 0);
|
||||
@ -4035,7 +4073,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
* anonymous function, so it doesn't get decompiled as
|
||||
* a generator function in a getter or setter context.
|
||||
* The precedence level is the same for JSOP_NAME and
|
||||
* JSOP_ANONFUNOBJ.
|
||||
* JSOP_LAMBDA.
|
||||
*/
|
||||
LOCAL_ASSERT(js_CodeSpec[JSOP_NAME].prec ==
|
||||
js_CodeSpec[saveop].prec);
|
||||
@ -4058,7 +4096,6 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
#endif /* JS_HAS_GENERATOR_EXPRS */
|
||||
/* FALL THROUGH */
|
||||
|
||||
case JSOP_NAMEDFUNOBJ:
|
||||
LOAD_FUNCTION(0);
|
||||
{
|
||||
uintN indent = JS_DONT_PRETTY_PRINT;
|
||||
@ -4067,7 +4104,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
* Always parenthesize expression closures. We can't force
|
||||
* saveop to a low-precedence op to arrange for auto-magic
|
||||
* parenthesization without confusing getter/setter code
|
||||
* that checks for JSOP_ANONFUNOBJ and JSOP_NAMEDFUNOBJ.
|
||||
* that checks for JSOP_LAMBDA.
|
||||
*/
|
||||
if (!(fun->flags & JSFUN_EXPR_CLOSURE))
|
||||
indent |= JS_IN_GROUP_CONTEXT;
|
||||
@ -4079,6 +4116,11 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
todo = SprintString(&ss->sprinter, str);
|
||||
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:
|
||||
LOAD_OBJECT(0);
|
||||
LOCAL_ASSERT(OBJ_GET_CLASS(cx, obj) == &js_RegExpClass);
|
||||
@ -4294,6 +4336,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
}
|
||||
|
||||
case JSOP_DEFFUN:
|
||||
case JSOP_DEFFUN_FC:
|
||||
LOAD_FUNCTION(0);
|
||||
todo = -2;
|
||||
goto do_function;
|
||||
@ -4465,8 +4508,8 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
!ATOM_IS_STRING(atom) ||
|
||||
!ATOM_IS_IDENTIFIER(atom) ||
|
||||
ATOM_IS_KEYWORD(atom) ||
|
||||
(ss->opcodes[ss->top+1] != JSOP_ANONFUNOBJ &&
|
||||
ss->opcodes[ss->top+1] != JSOP_NAMEDFUNOBJ)) {
|
||||
(ss->opcodes[ss->top+1] != JSOP_LAMBDA &&
|
||||
ss->opcodes[ss->top+1] != JSOP_LAMBDA_FC)) {
|
||||
todo = Sprint(&ss->sprinter, "%s%s%s %s: %s",
|
||||
lval,
|
||||
maybeComma,
|
||||
|
@ -65,13 +65,13 @@ typedef enum JSOp {
|
||||
*/
|
||||
#define JOF_BYTE 0 /* single bytecode, no immediates */
|
||||
#define JOF_JUMP 1 /* signed 16-bit jump offset immediate */
|
||||
#define JOF_ATOM 2 /* unsigned 16-bit constant pool index */
|
||||
#define JOF_ATOM 2 /* unsigned 16-bit constant index */
|
||||
#define JOF_UINT16 3 /* unsigned 16-bit immediate operand */
|
||||
#define JOF_TABLESWITCH 4 /* table switch */
|
||||
#define JOF_LOOKUPSWITCH 5 /* lookup switch */
|
||||
#define JOF_QARG 6 /* quickened get/set function argument ops */
|
||||
#define JOF_LOCAL 7 /* var or block-local variable */
|
||||
#define JOF_SLOTATOM 8 /* uint16 slot index + constant pool index */
|
||||
#define JOF_SLOTATOM 8 /* uint16 slot + constant index */
|
||||
#define JOF_JUMPX 9 /* signed 32-bit jump offset immediate */
|
||||
#define JOF_TABLESWITCHX 10 /* extended (32-bit offset) table switch */
|
||||
#define JOF_LOOKUPSWITCHX 11 /* extended (32-bit offset) lookup switch */
|
||||
@ -79,10 +79,11 @@ typedef enum JSOp {
|
||||
#define JOF_UINT8 13 /* uint8 immediate, e.g. top 8 bits of 24-bit
|
||||
atom index */
|
||||
#define JOF_INT32 14 /* int32 immediate operand */
|
||||
#define JOF_OBJECT 15 /* unsigned 16-bit object pool index */
|
||||
#define JOF_SLOTOBJECT 16 /* uint16 slot index + object pool index */
|
||||
#define JOF_REGEXP 17 /* unsigned 16-bit regexp pool index */
|
||||
#define JOF_OBJECT 15 /* unsigned 16-bit object index */
|
||||
#define JOF_SLOTOBJECT 16 /* uint16 slot index + object index */
|
||||
#define JOF_REGEXP 17 /* unsigned 16-bit regexp index */
|
||||
#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_NAME (1U<<5) /* name operation */
|
||||
|
@ -84,7 +84,7 @@
|
||||
* 17 new JSOP_NEW
|
||||
* 18 x.y, f(), etc. JSOP_GETPROP, JSOP_CALL, etc.
|
||||
* 19 x, null, JSOP_NAME, JSOP_NULL, etc.;
|
||||
* function (...) ... and JSOP_ANONFUNOBJ, JSOP_NAMEDFUNOBJ
|
||||
* function (...) ... and JSOP_LAMBDA
|
||||
*
|
||||
* The push-numeric-constant operators, JSOP_ZERO, JSOP_DOUBLE, etc., have
|
||||
* lower precedence than the member operators emitted for the . operator, to
|
||||
@ -197,8 +197,11 @@ 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_STRICTNE, 73, "strictne", "!==", 1, 2, 1, 10, JOF_BYTE|JOF_DETECTING|JOF_LEFTASSOC)
|
||||
|
||||
/* Variant of JSOP_NULL for default (global) |this| parameter pushing. */
|
||||
OPDEF(JSOP_NULLTHIS, 74, "nullthis", "nullthis", 1, 0, 1, 19, JOF_BYTE)
|
||||
/*
|
||||
* Host object extension: given 'o.item(i) = j', the left-hand side compiles
|
||||
* 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
|
||||
@ -325,11 +328,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_DEFVAR, 127,"defvar", NULL, 3, 0, 0, 0, JOF_ATOM|JOF_DECLARING)
|
||||
|
||||
/* Auto-clone (if needed due to re-parenting) and push an anonymous function. */
|
||||
OPDEF(JSOP_ANONFUNOBJ, 128, "anonfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT)
|
||||
/* Push a closure for a named or anonymous function expression. */
|
||||
OPDEF(JSOP_LAMBDA, 128, "lambda", NULL, 3, 0, 1, 19, JOF_OBJECT)
|
||||
|
||||
/* ECMA ed. 3 named function expression. */
|
||||
OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT)
|
||||
/* Used for named function expression self-naming, if lightweight. */
|
||||
OPDEF(JSOP_CALLEE, 129, "callee", NULL, 1, 0, 1, 19, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Like JSOP_SETLOCAL, but specialized to avoid requiring JSOP_POP immediately
|
||||
@ -337,28 +340,23 @@ OPDEF(JSOP_NAMEDFUNOBJ, 129, "namedfunobj", NULL, 3, 0, 1, 19, JOF_OBJECT
|
||||
*/
|
||||
OPDEF(JSOP_SETLOCALPOP, 130, "setlocalpop", NULL, 3, 1, 0, 3, JOF_LOCAL|JOF_NAME|JOF_SET)
|
||||
|
||||
/* Jump to target if top of stack value is of primitive type. */
|
||||
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)
|
||||
/* Pick an element from the stack. */
|
||||
OPDEF(JSOP_PICK, 131, "pick", NULL, 2, 0, 0, 0, JOF_UINT8)
|
||||
|
||||
/*
|
||||
* Exception handling no-op, for more economical byte-coding than SRC_TRYFIN
|
||||
* srcnote-annotated JSOP_NOPs and to simply stack balance handling.
|
||||
*/
|
||||
OPDEF(JSOP_TRY, 133,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_FINALLY, 134,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_TRY, 132,"try", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_FINALLY, 133,"finally", NULL, 1, 0, 2, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* 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).
|
||||
* Get a dynamic slot from an object known to have at least one greater than
|
||||
* the slot index number of values at obj->dslots. The CALL variant computes
|
||||
* the callee and this-object in preparation for a JSOP_CALL.
|
||||
*/
|
||||
OPDEF(JSOP_OBJTOP, 135,"objtop", NULL, 3, 0, 0, 0, JOF_UINT16)
|
||||
OPDEF(JSOP_GETDSLOT, 134,"getdslot", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
|
||||
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:
|
||||
@ -434,12 +432,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_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_GETFUNNS, 185,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Get a display (free) variable from the closure's reserved slots.
|
||||
*/
|
||||
OPDEF(JSOP_GETUPVAR, 186,"getupvar", NULL, 3, 0, 1, 19, JOF_UINT16|JOF_NAME)
|
||||
OPDEF(JSOP_GETUPVAR, 185,"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)
|
||||
|
||||
@ -491,76 +489,65 @@ 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_LEAVEBLOCK, 200,"leaveblock", NULL, 3, -1, 0, 0, JOF_UINT16)
|
||||
|
||||
/*
|
||||
* Pick an element from the stack.
|
||||
*/
|
||||
OPDEF(JSOP_PICK, 201,"pick", NULL, 2, 0, 0, 0, JOF_UINT8)
|
||||
/* 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)
|
||||
|
||||
/* 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_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)
|
||||
OPDEF(JSOP_PRIMTOP, 202,"primtop", NULL, 1, 1, 1, 0, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Generator and array comprehension support.
|
||||
*/
|
||||
OPDEF(JSOP_GENERATOR, 210,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_YIELD, 211,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
|
||||
OPDEF(JSOP_ARRAYPUSH, 212,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
|
||||
OPDEF(JSOP_GENERATOR, 203,"generator", NULL, 1, 0, 0, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_YIELD, 204,"yield", NULL, 1, 1, 1, 1, JOF_BYTE)
|
||||
OPDEF(JSOP_ARRAYPUSH, 205,"arraypush", NULL, 3, 1, 0, 3, JOF_LOCAL)
|
||||
|
||||
/*
|
||||
* In the forthcoming great opcode reorg, this should go next to JSOP_GETUPVAR.
|
||||
* Get the built-in function::foo namespace and push it.
|
||||
*/
|
||||
OPDEF(JSOP_CALLUPVAR, 213, "callupvar", NULL, 3, 0, 2, 19, JOF_UINT16|JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_GETFUNNS, 206,"getfunns", NULL, 1, 0, 1, 19, JOF_BYTE)
|
||||
|
||||
/*
|
||||
* Variant of JSOP_ENUMELEM for destructuring const (const [a, b] = ...).
|
||||
*/
|
||||
OPDEF(JSOP_ENUMCONSTELEM, 214,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
|
||||
OPDEF(JSOP_ENUMCONSTELEM, 207,"enumconstelem",NULL, 1, 3, 0, 3, JOF_BYTE|JOF_SET)
|
||||
|
||||
/*
|
||||
* Variant of JSOP_LEAVEBLOCK has a result on the stack above the locals,
|
||||
* which must be moved down when the block pops.
|
||||
*/
|
||||
OPDEF(JSOP_LEAVEBLOCKEXPR,215,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16)
|
||||
OPDEF(JSOP_LEAVEBLOCKEXPR,208,"leaveblockexpr",NULL, 3, -1, 1, 3, JOF_UINT16)
|
||||
|
||||
/*
|
||||
* Optimize common JSOP_{THIS,GET{ARG,LOCAL}} -> JSOP_GETPROP cliches.
|
||||
*/
|
||||
OPDEF(JSOP_GETTHISPROP, 216,"getthisprop", NULL, 3, 0, 1, 18, JOF_ATOM|JOF_VARPROP)
|
||||
OPDEF(JSOP_GETARGPROP, 217,"getargprop", 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)
|
||||
OPDEF(JSOP_GETTHISPROP, 209,"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_GETLOCALPROP, 211,"getlocalprop", NULL, 5, 0, 1, 18, JOF_SLOTATOM|JOF_VARPROP)
|
||||
|
||||
/*
|
||||
* Optimize atom segments 1-3. These must be followed by JSOP_RESETBASE0 after
|
||||
* the opcode that they prefix.
|
||||
*/
|
||||
OPDEF(JSOP_INDEXBASE1, 220,"atombase1", 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, 222,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
|
||||
OPDEF(JSOP_INDEXBASE1, 212,"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_INDEXBASE3, 214,"atombase3", NULL, 1, 0, 0, 0, JOF_BYTE |JOF_INDEXBASE)
|
||||
|
||||
OPDEF(JSOP_CALLGVAR, 223, "callgvar", NULL, 3, 0, 2, 19, JOF_ATOM|JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_CALLLOCAL, 224, "calllocal", NULL, 3, 0, 2, 19, JOF_LOCAL|JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_CALLARG, 225, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_CALLBUILTIN, 226, "callbuiltin", NULL, 3, 0, 2, 0, JOF_UINT16)
|
||||
OPDEF(JSOP_CALLGVAR, 215, "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_CALLARG, 217, "callarg", NULL, 3, 0, 2, 19, JOF_QARG |JOF_NAME|JOF_CALLOP)
|
||||
OPDEF(JSOP_CALLBUILTIN, 218, "callbuiltin", NULL, 3, 0, 2, 0, JOF_UINT16)
|
||||
|
||||
/*
|
||||
* Opcodes to hold 8-bit and 32-bit immediate integer operands.
|
||||
*/
|
||||
OPDEF(JSOP_INT8, 227, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
|
||||
OPDEF(JSOP_INT32, 228, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
|
||||
OPDEF(JSOP_INT8, 219, "int8", NULL, 2, 0, 1, 16, JOF_INT8)
|
||||
OPDEF(JSOP_INT32, 220, "int32", NULL, 5, 0, 1, 16, JOF_INT32)
|
||||
|
||||
/*
|
||||
* Get the value of the 'length' property from a stacked object.
|
||||
*/
|
||||
OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
|
||||
OPDEF(JSOP_LENGTH, 221, "length", NULL, 1, 1, 1, 18, JOF_BYTE|JOF_PROP)
|
||||
|
||||
/*
|
||||
* Construct a new dense array whose contents are the values provided on the
|
||||
@ -569,13 +556,27 @@ OPDEF(JSOP_LENGTH, 229, "length", NULL, 1, 1, 1, 18, JOF_BYTE|J
|
||||
* bottommost value is the first value in the array; the array length is a
|
||||
* 24-bit immediate operand to the instruction.
|
||||
*/
|
||||
OPDEF(JSOP_NEWARRAY, 230, "newarray", NULL, 4, -1, 1, 19, JOF_UINT24)
|
||||
OPDEF(JSOP_NEWARRAY, 222, "newarray", NULL, 4, -1, 1, 19, JOF_UINT24)
|
||||
|
||||
/*
|
||||
* 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
|
||||
* with the JSOP_NEWARRAY and JSOP_NEWINIT opcodes.
|
||||
*/
|
||||
OPDEF(JSOP_HOLE, 231, "hole", NULL, 1, 0, 1, 0, JOF_BYTE)
|
||||
OPDEF(JSOP_HOLE, 223, "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)
|
||||
|
4019
js/src/jsparse.cpp
4019
js/src/jsparse.cpp
File diff suppressed because it is too large
Load Diff
705
js/src/jsparse.h
705
js/src/jsparse.h
@ -46,6 +46,7 @@
|
||||
#include "jsversion.h"
|
||||
#include "jsprvtd.h"
|
||||
#include "jspubtd.h"
|
||||
#include "jsatom.h"
|
||||
#include "jsscan.h"
|
||||
|
||||
JS_BEGIN_EXTERN_C
|
||||
@ -61,13 +62,25 @@ JS_BEGIN_EXTERN_C
|
||||
* Label Variant Members
|
||||
* ----- ------- -------
|
||||
* <Definitions>
|
||||
* TOK_FUNCTION func pn_funpob: JSParsedObjectBox holding function
|
||||
* TOK_FUNCTION name pn_funbox: ptr to JSFunctionBox holding function
|
||||
* object containing arg and var properties. We
|
||||
* create the function object at parse (not emit)
|
||||
* time to specialize arg and var bytecodes early.
|
||||
* pn_body: TOK_LC node for function body statements
|
||||
* pn_flags: TCF_FUN_* flags (see jsemit.h) collected
|
||||
* while parsing the function's body
|
||||
* pn_body: TOK_UPVARS if the function's source body
|
||||
* depends on outer names, else TOK_ARGSBODY
|
||||
* if formal parameters, else TOK_LC node for
|
||||
* 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>
|
||||
* TOK_LC list pn_head: list of pn_count statements
|
||||
@ -86,7 +99,7 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_FOR binary pn_left: either
|
||||
* for/in loop: a binary TOK_IN node with
|
||||
* pn_left: TOK_VAR or TOK_NAME to left of 'in'
|
||||
* if TOK_VAR, its pn_extra may have PNX_POPVAR
|
||||
* if TOK_VAR, its pn_xflags may have PNX_POPVAR
|
||||
* and PNX_FORINVAR bits set
|
||||
* pn_right: object expr to right of 'in'
|
||||
* for(;;) loop: a ternary TOK_RESERVED node with
|
||||
@ -108,10 +121,15 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_BREAK 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_VAR list pn_head: list of pn_count TOK_NAME nodes
|
||||
* TOK_VAR list pn_head: list of TOK_NAME or TOK_ASSIGN nodes
|
||||
* each name node has
|
||||
* pn_used: false
|
||||
* pn_atom: variable name
|
||||
* 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_SEMI unary pn_kid: expr or null statement
|
||||
* TOK_COLON name pn_atom: label, pn_expr: labeled statement
|
||||
@ -136,12 +154,12 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_SHOP binary pn_left: left-assoc SH expr, pn_right: ADD expr
|
||||
* pn_op: JSOP_LSH, JSOP_RSH, JSOP_URSH
|
||||
* TOK_PLUS, binary pn_left: left-assoc ADD expr, pn_right: MUL expr
|
||||
* pn_extra: if a left-associated binary TOK_PLUS
|
||||
* pn_xflags: if a left-associated binary TOK_PLUS
|
||||
* tree has been flattened into a list (see above
|
||||
* under <Expressions>), pn_extra will contain
|
||||
* under <Expressions>), pn_xflags will contain
|
||||
* PNX_STRCAT if at least one list element is a
|
||||
* string literal (TOK_STRING); if such a list has
|
||||
* any non-string, non-number term, pn_extra will
|
||||
* any non-string, non-number term, pn_xflags will
|
||||
* contain PNX_CANTFOLD.
|
||||
* pn_
|
||||
* TOK_MINUS pn_op: JSOP_ADD, JSOP_SUB
|
||||
@ -164,12 +182,12 @@ JS_BEGIN_EXTERN_C
|
||||
* call is a MEMBER expr naming a callable object
|
||||
* TOK_RB list pn_head: list of pn_count array element exprs
|
||||
* [,,] holes are represented by TOK_COMMA nodes
|
||||
* pn_extra: PN_ENDCOMMA if extra comma at end
|
||||
* pn_xflags: PN_ENDCOMMA if extra comma at end
|
||||
* TOK_RC list pn_head: list of pn_count TOK_COLON nodes where
|
||||
* each has pn_left: property id, pn_right: value
|
||||
* var {x} = object destructuring shorthand shares
|
||||
* PN_NAME node for x on left and right of TOK_COLON
|
||||
* node in TOK_RC's list, has PNX_SHORTHAND flag
|
||||
* node in TOK_RC's list, has PNX_DESTRUCT flag
|
||||
* TOK_DEFSHARP unary pn_num: jsint value of n in #n=
|
||||
* pn_kid: primary function, paren, name, object or
|
||||
* array literal expressions
|
||||
@ -179,7 +197,11 @@ JS_BEGIN_EXTERN_C
|
||||
* TOK_STRING, pn_op: JSOP_NAME, JSOP_STRING, or JSOP_OBJECT, or
|
||||
* JSOP_REGEXP
|
||||
* TOK_REGEXP If JSOP_NAME, pn_op may be JSOP_*ARG or JSOP_*VAR
|
||||
* with pn_slot >= 0 and pn_const telling const-ness
|
||||
* with pn_cookie telling (staticLevel, slot) (see
|
||||
* 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_PRIMARY nullary pn_op: JSOp bytecode
|
||||
*
|
||||
@ -242,44 +264,49 @@ JS_BEGIN_EXTERN_C
|
||||
* Label Variant Members
|
||||
* ----- ------- -------
|
||||
* TOK_LEXICALSCOPE name pn_op: JSOP_LEAVEBLOCK or JSOP_LEAVEBLOCKEXPR
|
||||
* pn_pob: block object
|
||||
* pn_objbox: block object in JSObjectBox holder
|
||||
* pn_expr: block body
|
||||
* TOK_ARRAYCOMP list pn_head: list of pn_count (1 or 2) elements
|
||||
* if pn_count is 2, first element is #n=[...]
|
||||
* last element is block enclosing for loop(s)
|
||||
* and optionally if-guarded TOK_ARRAYPUSH
|
||||
* pn_extra: stack slot, used during code gen
|
||||
* TOK_ARRAYPUSH unary pn_op: JSOP_ARRAYCOMP
|
||||
* pn_kid: array comprehension expression
|
||||
*/
|
||||
typedef enum JSParseNodeArity {
|
||||
PN_FUNC = -3,
|
||||
PN_LIST = -2,
|
||||
PN_TERNARY = 3,
|
||||
PN_BINARY = 2,
|
||||
PN_UNARY = 1,
|
||||
PN_NAME = -1,
|
||||
PN_NULLARY = 0
|
||||
PN_NULLARY, /* 0 kids, only pn_atom/pn_dval/etc. */
|
||||
PN_UNARY, /* one kid, plus a couple of scalars */
|
||||
PN_BINARY, /* two kids, plus a couple of scalars */
|
||||
PN_TERNARY, /* three kids */
|
||||
PN_FUNC, /* function definition node */
|
||||
PN_LIST, /* generic singly linked list */
|
||||
PN_NAME, /* name use or definition node */
|
||||
PN_NAMESET /* JSAtomList + JSParseNode ptr */
|
||||
} JSParseNodeArity;
|
||||
|
||||
struct JSDefinition;
|
||||
|
||||
struct JSParseNode {
|
||||
uint16 pn_type;
|
||||
uint8 pn_op;
|
||||
int8 pn_arity;
|
||||
JSTokenPos pn_pos;
|
||||
ptrdiff_t pn_offset; /* first generated bytecode offset */
|
||||
uint32 pn_type:16, /* TOK_* type, see jsscan.h */
|
||||
pn_op:8, /* see JSOp enum and jsopcode.tbl */
|
||||
pn_arity:6, /* see JSParseNodeArity enum */
|
||||
pn_used:1, /* name node is on a use-chain */
|
||||
pn_defn:1; /* this node is a JSDefinition */
|
||||
|
||||
#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 {
|
||||
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 */
|
||||
JSParseNode *head; /* first node in list */
|
||||
JSParseNode **tail; /* ptr to ptr to last node in list */
|
||||
uint32 count; /* number of nodes in list */
|
||||
uint32 extra; /* extra flags, see below */
|
||||
uint32 xflags:12, /* extra flags, see below */
|
||||
blockid:20; /* see name variant below */
|
||||
} list;
|
||||
struct { /* ternary: if, for(;;), ?: */
|
||||
JSParseNode *kid1; /* condition, discriminant, etc. */
|
||||
@ -298,36 +325,44 @@ struct JSParseNode {
|
||||
JSBool hidden; /* hidden genexp-induced JSOP_YIELD */
|
||||
} unary;
|
||||
struct { /* name, labeled statement, etc. */
|
||||
JSAtom *atom; /* name or label atom, null if slot */
|
||||
JSParseNode *expr; /* object or initializer */
|
||||
jsint slot; /* -1 or arg or local var slot */
|
||||
JSBool isconst; /* true for const names */
|
||||
union {
|
||||
JSAtom *atom; /* lexical name or label atom */
|
||||
JSFunctionBox *funbox; /* function object */
|
||||
JSObjectBox *objbox; /* block or regexp object */
|
||||
};
|
||||
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;
|
||||
struct { /* lexical scope. */
|
||||
JSParsedObjectBox *pob; /* block object */
|
||||
JSParseNode *expr; /* object or initializer */
|
||||
jsint slot; /* -1 or arg or local var slot */
|
||||
} lexical;
|
||||
struct {
|
||||
struct { /* lexical dependencies + sub-tree */
|
||||
JSAtomSet names; /* set of names with JSDefinitions */
|
||||
JSParseNode *tree; /* sub-tree containing name uses */
|
||||
} nameset;
|
||||
struct { /* PN_NULLARY variant for E4X */
|
||||
JSAtom *atom; /* first atom in pair */
|
||||
JSAtom *atom2; /* second atom in pair or null */
|
||||
} apair;
|
||||
struct { /* object literal */
|
||||
JSParsedObjectBox *pob;
|
||||
} object;
|
||||
jsdouble dval; /* aligned numeric literal value */
|
||||
} pn_u;
|
||||
JSParseNode *pn_next; /* to align dval and pn_u on RISCs */
|
||||
};
|
||||
|
||||
#define pn_funpob pn_u.func.funpob
|
||||
#define pn_body pn_u.func.body
|
||||
#define pn_flags pn_u.func.flags
|
||||
#define pn_index pn_u.func.index
|
||||
#define pn_funbox pn_u.name.funbox
|
||||
#define pn_body pn_u.name.expr
|
||||
#define pn_cookie pn_u.name.cookie
|
||||
#define pn_dflags pn_u.name.dflags
|
||||
#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_tail pn_u.list.tail
|
||||
#define pn_count pn_u.list.count
|
||||
#define pn_extra pn_u.list.extra
|
||||
#define pn_xflags pn_u.list.xflags
|
||||
#define pn_kid1 pn_u.ternary.kid1
|
||||
#define pn_kid2 pn_u.ternary.kid2
|
||||
#define pn_kid3 pn_u.ternary.kid3
|
||||
@ -339,14 +374,50 @@ struct JSParseNode {
|
||||
#define pn_num pn_u.unary.num
|
||||
#define pn_hidden pn_u.unary.hidden
|
||||
#define pn_atom pn_u.name.atom
|
||||
#define pn_objbox pn_u.name.objbox
|
||||
#define pn_expr pn_u.name.expr
|
||||
#define pn_slot pn_u.name.slot
|
||||
#define pn_const pn_u.name.isconst
|
||||
#define pn_lexdef pn_u.name.lexdef
|
||||
#define pn_names pn_u.nameset.names
|
||||
#define pn_tree pn_u.nameset.tree
|
||||
#define pn_dval pn_u.dval
|
||||
#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_CANTFOLD 0x02 /* TOK_PLUS list has unfoldable term */
|
||||
#define PNX_POPVAR 0x04 /* TOK_VAR last result needs popping */
|
||||
@ -358,151 +429,435 @@ struct JSParseNode {
|
||||
#define PNX_NEEDBRACES 0x80 /* braces necessary due to closure */
|
||||
#define PNX_FUNCDEFS 0x100 /* contains top-level function
|
||||
statements */
|
||||
#define PNX_SHORTHAND 0x200 /* shorthand syntax used, at present
|
||||
object destructuring ({x,y}) only */
|
||||
#define PNX_DESTRARGS 0x400 /* the first child is node defining
|
||||
destructuring arguments */
|
||||
#define PNX_DESTRUCT 0x200 /* destructuring special cases:
|
||||
1. shorthand syntax used, at present
|
||||
object destructuring ({x,y}) only;
|
||||
2. the first child of function body
|
||||
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++;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Move pn2 into pn, preserving pn->pn_pos and pn->pn_offset and handing off
|
||||
* any kids in pn2->pn_u, by clearing pn2.
|
||||
* JSDefinition is a degenerate subtype of the PN_FUNC and PN_NAME variants of
|
||||
* JSParseNode, allocated only for function, var, const, and let declarations
|
||||
* 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 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
|
||||
#define dn_uses pn_link
|
||||
|
||||
#define PN_CLEAR_NODE(pn) \
|
||||
JS_BEGIN_MACRO \
|
||||
(pn)->pn_type = TOK_EOF; \
|
||||
(pn)->pn_op = JSOP_NOP; \
|
||||
(pn)->pn_arity = PN_NULLARY; \
|
||||
JS_END_MACRO
|
||||
struct JSDefinition : public JSParseNode
|
||||
{
|
||||
/*
|
||||
* We store definition pointers in PN_NAMESET JSAtomLists in the AST, but
|
||||
* due to redefinition these nodes may become uses of other definitions.
|
||||
* This is unusual, so we simply chase the pn_lexdef link to find the final
|
||||
* 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;
|
||||
}
|
||||
|
||||
/* True if pn is a parsenode representing a literal constant. */
|
||||
#define PN_IS_CONSTANT(pn) \
|
||||
((pn)->pn_type == TOK_NUMBER || \
|
||||
(pn)->pn_type == TOK_STRING || \
|
||||
((pn)->pn_type == TOK_PRIMARY && (pn)->pn_op != JSOP_THIS))
|
||||
bool test(uintN flag) const {
|
||||
JS_ASSERT(pn_defn);
|
||||
if (pn_dflags & flag)
|
||||
return true;
|
||||
#ifdef DEBUG
|
||||
for (JSParseNode *pn = dn_uses; pn; pn = pn->pn_link) {
|
||||
JS_ASSERT(!pn->pn_defn);
|
||||
JS_ASSERT(!(pn->pn_dflags & flag));
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#define PN_OP(pn) ((JSOp)(pn)->pn_op)
|
||||
#define PN_TYPE(pn) ((JSTokenType)(pn)->pn_type)
|
||||
bool isAssigned() const {
|
||||
return test(PND_ASSIGNED);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Compute a pointer to the last JSParseNode element in a singly-linked list.
|
||||
* NB: list must be non-empty for correct PN_LAST usage!
|
||||
* These two are overridden by JSDefinition and we cannot afford virtual
|
||||
* methods -- so we use the mighty 'if' statement!
|
||||
*/
|
||||
#define PN_LAST(list) \
|
||||
((JSParseNode *)((char *)(list)->pn_tail - offsetof(JSParseNode, pn_next)))
|
||||
inline bool
|
||||
JSParseNode::isAssigned() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (pn_defn)
|
||||
return ((JSDefinition *)this)->isAssigned();
|
||||
#endif
|
||||
return test(PND_ASSIGNED);
|
||||
}
|
||||
|
||||
#define PN_INIT_LIST(list) \
|
||||
JS_BEGIN_MACRO \
|
||||
(list)->pn_head = NULL; \
|
||||
(list)->pn_tail = &(list)->pn_head; \
|
||||
(list)->pn_count = (list)->pn_extra = 0; \
|
||||
JS_END_MACRO
|
||||
inline bool
|
||||
JSParseNode::isFunArg() const
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (pn_defn)
|
||||
return ((JSDefinition *)this)->isFunArg();
|
||||
#endif
|
||||
return test(PND_FUNARG);
|
||||
}
|
||||
|
||||
#define PN_INIT_LIST_1(list, pn) \
|
||||
JS_BEGIN_MACRO \
|
||||
(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;
|
||||
struct JSObjectBox {
|
||||
JSObjectBox *traceLink;
|
||||
JSObjectBox *emitLink;
|
||||
JSObject *object;
|
||||
};
|
||||
|
||||
struct JSParseContext {
|
||||
struct JSFunctionBox : public JSObjectBox
|
||||
{
|
||||
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;
|
||||
void *tempPoolMark; /* initial JSContext.tempPool mark */
|
||||
JSPrincipals *principals; /* principals associated with source */
|
||||
JSStackFrame *callerFrame; /* scripted caller frame for eval and
|
||||
debug scripts */
|
||||
JSParseNode *nodeList; /* list of recyclable parse-node
|
||||
structs */
|
||||
JSParsedObjectBox *traceListHead; /* list of parsed object for GC
|
||||
tracing */
|
||||
JSStackFrame *callerFrame; /* scripted caller frame for eval and dbgapi */
|
||||
JSParseNode *nodeList; /* list of recyclable parse-node structs */
|
||||
uint32 functionCount; /* number of functions in current unit */
|
||||
JSObjectBox *traceListHead; /* list of parsed object for GC tracing */
|
||||
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 JSParseContext.tokenStream as a pointer.
|
||||
* Convenience macro to access JSCompiler.tokenStream as a pointer.
|
||||
*/
|
||||
#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);
|
||||
#define TS(jsc) (&(jsc)->tokenStream)
|
||||
|
||||
extern JSBool
|
||||
js_FoldConstants(JSContext *cx, JSParseNode *pn, JSTreeContext *tc,
|
||||
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
|
||||
|
||||
#endif /* jsparse_h___ */
|
||||
|
@ -94,8 +94,9 @@ typedef struct JSCodeGenerator JSCodeGenerator;
|
||||
typedef struct JSGCThing JSGCThing;
|
||||
typedef struct JSGenerator JSGenerator;
|
||||
typedef struct JSNativeEnumerator JSNativeEnumerator;
|
||||
typedef struct JSParseContext JSParseContext;
|
||||
typedef struct JSParsedObjectBox JSParsedObjectBox;
|
||||
typedef struct JSCompiler JSCompiler;
|
||||
typedef struct JSFunctionBox JSFunctionBox;
|
||||
typedef struct JSObjectBox JSObjectBox;
|
||||
typedef struct JSParseNode JSParseNode;
|
||||
typedef struct JSPropCacheEntry JSPropCacheEntry;
|
||||
typedef struct JSProperty JSProperty;
|
||||
@ -243,7 +244,7 @@ typedef union JSTempValueUnion {
|
||||
JSTempValueTrace trace;
|
||||
JSScopeProperty *sprop;
|
||||
JSWeakRoots *weakRoots;
|
||||
JSParseContext *parseContext;
|
||||
JSCompiler *compiler;
|
||||
JSScript *script;
|
||||
jsval *array;
|
||||
} JSTempValueUnion;
|
||||
|
@ -137,6 +137,10 @@ typedef enum JSTokenType {
|
||||
TOK_SEQ = 82, /* synthetic sequence of statements,
|
||||
not a block */
|
||||
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_LIMIT /* domain size */
|
||||
} JSTokenType;
|
||||
@ -190,11 +194,45 @@ js_AppendJSString(JSStringBuffer *sb, JSString *str);
|
||||
struct JSTokenPtr {
|
||||
uint16 index; /* index of char in physical line */
|
||||
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 {
|
||||
JSTokenPtr begin; /* first character and line of token */
|
||||
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 {
|
||||
@ -298,6 +336,9 @@ struct JSTokenStream {
|
||||
/* Ignore keywords and return TOK_NAME instead to the parser. */
|
||||
#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 */
|
||||
#define LINE_SEPARATOR 0x2028
|
||||
#define PARA_SEPARATOR 0x2029
|
||||
|
@ -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.
|
||||
*/
|
||||
tcflags = 0;
|
||||
script = js_CompileScript(cx, scopeobj, NULL, principals, tcflags,
|
||||
JSSTRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
NULL, file, line);
|
||||
script = JSCompiler::compileScript(cx, scopeobj, NULL, principals, tcflags,
|
||||
STRING_CHARS(str), JSSTRING_LENGTH(str),
|
||||
NULL, file, line);
|
||||
if (!script)
|
||||
return JS_FALSE;
|
||||
|
||||
@ -457,7 +457,7 @@ js_XDRScript(JSXDRState *xdr, JSScript **scriptp, JSBool *hasMagic)
|
||||
version = (uint32)script->version | (script->nfixed << 16);
|
||||
lineno = (uint32)script->lineno;
|
||||
nslots = (uint32)script->nslots;
|
||||
nslots = (uint32)((script->staticDepth << 16) | script->nslots);
|
||||
nslots = (uint32)((script->staticLevel << 16) | script->nslots);
|
||||
natoms = (uint32)script->atomMap.length;
|
||||
|
||||
/* 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->nslots = (uint16)nslots;
|
||||
script->staticDepth = (uint16)(nslots >> 16);
|
||||
script->staticLevel = (uint16)(nslots >> 16);
|
||||
}
|
||||
|
||||
for (i = 0; i != natoms; ++i) {
|
||||
@ -983,7 +983,7 @@ js_alloc_table_space(void *priv, size_t size)
|
||||
}
|
||||
|
||||
static void
|
||||
js_free_table_space(void *priv, void *item)
|
||||
js_free_table_space(void *priv, void *item, size_t size)
|
||||
{
|
||||
free(item);
|
||||
}
|
||||
@ -1145,6 +1145,14 @@ SaveScriptFilename(JSRuntime *rt, const char *filename, uint32 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;
|
||||
}
|
||||
|
||||
@ -1471,14 +1479,14 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
script->main += prologLength;
|
||||
memcpy(script->code, CG_PROLOG_BASE(cg), prologLength * sizeof(jsbytecode));
|
||||
memcpy(script->main, CG_BASE(cg), mainLength * sizeof(jsbytecode));
|
||||
nfixed = (cg->treeContext.flags & TCF_IN_FUNCTION)
|
||||
? cg->treeContext.u.fun->u.i.nvars
|
||||
: cg->treeContext.ngvars + cg->regexpList.length;
|
||||
nfixed = (cg->flags & TCF_IN_FUNCTION)
|
||||
? cg->fun->u.i.nvars
|
||||
: cg->ngvars + cg->regexpList.length;
|
||||
JS_ASSERT(nfixed < SLOTNO_LIMIT);
|
||||
script->nfixed = (uint16) nfixed;
|
||||
js_InitAtomMap(cx, &script->atomMap, &cg->atomList);
|
||||
|
||||
filename = cg->treeContext.parseContext->tokenStream.filename;
|
||||
filename = cg->compiler->tokenStream.filename;
|
||||
if (filename) {
|
||||
script->filename = js_SaveScriptFilename(cx, filename);
|
||||
if (!script->filename)
|
||||
@ -1491,8 +1499,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
goto bad;
|
||||
}
|
||||
script->nslots = script->nfixed + cg->maxStackDepth;
|
||||
script->staticDepth = cg->staticDepth;
|
||||
script->principals = cg->treeContext.parseContext->principals;
|
||||
script->staticLevel = cg->staticLevel;
|
||||
script->principals = cg->compiler->principals;
|
||||
if (script->principals)
|
||||
JSPRINCIPALS_HOLD(cx, script->principals);
|
||||
|
||||
@ -1501,17 +1509,17 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
if (cg->ntrynotes != 0)
|
||||
js_FinishTakingTryNotes(cg, JS_SCRIPT_TRYNOTES(script));
|
||||
if (cg->objectList.length != 0)
|
||||
FinishParsedObjects(&cg->objectList, JS_SCRIPT_OBJECTS(script));
|
||||
cg->objectList.finish(JS_SCRIPT_OBJECTS(script));
|
||||
if (cg->regexpList.length != 0)
|
||||
FinishParsedObjects(&cg->regexpList, JS_SCRIPT_REGEXPS(script));
|
||||
if (cg->treeContext.flags & TCF_NO_SCRIPT_RVAL)
|
||||
cg->regexpList.finish(JS_SCRIPT_REGEXPS(script));
|
||||
if (cg->flags & TCF_NO_SCRIPT_RVAL)
|
||||
script->flags |= JSSF_NO_SCRIPT_RVAL;
|
||||
|
||||
if (cg->upvarList.count != 0) {
|
||||
JS_ASSERT(cg->upvarList.count <= cg->upvarMap.length);
|
||||
memcpy(JS_SCRIPT_UPVARS(script)->vector, cg->upvarMap.vector,
|
||||
cg->upvarList.count * sizeof(uint32));
|
||||
ATOM_LIST_INIT(&cg->upvarList);
|
||||
cg->upvarList.clear();
|
||||
JS_free(cx, cg->upvarMap.vector);
|
||||
cg->upvarMap.vector = NULL;
|
||||
}
|
||||
@ -1521,8 +1529,8 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
* so that the debugger has a valid FUN_SCRIPT(fun).
|
||||
*/
|
||||
fun = NULL;
|
||||
if (cg->treeContext.flags & TCF_IN_FUNCTION) {
|
||||
fun = cg->treeContext.u.fun;
|
||||
if (cg->flags & TCF_IN_FUNCTION) {
|
||||
fun = cg->fun;
|
||||
JS_ASSERT(FUN_INTERPRETED(fun) && !FUN_SCRIPT(fun));
|
||||
JS_ASSERT_IF(script->upvarsOffset != 0,
|
||||
JS_SCRIPT_UPVARS(script)->length == fun->u.i.nupvars);
|
||||
@ -1532,7 +1540,7 @@ js_NewScriptFromCG(JSContext *cx, JSCodeGenerator *cg)
|
||||
#ifdef CHECK_SCRIPT_OWNER
|
||||
script->owner = NULL;
|
||||
#endif
|
||||
if (cg->treeContext.flags & TCF_FUN_HEAVYWEIGHT)
|
||||
if (cg->flags & TCF_FUN_HEAVYWEIGHT)
|
||||
fun->flags |= JSFUN_HEAVYWEIGHT;
|
||||
}
|
||||
|
||||
|
@ -86,6 +86,7 @@ typedef struct JSUpvarArray {
|
||||
uint32 length; /* count of indexed upvar cookies */
|
||||
} JSUpvarArray;
|
||||
|
||||
#define FREE_UPVAR_COOKIE 0xffffffff
|
||||
#define MAKE_UPVAR_COOKIE(skip,slot) ((skip) << 16 | (slot))
|
||||
#define UPVAR_FRAME_SKIP(cookie) ((uint32)(cookie) >> 16)
|
||||
#define UPVAR_FRAME_SLOT(cookie) ((uint16)(cookie))
|
||||
@ -118,7 +119,7 @@ struct JSScript {
|
||||
const char *filename; /* source filename or null */
|
||||
uint32 lineno; /* base line number of script */
|
||||
uint16 nslots; /* vars plus maximum stack depth */
|
||||
uint16 staticDepth;/* static depth for display maintenance */
|
||||
uint16 staticLevel;/* static level for display maintenance */
|
||||
JSPrincipals *principals;/* principals for this script */
|
||||
union {
|
||||
JSObject *object; /* optional Script-class object wrapper */
|
||||
|
@ -3123,7 +3123,7 @@ js_CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj
|
||||
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
|
||||
GlobalState &state = tm->globalStates[i];
|
||||
|
||||
if (state.globalShape == (uint32) -1) {
|
||||
if (state.globalShape == uint32(-1)) {
|
||||
state.globalObj = globalObj;
|
||||
state.globalShape = globalShape;
|
||||
JS_ASSERT(state.globalSlots);
|
||||
@ -3315,8 +3315,8 @@ js_SynthesizeFrame(JSContext* cx, const FrameInfo& fi)
|
||||
newifp->frame.regs->sp = newsp + script->nfixed;
|
||||
newifp->frame.imacpc = NULL;
|
||||
newifp->frame.slots = newsp;
|
||||
if (script->staticDepth < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticDepth];
|
||||
if (script->staticLevel < JS_DISPLAY_SIZE) {
|
||||
JSStackFrame **disp = &cx->display[script->staticLevel];
|
||||
newifp->frame.displaySave = *disp;
|
||||
*disp = &newifp->frame;
|
||||
}
|
||||
@ -4252,7 +4252,7 @@ LeaveTree(InterpState& state, VMSideExit* lr)
|
||||
#ifdef DEBUG
|
||||
// Verify that our state restoration worked.
|
||||
for (JSStackFrame* fp = cx->fp; fp; fp = fp->down) {
|
||||
JS_ASSERT(!fp->callee || JSVAL_IS_OBJECT(fp->argv[-1]));
|
||||
JS_ASSERT_IF(fp->callee, JSVAL_IS_OBJECT(fp->argv[-1]));
|
||||
JS_ASSERT_IF(fp->callee && fp->thisp != JSVAL_TO_OBJECT(fp->argv[-1]),
|
||||
!(fp->flags & JSFRAME_COMPUTED_THIS) && !fp->thisp);
|
||||
}
|
||||
@ -4394,9 +4394,7 @@ TraceRecorder::monitorRecording(JSContext* cx, TraceRecorder* tr, JSOp op)
|
||||
tr->pendingTraceableNative = NULL;
|
||||
|
||||
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);)
|
||||
|
||||
/* If op is not a break or a return from a loop, continue recording and follow the
|
||||
@ -6097,15 +6095,20 @@ TraceRecorder::stobj_get_fslot(LIns* obj_ins, unsigned slot)
|
||||
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*
|
||||
TraceRecorder::stobj_get_slot(LIns* obj_ins, unsigned slot, LIns*& dslots_ins)
|
||||
{
|
||||
if (slot < JS_INITIAL_NSLOTS)
|
||||
return stobj_get_fslot(obj_ins, slot);
|
||||
|
||||
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));
|
||||
return stobj_get_dslot(obj_ins, slot - JS_INITIAL_NSLOTS, dslots_ins);
|
||||
}
|
||||
|
||||
bool
|
||||
@ -6120,7 +6123,7 @@ TraceRecorder::native_set(LIns* obj_ins, JSScopeProperty* sprop, LIns*& dslots_i
|
||||
|
||||
bool
|
||||
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))
|
||||
return false;
|
||||
@ -6271,13 +6274,17 @@ TraceRecorder::guardDenseArrayIndex(JSObject* obj, jsint idx, LIns* obj_ins,
|
||||
idx_ins,
|
||||
stobj_get_fslot(obj_ins, JSSLOT_ARRAY_LENGTH)),
|
||||
NULL);
|
||||
|
||||
/* If dslots is NULL, stay on trace (and read value as undefined). */
|
||||
LIns* br2 = lir->insBranch(LIR_jt, lir->ins_eq0(dslots_ins), NULL);
|
||||
|
||||
/* If not idx < capacity, stay on trace (and read value as undefined). */
|
||||
LIns* br3 = lir->insBranch(LIR_jf,
|
||||
lir->ins2(LIR_ult,
|
||||
idx_ins,
|
||||
lir->insLoad(LIR_ldp, dslots_ins, 0 - (int)sizeof(jsval))),
|
||||
lir->insLoad(LIR_ldp,
|
||||
dslots_ins,
|
||||
-(int)sizeof(jsval))),
|
||||
NULL);
|
||||
lir->insGuard(LIR_x, lir->insImm(1), snapshot(exitType));
|
||||
LIns* label = lir->ins0(LIR_label);
|
||||
@ -6828,7 +6835,17 @@ TraceRecorder::getClassPrototype(JSObject* ctor, LIns*& proto_ins)
|
||||
}
|
||||
|
||||
bool
|
||||
TraceRecorder::newArray(JSObject *ctor, uint32 argc, jsval *argv, jsval *rval)
|
||||
TraceRecorder::getClassPrototype(JSProtoKey key, LIns*& proto_ins)
|
||||
{
|
||||
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;
|
||||
if (!getClassPrototype(ctor, proto_ins))
|
||||
@ -7731,12 +7748,67 @@ TraceRecorder::record_JSOP_CALLNAME()
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_GETUPVAR()
|
||||
{
|
||||
return false;
|
||||
uintN index = GET_UINT16(cx->fp->regs->pc);
|
||||
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
|
||||
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;
|
||||
}
|
||||
|
||||
@ -8450,13 +8522,12 @@ JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_NEWINIT()
|
||||
{
|
||||
JSProtoKey key = JSProtoKey(GET_INT8(cx->fp->regs->pc));
|
||||
JSObject* obj;
|
||||
const CallInfo *ci;
|
||||
|
||||
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(key), &obj))
|
||||
LIns *proto_ins;
|
||||
if (!getClassPrototype(key, proto_ins))
|
||||
return false;
|
||||
ci = (key == JSProto_Array) ? &js_FastNewArray_ci : &js_Object_tn_ci;
|
||||
LIns* args[] = { INS_CONSTPTR(obj), cx_ins };
|
||||
|
||||
LIns* args[] = { proto_ins, cx_ins };
|
||||
const CallInfo *ci = (key == JSProto_Array) ? &js_FastNewArray_ci : &js_Object_tn_ci;
|
||||
LIns* v_ins = lir->insCall(ci, args);
|
||||
guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
|
||||
stack(0, v_ins);
|
||||
@ -8824,6 +8895,12 @@ TraceRecorder::record_JSOP_DEFFUN()
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_DEFFUN_FC()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_DEFCONST()
|
||||
{
|
||||
@ -8836,38 +8913,46 @@ TraceRecorder::record_JSOP_DEFVAR()
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX could hoist out to jsinterp.h and share with jsinterp.cpp, but
|
||||
* XXX jsopcode.cpp has different definitions of same-named macros.
|
||||
*/
|
||||
#define GET_FULL_INDEX(PCOFF) \
|
||||
(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
|
||||
TraceRecorder::record_JSOP_ANONFUNOBJ()
|
||||
jsatomid
|
||||
TraceRecorder::getFullIndex(ptrdiff_t pcoff)
|
||||
{
|
||||
JSFunction* fun;
|
||||
JSFrameRegs& regs = *cx->fp->regs;
|
||||
JSScript* script = cx->fp->script;
|
||||
LOAD_FUNCTION(0); // needs script, regs, fun
|
||||
|
||||
JSObject* obj = FUN_OBJECT(fun);
|
||||
if (OBJ_GET_PARENT(cx, obj) != cx->fp->scopeChain)
|
||||
ABORT_TRACE("can't trace with activation object on scopeChain");
|
||||
|
||||
stack(0, INS_CONSTPTR(obj));
|
||||
return true;
|
||||
jsatomid index = GET_INDEX(cx->fp->regs->pc + pcoff);
|
||||
index += atoms - cx->fp->script->atomMap.vector;
|
||||
return index;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_NAMEDFUNOBJ()
|
||||
TraceRecorder::record_JSOP_LAMBDA()
|
||||
{
|
||||
JSFunction* fun;
|
||||
JS_GET_SCRIPT_FUNCTION(cx->fp->script, getFullIndex(), fun);
|
||||
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_SETLOCALPOP()
|
||||
{
|
||||
@ -8936,8 +9021,20 @@ TraceRecorder::record_JSOP_ARGCNT()
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_DefLocalFunSetSlot(uint32 slot, JSObject* obj)
|
||||
{
|
||||
var(slot, INS_CONSTPTR(obj));
|
||||
return true;
|
||||
JSFunction* fun = GET_FUNCTION_PRIVATE(cx, 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
|
||||
@ -8946,6 +9043,12 @@ TraceRecorder::record_JSOP_DEFLOCALFUN()
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_DEFLOCALFUN_FC()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_GOTOX()
|
||||
{
|
||||
@ -9463,10 +9566,8 @@ TraceRecorder::record_JSOP_TYPEOFEXPR()
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_ENTERBLOCK()
|
||||
{
|
||||
JSScript* script = cx->fp->script;
|
||||
JSFrameRegs& regs = *cx->fp->regs;
|
||||
JSObject* obj;
|
||||
JS_GET_SCRIPT_OBJECT(script, GET_FULL_INDEX(0), obj);
|
||||
JS_GET_SCRIPT_OBJECT(cx->fp->script, getFullIndex(0), obj);
|
||||
|
||||
LIns* void_ins = INS_CONST(JSVAL_TO_PSEUDO_BOOLEAN(JSVAL_VOID));
|
||||
for (int i = 0, n = OBJ_BLOCK_COUNT(cx, obj); i < n; i++)
|
||||
@ -9737,13 +9838,6 @@ TraceRecorder::record_JSOP_CALLBUILTIN()
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_NULLTHIS()
|
||||
{
|
||||
stack(0, INS_CONSTPTR(NULL));
|
||||
return true;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_INT8()
|
||||
{
|
||||
@ -9815,14 +9909,13 @@ TraceRecorder::record_JSOP_LENGTH()
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::record_JSOP_NEWARRAY()
|
||||
{
|
||||
JSObject* proto;
|
||||
const CallInfo* ci = &js_NewUninitializedArray_ci;
|
||||
if (!js_GetClassPrototype(cx, globalObj, INT_TO_JSID(JSProto_Array), &proto))
|
||||
LIns *proto_ins;
|
||||
if (!getClassPrototype(JSProto_Array, proto_ins))
|
||||
return false;
|
||||
|
||||
uint32 len = GET_UINT24(cx->fp->regs->pc);
|
||||
LIns* args[] = { lir->insImm(len), INS_CONSTPTR(proto), cx_ins };
|
||||
LIns* v_ins = lir->insCall(ci, args);
|
||||
LIns* args[] = { lir->insImm(len), proto_ins, cx_ins };
|
||||
LIns* v_ins = lir->insCall(&js_NewUninitializedArray_ci, args);
|
||||
guard(false, lir->ins_eq0(v_ins), OOM_EXIT);
|
||||
|
||||
LIns* dslots_ins = NULL;
|
||||
@ -9895,12 +9988,3 @@ js_DumpPeerStability(JSTraceMonitor* tm, const void* ip, JSObject* globalObj, ui
|
||||
JS_NOT_REACHED("JSOP_UNUSED" # n); \
|
||||
return false; \
|
||||
}
|
||||
|
||||
UNUSED(203)
|
||||
UNUSED(204)
|
||||
UNUSED(205)
|
||||
UNUSED(206)
|
||||
UNUSED(207)
|
||||
UNUSED(208)
|
||||
UNUSED(209)
|
||||
UNUSED(219)
|
||||
|
@ -512,6 +512,8 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
nanojit::LIns* v_ins, const char *name);
|
||||
|
||||
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*& dslots_ins);
|
||||
bool native_set(nanojit::LIns* obj_ins, JSScopeProperty* sprop,
|
||||
@ -541,6 +543,7 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
void clearFrameSlotsFromCache();
|
||||
JS_REQUIRES_STACK bool guardCallee(jsval& callee);
|
||||
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 newString(JSObject* ctor, jsval& arg, jsval* rval);
|
||||
JS_REQUIRES_STACK bool interpretedFunctionCall(jsval& fval, JSFunction* fun, uintN argc,
|
||||
@ -559,6 +562,8 @@ class TraceRecorder : public avmplus::GCObject {
|
||||
bool hasMethod(JSObject* obj, jsid id);
|
||||
JS_REQUIRES_STACK bool hasIteratorMethod(JSObject* obj);
|
||||
|
||||
jsatomid getFullIndex(ptrdiff_t pcoff = 0);
|
||||
|
||||
public:
|
||||
JS_REQUIRES_STACK
|
||||
TraceRecorder(JSContext* cx, VMSideExit*, nanojit::Fragment*, TreeInfo*,
|
||||
@ -608,9 +613,10 @@ public:
|
||||
#define TRACE_RECORDER(cx) (JS_TRACE_MONITOR(cx).recorder)
|
||||
#define SET_TRACE_RECORDER(cx,tr) (JS_TRACE_MONITOR(cx).recorder = (tr))
|
||||
|
||||
#define JSOP_IS_BINARY(op) ((uintN)((op) - JSOP_BITOR) <= (uintN)(JSOP_MOD - JSOP_BITOR))
|
||||
#define JSOP_IS_UNARY(op) ((uintN)((op) - JSOP_NEG) <= (uintN)(JSOP_POS - JSOP_NEG))
|
||||
#define JSOP_IS_EQUALITY(op) ((uintN)((op) - JSOP_EQ) <= (uintN)(JSOP_NE - JSOP_EQ))
|
||||
#define JSOP_IN_RANGE(op,lo,hi) (uintN((op) - (lo)) <= uintN((hi) - (lo)))
|
||||
#define JSOP_IS_BINARY(op) JSOP_IN_RANGE(op, JSOP_BITOR, JSOP_MOD)
|
||||
#define JSOP_IS_UNARY(op) JSOP_IN_RANGE(op, JSOP_NEG, JSOP_POS)
|
||||
#define JSOP_IS_EQUALITY(op) JSOP_IN_RANGE(op, JSOP_EQ, JSOP_NE)
|
||||
|
||||
#define TRACE_ARGS_(x,args) \
|
||||
JS_BEGIN_MACRO \
|
||||
|
@ -204,7 +204,7 @@ JS_XDRFindClassById(JSXDRState *xdr, uint32 id);
|
||||
* before deserialization of bytecode. If the saved version does not match
|
||||
* the current version, abort deserialization and invalidate the file.
|
||||
*/
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 42)
|
||||
#define JSXDR_BYTECODE_VERSION (0xb973c0de - 43)
|
||||
|
||||
/*
|
||||
* Library-private functions.
|
||||
|
@ -1261,9 +1261,10 @@ 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 JSObject *
|
||||
ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
ParseNodeToQName(JSCompiler *jsc, JSParseNode *pn,
|
||||
JSXMLArray *inScopeNSes, JSBool isAttributeName)
|
||||
{
|
||||
JSContext *cx = jsc->context;
|
||||
JSString *str, *uri, *prefix, *localName;
|
||||
size_t length, offset;
|
||||
const jschar *start, *limit, *colon;
|
||||
@ -1313,15 +1314,15 @@ ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
}
|
||||
|
||||
if (!uri) {
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
|
||||
js_ReportCompileErrorNumber(jsc->context, &jsc->tokenStream, pn,
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_BAD_XML_NAMESPACE,
|
||||
js_ValueToPrintableString(cx,
|
||||
js_ValueToPrintableString(jsc->context,
|
||||
STRING_TO_JSVAL(prefix)));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
|
||||
localName = js_NewStringCopyN(jsc->context, colon + 1, length - (offset + 1));
|
||||
if (!localName)
|
||||
return NULL;
|
||||
} else {
|
||||
@ -1346,12 +1347,12 @@ ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
break;
|
||||
}
|
||||
}
|
||||
prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
|
||||
prefix = IS_EMPTY(uri) ? jsc->context->runtime->emptyString : NULL;
|
||||
}
|
||||
localName = str;
|
||||
}
|
||||
|
||||
return NewXMLQName(cx, uri, prefix, localName);
|
||||
return NewXMLQName(jsc->context, uri, prefix, localName);
|
||||
}
|
||||
|
||||
static JSString *
|
||||
@ -1381,9 +1382,10 @@ ChompXMLWhitespace(JSContext *cx, JSString *str)
|
||||
}
|
||||
|
||||
static JSXML *
|
||||
ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
ParseNodeToXML(JSCompiler *jsc, JSParseNode *pn,
|
||||
JSXMLArray *inScopeNSes, uintN flags)
|
||||
{
|
||||
JSContext *cx = jsc->context;
|
||||
JSXML *xml, *kid, *attr, *attrj;
|
||||
JSString *str;
|
||||
uint32 length, n, i, j;
|
||||
@ -1394,7 +1396,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
int stackDummy;
|
||||
|
||||
if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
|
||||
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
|
||||
JSMSG_OVER_RECURSED);
|
||||
return NULL;
|
||||
}
|
||||
@ -1413,7 +1415,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
case TOK_XMLELEM:
|
||||
length = inScopeNSes->length;
|
||||
pn2 = pn->pn_head;
|
||||
xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
|
||||
xml = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
|
||||
if (!xml)
|
||||
goto fail;
|
||||
|
||||
@ -1438,7 +1440,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
continue;
|
||||
}
|
||||
|
||||
kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
|
||||
kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
|
||||
if (kid == PN2X_SKIP_CHILD) {
|
||||
--n;
|
||||
continue;
|
||||
@ -1489,7 +1491,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
continue;
|
||||
}
|
||||
|
||||
kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
|
||||
kid = ParseNodeToXML(jsc, pn2, inScopeNSes, flags);
|
||||
if (kid == PN2X_SKIP_CHILD) {
|
||||
--n;
|
||||
continue;
|
||||
@ -1533,7 +1535,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
/* Enforce "Well-formedness constraint: Unique Att Spec". */
|
||||
for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
|
||||
if (pn3->pn_atom == pn2->pn_atom) {
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
|
||||
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_DUPLICATE_XML_ATTR,
|
||||
js_ValueToPrintableString(cx,
|
||||
@ -1618,7 +1620,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
|
||||
/* Second pass: process tag name and attributes, using namespaces. */
|
||||
pn2 = pn->pn_head;
|
||||
qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
|
||||
qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_FALSE);
|
||||
if (!qn)
|
||||
goto fail;
|
||||
xml->name = qn;
|
||||
@ -1629,7 +1631,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
goto fail;
|
||||
|
||||
for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
|
||||
qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
|
||||
qn = ParseNodeToQName(jsc, pn2, inScopeNSes, JS_TRUE);
|
||||
if (!qn) {
|
||||
xml->xml_attrs.length = i;
|
||||
goto fail;
|
||||
@ -1644,7 +1646,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
attrjqn = attrj->name;
|
||||
if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
|
||||
js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
|
||||
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn2,
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_DUPLICATE_XML_ATTR,
|
||||
js_ValueToPrintableString(cx,
|
||||
@ -1685,7 +1687,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
xml_class = JSXML_CLASS_COMMENT;
|
||||
} else if (pn->pn_type == TOK_XMLPI) {
|
||||
if (IS_XML(str)) {
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
|
||||
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn,
|
||||
JSREPORT_ERROR,
|
||||
JSMSG_RESERVED_ID,
|
||||
js_ValueToPrintableString(cx,
|
||||
@ -1696,7 +1698,7 @@ ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
|
||||
if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
|
||||
goto skip_child;
|
||||
|
||||
qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
|
||||
qn = ParseNodeToQName(jsc, pn, inScopeNSes, JS_FALSE);
|
||||
if (!qn)
|
||||
goto fail;
|
||||
|
||||
@ -1734,7 +1736,7 @@ skip_child:
|
||||
#undef PN2X_SKIP_CHILD
|
||||
|
||||
syntax:
|
||||
js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
|
||||
js_ReportCompileErrorNumber(cx, &jsc->tokenStream, pn, JSREPORT_ERROR,
|
||||
JSMSG_BAD_XML_MARKUP);
|
||||
fail:
|
||||
js_LeaveLocalRootScope(cx);
|
||||
@ -1828,7 +1830,6 @@ ParseXMLSource(JSContext *cx, JSString *src)
|
||||
jschar *chars;
|
||||
const jschar *srcp, *endp;
|
||||
JSXML *xml;
|
||||
JSParseContext pc;
|
||||
const char *filename;
|
||||
uintN lineno;
|
||||
JSStackFrame *fp;
|
||||
@ -1891,20 +1892,19 @@ ParseXMLSource(JSContext *cx, JSString *src)
|
||||
}
|
||||
}
|
||||
|
||||
if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
|
||||
filename, lineno))
|
||||
goto out;
|
||||
pn = js_ParseXMLText(cx, js_GetTopStackFrame(cx)->scopeChain, &pc,
|
||||
JS_FALSE);
|
||||
if (pn && XMLArrayInit(cx, &nsarray, 1)) {
|
||||
if (GetXMLSettingFlags(cx, &flags))
|
||||
xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
|
||||
{
|
||||
JSCompiler jsc(cx);
|
||||
if (jsc.init(chars, length, NULL, filename, lineno)) {
|
||||
pn = jsc.parseXMLText(js_GetTopStackFrame(cx)->scopeChain, false);
|
||||
if (pn && XMLArrayInit(cx, &nsarray, 1)) {
|
||||
if (GetXMLSettingFlags(cx, &flags))
|
||||
xml = ParseNodeToXML(&jsc, pn, &nsarray, flags);
|
||||
|
||||
XMLArrayFinish(cx, &nsarray);
|
||||
XMLArrayFinish(cx, &nsarray);
|
||||
}
|
||||
}
|
||||
}
|
||||
js_FinishParseContext(cx, &pc);
|
||||
|
||||
out:
|
||||
JS_free(cx, chars);
|
||||
return xml;
|
||||
|
||||
@ -7433,24 +7433,24 @@ js_FinalizeXML(JSContext *cx, JSXML *xml)
|
||||
}
|
||||
|
||||
JSObject *
|
||||
js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn)
|
||||
js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn)
|
||||
{
|
||||
jsval nsval;
|
||||
JSObject *ns;
|
||||
JSXMLArray nsarray;
|
||||
JSXML *xml;
|
||||
|
||||
if (!js_GetDefaultXMLNamespace(cx, &nsval))
|
||||
if (!js_GetDefaultXMLNamespace(jsc->context, &nsval))
|
||||
return NULL;
|
||||
JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
|
||||
ns = JSVAL_TO_OBJECT(nsval);
|
||||
|
||||
if (!XMLArrayInit(cx, &nsarray, 1))
|
||||
if (!XMLArrayInit(jsc->context, &nsarray, 1))
|
||||
return NULL;
|
||||
|
||||
XMLARRAY_APPEND(cx, &nsarray, ns);
|
||||
xml = ParseNodeToXML(cx, pc, pn, &nsarray, XSF_PRECOMPILED_ROOT);
|
||||
XMLArrayFinish(cx, &nsarray);
|
||||
XMLARRAY_APPEND(jsc->context, &nsarray, ns);
|
||||
xml = ParseNodeToXML(jsc, pn, &nsarray, XSF_PRECOMPILED_ROOT);
|
||||
XMLArrayFinish(jsc->context, &nsarray);
|
||||
if (!xml)
|
||||
return NULL;
|
||||
|
||||
|
@ -160,7 +160,7 @@ extern void
|
||||
js_FinalizeXML(JSContext *cx, JSXML *xml);
|
||||
|
||||
extern JSObject *
|
||||
js_ParseNodeToXMLObject(JSContext *cx, JSParseContext *pc, JSParseNode *pn);
|
||||
js_ParseNodeToXMLObject(JSCompiler *jsc, JSParseNode *pn);
|
||||
|
||||
extern JSObject *
|
||||
js_NewXMLObject(JSContext *cx, JSXMLClass xml_class);
|
||||
|
@ -62,6 +62,7 @@
|
||||
#include "jsemit.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsgc.h"
|
||||
#include "jsiter.h"
|
||||
#include "jslock.h"
|
||||
#include "jsnum.h"
|
||||
#include "jsobj.h"
|
||||
@ -1348,22 +1349,31 @@ CountHeap(JSContext *cx, uintN argc, jsval *vp)
|
||||
static JSScript *
|
||||
ValueToScript(JSContext *cx, jsval v)
|
||||
{
|
||||
JSScript *script;
|
||||
JSScript *script = NULL;
|
||||
JSFunction *fun;
|
||||
|
||||
if (!JSVAL_IS_PRIMITIVE(v) &&
|
||||
JS_GET_CLASS(cx, JSVAL_TO_OBJECT(v)) == &js_ScriptClass) {
|
||||
script = (JSScript *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(v));
|
||||
} else {
|
||||
if (!JSVAL_IS_PRIMITIVE(v)) {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(v);
|
||||
JSClass *clasp = JS_GET_CLASS(cx, obj);
|
||||
|
||||
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);
|
||||
if (!fun)
|
||||
return NULL;
|
||||
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;
|
||||
@ -1706,7 +1716,7 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
return JS_FALSE;
|
||||
if (VALUE_IS_FUNCTION(cx, argv[i])) {
|
||||
JSFunction *fun = JS_ValueToFunction(cx, argv[i]);
|
||||
if (fun && (fun->flags & JSFUN_FLAGS_MASK)) {
|
||||
if (fun && (fun->flags & ~7U)) {
|
||||
uint16 flags = fun->flags;
|
||||
fputs("flags:", stdout);
|
||||
|
||||
@ -1721,9 +1731,14 @@ Disassemble(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
SHOW_FLAG(THISP_NUMBER);
|
||||
SHOW_FLAG(THISP_BOOLEAN);
|
||||
SHOW_FLAG(EXPR_CLOSURE);
|
||||
SHOW_FLAG(INTERPRETED);
|
||||
SHOW_FLAG(TRACEABLE);
|
||||
|
||||
#undef SHOW_FLAG
|
||||
|
||||
if (FUN_NULL_CLOSURE(fun))
|
||||
fputs(" NULL_CLOSURE", stdout);
|
||||
else if (FUN_FLAT_CLOSURE(fun))
|
||||
fputs(" FLAT_CLOSURE", stdout);
|
||||
putchar('\n');
|
||||
}
|
||||
}
|
||||
@ -2251,13 +2266,16 @@ Intern(JSContext *cx, uintN argc, jsval *vp)
|
||||
static JSBool
|
||||
Clone(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
{
|
||||
JSFunction *fun;
|
||||
JSObject *funobj, *parent, *clone;
|
||||
|
||||
fun = JS_ValueToFunction(cx, argv[0]);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
funobj = JS_GetFunctionObject(fun);
|
||||
if (VALUE_IS_FUNCTION(cx, argv[0])) {
|
||||
funobj = JSVAL_TO_OBJECT(argv[0]);
|
||||
} else {
|
||||
JSFunction *fun = JS_ValueToFunction(cx, argv[0]);
|
||||
if (!fun)
|
||||
return JS_FALSE;
|
||||
funobj = JS_GetFunctionObject(fun);
|
||||
}
|
||||
if (argc > 1) {
|
||||
if (!JS_ValueToObject(cx, argv[1], &parent))
|
||||
return JS_FALSE;
|
||||
|
@ -4490,36 +4490,36 @@ test(testString);
|
||||
|
||||
function testToStringBeforeValueOf()
|
||||
{
|
||||
var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } };
|
||||
var a = [];
|
||||
for (var i = 0; i < 10; i++)
|
||||
a.push(String(o));
|
||||
return a.join(",");
|
||||
var o = {toString: function() { return "s"; }, valueOf: function() { return "v"; } };
|
||||
var a = [];
|
||||
for (var i = 0; i < 10; i++)
|
||||
a.push(String(o));
|
||||
return a.join(",");
|
||||
}
|
||||
testToStringBeforeValueOf.expected = "s,s,s,s,s,s,s,s,s,s";
|
||||
testToStringBeforeValueOf.jitstats = {
|
||||
recorderStarted: 1,
|
||||
sideExitIntoInterpreter: 1
|
||||
recorderStarted: 1,
|
||||
sideExitIntoInterpreter: 1
|
||||
};
|
||||
test(testToStringBeforeValueOf);
|
||||
|
||||
function testNullToString()
|
||||
{
|
||||
var a = [];
|
||||
for (var i = 0; i < 10; i++)
|
||||
a.push(String(null));
|
||||
for (i = 0; i < 10; i++) {
|
||||
var t = typeof a[i];
|
||||
if (t != "string")
|
||||
a.push(t);
|
||||
}
|
||||
return a.join(",");
|
||||
var a = [];
|
||||
for (var i = 0; i < 10; i++)
|
||||
a.push(String(null));
|
||||
for (i = 0; i < 10; i++) {
|
||||
var t = typeof a[i];
|
||||
if (t != "string")
|
||||
a.push(t);
|
||||
}
|
||||
return a.join(",");
|
||||
}
|
||||
testNullToString.expected = "null,null,null,null,null,null,null,null,null,null";
|
||||
testNullToString.jitstats = {
|
||||
recorderStarted: 2,
|
||||
sideExitIntoInterpreter: 2,
|
||||
recorderAborted: 0
|
||||
recorderStarted: 2,
|
||||
sideExitIntoInterpreter: 2,
|
||||
recorderAborted: 0
|
||||
};
|
||||
test(testNullToString);
|
||||
|
||||
@ -4532,12 +4532,162 @@ function testAddNull()
|
||||
}
|
||||
testAddNull.expected = "null,";
|
||||
testAddNull.jitstats = {
|
||||
recorderStarted: 1,
|
||||
sideExitIntoInterpreter: 1,
|
||||
recorderAborted: 0
|
||||
recorderStarted: 1,
|
||||
sideExitIntoInterpreter: 1,
|
||||
recorderAborted: 0
|
||||
};
|
||||
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()
|
||||
{
|
||||
var x = "unset";
|
||||
|
@ -56,7 +56,7 @@ function test()
|
||||
var f;
|
||||
|
||||
f = function() { (function x() { }); return x; }
|
||||
expect = 'function() { (function x() { }); return x; }';
|
||||
expect = 'function() { return x; }';
|
||||
actual = f + '';
|
||||
compareSource(expect, actual, summary);
|
||||
|
||||
|
@ -53,7 +53,7 @@ function test()
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
expect = '2';
|
||||
expect = '1';
|
||||
|
||||
// ------- Comment #74 From Jesse Ruderman
|
||||
|
||||
|
@ -53,7 +53,7 @@ function test()
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
expect = '2';
|
||||
expect = '1';
|
||||
|
||||
// ------- Comment #77 From Brendan Eich
|
||||
|
||||
|
@ -57,7 +57,11 @@ function test()
|
||||
|
||||
// Does not require -j:
|
||||
// =====
|
||||
({ set x x () { for(x in function(){}){}} })
|
||||
try {
|
||||
eval("({ set x x () { for(x in function(){}){}} })");
|
||||
} catch (e)
|
||||
{
|
||||
}
|
||||
|
||||
// Assertion failure: JOF_OPTYPE(op) == JOF_ATOM, at ../jsemit.cpp:1710
|
||||
// =====
|
||||
|
@ -35,7 +35,6 @@ js1_5/Regress/regress-312588.js
|
||||
js1_5/Regress/regress-314401.js
|
||||
js1_5/Regress/regress-329530.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-456826.js
|
||||
js1_7/extensions/regress-455982-01.js
|
||||
|
Loading…
Reference in New Issue
Block a user