Bug 1271650 - Implement a C++ interface for DebuggerFrame.arguments. r=jimb
authorEddy Bruel <ejpbruel@mozilla.com>
Thu, 29 Dec 2016 15:10:11 +0100
changeset 327541 0b58022dda33469d7d7cb652ec31407e7fa6b8a8
parent 327540 c274a7c25dc7e3a9228b3b9a5c1917e985ff4192
child 327542 05a6f5bcb7a086985dac27c3db82324326434459
push id35517
push userkwierso@gmail.com
push dateThu, 29 Dec 2016 20:22:54 +0000
treeherderautoland@3f2f8d77ad27 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1271650
milestone53.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1271650 - Implement a C++ interface for DebuggerFrame.arguments. r=jimb
js/src/gc/Rooting.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -19,16 +19,17 @@ class PropertyName;
 class NativeObject;
 class ArrayObject;
 class GlobalObject;
 class PlainObject;
 class ScriptSourceObject;
 class SavedFrame;
 class Shape;
 class ObjectGroup;
+class DebuggerArguments;
 class DebuggerEnvironment;
 class DebuggerFrame;
 class DebuggerObject;
 class Scope;
 
 // These are internal counterparts to the public types such as HandleObject.
 
 typedef JS::Handle<NativeObject*>           HandleNativeObject;
@@ -36,42 +37,45 @@ typedef JS::Handle<Shape*>              
 typedef JS::Handle<ObjectGroup*>            HandleObjectGroup;
 typedef JS::Handle<JSAtom*>                 HandleAtom;
 typedef JS::Handle<JSLinearString*>         HandleLinearString;
 typedef JS::Handle<PropertyName*>           HandlePropertyName;
 typedef JS::Handle<ArrayObject*>            HandleArrayObject;
 typedef JS::Handle<PlainObject*>            HandlePlainObject;
 typedef JS::Handle<SavedFrame*>             HandleSavedFrame;
 typedef JS::Handle<ScriptSourceObject*>     HandleScriptSource;
+typedef JS::Handle<DebuggerArguments*>      HandleDebuggerArguments;
 typedef JS::Handle<DebuggerEnvironment*>    HandleDebuggerEnvironment;
 typedef JS::Handle<DebuggerFrame*>          HandleDebuggerFrame;
 typedef JS::Handle<DebuggerObject*>         HandleDebuggerObject;
 typedef JS::Handle<Scope*>                  HandleScope;
 
 typedef JS::MutableHandle<Shape*>               MutableHandleShape;
 typedef JS::MutableHandle<JSAtom*>              MutableHandleAtom;
 typedef JS::MutableHandle<NativeObject*>        MutableHandleNativeObject;
 typedef JS::MutableHandle<PlainObject*>         MutableHandlePlainObject;
 typedef JS::MutableHandle<SavedFrame*>          MutableHandleSavedFrame;
+typedef JS::MutableHandle<DebuggerArguments*>   MutableHandleDebuggerArguments;
 typedef JS::MutableHandle<DebuggerEnvironment*> MutableHandleDebuggerEnvironment;
 typedef JS::MutableHandle<DebuggerFrame*>       MutableHandleDebuggerFrame;
 typedef JS::MutableHandle<DebuggerObject*>      MutableHandleDebuggerObject;
 typedef JS::MutableHandle<Scope*>               MutableHandleScope;
 
 typedef JS::Rooted<NativeObject*>           RootedNativeObject;
 typedef JS::Rooted<Shape*>                  RootedShape;
 typedef JS::Rooted<ObjectGroup*>            RootedObjectGroup;
 typedef JS::Rooted<JSAtom*>                 RootedAtom;
 typedef JS::Rooted<JSLinearString*>         RootedLinearString;
 typedef JS::Rooted<PropertyName*>           RootedPropertyName;
 typedef JS::Rooted<ArrayObject*>            RootedArrayObject;
 typedef JS::Rooted<GlobalObject*>           RootedGlobalObject;
 typedef JS::Rooted<PlainObject*>            RootedPlainObject;
 typedef JS::Rooted<SavedFrame*>             RootedSavedFrame;
 typedef JS::Rooted<ScriptSourceObject*>     RootedScriptSource;
