Address bug 638316 and bug 694247 review comments, r=luke.
authorBrian Hackett <bhackett1024@gmail.com>
Wed, 09 Nov 2011 09:52:59 -0800
changeset 81300 c2feba11f30fc909e30e36e88901673120c58bf7
parent 81299 493d52c0a104cc825590170e4ac2e336a904b3c4
child 81301 0c887d263ec6afbd2581c60e9b4582986c221b30
push idunknown
push userunknown
push dateunknown
reviewersluke
bugs638316, 694247
milestone10.0a1
Address bug 638316 and bug 694247 review comments, r=luke.
dom/base/nsJSUtils.cpp
js/src/jsapi.cpp
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/src/jsfun.cpp
js/src/jsfuninlines.h
js/src/jsgcmark.cpp
js/src/jsinfer.cpp
js/src/jsinterp.cpp
js/src/jsobj.cpp
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jspropertycache.cpp
js/src/jsscope.cpp
js/src/jsscope.h
js/src/jsscopeinlines.h
js/src/jsxml.cpp
js/src/methodjit/PolyIC.cpp
js/src/methodjit/StubCalls.cpp
js/src/vm/CallObject.cpp
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
js/xpconnect/src/dombindings.cpp
js/xpconnect/src/nsXPConnect.cpp
--- a/dom/base/nsJSUtils.cpp
+++ b/dom/base/nsJSUtils.cpp
@@ -43,17 +43,16 @@
  * invoked from the JavaScript code generated from IDL interfaces.
  * The goal of the utility functions is to cut down on the size of
  * the generated code itself.
  */
 
 #include "nsJSUtils.h"
 #include "jsapi.h"
 #include "jsdbgapi.h"
-#include "jsfriendapi.h"
 #include "prprf.h"
 #include "nsIScriptContext.h"
 #include "nsIScriptObjectOwner.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsIServiceManager.h"
 #include "nsIXPConnect.h"
 #include "nsCOMPtr.h"
 #include "nsContentUtils.h"
