Bug 1271653 - Implement a C++ interface for isCallable/isArrowFunction/parameterNames;r=jimb
authorEddy Bruel <ejpbruel@mozilla.com>
Tue, 07 Jun 2016 16:29:43 +0200
changeset 300870 a3b2760970aaf20ea39a527ff6c848c96ef05692
parent 300869 a0865e196a4d6bf0eb86f02f779af2e7706ddbff
child 300871 8bca03ed2ecbb748bf18d6ac982116de94995c11
push id78111
push userejpbruel@mozilla.com
push dateTue, 07 Jun 2016 14:29:55 +0000
treeherdermozilla-inbound@a3b2760970aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb
bugs1271653
milestone50.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 1271653 - Implement a C++ interface for isCallable/isArrowFunction/parameterNames;r=jimb
js/src/jit-test/tests/debug/Object-isArrowFunction.js
js/src/jit-test/tests/debug/Object-script-lazy.js
js/src/jsapi.h
js/src/vm/Debugger.cpp
js/src/vm/Debugger.h
--- a/js/src/jit-test/tests/debug/Object-isArrowFunction.js
+++ b/js/src/jit-test/tests/debug/Object-isArrowFunction.js
@@ -10,13 +10,13 @@ function checkIsArrow(shouldBe, expr) {
   assertEq(gDO.executeInGlobal(expr).return.isArrowFunction, shouldBe);
 }
 
 checkIsArrow(true, '() => { }');
 checkIsArrow(true, '(a) => { bleh; }');
 checkIsArrow(false, 'Object.getPrototypeOf(() => { })');
 checkIsArrow(false, '(function () { })');
 checkIsArrow(false, 'function f() { } f');
-checkIsArrow(false, '({})');
+checkIsArrow((void 0), '({})');
 checkIsArrow(false, 'Math.atan2');
 checkIsArrow(false, 'Function.prototype');
 checkIsArrow(false, 'Function("")');
 checkIsArrow(false, 'new Function("")');
--- a/js/src/jit-test/tests/debug/Object-script-lazy.js
+++ b/js/src/jit-test/tests/debug/Object-script-lazy.js
@@ -38,22 +38,24 @@ assertEq(gDO.global, g2w);
 assertEq(gDO.unwrap().global === g2w, false);
 assertEq(gDO.unwrap().parameterNames, undefined);
 
 // Similarly for g1.h, and asking for its bound function properties.
 var hDO = g2w.getOwnPropertyDescriptor('h').value;
 assertEq(hDO.global, g2w);
 assertEq(hDO.unwrap().global === g2w, false);
 assertEq(hDO.unwrap().isBoundFunction, undefined);
+assertEq(hDO.unwrap().isArrowFunction, undefined);
 assertEq(hDO.unwrap().boundTargetFunction, undefined);
 assertEq(hDO.unwrap().boundThis, undefined);
 assertEq(hDO.unwrap().boundArguments, undefined);
 
 // Add g1 as a debuggee, and verify that we can get everything.
 dbg.addDebuggee(g1);
 assertEq(fDO.unwrap().script instanceof Debugger.Script, true);
 assertEq(gDO.unwrap().parameterNames instanceof Array, true);
 assertEq(hDO.unwrap().isBoundFunction, true);
+assertEq(hDO.unwrap().isArrowFunction, false);
 assertEq(hDO.unwrap().boundTargetFunction, fDO.unwrap());
 assertEq(hDO.unwrap().boundThis, gDO.unwrap());
 assertEq(hDO.unwrap().boundArguments.length, 2);
 assertEq(hDO.unwrap().boundArguments[0], 42);
 assertEq(hDO.unwrap().boundArguments[1], "foo");
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -217,16 +217,17 @@ class MOZ_RAII AutoVectorRooter : public
 
 typedef AutoVectorRooter<Value> AutoValueVector;
 typedef AutoVectorRooter<jsid> AutoIdVector;
 typedef AutoVectorRooter<JSObject*> AutoObjectVector;
 
 using ValueVector = JS::GCVector<JS::Value>;
 using IdVector = JS::GCVector<jsid>;
 using ScriptVector = JS::GCVector<JSScript*>;