+typedef JS::Rooted<DebuggerArguments*>      RootedDebuggerArguments;
 typedef JS::Rooted<DebuggerEnvironment*>    RootedDebuggerEnvironment;
 typedef JS::Rooted<DebuggerFrame*>          RootedDebuggerFrame;
 typedef JS::Rooted<DebuggerObject*>         RootedDebuggerObject;
 typedef JS::Rooted<Scope*>                  RootedScope;
 
 typedef JS::GCVector<JSFunction*>   FunctionVector;
 typedef JS::GCVector<PropertyName*> PropertyNameVector;
 typedef JS::GCVector<Shape*>        ShapeVector;
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -105,17 +105,17 @@ const Class DebuggerFrame::class_ = {
     &DebuggerFrame::classOps_
 };
 
 enum {
     JSSLOT_DEBUGARGUMENTS_FRAME,
     JSSLOT_DEBUGARGUMENTS_COUNT
 };
 
-static const Class DebuggerArguments_class = {
+const Class DebuggerArguments::class_ = {
     "Arguments",
     JSCLASS_HAS_RESERVED_SLOTS(JSSLOT_DEBUGARGUMENTS_COUNT)
 };
 
 const ClassOps DebuggerEnvironment::classOps_ = {
     nullptr,    /* addProperty */
     nullptr,    /* delProperty */
     nullptr,    /* getProperty */
@@ -7573,16 +7573,44 @@ DebuggerFrame::setOnStepHandler(JSContex
     }
 
     /* Now that the step mode switch has succeeded, we can install the handler. */
     frame->setReservedSlot(JSSLOT_DEBUGFRAME_ONSTEP_HANDLER,
                            handler ? PrivateValue(handler) : UndefinedValue());
     return true;
 }
 
+/* static */ bool
+DebuggerFrame::getArguments(JSContext *cx, HandleDebuggerFrame frame,
+                            MutableHandleDebuggerArguments result)
+{
+    Value argumentsv = frame->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);
+    if (!argumentsv.isUndefined()) {
+        result.set(argumentsv.isObject() ? &argumentsv.toObject().as<DebuggerArguments>() : nullptr);
+        return true;
+    }
+
+    AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
+
+    RootedDebuggerArguments arguments(cx);
+    if (referent.hasArgs()) {
+        Rooted<GlobalObject*> global(cx, &frame->global());
+        RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global));
+        if (!proto)
+            return false;
+        arguments = DebuggerArguments::create(cx, proto, frame);
+    } else {
+        arguments = nullptr;
+    }
+
+    result.set(arguments);
+    frame->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, ObjectOrNullValue(result));
+    return true;
+}
+
 /*
  * Evaluate |chars[0..length-1]| in the environment |env|, treating that
  * source as appearing starting at |lineno| in |filename|. Store the return
  * value in |*rval|. Use |thisv| as the 'this' value.
  *
  * If |frame| is non-nullptr, evaluate as for a direct eval in that frame; |env|
  * must be either |frame|'s DebugScopeObject, or some extension of that
  * environment; either way, |frame|'s scope is where newly declared variables
@@ -8099,17 +8127,17 @@ DebuggerArguments_getArg(JSContext* cx, 
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     int32_t i = args.callee().as<JSFunction>().getExtendedSlot(0).toInt32();
 
     /* Check that the this value is an Arguments object. */
     RootedObject argsobj(cx, NonNullObject(cx, args.thisv()));
     if (!argsobj)
         return false;
