Bug 1453057 - Make Xrays to Arguments objects work correctly. r=bzbarsky
authorBobby Holley <bobbyholley@gmail.com>
Fri, 14 Sep 2018 16:14:49 +0000
changeset 436364 e2128a59b167cda5861f4a83b218f94af1e75005
parent 436363 6f6886c89554f9face1b3e8a7090cc0e85800375
child 436365 841dccc8771929583260a968a97910adad8c4a6b
push id34643
push userbtara@mozilla.com
push dateFri, 14 Sep 2018 21:48:55 +0000
treeherdermozilla-central@750e71a8f79b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbzbarsky
bugs1453057
milestone64.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 1453057 - Make Xrays to Arguments objects work correctly. r=bzbarsky MozReview-Commit-ID: XeWrs0cNL5 Differential Revision: https://phabricator.services.mozilla.com/D5074
js/src/jsfriendapi.cpp
js/src/jsfriendapi.h
js/xpconnect/tests/unit/test_xrayed_arguments.js
js/xpconnect/tests/unit/xpcshell.ini
js/xpconnect/wrappers/XrayWrapper.cpp
testing/web-platform/meta/webdriver/tests/execute_async_script/collections.py.ini
--- a/js/src/jsfriendapi.cpp
+++ b/js/src/jsfriendapi.cpp
@@ -341,16 +341,22 @@ js::GetBuiltinClass(JSContext* cx, Handl
 #endif
     } else {
         *cls = ESClass::Other;
     }
 
     return true;
 }
 
+JS_FRIEND_API(bool)
+js::IsArgumentsObject(HandleObject obj)
+{
+    return obj->is<ArgumentsObject>();
+}
+
 JS_FRIEND_API(const char*)
 js::ObjectClassName(JSContext* cx, HandleObject obj)
 {
     cx->check(obj);
     return GetObjectClassName(cx, obj);
 }
 
 JS_FRIEND_API(JS::Zone*)
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -219,16 +219,19 @@ JS_InitializePropertiesFromCompatibleNat
                                                   JS::HandleObject dst,
                                                   JS::HandleObject src);
 
 namespace js {
 
 JS_FRIEND_API(bool)
 GetBuiltinClass(JSContext* cx, JS::HandleObject obj, ESClass* cls);
 
+JS_FRIEND_API(bool)
+IsArgumentsObject(JS::HandleObject obj);
+
 JS_FRIEND_API(const char*)
 ObjectClassName(JSContext* cx, JS::HandleObject obj);
 
 JS_FRIEND_API(void)
 ReportOverRecursed(JSContext* maybecx);
 
 JS_FRIEND_API(bool)
 AddRawValueRoot(JSContext* cx, JS::Value* vp, const char* name);
new file mode 100644
--- /dev/null
+++ b/js/xpconnect/tests/unit/test_xrayed_arguments.js
@@ -0,0 +1,16 @@
+function run_test() {
+  var sbContent = Cu.Sandbox(null);
+  let xrayedArgs = sbContent.eval("(function(a, b) { return arguments; })('hi', 42)");
+
+  function checkArgs(a) {
+    Assert.equal(a.length, 2);
+    Assert.equal(a[0], 'hi');
+    Assert.equal(a[1], 42);
+  }
+
+  // Check Xrays to the args.
+  checkArgs(xrayedArgs);
+
+  // Make sure the spread operator works.
+  checkArgs([...xrayedArgs]);
+}
--- a/js/xpconnect/tests/unit/xpcshell.ini
+++ b/js/xpconnect/tests/unit/xpcshell.ini
@@ -130,16 +130,17 @@ head = head_watchdog.js
 head = head_watchdog.js
 [test_watchdog_default.js]
 head = head_watchdog.js
 skip-if = (verify && debug && os == 'android')
 [test_watchdog_hibernate.js]
 head = head_watchdog.js
 [test_weak_keys.js]
 [test_xpcwn_tamperproof.js]
+[test_xrayed_arguments.js]
 [test_xrayed_iterator.js]
 [test_xray_named_element_access.js]
 [test_xray_SavedFrame.js]
 [test_xray_SavedFrame-02.js]
 [test_xray_regexp.js]
 [test_resolve_dead_promise.js]
 [test_asyncLoadSubScriptError.js]
 [test_function_names.js]
--- a/js/xpconnect/wrappers/XrayWrapper.cpp
+++ b/js/xpconnect/wrappers/XrayWrapper.cpp
@@ -1035,16 +1035,39 @@ JSXrayTraits::createHolder(JSContext* cx
     bool isPrototype = false;
     JSProtoKey key = IdentifyStandardInstance(target);
     if (key == JSProto_Null) {
         isPrototype = true;
         key = IdentifyStandardPrototype(target);
     }
     MOZ_ASSERT(key != JSProto_Null);
 
+    // Special case: pretend Arguments objects are arrays for Xrays.
+    //
+    // Arguments objects are strange beasts - they inherit Object.prototype,
+    // and implement iteration by defining an |own| property for
+    // Symbol.iterator. Since this value is callable, Array/Object Xrays will
+    // filter it out, causing the Xray view to be non-iterable, which in turn
+    // breaks consumers.
+    //
+    // We can't trust the iterator value from the content compartment,
+    // but the generic one on Array.prototype works well enough. So we force
+    // the Xray view of Arguments objects to inherit Array.prototype, which
+    // in turn allows iteration via the inherited Array.prototype[Symbol.iterator].
+    // This doesn't emulate any of the weird semantics of Arguments iterators,
+    // but is probably good enough.
+    //
+    // Note that there are various Xray traps that do other special behavior for
+    // JSProto_Array, but they also provide that special behavior for
+    // JSProto_Object, and since Arguments would otherwise get JSProto_Object,
+    // this does not cause any behavior change at those sites.
+    if (key == JSProto_Object && js::IsArgumentsObject(target)) {
+        key = JSProto_Array;
+    }
+
     // Store it on the holder.
     RootedValue v(cx);
     v.setNumber(static_cast<uint32_t>(key));
     js::SetReservedSlot(holder, SLOT_PROTOKEY, v);
     v.setBoolean(isPrototype);
     js::SetReservedSlot(holder, SLOT_ISPROTOTYPE, v);
 
     // If this is a function, also compute whether it serves as a constructor
deleted file mode 100644
--- a/testing/web-platform/meta/webdriver/tests/execute_async_script/collections.py.ini
+++ /dev/null
@@ -1,5 +0,0 @@
-[collections.py]
-  [test_arguments]
-    bug: 1453057
-    expected: FAIL
-