+using StringVector = JS::GCVector<JSString*>;
 
 template<class Key, class Value>
 class MOZ_RAII AutoHashMapRooter : protected AutoGCRooter
 {
   private:
     typedef js::HashMap<Key, Value> HashMapImpl;
 
   public:
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -7893,18 +7893,19 @@ DebuggerObject_getClass(JSContext* cx, u
 
     args.rval().setString(result);
     return true;
 }
 
 static bool
 DebuggerObject_getCallable(JSContext* cx, unsigned argc, Value* vp)
 {
-    THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get callable", args, refobj);
-    args.rval().setBoolean(refobj->isCallable());
+    THIS_DEBUGOBJECT(cx, argc, vp, "get callable", args, object)
+
+    args.rval().setBoolean(DebuggerObject::isCallable(cx, object));
     return true;
 }
 
 static bool
 DebuggerObject_getName(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT(cx, argc, vp, "get name", args, object)
 
@@ -7943,60 +7944,42 @@ DebuggerObject_getDisplayName(JSContext*
     else
         args.rval().setUndefined();
     return true;
 }
 
 static bool
 DebuggerObject_getParameterNames(JSContext* cx, unsigned argc, Value* vp)
 {
-    THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get parameterNames", args, dbg, obj);
-    if (!obj->is<JSFunction>()) {
-        args.rval().setUndefined();
-        return true;
-    }
-
-    RootedFunction fun(cx, &obj->as<JSFunction>());
-
-    /* Only hand out parameter info for debuggee functions. */
-    if (!dbg->observesGlobal(&fun->global())) {
+    THIS_DEBUGOBJECT(cx, argc, vp, "get parameterNames", args, object)
+
+    if (!DebuggerObject::isDebuggeeFunction(cx, object)) {
         args.rval().setUndefined();
         return true;
     }
 
-    RootedArrayObject result(cx, NewDenseFullyAllocatedArray(cx, fun->nargs()));
-    if (!result)
-        return false;
-    result->ensureDenseInitializedLength(cx, 0, fun->nargs());
-
-    if (fun->isInterpreted()) {
-        RootedScript script(cx, GetOrCreateFunctionScript(cx, fun));
-        if (!script)
-            return false;
-
-        MOZ_ASSERT(fun->nargs() == script->bindings.numArgs());
-
-        if (fun->nargs() > 0) {
-            BindingIter bi(script);
-            for (size_t i = 0; i < fun->nargs(); i++, bi++) {
-                MOZ_ASSERT(bi.argIndex() == i);
-                Value v;
-                if (bi->name()->length() == 0)
-                    v = UndefinedValue();
-                else
-                    v = StringValue(bi->name());
-                result->setDenseElement(i, v);
-            }
-        }
-    } else {
-        for (size_t i = 0; i < fun->nargs(); i++)
-            result->setDenseElement(i, UndefinedValue());
-    }
-
-    args.rval().setObject(*result);
+    Rooted<StringVector> names(cx, StringVector(cx));
+    if (!DebuggerObject::parameterNames(cx, object, &names))
+        return false;
+
+    RootedArrayObject obj(cx, NewDenseFullyAllocatedArray(cx, names.length()));
+    if (!obj)
+        return false;
+
+    obj->ensureDenseInitializedLength(cx, 0, names.length());
+    for (size_t i = 0; i < names.length(); ++i) {
+        Value v;
+        if (names[i])
+            v = StringValue(names[i]);
+        else
+            v = UndefinedValue();
+        obj->setDenseElement(i, v);
+    }
+
+    args.rval().setObject(*obj);
     return true;
 }
 
 static bool
 DebuggerObject_getScript(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT_OWNER_REFERENT(cx, argc, vp, "get script", args, dbg, obj);
 
@@ -8056,20 +8039,24 @@ DebuggerObject_getEnvironment(JSContext*
     }
 
     return dbg->wrapEnvironment(cx, env, args.rval());
 }
 
 static bool
 DebuggerObject_getIsArrowFunction(JSContext* cx, unsigned argc, Value* vp)
 {
-    THIS_DEBUGOBJECT_REFERENT(cx, argc, vp, "get isArrowFunction", args, refobj);
-
-    args.rval().setBoolean(refobj->is<JSFunction>()
-                           && refobj->as<JSFunction>().isArrow());
+    THIS_DEBUGOBJECT(cx, argc, vp, "get isArrowFunction", args, object)
+
+    if (!DebuggerObject::isDebuggeeFunction(cx, object)) {
+        args.rval().setUndefined();
+        return true;
+    }
+
+    args.rval().setBoolean(DebuggerObject::isArrowFunction(cx, object));
     return true;
 }
 
 static bool
 DebuggerObject_getIsBoundFunction(JSContext* cx, unsigned argc, Value* vp)
 {
     THIS_DEBUGOBJECT(cx, argc, vp, "get isBoundFunction", args, object)
 
@@ -8858,16 +8845,24 @@ DebuggerObject::create(JSContext* cx, Ha
   DebuggerObject& object = obj->as<DebuggerObject>();
   object.setPrivateGCThing(referent);
   object.setReservedSlot(JSSLOT_DEBUGOBJECT_OWNER, ObjectValue(*debugger));
 
   return &object;
 }
 
 /* static */ bool
+DebuggerObject::isCallable(JSContext* cx, Handle<DebuggerObject*> object)
+{
+    RootedObject referent(cx, object->referent());
+
+    return referent->isCallable();
+}
+
+/* static */ bool
 DebuggerObject::isFunction(JSContext* cx, Handle<DebuggerObject*> object)
 {
     RootedObject referent(cx, object->referent());
 
     return referent->is<JSFunction>();
 }
 
 /* static */ bool
@@ -8876,16 +8871,36 @@ DebuggerObject::isDebuggeeFunction(JSCon
     RootedObject referent(cx, object->referent());
     Debugger* dbg = object->owner();
 
     return referent->is<JSFunction>() &&
            dbg->observesGlobal(&referent->as<JSFunction>().global());
 }
 
 /* static */ bool
+DebuggerObject::isBoundFunction(JSContext* cx, Handle<DebuggerObject*> object)
+{
+    MOZ_ASSERT(isDebuggeeFunction(cx, object));
+
+    RootedFunction referent(cx, &object->referent()->as<JSFunction>());
+
+    return referent->isBoundFunction();
+}
+
+/* static */ bool
+DebuggerObject::isArrowFunction(JSContext* cx, Handle<DebuggerObject*> object)
+{
+    MOZ_ASSERT(isDebuggeeFunction(cx, object));
+
+    RootedFunction referent(cx, &object->referent()->as<JSFunction>());
+
+    return referent->isArrow();
+}
+
+/* static */ bool
 DebuggerObject::isGlobal(JSContext* cx, Handle<DebuggerObject*> object)
 {
     RootedObject referent(cx, object->referent());
 
     return referent->is<GlobalObject>();
 }
 
 /* static */ bool
@@ -8927,23 +8942,48 @@ DebuggerObject::displayName(JSContext* c
 
     RootedFunction referent(cx, &object->referent()->as<JSFunction>());
 
     result.set(referent->displayAtom());
     return true;
 }
 
 /* static */ bool
-DebuggerObject::isBoundFunction(JSContext* cx, Handle<DebuggerObject*> object)
+DebuggerObject::parameterNames(JSContext* cx, Handle<DebuggerObject*> object,
+                               MutableHandle<StringVector> result)
 {
     MOZ_ASSERT(isDebuggeeFunction(cx, object));
 
-    RootedObject referent(cx, object->referent());
-
-    return referent->isBoundFunction();
+    RootedFunction referent(cx, &object->referent()->as<JSFunction>());
+
+    if (!result.growBy(referent->nargs()))
+        return false;
+    if (referent->isInterpreted()) {
+        RootedScript script(cx, GetOrCreateFunctionScript(cx, referent));
+        if (!script)
+            return false;
+
+        MOZ_ASSERT(referent->nargs() == script->bindings.numArgs());
+
+        if (referent->nargs() > 0) {
+            BindingIter bi(script);
+            for (size_t i = 0; i < referent->nargs(); i++, bi++) {
+                MOZ_ASSERT(bi.argIndex() == i);
+                if (bi->name()->length() == 0)
+                    result[i].set(nullptr);
+                else
+                    result[i].set(bi->name());
+            }
+        }
+    } else {
+        for (size_t i = 0; i < referent->nargs(); i++)
+            result[i].set(nullptr);
+    }
+
+    return true;
 }
 
 /* static */ bool
 DebuggerObject::boundTargetFunction(JSContext* cx, Handle<DebuggerObject*> object,
                                     MutableHandleObject result)
 {
     MOZ_ASSERT(isBoundFunction(cx, object));
 
--- a/js/src/vm/Debugger.h
+++ b/js/src/vm/Debugger.h
@@ -1046,25 +1046,30 @@ class DebuggerObject : public NativeObje
 {
   public:
     static const Class class_;
 
     static NativeObject* initClass(JSContext* cx, HandleObject obj, HandleObject debugCtor);
     static DebuggerObject* create(JSContext* cx, HandleObject proto, HandleObject obj,
                                   HandleNativeObject debugger);
 
+    static bool isCallable(JSContext* cx, Handle<DebuggerObject*> object);
     static bool isFunction(JSContext* cx, Handle<DebuggerObject*> object);
     static bool isDebuggeeFunction(JSContext* cx, Handle<DebuggerObject*> object);
+    static bool isBoundFunction(JSContext* cx, Handle<DebuggerObject*> object);
+    static bool isArrowFunction(JSContext* cx, Handle<DebuggerObject*> object);
     static bool isGlobal(JSContext* cx, Handle<DebuggerObject*> object);
     static bool className(JSContext* cx, Handle<DebuggerObject*> object,
                           MutableHandleString result);
     static bool name(JSContext* cx, Handle<DebuggerObject*> object, MutableHandleString result);
     static bool displayName(JSContext* cx, Handle<DebuggerObject*> object,
                             MutableHandleString result);
-    static bool isBoundFunction(JSContext* cx, Handle<DebuggerObject*> object);
+
+    static bool parameterNames(JSContext* cx, Handle<DebuggerObject*> object,
+                               MutableHandle<StringVector> result);
     static bool boundTargetFunction(JSContext* cx, Handle<DebuggerObject*> object,
                                     MutableHandleObject result);
     static bool boundThis(JSContext* cx, Handle<DebuggerObject*> object,
                           MutableHandleValue result);
     static bool boundArguments(JSContext* cx, Handle<DebuggerObject*> object,
                                MutableHandle<ValueVector> result);
 
     static bool isExtensible(JSContext* cx, Handle<DebuggerObject*> object, bool& result);