upvar2, aka the big one (452598, r=mrbkap).

This commit is contained in:
Brendan Eich 2009-04-04 10:05:49 +01:00
parent a2d4d5a779
commit 5f4d9ff474
49 changed files with 6182 additions and 2552 deletions

View File

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

View File

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

View File

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

View File

@ -221,7 +221,7 @@ MSG_DEF(JSMSG_UNTERMINATED_STRING, 138, 0, JSEXN_SYNTAXERR, "unterminated str
MSG_DEF(JSMSG_TOO_MANY_PARENS, 139, 0, JSEXN_INTERNALERR, "too many parentheses in regular expression")
MSG_DEF(JSMSG_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")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -221,7 +221,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj, jsuword kshape,
*
* So here, on first cache fill for this method, we brand
* 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);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -263,9 +263,9 @@ script_compile_sub(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
* jsparse.c to optimize based on identity of run- and compile-time scope.
*/
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;
}

View File

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

View File

@ -3123,7 +3123,7 @@ js_CheckGlobalObjectShape(JSContext* cx, JSTraceMonitor* tm, JSObject* globalObj
for (size_t i = 0; i < MONITOR_N_GLOBAL_STATES; ++i) {
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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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