Move scope chains of scope objects to reserved slots, bug 694247.

This commit is contained in:
Brian Hackett 2011-10-12 22:29:43 -07:00
Родитель a4c76d19d1
Коммит 1ca40e7627
23 изменённых файлов: 293 добавлений и 178 удалений

Просмотреть файл

@ -2429,7 +2429,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
jsClass = js::GetObjectClass(aObj);
if (jsClass == &js::CallClass) {
aObj = js::GetObjectParent(aObj);
aObj = js::GetObjectParentMaybeScope(aObj);
if (!aObj)
return nsnull;
@ -2481,7 +2481,7 @@ nsScriptSecurityManager::doGetObjectPrincipal(JSObject *aObj
}
}
aObj = js::GetObjectParent(aObj);
aObj = js::GetObjectParentMaybeScope(aObj);
if (!aObj)
break;

Просмотреть файл

@ -48,6 +48,7 @@
#include "nsJSUtils.h"
#include "jsapi.h"
#include "jsdbgapi.h"
#include "jsfriendapi.h"
#include "prprf.h"
#include "nsIScriptContext.h"
#include "nsIScriptObjectOwner.h"
@ -107,8 +108,7 @@ nsJSUtils::GetStaticScriptGlobal(JSContext* aContext, JSObject* aObj)
if (!glob)
return nsnull;
while ((parent = ::JS_GetParent(aContext, glob)))
glob = parent;
glob = js::GetGlobalForObject(glob);
clazz = JS_GET_CLASS(aContext, glob);

Просмотреть файл

@ -3063,6 +3063,7 @@ JS_PUBLIC_API(JSBool)
JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
{
CHECK_REQUEST(cx);
JS_ASSERT(!obj->isScope());
JS_ASSERT(parent || !obj->getParent());
assertSameCompartment(cx, obj, parent);
obj->setParent(parent);
@ -4346,7 +4347,7 @@ JS_CloneFunctionObject(JSContext *cx, JSObject *funobj, JSObject *parent)
JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
return NULL;
}
obj = obj->getParent();
obj = obj->getParentOrScopeChain();
}
Value v;

Просмотреть файл

@ -389,7 +389,7 @@ DeepBail(JSContext *cx);
static JS_INLINE void
LeaveTraceIfGlobalObject(JSContext *cx, JSObject *obj)
{
if (!obj->getParent())
if (obj->isGlobal())
LeaveTrace(cx);
}

Просмотреть файл

@ -170,6 +170,24 @@ AutoSwitchCompartment::~AutoSwitchCompartment()
cx->compartment = oldCompartment;
}
bool
js::IsScopeObject(const JSObject *obj)
{
return obj->isScope();
}
JS_FRIEND_API(JSObject *)
js::GetObjectParentMaybeScope(const JSObject *obj)
{
return obj->getParentOrScopeChain();
}
JS_FRIEND_API(JSObject *)
js::GetGlobalForObject(JSObject *obj)
{
return obj->getGlobal();
}
/*
* The below code is for temporary telemetry use. It can be removed when
* sufficient data has been harvested.

Просмотреть файл

@ -236,12 +236,21 @@ GetObjectJSClass(const JSObject *obj)
return js::Jsvalify(GetObjectClass(obj));
}
bool IsScopeObject(const JSObject *obj);
inline JSObject *
GetObjectParent(const JSObject *obj)
{
JS_ASSERT(!IsScopeObject(obj));
return reinterpret_cast<const shadow::Object*>(obj)->parent;
}
JS_FRIEND_API(JSObject *)
GetObjectParentMaybeScope(const JSObject *obj);
JS_FRIEND_API(JSObject *)
GetGlobalForObject(JSObject *obj);
inline JSObject *
GetObjectProto(const JSObject *obj)
{

Просмотреть файл

@ -747,7 +747,9 @@ Class js::StrictArgumentsObjectClass = {
*/
Class js::DeclEnvClass = {
js_Object_str,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_RESERVED_SLOTS(CallObject::DECL_ENV_RESERVED_SLOTS) |
JSCLASS_HAS_CACHED_PROTO(JSProto_Object),
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@ -771,10 +773,12 @@ NewDeclEnvObject(JSContext *cx, StackFrame *fp)
EmptyShape *emptyDeclEnvShape = EmptyShape::getEmptyDeclEnvShape(cx);
if (!emptyDeclEnvShape)
return NULL;
envobj->init(cx, type, &fp->scopeChain(), false);
envobj->init(cx, type, fp->scopeChain().getGlobal(), false);
envobj->setInitialPropertyInfallible(emptyDeclEnvShape);
envobj->setPrivate(fp);
envobj->setScopeChain(&fp->scopeChain());
return envobj;
}
@ -915,7 +919,7 @@ js_PutCallObject(StackFrame *fp)
/* Clear private pointers to fp, which is about to go away. */
if (js_IsNamedLambda(fun)) {
JSObject *env = callobj.getParent();
JSObject *env = callobj.scopeChain();
JS_ASSERT(env->isDeclEnv());
JS_ASSERT(env->getPrivate() == fp);

Просмотреть файл

@ -163,10 +163,7 @@ struct JSFunction : public JSObject_Slots2
return flags & JSFUN_JOINABLE;
}
JSObject *callScope() const {
JS_ASSERT(isInterpreted());
return u.i.scope;
}
inline JSObject *callScope() const;
/*
* FunctionClass reserves two slots, which are free in JSObject::fslots
@ -286,49 +283,6 @@ extern JSFunction * JS_FASTCALL
js_CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
JSObject *proto);
inline JSFunction *
CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
bool ignoreSingletonClone = false)
{
JS_ASSERT(parent);
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
return NULL;
/*
* For attempts to clone functions at a function definition opcode or from
* a method barrier, don't perform the clone if the function has singleton
* type. CloneFunctionObject was called pessimistically, and we need to
* preserve the type's property that if it is singleton there is only a
* single object with its type in existence.
*/
if (ignoreSingletonClone && fun->hasSingletonType()) {
JS_ASSERT(fun->getProto() == proto);
fun->setParent(parent);
return fun;
}
return js_CloneFunctionObject(cx, fun, parent, proto);
}
inline JSFunction *
CloneFunctionObject(JSContext *cx, JSFunction *fun)
{
/*
* Variant which makes an exact clone of fun, preserving parent and proto.
* Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
* is not equivalent: API clients, including XPConnect, can reparent
* objects so that fun->getGlobal() != fun->getProto()->getGlobal().
* See ReparentWrapperIfFound.
*/
JS_ASSERT(fun->getParent() && fun->getProto());
if (fun->hasSingletonType())
return fun;
return js_CloneFunctionObject(cx, fun, fun->getParent(), fun->getProto());
}
extern JSObject * JS_FASTCALL
js_AllocFlatClosure(JSContext *cx, JSFunction *fun, JSObject *scopeChain);