@@ -102,17 +101,17 @@ nsJSUtils::GetStaticScriptGlobal(JSConte
 {
   nsISupports* supports;
   JSClass* clazz;
   JSObject* glob = aObj; // starting point for search
 
   if (!glob)
     return nsnull;
 
-  glob = js::GetObjectGlobal(glob);
+  glob = JS_GetGlobalForObject(aContext, glob);
   NS_ABORT_IF_FALSE(glob, "Infallible returns null");
 
   clazz = JS_GET_CLASS(aContext, glob);
 
   if (!clazz ||
       !(clazz->flags & JSCLASS_HAS_PRIVATE) ||
       !(clazz->flags & JSCLASS_PRIVATE_IS_NSISUPPORTS) ||
       !(supports = (nsISupports*)::JS_GetPrivate(aContext, glob))) {
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3066,25 +3066,26 @@ JS_SetPrototype(JSContext *cx, JSObject 
     CHECK_REQUEST(cx);
     assertSameCompartment(cx, obj, proto);
     return SetProto(cx, obj, proto, JS_FALSE);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParent(JSContext *cx, JSObject *obj)
 {
+    JS_ASSERT(!obj->isInternalScope());
     assertSameCompartment(cx, obj);
     return obj->getParent();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetParent(JSContext *cx, JSObject *obj, JSObject *parent)
 {
     CHECK_REQUEST(cx);
-    JS_ASSERT(!obj->isScope());
+    JS_ASSERT(!obj->isInternalScope());
     JS_ASSERT(parent || !obj->getParent());
     assertSameCompartment(cx, obj, parent);
     return obj->setParent(cx, parent);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetConstructor(JSContext *cx, JSObject *proto)
 {
@@ -4352,17 +4353,17 @@ JS_CloneFunctionObject(JSContext *cx, JS
         JSObject *obj = parent;
         int skip = uva->vector[i].level();
         while (--skip > 0) {
             if (!obj) {
                 JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
                                      JSMSG_BAD_CLONE_FUNOBJ_SCOPE);
                 return NULL;
             }
-            obj = obj->getParentOrScopeChain();
+            obj = obj->scopeChain();
         }
 
         Value v;
         if (!obj->getGeneric(cx, r.front().propid(), &v))
             return NULL;
         clone->toFunction()->getFlatClosureUpvars()[i] = v;
     }
 
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -175,29 +175,23 @@ AutoSwitchCompartment::~AutoSwitchCompar
 {
     /* The old compartment may have been destroyed, so we can't use cx->setCompartment. */
     cx->compartment = oldCompartment;
 }
 
 JS_FRIEND_API(bool)
 js::IsScopeObject(const JSObject *obj)
 {
-    return obj->isScope();
+    return obj->isInternalScope();
 }
 
 JS_FRIEND_API(JSObject *)
 js::GetObjectParentMaybeScope(const JSObject *obj)
 {
-    return obj->getParentOrScopeChain();
-}
-
-JS_FRIEND_API(JSObject *)
-js::GetObjectGlobal(JSObject *obj)
-{
-    return obj->getGlobal();
+    return obj->scopeChain();
 }
 
 JS_FRIEND_API(uint32)
 js::GetObjectSlotSpan(const JSObject *obj)
 {
     return obj->slotSpan();
 }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -278,19 +278,16 @@ GetObjectParent(const JSObject *obj)
 {
     JS_ASSERT(!IsScopeObject(obj));
     return reinterpret_cast<const shadow::Object*>(obj)->shape->base->parent;
 }
 
 JS_FRIEND_API(JSObject *)
 GetObjectParentMaybeScope(const JSObject *obj);
 
-JS_FRIEND_API(JSObject *)
-GetObjectGlobal(JSObject *obj);
-
 JS_FRIEND_API(bool)
 IsOriginalScriptFunction(JSFunction *fun);
 
 JS_FRIEND_API(JSFunction *)
 DefineFunctionWithReserved(JSContext *cx, JSObject *obj, const char *name, JSNative call,
                            uintN nargs, uintN attrs);
 
 JS_FRIEND_API(JSFunction *)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -713,17 +713,17 @@ NewDeclEnvObject(JSContext *cx, StackFra
         return NULL;
 
     JSObject *envobj = js_NewGCObject(cx, FINALIZE_OBJECT2);
     if (!envobj)
         return NULL;
     envobj->initialize(emptyDeclEnvShape, type, NULL);
     envobj->setPrivate(fp);
 
-    if (!envobj->setScopeChain(cx, &fp->scopeChain()))
+    if (!envobj->setInternalScopeChain(cx, &fp->scopeChain()))
         return NULL;
 
     return envobj;
 }
 
 namespace js {
 
 CallObject *
@@ -856,17 +856,17 @@ js_PutCallObject(StackFrame *fp)
             if (nesting && script->isOuterFunction) {
                 nesting->argArray = callobj.argArray();
                 nesting->varArray = callobj.varArray();
             }
         }
 
         /* Clear private pointers to fp, which is about to go away. */
         if (js_IsNamedLambda(fun)) {
-            JSObject *env = callobj.scopeChain();
+            JSObject *env = callobj.internalScopeChain();
 
             JS_ASSERT(env->isDeclEnv());
             JS_ASSERT(env->getPrivate() == fp);
             env->setPrivate(NULL);
         }
     }
 
     callobj.setStackFrame(NULL);
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -323,18 +323,18 @@ IsBuiltinFunctionConstructor(JSFunction 
 const Shape *
 LookupInterpretedFunctionPrototype(JSContext *cx, JSObject *funobj);
 
 static inline JSObject *
 SkipScopeParent(JSObject *parent)
 {
     if (!parent)
         return NULL;
-    while (parent->isScope())
-        parent = parent->getParentMaybeScope();
+    while (parent->isInternalScope())
+        parent = parent->scopeChain();
     return parent;
 }
 
 inline JSFunction *
 CloneFunctionObject(JSContext *cx, JSFunction *fun, JSObject *parent,
                     gc::AllocKind kind = JSFunction::FinalizeKind)
 {
     JS_ASSERT(parent);
--- a/js/src/jsgcmark.cpp
+++ b/js/src/jsgcmark.cpp
@@ -689,17 +689,17 @@ PushMarkStack(GCMarker *gcmarker, JSStri
 static const uintN LARGE_OBJECT_CHUNK_SIZE = 2048;
 
 static void
 ScanObject(GCMarker *gcmarker, JSObject *obj)
 {
     types::TypeObject *type = obj->typeFromGC();
     PushMarkStack(gcmarker, type);
 
-    if (JSObject *parent = obj->getParentMaybeScope())
+    if (JSObject *parent = obj->getParent())
         PushMarkStack(gcmarker, parent);
 
     /*
      * Call the trace hook if necessary, and check for a newType on objects
      * which are not dense arrays (dense arrays have trace hooks).
      */
     Class *clasp = obj->getClass();
     if (clasp->trace) {
@@ -758,17 +758,17 @@ ScanLargeObject(GCMarker *gcmarker, Larg
 }
 
 void
 MarkChildren(JSTracer *trc, JSObject *obj)
 {
     MarkTypeObject(trc, obj->typeFromGC(), "type");
 
     /* Trace universal (ops-independent) members. */
-    if (JSObject *parent = obj->getParentMaybeScope())
+    if (JSObject *parent = obj->getParent())
         MarkObject(trc, *parent, "parent");
 
     Class *clasp = obj->getClass();
     if (clasp->trace)
         clasp->trace(trc, obj);
 
     MarkShape(trc, obj->lastProperty(), "shape");
 
--- a/js/src/jsinfer.cpp
+++ b/js/src/jsinfer.cpp
@@ -5157,17 +5157,17 @@ TypeScript::SetScope(JSContext *cx, JSSc
         return true;
     }
 
     /*
      * Walk the scope chain to the next call object, which will be the function
      * the script is nested inside.
      */
     while (!scope->isCall())
-        scope = scope->scopeChain();
+        scope = scope->internalScopeChain();
 
     CallObject &call = scope->asCall();
 
     /* The isInnerFunction test ensures there is no intervening strict eval call object. */
     JS_ASSERT(!call.isForEval());
 
     /* Don't track non-heavyweight parents, NAME ops won't reach into them. */
     JSFunction *parentFun = call.getCalleeFunction();
@@ -5190,17 +5190,17 @@ TypeScript::SetScope(JSContext *cx, JSSc
      * compartment and there may be inner function objects parented to an
      * activation of the outer function sticking around. In such cases, treat
      * the parent's call object as the most recent one, so that it is not
      * marked as reentrant.
      */
     if (!parent->ensureHasTypes(cx))
         return false;
     if (!parent->types->hasScope()) {
-        if (!SetScope(cx, parent, scope->scopeChain()))
+        if (!SetScope(cx, parent, scope->internalScopeChain()))
             return false;
         parent->nesting()->activeCall = scope;
         parent->nesting()->argArray = call.argArray();
         parent->nesting()->varArray = call.varArray();
     }
 
     JS_ASSERT(!script->types->nesting);
 
@@ -5285,32 +5285,32 @@ ClearActiveNesting(JSScript *start)
 static void
 CheckNestingParent(JSContext *cx, JSObject *scope, JSScript *script)
 {
   restart:
     JSScript *parent = script->nesting()->parent;
     JS_ASSERT(parent);
 
     while (!scope->isCall() || scope->asCall().getCalleeFunction()->script() != parent)
-        scope = scope->scopeChain();
+        scope = scope->internalScopeChain();
 
     if (scope != parent->nesting()->activeCall) {
         parent->reentrantOuterFunction = true;
         MarkTypeObjectFlags(cx, parent->function(), OBJECT_FLAG_REENTRANT_FUNCTION);
 
         /*
          * Continue checking parents to see if this is reentrant for them too.
          * We don't need to check this in for non-reentrant calls on the outer
          * function: when we entered any outer function to the immediate parent
          * we cleared the active call for its transitive children, so a
          * non-reentrant call on a child is also a non-reentrant call on the
          * parent.
          */
         if (parent->nesting()->parent) {
-            scope = scope->scopeChain();
+            scope = scope->internalScopeChain();
             script = parent;
             goto restart;
         }
     }
 }
 
 void
 NestingPrologue(JSContext *cx, StackFrame *fp)
--- a/js/src/jsinterp.cpp
+++ b/js/src/jsinterp.cpp
@@ -294,17 +294,17 @@ GetScopeChainFull(JSContext *cx, StackFr
         /*
          * scopeChain includes all blocks whose static scope we're within that
          * have already been cloned.  Find the innermost such block.  Its
          * prototype should appear on blockChain; we'll clone blockChain up
          * to, but not including, that prototype.
          */
         limitClone = &fp->scopeChain();
         while (limitClone->isWith())
-            limitClone = limitClone->scopeChain();
+            limitClone = limitClone->internalScopeChain();
         JS_ASSERT(limitClone);
 
         /*
          * It may seem like we don't know enough about limitClone to be able
          * to just grab its prototype as we do here, but it's actually okay.
          *
          * If limitClone is a block object belonging to this frame, then its
          * prototype is the innermost entry in blockChain that we have already
@@ -352,21 +352,21 @@ GetScopeChainFull(JSContext *cx, StackFr
         if (sharedBlock == limitBlock || !sharedBlock)
             break;
 
         /* As in the call above, we don't know the real parent yet.  */
         JSObject *clone = js_CloneBlockObject(cx, sharedBlock, fp);
         if (!clone)
             return NULL;
 
-        if (!newChild->setScopeChain(cx, clone))
+        if (!newChild->setInternalScopeChain(cx, clone))
             return NULL;
         newChild = clone;
     }
-    if (!newChild->setScopeChain(cx, &fp->scopeChain()))
+    if (!newChild->setInternalScopeChain(cx, &fp->scopeChain()))
         return NULL;
 
 
     /*
      * If we found a limit block belonging to this frame, then we should have
      * found it in blockChain.
      */
     JS_ASSERT_IF(limitBlock &&
@@ -1210,17 +1210,17 @@ LeaveWith(JSContext *cx)
 {
     JSObject *withobj;
 
     withobj = &cx->fp()->scopeChain();
     JS_ASSERT(withobj->getClass() == &WithClass);
     JS_ASSERT(withobj->getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()));
     JS_ASSERT(OBJ_BLOCK_DEPTH(cx, withobj) >= 0);
     withobj->setPrivate(NULL);
-    cx->fp()->setScopeChainNoCallObj(*withobj->scopeChain());
+    cx->fp()->setScopeChainNoCallObj(*withobj->internalScopeChain());
 }
 
 bool
 js::IsActiveWithOrBlock(JSContext *cx, JSObject &obj, int stackDepth)
 {
     return (obj.isWith() || obj.isBlock()) &&
            obj.getPrivate() == js_FloatingFrameIfGenerator(cx, cx->fp()) &&
            OBJ_BLOCK_DEPTH(cx, &obj) >= stackDepth;
@@ -2134,17 +2134,17 @@ BEGIN_CASE(JSOP_POPN)
 {
     regs.sp -= GET_UINT16(regs.pc);
 #ifdef DEBUG
     JS_ASSERT(regs.fp()->base() <= regs.sp);
     JSObject *obj = GetBlockChain(cx, regs.fp());
     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->getParentOrScopeChain()) {
+    for (obj = &regs.fp()->scopeChain(); obj; obj = obj->scopeChain()) {
         if (!obj->isBlock() || !obj->isWith())
             continue;
         if (obj->getPrivate() != js_FloatingFrameIfGenerator(cx, regs.fp()))
             break;
         JS_ASSERT(regs.fp()->base() + OBJ_BLOCK_DEPTH(cx, obj)
                   + (obj->isBlock() ? OBJ_BLOCK_COUNT(cx, obj) : 1)
                   <= regs.sp);
     }
@@ -5435,23 +5435,23 @@ BEGIN_CASE(JSOP_ENTERBLOCK)
      * The young end of fp->scopeChain may omit blocks if we haven't closed
      * over them, but if there are any closure blocks on fp->scopeChain, they'd
      * better be (clones of) ancestors of the block we're entering now;
      * anything else we should have popped off fp->scopeChain when we left its
      * static scope.
      */
     JSObject *obj2 = &regs.fp()->scopeChain();
     while (obj2->isWith())
-        obj2 = obj2->scopeChain();
+        obj2 = obj2->internalScopeChain();
     if (obj2->isBlock() &&
         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, regs.fp())) {
         JSObject *youngestProto = obj2->getProto();
         JS_ASSERT(youngestProto->isStaticBlock());
         JSObject *parent = obj;
-        while ((parent = parent->getParentOrScopeChain()) != youngestProto)
+        while ((parent = parent->scopeChain()) != youngestProto)
             JS_ASSERT(parent);
     }
 #endif
 }
 END_CASE(JSOP_ENTERBLOCK)
 
 BEGIN_CASE(JSOP_LEAVEBLOCKEXPR)
 BEGIN_CASE(JSOP_LEAVEBLOCK)
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -957,17 +957,17 @@ obj_valueOf(JSContext *cx, uintN argc, V
     return true;
 }
 
 /* We should be able to assert this for *any* fp->scopeChain(). */
 static void
 AssertInnerizedScopeChain(JSContext *cx, JSObject &scopeobj)
 {
 #ifdef DEBUG
-    for (JSObject *o = &scopeobj; o; o = o->getParentOrScopeChain()) {
+    for (JSObject *o = &scopeobj; o; o = o->scopeChain()) {
         if (JSObjectOp op = o->getClass()->ext.innerObject)
             JS_ASSERT(op(cx, o) == o);
     }
 #endif
 }
 
 #ifndef EVAL_CACHE_CHAIN_LIMIT
 # define EVAL_CACHE_CHAIN_LIMIT 4
@@ -3599,17 +3599,17 @@ js_NewWithObject(JSContext *cx, JSObject
         return NULL;
 
     obj = js_NewGCObject(cx, FINALIZE_OBJECT4);
     if (!obj)
         return NULL;
     obj->initialize(emptyWithShape, type, NULL);
     OBJ_SET_BLOCK_DEPTH(cx, obj, depth);
 
-    if (!obj->setScopeChain(cx, parent))
+    if (!obj->setInternalScopeChain(cx, parent))
         return NULL;
     obj->setPrivate(priv);
 
     AutoObjectRooter tvr(cx, obj);
     JSObject *thisp = proto->thisObject(cx);
     if (!thisp)
         return NULL;
 
@@ -3659,18 +3659,18 @@ js_CloneBlockObject(JSContext *cx, JSObj
         return NULL;
 
     clone->initialize(proto->lastProperty(), type, slots);
 
     StackFrame *priv = js_FloatingFrameIfGenerator(cx, fp);
 
     /* Set the parent if necessary, as for call objects. */
     JSObject *global = priv->scopeChain().getGlobal();
-    if (global != clone->getParentMaybeScope()) {
-        JS_ASSERT(clone->getParentMaybeScope() == NULL);
+    if (global != clone->getParent()) {
+        JS_ASSERT(clone->getParent() == NULL);
         if (!clone->setParent(cx, global))
             return NULL;
     }
 
     JS_ASSERT(!clone->inDictionaryMode());
     JS_ASSERT(clone->isClonedBlock());
     JS_ASSERT(clone->slotSpan() >= OBJ_BLOCK_COUNT(cx, proto) + BLOCK_RESERVED_SLOTS);
 
@@ -3706,17 +3706,17 @@ js_PutBlockObject(JSContext *cx, JSBool 
     if (normalUnwind) {
         uintN slot = JSSLOT_BLOCK_FIRST_FREE_SLOT;
         depth += fp->numFixed();
         obj->copySlotRange(slot, fp->slots() + depth, count);
     }
 
     /* We must clear the private slot even with errors. */
     obj->setPrivate(NULL);
-    fp->setScopeChainNoCallObj(*obj->scopeChain());
+    fp->setScopeChainNoCallObj(*obj->internalScopeChain());
     return normalUnwind;
 }
 
 static JSBool
 block_getProperty(JSContext *cx, JSObject *obj, jsid id, Value *vp)
 {
     /*
      * Block objects are never exposed to script, and the engine handles them
@@ -5298,17 +5298,17 @@ js_PurgeScopeChainHelper(JSContext *cx, 
 
     /*
      * We must purge the scope chain only for Call objects as they are the only
      * kind of cacheable non-global object that can gain properties after outer
      * properties with the same names have been cached or traced. Call objects
      * may gain such properties via eval introducing new vars; see bug 490364.
      */
     if (obj->isCall()) {
-        while ((obj = obj->getParentOrScopeChain()) != NULL) {
+        while ((obj = obj->scopeChain()) != NULL) {
             if (!PurgeProtoChain(cx, obj, id))
                 return false;
         }
     }
 
     return true;
 }
 
@@ -5743,17 +5743,17 @@ js_FindPropertyHelper(JSContext *cx, jsi
          * before the global object.
          */
         scopeChain = scopeChain->getGlobal();
     }
 
     /* Scan entries on the scope chain that we can cache across. */
     entry = JS_NO_PROP_CACHE_FILL;
     obj = scopeChain;
-    parent = obj->getParentOrScopeChain();
+    parent = obj->scopeChain();
     for (scopeIndex = 0;
          parent
          ? IsCacheableNonGlobalScope(obj)
          : !obj->getOps()->lookupProperty;
          ++scopeIndex) {
         if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
             return NULL;
 
@@ -5789,32 +5789,32 @@ js_FindPropertyHelper(JSContext *cx, jsi
             goto out;
         }
 
         if (!parent) {
             pobj = NULL;
             goto out;
         }
         obj = parent;
-        parent = obj->getParentOrScopeChain();
+        parent = obj->scopeChain();
     }
 
     for (;;) {
         if (!obj->lookupGeneric(cx, id, &pobj, &prop))
             return NULL;
         if (prop) {
             PCMETER(JS_PROPERTY_CACHE(cx).nofills++);
             goto out;
         }
 
         /*
          * We conservatively assume that a resolve hook could mutate the scope
          * chain during JSObject::lookupGeneric. So we read parent here again.
          */
-        parent = obj->getParentOrScopeChain();
+        parent = obj->scopeChain();
         if (!parent) {
             pobj = NULL;
             break;
         }
         obj = parent;
     }
 
   out:
@@ -5863,24 +5863,24 @@ js_FindIdentifierBase(JSContext *cx, JSO
         JSProperty *prop;
         if (!LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags, &pobj, &prop))
             return NULL;
         if (prop) {
             if (!pobj->isNative()) {
                 JS_ASSERT(obj->isGlobal());
                 return obj;
             }
-            JS_ASSERT_IF(obj->isScope(), pobj->getClass() == obj->getClass());
+            JS_ASSERT_IF(obj->isInternalScope(), 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->getParentOrScopeChain();
+        JSObject *parent = obj->scopeChain();
         if (!parent)
             return obj;
         obj = parent;
     }
 
     /* Loop until we find a property or reach the global object. */
     do {
         JSObject *pobj;
@@ -5890,17 +5890,17 @@ js_FindIdentifierBase(JSContext *cx, JSO
         if (prop)
             break;
 
         /*
          * We conservatively assume that a resolve hook could mutate the scope
          * chain during JSObject::lookupGeneric. So we must check if parent is
          * not null here even if it wasn't before the lookup.
          */
-        JSObject *parent = obj->getParentOrScopeChain();
+        JSObject *parent = obj->scopeChain();
         if (!parent)
             break;
         obj = parent;
     } while (!obj->isGlobal());
     return obj;
 }
 
 static JS_ALWAYS_INLINE JSBool
@@ -7466,17 +7466,17 @@ js_DumpObject(JSObject *obj)
         return;
     }
 
     fprintf(stderr, "proto ");
     dumpValue(ObjectOrNullValue(obj->getProto()));
     fputc('\n', stderr);
 
     fprintf(stderr, "parent ");
-    dumpValue(ObjectOrNullValue(obj->getParentMaybeScope()));
+    dumpValue(ObjectOrNullValue(obj->getParent()));
     fputc('\n', stderr);
 
     if (clasp->flags & JSCLASS_HAS_PRIVATE)
         fprintf(stderr, "private %p\n", obj->getPrivate());
 
     if (!obj->isNative())
         fprintf(stderr, "not native\n");
 
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -846,42 +846,73 @@ struct JSObject : js::gc::Cell
      * or the global object.
      */
     bool shouldSplicePrototype(JSContext *cx);
 
     JSObject * getProto() const {
         return type_->proto;
     }
 
+    /*
+     * Parents and scope chains.
+     *
+     * All script-accessible objects with a NULL parent are global objects,
+     * and all global objects have a NULL parent. Some builtin objects which
+     * are not script-accessible also have a NULL parent, such as parser
+     * created functions for non-compileAndGo scripts.
+     *
+     * Except for the non-script-accessible builtins, the global with which an
+     * object is associated can be reached by following parent links to that
+     * global (see getGlobal()).
+     *
+     * The scope chain of an object is the link in the search path when a
+     * script does a name lookup on a scope object. For JS internal scope
+     * objects --- Call, Block, DeclEnv and With --- the chain is stored in
+     * the first fixed slot of the object, and the object's parent is the
+     * associated global. For other scope objects, the chain is stored in the
+     * object's parent.
+     *
+     * In compileAndGo code, scope chains can contain only internal scope
+     * objects with a global object at the root as the scope of the outermost
+     * non-function script. In non-compileAndGo code, the scope of the
+     * outermost non-function script might not be a global object, and can have
+     * a mix of other objects above it before the global object is reached.
+     */
+
+    /* Access the parent link of an object. */
     inline JSObject *getParent() const;
     bool setParent(JSContext *cx, JSObject *newParent);
 
+    /* Get the scope chain of an arbitrary scope object. */
+    inline JSObject *scopeChain() const;
+
     inline bool isGlobal() const;
     inline js::GlobalObject *asGlobal();
+    inline js::GlobalObject *getGlobal() const;
 
-    inline js::GlobalObject *getGlobal() const;
+    inline bool isInternalScope() const;
+
+    /* Access the scope chain of an internal scope object. */
+    inline JSObject *internalScopeChain() const;
+    inline bool setInternalScopeChain(JSContext *cx, JSObject *obj);
+    static inline size_t offsetOfInternalScopeChain();
 
     /*
-     * 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.
+     * Access the scope chain of a static block object. These do not appear
+     * on scope chains but mirror their structure, and can have a NULL
+     * scope chain.
      */
-    inline bool isScope() const;
-    inline JSObject *scopeChain() const;
-    inline bool setScopeChain(JSContext *cx, JSObject *obj);
-
-    static inline size_t offsetOfScopeChain();
-
-    inline JSObject *getParentOrScopeChain() const;
-    inline JSObject *getParentMaybeScope() const;
     inline JSObject *getStaticBlockScopeChain() const;
     inline void setStaticBlockScopeChain(JSObject *obj);
 
+    /* Common fixed slot for the scope chain of internal scope objects. */
     static const uint32 SCOPE_CHAIN_SLOT = 0;
 
+    /* Private data accessors. */
+
     inline bool hasPrivate() const;
     inline void *getPrivate() const;
     inline void setPrivate(void *data);
 
     /* Access private data for an object with a known number of fixed slots. */
     inline void *getPrivate(size_t nfixed) const;
 
     /* N.B. Infallible: NULL means 'no principal', not an error. */
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -269,84 +269,66 @@ JSObject::finalize(JSContext *cx, bool b
     }
 
     finish(cx);
 }
 
 inline JSObject *
 JSObject::getParent() const
 {
-    JS_ASSERT(!isScope());
     return lastProperty()->getObjectParent();
 }
 
 inline bool
-JSObject::isScope() const
+JSObject::isInternalScope() const
 {
     return isCall() || isDeclEnv() || isBlock() || isWith();
 }
 
 inline JSObject *
-JSObject::scopeChain() const
+JSObject::internalScopeChain() const
+{
+    JS_ASSERT(isInternalScope());
+    return &getFixedSlot(SCOPE_CHAIN_SLOT).toObject();
+}
+
+inline bool
+JSObject::setInternalScopeChain(JSContext *cx, JSObject *obj)
 {
-    JS_ASSERT(isScope());
-    return &getFixedSlot(0).toObject();
+    JS_ASSERT(isInternalScope());
+    if (!obj->setDelegate(cx))
+        return false;
+    setFixedSlot(SCOPE_CHAIN_SLOT, JS::ObjectValue(*obj));
+    return true;
+}
+
+/*static*/ inline size_t
+JSObject::offsetOfInternalScopeChain()
+{
+    return getFixedSlotOffset(SCOPE_CHAIN_SLOT);
 }
 
 inline JSObject *
-JSObject::getParentOrScopeChain() const
+JSObject::scopeChain() const
 {
-    return isScope() ? scopeChain() : getParent();
+    return isInternalScope() ? internalScopeChain() : getParent();
 }
 
 inline JSObject *
 JSObject::getStaticBlockScopeChain() const
 {
-    /*
-     * Unlike other scope objects, static blocks not nested in one another
-     * do not have a scope chain.
-     */
     JS_ASSERT(isStaticBlock());
-    return getFixedSlot(0).isObject() ? &getFixedSlot(0).toObject() : NULL;
+    return getFixedSlot(SCOPE_CHAIN_SLOT).toObjectOrNull();
 }
 
 inline void
 JSObject::setStaticBlockScopeChain(JSObject *obj)
 {
-    /*
-     * Static blocks may have a block chain set and then overwritten with NULL.
-     * XXX bug 700799 this should not be able to happen.
-     */
     JS_ASSERT(isStaticBlock());
-    if (obj)
-        setFixedSlot(0, js::ObjectValue(*obj));
-    else
-        setFixedSlot(0, js::UndefinedValue());
-}
-
-inline JSObject *
-JSObject::getParentMaybeScope() const
-{
-    return lastProperty()->getObjectParent();
-}
-
-inline bool
-JSObject::setScopeChain(JSContext *cx, JSObject *obj)
-{
-    JS_ASSERT(isScope());
-    if (!obj->setDelegate(cx))
-        return false;
-    setFixedSlot(0, JS::ObjectValue(*obj));
-    return true;
-}
-
-/*static*/ inline size_t
-JSObject::offsetOfScopeChain()
-{
-    return getFixedSlotOffset(0);
+    setFixedSlot(SCOPE_CHAIN_SLOT, JS::ObjectOrNullValue(obj));
 }
 
 /*
  * Property read barrier for deferred cloning of compiler-created function
  * objects optimized as typically non-escaping, ad-hoc methods in obj.
  */
 inline const js::Shape *
 JSObject::methodReadBarrier(JSContext *cx, const js::Shape &shape, js::Value *vp)
@@ -448,21 +430,21 @@ JSObject::removeLastProperty(JSContext *
     JS_ASSERT(canRemoveLastProperty());
     JS_ALWAYS_TRUE(setLastProperty(cx, lastProperty()->previous()));
 }
 
 inline bool
 JSObject::canRemoveLastProperty()
 {
     /*
-     * Some information stored in shapes describes the object itself, and can
-     * be changed via replaceLastProperty without converting to a dictionary.
-     * Parent shapes in the property tree may not have this information set,
-     * and we need to ensure when unwinding properties that the per-object
-     * information is not accidentally reset.
+     * Check that the information about the object stored in the last
+     * property's base shape is consistent with that stored in the previous
+     * shape. If not consistent, then the last property cannot be removed as it
+     * will induce a change in the object itself, and the object must be
+     * converted to dictionary mode instead. See BaseShape comment in jsscope.h
      */
     JS_ASSERT(!inDictionaryMode());
     const js::Shape *previous = lastProperty()->previous();
     return previous->getObjectParent() == lastProperty()->getObjectParent()
         && previous->getObjectFlags() == lastProperty()->getObjectFlags();
 }
 
 inline js::Value
@@ -1276,17 +1258,17 @@ JSObject::isWrapper() const
 {
     return js::IsWrapper(this);
 }
 
 inline js::GlobalObject *
 JSObject::getGlobal() const
 {
     JSObject *obj = const_cast<JSObject *>(this);
-    while (JSObject *parent = obj->getParentMaybeScope())
+    while (JSObject *parent = obj->getParent())
         obj = parent;
     return obj->asGlobal();
 }
 
 static inline bool
 js_IsCallable(const js::Value &v)
 {
     return v.isObject() && v.toObject().isCallable();
--- a/js/src/jspropertycache.cpp
+++ b/js/src/jspropertycache.cpp
@@ -76,17 +76,17 @@ PropertyCache::fill(JSContext *cx, JSObj
      * before any running script might consult a parent-linked scope chain. If
      * this requirement is not satisfied, the fill in progress will never hit,
      * but scope shape tests ensure nothing malfunctions.
      */
     JS_ASSERT_IF(obj == pobj, scopeIndex == 0);
 
     JSObject *tmp = obj;
     for (uintN i = 0; i != scopeIndex; i++)
-        tmp = tmp->scopeChain();
+        tmp = tmp->internalScopeChain();
 
     uintN protoIndex = 0;
     while (tmp != pobj) {
         tmp = tmp->getProto();
 
         /*
          * We cannot cache properties coming from native objects behind
          * non-native ones on the prototype chain. The non-natives can
@@ -219,17 +219,17 @@ PropertyCache::fullTest(JSContext *cx, j
     /*
      * PropertyCache::test handles only the direct and immediate-prototype hit
      * cases. All others go here.
      */
     pobj = obj;
 
     if (JOF_MODE(cs.format) == JOF_NAME) {
         while (vindex & (PCINDEX_SCOPEMASK << PCINDEX_PROTOBITS)) {
-            tmp = pobj->scopeChain();
+            tmp = pobj->internalScopeChain();
             if (!tmp || !tmp->isNative())
                 break;
             pobj = tmp;
             vindex -= PCINDEX_PROTOSIZE;
         }
 
         *objp = pobj;
     }
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -336,22 +336,27 @@ Shape::getChildBinding(JSContext *cx, co
         }
 
         shape->setNumFixedSlots(nfixed - 1);
     }
     return shape;
 }
 
 /* static */ bool
-Shape::replaceLastProperty(JSContext *cx, const js::Shape &child, Shape **lastp)
+Shape::replaceLastProperty(JSContext *cx, const BaseShape &base, Shape **lastp)
 {
+    BaseShape *nbase = BaseShape::lookup(cx, base);
+    if (!nbase)
+        return false;
+
     Shape *shape = *lastp;
+    JS_ASSERT(!shape->inDictionary());
 
-    JS_ASSERT(!child.inDictionary());
-    JS_ASSERT(!shape->inDictionary());
+    Shape child(shape);
+    child.base_ = nbase;
 
     Shape *newShape;
     if (shape->parent) {
         newShape = JS_PROPERTY_TREE(cx).getChild(cx, shape->parent, child);
         if (!newShape)
             return false;
     } else {
         newShape = js_NewGCShape(cx);
@@ -640,17 +645,17 @@ JSObject::addPropertyInternal(JSContext 
     }
 
     if (!updateFlags(cx, id))
         return NULL;
 
     /* Find or create a property tree node labeled by our arguments. */
     Shape *shape;
     {
-        BaseShape base(getClass(), getParentMaybeScope(), lastProperty()->getObjectFlags(),
+        BaseShape base(getClass(), getParent(), lastProperty()->getObjectFlags(),
                        attrs, getter, setter);
         BaseShape *nbase = BaseShape::lookup(cx, base);
         if (!nbase)
             return NULL;
 
         Shape child(nbase, id, slot, numFixedSlots(), attrs, flags, shortid);
         shape = getChildProperty(cx, lastProperty(), child);
     }
@@ -739,17 +744,17 @@ JSObject::putProperty(JSContext *cx, jsi
      */
     bool hadSlot = shape->hasSlot();
     uint32 oldSlot = shape->maybeSlot();
     if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
         slot = oldSlot;
 
     UnownedBaseShape *nbase;
     {
-        BaseShape base(getClass(), getParentMaybeScope(), lastProperty()->getObjectFlags(),
+        BaseShape base(getClass(), getParent(), lastProperty()->getObjectFlags(),
                        attrs, getter, setter);
         nbase = BaseShape::lookup(cx, base);
         if (!nbase)
             return NULL;
     }
 
     /*
      * Now that we've possibly preserved slot, check whether all members match.
@@ -819,17 +824,17 @@ JSObject::putProperty(JSContext *cx, jsi
          * Updating the last property in a non-dictionary-mode object. Such
          * objects share their shapes via a tree rooted at a prototype
          * emptyShape, or perhaps a well-known compartment-wide singleton
          * emptyShape.
          *
          * If any shape in the tree has a property hashtable, it is shared and
          * immutable too, therefore we must not update *spp.
          */
-        BaseShape base(getClass(), getParentMaybeScope(), lastProperty()->getObjectFlags(),
+        BaseShape base(getClass(), getParent(), lastProperty()->getObjectFlags(),
                        attrs, getter, setter);
         BaseShape *nbase = BaseShape::lookup(cx, base);
         if (!nbase)
             return NULL;
 
         JS_ASSERT(shape == lastProperty());
 
         /* Find or create a property tree node labeled by our arguments. */
@@ -1155,24 +1160,18 @@ JSObject::setParent(JSContext *cx, JSObj
 /* static */ bool
 Shape::setObjectParent(JSContext *cx, JSObject *parent, Shape **listp)
 {
     if ((*listp)->getObjectParent() == parent)
         return true;
 
     BaseShape base(*(*listp)->base()->unowned());
     base.setParent(parent);
-    BaseShape *nbase = BaseShape::lookup(cx, base);
-    if (!nbase)
-        return false;
 
-    Shape child(*listp);
-    child.base_ = nbase;
-
-    return replaceLastProperty(cx, child, listp);
+    return replaceLastProperty(cx, base, listp);
 }
 
 bool
 JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
 {
     JS_ASSERT(isExtensible());
 
     if (props) {
@@ -1214,24 +1213,18 @@ JSObject::setFlag(JSContext *cx, /*BaseS
 /* static */ bool
 Shape::setObjectFlag(JSContext *cx, BaseShape::Flag flag, Shape **listp)
 {
     if ((*listp)->getObjectFlags() & flag)
         return true;
 
     BaseShape base(*(*listp)->base()->unowned());
     base.flags |= flag;
-    BaseShape *nbase = BaseShape::lookup(cx, base);
-    if (!nbase)
-        return false;
 
-    Shape child(*listp);
-    child.base_ = nbase;
-
-    return replaceLastProperty(cx, child, listp);
+    return replaceLastProperty(cx, base, listp);
 }
 
 /* static */ inline HashNumber
 JSCompartment::BaseShapeEntry::hash(const js::BaseShape *base)
 {
     JS_ASSERT(!base->isOwned());
 
     JSDHashNumber hash = base->flags;
@@ -1363,24 +1356,18 @@ BaseShape::finalize(JSContext *cx, bool 
 /* static */ bool
 Shape::setExtensibleParents(JSContext *cx, Shape **listp)
 {
     Shape *shape = *listp;
     JS_ASSERT(!shape->inDictionary());
 
     BaseShape base(*shape->base()->unowned());
     base.flags |= BaseShape::EXTENSIBLE_PARENTS;
-    BaseShape *nbase = BaseShape::lookup(cx, base);
-    if (!nbase)
-        return NULL;
 
-    Shape child(shape);
-    child.base_ = nbase;
-
-    return replaceLastProperty(cx, child, listp);
+    return replaceLastProperty(cx, base, listp);
 }
 
 bool
 Bindings::setExtensibleParents(JSContext *cx)
 {
     if (!ensureShape(cx))
         return false;
     return Shape::setExtensibleParents(cx, &lastBinding);
--- a/js/src/jsscope.h
+++ b/js/src/jsscope.h
@@ -323,16 +323,26 @@ class PropertyTree;
  *
  * Unowned Shape, Owned BaseShape:
  *
  *     Property in the property tree which has a property table.
  *
  * Unowned Shape, Unowned BaseShape:
  *
  *     Property in the property tree which does not have a property table.
+ *
+ * BaseShapes additionally encode some information about the referring object
+ * itself. This includes the object's class, parent and various flags that may
+ * be set for the object. Except for the class, this information is mutable and
+ * may change when the object has an established property lineage. On such
+ * changes the entire property lineage is not updated, but rather only the
+ * last property (and its base shape). This works because only the object's
+ * last property is used to query information about the object. Care must be
+ * taken to call JSObject::canRemoveLastProperty when unwinding an object to
+ * an earlier property, however.
  */
 
 class UnownedBaseShape;
 
 class BaseShape : public js::gc::Cell
 {
   public:
     friend struct Shape;
@@ -560,18 +570,18 @@ struct Shape : public js::gc::Cell
 
     inline void removeFromDictionary(JSObject *obj);
     inline void insertIntoDictionary(js::Shape **dictp);
 
     inline void initDictionaryShape(const js::Shape &child, js::Shape **dictp);
 
     js::Shape *getChildBinding(JSContext *cx, const js::Shape &child, js::Shape **lastBinding);
 
-    /* Replace the last shape in a non-dictionary lineage with child. */
-    static bool replaceLastProperty(JSContext *cx, const js::Shape &child, Shape **lastp);
+    /* Replace the base shape of the last shape in a non-dictionary lineage with base. */
+    static bool replaceLastProperty(JSContext *cx, const BaseShape &base, Shape **lastp);
 
     bool hashify(JSContext *cx);
     void handoffTableTo(Shape *newShape);
 
     void setParent(js::Shape *p) {
         JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
                      p->maybeSlot() <= maybeSlot());
         JS_ASSERT_IF(p && !inDictionary(),
--- a/js/src/jsscopeinlines.h
+++ b/js/src/jsscopeinlines.h
@@ -170,17 +170,17 @@ BaseShape::adoptUnowned(UnownedBaseShape
     JS_ASSERT(isOwned());
 
     JSObject *parent = this->parent;
     uint32 flags = (this->flags & OBJECT_FLAG_MASK);
 
     uint32 span = slotSpan();
     PropertyTable *table = &this->table();
 
-    *this = *static_cast<BaseShape *>(other);
+    *this = *other;
     setOwned(other);
     this->parent = parent;
     this->flags |= flags;
     setTable(table);
     setSlotSpan(span);
 }
 
 inline
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -7543,17 +7543,17 @@ js_GetDefaultXMLNamespace(JSContext *cx,
     JSObject *ns, *obj, *tmp;
     jsval v;
 
     JSObject *scopeChain = GetCurrentScopeChain(cx);
     if (!scopeChain)
         return false;
 
     obj = NULL;
-    for (tmp = scopeChain; tmp; tmp = tmp->getParentOrScopeChain()) {
+    for (tmp = scopeChain; tmp; tmp = tmp->scopeChain()) {
         Class *clasp = tmp->getClass();
         if (clasp == &BlockClass || clasp == &WithClass)
             continue;
         if (!tmp->getSpecial(cx, SpecialId::defaultXMLNamespace(), &v))
             return JS_FALSE;
         if (!JSVAL_IS_PRIMITIVE(v)) {
             *vp = v;
             return JS_TRUE;
@@ -7741,17 +7741,17 @@ js_FindXMLProperty(JSContext *cx, const 
             if (!target->lookupGeneric(cx, funid, &pobj, &prop))
                 return JS_FALSE;
             if (prop) {
                 *idp = funid;
                 *objp = target;
                 return JS_TRUE;
             }
         }
-    } while ((obj = obj->getParentOrScopeChain()) != NULL);
+    } while ((obj = obj->scopeChain()) != NULL);
 
     JSAutoByteString printable;
     JSString *str = ConvertQNameToString(cx, nameobj);
     if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                      JSMSG_UNDEFINED_XML_NAME, printable.ptr());
     }
     return JS_FALSE;
--- a/js/src/methodjit/PolyIC.cpp
+++ b/js/src/methodjit/PolyIC.cpp
@@ -1372,20 +1372,20 @@ class ScopeNameCompiler : public PICStub
             /* Guard on intervening shapes. */
             masm.loadShape(pic.objReg, pic.shapeReg);
             Jump j = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                     ImmPtr(tobj->lastProperty()));
             if (!fails.append(j))
                 return error();
 
             /* Load the next link in the scope chain. */
-            Address parent(pic.objReg, JSObject::offsetOfScopeChain());
+            Address parent(pic.objReg, JSObject::offsetOfInternalScopeChain());
             masm.loadPayload(parent, pic.objReg);
 
-            tobj = tobj->scopeChain();
+            tobj = tobj->internalScopeChain();
         }
 
         if (tobj != getprop.holder)
             return disable("scope chain walk terminated early");
 
         return Lookup_Cacheable;
     }
 
@@ -1754,27 +1754,27 @@ class BindNameCompiler : public PICStubC
         /* Guard on the shape of the scope chain. */
         masm.loadPtr(Address(JSFrameReg, StackFrame::offsetOfScopeChain()), pic.objReg);
         masm.loadShape(pic.objReg, pic.shapeReg);
         Jump firstShape = masm.branchPtr(Assembler::NotEqual, pic.shapeReg,
                                          ImmPtr(scopeChain->lastProperty()));
 
         /* Walk up the scope chain. */
         JSObject *tobj = scopeChain;
-        Address parent(pic.objReg, JSObject::offsetOfScopeChain());
+        Address parent(pic.objReg, JSObject::offsetOfInternalScopeChain());
         while (tobj && tobj != obj) {
             if (!IsCacheableNonGlobalScope(tobj))
                 return disable("non-cacheable obj in scope chain");
             masm.loadPayload(parent, pic.objReg);
             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->scopeChain();
+            tobj = tobj->internalScopeChain();
         }
         if (tobj != obj)
             return disable("indirect hit");
 
         Jump done = masm.jump();
 
         // All failures flow to here, so there is a common point to patch.
         for (Jump *pj = fails.begin(); pj != fails.end(); ++pj)
--- a/js/src/methodjit/StubCalls.cpp
+++ b/js/src/methodjit/StubCalls.cpp
@@ -1854,23 +1854,23 @@ stubs::EnterBlock(VMFrame &f, JSObject *
      * The young end of fp->scopeChain() may omit blocks if we haven't closed
      * over them, but if there are any closure blocks on fp->scopeChain(), they'd
      * better be (clones of) ancestors of the block we're entering now;
      * anything else we should have popped off fp->scopeChain() when we left its
      * static scope.
      */
     JSObject *obj2 = &fp->scopeChain();
     while (obj2->isWith())
-        obj2 = obj2->scopeChain();
+        obj2 = obj2->internalScopeChain();
     if (obj2->isBlock() &&
         obj2->getPrivate() == js_FloatingFrameIfGenerator(cx, fp)) {
         JSObject *youngestProto = obj2->getProto();
         JS_ASSERT(youngestProto->isStaticBlock());
         JSObject *parent = obj;
-        while ((parent = parent->getParentOrScopeChain()) != youngestProto)
+        while ((parent = parent->scopeChain()) != youngestProto)
             JS_ASSERT(parent);
     }
 #endif
 }
 
 void JS_FASTCALL
 stubs::LeaveBlock(VMFrame &f, JSObject *blockChain)
 {
--- a/js/src/vm/CallObject.cpp
+++ b/js/src/vm/CallObject.cpp
@@ -72,18 +72,18 @@ CallObject::create(JSContext *cx, JSScri
     obj->initialize(bindings.lastShape(), type, slots);
 
     /*
      * Update the parent for bindings associated with non-compileAndGo scripts,
      * whose call objects do not have a consistent global variable and need
      * to be updated dynamically.
      */
     JSObject *global = scopeChain.getGlobal();
-    if (global != obj->getParentMaybeScope()) {
-        JS_ASSERT(obj->getParentMaybeScope() == NULL);
+    if (global != obj->getParent()) {
+        JS_ASSERT(obj->getParent() == NULL);
         if (!obj->setParent(cx, global))
             return NULL;
     }
 
 #ifdef DEBUG
     for (Shape::Range r = obj->lastProperty(); !r.empty(); r.popFront()) {
         const Shape &s = r.front();
         if (s.hasSlot()) {
@@ -91,17 +91,17 @@ CallObject::create(JSContext *cx, JSScri
             break;
         }
     }
 #endif
 
     JS_ASSERT(obj->isCall());
     JS_ASSERT(!obj->inDictionaryMode());
 
-    if (!obj->setScopeChain(cx, &scopeChain))
+    if (!obj->setInternalScopeChain(cx, &scopeChain))
         return NULL;
 
     /*
      * If |bindings| is for a function that has extensible parents, that means
      * its Call should have its own shape; see js::BaseShape::extensibleParents.
      */
     if (obj->lastProperty()->extensibleParents() && !obj->generateOwnShape(cx))
         return NULL;
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -79,17 +79,17 @@ StackFrame::scopeChain() const
     return *scopeChain_;
 }
 
 inline JSObject &
 StackFrame::varObj()
 {
     JSObject *obj = &scopeChain();
     while (!obj->isVarObj())
-        obj = obj->getParentOrScopeChain();
+        obj = obj->getParent();
     return *obj;
 }
 
 inline JSCompartment *
 StackFrame::compartment() const
 {
     JS_ASSERT_IF(isScriptFrame(), scopeChain().compartment() == script()->compartment());
     return scopeChain().compartment();
@@ -371,20 +371,20 @@ inline void
 StackFrame::setScopeChainNoCallObj(JSObject &obj)
 {
 #ifdef DEBUG
     JS_ASSERT(&obj != NULL);
     if (&obj != sInvalidScopeChain) {
         if (hasCallObj()) {
             JSObject *pobj = &obj;
             while (pobj && pobj->getPrivate() != this)
-                pobj = pobj->getParentOrScopeChain();
+                pobj = pobj->scopeChain();
             JS_ASSERT(pobj);
         } else {
-            for (JSObject *pobj = &obj; pobj->isScope(); pobj = pobj->getParentOrScopeChain())
+            for (JSObject *pobj = &obj; pobj->isInternalScope(); pobj = pobj->scopeChain())
                 JS_ASSERT_IF(pobj->isCall(), pobj->getPrivate() != this);
         }
     }
 #endif
     scopeChain_ = &obj;
     flags_ |= HAS_SCOPECHAIN;
 }
 
@@ -399,17 +399,17 @@ StackFrame::setScopeChainWithOwnCallObj(
 
 inline CallObject &
 StackFrame::callObj() const
 {
     JS_ASSERT_IF(isNonEvalFunctionFrame() || isStrictEvalFrame(), hasCallObj());
 
     JSObject *pobj = &scopeChain();
     while (JS_UNLIKELY(!pobj->isCall()))
-        pobj = pobj->getParentOrScopeChain();
+        pobj = pobj->scopeChain();
     return pobj->asCall();
 }
 
 inline bool
 StackFrame::maintainNestingState() const
 {
     /*
      * Whether to invoke the nesting epilogue/prologue to maintain active
@@ -471,17 +471,17 @@ StackFrame::markFunctionEpilogueDone()
              * For function frames, the call object may or may not have have an
              * enclosing DeclEnv object, so we use the callee's parent, since
              * it was the initial scope chain. For global (strict) eval frames,
              * there is no callee, but the call object's parent is the initial
              * scope chain.
              */
             scopeChain_ = isFunctionFrame()
                           ? callee().toFunction()->environment()
-                          : scopeChain_->scopeChain();
+                          : scopeChain_->internalScopeChain();
             flags_ &= ~HAS_CALL_OBJ;
         }
     }
 
     /*
      * For outer/inner function frames, undo the active frame balancing so that
      * when we redo it in the epilogue we get the right final value. The other
      * nesting epilogue changes (update active args/vars) are idempotent.
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -146,17 +146,17 @@ StackFrame::stealFrameAndSlots(Value *vp
      * Block and With objects are done indirectly through 'liveFrame'. See
      * js_LiveFrameToFloating comment in jsiter.h.
      */
     if (hasCallObj()) {
         JSObject &obj = callObj();
         obj.setPrivate(this);
         otherfp->flags_ &= ~HAS_CALL_OBJ;
         if (js_IsNamedLambda(fun())) {
-            JSObject *env = obj.scopeChain();
+            JSObject *env = obj.internalScopeChain();
             JS_ASSERT(env->isDeclEnv());
             env->setPrivate(this);
         }
     }
     if (hasArgsObj()) {
         ArgumentsObject &argsobj = argsObj();
         if (argsobj.isNormalArguments())
             argsobj.setStackFrame(this);
--- a/js/xpconnect/src/dombindings.cpp
+++ b/js/xpconnect/src/dombindings.cpp
@@ -268,17 +268,17 @@ ListBase<LC>::setProtoShape(JSObject *ob
     js::SetProxyExtra(obj, JSPROXYSLOT_PROTOSHAPE, PrivateValue(shape));
 }
 
 template<class LC>
 bool
 ListBase<LC>::instanceIsListObject(JSContext *cx, JSObject *obj, JSObject *callee)
 {
     if (XPCWrapper::IsSecurityWrapper(obj)) {
-        if (callee && js::GetObjectGlobal(obj) == js::GetObjectGlobal(callee)) {
+        if (callee && JS_GetGlobalForObject(cx, obj) == JS_GetGlobalForObject(cx, callee)) {
             obj = js::UnwrapObject(obj);
         } else {
             obj = XPCWrapper::Unwrap(cx, obj);
             if (!obj)
                 return Throw(cx, NS_ERROR_XPC_SECURITY_MANAGER_VETO);
         }
     }
 
--- a/js/xpconnect/src/nsXPConnect.cpp
+++ b/js/xpconnect/src/nsXPConnect.cpp
@@ -803,17 +803,17 @@ nsXPConnect::Traverse(void *p, nsCycleCo
                 "BaseShape",
                 "TypeObject",
             };
             JS_STATIC_ASSERT(NS_ARRAY_LENGTH(trace_types) == JSTRACE_LAST + 1);
             JS_snprintf(name, sizeof(name), "JS %s", trace_types[traceKind]);
         }
 
         if(traceKind == JSTRACE_OBJECT) {
-            JSObject *global = js::GetObjectGlobal(static_cast<JSObject*>(p));
+            JSObject *global = JS_GetGlobalForObject(NULL, static_cast<JSObject*>(p));
             char fullname[100];
             JS_snprintf(fullname, sizeof(fullname),
                         "%s (global=%p)", name, global);
             cb.DescribeGCedNode(isMarked, sizeof(JSObject), fullname);
         } else {
             cb.DescribeGCedNode(isMarked, sizeof(JSObject), name);
         }
     } else {