mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-29 07:42:04 +00:00
Merge tracemonkey to mozilla-central.
This commit is contained in:
commit
7ef41dd5da
@ -51,6 +51,7 @@ CPPSRCS = \
|
||||
testContexts.cpp \
|
||||
testDebugger.cpp \
|
||||
testDefineGetterSetterNonEnumerable.cpp \
|
||||
testExtendedEq.cpp \
|
||||
testIntString.cpp \
|
||||
testLookup.cpp \
|
||||
testPropCache.cpp \
|
||||
|
45
js/src/jsapi-tests/testExtendedEq.cpp
Normal file
45
js/src/jsapi-tests/testExtendedEq.cpp
Normal file
@ -0,0 +1,45 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||
* vim: set ts=4 sw=4 et tw=99:
|
||||
*
|
||||
* This tests user-specified (via JSExtendedClass) equality operations on
|
||||
* trace.
|
||||
*/
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
static JSBool
|
||||
my_Equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
|
||||
{
|
||||
*bp = JS_TRUE;
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
JSExtendedClass TestExtendedEq_JSClass = {
|
||||
{ "TestExtendedEq",
|
||||
JSCLASS_IS_EXTENDED,
|
||||
JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
|
||||
JS_EnumerateStub, JS_ResolveStub, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL,
|
||||
NULL, NULL, NULL, NULL
|
||||
},
|
||||
// JSExtendedClass initialization
|
||||
my_Equality,
|
||||
NULL, NULL, NULL, NULL, JSCLASS_NO_RESERVED_MEMBERS
|
||||
};
|
||||
|
||||
BEGIN_TEST(testExtendedEq_bug530489)
|
||||
{
|
||||
JSClass *clasp = (JSClass *) &TestExtendedEq_JSClass;
|
||||
|
||||
JSObject *global = JS_GetGlobalObject(cx);
|
||||
JS_InitClass(cx, global, global, clasp, NULL, 0, NULL, NULL, NULL, NULL);
|
||||
|
||||
JS_DefineObject(cx, global, "obj1", clasp, NULL, 0);
|
||||
JS_DefineObject(cx, global, "obj2", clasp, NULL, 0);
|
||||
|
||||
jsval v;
|
||||
EVAL("(function() { var r; for (var i = 0; i < 10; ++i) r = obj1 == obj2; return r; })()", &v);
|
||||
CHECK_SAME(v, JSVAL_TRUE);
|
||||
return true;
|
||||
}
|
||||
END_TEST(testExtendedEq_bug530489)
|
@ -1618,15 +1618,11 @@ JS_ResolveStandardClass(JSContext *cx, JSObject *obj, jsval id,
|
||||
static JSBool
|
||||
AlreadyHasOwnProperty(JSContext *cx, JSObject *obj, JSAtom *atom)
|
||||
{
|
||||
JSScopeProperty *sprop;
|
||||
JSScope *scope;
|
||||
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
sprop = scope->lookup(ATOM_TO_JSID(atom));
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
bool found = scope->hasProperty(ATOM_TO_JSID(atom));
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return sprop != NULL;
|
||||
return found;
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
@ -3402,7 +3398,7 @@ AlreadyHasOwnPropertyHelper(JSContext *cx, JSObject *obj, jsid id,
|
||||
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
scope = OBJ_SCOPE(obj);
|
||||
*foundp = (scope->lookup(id) != NULL);
|
||||
*foundp = scope->hasProperty(id);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return JS_TRUE;
|
||||
}
|
||||
@ -4086,7 +4082,7 @@ JS_NewPropertyIterator(JSContext *cx, JSObject *obj)
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
/* Native case: start with the last property in obj's own scope. */
|
||||
scope = OBJ_SCOPE(obj);
|
||||
pdata = scope->lastProp;
|
||||
pdata = scope->lastProperty();
|
||||
index = -1;
|
||||
} else {
|
||||
/*
|
||||
@ -4129,15 +4125,12 @@ JS_NextProperty(JSContext *cx, JSObject *iterobj, jsid *idp)
|
||||
|
||||
/*
|
||||
* If the next property mapped by scope in the property tree ancestor
|
||||
* line is not enumerable, or it's an alias, or one or more properties
|
||||
* were deleted from the "middle" of the scope-mapped ancestor line
|
||||
* and the next property was among those deleted, skip it and keep on
|
||||
* trying to find an enumerable property that is still in scope.
|
||||
* line is not enumerable, or it's an alias, skip it and keep on trying
|
||||
* to find an enumerable property that is still in scope.
|
||||
*/
|
||||
while (sprop &&
|
||||
(!(sprop->attrs & JSPROP_ENUMERATE) ||
|
||||
(sprop->flags & SPROP_IS_ALIAS) ||
|
||||
(scope->hadMiddleDelete() && !scope->has(sprop)))) {
|
||||
(sprop->flags & SPROP_IS_ALIAS))) {
|
||||
sprop = sprop->parent;
|
||||
}
|
||||
|
||||
|
@ -1327,8 +1327,8 @@ js_MakeArraySlow(JSContext *cx, JSObject *obj)
|
||||
continue;
|
||||
}
|
||||
|
||||
sprop = scope->add(cx, id, NULL, NULL, i + JS_INITIAL_NSLOTS,
|
||||
JSPROP_ENUMERATE, 0, 0);
|
||||
sprop = scope->addDataProperty(cx, id, JS_INITIAL_NSLOTS + i,
|
||||
JSPROP_ENUMERATE);
|
||||
if (!sprop)
|
||||
goto out_bad;
|
||||
}
|
||||
|
@ -748,7 +748,7 @@ js_AtomizeString(JSContext *cx, JSString *str, uintN flags)
|
||||
if (!key)
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
} else {
|
||||
JS_ASSERT(str->isDependent());
|
||||
if (!js_UndependString(cx, str))
|
||||
return NULL;
|
||||
|
@ -209,7 +209,7 @@ js_StringToInt32(JSContext* cx, JSString* str)
|
||||
const jschar* end;
|
||||
const jschar* ep;
|
||||
jsdouble d;
|
||||
|
||||
|
||||
if (str->length() == 1) {
|
||||
jschar c = str->chars()[0];
|
||||
if ('0' <= c && c <= '9')
|
||||
@ -231,21 +231,22 @@ JS_DEFINE_CALLINFO_2(extern, INT32, js_StringToInt32, CONTEXT, STRING, 1, 1)
|
||||
JSBool FASTCALL
|
||||
js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
||||
{
|
||||
JS_ASSERT(OBJ_IS_NATIVE(obj));
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
|
||||
uint32 slot = sprop->slot;
|
||||
JSScope* scope = OBJ_SCOPE(obj);
|
||||
uint32 slot;
|
||||
JS_ASSERT(slot == scope->freeslot);
|
||||
JS_ASSERT(sprop->parent == scope->lastProperty());
|
||||
|
||||
if (scope->owned()) {
|
||||
JS_ASSERT(!scope->has(sprop));
|
||||
JS_ASSERT(!scope->hasProperty(sprop));
|
||||
} else {
|
||||
scope = js_GetMutableScope(cx, obj);
|
||||
if (!scope)
|
||||
goto exit_trace;
|
||||
}
|
||||
|
||||
slot = sprop->slot;
|
||||
if (!scope->table && sprop->parent == scope->lastProp && slot == scope->freeslot) {
|
||||
if (!scope->table) {
|
||||
if (slot < STOBJ_NSLOTS(obj) && !OBJ_GET_CLASS(cx, obj)->reserveSlots) {
|
||||
JS_ASSERT(JSVAL_IS_VOID(STOBJ_GET_SLOT(obj, scope->freeslot)));
|
||||
++scope->freeslot;
|
||||
@ -261,10 +262,9 @@ js_AddProperty(JSContext* cx, JSObject* obj, JSScopeProperty* sprop)
|
||||
|
||||
scope->extend(cx, sprop);
|
||||
} else {
|
||||
JSScopeProperty *sprop2 = scope->add(cx, sprop->id,
|
||||
sprop->getter, sprop->setter,
|
||||
SPROP_INVALID_SLOT, sprop->attrs,
|
||||
sprop->flags, sprop->shortid);
|
||||
JSScopeProperty *sprop2 =
|
||||
scope->addProperty(cx, sprop->id, sprop->getter, sprop->setter, SPROP_INVALID_SLOT,
|
||||
sprop->attrs, sprop->flags, sprop->shortid);
|
||||
if (sprop2 != sprop)
|
||||
goto exit_trace;
|
||||
}
|
||||
@ -400,11 +400,13 @@ js_PopInterpFrame(JSContext* cx, InterpState* state)
|
||||
return JS_FALSE;
|
||||
if (cx->fp->imacpc)
|
||||
return JS_FALSE;
|
||||
|
||||
cx->fp->putActivationObjects(cx);
|
||||
|
||||
/* Update display table. */
|
||||
if (cx->fp->script->staticLevel < JS_DISPLAY_SIZE)
|
||||
cx->display[cx->fp->script->staticLevel] = cx->fp->displaySave;
|
||||
|
||||
|
||||
/* Pop the frame and its memory. */
|
||||
cx->fp = cx->fp->down;
|
||||
JS_ASSERT(cx->fp->regs == &ifp->callerRegs);
|
||||
|
@ -820,7 +820,7 @@ struct JSRuntime {
|
||||
JSBasicStats loopStats;
|
||||
#endif
|
||||
|
||||
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
|
||||
#ifdef DEBUG
|
||||
/* Function invocation metering. */
|
||||
jsrefcount inlineCalls;
|
||||
jsrefcount nativeCalls;
|
||||
@ -842,7 +842,6 @@ struct JSRuntime {
|
||||
jsrefcount duplicatePropTreeNodes;
|
||||
jsrefcount totalPropTreeNodes;
|
||||
jsrefcount propTreeKidsChunks;
|
||||
jsrefcount middleDeleteFixups;
|
||||
|
||||
/* String instrumentation. */
|
||||
jsrefcount liveStrings;
|
||||
@ -860,7 +859,7 @@ struct JSRuntime {
|
||||
jsrefcount totalScripts;
|
||||
jsrefcount liveEmptyScripts;
|
||||
jsrefcount totalEmptyScripts;
|
||||
#endif /* DEBUG || JS_DUMP_PROPTREE_STATS */
|
||||
#endif /* DEBUG */
|
||||
|
||||
#ifdef JS_SCOPE_DEPTH_METER
|
||||
/*
|
||||
|
@ -64,6 +64,7 @@
|
||||
#include "jsstr.h"
|
||||
|
||||
#include "jsatominlines.h"
|
||||
#include "jsscopeinlines.h"
|
||||
|
||||
#include "jsautooplen.h"
|
||||
|
||||
@ -422,13 +423,16 @@ typedef struct JSWatchPoint {
|
||||
#define JSWP_LIVE 0x1 /* live because set and not cleared */
|
||||
#define JSWP_HELD 0x2 /* held while running handler/setter */
|
||||
|
||||
static bool
|
||||
IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop);
|
||||
|
||||
/*
|
||||
* NB: DropWatchPointAndUnlock releases cx->runtime->debuggerLock in all cases.
|
||||
*/
|
||||
static JSBool
|
||||
DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
|
||||
{
|
||||
JSBool ok, found;
|
||||
JSBool ok;
|
||||
JSScopeProperty *sprop;
|
||||
JSScope *scope;
|
||||
JSPropertyOp setter;
|
||||
@ -459,20 +463,22 @@ DropWatchPointAndUnlock(JSContext *cx, JSWatchPoint *wp, uintN flag)
|
||||
if (!setter) {
|
||||
JS_LOCK_OBJ(cx, wp->object);
|
||||
scope = OBJ_SCOPE(wp->object);
|
||||
found = (scope->lookup(sprop->id) != NULL);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
|
||||
/*
|
||||
* If the property wasn't found on wp->object or didn't exist, then
|
||||
* someone else has dealt with this sprop, and we don't need to change
|
||||
* the property attributes.
|
||||
* If the property wasn't found on wp->object, or it isn't still being
|
||||
* watched, then someone else must have deleted or unwatched it, and we
|
||||
* don't need to change the property attributes.
|
||||
*/
|
||||
if (found) {
|
||||
sprop = scope->change(cx, sprop, 0, sprop->attrs,
|
||||
sprop->getter, wp->setter);
|
||||
JSScopeProperty *wprop = scope->lookup(sprop->id);
|
||||
if (wprop &&
|
||||
((wprop->attrs ^ sprop->attrs) & JSPROP_SETTER) == 0 &&
|
||||
IsWatchedProperty(cx, wprop)) {
|
||||
sprop = scope->changeProperty(cx, wprop, 0, wprop->attrs,
|
||||
wprop->getter, wp->setter);
|
||||
if (!sprop)
|
||||
ok = JS_FALSE;
|
||||
}
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
}
|
||||
|
||||
cx->free(wp);
|
||||
@ -762,6 +768,18 @@ js_watch_set_wrapper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
|
||||
return js_watch_set(cx, obj, userid, rval);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsWatchedProperty(JSContext *cx, JSScopeProperty *sprop)
|
||||
{
|
||||
if (sprop->attrs & JSPROP_SETTER) {
|
||||
JSObject *funobj = js_CastAsObject(sprop->setter);
|
||||
JSFunction *fun = GET_FUNCTION_PRIVATE(cx, funobj);
|
||||
|
||||
return FUN_NATIVE(fun) == js_watch_set_wrapper;
|
||||
}
|
||||
return sprop->setter == js_watch_set;
|
||||
}
|
||||
|
||||
JSPropertyOp
|
||||
js_WrapWatchedSetter(JSContext *cx, jsid id, uintN attrs, JSPropertyOp setter)
|
||||
{
|
||||
@ -1218,7 +1236,7 @@ JS_GetFrameThis(JSContext *cx, JSStackFrame *fp)
|
||||
afp = NULL;
|
||||
}
|
||||
|
||||
if (JSVAL_IS_NULL(fp->thisv) && fp->argv)
|
||||
if (fp->argv)
|
||||
fp->thisv = OBJECT_TO_JSVAL(js_ComputeThis(cx, JS_TRUE, fp->argv));
|
||||
|
||||
if (afp) {
|
||||
@ -1432,16 +1450,7 @@ JS_PropertyIterator(JSObject *obj, JSScopeProperty **iteratorp)
|
||||
scope = OBJ_SCOPE(obj);
|
||||
|
||||
/* XXXbe minor(?) incompatibility: iterate in reverse definition order */
|
||||
if (!sprop) {
|
||||
sprop = SCOPE_LAST_PROP(scope);
|
||||
} else {
|
||||
while ((sprop = sprop->parent) != NULL) {
|
||||
if (!scope->hadMiddleDelete())
|
||||
break;
|
||||
if (scope->has(sprop))
|
||||
break;
|
||||
}
|
||||
}
|
||||
sprop = sprop ? sprop->parent : scope->lastProperty();
|
||||
*iteratorp = sprop;
|
||||
return sprop;
|
||||
}
|
||||
@ -1490,7 +1499,7 @@ JS_GetPropertyDesc(JSContext *cx, JSObject *obj, JSScopeProperty *sprop,
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
if (SPROP_HAS_VALID_SLOT(sprop, scope)) {
|
||||
JSScopeProperty *aprop;
|
||||
for (aprop = SCOPE_LAST_PROP(scope); aprop; aprop = aprop->parent) {
|
||||
for (aprop = scope->lastProperty(); aprop; aprop = aprop->parent) {
|
||||
if (aprop != sprop && aprop->slot == sprop->slot) {
|
||||
pd->alias = ID_TO_VALUE(aprop->id);
|
||||
break;
|
||||
@ -1531,9 +1540,7 @@ JS_GetPropertyDescArray(JSContext *cx, JSObject *obj, JSPropertyDescArray *pda)
|
||||
if (!pd)
|
||||
return JS_FALSE;
|
||||
i = 0;
|
||||
for (sprop = SCOPE_LAST_PROP(scope); sprop; sprop = sprop->parent) {
|
||||
if (scope->hadMiddleDelete() && !scope->has(sprop))
|
||||
continue;
|
||||
for (sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
if (!js_AddRoot(cx, &pd[i].id, NULL))
|
||||
goto bad;
|
||||
if (!js_AddRoot(cx, &pd[i].value, NULL))
|
||||
|
@ -257,7 +257,6 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
#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 */
|
||||
#define TCF_FUN_PARAM_EVAL 0x10000 /* function has parameter named 'eval' */
|
||||
|
||||
/*
|
||||
* Set when parsing a declaration-like destructuring pattern. This
|
||||
@ -286,7 +285,10 @@ struct JSTreeContext { /* tree context for semantic checks */
|
||||
* the use of 'with'. See also TSF_STRICT_MODE_CODE,
|
||||
* JSScript::strictModeCode, and JSREPORT_STRICT_ERROR.
|
||||
*/
|
||||
#define TCF_STRICT_MODE_CODE 0x40000
|
||||
#define TCF_STRICT_MODE_CODE 0x40000
|
||||
|
||||
/* Function has parameter named 'eval'. */
|
||||
#define TCF_FUN_PARAM_EVAL 0x80000
|
||||
|
||||
/*
|
||||
* Flags to propagate out of the blocks.
|
||||
|
@ -207,6 +207,7 @@ struct JSFunction : public JSObject {
|
||||
extern JSClass js_ArgumentsClass;
|
||||
extern JS_FRIEND_DATA(JSClass) js_CallClass;
|
||||
extern JSClass js_DeclEnvClass;
|
||||
extern const uint32 CALL_CLASS_FIXED_RESERVED_SLOTS;
|
||||
|
||||
/* JS_FRIEND_DATA so that VALUE_IS_FUNCTION is callable from the shell. */
|
||||
extern JS_FRIEND_DATA(JSClass) js_FunctionClass;
|
||||
|
@ -122,7 +122,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
* from pobj's scope (via unwatch or delete, e.g.).
|
||||
*/
|
||||
scope = OBJ_SCOPE(pobj);
|
||||
if (!scope->has(sprop)) {
|
||||
if (!scope->hasProperty(sprop)) {
|
||||
PCMETER(cache->oddfills++);
|
||||
return JS_NO_PROP_CACHE_FILL;
|
||||
}
|
||||
@ -132,8 +132,8 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
* and setter hooks can change the prototype chain using JS_SetPrototype
|
||||
* after js_LookupPropertyWithFlags has returned the nominal protoIndex,
|
||||
* we have to validate protoIndex if it is non-zero. If it is zero, then
|
||||
* we know thanks to the scope->has test above, combined with the fact that
|
||||
* obj == pobj, that protoIndex is invariant.
|
||||
* we know thanks to the scope->hasProperty test above, combined with the
|
||||
* fact that obj == pobj, that protoIndex is invariant.
|
||||
*
|
||||
* The scopeIndex can't be wrong. We require JS_SetParent calls to happen
|
||||
* before any running script might consult a parent-linked scope chain. If
|
||||
@ -251,7 +251,7 @@ js_FillPropertyCache(JSContext *cx, JSObject *obj,
|
||||
/* Best we can do is to cache sprop (still a nice speedup). */
|
||||
vword = SPROP_TO_PCVAL(sprop);
|
||||
if (adding &&
|
||||
sprop == scope->lastProp &&
|
||||
sprop == scope->lastProperty() &&
|
||||
scope->shape == sprop->shape) {
|
||||
/*
|
||||
* Our caller added a new property. We also know that a setter
|
||||
|
@ -766,7 +766,7 @@ js_NewGenerator(JSContext *cx, JSStackFrame *fp)
|
||||
/* Copy rval, argv and vars. */
|
||||
gen->frame.rval = fp->rval;
|
||||
memcpy(slots, fp->argv - 2, (2 + nargs) * sizeof(jsval));
|
||||
gen->frame.argc = nargs;
|
||||
gen->frame.argc = fp->argc;
|
||||
gen->frame.argv = slots + 2;
|
||||
slots += 2 + nargs;
|
||||
memcpy(slots, fp->slots, fp->script->nfixed * sizeof(jsval));
|
||||
|
@ -1404,7 +1404,9 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
* with object to maintain invariants in the engine (see bug 520164).
|
||||
*/
|
||||
if (scopeobj->getParent()) {
|
||||
withObject = js_NewWithObject(cx, scopeobj, scopeobj->getParent(), 0);
|
||||
withObject = js_NewWithObject(cx, scopeobj,
|
||||
JS_GetGlobalForObject(cx, scopeobj),
|
||||
0);
|
||||
if (!withObject) {
|
||||
ok = JS_FALSE;
|
||||
goto out;
|
||||
@ -1442,13 +1444,14 @@ obj_eval(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||
* calls to eval from global code are not cached.
|
||||
*/
|
||||
bucket = EvalCacheHash(cx, str);
|
||||
if (!indirectCall && argc == 1 && caller->fun) {
|
||||
if (!indirectCall && caller->fun) {
|
||||
uintN count = 0;
|
||||
JSScript **scriptp = bucket;
|
||||
|
||||
EVAL_CACHE_METER(probe);
|
||||
while ((script = *scriptp) != NULL) {
|
||||
if (script->savedCallerFun &&
|
||||
script->staticLevel == staticLevel &&
|
||||
script->version == cx->version &&
|
||||
(script->principals == principals ||
|
||||
(principals->subsume(principals, script->principals) &&
|
||||
@ -2958,7 +2961,7 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
|
||||
/* Find a property to XDR. */
|
||||
do {
|
||||
/* If sprop is NULL, this is the first property. */
|
||||
sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProp;
|
||||
sprop = sprop ? sprop->parent : OBJ_SCOPE(obj)->lastProperty();
|
||||
} while (!(sprop->flags & SPROP_HAS_SHORTID));
|
||||
|
||||
JS_ASSERT(sprop->getter == block_getProperty);
|
||||
@ -3726,7 +3729,7 @@ js_AddNativeProperty(JSContext *cx, JSObject *obj, jsid id,
|
||||
} else {
|
||||
/* Convert string indices to integers if appropriate. */
|
||||
id = js_CheckForStringIndex(id);
|
||||
sprop = scope->add(cx, id, getter, setter, slot, attrs, flags, shortid);
|
||||
sprop = scope->putProperty(cx, id, getter, setter, slot, attrs, flags, shortid);
|
||||
}
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
return sprop;
|
||||
@ -3744,7 +3747,7 @@ js_ChangeNativePropertyAttrs(JSContext *cx, JSObject *obj,
|
||||
if (!scope) {
|
||||
sprop = NULL;
|
||||
} else {
|
||||
sprop = scope->change(cx, sprop, attrs, mask, getter, setter);
|
||||
sprop = scope->changeProperty(cx, sprop, attrs, mask, getter, setter);
|
||||
}
|
||||
JS_UNLOCK_OBJ(cx, obj);
|
||||
return sprop;
|
||||
@ -3823,14 +3826,14 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
if (sprop &&
|
||||
pobj == obj &&
|
||||
(sprop->attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
||||
sprop = OBJ_SCOPE(obj)->change(cx, sprop, attrs,
|
||||
JSPROP_GETTER | JSPROP_SETTER,
|
||||
(attrs & JSPROP_GETTER)
|
||||
? getter
|
||||
: sprop->getter,
|
||||
(attrs & JSPROP_SETTER)
|
||||
? setter
|
||||
: sprop->setter);
|
||||
sprop = OBJ_SCOPE(obj)->changeProperty(cx, sprop, attrs,
|
||||
JSPROP_GETTER | JSPROP_SETTER,
|
||||
(attrs & JSPROP_GETTER)
|
||||
? getter
|
||||
: sprop->getter,
|
||||
(attrs & JSPROP_SETTER)
|
||||
? setter
|
||||
: sprop->setter);
|
||||
|
||||
/* NB: obj == pobj, so we can share unlock code at the bottom. */
|
||||
if (!sprop)
|
||||
@ -3896,9 +3899,9 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
}
|
||||
}
|
||||
|
||||
added = !scope->lookup(id);
|
||||
sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs,
|
||||
flags, shortid);
|
||||
added = !scope->hasProperty(id);
|
||||
sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
|
||||
attrs, flags, shortid);
|
||||
if (!sprop)
|
||||
goto error;
|
||||
}
|
||||
@ -3909,7 +3912,7 @@ js_DefineNativeProperty(JSContext *cx, JSObject *obj, jsid id, jsval value,
|
||||
|
||||
/* XXXbe called with lock held */
|
||||
if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, &value)) {
|
||||
scope->remove(cx, id);
|
||||
scope->removeProperty(cx, id);
|
||||
goto error;
|
||||
}
|
||||
|
||||
@ -4330,7 +4333,7 @@ js_NativeGet(JSContext *cx, JSObject *obj, JSObject *pobj,
|
||||
JS_LOCK_SCOPE(cx, scope);
|
||||
if (SLOT_IN_SCOPE(slot, scope) &&
|
||||
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
||||
scope->has(sprop))) {
|
||||
scope->hasProperty(sprop))) {
|
||||
jsval v = *vp;
|
||||
if (!scope->methodWriteBarrier(cx, sprop, v)) {
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
@ -4392,7 +4395,7 @@ js_NativeSet(JSContext *cx, JSObject *obj, JSScopeProperty *sprop, bool added,
|
||||
JS_LOCK_SCOPE(cx, scope);
|
||||
if (SLOT_IN_SCOPE(slot, scope) &&
|
||||
(JS_LIKELY(cx->runtime->propertyRemovals == sample) ||
|
||||
scope->has(sprop))) {
|
||||
scope->hasProperty(sprop))) {
|
||||
jsval v = *vp;
|
||||
if (!added && !scope->methodWriteBarrier(cx, sprop, v)) {
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
@ -4760,8 +4763,8 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
}
|
||||
}
|
||||
|
||||
sprop = scope->add(cx, id, getter, setter, SPROP_INVALID_SLOT, attrs,
|
||||
flags, shortid);
|
||||
sprop = scope->putProperty(cx, id, getter, setter, SPROP_INVALID_SLOT,
|
||||
attrs, flags, shortid);
|
||||
if (!sprop) {
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return JS_FALSE;
|
||||
@ -4777,7 +4780,7 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
||||
|
||||
/* XXXbe called with obj locked */
|
||||
if (!AddPropertyHelper(cx, clasp, obj, scope, sprop, vp)) {
|
||||
scope->remove(cx, id);
|
||||
scope->removeProperty(cx, id);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
return JS_FALSE;
|
||||
}
|
||||
@ -4924,7 +4927,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
|
||||
if (SPROP_HAS_VALID_SLOT(sprop, scope))
|
||||
GC_POKE(cx, LOCKED_OBJ_GET_SLOT(obj, sprop->slot));
|
||||
|
||||
ok = scope->remove(cx, id);
|
||||
ok = scope->removeProperty(cx, id);
|
||||
obj->dropProperty(cx, prop);
|
||||
return ok;
|
||||
}
|
||||
@ -5159,12 +5162,9 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
||||
/* Count all enumerable properties in object's scope. */
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
length = 0;
|
||||
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope);
|
||||
sprop;
|
||||
sprop = sprop->parent) {
|
||||
for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
if ((sprop->attrs & JSPROP_ENUMERATE) &&
|
||||
!(sprop->flags & SPROP_IS_ALIAS) &&
|
||||
(!scope->hadMiddleDelete() || scope->has(sprop))) {
|
||||
!(sprop->flags & SPROP_IS_ALIAS)) {
|
||||
length++;
|
||||
}
|
||||
}
|
||||
@ -5195,12 +5195,9 @@ js_Enumerate(JSContext *cx, JSObject *obj, JSIterateOp enum_op,
|
||||
ne->shape = shape;
|
||||
|
||||
jsid *ids = ne->ids;
|
||||
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope);
|
||||
sprop;
|
||||
sprop = sprop->parent) {
|
||||
for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
if ((sprop->attrs & JSPROP_ENUMERATE) &&
|
||||
!(sprop->flags & SPROP_IS_ALIAS) &&
|
||||
(!scope->hadMiddleDelete() || scope->has(sprop))) {
|
||||
!(sprop->flags & SPROP_IS_ALIAS)) {
|
||||
JS_ASSERT(ids < ne->ids + length);
|
||||
*ids++ = sprop->id;
|
||||
}
|
||||
@ -5869,7 +5866,7 @@ js_PrintObjectSlotName(JSTracer *trc, char *buf, size_t bufsize)
|
||||
JSScopeProperty *sprop;
|
||||
if (OBJ_IS_NATIVE(obj)) {
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
sprop = SCOPE_LAST_PROP(scope);
|
||||
sprop = scope->lastProperty();
|
||||
while (sprop && sprop->slot != slot)
|
||||
sprop = sprop->parent;
|
||||
} else {
|
||||
@ -6300,10 +6297,9 @@ js_DumpObject(JSObject *obj)
|
||||
fprintf(stderr, "sealed\n");
|
||||
|
||||
fprintf(stderr, "properties:\n");
|
||||
for (JSScopeProperty *sprop = SCOPE_LAST_PROP(scope); sprop;
|
||||
for (JSScopeProperty *sprop = scope->lastProperty(); sprop;
|
||||
sprop = sprop->parent) {
|
||||
if (!scope->hadMiddleDelete() || scope->has(sprop))
|
||||
dumpScopeProp(sprop);
|
||||
dumpScopeProp(sprop);
|
||||
}
|
||||
} else {
|
||||
if (!OBJ_IS_NATIVE(obj))
|
||||
|
@ -277,7 +277,7 @@ ToDisassemblySource(JSContext *cx, jsval v)
|
||||
|
||||
if (clasp == &js_BlockClass) {
|
||||
char *source = JS_sprintf_append(NULL, "depth %d {", OBJ_BLOCK_DEPTH(cx, obj));
|
||||
for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProp;
|
||||
for (JSScopeProperty *sprop = OBJ_SCOPE(obj)->lastProperty();
|
||||
sprop;
|
||||
sprop = sprop->parent) {
|
||||
const char *bytes = js_AtomToPrintableString(cx, JSID_TO_ATOM(sprop->id));
|
||||
@ -1316,7 +1316,7 @@ GetLocal(SprintStack *ss, jsint i)
|
||||
}
|
||||
|
||||
i -= depth;
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop; sprop = sprop->parent) {
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
if (sprop->shortid == i)
|
||||
break;
|
||||
}
|
||||
@ -2634,7 +2634,7 @@ Decompile(SprintStack *ss, jsbytecode *pc, intN nb, JSOp nextop)
|
||||
MUST_FLOW_THROUGH("enterblock_out");
|
||||
#define LOCAL_ASSERT_OUT(expr) LOCAL_ASSERT_CUSTOM(expr, ok = JS_FALSE; \
|
||||
goto enterblock_out)
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProp; sprop;
|
||||
for (sprop = OBJ_SCOPE(obj)->lastProperty(); sprop;
|
||||
sprop = sprop->parent) {
|
||||
if (!(sprop->flags & SPROP_HAS_SHORTID))
|
||||
continue;
|
||||
|
@ -1756,7 +1756,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
/* The cache entry doesn't apply. vshape mismatch. */
|
||||
checkForAdd = false;
|
||||
} else if (scope->owned()) {
|
||||
if (sprop == scope->lastProp || scope->has(sprop)) {
|
||||
if (sprop == scope->lastProperty() || scope->hasProperty(sprop)) {
|
||||
fast_set_propcache_hit:
|
||||
PCMETER(cache->pchits++);
|
||||
PCMETER(cache->setpchits++);
|
||||
@ -1766,8 +1766,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
}
|
||||
checkForAdd =
|
||||
!(sprop->attrs & JSPROP_SHARED) &&
|
||||
sprop->parent == scope->lastProp &&
|
||||
!scope->hadMiddleDelete();
|
||||
sprop->parent == scope->lastProperty();
|
||||
} else {
|
||||
scope = js_GetMutableScope(cx, obj);
|
||||
if (!scope) {
|
||||
@ -1814,7 +1813,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
/*
|
||||
* If this obj's number of reserved slots differed, or
|
||||
* if something created a hash table for scope, we must
|
||||
* pay the price of JSScope::add.
|
||||
* pay the price of JSScope::putProperty.
|
||||
*
|
||||
* If slot does not match the cached sprop's slot,
|
||||
* update the cache entry in the hope that obj and
|
||||
@ -1823,10 +1822,10 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
||||
*/
|
||||
if (slot != sprop->slot || scope->table) {
|
||||
JSScopeProperty *sprop2 =
|
||||
scope->add(cx, sprop->id,
|
||||
sprop->getter, sprop->setter,
|
||||
slot, sprop->attrs,
|
||||
sprop->flags, sprop->shortid);
|
||||
scope->putProperty(cx, sprop->id,
|
||||
sprop->getter, sprop->setter,
|
||||
slot, sprop->attrs,
|
||||
sprop->flags, sprop->shortid);
|
||||
if (!sprop2) {
|
||||
js_FreeSlot(cx, obj, slot);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
@ -3537,10 +3536,10 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
|
||||
/*
|
||||
* Detect a repeated property name and force a miss to share the
|
||||
* strict warning code and cope with complexity managed by
|
||||
* JSScope::add.
|
||||
* strict warning code and consolidate all the complexity managed
|
||||
* by JSScope::addProperty.
|
||||
*/
|
||||
if (sprop->parent != scope->lastProp)
|
||||
if (sprop->parent != scope->lastProperty())
|
||||
goto do_initprop_miss;
|
||||
|
||||
/*
|
||||
@ -3548,8 +3547,8 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
* proto-property, and there cannot have been any deletions of
|
||||
* prior properties.
|
||||
*/
|
||||
JS_ASSERT(!scope->hadMiddleDelete());
|
||||
JS_ASSERT_IF(scope->table, !scope->has(sprop));
|
||||
JS_ASSERT(!scope->inDictionaryMode());
|
||||
JS_ASSERT_IF(scope->table, !scope->hasProperty(sprop));
|
||||
|
||||
slot = sprop->slot;
|
||||
JS_ASSERT(slot == scope->freeslot);
|
||||
@ -3563,14 +3562,11 @@ BEGIN_CASE(JSOP_INITMETHOD)
|
||||
JS_ASSERT(slot == sprop->slot);
|
||||
}
|
||||
|
||||
JS_ASSERT(!scope->lastProp ||
|
||||
scope->shape == scope->lastProp->shape);
|
||||
JS_ASSERT(!scope->lastProperty() ||
|
||||
scope->shape == scope->lastProperty()->shape);
|
||||
if (scope->table) {
|
||||
JSScopeProperty *sprop2 =
|
||||
scope->add(cx, sprop->id,
|
||||
sprop->getter, sprop->setter,
|
||||
slot, sprop->attrs,
|
||||
sprop->flags, sprop->shortid);
|
||||
scope->addDataProperty(cx, sprop->id, slot, sprop->attrs);
|
||||
if (!sprop2) {
|
||||
js_FreeSlot(cx, obj, slot);
|
||||
JS_UNLOCK_SCOPE(cx, scope);
|
||||
|
@ -3262,7 +3262,7 @@ PopStatement(JSTreeContext *tc)
|
||||
JSScope *scope = OBJ_SCOPE(obj);
|
||||
JS_ASSERT(!OBJ_IS_CLONED_BLOCK(obj));
|
||||
|
||||
for (JSScopeProperty *sprop = scope->lastProp; sprop; sprop = sprop->parent) {
|
||||
for (JSScopeProperty *sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
JSAtom *atom = JSID_TO_ATOM(sprop->id);
|
||||
|
||||
/* Beware the empty destructuring dummy. */
|
||||
|
1171
js/src/jsscope.cpp
1171
js/src/jsscope.cpp
File diff suppressed because it is too large
Load Diff
228
js/src/jsscope.h
228
js/src/jsscope.h
@ -114,10 +114,10 @@ JS_BEGIN_EXTERN_C
|
||||
* skipping nodes that lack entries.
|
||||
*
|
||||
* What if we add Y again? X->Y->Z->Y is wrong and we'll enumerate Y twice.
|
||||
* Therefore we must fork in such a case, if not earlier. Because delete is
|
||||
* "bursty", we should not fork eagerly. Delaying a fork till we are at risk
|
||||
* of adding Y after it was deleted already requires a flag in the JSScope, to
|
||||
* wit, SCOPE_MIDDLE_DELETE.
|
||||
* Therefore we must fork in such a case if not earlier, or do something else.
|
||||
* We used to fork on the theory that set after delete is rare, but the Web is
|
||||
* a harsh mistress, and we now convert the scope to a "dictionary" on first
|
||||
* delete, to avoid O(n^2) growth in the property tree.
|
||||
*
|
||||
* What about thread safety? If the property tree operations done by requests
|
||||
* are find-node and insert-node, then the only hazard is duplicate insertion.
|
||||
@ -195,13 +195,15 @@ JS_BEGIN_EXTERN_C
|
||||
* in Mozilla is < 5, with a large standard deviation (~8). Instead of always
|
||||
* allocating scope->table, we leave it null while initializing all the other
|
||||
* scope members as if it were non-null and minimal-length. Until a property
|
||||
* is added that crosses the threshold of 6 or more entries for hashing, or
|
||||
* until a "middle delete" occurs, we use linear search from scope->lastProp
|
||||
* to find a given id, and save on the space overhead of a hash table.
|
||||
* is added that crosses the threshold of 6 or more entries for hashing, we use
|
||||
* linear search from scope->lastProp to find a given id, and save on the space
|
||||
* overhead of a hash table.
|
||||
*/
|
||||
|
||||
struct JSEmptyScope;
|
||||
|
||||
#define SPROP_INVALID_SLOT 0xffffffff
|
||||
|
||||
struct JSScope : public JSObjectMap
|
||||
{
|
||||
#ifdef JS_THREADSAFE
|
||||
@ -218,9 +220,44 @@ struct JSScope : public JSObjectMap
|
||||
uint32 entryCount; /* number of entries in table */
|
||||
uint32 removedCount; /* removed entry sentinels in table */
|
||||
JSScopeProperty **table; /* table of ptrs to shared tree nodes */
|
||||
JSScopeProperty *lastProp; /* pointer to last property added */
|
||||
|
||||
/*
|
||||
* A little information hiding for scope->lastProp, in case it ever becomes
|
||||
* a tagged pointer again.
|
||||
*/
|
||||
inline JSScopeProperty *lastProperty() const;
|
||||
|
||||
private:
|
||||
JSScopeProperty *getChildProperty(JSContext *cx, JSScopeProperty *parent,
|
||||
JSScopeProperty &child);
|
||||
|
||||
JSScopeProperty *newDictionaryProperty(JSContext *cx, const JSScopeProperty &child,
|
||||
JSScopeProperty **childp);
|
||||
|
||||
bool toDictionaryMode(JSContext *cx, JSScopeProperty *&aprop);
|
||||
|
||||
/*
|
||||
* Private pointer to the last added property and methods to manipulate the
|
||||
* list it links among properties in this scope. The {remove,insert} pair
|
||||
* for DictionaryProperties assert that the scope is in dictionary mode and
|
||||
* any reachable properties are flagged as dictionary properties.
|
||||
*
|
||||
* NB: these private methods do *not* update this scope's shape to track
|
||||
* lastProp->shape after they finish updating the linked list in the case
|
||||
* where lastProp is updated. It is up to calling code in jsscope.cpp to
|
||||
* call updateShape(cx) after updating lastProp.
|
||||
*/
|
||||
JSScopeProperty *lastProp;
|
||||
|
||||
/* These four inline methods are defined further below in this .h file. */
|
||||
inline void setLastProperty(JSScopeProperty *sprop);
|
||||
inline void removeLastProperty();
|
||||
inline void removeDictionaryProperty(JSScopeProperty *sprop);
|
||||
inline void insertDictionaryProperty(JSScopeProperty *sprop, JSScopeProperty **childp);
|
||||
|
||||
/* Defined in jsscopeinlines.h to avoid including implementation dependencies here. */
|
||||
inline void updateShape(JSContext *cx);
|
||||
|
||||
void initMinimal(JSContext *cx, uint32 newShape);
|
||||
bool createTable(JSContext *cx, bool report);
|
||||
bool changeTable(JSContext *cx, int change);
|
||||
@ -230,6 +267,12 @@ struct JSScope : public JSObjectMap
|
||||
inline JSScopeProperty **search(jsid id, bool adding);
|
||||
JSEmptyScope *createEmptyScope(JSContext *cx, JSClass *clasp);
|
||||
|
||||
JSScopeProperty *addPropertyHelper(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid,
|
||||
JSScopeProperty **spp);
|
||||
|
||||
public:
|
||||
explicit JSScope(const JSObjectOps *ops, JSObject *obj = NULL)
|
||||
: JSObjectMap(ops, 0), object(obj) {}
|
||||
@ -253,27 +296,48 @@ struct JSScope : public JSObjectMap
|
||||
inline JSEmptyScope *getEmptyScope(JSContext *cx, JSClass *clasp);
|
||||
|
||||
inline bool canProvideEmptyScope(JSObjectOps *ops, JSClass *clasp);
|
||||
|
||||
|
||||
JSScopeProperty *lookup(jsid id);
|
||||
bool has(JSScopeProperty *sprop);
|
||||
|
||||
JSScopeProperty *add(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid);
|
||||
inline bool hasProperty(jsid id) { return lookup(id) != NULL; }
|
||||
inline bool hasProperty(JSScopeProperty *sprop);
|
||||
|
||||
JSScopeProperty *change(JSContext *cx, JSScopeProperty *sprop,
|
||||
uintN attrs, uintN mask,
|
||||
JSPropertyOp getter, JSPropertyOp setter);
|
||||
/* Add a property whose id is not yet in this scope. */
|
||||
JSScopeProperty *addProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid);
|
||||
|
||||
bool remove(JSContext *cx, jsid id);
|
||||
/* Add a data property whose id is not yet in this scope. */
|
||||
JSScopeProperty *addDataProperty(JSContext *cx, jsid id, uint32 slot, uintN attrs) {
|
||||
JS_ASSERT(!(attrs & (JSPROP_GETTER | JSPROP_SETTER)));
|
||||
return addProperty(cx, id, NULL, NULL, slot, attrs, 0, 0);
|
||||
}
|
||||
|
||||
/* Add or overwrite a property for id in this scope. */
|
||||
JSScopeProperty *putProperty(JSContext *cx, jsid id,
|
||||
JSPropertyOp getter, JSPropertyOp setter,
|
||||
uint32 slot, uintN attrs,
|
||||
uintN flags, intN shortid);
|
||||
|
||||
/* Change the given property into a sibling with the same id in this scope. */
|
||||
JSScopeProperty *changeProperty(JSContext *cx, JSScopeProperty *sprop,
|
||||
uintN attrs, uintN mask,
|
||||
JSPropertyOp getter, JSPropertyOp setter);
|
||||
|
||||
/* Remove id from this scope. */
|
||||
bool removeProperty(JSContext *cx, jsid id);
|
||||
|
||||
/* Clear the scope, making it empty. */
|
||||
void clear(JSContext *cx);
|
||||
|
||||
/* Extend this scope to have sprop as its last-added property. */
|
||||
void extend(JSContext *cx, JSScopeProperty *sprop);
|
||||
|
||||
/*
|
||||
* Read barrier to clone a joined function object stored as a method.
|
||||
* Defined inline further below.
|
||||
* Defined in jsscopeinlines.h, but not declared inline per standard style
|
||||
* in order to avoid gcc warnings.
|
||||
*/
|
||||
bool methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp);
|
||||
|
||||
@ -294,15 +358,14 @@ struct JSScope : public JSObjectMap
|
||||
bool methodShapeChange(JSContext *cx, JSScopeProperty *sprop, jsval toval);
|
||||
bool methodShapeChange(JSContext *cx, uint32 slot, jsval toval);
|
||||
void protoShapeChange(JSContext *cx);
|
||||
void replacingShapeChange(JSContext *cx, JSScopeProperty *sprop, JSScopeProperty *newsprop);
|
||||
void sealingShapeChange(JSContext *cx);
|
||||
void shadowingShapeChange(JSContext *cx, JSScopeProperty *sprop);
|
||||
|
||||
/* By definition, hashShift = JS_DHASH_BITS - log2(capacity). */
|
||||
#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
|
||||
#define SCOPE_CAPACITY(scope) JS_BIT(JS_DHASH_BITS-(scope)->hashShift)
|
||||
|
||||
enum {
|
||||
MIDDLE_DELETE = 0x0001,
|
||||
DICTIONARY_MODE = 0x0001,
|
||||
SEALED = 0x0002,
|
||||
BRANDED = 0x0004,
|
||||
INDEXED_PROPERTIES = 0x0008,
|
||||
@ -316,9 +379,9 @@ struct JSScope : public JSObjectMap
|
||||
SHAPE_REGEN = 0x0040
|
||||
};
|
||||
|
||||
bool hadMiddleDelete() { return flags & MIDDLE_DELETE; }
|
||||
void setMiddleDelete() { flags |= MIDDLE_DELETE; }
|
||||
void clearMiddleDelete() { flags &= ~MIDDLE_DELETE; }
|
||||
bool inDictionaryMode() { return flags & DICTIONARY_MODE; }
|
||||
void setDictionaryMode() { flags |= DICTIONARY_MODE; }
|
||||
void clearDictionaryMode() { flags &= ~DICTIONARY_MODE; }
|
||||
|
||||
/*
|
||||
* Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
|
||||
@ -341,6 +404,7 @@ struct JSScope : public JSObjectMap
|
||||
|
||||
bool hasOwnShape() { return flags & OWN_SHAPE; }
|
||||
void setOwnShape() { flags |= OWN_SHAPE; }
|
||||
void clearOwnShape() { flags &= ~OWN_SHAPE; }
|
||||
|
||||
bool hasRegenFlag(uint8 regenFlag) { return (flags & SHAPE_REGEN) == regenFlag; }
|
||||
|
||||
@ -427,18 +491,6 @@ OBJ_SHAPE(JSObject *obj)
|
||||
return obj->map->shape;
|
||||
}
|
||||
|
||||
/*
|
||||
* A little information hiding for scope->lastProp, in case it ever becomes
|
||||
* a tagged pointer again.
|
||||
*/
|
||||
#define SCOPE_LAST_PROP(scope) \
|
||||
(JS_ASSERT_IF((scope)->lastProp, !JSVAL_IS_NULL((scope)->lastProp->id)), \
|
||||
(scope)->lastProp)
|
||||
#define SCOPE_REMOVE_LAST_PROP(scope) \
|
||||
(JS_ASSERT_IF((scope)->lastProp->parent, \
|
||||
!JSVAL_IS_NULL((scope)->lastProp->parent->id)), \
|
||||
(scope)->lastProp = (scope)->lastProp->parent)
|
||||
|
||||
/*
|
||||
* Helpers for reinterpreting JSPropertyOp as JSObject* for scripted getters
|
||||
* and setters.
|
||||
@ -471,8 +523,14 @@ struct JSScopeProperty {
|
||||
uint8 flags; /* flags, see below for defines */
|
||||
int16 shortid; /* tinyid, or local arg/var index */
|
||||
JSScopeProperty *parent; /* parent node, reverse for..in order */
|
||||
JSScopeProperty *kids; /* null, single child, or a tagged ptr
|
||||
union {
|
||||
JSScopeProperty *kids; /* null, single child, or a tagged ptr
|
||||
to many-kids data structure */
|
||||
JSScopeProperty **childp; /* dictionary list starting at lastProp
|
||||
has a double-indirect back pointer,
|
||||
either to sprop->parent if not last,
|
||||
else to scope->lastProp */
|
||||
};
|
||||
uint32 shape; /* property cache shape identifier */
|
||||
|
||||
/* Bits stored in sprop->flags. */
|
||||
@ -481,6 +539,7 @@ struct JSScopeProperty {
|
||||
#define SPROP_HAS_SHORTID 0x04
|
||||
#define SPROP_FLAG_SHAPE_REGEN 0x08
|
||||
#define SPROP_IS_METHOD 0x10
|
||||
#define SPROP_IN_DICTIONARY 0x20
|
||||
|
||||
bool isMethod() const {
|
||||
return flags & SPROP_IS_METHOD;
|
||||
@ -551,11 +610,85 @@ JSScope::lookup(jsid id)
|
||||
}
|
||||
|
||||
inline bool
|
||||
JSScope::has(JSScopeProperty *sprop)
|
||||
JSScope::hasProperty(JSScopeProperty *sprop)
|
||||
{
|
||||
return lookup(sprop->id) == sprop;
|
||||
}
|
||||
|
||||
inline JSScopeProperty *
|
||||
JSScope::lastProperty() const
|
||||
{
|
||||
JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id));
|
||||
return lastProp;
|
||||
}
|
||||
|
||||
/*
|
||||
* Note that sprop must not be null, as emptying a scope requires extra work
|
||||
* done only by methods in jsscope.cpp.
|
||||
*/
|
||||
inline void
|
||||
JSScope::setLastProperty(JSScopeProperty *sprop)
|
||||
{
|
||||
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
|
||||
JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id));
|
||||
|
||||
lastProp = sprop;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::removeLastProperty()
|
||||
{
|
||||
JS_ASSERT(!inDictionaryMode());
|
||||
JS_ASSERT_IF(lastProp->parent, !JSVAL_IS_NULL(lastProp->parent->id));
|
||||
|
||||
lastProp = lastProp->parent;
|
||||
--entryCount;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::removeDictionaryProperty(JSScopeProperty *sprop)
|
||||
{
|
||||
JS_ASSERT(inDictionaryMode());
|
||||
JS_ASSERT(sprop->flags & SPROP_IN_DICTIONARY);
|
||||
JS_ASSERT(sprop->childp);
|
||||
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
|
||||
|
||||
JS_ASSERT(lastProp->flags & SPROP_IN_DICTIONARY);
|
||||
JS_ASSERT(lastProp->childp == &lastProp);
|
||||
JS_ASSERT_IF(lastProp != sprop, !JSVAL_IS_NULL(lastProp->id));
|
||||
JS_ASSERT_IF(lastProp->parent, !JSVAL_IS_NULL(lastProp->parent->id));
|
||||
|
||||
if (sprop->parent)
|
||||
sprop->parent->childp = sprop->childp;
|
||||
*sprop->childp = sprop->parent;
|
||||
--entryCount;
|
||||
sprop->childp = NULL;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::insertDictionaryProperty(JSScopeProperty *sprop, JSScopeProperty **childp)
|
||||
{
|
||||
/*
|
||||
* Don't assert inDictionaryMode() here because we may be called from
|
||||
* toDictionaryMode via newDictionaryProperty.
|
||||
*/
|
||||
JS_ASSERT(sprop->flags & SPROP_IN_DICTIONARY);
|
||||
JS_ASSERT(!sprop->childp);
|
||||
JS_ASSERT(!JSVAL_IS_NULL(sprop->id));
|
||||
|
||||
JS_ASSERT_IF(*childp, (*childp)->flags & SPROP_IN_DICTIONARY);
|
||||
JS_ASSERT_IF(lastProp, lastProp->flags & SPROP_IN_DICTIONARY);
|
||||
JS_ASSERT_IF(lastProp, lastProp->childp == &lastProp);
|
||||
JS_ASSERT_IF(lastProp, !JSVAL_IS_NULL(lastProp->id));
|
||||
|
||||
sprop->parent = *childp;
|
||||
*childp = sprop;
|
||||
if (sprop->parent)
|
||||
sprop->parent->childp = &sprop->parent;
|
||||
sprop->childp = childp;
|
||||
++entryCount;
|
||||
}
|
||||
|
||||
/*
|
||||
* If SPROP_HAS_SHORTID is set in sprop->flags, we use sprop->shortid rather
|
||||
* than id when calling sprop's getter or setter.
|
||||
@ -564,8 +697,6 @@ JSScope::has(JSScopeProperty *sprop)
|
||||
(((sprop)->flags & SPROP_HAS_SHORTID) ? INT_TO_JSVAL((sprop)->shortid) \
|
||||
: ID_TO_VALUE((sprop)->id))
|
||||
|
||||
#define SPROP_INVALID_SLOT 0xffffffff
|
||||
|
||||
#define SLOT_IN_SCOPE(slot,scope) ((slot) < (scope)->freeslot)
|
||||
#define SPROP_HAS_VALID_SLOT(sprop,scope) SLOT_IN_SCOPE((sprop)->slot, scope)
|
||||
|
||||
@ -582,7 +713,7 @@ JSScope::has(JSScopeProperty *sprop)
|
||||
extern uint32
|
||||
js_GenerateShape(JSContext *cx, bool gcLocked);
|
||||
|
||||
#ifdef JS_DUMP_PROPTREE_STATS
|
||||
#ifdef DEBUG
|
||||
struct JSScopeStats {
|
||||
jsrefcount searches;
|
||||
jsrefcount hits;
|
||||
@ -591,10 +722,16 @@ struct JSScopeStats {
|
||||
jsrefcount steps;
|
||||
jsrefcount stepHits;
|
||||
jsrefcount stepMisses;
|
||||
jsrefcount tableAllocFails;
|
||||
jsrefcount toDictFails;
|
||||
jsrefcount wrapWatchFails;
|
||||
jsrefcount adds;
|
||||
jsrefcount redundantAdds;
|
||||
jsrefcount addFailures;
|
||||
jsrefcount changeFailures;
|
||||
jsrefcount addFails;
|
||||
jsrefcount puts;
|
||||
jsrefcount redundantPuts;
|
||||
jsrefcount putFails;
|
||||
jsrefcount changes;
|
||||
jsrefcount changeFails;
|
||||
jsrefcount compresses;
|
||||
jsrefcount grows;
|
||||
jsrefcount removes;
|
||||
@ -618,7 +755,6 @@ JSScope::search(jsid id, bool adding)
|
||||
METER(searches);
|
||||
if (!table) {
|
||||
/* Not enough properties to justify hashing: search from lastProp. */
|
||||
JS_ASSERT(!hadMiddleDelete());
|
||||
for (spp = &lastProp; (sprop = *spp); spp = &sprop->parent) {
|
||||
if (sprop->id == id) {
|
||||
METER(hits);
|
||||
|
@ -41,20 +41,26 @@
|
||||
#define jsscopeinlines_h___
|
||||
|
||||
#include "jscntxt.h"
|
||||
#include "jsdbgapi.h"
|
||||
#include "jsfun.h"
|
||||
#include "jsinterp.h"
|
||||
#include "jsobj.h"
|
||||
#include "jsscope.h"
|
||||
|
||||
inline void
|
||||
JSScope::updateShape(JSContext *cx)
|
||||
{
|
||||
JS_ASSERT(object);
|
||||
js_LeaveTraceIfGlobalObject(cx, object);
|
||||
|
||||
shape = (hasOwnShape() || !lastProp) ? js_GenerateShape(cx, false) : lastProp->shape;
|
||||
}
|
||||
|
||||
inline void
|
||||
JSScope::extend(JSContext *cx, JSScopeProperty *sprop)
|
||||
{
|
||||
js_LeaveTraceIfGlobalObject(cx, object);
|
||||
shape = (!lastProp || shape == lastProp->shape)
|
||||
? sprop->shape
|
||||
: js_GenerateShape(cx, false);
|
||||
++entryCount;
|
||||
lastProp = sprop;
|
||||
setLastProperty(sprop);
|
||||
updateShape(cx);
|
||||
|
||||
jsuint index;
|
||||
if (js_IdIsIndex(sprop->id, &index))
|
||||
@ -72,7 +78,7 @@ inline bool
|
||||
JSScope::methodReadBarrier(JSContext *cx, JSScopeProperty *sprop, jsval *vp)
|
||||
{
|
||||
JS_ASSERT(hasMethodBarrier());
|
||||
JS_ASSERT(has(sprop));
|
||||
JS_ASSERT(hasProperty(sprop));
|
||||
JS_ASSERT(sprop->isMethod());
|
||||
JS_ASSERT(sprop->methodValue() == *vp);
|
||||
JS_ASSERT(object->getClass() == &js_ObjectClass);
|
||||
@ -148,12 +154,10 @@ JSScope::trace(JSTracer *trc)
|
||||
}
|
||||
}
|
||||
if (sprop) {
|
||||
JS_ASSERT(has(sprop));
|
||||
JS_ASSERT(hasProperty(sprop));
|
||||
|
||||
/* Trace scope's property tree ancestor line. */
|
||||
do {
|
||||
if (hadMiddleDelete() && !has(sprop))
|
||||
continue;
|
||||
sprop->trace(trc);
|
||||
} while ((sprop = sprop->parent) != NULL);
|
||||
}
|
||||
|
@ -101,13 +101,8 @@ MinimizeDependentStrings(JSString *str, int level, JSString **basep)
|
||||
base = base->dependentBase();
|
||||
} while (base->isDependent());
|
||||
}
|
||||
if (start == 0) {
|
||||
JS_ASSERT(str->dependentIsPrefix());
|
||||
str->prefixSetBase(base);
|
||||
} else if (start <= JSString::MAX_DEPENDENT_START) {
|
||||
length = str->dependentLength();
|
||||
str->reinitDependent(base, start, length);
|
||||
}
|
||||
length = str->dependentLength();
|
||||
str->reinitDependent(base, start, length);
|
||||
}
|
||||
*basep = base;
|
||||
return start;
|
||||
@ -188,9 +183,9 @@ js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
|
||||
} else {
|
||||
str->flatSetMutable();
|
||||
|
||||
/* Morph left into a dependent prefix if we realloc'd its buffer. */
|
||||
/* Morph left into a dependent string if we realloc'd its buffer. */
|
||||
if (ldep) {
|
||||
ldep->reinitPrefix(str, ln);
|
||||
ldep->reinitDependent(str, 0, ln);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
@ -654,6 +649,21 @@ NormalizeThis(JSContext *cx, jsval *vp)
|
||||
|
||||
if (JSVAL_IS_NULL(vp[1]) && JSVAL_IS_NULL(JS_THIS(cx, vp)))
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* js_GetPrimitiveThis seems to do a bunch of work (like calls to
|
||||
* JS_THIS_OBJECT) which we don't need in the common case (where
|
||||
* vp[1] is a String object) here. Note that vp[1] can still be a
|
||||
* primitive value at this point.
|
||||
*/
|
||||
if (!JSVAL_IS_PRIMITIVE(vp[1])) {
|
||||
JSObject *obj = JSVAL_TO_OBJECT(vp[1]);
|
||||
if (obj->getClass() == &js_StringClass) {
|
||||
vp[1] = obj->fslots[JSSLOT_PRIMITIVE_THIS];
|
||||
return JSVAL_TO_STRING(vp[1]);
|
||||
}
|
||||
}
|
||||
|
||||
str = js_ValueToString(cx, vp[1]);
|
||||
if (!str)
|
||||
return NULL;
|
||||
@ -933,9 +943,7 @@ str_charAt(JSContext *cx, uintN argc, jsval *vp)
|
||||
if ((size_t)i >= str->length())
|
||||
goto out_of_range;
|
||||
} else {
|
||||
str = NormalizeThis(cx, vp);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
NORMALIZE_THIS(cx, vp, str);
|
||||
|
||||
if (argc == 0) {
|
||||
d = 0.0;
|
||||
@ -977,9 +985,7 @@ str_charCodeAt(JSContext *cx, uintN argc, jsval *vp)
|
||||
if ((size_t)i >= str->length())
|
||||
goto out_of_range;
|
||||
} else {
|
||||
str = NormalizeThis(cx, vp);
|
||||
if (!str)
|
||||
return JS_FALSE;
|
||||
NORMALIZE_THIS(cx, vp, str);
|
||||
|
||||
if (argc == 0) {
|
||||
d = 0.0;
|
||||
@ -2636,7 +2642,7 @@ static const jschar UnitStringData[] = {
|
||||
C(0xf8), C(0xf9), C(0xfa), C(0xfb), C(0xfc), C(0xfd), C(0xfe), C(0xff)
|
||||
};
|
||||
|
||||
#define U(c) { 1 | JSString::ATOMIZED, {(jschar *)UnitStringData + (c) * 2} }
|
||||
#define U(c) { 1, 0, JSString::ATOMIZED, {(jschar *)UnitStringData + (c) * 2} }
|
||||
|
||||
#ifdef __SUNPRO_CC
|
||||
#pragma pack(8)
|
||||
@ -2743,9 +2749,9 @@ static const jschar Hundreds[] = {
|
||||
O25(0x30), O25(0x31), O25(0x32), O25(0x33), O25(0x34), O25(0x35)
|
||||
};
|
||||
|
||||
#define L1(c) { 1 | JSString::ATOMIZED, {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */
|
||||
#define L2(c) { 2 | JSString::ATOMIZED, {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */
|
||||
#define L3(c) { 3 | JSString::ATOMIZED, {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */
|
||||
#define L1(c) { 1, 0, JSString::ATOMIZED, {(jschar *)Hundreds + 2 + (c) * 4} } /* length 1: 0..9 */
|
||||
#define L2(c) { 2, 0, JSString::ATOMIZED, {(jschar *)Hundreds + 41 + (c - 10) * 4} } /* length 2: 10..99 */
|
||||
#define L3(c) { 3, 0, JSString::ATOMIZED, {(jschar *)Hundreds + (c - 100) * 4} } /* length 3: 100..255 */
|
||||
|
||||
#ifdef __SUNPRO_CC
|
||||
#pragma pack(8)
|
||||
@ -3156,18 +3162,10 @@ js_NewDependentString(JSContext *cx, JSString *base, size_t start,
|
||||
if (start == 0 && length == base->length())
|
||||
return base;
|
||||
|
||||
if (start > JSString::MAX_DEPENDENT_START ||
|
||||
(start != 0 && length > JSString::MAX_DEPENDENT_LENGTH)) {
|
||||
return js_NewStringCopyN(cx, base->chars() + start, length);
|
||||
}
|
||||
|
||||
ds = js_NewGCString(cx);
|
||||
if (!ds)
|
||||
return NULL;
|
||||
if (start == 0)
|
||||
ds->initPrefix(base, length);
|
||||
else
|
||||
ds->initDependent(base, start, length);
|
||||
ds->initDependent(base, start, length);
|
||||
#ifdef DEBUG
|
||||
{
|
||||
JSRuntime *rt = cx->runtime;
|
||||
@ -5482,7 +5480,7 @@ Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length)
|
||||
return ucs4Char;
|
||||
}
|
||||
|
||||
#if defined DEBUG || defined JS_DUMP_PROPTREE_STATS
|
||||
#ifdef DEBUG
|
||||
|
||||
JS_FRIEND_API(size_t)
|
||||
js_PutEscapedStringImpl(char *buffer, size_t bufferSize, FILE *fp,
|
||||
|
138
js/src/jsstr.h
138
js/src/jsstr.h
@ -73,7 +73,7 @@ JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
|
||||
/*
|
||||
* The GC-thing "string" type.
|
||||
*
|
||||
* When the DEPENDENT bit of the mLength field is unset, the mChars field
|
||||
* When the DEPENDENT bit of the mFlags field is unset, the mChars field
|
||||
* points to a flat character array owned by its GC-thing descriptor. The
|
||||
* array is terminated at index length by a zero character and the size of the
|
||||
* array in bytes is (length + 1) * sizeof(jschar). The terminator is purely a
|
||||
@ -97,16 +97,6 @@ JS_STATIC_ASSERT(JS_BITS_PER_WORD >= 32);
|
||||
* string strongly referenced by the mBase field. The base member may point to
|
||||
* another dependent string if chars() has not been called yet.
|
||||
*
|
||||
* The PREFIX flag determines the kind of the dependent string. When the flag
|
||||
* is unset, the mLength field encodes both starting position relative to the
|
||||
* base string and the number of characters in the dependent string, see
|
||||
* DEPENDENT_START_MASK and DEPENDENT_LENGTH_MASK below for details.
|
||||
*
|
||||
* When the PREFIX flag is set, the dependent string is a prefix of the base
|
||||
* string. The number of characters in the prefix is encoded using all non-flag
|
||||
* bits of the mLength field and spans the same 0 .. SIZE_T_MAX/4 range as the
|
||||
* length of the flat string.
|
||||
*
|
||||
* NB: Always use the length() and chars() accessor methods.
|
||||
*/
|
||||
struct JSString {
|
||||
@ -118,46 +108,33 @@ struct JSString {
|
||||
friend JSString * JS_FASTCALL
|
||||
js_ConcatStrings(JSContext *cx, JSString *left, JSString *right);
|
||||
|
||||
// Not private because we want to be able to use static
|
||||
// initializers for them. Don't use these directly!
|
||||
size_t mLength;
|
||||
size_t mOffset;
|
||||
jsword mFlags;
|
||||
union {
|
||||
jschar *mChars;
|
||||
JSString *mBase;
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions for flags stored in the high order bits of mLength.
|
||||
*
|
||||
* PREFIX and MUTABLE are two aliases for the same bit. PREFIX should be
|
||||
* used only if DEPENDENT is set and MUTABLE should be used only if the
|
||||
* string is flat.
|
||||
* Definitions for flags stored in mFlags.
|
||||
*
|
||||
* ATOMIZED is used only with flat, immutable strings.
|
||||
*/
|
||||
static const size_t DEPENDENT = JSSTRING_BIT(JS_BITS_PER_WORD - 1);
|
||||
static const size_t PREFIX = JSSTRING_BIT(JS_BITS_PER_WORD - 2);
|
||||
static const size_t MUTABLE = PREFIX;
|
||||
static const size_t ATOMIZED = JSSTRING_BIT(JS_BITS_PER_WORD - 3);
|
||||
static const size_t DEFLATED = JSSTRING_BIT(JS_BITS_PER_WORD - 4);
|
||||
#if JS_BITS_PER_WORD > 32
|
||||
static const size_t LENGTH_BITS = 28;
|
||||
#else
|
||||
static const size_t LENGTH_BITS = JS_BITS_PER_WORD - 4;
|
||||
#endif
|
||||
static const size_t LENGTH_MASK = JSSTRING_BITMASK(LENGTH_BITS);
|
||||
static const size_t DEPENDENT_LENGTH_BITS = 8;
|
||||
static const size_t DEPENDENT_LENGTH_MASK = JSSTRING_BITMASK(DEPENDENT_LENGTH_BITS);
|
||||
static const size_t DEPENDENT_START_BITS = LENGTH_BITS - DEPENDENT_LENGTH_BITS;
|
||||
static const size_t DEPENDENT_START_SHIFT = DEPENDENT_LENGTH_BITS;
|
||||
static const size_t DEPENDENT_START_MASK = JSSTRING_BITMASK(DEPENDENT_START_BITS);
|
||||
static const size_t DEPENDENT = JSSTRING_BIT(1);
|
||||
static const size_t MUTABLE = JSSTRING_BIT(2);
|
||||
static const size_t ATOMIZED = JSSTRING_BIT(3);
|
||||
static const size_t DEFLATED = JSSTRING_BIT(4);
|
||||
|
||||
bool hasFlag(size_t flag) const {
|
||||
return (mLength & flag) != 0;
|
||||
return (mFlags & flag) != 0;
|
||||
}
|
||||
|
||||
public:
|
||||
static const size_t MAX_LENGTH = LENGTH_MASK;
|
||||
static const size_t MAX_DEPENDENT_START = DEPENDENT_START_MASK;
|
||||
static const size_t MAX_DEPENDENT_LENGTH = DEPENDENT_LENGTH_MASK;
|
||||
/* Generous but sane length bound. */
|
||||
static const size_t MAX_LENGTH = (1 << 28);
|
||||
|
||||
bool isDependent() const {
|
||||
return hasFlag(DEPENDENT);
|
||||
@ -172,7 +149,7 @@ struct JSString {
|
||||
}
|
||||
|
||||
void setDeflated() {
|
||||
JS_ATOMIC_SET_MASK((jsword *) &mLength, DEFLATED);
|
||||
JS_ATOMIC_SET_MASK(&mFlags, DEFLATED);
|
||||
}
|
||||
|
||||
bool isMutable() const {
|
||||
@ -188,7 +165,7 @@ struct JSString {
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE size_t length() const {
|
||||
return isDependent() ? dependentLength() : flatLength();
|
||||
return mLength;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE bool empty() const {
|
||||
@ -196,25 +173,20 @@ struct JSString {
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void getCharsAndLength(const jschar *&chars, size_t &length) {
|
||||
if (isDependent()) {
|
||||
length = dependentLength();
|
||||
chars = dependentChars();
|
||||
} else {
|
||||
length = flatLength();
|
||||
chars = flatChars();
|
||||
}
|
||||
chars = this->chars();
|
||||
length = this->length();
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE void getCharsAndEnd(const jschar *&chars, const jschar *&end) {
|
||||
end = isDependent()
|
||||
? dependentLength() + (chars = dependentChars())
|
||||
: flatLength() + (chars = flatChars());
|
||||
end = length() + (chars = this->chars());
|
||||
}
|
||||
|
||||
/* Specific flat string initializer and accessor methods. */
|
||||
void initFlat(jschar *chars, size_t length) {
|
||||
JS_ASSERT(length <= MAX_LENGTH);
|
||||
mLength = length;
|
||||
mOffset = 0;
|
||||
mFlags = 0;
|
||||
mChars = chars;
|
||||
}
|
||||
|
||||
@ -223,19 +195,20 @@ struct JSString {
|
||||
return mChars;
|
||||
}
|
||||
|
||||
size_t flatLength() const {
|
||||
JS_ALWAYS_INLINE size_t flatLength() const {
|
||||
JS_ASSERT(isFlat());
|
||||
return mLength & LENGTH_MASK;
|
||||
return length();
|
||||
}
|
||||
|
||||
/*
|
||||
* Special flat string initializer that preserves the JSSTR_DEFLATED flag.
|
||||
* Special flat string initializer that preserves the DEFLATED flag.
|
||||
* Use this method when reinitializing an existing string which may be
|
||||
* hashed to its deflated bytes. Newborn strings must use initFlat.
|
||||
*/
|
||||
void reinitFlat(jschar *chars, size_t length) {
|
||||
JS_ASSERT(length <= MAX_LENGTH);
|
||||
mLength = (mLength & DEFLATED) | (length & ~DEFLATED);
|
||||
mLength = length;
|
||||
mOffset = 0;
|
||||
mFlags = mFlags & DEFLATED;
|
||||
mChars = chars;
|
||||
}
|
||||
|
||||
@ -255,7 +228,7 @@ struct JSString {
|
||||
* only one thread can access the string (see previous property).
|
||||
*
|
||||
* Thus, when multiple threads access the string, JSString::flatSetAtomized
|
||||
* is the only function that can update the mLength field of the string by
|
||||
* is the only function that can update the mFlags field of the string by
|
||||
* changing the mutable bit from 0 to 1. We call the method only after the
|
||||
* string has been hashed. When some threads in js_ValueToStringId see that
|
||||
* the flag is set, it knows that the string was atomized.
|
||||
@ -268,33 +241,34 @@ struct JSString {
|
||||
*/
|
||||
void flatSetAtomized() {
|
||||
JS_ASSERT(isFlat() && !isMutable());
|
||||
JS_STATIC_ASSERT(sizeof(mLength) == sizeof(jsword));
|
||||
JS_ATOMIC_SET_MASK((jsword *) &mLength, ATOMIZED);
|
||||
JS_ATOMIC_SET_MASK(&mFlags, ATOMIZED);
|
||||
}
|
||||
|
||||
void flatSetMutable() {
|
||||
JS_ASSERT(isFlat() && !isAtomized());
|
||||
mLength |= MUTABLE;
|
||||
mFlags |= MUTABLE;
|
||||
}
|
||||
|
||||
void flatClearMutable() {
|
||||
JS_ASSERT(isFlat());
|
||||
if (hasFlag(MUTABLE))
|
||||
mLength &= ~MUTABLE;
|
||||
mFlags &= ~MUTABLE;
|
||||
}
|
||||
|
||||
void initDependent(JSString *bstr, size_t off, size_t len) {
|
||||
JS_ASSERT(off <= MAX_DEPENDENT_START);
|
||||
JS_ASSERT(len <= MAX_DEPENDENT_LENGTH);
|
||||
mLength = DEPENDENT | (off << DEPENDENT_START_SHIFT) | len;
|
||||
JS_ASSERT(len <= MAX_LENGTH);
|
||||
mLength = len;
|
||||
mOffset = off;
|
||||
mFlags = DEPENDENT;
|
||||
mBase = bstr;
|
||||
}
|
||||
|
||||
/* See JSString::reinitFlat. */
|
||||
void reinitDependent(JSString *bstr, size_t off, size_t len) {
|
||||
JS_ASSERT(off <= MAX_DEPENDENT_START);
|
||||
JS_ASSERT(len <= MAX_DEPENDENT_LENGTH);
|
||||
mLength = DEPENDENT | (mLength & DEFLATED) | (off << DEPENDENT_START_SHIFT) | len;
|
||||
JS_ASSERT(len <= MAX_LENGTH);
|
||||
mLength = len;
|
||||
mOffset = off;
|
||||
mFlags = DEPENDENT | (mFlags & DEFLATED);
|
||||
mBase = bstr;
|
||||
}
|
||||
|
||||
@ -303,11 +277,6 @@ struct JSString {
|
||||
return mBase;
|
||||
}
|
||||
|
||||
bool dependentIsPrefix() const {
|
||||
JS_ASSERT(isDependent());
|
||||
return hasFlag(PREFIX);
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE jschar *dependentChars() {
|
||||
return dependentBase()->isDependent()
|
||||
? js_GetDependentStringChars(this)
|
||||
@ -315,39 +284,12 @@ struct JSString {
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE size_t dependentStart() const {
|
||||
return dependentIsPrefix()
|
||||
? 0
|
||||
: ((mLength >> DEPENDENT_START_SHIFT) & DEPENDENT_START_MASK);
|
||||
return mOffset;
|
||||
}
|
||||
|
||||
JS_ALWAYS_INLINE size_t dependentLength() const {
|
||||
JS_ASSERT(isDependent());
|
||||
if (dependentIsPrefix())
|
||||
return mLength & LENGTH_MASK;
|
||||
return mLength & DEPENDENT_LENGTH_MASK;
|
||||
}
|
||||
|
||||
void initPrefix(JSString *bstr, size_t len) {
|
||||
JS_ASSERT(len <= MAX_LENGTH);
|
||||
mLength = DEPENDENT | PREFIX | len;
|
||||
mBase = bstr;
|
||||
}
|
||||
|
||||
/* See JSString::reinitFlat. */
|
||||
void reinitPrefix(JSString *bstr, size_t len) {
|
||||
JS_ASSERT(len <= MAX_LENGTH);
|
||||
mLength = DEPENDENT | PREFIX | (mLength & DEFLATED) | len;
|
||||
mBase = bstr;
|
||||
}
|
||||
|
||||
JSString *prefixBase() const {
|
||||
JS_ASSERT(isDependent() && dependentIsPrefix());
|
||||
return dependentBase();
|
||||
}
|
||||
|
||||
void prefixSetBase(JSString *bstr) {
|
||||
JS_ASSERT(isDependent() && dependentIsPrefix());
|
||||
mBase = bstr;
|
||||
return length();
|
||||
}
|
||||
|
||||
static inline bool isUnitString(void *ptr) {
|
||||
|
@ -3232,10 +3232,10 @@ GetUpvarStackOnTrace(JSContext* cx, uint32 upvarLevel, int32 slot, uint32 callDe
|
||||
// Parameters needed to access a value from a closure on trace.
|
||||
struct ClosureVarInfo
|
||||
{
|
||||
jsid id;
|
||||
uint32 slot;
|
||||
#ifdef DEBUG
|
||||
uint32 callDepth;
|
||||
uint32 resolveFlags;
|
||||
#endif
|
||||
};
|
||||
|
||||
/*
|
||||
@ -3286,13 +3286,15 @@ GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double*
|
||||
if (fp) {
|
||||
v = T::slots(fp)[slot];
|
||||
} else {
|
||||
JS_ASSERT(cv->resolveFlags != JSRESOLVE_INFER);
|
||||
JSAutoResolveFlags rf(cx, cv->resolveFlags);
|
||||
#ifdef DEBUG
|
||||
JSBool rv =
|
||||
#endif
|
||||
js_GetPropertyHelper(cx, call, cv->id, JSGET_METHOD_BARRIER, &v);
|
||||
JS_ASSERT(rv);
|
||||
/*
|
||||
* Get the value from the object. We know we have a Call object, and
|
||||
* that our slot index is fine, so don't monkey around with calling the
|
||||
* property getter (which just looks in the slot) or calling
|
||||
* js_GetReservedSlot. Just get the slot directly. Note the static
|
||||
* asserts in jsfun.cpp which make sure Call objects use dslots.
|
||||
*/
|
||||
JS_ASSERT(slot < T::slot_count(call));
|
||||
v = T::slots(call)[slot];
|
||||
}
|
||||
JSTraceType type = getCoercedType(v);
|
||||
ValueToNative(cx, v, type, result);
|
||||
@ -3301,8 +3303,28 @@ GetFromClosure(JSContext* cx, JSObject* call, const ClosureVarInfo* cv, double*
|
||||
|
||||
struct ArgClosureTraits
|
||||
{
|
||||
static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return fp->argc + slot; }
|
||||
// Adjust our slot to point to the correct slot on the native stack.
|
||||
// See also UpvarArgTraits.
|
||||
static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return 2 + slot; }
|
||||
|
||||
// Get the right frame slots to use our slot index with.
|
||||
// See also UpvarArgTraits.
|
||||
static inline jsval* slots(JSStackFrame* fp) { return fp->argv; }
|
||||
|
||||
// Get the right object slots to use our slot index with.
|
||||
static inline jsval* slots(JSObject* obj) {
|
||||
// We know Call objects use dslots.
|
||||
return obj->dslots + slot_offset(obj);
|
||||
}
|
||||
// Get the offset of our object slots from the object's dslots pointer.
|
||||
static inline uint32 slot_offset(JSObject* obj) {
|
||||
return JSSLOT_START(&js_CallClass) +
|
||||
CALL_CLASS_FIXED_RESERVED_SLOTS - JS_INITIAL_NSLOTS;
|
||||
}
|
||||
// Get the maximum slot index of this type that should be allowed
|
||||
static inline uint16 slot_count(JSObject* obj) {
|
||||
return js_GetCallObjectFunction(obj)->nargs;
|
||||
}
|
||||
private:
|
||||
ArgClosureTraits();
|
||||
};
|
||||
@ -3315,8 +3337,25 @@ GetClosureArg(JSContext* cx, JSObject* callee, const ClosureVarInfo* cv, double*
|
||||
|
||||
struct VarClosureTraits
|
||||
{
|
||||
static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return slot; }
|
||||
// See documentation on ArgClosureTraits for what these functions
|
||||
// should be doing.
|
||||
// See also UpvarVarTraits.
|
||||
static inline uint32 adj_slot(JSStackFrame* fp, uint32 slot) { return 3 + fp->argc + slot; }
|
||||
|
||||
// See also UpvarVarTraits.
|
||||
static inline jsval* slots(JSStackFrame* fp) { return fp->slots; }
|
||||
static inline jsval* slots(JSObject* obj) {
|
||||
// We know Call objects use dslots.
|
||||
return obj->dslots + slot_offset(obj);
|
||||
}
|
||||
static inline uint32 slot_offset(JSObject* obj) {
|
||||
return JSSLOT_START(&js_CallClass) +
|
||||
CALL_CLASS_FIXED_RESERVED_SLOTS - JS_INITIAL_NSLOTS +
|
||||
js_GetCallObjectFunction(obj)->nargs;
|
||||
}
|
||||
static inline uint16 slot_count(JSObject* obj) {
|
||||
return js_GetCallObjectFunction(obj)->u.i.nvars;
|
||||
}
|
||||
private:
|
||||
VarClosureTraits();
|
||||
};
|
||||
@ -6657,7 +6696,7 @@ LeaveTree(InterpState& state, VMSideExit* lr)
|
||||
op == JSOP_GETLOCALPROP || op == JSOP_LENGTH ||
|
||||
op == JSOP_GETELEM || op == JSOP_CALLELEM ||
|
||||
op == JSOP_SETPROP || op == JSOP_SETNAME || op == JSOP_SETMETHOD ||
|
||||
op == JSOP_SETELEM || op == JSOP_INITELEM ||
|
||||
op == JSOP_SETELEM || op == JSOP_INITELEM || op == JSOP_ENUMELEM ||
|
||||
op == JSOP_INSTANCEOF);
|
||||
|
||||
/*
|
||||
@ -7941,8 +7980,12 @@ TraceRecorder::scopeChainProp(JSObject* chainHead, jsval*& vp, LIns*& ins, NameR
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass)
|
||||
return InjectStatus(callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr));
|
||||
if (obj == obj2 && OBJ_GET_CLASS(cx, obj) == &js_CallClass) {
|
||||
AbortableRecordingStatus status =
|
||||
InjectStatus(callProp(obj, prop, ATOM_TO_JSID(atom), vp, ins, nr));
|
||||
obj->dropProperty(cx, prop);
|
||||
return status;
|
||||
}
|
||||
|
||||
obj2->dropProperty(cx, prop);
|
||||
RETURN_STOP_A("fp->scopeChain is not global or active call object");
|
||||
@ -7962,7 +8005,7 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
|
||||
if (setflags && (sprop->attrs & JSPROP_READONLY))
|
||||
RETURN_STOP("writing to a read-only property");
|
||||
|
||||
uintN slot = sprop->shortid;
|
||||
uintN slot = uint16(sprop->shortid);
|
||||
|
||||
vp = NULL;
|
||||
uintN upvar_slot = SPROP_INVALID_SLOT;
|
||||
@ -7973,7 +8016,8 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
|
||||
vp = &cfp->argv[slot];
|
||||
upvar_slot = slot;
|
||||
nr.v = *vp;
|
||||
} else if (sprop->getter == js_GetCallVar) {
|
||||
} else if (sprop->getter == js_GetCallVar ||
|
||||
sprop->getter == js_GetCallVarChecked) {
|
||||
JS_ASSERT(slot < cfp->script->nslots);
|
||||
vp = &cfp->slots[slot];
|
||||
upvar_slot = cx->fp->fun->nargs + slot;
|
||||
@ -7981,7 +8025,9 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
|
||||
} else {
|
||||
RETURN_STOP("dynamic property of Call object");
|
||||
}
|
||||
obj->dropProperty(cx, prop);
|
||||
|
||||
// Now assert that our use of sprop->shortid was in fact kosher.
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
|
||||
if (frameIfInRange(obj)) {
|
||||
// At this point we are guaranteed to be looking at an active call oject
|
||||
@ -8003,7 +8049,6 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
|
||||
: JSGET_METHOD_BARRIER,
|
||||
&nr.v);
|
||||
JS_ASSERT(rv);
|
||||
obj->dropProperty(cx, prop);
|
||||
}
|
||||
|
||||
LIns* obj_ins;
|
||||
@ -8011,34 +8056,67 @@ TraceRecorder::callProp(JSObject* obj, JSProperty* prop, jsid id, jsval*& vp,
|
||||
LIns* parent_ins = stobj_get_parent(get(&cx->fp->argv[-2]));
|
||||
CHECK_STATUS(traverseScopeChain(parent, parent_ins, obj, obj_ins));
|
||||
|
||||
ClosureVarInfo* cv = new (traceAlloc()) ClosureVarInfo();
|
||||
cv->id = id;
|
||||
cv->slot = slot;
|
||||
cv->callDepth = callDepth;
|
||||
cv->resolveFlags = cx->resolveFlags == JSRESOLVE_INFER
|
||||
? js_InferFlags(cx, 0)
|
||||
: cx->resolveFlags;
|
||||
LIns* call_ins;
|
||||
if (!cfp) {
|
||||
// Because the parent guard in guardCallee ensures this Call object
|
||||
// will be the same object now and on trace, and because once a Call
|
||||
// object loses its frame it never regains one, on trace we will also
|
||||
// have a null private in the Call object. So all we need to do is
|
||||
// write the value to the Call object's slot.
|
||||
int32 dslot_index = slot;
|
||||
if (sprop->getter == js_GetCallArg) {
|
||||
JS_ASSERT(dslot_index < ArgClosureTraits::slot_count(obj));
|
||||
dslot_index += ArgClosureTraits::slot_offset(obj);
|
||||
} else if (sprop->getter == js_GetCallVar ||
|
||||
sprop->getter == js_GetCallVarChecked) {
|
||||
JS_ASSERT(dslot_index < VarClosureTraits::slot_count(obj));
|
||||
dslot_index += VarClosureTraits::slot_offset(obj);
|
||||
} else {
|
||||
RETURN_STOP("dynamic property of Call object");
|
||||
}
|
||||
|
||||
LIns* outp = lir->insAlloc(sizeof(double));
|
||||
LIns* args[] = {
|
||||
outp,
|
||||
INS_CONSTPTR(cv),
|
||||
obj_ins,
|
||||
cx_ins
|
||||
};
|
||||
const CallInfo* ci;
|
||||
if (sprop->getter == js_GetCallArg)
|
||||
ci = &GetClosureArg_ci;
|
||||
else
|
||||
ci = &GetClosureVar_ci;
|
||||
// Now assert that our use of sprop->shortid was in fact kosher.
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
|
||||
LIns* call_ins = lir->insCall(ci, args);
|
||||
JSTraceType type = getCoercedType(nr.v);
|
||||
guard(true,
|
||||
addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)),
|
||||
"guard(type-stable name access)"),
|
||||
BRANCH_EXIT);
|
||||
ins = stackLoad(outp, type);
|
||||
LIns* base = lir->insLoad(LIR_ldp, obj_ins, offsetof(JSObject, dslots));
|
||||
LIns* val_ins = lir->insLoad(LIR_ldp, base, dslot_index * sizeof(jsval));
|
||||
ins = unbox_jsval(obj->dslots[dslot_index], val_ins, snapshot(BRANCH_EXIT));
|
||||
} else {
|
||||
ClosureVarInfo* cv = new (traceAlloc()) ClosureVarInfo();
|
||||
cv->slot = slot;
|
||||
#ifdef DEBUG
|
||||
cv->callDepth = callDepth;
|
||||
#endif
|
||||
|
||||
LIns* outp = lir->insAlloc(sizeof(double));
|
||||
LIns* args[] = {
|
||||
outp,
|
||||
INS_CONSTPTR(cv),
|
||||
obj_ins,
|
||||
cx_ins
|
||||
};
|
||||
const CallInfo* ci;
|
||||
if (sprop->getter == js_GetCallArg) {
|
||||
ci = &GetClosureArg_ci;
|
||||
} else if (sprop->getter == js_GetCallVar ||
|
||||
sprop->getter == js_GetCallVarChecked) {
|
||||
ci = &GetClosureVar_ci;
|
||||
} else {
|
||||
RETURN_STOP("dynamic property of Call object");
|
||||
}
|
||||
|
||||
// Now assert that our use of sprop->shortid was in fact kosher.
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
|
||||
call_ins = lir->insCall(ci, args);
|
||||
|
||||
JSTraceType type = getCoercedType(nr.v);
|
||||
guard(true,
|
||||
addName(lir->ins2(LIR_eq, call_ins, lir->insImm(type)),
|
||||
"guard(type-stable name access)"),
|
||||
BRANCH_EXIT);
|
||||
ins = stackLoad(outp, type);
|
||||
}
|
||||
nr.tracked = false;
|
||||
nr.obj = obj;
|
||||
nr.obj_ins = obj_ins;
|
||||
@ -8324,11 +8402,7 @@ TraceRecorder::ifop()
|
||||
lir->ins_eq0(lir->ins2(LIR_feq, v_ins, lir->insImmf(0))));
|
||||
} else if (JSVAL_IS_STRING(v)) {
|
||||
cond = JSVAL_TO_STRING(v)->length() != 0;
|
||||
x = lir->ins2(LIR_piand,
|
||||
lir->insLoad(LIR_ldp,
|
||||
v_ins,
|
||||
(int)offsetof(JSString, mLength)),
|
||||
INS_CONSTWORD(JSString::LENGTH_MASK));
|
||||
x = lir->insLoad(LIR_ldp, v_ins, offsetof(JSString, mLength));
|
||||
} else {
|
||||
JS_NOT_REACHED("ifop");
|
||||
return ARECORD_STOP;
|
||||
@ -8641,6 +8715,11 @@ TraceRecorder::equalityHelper(jsval l, jsval r, LIns* l_ins, LIns* r_ins,
|
||||
|
||||
if (GetPromotedType(l) == GetPromotedType(r)) {
|
||||
if (JSVAL_TAG(l) == JSVAL_OBJECT || JSVAL_IS_SPECIAL(l)) {
|
||||
if (JSVAL_TAG(l) == JSVAL_OBJECT && l) {
|
||||
JSClass *clasp = OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(l));
|
||||
if ((clasp->flags & JSCLASS_IS_EXTENDED) && ((JSExtendedClass*) clasp)->equality)
|
||||
RETURN_STOP_A("Can't trace extended class equality operator");
|
||||
}
|
||||
if (JSVAL_TAG(l) == JSVAL_OBJECT)
|
||||
op = LIR_peq;
|
||||
cond = (l == r);
|
||||
@ -9002,7 +9081,7 @@ DumpShape(JSObject* obj, const char* prefix)
|
||||
}
|
||||
|
||||
fprintf(shapefp, "\n%s: shape %u flags %x\n", prefix, scope->shape, scope->flags);
|
||||
for (JSScopeProperty* sprop = scope->lastProp; sprop; sprop = sprop->parent) {
|
||||
for (JSScopeProperty* sprop = scope->lastProperty(); sprop; sprop = sprop->parent) {
|
||||
if (JSID_IS_ATOM(sprop->id)) {
|
||||
fprintf(shapefp, " %s", JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(sprop->id))));
|
||||
} else {
|
||||
@ -9552,31 +9631,6 @@ TraceRecorder::getThis(LIns*& this_ins)
|
||||
}
|
||||
|
||||
|
||||
LIns*
|
||||
TraceRecorder::getStringLength(LIns* str_ins)
|
||||
{
|
||||
LIns* len_ins = lir->insLoad(LIR_ldp, str_ins, (int)offsetof(JSString, mLength));
|
||||
|
||||
LIns* masked_len_ins = lir->ins2(LIR_piand,
|
||||
len_ins,
|
||||
INS_CONSTWORD(JSString::LENGTH_MASK));
|
||||
|
||||
LIns* real_len =
|
||||
lir->ins_choose(lir->ins_peq0(lir->ins2(LIR_piand,
|
||||
len_ins,
|
||||
INS_CONSTWORD(JSString::DEPENDENT))),
|
||||
masked_len_ins,
|
||||
lir->ins_choose(lir->ins_peq0(lir->ins2(LIR_piand,
|
||||
len_ins,
|
||||
INS_CONSTWORD(JSString::PREFIX))),
|
||||
lir->ins2(LIR_piand,
|
||||
len_ins,
|
||||
INS_CONSTWORD(JSString::DEPENDENT_LENGTH_MASK)),
|
||||
masked_len_ins, avmplus::AvmCore::use_cmov()),
|
||||
avmplus::AvmCore::use_cmov());
|
||||
return p2i(real_len);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK bool
|
||||
TraceRecorder::guardClass(JSObject* obj, LIns* obj_ins, JSClass* clasp, VMSideExit* exit)
|
||||
{
|
||||
@ -10189,9 +10243,8 @@ TraceRecorder::record_JSOP_NOT()
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
JS_ASSERT(JSVAL_IS_STRING(v));
|
||||
set(&v, lir->ins_peq0(lir->ins2(LIR_piand,
|
||||
lir->insLoad(LIR_ldp, get(&v), (int)offsetof(JSString, mLength)),
|
||||
INS_CONSTWORD(JSString::LENGTH_MASK))));
|
||||
set(&v, lir->ins_peq0(lir->insLoad(LIR_ldp, get(&v),
|
||||
offsetof(JSString, mLength))));
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
@ -11215,7 +11268,7 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
|
||||
LIns* obj_ins = get(&l);
|
||||
JSScope* scope = OBJ_SCOPE(obj);
|
||||
|
||||
JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->has(sprop));
|
||||
JS_ASSERT_IF(entry->vcap == PCVCAP_MAKE(entry->kshape, 0, 0), scope->hasProperty(sprop));
|
||||
|
||||
// Fast path for CallClass. This is about 20% faster than the general case.
|
||||
v_ins = get(&v);
|
||||
@ -11237,7 +11290,7 @@ TraceRecorder::setProp(jsval &l, JSPropCacheEntry* entry, JSScopeProperty* sprop
|
||||
jsuword pcval;
|
||||
CHECK_STATUS(guardPropertyCacheHit(obj_ins, map_ins, obj, obj2, entry, pcval));
|
||||
JS_ASSERT(scope->object == obj2);
|
||||
JS_ASSERT(scope->has(sprop));
|
||||
JS_ASSERT(scope->hasProperty(sprop));
|
||||
JS_ASSERT_IF(obj2 != obj, sprop->attrs & JSPROP_SHARED);
|
||||
|
||||
/*
|
||||
@ -11279,13 +11332,15 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty
|
||||
JSStackFrame *fp = frameIfInRange(callobj);
|
||||
if (fp) {
|
||||
if (sprop->setter == SetCallArg) {
|
||||
jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop));
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
uintN slot = uint16(sprop->shortid);
|
||||
jsval *vp2 = &fp->argv[slot];
|
||||
set(vp2, v_ins);
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
if (sprop->setter == SetCallVar) {
|
||||
jsint slot = JSVAL_TO_INT(SPROP_USERID(sprop));
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
uintN slot = uint16(sprop->shortid);
|
||||
jsval *vp2 = &fp->slots[slot];
|
||||
set(vp2, v_ins);
|
||||
return RECORD_CONTINUE;
|
||||
@ -11293,6 +11348,37 @@ TraceRecorder::setCallProp(JSObject *callobj, LIns *callobj_ins, JSScopeProperty
|
||||
RETURN_STOP("can't trace special CallClass setter");
|
||||
}
|
||||
|
||||
if (!callobj->getPrivate()) {
|
||||
// Because the parent guard in guardCallee ensures this Call object
|
||||
// will be the same object now and on trace, and because once a Call
|
||||
// object loses its frame it never regains one, on trace we will also
|
||||
// have a null private in the Call object. So all we need to do is
|
||||
// write the value to the Call object's slot.
|
||||
int32 dslot_index = uint16(sprop->shortid);
|
||||
if (sprop->setter == SetCallArg) {
|
||||
JS_ASSERT(dslot_index < ArgClosureTraits::slot_count(callobj));
|
||||
dslot_index += ArgClosureTraits::slot_offset(callobj);
|
||||
} else if (sprop->setter == SetCallVar) {
|
||||
JS_ASSERT(dslot_index < VarClosureTraits::slot_count(callobj));
|
||||
dslot_index += VarClosureTraits::slot_offset(callobj);
|
||||
} else {
|
||||
RETURN_STOP("can't trace special CallClass setter");
|
||||
}
|
||||
|
||||
// Now assert that the shortid get we did above was ok. Have to do it
|
||||
// after the RETURN_STOP above, since in that case we may in fact not
|
||||
// have a valid shortid; but we don't use it in that case anyway.
|
||||
JS_ASSERT(sprop->flags & SPROP_HAS_SHORTID);
|
||||
|
||||
LIns* base = lir->insLoad(LIR_ldp, callobj_ins, offsetof(JSObject, dslots));
|
||||
lir->insStorei(box_jsval(v, v_ins), base, dslot_index * sizeof(jsval));
|
||||
return RECORD_CONTINUE;
|
||||
}
|
||||
|
||||
// This is the hard case: we have a JSStackFrame private, but it's not in
|
||||
// range. During trace execution we may or may not have a JSStackFrame
|
||||
// anymore. Call the standard builtins, which handle that situation.
|
||||
|
||||
// Set variables in off-trace-stack call objects by calling standard builtins.
|
||||
const CallInfo* ci = NULL;
|
||||
if (sprop->setter == SetCallArg)
|
||||
@ -11825,11 +11911,11 @@ TraceRecorder::initOrSetPropertyByIndex(LIns* obj_ins, LIns* index_ins, jsval* r
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETELEM()
|
||||
TraceRecorder::setElem(int lval_spindex, int idx_spindex, int v_spindex)
|
||||
{
|
||||
jsval& v = stackval(-1);
|
||||
jsval& idx = stackval(-2);
|
||||
jsval& lval = stackval(-3);
|
||||
jsval& v = stackval(v_spindex);
|
||||
jsval& idx = stackval(idx_spindex);
|
||||
jsval& lval = stackval(lval_spindex);
|
||||
|
||||
if (JSVAL_IS_PRIMITIVE(lval))
|
||||
RETURN_STOP_A("left JSOP_SETELEM operand is not an object");
|
||||
@ -11889,6 +11975,12 @@ TraceRecorder::record_JSOP_SETELEM()
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_SETELEM()
|
||||
{
|
||||
return setElem(-3, -2, -1);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_CALLNAME()
|
||||
{
|
||||
@ -12082,6 +12174,16 @@ TraceRecorder::guardCallee(jsval& callee)
|
||||
stobj_get_private(callee_ins),
|
||||
INS_CONSTPTR(callee_obj->getPrivate())),
|
||||
branchExit);
|
||||
|
||||
/*
|
||||
* As long as we have this parent guard, we're guaranteed that if we record
|
||||
* with a Call object which has a null getPrivate(), then on trace that
|
||||
* Call object will continue to have a null private, because we're
|
||||
* effectively guarding on Call object identity and Call objects can't pick
|
||||
* up a stack frame once they have none. callProp and setCallProp depend
|
||||
* on this and document where; if this guard is removed make sure to fix
|
||||
* those methods. Search for the "parent guard" comments in them.
|
||||
*/
|
||||
guard(true,
|
||||
lir->ins2(LIR_peq,
|
||||
stobj_get_parent(callee_ins),
|
||||
@ -12543,7 +12645,7 @@ TraceRecorder::prop(JSObject* obj, LIns* obj_ins, uint32 *slotp, LIns** v_insp,
|
||||
|
||||
if (PCVAL_IS_SPROP(pcval)) {
|
||||
sprop = PCVAL_TO_SPROP(pcval);
|
||||
JS_ASSERT(OBJ_SCOPE(obj2)->has(sprop));
|
||||
JS_ASSERT(OBJ_SCOPE(obj2)->hasProperty(sprop));
|
||||
|
||||
if (setflags && !SPROP_HAS_STUB_SETTER(sprop))
|
||||
RETURN_STOP_A("non-stub setter");
|
||||
@ -12957,7 +13059,7 @@ TraceRecorder::record_JSOP_INITPROP()
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_INITELEM()
|
||||
{
|
||||
return record_JSOP_SETELEM();
|
||||
return setElem(-3, -2, -1);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
@ -13415,7 +13517,11 @@ TraceRecorder::record_JSOP_EVAL()
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
TraceRecorder::record_JSOP_ENUMELEM()
|
||||
{
|
||||
return ARECORD_STOP;
|
||||
/*
|
||||
* To quote from jsops.cpp's JSOP_ENUMELEM case:
|
||||
* Funky: the value to set is under the [obj, id] pair.
|
||||
*/
|
||||
return setElem(-2, -1, -3);
|
||||
}
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus
|
||||
@ -14456,7 +14562,9 @@ TraceRecorder::record_JSOP_LENGTH()
|
||||
if (JSVAL_IS_PRIMITIVE(l)) {
|
||||
if (!JSVAL_IS_STRING(l))
|
||||
RETURN_STOP_A("non-string primitive JSOP_LENGTH unsupported");
|
||||
set(&l, lir->ins1(LIR_i2f, getStringLength(get(&l))));
|
||||
set(&l, lir->ins1(LIR_i2f,
|
||||
p2i(lir->insLoad(LIR_ldp, get(&l),
|
||||
offsetof(JSString, mLength)))));
|
||||
return ARECORD_CONTINUE;
|
||||
}
|
||||
|
||||
|
@ -1231,8 +1231,6 @@ class TraceRecorder
|
||||
return stobj_get_fslot(obj_ins, JSSLOT_PARENT);
|
||||
}
|
||||
|
||||
nanojit::LIns* getStringLength(nanojit::LIns* str_ins);
|
||||
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus name(jsval*& vp, nanojit::LIns*& ins, NameResult& nr);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus prop(JSObject* obj, nanojit::LIns* obj_ins, uint32 *slotp,
|
||||
nanojit::LIns** v_insp, jsval* outp);
|
||||
@ -1273,6 +1271,8 @@ class TraceRecorder
|
||||
JS_REQUIRES_STACK RecordingStatus initOrSetPropertyByIndex(nanojit::LIns* obj_ins,
|
||||
nanojit::LIns* index_ins,
|
||||
jsval* rvalp, bool init);
|
||||
JS_REQUIRES_STACK AbortableRecordingStatus setElem(int lval_spindex, int idx_spindex,
|
||||
int v_spindex);
|
||||
|
||||
JS_REQUIRES_STACK nanojit::LIns* box_jsval(jsval v, nanojit::LIns* v_ins);
|
||||
JS_REQUIRES_STACK nanojit::LIns* unbox_jsval(jsval v, nanojit::LIns* v_ins, VMSideExit* exit);
|
||||
|
@ -1,2 +1,3 @@
|
||||
include Date/jstests.list
|
||||
include Object/jstests.list
|
||||
include strict/jstests.list
|
||||
|
@ -34,3 +34,5 @@ assertEq(completesNormally("Function('010')"),
|
||||
true);
|
||||
assertEq(raisesException(SyntaxError)("Function('\"use strict\"; 010')"),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -138,3 +138,5 @@ assertEq(testLenientAndStrict('({x getter: function() {}, x getter: function() {
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('(eval)=1',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('(eval)+=1',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('(eval)++',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('(eval)--',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -37,3 +37,5 @@ assertEq(testLenientAndStrict('function f() { "use strict"; delete x; }',
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('++(eval)',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -25,3 +25,5 @@ assertEq(testLenientAndStrict('--(eval)',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -26,3 +26,5 @@ assertEq(testLenientAndStrict('function f() { "use strict"; with (1) {} }',
|
||||
*/
|
||||
assertEq(parsesSuccessfully('function f() { "use strict"; }; with (1) {}'),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -33,3 +33,5 @@ assertEq(testLenientAndStrict('try{}catch({x:arguments}){}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -21,3 +21,5 @@ assertEq(testLenientAndStrict('var x,arguments;',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -14,21 +14,21 @@
|
||||
* The parameters of ordinary function definitions should not contain
|
||||
* duplicate identifiers.
|
||||
*/
|
||||
assertEq(testLenientAndStrict('function(x,y) {}',
|
||||
assertEq(testLenientAndStrict('function f(x,y) {}',
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('function(x,x) {}',
|
||||
assertEq(testLenientAndStrict('function f(x,x) {}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('function(x,y,z,y) {}',
|
||||
assertEq(testLenientAndStrict('function f(x,y,z,y) {}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
/* Exercise the hashed local name map case. */
|
||||
assertEq(testLenientAndStrict('function(a,b,c,d,e,f,g,h,d) {}',
|
||||
assertEq(testLenientAndStrict('function f(a,b,c,d,e,f,g,h,d) {}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
@ -37,15 +37,15 @@ assertEq(testLenientAndStrict('function(a,b,c,d,e,f,g,h,d) {}',
|
||||
* SpiderMonkey has always treated duplicates in destructuring
|
||||
* patterns as an error. Strict mode should not affect this.
|
||||
*/
|
||||
assertEq(testLenientAndStrict('function([x,y]) {}',
|
||||
assertEq(testLenientAndStrict('function f([x,y]) {}',
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('function([x,x]){}',
|
||||
assertEq(testLenientAndStrict('function f([x,x]){}',
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
assertEq(testLenientAndStrict('function(x,[x]){}',
|
||||
assertEq(testLenientAndStrict('function f(x,[x]){}',
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
@ -54,7 +54,7 @@ assertEq(testLenientAndStrict('function(x,[x]){}',
|
||||
* Strict rules apply to the parameters if the function's body is
|
||||
* strict.
|
||||
*/
|
||||
assertEq(testLenientAndStrict('function(x,x) { "use strict" };',
|
||||
assertEq(testLenientAndStrict('function f(x,x) { "use strict" };',
|
||||
parseRaisesException(SyntaxError),
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
@ -410,3 +410,5 @@ assertEq(testLenientAndStrict('Function("arguments","\'use strict\';")',
|
||||
raisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -35,3 +35,5 @@ assertEq(testLenientAndStrict('undeclared_at_compiletime=1',
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -27,3 +27,5 @@ assertEq(parsesSuccessfully('function f() { "use strict"; }; 010'),
|
||||
/* Octal integer literal in function body */
|
||||
assertEq(parsesSuccessfully('function f() { 010; }'),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -34,3 +34,5 @@ assertEq(testLenientAndStrict('"\\0x"',
|
||||
parsesSuccessfully,
|
||||
parsesSuccessfully),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -15,3 +15,5 @@ script 12.14.1.js
|
||||
script 13.1.js
|
||||
script B.1.1.js
|
||||
script B.1.2.js
|
||||
script regress-532254.js
|
||||
script regress-532041.js
|
||||
|
17
js/src/tests/ecma_5/strict/regress-532041.js
Normal file
17
js/src/tests/ecma_5/strict/regress-532041.js
Normal file
@ -0,0 +1,17 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* JSFunction::findDuplicateFormal (nee js_FindDuplicateFormal), used
|
||||
* by strict checks, sometimes failed to choose the correct branch of
|
||||
* the fun->u.i.names union: it used the argument count, not the
|
||||
* overall name count.
|
||||
*/
|
||||
function f(a1,a2,a3,a4,a5) { "use strict"; var v1, v2, v3, v4, v5, v6, v7; }
|
||||
|
||||
reportCompare(true, true);
|
13
js/src/tests/ecma_5/strict/regress-532254.js
Normal file
13
js/src/tests/ecma_5/strict/regress-532254.js
Normal file
@ -0,0 +1,13 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
assertEq(testLenientAndStrict('function f(eval,[x]){}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
35
js/src/tests/js1_4/Eval/regress-531682.js
Normal file
35
js/src/tests/js1_4/Eval/regress-531682.js
Normal file
@ -0,0 +1,35 @@
|
||||
/* -*- Mode: java; tab-width:8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
*/
|
||||
|
||||
var gTestfile = 'regress-531682.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 531682;
|
||||
var summary = 'Checking proper wrapping of scope in eval(source, scope)';
|
||||
var actual;
|
||||
var expect;
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var x = 0;
|
||||
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function scope1() {
|
||||
eval('var x = 1;');
|
||||
return function() { return x; }
|
||||
}
|
||||
|
||||
function test() {
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
// The scope chain in eval should be just scope1() and the global object.
|
||||
actual = eval('x', scope1());
|
||||
expect = 0;
|
||||
reportCompare(expect, actual, summary);
|
||||
exitFunc ('test');
|
||||
}
|
@ -45,4 +45,6 @@ assertEq(isSyntaxError("function f(a,b,c,d,e,f,g,h,b,[y]){}"), true);
|
||||
assertEq(isSyntaxError("function f([y],a,b,c,d,e,f,g,h,a){}"), true);
|
||||
assertEq(isSyntaxError("function f([a],b,c,d,e,f,g,h,i,a){}"), true);
|
||||
assertEq(isSyntaxError("function f(a,b,c,d,e,f,g,h,i,[a]){}"), true);
|
||||
reportCompare(isSyntaxError("function f(a,b,c,d,e,f,g,h,i,[a]){}"), true);
|
||||
assertEq(isSyntaxError("function f(a,b,c,d,e,f,g,h,i,[a]){}"), true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
71
js/src/tests/js1_8/regress/regress-532491.js
Normal file
71
js/src/tests/js1_8/regress/regress-532491.js
Normal file
@ -0,0 +1,71 @@
|
||||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is JavaScript Engine testing utilities.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Andreas Gal
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
var gTestfile = 'regress-532491.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 466128;
|
||||
var summary = 'Assertion failure: staticLevel == script->staticLevel, at ../jsobj.cpp';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
jit(false);
|
||||
function f(foo) {
|
||||
if (a % 2 == 1) {
|
||||
try {
|
||||
eval(foo);
|
||||
} catch(e) {}
|
||||
}
|
||||
}
|
||||
a = 1;
|
||||
f("eval(\"x\")");
|
||||
f("x");
|
||||
|
||||
reportCompare(expect, actual, summary);
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
@ -3,4 +3,5 @@ include extensions/jstests.list
|
||||
include JSON/jstests.list
|
||||
include regress/jstests.list
|
||||
include String/jstests.list
|
||||
include strict/jstests.list
|
||||
include trace/jstests.list
|
||||
|
@ -83,3 +83,4 @@ script regress-515885.js
|
||||
skip-if(isDebugBuild&&!xulRuntime.shell) script regress-524743.js # hang
|
||||
script regress-522123.js
|
||||
script regress-524264.js
|
||||
script regress-530879.js
|
||||
|
@ -66,3 +66,5 @@ function test()
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
||||
|
||||
reportCompare(true, true);
|
||||
|
10
js/src/tests/js1_8_1/regress/regress-530879.js
Normal file
10
js/src/tests/js1_8_1/regress/regress-530879.js
Normal file
@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor: Jason Orendorff
|
||||
*/
|
||||
gTestfile = 'regress-530879';
|
||||
function f(a, b, c, d) {
|
||||
yield arguments.length;
|
||||
}
|
||||
reportCompare(0, f().next(), "bug 530879");
|
@ -70,3 +70,5 @@ assertEq(testLenientAndStrict('for (let {x:arguments} in [])break;',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -12,3 +12,5 @@ assertEq(testLenientAndStrict('let let_declared; let_declared=1',
|
||||
completesNormally,
|
||||
completesNormally),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -33,3 +33,5 @@ assertEq(testLenientAndStrict('(1 for ({x:arguments} in []))',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
@ -30,3 +30,5 @@ assertEq(testLenientAndStrict('let ({x:arguments}=1) {}',
|
||||
parsesSuccessfully,
|
||||
parseRaisesException(SyntaxError)),
|
||||
true);
|
||||
|
||||
reportCompare(true, true);
|
||||
|
23
js/src/trace-test/tests/basic/bug532823.js
Normal file
23
js/src/trace-test/tests/basic/bug532823.js
Normal file
@ -0,0 +1,23 @@
|
||||
function loop(f) {
|
||||
var p;
|
||||
for (var i = 0; i < 10; ++i) {
|
||||
p = f();
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
function f(j, k) {
|
||||
var g = function() { return k; }
|
||||
|
||||
var ans = '';
|
||||
|
||||
for (k = 0; k < 5; ++k) {
|
||||
ans += loop(g);
|
||||
}
|
||||
return ans;
|
||||
}
|
||||
|
||||
var t0 = new Date;
|
||||
var actual = f(1);
|
||||
|
||||
assertEq(actual, '01234');
|
22
js/src/trace-test/tests/basic/testGroupAssignment.js
Normal file
22
js/src/trace-test/tests/basic/testGroupAssignment.js
Normal file
@ -0,0 +1,22 @@
|
||||
assertEq(
|
||||
(function () {
|
||||
var arr = [ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
|
||||
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32 ];
|
||||
|
||||
for (var i = 0; i < 4; ++i) {
|
||||
var src = i * 8;
|
||||
var dst = i * 8 + 7;
|
||||
for (var j = 0; j < 4; ++j) {
|
||||
[arr[dst--], arr[src++]] = [arr[src], arr[dst]];
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
})().toSource(),
|
||||
"[7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8, 23, 22, 21, 20, 19, 18, 17, 16, 31, 30, 29, 28, 27, 26, 25, 24, 32]");
|
||||
|
||||
checkStats({
|
||||
recorderStarted: 2,
|
||||
traceCompleted: 2,
|
||||
sideExitIntoInterpreter: 3,
|
||||
traceTriggered: 3
|
||||
});
|
@ -96,9 +96,9 @@ crashtest:
|
||||
$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH))
|
||||
$(CHECK_TEST_ERROR)
|
||||
|
||||
jstestbrowser: EXTRA_TEST_ARGS += --extra-profile-file=$(topsrcdir)/js/src/tests/user.js
|
||||
jstestbrowser: TEST_PATH=js/src/tests/jstests.list
|
||||
jstestbrowser:
|
||||
$(call RUN_REFTEST,$(topsrcdir)/js/src/tests/jstests.list)
|
||||
$(call RUN_REFTEST,$(topsrcdir)/$(TEST_PATH) --extra-profile-file=$(topsrcdir)/js/src/tests/user.js)
|
||||
$(CHECK_TEST_ERROR)
|
||||
|
||||
# Execute all xpcshell tests in the directories listed in the manifest.
|
||||
|
Loading…
Reference in New Issue
Block a user