Bug 880041 (part 18) - Use JSObject::{is,as} for {,Debug}ScopeObject. r=luke.
authorNicholas Nethercote <nnethercote@mozilla.com>
Sun, 16 Jun 2013 22:30:58 -0700
changeset 136093 0f3cacf6ba53c3b06cfcd01f690cdb75604f8e68
parent 136092 b35dcd7b09858ebd641130f125fdd2552099bb8b
child 136094 20542fdcbe71adbe0dfd5ea0c5bf69055e4ec5b8
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersluke
bugs880041
milestone24.0a1
Bug 880041 (part 18) - Use JSObject::{is,as} for {,Debug}ScopeObject. r=luke.
js/src/frontend/BytecodeCompiler.cpp
js/src/ion/BaselineFrame-inl.h
js/src/ion/BaselineIC.cpp
js/src/ion/IonCaches.cpp
js/src/jsapi.cpp
js/src/jsdbgapi.cpp
js/src/jsfriendapi.cpp
js/src/jsfuninlines.h
js/src/jsobj.h
js/src/jsobjinlines.h
js/src/jsscript.cpp
js/src/jsstr.cpp
js/src/vm/Debugger.cpp
js/src/vm/Interpreter.cpp
js/src/vm/ScopeObject-inl.h
js/src/vm/ScopeObject.cpp
js/src/vm/ScopeObject.h
js/src/vm/Stack-inl.h
js/src/vm/Stack.cpp
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -99,17 +99,17 @@ MaybeCheckEvalFreeVariables(JSContext *c
     }
 
     // If the eval'ed script contains any debugger statement, force construction
     // of arguments objects for the caller script and any other scripts it is
     // transitively nested inside. The debugger can access any variable on the
     // scope chain.
     if (pc.sc->hasDebuggerStatement()) {
         RootedObject scope(cx, scopeChain);
-        while (scope->isScope() || scope->isDebugScope()) {
+        while (scope->is<ScopeObject>() || scope->is<DebugScopeObject>()) {
             if (scope->is<CallObject>() && !scope->as<CallObject>().isForEval()) {
                 RootedScript script(cx, scope->as<CallObject>().callee().nonLazyScript());
                 if (script->argumentsHasVarBinding()) {
                     if (!JSScript::argumentsOptimizationFailed(cx, script))
                         return false;
                 }
             }
             scope = scope->enclosingScope();
--- a/js/src/ion/BaselineFrame-inl.h
+++ b/js/src/ion/BaselineFrame-inl.h
@@ -24,17 +24,17 @@ BaselineFrame::pushOnScopeChain(ScopeObj
     JS_ASSERT(*scopeChain() == scope.enclosingScope() ||
               *scopeChain() == scope.as<CallObject>().enclosingScope().as<DeclEnvObject>().enclosingScope());
     scopeChain_ = &scope;
 }
 
 inline void
 BaselineFrame::popOffScopeChain()
 {
-    scopeChain_ = &scopeChain_->asScope().enclosingScope();
+    scopeChain_ = &scopeChain_->as<ScopeObject>().enclosingScope();
 }
 
 inline bool
 BaselineFrame::pushBlock(JSContext *cx, Handle<StaticBlockObject *> block)
 {
     JS_ASSERT_IF(hasBlockChain(), blockChain() == *block->enclosingBlock());
 
     if (block->needsClone()) {
--- a/js/src/ion/BaselineIC.cpp
+++ b/js/src/ion/BaselineIC.cpp
@@ -4828,17 +4828,17 @@ TryAttachScopeNameStub(JSContext *cx, Ha
 
         if (scopeChain->isGlobal()) {
             shape = scopeChain->nativeLookup(cx, id);
             if (shape)
                 break;
             return true;
         }
 
-        if (!scopeChain->isScope() || scopeChain->is<WithObject>())
+        if (!scopeChain->is<ScopeObject>() || scopeChain->is<WithObject>())
             return true;
 
         // Check for an 'own' property on the scope. There is no need to
         // check the prototype as non-with scopes do not inherit properties
         // from any prototype.
         shape = scopeChain->nativeLookup(cx, id);
         if (shape)
             break;
@@ -6297,17 +6297,17 @@ DoSetPropFallback(JSContext *cx, Baselin
     if (op == JSOP_INITPROP && name != cx->names().proto) {
         JS_ASSERT(obj->isObject());
         if (!DefineNativeProperty(cx, obj, id, rhs, NULL, NULL, JSPROP_ENUMERATE, 0, 0, 0))
             return false;
     } else if (op == JSOP_SETNAME || op == JSOP_SETGNAME) {
         if (!SetNameOperation(cx, script, pc, obj, rhs))
             return false;
     } else if (op == JSOP_SETALIASEDVAR) {
-        obj->asScope().setAliasedVar(cx, pc, name, rhs);
+        obj->as<ScopeObject>().setAliasedVar(cx, pc, name, rhs);
     } else if (script->strict) {
         if (!js::SetProperty<true>(cx, obj, id, rhs))
             return false;
     } else {
         if (!js::SetProperty<false>(cx, obj, id, rhs))
             return false;
     }
 
--- a/js/src/ion/IonCaches.cpp
+++ b/js/src/ion/IonCaches.cpp
@@ -2768,17 +2768,17 @@ GenerateScopeChainGuards(MacroAssembler 
     while (true) {
         JS_ASSERT(IsCacheableNonGlobalScope(tobj) || tobj->isGlobal());
 
         GenerateScopeChainGuard(masm, tobj, outputReg, NULL, failures);
         if (tobj == holder)
             break;
 
         // Load the next link.
-        tobj = &tobj->asScope().enclosingScope();
+        tobj = &tobj->as<ScopeObject>().enclosingScope();
         masm.extractObject(Address(outputReg, ScopeObject::offsetOfEnclosingScope()), outputReg);
     }
 }
 
 bool
 BindNameIC::attachNonGlobal(JSContext *cx, IonScript *ion, JSObject *scopeChain, JSObject *holder)
 {
     JS_ASSERT(IsCacheableNonGlobalScope(scopeChain));
@@ -2789,17 +2789,17 @@ BindNameIC::attachNonGlobal(JSContext *c
     // Guard on the shape of the scope chain.
     Label failures;
     attacher.branchNextStubOrLabel(masm, Assembler::NotEqual,
                                    Address(scopeChainReg(), JSObject::offsetOfShape()),
                                    ImmGCPtr(scopeChain->lastProperty()),
                                    holder != scopeChain ? &failures : NULL);
 
     if (holder != scopeChain) {
-        JSObject *parent = &scopeChain->asScope().enclosingScope();
+        JSObject *parent = &scopeChain->as<ScopeObject>().enclosingScope();
         masm.extractObject(Address(scopeChainReg(), ScopeObject::offsetOfEnclosingScope()), outputReg());
 
         GenerateScopeChainGuards(masm, parent, holder, outputReg(), &failures);
     } else {
         masm.movePtr(scopeChainReg(), outputReg());
     }
 
     // At this point outputReg holds the object on which the property
@@ -2822,17 +2822,17 @@ IsCacheableScopeChain(JSObject *scopeCha
         if (!IsCacheableNonGlobalScope(scopeChain)) {
             IonSpew(IonSpew_InlineCaches, "Non-cacheable object on scope chain");
             return false;
         }
 
         if (scopeChain == holder)
             return true;
 
-        scopeChain = &scopeChain->asScope().enclosingScope();
+        scopeChain = &scopeChain->as<ScopeObject>().enclosingScope();
         if (!scopeChain) {
             IonSpew(IonSpew_InlineCaches, "Scope chain indirect hit");
             return false;
         }
     }
 
     JS_NOT_REACHED("Shouldn't get here");
     return false;
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -3219,28 +3219,28 @@ JS_SetPrototype(JSContext *cx, JSObject 
     assertSameCompartment(cx, obj, proto);
 
     return SetClassAndProto(cx, obj, obj->getClass(), proto, JS_FALSE);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetParent(JSObject *obj)
 {
-    JS_ASSERT(!obj->isScope());
+    JS_ASSERT(!obj->is<ScopeObject>());
     return obj->getParent();
 }
 
 JS_PUBLIC_API(JSBool)
 JS_SetParent(JSContext *cx, JSObject *objArg, JSObject *parentArg)
 {
     RootedObject obj(cx, objArg);
     RootedObject parent(cx, parentArg);
     AssertHeapIsIdle(cx);
     CHECK_REQUEST(cx);
-    JS_ASSERT(!obj->isScope());
+    JS_ASSERT(!obj->is<ScopeObject>());
     JS_ASSERT(parent || !obj->getParent());
     assertSameCompartment(cx, obj, parent);
 
     return JSObject::setParent(cx, obj, parent);
 }
 
 JS_PUBLIC_API(JSObject *)
 JS_GetConstructor(JSContext *cx, JSObject *protoArg)
--- a/js/src/jsdbgapi.cpp
+++ b/js/src/jsdbgapi.cpp
@@ -558,18 +558,18 @@ JS_PUBLIC_API(JSObject *)
 JS_GetParentOrScopeChain(JSContext *cx, JSObject *obj)
 {
     return obj->enclosingScope();
 }
 
 JS_PUBLIC_API(const char *)
 JS_GetDebugClassName(JSObject *obj)
 {
-    if (obj->isDebugScope())
-        return obj->asDebugScope().scope().getClass()->name;
+    if (obj->is<DebugScopeObject>())
+        return obj->as<DebugScopeObject>().scope().getClass()->name;
     return obj->getClass()->name;
 }
 
 /************************************************************************/
 
 JS_PUBLIC_API(const char *)
 JS_GetScriptFilename(JSContext *cx, JSScript *script)
 {
@@ -674,17 +674,17 @@ JS_PUBLIC_API(JSBool)
 JS_GetPropertyDescArray(JSContext *cx, JSObject *obj_, JSPropertyDescArray *pda)
 {
     RootedObject obj(cx, obj_);
 
     assertSameCompartment(cx, obj);
     uint32_t i = 0;
     JSPropertyDesc *pd = NULL;
 
-    if (obj->isDebugScope()) {
+    if (obj->is<DebugScopeObject>()) {
         AutoIdVector props(cx);
         if (!Proxy::enumerate(cx, obj, props))
             return false;
 
         pd = cx->pod_calloc<JSPropertyDesc>(props.length());
         if (!pd)
             return false;
 
@@ -1261,17 +1261,17 @@ JSAbstractFramePtr::callObject(JSContext
      * Given that fp is a function frame and GetDebugScopeForFrame always fills
      * in missing scopes, we can expect to find fp's CallObject on 'o'. Note:
      *  - GetDebugScopeForFrame wraps every ScopeObject (missing or not) with
      *    a DebugScopeObject proxy.
      *  - If fp is an eval-in-function, then fp has no callobj of its own and
      *    JS_GetFrameCallObject will return the innermost function's callobj.
      */
     while (o) {
-        ScopeObject &scope = o->asDebugScope().scope();
+        ScopeObject &scope = o->as<DebugScopeObject>().scope();
         if (scope.is<CallObject>())
             return o;
         o = o->enclosingScope();
     }
     return NULL;
 }
 
 JSFunction *
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -345,17 +345,17 @@ JS_FRIEND_API(bool)
 js::IsAtomsCompartment(JSCompartment *comp)
 {
     return comp == comp->rt->atomsCompartment;
 }
 
 JS_FRIEND_API(bool)
 js::IsScopeObject(JSObject *obj)
 {
-    return obj->isScope();
+    return obj->is<ScopeObject>();
 }
 
 JS_FRIEND_API(bool)
 js::IsCallObject(JSObject *obj)
 {
     return obj->is<CallObject>();
 }
 
--- a/js/src/jsfuninlines.h
+++ b/js/src/jsfuninlines.h
@@ -158,18 +158,18 @@ Function(JSContext *cx, unsigned argc, V
 extern bool
 IsBuiltinFunctionConstructor(JSFunction *fun);
 
 static inline JSObject *
 SkipScopeParent(JSObject *parent)
 {
     if (!parent)
         return NULL;
-    while (parent->isScope())
-        parent = &parent->asScope().enclosingScope();
+    while (parent->is<ScopeObject>())
+        parent = &parent->as<ScopeObject>().enclosingScope();
     return parent;
 }
 
 inline bool
 CanReuseFunctionForClone(JSContext *cx, HandleFunction fun)
 {
     if (!fun->hasSingletonType())
         return false;
--- a/js/src/jsobj.h
+++ b/js/src/jsobj.h
@@ -215,23 +215,21 @@ extern Class ObjectClass;
 extern Class ProxyClass;
 extern Class RegExpStaticsClass;
 extern Class StopIterationClass;
 extern Class StringClass;
 extern Class WeakMapClass;
 
 class ArrayBufferObject;
 class BooleanObject;
-class DebugScopeObject;
 class GlobalObject;
 class MapObject;
 class NewObjectCache;
 class NormalArgumentsObject;
 class NumberObject;
-class ScopeObject;
 class SetObject;
 class StrictArgumentsObject;
 class StringObject;
 
 }  /* namespace js */
 
 #define JSSLOT_FREE(clasp)  JSCLASS_RESERVED_SLOTS(clasp)
 
@@ -918,18 +916,18 @@ class JSObject : public js::ObjectImpl
      *
      *   if (obj.isX()) {
      *     XObject &x = obj.asX();
      *     x.foo();
      *   }
      *
      * These XObject classes form a hierarchy. For example, for a cloned block
      * object, the following predicates are true: is<ClonedBlockObject>,
-     * is<BlockObject>, is<NestedScopeObject> and isScope. Each of these has a
-     * respective class that derives and adds operations.
+     * is<BlockObject>, is<NestedScopeObject> and is<ScopeObject>. Each of
+     * these has a respective class that derives and adds operations.
      *
      * A class XObject is defined in a vm/XObject{.h, .cpp, -inl.h} file
      * triplet (along with any class YObject that derives XObject).
      *
      * Note that X represents a low-level representation and does not query the
      * [[Class]] property of object defined by the spec (for this, see
      * js::ObjectClassIs).
      *
@@ -959,38 +957,34 @@ class JSObject : public js::ObjectImpl
     inline bool isDate()             const { return hasClass(&js::DateClass); }
     inline bool isError()            const { return hasClass(&js::ErrorClass); }
     inline bool isFunction()         const { return hasClass(&js::FunctionClass); }
     inline bool isGenerator()        const { return hasClass(&js::GeneratorClass); }
     inline bool isGlobal()           const;
     inline bool isObject()           const { return hasClass(&js::ObjectClass); }
     using js::ObjectImpl::isProxy;
     inline bool isRegExpStatics()    const { return hasClass(&js::RegExpStaticsClass); }
-    inline bool isScope()            const;
     inline bool isStopIteration()    const { return hasClass(&js::StopIterationClass); }
     inline bool isTypedArray()       const;
     inline bool isWeakMap()          const { return hasClass(&js::WeakMapClass); }
 
     /* Subtypes of PrimitiveObject. */
     inline bool isBoolean() const { return hasClass(&js::BooleanClass); }
     inline bool isNumber()  const { return hasClass(&js::NumberClass); }
     inline bool isString()  const { return hasClass(&js::StringClass); }
 
     /* Subtypes of Proxy. */
-    inline bool isDebugScope()              const;
     inline bool isWrapper()                 const;
     inline bool isFunctionProxy()           const { return hasClass(&js::FunctionProxyClass); }
     inline bool isCrossCompartmentWrapper() const;
 
     inline js::BooleanObject &asBoolean();
-    inline js::DebugScopeObject &asDebugScope();
     inline js::GlobalObject &asGlobal();
     inline js::MapObject &asMap();
     inline js::NumberObject &asNumber();
-    inline js::ScopeObject &asScope();
     inline js::SetObject &asSet();
     inline js::StringObject &asString();
 
     static inline js::ThingRootKind rootKind() { return js::THING_ROOT_OBJECT; }
 
 #ifdef DEBUG
     void dump();
 #endif
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -222,26 +222,16 @@ JSObject::getParent() const
 }
 
 inline JSObject *
 JSObject::getMetadata() const
 {
     return lastProperty()->getObjectMetadata();
 }
 
-inline JSObject *
-JSObject::enclosingScope()
-{
-    return isScope()
-           ? &asScope().enclosingScope()
-           : isDebugScope()
-           ? &asDebugScope().enclosingScope()
-           : getParent();
-}
-
 inline bool
 JSObject::isFixedSlot(size_t slot)
 {
     return slot < numFixedSlots();
 }
 
 inline size_t
 JSObject::dynamicSlotIndex(size_t slot)
@@ -790,18 +780,18 @@ inline bool JSObject::setIteratedSinglet
 
 inline bool JSObject::setDelegate(JSContext *cx)
 {
     return setFlag(cx, js::BaseShape::DELEGATE, GENERATE_SHAPE);
 }
 
 inline bool JSObject::isVarObj()
 {
-    if (isDebugScope())
-        return asDebugScope().scope().isVarObj();
+    if (is<js::DebugScopeObject>())
+        return as<js::DebugScopeObject>().scope().isVarObj();
     return lastProperty()->hasObjectFlag(js::BaseShape::VAROBJ);
 }
 
 inline bool JSObject::setVarObj(JSContext *cx)
 {
     return setFlag(cx, js::BaseShape::VAROBJ);
 }
 
@@ -856,23 +846,16 @@ JSObject::asNumber()
 
 inline js::StringObject &
 JSObject::asString()
 {
     JS_ASSERT(isString());
     return *static_cast<js::StringObject *>(this);
 }
 
-inline bool
-JSObject::isDebugScope() const
-{
-    extern bool js_IsDebugScopeSlow(JSObject *obj);
-    return getClass() == &js::ObjectProxyClass && js_IsDebugScopeSlow(const_cast<JSObject*>(this));
-}
-
 /* static */ inline JSObject *
 JSObject::create(JSContext *cx, js::gc::AllocKind kind, js::gc::InitialHeap heap,
                  js::HandleShape shape, js::HandleTypeObject type,
                  js::HeapSlot *extantSlots /* = NULL */)
 {
     /*
      * Callers must use dynamicSlotsCount to size the initial slot array of the
      * object. We can't check the allocated capacity of the dynamic slots, but
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2828,18 +2828,18 @@ js::SetFrameArgumentsObject(JSContext *c
          * is assigned to.
          */
         jsbytecode *pc = script->code;
         while (*pc != JSOP_ARGUMENTS)
             pc += GetBytecodeLength(pc);
         pc += JSOP_ARGUMENTS_LENGTH;
         JS_ASSERT(*pc == JSOP_SETALIASEDVAR);
 
-        if (frame.callObj().asScope().aliasedVar(pc).isMagic(JS_OPTIMIZED_ARGUMENTS))
-            frame.callObj().asScope().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj));
+        if (frame.callObj().as<ScopeObject>().aliasedVar(pc).isMagic(JS_OPTIMIZED_ARGUMENTS))
+            frame.callObj().as<ScopeObject>().setAliasedVar(cx, pc, cx->names().arguments, ObjectValue(*argsobj));
     } else {
         if (frame.unaliasedLocal(var).isMagic(JS_OPTIMIZED_ARGUMENTS))
             frame.unaliasedLocal(var) = ObjectValue(*argsobj);
     }
 }
 
 /* static */ bool
 JSScript::argumentsOptimizationFailed(JSContext *cx, HandleScript script)
--- a/js/src/jsstr.cpp
+++ b/js/src/jsstr.cpp
@@ -2706,19 +2706,19 @@ LambdaIsGetElem(JSContext *cx, JSObject 
     /*
      * JSOP_GETALIASEDVAR tells us exactly where to find the base object 'b'.
      * Rule out the (unlikely) possibility of a heavyweight function since it
      * would make our scope walk off by 1.
      */
     if (JSOp(*pc) != JSOP_GETALIASEDVAR || fun->isHeavyweight())
         return true;
     ScopeCoordinate sc(pc);
-    ScopeObject *scope = &fun->environment()->asScope();
+    ScopeObject *scope = &fun->environment()->as<ScopeObject>();
     for (unsigned i = 0; i < sc.hops; ++i)
-        scope = &scope->enclosingScope().asScope();
+        scope = &scope->enclosingScope().as<ScopeObject>();
     Value b = scope->aliasedVar(sc);
     pc += JSOP_GETALIASEDVAR_LENGTH;
 
     /* Look for 'a' to be the lambda's first argument. */
     if (JSOp(*pc) != JSOP_GETARG || GET_SLOTNO(pc) != 0)
         return true;
     pc += JSOP_GETARG_LENGTH;
 
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -641,17 +641,17 @@ Debugger::wrapEnvironment(JSContext *cx,
         rval.setNull();
         return true;
     }
 
     /*
      * DebuggerEnv should only wrap a debug scope chain obtained (transitively)
      * from GetDebugScopeFor(Frame|Function).
      */
-    JS_ASSERT(!env->isScope());
+    JS_ASSERT(!env->is<ScopeObject>());
 
     JSObject *envobj;
     ObjectWeakMap::AddPtr p = environments.lookupForAdd(env);
     if (p) {
         envobj = p->value;
     } else {
         /* Create a new Debugger.Environment for env. */
         JSObject *proto = &object->getReservedSlot(JSSLOT_DEBUG_ENV_PROTO).toObject();
@@ -5131,39 +5131,39 @@ DebuggerEnv_checkThis(JSContext *cx, con
 
 #define THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env)                \
     CallArgs args = CallArgsFromVp(argc, vp);                                 \
     JSObject *envobj = DebuggerEnv_checkThis(cx, args, fnname);               \
     if (!envobj)                                                              \
         return false;                                                         \
     Rooted<Env*> env(cx, static_cast<Env *>(envobj->getPrivate()));           \
     JS_ASSERT(env);                                                           \
-    JS_ASSERT(!env->isScope())
+    JS_ASSERT(!env->is<ScopeObject>())
 
 #define THIS_DEBUGENV_OWNER(cx, argc, vp, fnname, args, envobj, env, dbg)     \
     THIS_DEBUGENV(cx, argc, vp, fnname, args, envobj, env);                   \
     Debugger *dbg = Debugger::fromChildJSObject(envobj)
 
 static JSBool
 DebuggerEnv_construct(JSContext *cx, unsigned argc, Value *vp)
 {
     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NO_CONSTRUCTOR, "Debugger.Environment");
     return false;
 }
 
 static bool
 IsDeclarative(Env *env)
 {
-    return env->isDebugScope() && env->asDebugScope().isForDeclarative();
+    return env->is<DebugScopeObject>() && env->as<DebugScopeObject>().isForDeclarative();
 }
 
 static bool
 IsWith(Env *env)
 {
-    return env->isDebugScope() && env->asDebugScope().scope().is<WithObject>();
+    return env->is<DebugScopeObject>() && env->as<DebugScopeObject>().scope().is<WithObject>();
 }
 
 static JSBool
 DebuggerEnv_getType(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGENV(cx, argc, vp, "get type", args, envobj, env);
 
     /* Don't bother switching compartments just to check env's class. */
@@ -5203,39 +5203,39 @@ DebuggerEnv_getObject(JSContext *cx, uns
      */
     if (IsDeclarative(env)) {
         JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEBUG_NO_SCOPE_OBJECT);
         return false;
     }
 
     JSObject *obj;
     if (IsWith(env)) {
-        obj = &env->asDebugScope().scope().as<WithObject>().object();
+        obj = &env->as<DebugScopeObject>().scope().as<WithObject>().object();
     } else {
         obj = env;
-        JS_ASSERT(!obj->isDebugScope());
+        JS_ASSERT(!obj->is<DebugScopeObject>());
     }
 
     args.rval().setObject(*obj);
     if (!dbg->wrapDebuggeeValue(cx, args.rval()))
         return false;
     return true;
 }
 
 static JSBool
 DebuggerEnv_getCallee(JSContext *cx, unsigned argc, Value *vp)
 {
     THIS_DEBUGENV_OWNER(cx, argc, vp, "get callee", args, envobj, env, dbg);
 
     args.rval().setNull();
 
-    if (!env->isDebugScope())
+    if (!env->is<DebugScopeObject>())
         return true;
 
-    JSObject &scope = env->asDebugScope().scope();
+    JSObject &scope = env->as<DebugScopeObject>().scope();
     if (!scope.is<CallObject>())
         return true;
 
     CallObject &callobj = scope.as<CallObject>();
     if (callobj.isForEval())
         return true;
 
     args.rval().setObject(callobj.callee());
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -575,17 +575,17 @@ js::InvokeGetterOrSetter(JSContext *cx, 
     return Invoke(cx, ObjectValue(*obj), fval, argc, argv, rval);
 }
 
 bool
 js::ExecuteKernel(JSContext *cx, HandleScript script, JSObject &scopeChainArg, const Value &thisv,
                   ExecuteType type, AbstractFramePtr evalInFrame, Value *result)
 {
     JS_ASSERT_IF(evalInFrame, type == EXECUTE_DEBUG);
-    JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChainArg.isScope());
+    JS_ASSERT_IF(type == EXECUTE_GLOBAL, !scopeChainArg.is<ScopeObject>());
 
     if (script->isEmpty()) {
         if (result)
             result->setUndefined();
         return true;
     }
 
     TypeScript::SetThis(cx, script, thisv);
--- a/js/src/vm/ScopeObject-inl.h
+++ b/js/src/vm/ScopeObject-inl.h
@@ -22,31 +22,35 @@ JSObject::is<js::ClonedBlockObject>() co
 
 template<>
 inline bool
 JSObject::is<js::StaticBlockObject>() const
 {
     return is<js::BlockObject>() && !getProto();
 }
 
+inline JSObject *
+JSObject::enclosingScope()
+{
+    return is<js::ScopeObject>()
+           ? &as<js::ScopeObject>().enclosingScope()
+           : is<js::DebugScopeObject>()
+           ? &as<js::DebugScopeObject>().enclosingScope()
+           : getParent();
+}
+
 namespace js {
 
 inline
 ScopeCoordinate::ScopeCoordinate(jsbytecode *pc)
   : hops(GET_UINT16(pc)), slot(GET_UINT16(pc + 2))
 {
     JS_ASSERT(JOF_OPTYPE(*pc) == JOF_SCOPECOORD);
 }
 
-inline JSObject &
-ScopeObject::enclosingScope() const
-{
-    return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
-}
-
 inline void
 ScopeObject::setEnclosingScope(HandleObject obj)
 {
     JS_ASSERT_IF(obj->is<CallObject>() || obj->is<DeclEnvObject>() || obj->is<BlockObject>(),
                  obj->isDelegate());
     setFixedSlot(SCOPE_CHAIN_SLOT, ObjectValue(*obj));
 }
 
@@ -261,23 +265,9 @@ inline void
 ClonedBlockObject::setVar(unsigned i, const Value &v, MaybeCheckAliasing checkAliasing)
 {
     JS_ASSERT_IF(checkAliasing, staticBlock().isAliased(i));
     setSlotValue(i, v);
 }
 
 }  /* namespace js */
 
-inline js::ScopeObject &
-JSObject::asScope()
-{
-    JS_ASSERT(isScope());
-    return *static_cast<js::ScopeObject *>(this);
-}
-
-inline js::DebugScopeObject &
-JSObject::asDebugScope()
-{
-    JS_ASSERT(isDebugScope());
-    return *static_cast<js::DebugScopeObject *>(this);
-}
-
 #endif /* ScopeObject_inl_h___ */
--- a/js/src/vm/ScopeObject.cpp
+++ b/js/src/vm/ScopeObject.cpp
@@ -223,17 +223,17 @@ CallObject::createTemplateObject(JSConte
 CallObject *
 CallObject::create(JSContext *cx, HandleScript script, HandleObject enclosing, HandleFunction callee)
 {
     gc::InitialHeap heap = script->treatAsRunOnce ? gc::TenuredHeap : gc::DefaultHeap;
     CallObject *callobj = CallObject::createTemplateObject(cx, script, heap);
     if (!callobj)
         return NULL;
 
-    callobj->asScope().setEnclosingScope(enclosing);
+    callobj->as<ScopeObject>().setEnclosingScope(enclosing);
     callobj->initFixedSlot(CALLEE_SLOT, ObjectOrNullValue(callee));
 
     if (script->treatAsRunOnce) {
         Rooted<CallObject*> ncallobj(cx, callobj);
         if (!JSObject::setSingletonType(cx, ncallobj))
             return NULL;
         return ncallobj;
     }
@@ -360,17 +360,17 @@ DeclEnvObject::createTemplateObject(JSCo
 
 DeclEnvObject *
 DeclEnvObject::create(JSContext *cx, HandleObject enclosing, HandleFunction callee)
 {
     RootedObject obj(cx, createTemplateObject(cx, callee, gc::DefaultHeap));
     if (!obj)
         return NULL;
 
-    obj->asScope().setEnclosingScope(enclosing);
+    obj->as<ScopeObject>().setEnclosingScope(enclosing);
     obj->setFixedSlot(lambdaSlot(), ObjectValue(*callee));
     return &obj->as<DeclEnvObject>();
 }
 
 WithObject *
 WithObject::create(JSContext *cx, HandleObject proto, HandleObject enclosing, uint32_t depth)
 {
     RootedTypeObject type(cx, proto->getNewType(cx, &class_));
@@ -381,17 +381,17 @@ WithObject::create(JSContext *cx, Handle
                                                       &enclosing->global(), NULL, FINALIZE_KIND));
     if (!shape)
         return NULL;
 
     RootedObject obj(cx, JSObject::create(cx, FINALIZE_KIND, gc::DefaultHeap, shape, type));
     if (!obj)
         return NULL;
 
-    obj->asScope().setEnclosingScope(enclosing);
+    obj->as<ScopeObject>().setEnclosingScope(enclosing);
     obj->setReservedSlot(DEPTH_SLOT, PrivateUint32Value(depth));
 
     JSObject *thisp = JSObject::thisObject(cx, proto);
     if (!thisp)
         return NULL;
 
     obj->setFixedSlot(THIS_SLOT, ObjectValue(*thisp));
 
@@ -1006,17 +1006,17 @@ ScopeIter::ScopeIter(AbstractFramePtr fr
     settle();
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 }
 
 ScopeObject &
 ScopeIter::scope() const
 {
     JS_ASSERT(hasScopeObject());
-    return cur_->asScope();
+    return cur_->as<ScopeObject>();
 }
 
 ScopeIter &
 ScopeIter::operator++()
 {
     JS_ASSERT(!done());
     switch (type_) {
       case Call:
@@ -1106,17 +1106,17 @@ ScopeIter::settle()
         hasScopeObject_ = block_->needsClone();
         JS_ASSERT_IF(hasScopeObject_, cur_->as<ClonedBlockObject>().staticBlock() == *block_);
     } else if (cur_->is<CallObject>()) {
         CallObject &callobj = cur_->as<CallObject>();
         type_ = callobj.isForEval() ? StrictEvalScope : Call;
         hasScopeObject_ = true;
         JS_ASSERT_IF(type_ == Call, callobj.callee().nonLazyScript() == frame_.script());
     } else {
-        JS_ASSERT(!cur_->isScope());
+        JS_ASSERT(!cur_->is<ScopeObject>());
         JS_ASSERT(frame_.isGlobalFrame() || frame_.isDebuggerFrame());
         frame_ = NullFramePtr();
     }
 }
 
 /* static */ HashNumber
 ScopeIterKey::hash(ScopeIterKey si)
 {
@@ -1367,17 +1367,17 @@ class DebugScopeProxy : public BaseProxy
                                unsigned flags) MOZ_OVERRIDE
     {
         return getOwnPropertyDescriptor(cx, proxy, id, desc, flags);
     }
 
     bool getOwnPropertyDescriptor(JSContext *cx, HandleObject proxy, HandleId id,
                                   PropertyDescriptor *desc, unsigned flags) MOZ_OVERRIDE
     {
-        Rooted<DebugScopeObject*> debugScope(cx, &proxy->asDebugScope());
+        Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
         Rooted<ScopeObject*> scope(cx, &debugScope->scope());
 
         RootedArgumentsObject maybeArgsObj(cx);
         if (!checkForMissingArguments(cx, id, *scope, maybeArgsObj.address()))
             return false;
 
         if (maybeArgsObj) {
             PodZero(desc);
@@ -1397,18 +1397,18 @@ class DebugScopeProxy : public BaseProxy
         }
 
         return JS_GetPropertyDescriptorById(cx, scope, id, 0, desc);
     }
 
     bool get(JSContext *cx, HandleObject proxy, HandleObject receiver,  HandleId id,
              MutableHandleValue vp) MOZ_OVERRIDE
     {
-        Rooted<DebugScopeObject*> debugScope(cx, &proxy->asDebugScope());
-        Rooted<ScopeObject*> scope(cx, &proxy->asDebugScope().scope());
+        Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
+        Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         RootedArgumentsObject maybeArgsObj(cx);
         if (!checkForMissingArguments(cx, id, *scope, maybeArgsObj.address()))
             return false;
 
         if (maybeArgsObj) {
             vp.set(ObjectValue(*maybeArgsObj));
             return true;
@@ -1418,42 +1418,42 @@ class DebugScopeProxy : public BaseProxy
             return true;
 
         return JSObject::getGeneric(cx, scope, scope, id, vp);
     }
 
     bool set(JSContext *cx, HandleObject proxy, HandleObject receiver, HandleId id, bool strict,
              MutableHandleValue vp) MOZ_OVERRIDE
     {
-        Rooted<DebugScopeObject*> debugScope(cx, &proxy->asDebugScope());
-        Rooted<ScopeObject*> scope(cx, &proxy->asDebugScope().scope());
+        Rooted<DebugScopeObject*> debugScope(cx, &proxy->as<DebugScopeObject>());
+        Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
         if (handleUnaliasedAccess(cx, debugScope, scope, id, SET, vp))
             return true;
         return JSObject::setGeneric(cx, scope, scope, id, vp, strict);
     }
 
     bool defineProperty(JSContext *cx, HandleObject proxy, HandleId id,
                         PropertyDescriptor *desc) MOZ_OVERRIDE
     {
-        Rooted<ScopeObject*> scope(cx, &proxy->asDebugScope().scope());
+        Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         bool found;
         if (!has(cx, proxy, id, &found))
             return false;
         if (found)
             return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
 
         return JS_DefinePropertyById(cx, scope, id, desc->value, desc->getter, desc->setter,
                                      desc->attrs);
     }
 
     bool getScopePropertyNames(JSContext *cx, HandleObject proxy, AutoIdVector &props,
                                unsigned flags)
     {
-        Rooted<ScopeObject*> scope(cx, &proxy->asDebugScope().scope());
+        Rooted<ScopeObject*> scope(cx, &proxy->as<DebugScopeObject>().scope());
 
         if (isMissingArgumentsBinding(*scope)) {
             if (!props.append(NameToId(cx->names().arguments)))
                 return false;
         }
 
         if (!GetPropertyNames(cx, scope, flags, &props))
             return false;
@@ -1481,17 +1481,17 @@ class DebugScopeProxy : public BaseProxy
     bool enumerate(JSContext *cx, HandleObject proxy, AutoIdVector &props) MOZ_OVERRIDE
     {
         return getScopePropertyNames(cx, proxy, props, 0);
     }
 
     bool has(JSContext *cx, HandleObject proxy, HandleId id_, bool *bp) MOZ_OVERRIDE
     {
         RootedId id(cx, id_);
-        ScopeObject &scopeObj = proxy->asDebugScope().scope();
+        ScopeObject &scopeObj = proxy->as<DebugScopeObject>().scope();
 
         if (isArguments(cx, id) && isFunctionScope(scopeObj)) {
             *bp = true;
             return true;
         }
 
         JSBool found;
         RootedObject scope(cx, &scopeObj);
@@ -1532,27 +1532,27 @@ DebugScopeObject::create(JSContext *cx, 
 {
     JS_ASSERT(scope.compartment() == cx->compartment());
     RootedValue priv(cx, ObjectValue(scope));
     JSObject *obj = NewProxyObject(cx, &DebugScopeProxy::singleton, priv,
                                    NULL /* proto */, &scope.global(), ProxyNotCallable);
     if (!obj)
         return NULL;
 
-    JS_ASSERT(!enclosing->isScope());
+    JS_ASSERT(!enclosing->is<ScopeObject>());
     SetProxyExtra(obj, ENCLOSING_EXTRA, ObjectValue(*enclosing));
     SetProxyExtra(obj, SNAPSHOT_EXTRA, NullValue());
 
-    return &obj->asDebugScope();
+    return &obj->as<DebugScopeObject>();
 }
 
 ScopeObject &
 DebugScopeObject::scope() const
 {
-    return GetProxyTargetObject(const_cast<DebugScopeObject*>(this))->asScope();
+    return GetProxyTargetObject(const_cast<DebugScopeObject*>(this))->as<ScopeObject>();
 }
 
 JSObject &
 DebugScopeObject::enclosingScope() const
 {
     return GetProxyExtra(const_cast<DebugScopeObject*>(this), ENCLOSING_EXTRA).toObject();
 }
 
@@ -1689,17 +1689,17 @@ DebugScopeObject *
 DebugScopes::hasDebugScope(JSContext *cx, ScopeObject &scope)
 {
     DebugScopes *scopes = scope.compartment()->debugScopes;
     if (!scopes)
         return NULL;
 
     if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&scope)) {
         JS_ASSERT(CanUseDebugScopeMaps(cx));
-        return &p->value->asDebugScope();
+        return &p->value->as<DebugScopeObject>();
     }
 
     return NULL;
 }
 
 bool
 DebugScopes::addDebugScope(JSContext *cx, ScopeObject &scope, DebugScopeObject &debugScope)
 {
@@ -1786,17 +1786,17 @@ DebugScopes::onPopCall(AbstractFramePtr 
          * CallObject. See ScopeIter::settle.
          */
         if (!frame.hasCallObj())
             return;
 
         CallObject &callobj = frame.scopeChain()->as<CallObject>();
         scopes->liveScopes.remove(&callobj);
         if (ObjectWeakMap::Ptr p = scopes->proxiedScopes.lookup(&callobj))
-            debugScope = &p->value->asDebugScope();
+            debugScope = &p->value->as<DebugScopeObject>();
     } else {
         ScopeIter si(frame, cx);
         if (MissingScopeMap::Ptr p = scopes->missingScopes.lookup(si)) {
             debugScope = p->value;
             scopes->liveScopes.remove(&debugScope->scope().as<CallObject>());
             scopes->missingScopes.remove(p);
         }
     }
@@ -2120,26 +2120,26 @@ static JSObject *
 GetDebugScope(JSContext *cx, JSObject &obj)
 {
     /*
      * As an engine invariant (maintained internally and asserted by Execute),
      * ScopeObjects and non-ScopeObjects cannot be interleaved on the scope
      * chain; every scope chain must start with zero or more ScopeObjects and
      * terminate with one or more non-ScopeObjects (viz., GlobalObject).
      */
-    if (!obj.isScope()) {
+    if (!obj.is<ScopeObject>()) {
 #ifdef DEBUG
         JSObject *o = &obj;
         while ((o = o->enclosingScope()))
-            JS_ASSERT(!o->isScope());
+            JS_ASSERT(!o->is<ScopeObject>());
 #endif
         return &obj;
     }
 
-    Rooted<ScopeObject*> scope(cx, &obj.asScope());
+    Rooted<ScopeObject*> scope(cx, &obj.as<ScopeObject>());
     if (AbstractFramePtr frame = DebugScopes::hasLiveFrame(*scope)) {
         ScopeIter si(frame, *scope, cx);
         return GetDebugScope(cx, si);
     }
     ScopeIter si(scope->enclosingScope(), cx);
     return GetDebugScopeForScope(cx, scope, si);
 }
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -154,17 +154,19 @@ class ScopeObject : public JSObject
     static const uint32_t SCOPE_CHAIN_SLOT = 0;
 
   public:
     /*
      * Since every scope chain terminates with a global object and GlobalObject
      * does not derive ScopeObject (it has a completely different layout), the
      * enclosing scope of a ScopeObject is necessarily non-null.
      */
-    inline JSObject &enclosingScope() const;
+    inline JSObject &enclosingScope() const {
+        return getReservedSlot(SCOPE_CHAIN_SLOT).toObject();
+    }
     inline void setEnclosingScope(HandleObject obj);
 
     /*
      * Get or set an aliased variable contained in this scope. Unaliased
      * variables should instead access the StackFrame. Aliased variable access
      * is primarily made through JOF_SCOPECOORD ops which is why these members
      * take a ScopeCoordinate instead of just the slot index.
      */
@@ -626,15 +628,24 @@ class DebugScopes
 
 template<>
 inline bool
 JSObject::is<js::NestedScopeObject>() const
 {
     return is<js::BlockObject>() || is<js::WithObject>();
 }
 
+template<>
 inline bool
-JSObject::isScope() const
+JSObject::is<js::ScopeObject>() const
 {
     return is<js::CallObject>() || is<js::DeclEnvObject>() || is<js::NestedScopeObject>();
 }
 
+template<>
+inline bool
+JSObject::is<js::DebugScopeObject>() const
+{
+    extern bool js_IsDebugScopeSlow(JSObject *obj);
+    return getClass() == &js::ObjectProxyClass && js_IsDebugScopeSlow(const_cast<JSObject*>(this));
+}
+
 #endif /* ScopeObject_h___ */
--- a/js/src/vm/Stack-inl.h
+++ b/js/src/vm/Stack-inl.h
@@ -194,36 +194,36 @@ StackFrame::initArgsObj(ArgumentsObject 
     JS_ASSERT(script()->needsArgsObj());
     flags_ |= HAS_ARGS_OBJ;
     argsObj_ = &argsobj;
 }
 
 inline ScopeObject &
 StackFrame::aliasedVarScope(ScopeCoordinate sc) const
 {
-    JSObject *scope = &scopeChain()->asScope();
+    JSObject *scope = &scopeChain()->as<ScopeObject>();
     for (unsigned i = sc.hops; i; i--)
-        scope = &scope->asScope().enclosingScope();
-    return scope->asScope();
+        scope = &scope->as<ScopeObject>().enclosingScope();
+    return scope->as<ScopeObject>();
 }
 
 inline void
 StackFrame::pushOnScopeChain(ScopeObject &scope)
 {
     JS_ASSERT(*scopeChain() == scope.enclosingScope() ||
               *scopeChain() == scope.as<CallObject>().enclosingScope().as<DeclEnvObject>().enclosingScope());
     scopeChain_ = &scope;
     flags_ |= HAS_SCOPECHAIN;
 }
 
 inline void
 StackFrame::popOffScopeChain()
 {
     JS_ASSERT(flags_ & HAS_SCOPECHAIN);
-    scopeChain_ = &scopeChain_->asScope().enclosingScope();
+    scopeChain_ = &scopeChain_->as<ScopeObject>().enclosingScope();
 }
 
 inline CallObject &
 StackFrame::callObj() const
 {
     JS_ASSERT(fun()->isHeavyweight());
 
     JSObject *pobj = scopeChain();
--- a/js/src/vm/Stack.cpp
+++ b/js/src/vm/Stack.cpp
@@ -261,18 +261,19 @@ AssertDynamicScopeMatchesStaticScope(JSC
               case StaticScopeIter::NAMED_LAMBDA:
                 scope = &scope->as<DeclEnvObject>().enclosingScope();
                 break;
             }
         }
     }
 
     /*
-     * Ideally, we'd JS_ASSERT(!scope->isScope()) but the enclosing lexical
-     * scope chain stops at eval() boundaries. See StaticScopeIter comment.
+     * Ideally, we'd JS_ASSERT(!scope->is<ScopeObject>()) but the enclosing
+     * lexical scope chain stops at eval() boundaries. See StaticScopeIter
+     * comment.
      */
 #endif
 }
 
 bool
 StackFrame::initFunctionScopeObjects(JSContext *cx)
 {
     CallObject *callobj = CallObject::createForFunction(cx, this);
@@ -337,17 +338,17 @@ StackFrame::epilogue(JSContext *cx)
 
     if (isEvalFrame()) {
         if (isStrictEvalFrame()) {
             JS_ASSERT_IF(hasCallObj(), scopeChain()->as<CallObject>().isForEval());
             if (cx->compartment()->debugMode())
                 DebugScopes::onPopStrictEvalScope(this);
         } else if (isDirectEvalFrame()) {
             if (isDebuggerFrame())
-                JS_ASSERT(!scopeChain()->isScope());
+                JS_ASSERT(!scopeChain()->is<ScopeObject>());
         } else {
             /*
              * Debugger.Object.prototype.evalInGlobal creates indirect eval
              * frames scoped to the given global;
              * Debugger.Object.prototype.evalInGlobalWithBindings creates
              * indirect eval frames scoped to an object carrying the introduced
              * bindings.
              */
@@ -355,17 +356,17 @@ StackFrame::epilogue(JSContext *cx)
                 JS_ASSERT(scopeChain()->isGlobal() || scopeChain()->enclosingScope()->isGlobal());
             else
                 JS_ASSERT(scopeChain()->isGlobal());
         }
         return;
     }
 
     if (isGlobalFrame()) {
-        JS_ASSERT(!scopeChain()->isScope());
+        JS_ASSERT(!scopeChain()->is<ScopeObject>());
         return;
     }
 
     JS_ASSERT(isNonEvalFunctionFrame());
 
     if (fun()->isHeavyweight())
         JS_ASSERT_IF(hasCallObj(),
                      scopeChain()->as<CallObject>().callee().nonLazyScript() == script);