Bug 974546 - SpecialPowers wrappers should catch, wrap, and rethrow exceptions for callables. r=mrbkap
authorBobby Holley <bobbyholley@gmail.com>
Thu, 20 Feb 2014 08:57:20 -0800
changeset 170064 7dc90f3c1a213ba65292985de8c3d7c002bc8512
parent 170063 0d41267969e125873702e078a63e7adf8aff6328
child 170065 3da0c8a851f80a3d3433498cb80610a66003650d
child 170098 b89a9d7b4ca0a9f72d0b904218168e9cdc06a11b
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewersmrbkap
bugs974546
milestone30.0a1
Bug 974546 - SpecialPowers wrappers should catch, wrap, and rethrow exceptions for callables. r=mrbkap
testing/mochitest/tests/test_SpecialPowersExtension.html
testing/specialpowers/content/specialpowersAPI.js
--- a/testing/mochitest/tests/test_SpecialPowersExtension.html
+++ b/testing/mochitest/tests/test_SpecialPowersExtension.html
@@ -136,16 +136,34 @@ function starttest(){
   catch (e) {
     ok(false, "Threw while getting quickstubbed accessor prop from proto");
   }
 
   // Check functions that return null.
   var returnsNull = function() { return null; }
   is(SpecialPowers.wrap(returnsNull)(), null, "Should be able to handle functions that return null.");
 
+  // Check a function that throws.
+  var thrower = function() { throw new Error('hah'); }
+  try {
+    SpecialPowers.wrap(thrower)();
+    ok(false, "Should have thrown");
+  } catch (e) {
+    ok(SpecialPowers.isWrapper(e), "Exceptions should be wrapped for call");
+    is(e.message, 'hah', "Correct message");
+  }
+  try {
+    var ctor = SpecialPowers.wrap(thrower);
+    new ctor();
+    ok(false, "Should have thrown");
+  } catch (e) {
+    ok(SpecialPowers.isWrapper(e), "Exceptions should be wrapped for construct");
+    is(e.message, 'hah', "Correct message");
+  }
+
   // Play around with a JS object to check the non-xray path.
   var noxray_proto = {a: 3, b: 12};
   var noxray = {a: 5, c: 32};
   noxray.__proto__ = noxray_proto;
   var noxray_wrapper = SpecialPowers.wrap(noxray);
   is(noxray_wrapper.c, 32, "Regular properties should work.");
   is(noxray_wrapper.a, 5, "Shadow properties should work.");
   is(noxray_wrapper.b, 12, "Proto properties should work.");
--- a/testing/specialpowers/content/specialpowersAPI.js
+++ b/testing/specialpowers/content/specialpowersAPI.js
@@ -110,31 +110,41 @@ function wrapPrivileged(obj) {
 
   // If the object is callable, make a function proxy.
   if (typeof obj === "function") {
     var callTrap = function() {
       // The invocant and arguments may or may not be wrappers. Unwrap them if necessary.
       var invocant = unwrapIfWrapped(this);
       var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
 
-      return wrapPrivileged(doApply(obj, invocant, unwrappedArgs));
+      try {
+        return wrapPrivileged(doApply(obj, invocant, unwrappedArgs));
+      } catch (e) {
+        // Wrap exceptions and re-throw them.
+        throw wrapIfUnwrapped(e);
+      }
     };
     var constructTrap = function() {
       // The arguments may or may not be wrappers. Unwrap them if necessary.
       var unwrappedArgs = Array.prototype.slice.call(arguments).map(unwrapIfWrapped);
 
       // Constructors are tricky, because we can't easily call apply on them.
       // As a workaround, we create a wrapper constructor with the same
       // |prototype| property. ES semantics dictate that the return value from
       // |new| is the return value of the |new|-ed function i.f.f. the returned
       // value is an object. We can thus mimic the behavior of |new|-ing the
       // underlying constructor just be passing along its return value in our
       // constructor.
       var FakeConstructor = function() {
-        return doApply(obj, this, unwrappedArgs);
+        try {
+          return doApply(obj, this, unwrappedArgs);
+        } catch (e) {
+          // Wrap exceptions and re-throw them.
+          throw wrapIfUnwrapped(e);
+        }
       };
       FakeConstructor.prototype = obj.prototype;
 
       return wrapPrivileged(new FakeConstructor());
     };
 
     return Proxy.createFunction(handler, callTrap, constructTrap);
   }