Просмотреть файл

@ -49,6 +49,14 @@ JSFunction::inStrictMode() const
return script()->strictModeCode;
}
inline JSObject *
JSFunction::callScope() const
{
JS_ASSERT(isInterpreted());
return getParent();
//return u.i.scope;
}
inline void
JSFunction::setJoinable()
{
@ -223,6 +231,49 @@ IsBuiltinFunctionConstructor(JSFunction *fun);
const Shape *
LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
inline JSFunction *
CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
bool ignoreSingletonClone = false)
{
JS_ASSERT(parent);
JSObject *proto;
if (!js_GetClassPrototype(cx, parent, JSProto_Function, &proto))
return NULL;
/*
* For attempts to clone functions at a function definition opcode or from
* a method barrier, don't perform the clone if the function has singleton
* type. CloneFunctionObject was called pessimistically, and we need to
* preserve the type's property that if it is singleton there is only a
* single object with its type in existence.
*/
if (ignoreSingletonClone && fun->hasSingletonType()) {
JS_ASSERT(fun->getProto() == proto);
fun->setParent(parent);
return fun;
}
return js_CloneFunctionObject(cx, fun, parent, proto);
}
inline JSFunction *
CloneFunctionObject(JSContext *cx, JSFunction *fun)
{
/*
* Variant which makes an exact clone of fun, preserving parent and proto.
* Calling the above version CloneFunctionObject(cx, fun, fun->getParent())
* is not equivalent: API clients, including XPConnect, can reparent
* objects so that fun->getGlobal() != fun->getProto()->getGlobal().
* See ReparentWrapperIfFound.
*/
JS_ASSERT(fun->getParent() && fun->getProto());
if (fun->hasSingletonType())
return fun;
return js_CloneFunctionObject(cx, fun, fun->getParent(), fun->getProto());
}
} /* namespace js */
#endif /* jsfuninlines_h___ */

Просмотреть файл

@ -711,7 +711,7 @@ ScanObject(GCMarker *gcmarker, JSObject *obj)
types::TypeObject *type = obj->typeFromGC();
PushMarkStack(gcmarker, type);
if (JSObject *parent = obj->getParent())
if (JSObject *parent = obj->getParentMaybeScope())
PushMarkStack(gcmarker, parent);
/*
@ -784,7 +784,7 @@ MarkChildren(JSTracer *trc, JSObject *obj)
MarkTypeObject(trc, obj->typeFromGC(), "type");
/* Trace universal (ops-independent) members. */
if (JSObject *parent = obj->getParent())
if (JSObject *parent = obj->getParentMaybeScope())
MarkObject(trc, *parent, "parent");
Class *clasp = obj->getClass();

Просмотреть файл

