Bug 1131707 - Transparently forward the construct bit for function forwarders. r=gabor
authorBobby Holley <bobbyholley@gmail.com>
Tue, 05 May 2015 13:28:44 -0700
changeset 274076 3e4fa121bc55bfc7738cdfbf764771095d375c56
parent 274075 be70d2673c3541a2fc488d39fd927760adb59094
child 274077 f5bbfe33ed78953ad98391e02315e14a96017aa9
push id863
push userraliiev@mozilla.com
push dateMon, 03 Aug 2015 13:22:43 +0000
treeherdermozilla-release@f6321b14228d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgabor
bugs1131707
milestone40.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 1131707 - Transparently forward the construct bit for function forwarders. r=gabor
js/xpconnect/src/ExportHelpers.cpp
js/xpconnect/tests/unit/test_bug1131707.js
js/xpconnect/tests/unit/xpcshell.ini
--- a/js/xpconnect/src/ExportHelpers.cpp
+++ b/js/xpconnect/src/ExportHelpers.cpp
@@ -319,55 +319,59 @@ FunctionForwarder(JSContext* cx, unsigne
     FunctionForwarderOptions options(cx, optionsObj);
     if (!options.Parse())
         return false;
 
     // Grab and unwrap the underlying callable.
     RootedValue v(cx, js::GetFunctionNativeReserved(&args.callee(), 0));
     RootedObject unwrappedFun(cx, js::UncheckedUnwrap(&v.toObject()));
 
-    RootedObject thisObj(cx, JS_THIS_OBJECT(cx, vp));
-    if (!thisObj) {
-        return false;
-    }
-
+    RootedObject thisObj(cx, args.isConstructing() ? nullptr : JS_THIS_OBJECT(cx, vp));
     {
         // We manually implement the contents of CrossCompartmentWrapper::call
         // here, because certain function wrappers (notably content->nsEP) are
         // not callable.
         JSAutoCompartment ac(cx, unwrappedFun);
 
-        RootedValue thisVal(cx, ObjectValue(*thisObj));
+        RootedValue thisVal(cx, ObjectOrNullValue(thisObj));
         if (!CheckSameOriginArg(cx, options, thisVal) || !JS_WrapObject(cx, &thisObj))
             return false;
 
         for (size_t n = 0;  n < args.length(); ++n) {
             if (!CheckSameOriginArg(cx, options, args[n]) || !JS_WrapValue(cx, args[n]))
                 return false;
         }
 
         RootedValue fval(cx, ObjectValue(*unwrappedFun));
-        if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
-            return false;
+        if (args.isConstructing()) {
+            if (!JS::Construct(cx, fval, args, args.rval()))
+                return false;
+        } else {
+            if (!JS_CallFunctionValue(cx, thisObj, fval, args, args.rval()))
+                return false;
+        }
     }
 
     // Rewrap the return value into our compartment.
     return JS_WrapValue(cx, args.rval());
 }
 
 bool
 NewFunctionForwarder(JSContext* cx, HandleId idArg, HandleObject callable,
                      FunctionForwarderOptions& options, MutableHandleValue vp)
 {
     RootedId id(cx, idArg);
     if (id == JSID_VOIDHANDLE)
         id = GetRTIdByIndex(cx, XPCJSRuntime::IDX_EMPTYSTRING);
 
+    // We have no way of knowing whether the underlying function wants to be a
+    // constructor or not, so we just mark all forwarders as constructors, and
+    // let the underlying function throw for construct calls if it wants.
     JSFunction* fun = js::NewFunctionByIdWithReserved(cx, FunctionForwarder,
-                                                      0, 0, id);
+                                                      0, JSFUN_CONSTRUCTOR, id);
     if (!fun)
         return false;
 
     // Stash the callable in slot 0.
     AssertSameCompartment(cx, callable);
     RootedObject funobj(cx, JS_GetFunctionObject(fun));
     js::SetFunctionNativeReserved(funobj, 0, ObjectValue(*callable));
 
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_bug1131707.js
@@ -0,0 +1,22 @@
+const Cu = Components.utils;
+
+function testStrict(sb) {
+  "use strict";
+  do_check_eq(sb.eval("typeof wrappedCtor()"), "string");
+  do_check_eq(sb.eval("typeof new wrappedCtor()"), "object");
+}
+
+function run_test() {
+  var sb = new Cu.Sandbox(null);
+  var dateCtor = sb.Date;
+  sb.wrappedCtor = Cu.exportFunction(function wrapper(val) {
+    "use strict";
+    var constructing = this.constructor == wrapper;
+    return constructing ? new dateCtor(val) : dateCtor(val);
+  }, sb);
+  do_check_eq(typeof Date(), "string");
+  do_check_eq(typeof new Date(), "object");
+  do_check_eq(sb.eval("typeof wrappedCtor()"), "string");
+  do_check_eq(sb.eval("typeof new wrappedCtor()"), "object");
+  testStrict(sb);
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -49,16 +49,17 @@ support-files =
 [test_bug1021312.js]
 [test_bug1033253.js]
 [test_bug1033920.js]
 [test_bug1033927.js]
 [test_bug1034262.js]
 [test_bug1082450.js]
 [test_bug1081990.js]
 [test_bug1110546.js]
+[test_bug1131707.js]
 [test_bug1150771.js]
 [test_bug1151385.js]
 [test_bug_442086.js]
 [test_callFunctionWithAsyncStack.js]
 [test_file.js]
 [test_blob.js]
 [test_blob2.js]
 [test_file2.js]