-    if (argsobj->getClass() != &DebuggerArguments_class) {
+    if (argsobj->getClass() != &DebuggerArguments::class_) {
         JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                   "Arguments", "getArgument", argsobj->getClass()->name);
         return false;
     }
 
     /*
      * Put the Debugger.Frame into the this-value slot, then use THIS_FRAME
      * to check that it is still live and get the fp.
@@ -8153,70 +8181,67 @@ DebuggerArguments_getArg(JSContext* cx, 
     }
 
     if (!Debugger::fromChildJSObject(thisobj)->wrapDebuggeeValue(cx, &arg))
         return false;
     args.rval().set(arg);
     return true;
 }
 
-static bool
-DebuggerFrame_getArguments(JSContext* cx, unsigned argc, Value* vp)
-{
-    THIS_FRAME(cx, argc, vp, "get arguments", args, thisobj, frame);
-    Value argumentsv = thisobj->getReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS);
-    if (!argumentsv.isUndefined()) {
-        MOZ_ASSERT(argumentsv.isObjectOrNull());
-        args.rval().set(argumentsv);
-        return true;
-    }
-
-    RootedNativeObject argsobj(cx);
-    if (frame.hasArgs()) {
-        /* Create an arguments object. */
-        Rooted<GlobalObject*> global(cx, &args.callee().global());
-        RootedObject proto(cx, GlobalObject::getOrCreateArrayPrototype(cx, global));
-        if (!proto)
-            return false;
-        argsobj = NewNativeObjectWithGivenProto(cx, &DebuggerArguments_class, proto);
-        if (!argsobj)
-            return false;
-        SetReservedSlot(argsobj, JSSLOT_DEBUGARGUMENTS_FRAME, ObjectValue(*thisobj));
-
-        MOZ_ASSERT(frame.numActualArgs() <= 0x7fffffff);
-        unsigned fargc = frame.numActualArgs();
-        RootedValue fargcVal(cx, Int32Value(fargc));
-        if (!NativeDefineProperty(cx, argsobj, cx->names().length, fargcVal, nullptr, nullptr,
-                                  JSPROP_PERMANENT | JSPROP_READONLY))
+/* static */ DebuggerArguments*
+DebuggerArguments::create(JSContext* cx, HandleObject proto, HandleDebuggerFrame frame)
+{
+    AbstractFramePtr referent = DebuggerFrame::getReferent(frame);
+
+    RootedNativeObject obj(cx, NewNativeObjectWithGivenProto(cx, &DebuggerArguments::class_, proto));
+    if (!obj)
+        return nullptr;
+
+    SetReservedSlot(obj, FRAME_SLOT, ObjectValue(*frame));
+
+    MOZ_ASSERT(referent.numActualArgs() <= 0x7fffffff);
+    unsigned fargc = referent.numActualArgs();
+    RootedValue fargcVal(cx, Int32Value(fargc));
+    if (!NativeDefineProperty(cx, obj, cx->names().length, fargcVal, nullptr, nullptr,
+                              JSPROP_PERMANENT | JSPROP_READONLY))
+    {
+        return nullptr;
+    }
+
+    Rooted<jsid> id(cx);
+    for (unsigned i = 0; i < fargc; i++) {
+        RootedFunction getobj(cx);
+        getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
+                                   gc::AllocKind::FUNCTION_EXTENDED);
+        if (!getobj)
+            return nullptr;
+        id = INT_TO_JSID(i);
+        if (!getobj ||
+            !NativeDefineProperty(cx, obj, id, UndefinedHandleValue,
+                                  JS_DATA_TO_FUNC_PTR(GetterOp, getobj.get()), nullptr,
+                                  JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER))
         {
-            return false;
-        }
-
-        Rooted<jsid> id(cx);
-        for (unsigned i = 0; i < fargc; i++) {
-            RootedFunction getobj(cx);
-            getobj = NewNativeFunction(cx, DebuggerArguments_getArg, 0, nullptr,
-                                       gc::AllocKind::FUNCTION_EXTENDED);
-            if (!getobj)
-                return false;
-            id = INT_TO_JSID(i);
-            if (!getobj ||
-                !NativeDefineProperty(cx, argsobj, id, UndefinedHandleValue,
-                                      JS_DATA_TO_FUNC_PTR(GetterOp, getobj.get()), nullptr,
-                                      JSPROP_ENUMERATE | JSPROP_SHARED | JSPROP_GETTER))
-            {
-                return false;
-            }
-            getobj->setExtendedSlot(0, Int32Value(i));
-        }
-    } else {
-        argsobj = nullptr;
-    }
-    args.rval().setObjectOrNull(argsobj);
-    thisobj->setReservedSlot(JSSLOT_DEBUGFRAME_ARGUMENTS, args.rval());
+            return nullptr;
+        }
+        getobj->setExtendedSlot(0, Int32Value(i));
+    }
+
+    return &obj->as<DebuggerArguments>();
+}
+
+/* static */ bool
+DebuggerFrame::argumentsGetter(JSContext* cx, unsigned argc, Value* vp)
+{
+    THIS_DEBUGGER_FRAME(cx, argc, vp, "get arguments", args, frame);
+
+    RootedDebuggerArguments result(cx);
+    if (!DebuggerFrame::getArguments(cx, frame, &result))
+        return false;
+
+    args.rval().setObjectOrNull(result);
     return true;
 }
 
 static bool
 DebuggerFrame_getScript(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_FRAME(cx, argc, vp, "get script", args, thisobj, frame);
     Debugger* debug = Debugger::fromChildJSObject(thisobj);
@@ -8409,17 +8434,17 @@ DebuggerFrame::evalWithBindingsMethod(JS
 DebuggerFrame::construct(JSContext* cx, unsigned argc, Value* vp)
 {
     JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_NO_CONSTRUCTOR,
                               "Debugger.Frame");
     return false;
 }
 
 const JSPropertySpec DebuggerFrame::properties_[] = {
-    JS_PSG("arguments", DebuggerFrame_getArguments, 0),
+    JS_PSG("arguments", DebuggerFrame::argumentsGetter, 0),
     JS_PSG("callee", DebuggerFrame::calleeGetter, 0),
     JS_PSG("constructing", DebuggerFrame::constructingGetter, 0),
     JS_PSG("environment", DebuggerFrame::environmentGetter, 0),
     JS_PSG("generator", DebuggerFrame::generatorGetter, 0),
     JS_PSG("live", DebuggerFrame::liveGetter, 0),
     JS_PSG("offset", DebuggerFrame::offsetGetter, 0),
     JS_PSG("older", DebuggerFrame::olderGetter, 0),
     JS_PSG("script", DebuggerFrame_getScript, 0),
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1191,16 +1191,30 @@ struct Handler {
     /*
      * Traces the reference to the handler. This method will be called
      * by the reflection object on which the reference is stored whenever the
      * former is traced.
      */
     virtual void trace(JSTracer* tracer) = 0;
 };
 
+class DebuggerArguments : public NativeObject {
+  public:
+    static const Class class_;
+
+    static DebuggerArguments* create(JSContext* cx, HandleObject proto, HandleDebuggerFrame frame);
+
+  private:
+    enum {
+        FRAME_SLOT
+    };
+
+    static const unsigned RESERVED_SLOTS = 1;
+};
+
 /*
  * An OnStepHandler represents a handler function that is called when a small
  * amount of progress is made in a frame.
  */
 struct OnStepHandler : Handler {
     /*
      * If we have made a small amount of progress in a frame, this method is
      * called with the frame as argument. If succesful, this method should
@@ -1250,32 +1264,35 @@ class ScriptedOnPopHandler final : publi
                        MutableHandleValue vp) override;
 
   private:
     HeapPtr<JSObject*> object_;
 };
 
 class DebuggerFrame : public NativeObject
 {
+    friend class DebuggerArguments;
     friend class ScriptedOnStepHandler;
     friend class ScriptedOnPopHandler;
 
   public:
     enum {
         OWNER_SLOT
     };
 
     static const unsigned RESERVED_SLOTS = 1;
 
     static const Class class_;
 
     static NativeObject* initClass(JSContext* cx, HandleObject dbgCtor, HandleObject objProto);
     static DebuggerFrame* create(JSContext* cx, HandleObject proto, AbstractFramePtr referent,
                                  const ScriptFrameIter* maybeIter, HandleNativeObject debugger);
 
+    static MOZ_MUST_USE bool getArguments(JSContext* cx, HandleDebuggerFrame frame,
+                                          MutableHandleDebuggerArguments result);
     static MOZ_MUST_USE bool getCallee(JSContext* cx, HandleDebuggerFrame frame,
                                        MutableHandleDebuggerObject result);
     static MOZ_MUST_USE bool getIsConstructing(JSContext* cx, HandleDebuggerFrame frame,
                                                bool& result);
     static MOZ_MUST_USE bool getEnvironment(JSContext* cx, HandleDebuggerFrame frame,
                                             MutableHandleDebuggerEnvironment result);
     static bool getIsGenerator(HandleDebuggerFrame frame);
     static MOZ_MUST_USE bool getOffset(JSContext* cx, HandleDebuggerFrame frame, size_t& result);
@@ -1305,16 +1322,17 @@ class DebuggerFrame : public NativeObjec
     static const JSFunctionSpec methods_[];
 
     static AbstractFramePtr getReferent(HandleDebuggerFrame frame);
     static MOZ_MUST_USE bool getScriptFrameIter(JSContext* cx, HandleDebuggerFrame frame,
                                                 mozilla::Maybe<ScriptFrameIter>& result);
 
     static MOZ_MUST_USE bool construct(JSContext* cx, unsigned argc, Value* vp);
 
+    static MOZ_MUST_USE bool argumentsGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool calleeGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool constructingGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool environmentGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool generatorGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool liveGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool offsetGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool olderGetter(JSContext* cx, unsigned argc, Value* vp);
     static MOZ_MUST_USE bool thisGetter(JSContext* cx, unsigned argc, Value* vp);