@ -5115,7 +5115,7 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
* the script is nested inside.
*/
while (!scope->isCall())
scope = scope->getParent();
scope = scope->scopeChain();
CallObject &call = scope->asCall();
@ -5148,7 +5148,7 @@ TypeScript::SetScope(JSContext *cx, JSScript *script, JSObject *scope)
if (!parent->ensureHasTypes(cx, parentFun))
return false;
if (!parent->types->hasScope()) {
if (!SetScope(cx, parent, scope->getParent()))
if (!SetScope(cx, parent, scope->scopeChain()))
return false;
parent->nesting()->activeCall = scope;
parent->nesting()->argArray = call.argArray();
@ -5243,7 +5243,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
JS_ASSERT(parent);
while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
scope = scope->getParent();
scope = scope->scopeChain();
if (scope != parent->nesting()->activeCall) {
parent->reentrantOuterFunction = true;
@ -5258,7 +5258,7 @@ CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
* parent.
*/
if (parent->nesting()->parent) {
scope = scope->getParent();
scope = scope->scopeChain();
script = parent;
goto restart;
}

Просмотреть файл

@ -300,7 +300,7 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
*/
limitClone = &fp->scopeChain();
while (limitClone->isWith())
limitClone = limitClone->getParent();
limitClone = limitClone->scopeChain();
JS_ASSERT(limitClone);
/*
@ -358,10 +358,10 @@ GetScopeChainFull(JSContext *cx, StackFrame *fp, JSObject *blockChain)
if (!clone)
return NULL;
newChild->setParent(clone);
newChild->setScopeChain(clone);
newChild = clone;
}
newChild->setParent(&fp->scopeChain());
newChild->setScopeChain(&fp->scopeChain());
/*
@ -1208,7 +1208,7 @@ LeaveWith(JSContext *cx)
JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
withobj->setPrivate(NULL);
cx->fp()->setScopeChainNoCallObj(*withobj->getParent());
cx->fp()->setScopeChainNoCallObj(*withobj->scopeChain());
}
bool
@ -2185,7 +2185,7 @@ BEGIN_CASE(JSOP_POPN)
JS_ASSERT_IF(obj,
OBJ_BLOCK_DEPTH(cx, obj) + OBJ_BLOCK_COUNT(cx, obj)
<= (size_t) (regs.sp - regs.fp()->base()));
for (obj = &regs.fp()->scopeChain(); obj; obj = obj->getParent()) {
for (obj = &regs.fp()->scopeChain(); obj; obj = obj->getParentOrScopeChain()) {
if (!obj->isBlock() || !obj->isWith())
continue;
if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp()))
@ -2683,7 +2683,7 @@ BEGIN_CASE(JSOP_BINDNAME)
* forms.
*/
obj = &regs.fp()->scopeChain();
if (!obj->getParent())
if (obj->isGlobal())
break;
PropertyCacheEntry *entry;
@ -4360,7 +4360,7 @@ BEGIN_CASE(JSOP_DEFFUN)
* windows, and user-defined JS functions precompiled and then shared among
* requests in server-side JS.
*/
if (obj->getParent() != obj2) {
if (obj->toFunction()->callScope() != obj2) {
obj = CloneFunctionObject(cx, fun, obj2, true);
if (!obj)
goto error;
@ -4487,7 +4487,7 @@ BEGIN_CASE(JSOP_DEFLOCALFUN)
if (!parent)
goto error;
if (obj->getParent() != parent) {
if (obj->toFunction()->callScope() != parent) {
#ifdef JS_TRACER
if (TRACE_RECORDER(cx))
AbortRecording(cx, "DEFLOCALFUN for closure");
@ -5472,13 +5472,13 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
*/
JSObject *obj2 = &regs.fp()->scopeChain();
while (obj2->isWith())
obj2 = obj2->getParent();
obj2 = obj2->scopeChain();
if (obj2->isBlock() &&
obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) {
JSObject *youngestProto = obj2->getProto();
JS_ASSERT(youngestProto->isStaticBlock());
JSObject *parent = obj;
while ((parent = parent->getParent()) != youngestProto)
while ((parent = parent->getParentOrScopeChain()) != youngestProto)
JS_ASSERT(parent);
}
#endif

Просмотреть файл

@ -957,7 +957,7 @@ static void
AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
{
#ifdef DEBUG
for (JSObject *o = &scopeobj; o; o = o->getParent()) {
for (JSObject *o = &scopeobj; o; o = o->getParentOrScopeChain()) {
if (JSObjectOp op = o->getClass()->ext.innerObject)
JS_ASSERT(op(cx, o) == o);
}
@ -3401,7 +3401,7 @@ with_ThisObject(JSContext *cx, JSObject *obj)
Class js::WithClass = {
"With",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(2) | JSCLASS_IS_ANONYMOUS,
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(3) | JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@ -3470,7 +3470,7 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
StackFrame *priv = js_FloatingFrameIfGenerator(cx, cx->fp());
obj->init(cx, type, parent, false);
obj->init(cx, type, parent->getGlobal(), false);
EmptyShape *emptyWithShape = EmptyShape::getEmptyWithShape(cx);
if (!emptyWithShape)
@ -3479,6 +3479,7 @@ js_NewWithObject(JSContext *cx, JSObject *proto, JSObject *parent, jsint depth)
obj->setInitialPropertyInfallible(emptyWithShape);
OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
obj->setScopeChain(parent);
obj->setPrivate(priv);
AutoObjectRooter tvr(cx, obj);
@ -3499,7 +3500,7 @@ js_NewBlockObject(JSContext *cx)
* Null obj's proto slot so that Object.prototype.* does not pollute block
* scopes and to give the block object its own scope.
*/
JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT2);
JSObject *blockObj = js_NewGCObject(cx, FINALIZE_OBJECT4);
if (!blockObj)
return NULL;
@ -3517,13 +3518,15 @@ js_NewBlockObject(JSContext *cx)
return blockObj;
}
static const uint32 BLOCK_RESERVED_SLOTS = 2;
JSObject *
js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
{
JS_ASSERT(proto->isStaticBlock());
size_t count = OBJ_BLOCK_COUNT(cx, proto);
gc::AllocKind kind = gc::GetGCObjectKind(count + 1);
gc::AllocKind kind = gc::GetGCObjectKind(count + BLOCK_RESERVED_SLOTS + 1);
TypeObject *type = proto->getNewType(cx);
if (!type)
@ -3539,7 +3542,7 @@ js_CloneBlockObject(JSContext *cx, JSObject *proto, StackFrame *fp)
if (!clone->initClonedBlock(cx, type, priv))
return NULL;
JS_ASSERT(clone->slotSpan() >= count + 1);
JS_ASSERT(clone->slotSpan() >= count + BLOCK_RESERVED_SLOTS);
clone->setSlot(JSSLOT_BLOCK_DEPTH, proto->getSlot(JSSLOT_BLOCK_DEPTH));
@ -3575,7 +3578,7 @@ js_PutBlockObject(JSContext *cx, JSBool normalUnwind)
/* We must clear the private slot even with errors. */
obj->setPrivate(NULL);
fp->setScopeChainNoCallObj(*obj->getParent());
fp->setScopeChainNoCallObj(*obj->scopeChain());
return normalUnwind;
}
@ -4148,7 +4151,9 @@ js_XDRBlockObject(JSXDRState *xdr, JSObject **objp)
Class js::BlockClass = {
"Block",
JSCLASS_HAS_PRIVATE | JSCLASS_HAS_RESERVED_SLOTS(1) | JSCLASS_IS_ANONYMOUS,
JSCLASS_HAS_PRIVATE |
JSCLASS_HAS_RESERVED_SLOTS(BLOCK_RESERVED_SLOTS) |
JSCLASS_IS_ANONYMOUS,
JS_PropertyStub, /* addProperty */
JS_PropertyStub, /* delProperty */
JS_PropertyStub, /* getProperty */
@ -4559,6 +4564,8 @@ JSObject::setInitialProperty(JSContext *cx, const js::Shape *shape)
shape_ = const_cast<js::Shape *>(shape);
updateSlotsForSpan(0, span);
JS_ASSERT_IF(isScope(), parent);
return true;
}
@ -4579,6 +4586,8 @@ JSObject::setInitialPropertyInfallible(const js::Shape *shape)
size_t span = shape->slotSpan();
if (span)
updateSlotsForSpan(0, span);
JS_ASSERT_IF(isScope(), parent);
}
bool
@ -4974,11 +4983,7 @@ js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
start = &fp->scopeChain();
if (start) {
/* Find the topmost object in the scope chain. */
do {
obj = start;
start = obj->getParent();
} while (start);
obj = start->getGlobal();
} else {
obj = cx->globalObject;
if (!obj) {
@ -5169,7 +5174,7 @@ PurgeProtoChain(JSContext *cx, JSObject *obj, jsid id)
if (!obj->shadowingShapeChange(cx, *shape))
return false;
if (!obj->getParent()) {
if (obj->isGlobal()) {
/*
* All scope chains end in a global object, so this will change
* the global shape. jstracer.cpp assumes that the global shape
@ -5198,7 +5203,7 @@ js_PurgeScopeChainHelper(JSContext *cx, JSObject *obj, jsid id)
* may gain such properties via eval introducing new vars; see bug 490364.
*/
if (obj->isCall()) {
while ((obj = obj->getParent()) != NULL) {
while ((obj = obj->getParentOrScopeChain()) != NULL) {
if (!PurgeProtoChain(cx, obj, id))
return false;
}
@ -5643,7 +5648,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, bool cacheResult, bool global,
/* Scan entries on the scope chain that we can cache across. */
entry = JS_NO_PROP_CACHE_FILL;
obj = scopeChain;
parent = obj->getParent();
parent = obj->getParentOrScopeChain();
for (scopeIndex = 0;
parent
? IsCacheableNonGlobalScope(obj)
@ -5689,7 +5694,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, bool cacheResult, bool global,
goto out;
}
obj = parent;
parent = obj->getParent();
parent = obj->getParentOrScopeChain();
}
for (;;) {
@ -5704,7 +5709,7 @@ js_FindPropertyHelper(JSContext *cx, jsid id, bool cacheResult, bool global,
* We conservatively assume that a resolve hook could mutate the scope
* chain during JSObject::lookupProperty. So we read parent here again.
*/
parent = obj->getParent();
parent = obj->getParentOrScopeChain();
if (!parent) {
pobj = NULL;
break;
@ -5738,7 +5743,6 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
* This function should not be called for a global object or from the
* trace and should have a valid cache entry for native scopeChain.
*/
JS_ASSERT(scopeChain->getParent());
JS_ASSERT(!JS_ON_TRACE(cx));
JSObject *obj = scopeChain;
@ -5753,7 +5757,7 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
* must not be passed a global object (i.e. one with null parent).
*/
for (int scopeIndex = 0;
!obj->getParent() || IsCacheableNonGlobalScope(obj);
obj->isGlobal() || IsCacheableNonGlobalScope(obj);
scopeIndex++) {
JSObject *pobj;
JSProperty *prop;
@ -5761,17 +5765,17 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
return NULL;
if (prop) {
if (!pobj->isNative()) {
JS_ASSERT(!obj->getParent());
JS_ASSERT(obj->isGlobal());
return obj;
}
JS_ASSERT_IF(obj->getParent(), pobj->getClass() == obj->getClass());
JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
DebugOnly<PropertyCacheEntry*> entry =
JS_PROPERTY_CACHE(cx).fill(cx, scopeChain, scopeIndex, pobj, (Shape *) prop);
JS_ASSERT(entry);
return obj;
}
JSObject *parent = obj->getParent();
JSObject *parent = obj->getParentOrScopeChain();
if (!parent)
return obj;
obj = parent;
@ -5791,11 +5795,11 @@ js_FindIdentifierBase(JSContext *cx, JSObject *scopeChain, jsid id)
* chain during JSObject::lookupProperty. So we must check if parent is
* not null here even if it wasn't before the lookup.
*/
JSObject *parent = obj->getParent();
JSObject *parent = obj->getParentOrScopeChain();
if (!parent)
break;
obj = parent;
} while (obj->getParent());
} while (obj->isScope());
return obj;
}
@ -7098,7 +7102,7 @@ GlobalObject *
JSObject::getGlobal() const
{
JSObject *obj = const_cast<JSObject *>(this);
while (JSObject *parent = obj->getParent())
while (JSObject *parent = obj->getParentMaybeScope())
obj = parent;
return obj->asGlobal();
}
@ -7397,7 +7401,7 @@ js_DumpObject(JSObject *obj)
fputc('\n', stderr);
fprintf(stderr, "parent ");
dumpValue(ObjectOrNullValue(obj->getParent()));
dumpValue(ObjectOrNullValue(obj->getParentMaybeScope()));
fputc('\n', stderr);
if (clasp->flags & JSCLASS_HAS_PRIVATE)

Просмотреть файл

@ -866,28 +866,31 @@ struct JSObject : js::gc::Cell
return type_->proto;
}
JSObject *getParent() const {
return parent;
}
void clearParent() {
parent = NULL;
}
void setParent(JSObject *newParent) {
#ifdef DEBUG
for (JSObject *obj = newParent; obj; obj = obj->getParent())
JS_ASSERT(obj != this);
#endif
setDelegateNullSafe(newParent);
parent = newParent;
}
inline JSObject *getParent() const;
inline void clearParent();
inline void setParent(JSObject *newParent);
JS_FRIEND_API(js::GlobalObject *) getGlobal() const;
inline bool isGlobal() const;
inline js::GlobalObject *asGlobal();
/*
* Information for non-global scope chain objects (call/with/etc.). All
* objects on a scope chain are either isScope() or isGlobal(). isScope()
* objects do not escape to script and only appear on scope chains.
*/
inline bool isScope() const;
inline JSObject *scopeChain() const;
inline void setScopeChain(JSObject *obj);
static inline size_t offsetOfScopeChain();
inline JSObject *getParentOrScopeChain() const;
inline JSObject *getParentMaybeScope() const;
static const uint32 SCOPE_CHAIN_SLOT = 0;
inline bool hasPrivate() const;
inline void *getPrivate() const;
inline void *getPrivate(size_t nfixed) const;
@ -1520,21 +1523,22 @@ class ValueArray {
* Block scope object macros. The slots reserved by BlockClass are:
*
* private StackFrame * active frame pointer or null
* JSSLOT_SCOPE_CHAIN JSObject * scope chain, as for other scopes
* JSSLOT_BLOCK_DEPTH int depth of block slots in frame
*
* After JSSLOT_BLOCK_DEPTH come one or more slots for the block locals.
*
* A With object is like a Block object, in that both have one reserved slot
* A With object is like a Block object, in that both have a reserved slot
* telling the stack depth of the relevant slots (the slot whose value is the
* object named in the with statement, the slots containing the block's local
* variables); and both have a private slot referring to the StackFrame in
* whose activation they were created (or null if the with or block object
* outlives the frame).
*/
static const uint32 JSSLOT_BLOCK_DEPTH = 0;
static const uint32 JSSLOT_BLOCK_DEPTH = 1;
static const uint32 JSSLOT_BLOCK_FIRST_FREE_SLOT = JSSLOT_BLOCK_DEPTH + 1;
static const uint32 JSSLOT_WITH_THIS = 1;
static const uint32 JSSLOT_WITH_THIS = 2;
#define OBJ_BLOCK_COUNT(cx,obj) \
(obj)->propertyCount()

Просмотреть файл

@ -315,6 +315,70 @@ JSObject::finalize(JSContext *cx, bool background)
finish(cx);
}
inline JSObject *
JSObject::getParent() const
{
JS_ASSERT(!isScope());
return parent;
}
inline void
JSObject::clearParent()
{
JS_ASSERT(!isScope());
parent = NULL;
}
inline void
JSObject::setParent(JSObject *newParent)
{
#ifdef DEBUG
JS_ASSERT_IF(!isNewborn(), !isScope());
for (JSObject *obj = newParent; obj; obj = obj->getParentOrScopeChain())
JS_ASSERT(obj != this);
#endif
setDelegateNullSafe(newParent);
parent = newParent;
}
inline bool
JSObject::isScope() const
{
return isCall() || isDeclEnv() || isClonedBlock() || isWith();
}
inline JSObject *
JSObject::scopeChain() const
{
JS_ASSERT(isScope());
return &getFixedSlot(0).toObject();
}
inline JSObject *
JSObject::getParentOrScopeChain() const
{
return isScope() ? scopeChain() : getParent();
}
inline JSObject *
JSObject::getParentMaybeScope() const
{
return parent;
}
inline void
JSObject::setScopeChain(JSObject *obj)
{
JS_ASSERT(isScope());
setFixedSlot(0, JS::ObjectValue(*obj));
}
/*static*/ inline size_t
JSObject::offsetOfScopeChain()
{
return getFixedSlotOffset(0);
}
/*
* Initializer for Call objects for functions and eval frames. Set class,
* parent, map, and shape, and allocate slots.
@ -326,13 +390,15 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent
if (!type)
return false;
init(cx, type, parent, false);
init(cx, type, parent->getGlobal(), false);
if (!setInitialProperty(cx, bindings.lastShape()))
return false;
JS_ASSERT(isCall());
JS_ASSERT(!inDictionaryMode());
setScopeChain(parent);
/*
* If |bindings| is for a function that has extensible parents, that means
* its Call should have its own shape; see js::BaseShape::extensibleParents.
@ -349,7 +415,7 @@ JSObject::initCall(JSContext *cx, const js::Bindings &bindings, JSObject *parent
inline bool
JSObject::initClonedBlock(JSContext *cx, js::types::TypeObject *type, js::StackFrame *frame)
{
init(cx, type, NULL, false);
init(cx, type, frame->scopeChain().getGlobal(), false);
if (!setInitialProperty(cx, getProto()->lastProperty()))
return false;
@ -382,7 +448,7 @@ JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp
JS_ASSERT(!fun->isClonedMethod());
JS_ASSERT(fun->isNullClosure());
fun = CloneFunctionObject(cx, fun);
fun = js::CloneFunctionObject(cx, fun);
if (!fun)
return NULL;
fun->setMethodObj(*this);

Просмотреть файл

@ -81,7 +81,7 @@ PropertyCache::fill(JSContext *cx, JSObject *obj, uintN scopeIndex, JSObject *po
JSObject *tmp = obj;
for (uintN i = 0; i != scopeIndex; i++)
tmp = tmp->getParent();
tmp = tmp->scopeChain();
uintN protoIndex = 0;
while (tmp != pobj) {
@ -223,7 +223,7 @@ PropertyCache::fullTest(JSContext *cx, jsbytecode *pc, JSObject **objp, JSObject
if (JOF_MODE(cs.format) == JOF_NAME) {
while (vindex & (PCINDEX_SCOPEMASK << PCINDEX_PROTOBITS)) {
tmp = pobj->getParent();
tmp = pobj->scopeChain();
if (!tmp || !tmp->isNative())
break;
pobj = tmp;

Просмотреть файл

@ -7460,7 +7460,7 @@ js_GetDefaultXMLNamespace(JSContext *cx, jsval *vp)
JSObject *scopeChain = GetScopeChain(cx);
obj = NULL;
for (tmp = scopeChain; tmp; tmp = tmp->getParent()) {
for (tmp = scopeChain; tmp; tmp = tmp->getParentOrScopeChain()) {
Class *clasp = tmp->getClass();
if (clasp == &BlockClass || clasp == &WithClass)
continue;
@ -7655,7 +7655,7 @@ js_FindXMLProperty(JSContext *cx, const Value &nameval, JSObject **objp, jsid *i
return JS_TRUE;
}
}
} while ((obj = obj->getParent()) != NULL);
} while ((obj = obj->getParentOrScopeChain()) != NULL);
JSAutoByteString printable;
JSString *str = ConvertQNameToString(cx, nameobj);

Просмотреть файл

@ -1360,13 +1360,6 @@ class ScopeNameCompiler : public PICStubCompiler
return disable("non-cacheable scope chain object");
JS_ASSERT(tobj->isNative());
if (tobj != scopeChain) {
/* scopeChain will never be NULL, but parents can be NULL. */
Jump j = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
if (!fails.append(j))
return error();
}
/* Guard on intervening shapes. */
masm.loadShape(pic.objReg, pic.shapeReg);
Jump j = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
@ -1375,10 +1368,10 @@ class ScopeNameCompiler : public PICStubCompiler
return error();
/* Load the next link in the scope chain. */
Address parent(pic.objReg, offsetof(JSObject, parent));
Address parent(pic.objReg, JSObject::offsetOfScopeChain());
masm.loadPtr(parent, pic.objReg);
tobj = tobj->getParent();
tobj = tobj->scopeChain();
}
if (tobj != getprop.holder)
@ -1657,7 +1650,7 @@ class ScopeNameCompiler : public PICStubCompiler
if (status != Lookup_Cacheable)
return status;
if (!obj->getParent())
if (obj->isGlobal())
return generateGlobalStub(obj);
return disable("scope object not handled yet");
@ -1757,20 +1750,17 @@ class BindNameCompiler : public PICStubCompiler
/* Walk up the scope chain. */
JSObject *tobj = scopeChain;
Address parent(pic.objReg, offsetof(JSObject, parent));
Address parent(pic.objReg, JSObject::offsetOfScopeChain());
while (tobj && tobj != obj) {
if (!IsCacheableNonGlobalScope(tobj))
return disable("non-cacheable obj in scope chain");
masm.loadPtr(parent, pic.objReg);
Jump nullTest = masm.branchTestPtr(Assembler::Zero, pic.objReg, pic.objReg);
if (!fails.append(nullTest))
return error();
masm.loadShape(pic.objReg, pic.shapeReg);
Jump shapeTest = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
ImmPtr(tobj->lastProperty()));
if (!fails.append(shapeTest))
return error();
tobj = tobj->getParent();
tobj = tobj->scopeChain();
}
if (tobj != obj)
return disable("indirect hit");
@ -1814,7 +1804,6 @@ class BindNameCompiler : public PICStubCompiler
JSObject *update()
{
JS_ASSERT(scopeChain->getParent());
RecompilationMonitor monitor(cx);
JSObject *obj = js_FindIdentifierBase(cx, scopeChain, ATOM_TO_JSID(atom));

Просмотреть файл

@ -88,7 +88,7 @@ stubs::BindName(VMFrame &f)
PropertyCacheEntry *entry;
/* Fast-path should have caught this. See comment in interpreter. */
JS_ASSERT(f.fp()->scopeChain().getParent());
JS_ASSERT(f.fp()->scopeChain().isScope());
JSAtom *atom;
JSObject *obj2;
@ -1886,13 +1886,13 @@ stubs::EnterBlock(VMFrame &f, JSObject *obj)
*/
JSObject *obj2 = &fp->scopeChain();
while (obj2->isWith())
obj2 = obj2->getParent();
obj2 = obj2->scopeChain();
if (obj2->isBlock() &&
obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
JSObject *youngestProto = obj2->getProto();
JS_ASSERT(youngestProto->isStaticBlock());
JSObject *parent = obj;
while ((parent = parent->getParent()) != youngestProto)
while ((parent = parent->getParentOrScopeChain()) != youngestProto)
JS_ASSERT(parent);
}
#endif

Просмотреть файл

@ -48,16 +48,20 @@ class CallObject : public ::JSObject
/*
* Reserved slot structure for Call objects:
*
* private - the stack frame corresponding to the Call object
* until js_PutCallObject or its on-trace analog
* is called, null thereafter
* JSSLOT_CALL_CALLEE - callee function for the stack frame, or null if
* the stack frame is for strict mode eval code
* JSSLOT_CALL_ARGUMENTS - arguments object for non-strict mode eval stack
* frames (not valid for strict mode eval frames)
* SCOPE_CHAIN_SLOT - The enclosing scope. This must come first, for
* JSObject::scopeParent.
* CALLEE_SLOT - Callee function for the stack frame, or null if
* the stack frame is for strict mode eval code.
* ARGUMENTS_SLOT - Arguments object for non-strict mode eval stack
* frames (not valid for strict mode eval frames).
* private - The stack frame corresponding to the Call object
* until js_PutCallObject or its on-trace analog
* is called, null thereafter.
*
* DeclEnv objects use SCOPE_CHAIN_SLOT and private in the same fashion.
*/
static const uint32 CALLEE_SLOT = 0;
static const uint32 ARGUMENTS_SLOT = 1;
static const uint32 CALLEE_SLOT = 1;
static const uint32 ARGUMENTS_SLOT = 2;
public:
/* Create a CallObject for the given callee function. */
@ -65,6 +69,7 @@ public:
create(JSContext *cx, JSScript *script, JSObject &scopeChain, JSObject *callee);
static const uint32 RESERVED_SLOTS = 3;
static const uint32 DECL_ENV_RESERVED_SLOTS = 1;
/* True if this is for a strict mode eval frame or for a function call. */
inline bool isForEval() const;

Просмотреть файл

@ -62,14 +62,39 @@ namespace js {
static inline bool
IsCacheableNonGlobalScope(JSObject *obj)
{
JS_ASSERT(obj->getParent());
bool cacheable = (obj->isCall() || obj->isBlock() || obj->isDeclEnv());
JS_ASSERT_IF(cacheable, !obj->getOps()->lookupProperty);
return cacheable;
}
inline JSObject &
StackFrame::scopeChain() const
{
JS_ASSERT_IF(!(flags_ & HAS_SCOPECHAIN), isFunctionFrame());
if (!(flags_ & HAS_SCOPECHAIN)) {
scopeChain_ = callee().getParent();
flags_ |= HAS_SCOPECHAIN;
}
return *scopeChain_;
}
inline JSObject &
StackFrame::varObj()
{
JSObject *obj = &scopeChain();
while (!obj->isVarObj())
obj = obj->getParentOrScopeChain();
return *obj;
}
inline JSCompartment *
StackFrame::compartment() const
{
JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment());
return scopeChain().compartment();
}
inline void
StackFrame::initPrev(JSContext *cx)
{
@ -352,10 +377,10 @@ StackFrame::setScopeChainNoCallObj(JSObject &obj)
if (hasCallObj()) {
JSObject *pobj = &obj;
while (pobj && pobj->getPrivate() != this)
pobj = pobj->getParent();
pobj = pobj->scopeChain();
JS_ASSERT(pobj);
} else {
for (JSObject *pobj = &obj; pobj; pobj = pobj->getParent())
for (JSObject *pobj = &obj; pobj->isScope(); pobj = pobj->scopeChain())
JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
}
}
@ -381,7 +406,7 @@ StackFrame::callObj() const
JSObject *pobj = &scopeChain();
while (JS_UNLIKELY(!pobj->isCall())) {
JS_ASSERT(IsCacheableNonGlobalScope(pobj) || pobj->isWith());
pobj = pobj->getParent();
pobj = pobj->scopeChain();
}
return pobj->asCall();
}
@ -454,7 +479,7 @@ StackFrame::markFunctionEpilogueDone()
*/
scopeChain_ = isFunctionFrame()
? callee().getParent()
: scopeChain_->getParent();
: scopeChain_->scopeChain();
flags_ &= ~HAS_CALL_OBJ;
}
}

Просмотреть файл

@ -151,7 +151,7 @@ StackFrame::stealFrameAndSlots(Value *vp, StackFrame *otherfp,
obj.setPrivate(this);
otherfp->flags_ &= ~HAS_CALL_OBJ;
if (js_IsNamedLambda(fun())) {
JSObject *env = obj.getParent();
JSObject *env = obj.scopeChain();
JS_ASSERT(env->isDeclEnv());
env->setPrivate(this);
}

Просмотреть файл

@ -817,14 +817,7 @@ class StackFrame
* !fp->hasCall() && fp->scopeChain().isCall()
*/
JSObject &scopeChain() const {
JS_ASSERT_IF(!(flags_ & HAS_SCOPECHAIN), isFunctionFrame());
if (!(flags_ & HAS_SCOPECHAIN)) {
scopeChain_ = callee().getParent();
flags_ |= HAS_SCOPECHAIN;
}
return *scopeChain_;
}
inline JSObject &scopeChain() const;
bool hasCallObj() const {
bool ret = !!(flags_ & HAS_CALL_OBJ);
@ -873,12 +866,7 @@ class StackFrame
* variables object to collect and discard the script's global variables.
*/
JSObject &varObj() {
JSObject *obj = &scopeChain();
while (!obj->isVarObj())
obj = obj->getParent();
return *obj;
}
inline JSObject &varObj();
/*
* Frame compartment
@ -887,10 +875,7 @@ class StackFrame
* compartment when the frame was pushed.
*/
JSCompartment *compartment() const {
JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment());
return scopeChain().compartment();
}
inline JSCompartment *compartment() const;
/*
* Imacropc