Web Inspector: Crash generating object preview for ArrayIterator
authorjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 Jun 2017 17:43:03 +0000 (17:43 +0000)
committerjoepeck@webkit.org <joepeck@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 27 Jun 2017 17:43:03 +0000 (17:43 +0000)
https://bugs.webkit.org/show_bug.cgi?id=173754
<rdar://problem/32859012>

Reviewed by Saam Barati.

Source/JavaScriptCore:

When Inspector generates an object preview for an ArrayIterator instance it made
a "clone" of the original ArrayIterator instance by constructing a new object with
the instance's structure. However, user code could have modified that instance's
structure, such as adding / removing properties. The `return` property had special
meaning, and our clone did not fill that slot. This approach is brittle in that
we weren't satisfying the expectations of an object with a particular Structure,
and the original goal of having Web Inspector peek values of built-in Iterators
was to avoid observable behavior.

This tightens Web Inspector's Iterator preview to only peek values if the
Iterators would actually be non-observable. It also builds an ArrayIterator
clone like a regular object construction.

* inspector/JSInjectedScriptHost.cpp:
(Inspector::cloneArrayIteratorObject):
Build up the Object from scratch with a new ArrayIterator prototype.

(Inspector::JSInjectedScriptHost::iteratorEntries):
Only clone and peek iterators if it would not be observable.
Also update iteration to be more in line with IterationOperations, such as when
we call iteratorClose.

* runtime/JSGlobalObject.cpp:
(JSC::JSGlobalObject::JSGlobalObject):
(JSC::JSGlobalObject::init):
* runtime/JSGlobalObject.h:
(JSC::JSGlobalObject::stringIteratorProtocolWatchpoint):
* runtime/JSGlobalObjectInlines.h:
(JSC::JSGlobalObject::isStringPrototypeIteratorProtocolFastAndNonObservable):
Add a StringIterator WatchPoint in line with the Array/Map/Set iterator watchpoints.

* runtime/JSMap.cpp:
(JSC::JSMap::isIteratorProtocolFastAndNonObservable):
(JSC::JSMap::canCloneFastAndNonObservable):
* runtime/JSMap.h:
* runtime/JSSet.cpp:
(JSC::JSSet::isIteratorProtocolFastAndNonObservable):
(JSC::JSSet::canCloneFastAndNonObservable):
* runtime/JSSet.h:
Promote isIteratorProtocolFastAndNonObservable to a method.

* runtime/JSObject.cpp:
(JSC::canDoFastPutDirectIndex):
* runtime/JSTypeInfo.h:
(JSC::TypeInfo::isArgumentsType):
Helper to detect if an Object is an Arguments type.

LayoutTests:

* platform/mac/inspector/model/remote-object-expected.txt:
* inspector/model/remote-object-expected.txt:
* inspector/model/remote-object.html:
Test generating a preview for an ArrayIterator that has had a `return` property added to it.

* inspector/model/remote-object-mutated-iterators-expected.txt: Added.
* inspector/model/remote-object-mutated-iterators.html: Added.
Test generating a preview for different iterators after the IteratorPrototypes have been mutated.

* inspector/model/resources/remote-object-utilities.js: Added.
(runInBrowserTest):
(TestPage.registerInitializer):
(TestPage.registerInitializer.checkComplete):
(TestPage.registerInitializer.window.runSteps):
Share code for remote-object dump tests.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@218836 268f45cc-cd09-0410-ab3c-d52691b4dbfc

18 files changed:
LayoutTests/ChangeLog
LayoutTests/inspector/model/remote-object-expected.txt
LayoutTests/inspector/model/remote-object-mutated-iterators-expected.txt [new file with mode: 0644]
LayoutTests/inspector/model/remote-object-mutated-iterators.html [new file with mode: 0644]
LayoutTests/inspector/model/remote-object.html
LayoutTests/inspector/model/resources/remote-object-utilities.js [new file with mode: 0644]
LayoutTests/platform/mac/inspector/model/remote-object-expected.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/inspector/JSInjectedScriptHost.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.cpp
Source/JavaScriptCore/runtime/JSGlobalObject.h
Source/JavaScriptCore/runtime/JSGlobalObjectInlines.h
Source/JavaScriptCore/runtime/JSMap.cpp
Source/JavaScriptCore/runtime/JSMap.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSSet.cpp
Source/JavaScriptCore/runtime/JSSet.h
Source/JavaScriptCore/runtime/JSTypeInfo.h

index 15154f6..a65fe36 100644 (file)
@@ -1,3 +1,27 @@
+2017-06-27  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Crash generating object preview for ArrayIterator
+        https://bugs.webkit.org/show_bug.cgi?id=173754
+        <rdar://problem/32859012>
+
+        Reviewed by Saam Barati.
+
+        * platform/mac/inspector/model/remote-object-expected.txt:
+        * inspector/model/remote-object-expected.txt:
+        * inspector/model/remote-object.html:
+        Test generating a preview for an ArrayIterator that has had a `return` property added to it.
+
+        * inspector/model/remote-object-mutated-iterators-expected.txt: Added.
+        * inspector/model/remote-object-mutated-iterators.html: Added.
+        Test generating a preview for different iterators after the IteratorPrototypes have been mutated.
+
+        * inspector/model/resources/remote-object-utilities.js: Added.
+        (runInBrowserTest):
+        (TestPage.registerInitializer):
+        (TestPage.registerInitializer.checkComplete):
+        (TestPage.registerInitializer.window.runSteps):
+        Share code for remote-object dump tests.
+
 2017-06-27  Frederic Wang  <fwang@igalia.com>
 
         Some tests to verify forbidden frame navigation time out
index 3a5e6b9..fcaf515 100644 (file)
@@ -3922,6 +3922,79 @@ EXPRESSION: x = undefined; (function() { x = arguments; })(1, 'two'); x[Symbol.i
 }
 
 -----------------------------------------------------
+EXPRESSION: iter = [1, 2][Symbol.iterator](); iter['return'] = function(){}; iter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
 EXPRESSION: new Promise(function(){})
 {
   "_type": "object",
diff --git a/LayoutTests/inspector/model/remote-object-mutated-iterators-expected.txt b/LayoutTests/inspector/model/remote-object-mutated-iterators-expected.txt
new file mode 100644 (file)
index 0000000..d72fcef
--- /dev/null
@@ -0,0 +1,598 @@
+
+-----------------------------------------------------
+EXPRESSION: iter = 'alpha'[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "String Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "String Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "string",
+        "_type": "string",
+        "_value": "alpha",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "a",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "l",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "p",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "h",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "a",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: iter.__proto__.next = function(){}; iter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "String Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "String Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "string",
+        "_type": "string",
+        "_value": "alpha",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: map = new Map; map.set(1, 2); map.set('key', 'value'); iter = map[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key+value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "string",
+              "_value": "key"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "value"
+            }
+          ],
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: iter.__proto__.next = function(){}; iter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Map Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Map Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "map",
+        "_type": "object",
+        "_subtype": "map",
+        "_value": "Map",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "key+value",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: set = new Set; for (var i = 0; i <= 100; i++) set.add(i); iter = set[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Set Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Set Iterator",
+    "_lossless": false,
+    "_overflow": true,
+    "_properties": [
+      {
+        "_name": "set",
+        "_type": "object",
+        "_subtype": "set",
+        "_value": "Set",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "0",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "4",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: iter.__proto__.next = function(){}; iter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Set Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Set Iterator",
+    "_lossless": false,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "set",
+        "_type": "object",
+        "_subtype": "set",
+        "_value": "Set",
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arrayIter = [1,2,3][Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 3,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            },
+            {
+              "_name": "2",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: x = undefined; (function() { x = arguments; })(3, 'arg'); argumentsIter = x[Symbol.iterator]()
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Arguments",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "3"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "arg"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "3",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "string",
+          "_description": "arg",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: arrayIter.__proto__.next = function(){}; arrayIter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 3,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            },
+            {
+              "_name": "2",
+              "_type": "number",
+              "_value": "3"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
+-----------------------------------------------------
+EXPRESSION: argumentsIter.__proto__.next = function(){}; argumentsIter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Arguments",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "3"
+            },
+            {
+              "_name": "1",
+              "_type": "string",
+              "_value": "arg"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": null
+  }
+}
+
diff --git a/LayoutTests/inspector/model/remote-object-mutated-iterators.html b/LayoutTests/inspector/model/remote-object-mutated-iterators.html
new file mode 100644 (file)
index 0000000..299c43b
--- /dev/null
@@ -0,0 +1,40 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="utf-8">
+<script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="resources/remote-object-utilities.js"></script>
+<script>
+function test()
+{
+    let steps = [
+        // String Iterator
+        {expression: `iter = 'alpha'[Symbol.iterator]()`},
+        {expression: `iter.__proto__.next = function(){}; iter`},
+
+        // Map Iterator
+        {expression: `map = new Map; map.set(1, 2); map.set('key', 'value'); iter = map[Symbol.iterator]()`},
+        {expression: `iter.__proto__.next = function(){}; iter`},
+
+        // Set Iterator
+        {expression: `set = new Set; for (var i = 0; i <= 100; i++) set.add(i); iter = set[Symbol.iterator]()`},
+        {expression: `iter.__proto__.next = function(){}; iter`},
+
+        // Array Iterator (array and arguments)
+        {expression: `arrayIter = [1,2,3][Symbol.iterator]()`},
+        {expression: `x = undefined; (function() { x = arguments; })(3, 'arg'); argumentsIter = x[Symbol.iterator]()`},
+        {expression: `arrayIter.__proto__.next = function(){}; arrayIter`},
+        {expression: `argumentsIter.__proto__.next = function(){}; argumentsIter`},
+    ];
+
+    if (!window.WebInspector) {
+        window.steps = steps;
+        return;
+    }
+
+    runSteps(steps);
+}
+</script>
+</head>
+<body onload="runTest(); runInBrowserTest();"></body>
+</html>
index 0e8b3a5..712900a 100644 (file)
@@ -1,13 +1,13 @@
-<!doctype html>
+<!DOCTYPE html>
 <html>
 <head>
-<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
+<meta charset="utf-8">
 <script src="../../http/tests/inspector/resources/inspector-test.js"></script>
+<script src="resources/remote-object-utilities.js"></script>
 <script>
 function test()
 {
-    var currentStepIndex = 0;
-    var steps = [
+    let steps = [
     // Special:
 
         // Null / undefined
@@ -159,6 +159,7 @@ function test()
         {expression: "set = new Set; for (var i = 0; i <= 100; i++) set.add(i); set.values()"},
         {expression: "map.entries()"},
         {expression: "x = undefined; (function() { x = arguments; })(1, 'two'); x[Symbol.iterator]()"},
+        {expression: "iter = [1, 2][Symbol.iterator](); iter['return'] = function(){}; iter"},
 
         // Promise
         {expression: "new Promise(function(){})"},
@@ -191,60 +192,7 @@ function test()
         return;
     }
 
-    function remoteObjectJSONFilter(key, value)
-    {
-        if (key === "_target" || key === "_hasChildren" || key === "_listeners")
-            return undefined;
-        if (key === "_objectId" || key === "_stackTrace")
-            return "<filtered>";
-        return value;
-    }
-
-    function runSteps() {
-
-        function afterStep() {
-            if (++currentStepIndex >= steps.length)
-                InspectorTest.completeTest();
-        }
-
-        function runStep(step) {
-            if (step.browserOnly) {
-                afterStep();
-                return;
-            }
-
-            WebInspector.runtimeManager.evaluateInInspectedWindow(step.expression, {objectGroup: "test", doNotPauseOnExceptionsAndMuteConsole: true, generatePreview: true}, function(remoteObject, wasThrown) {
-                InspectorTest.log("");
-                InspectorTest.log("-----------------------------------------------------");
-                InspectorTest.log("EXPRESSION: " + step.expression);
-                InspectorTest.assert(remoteObject instanceof WebInspector.RemoteObject);
-                InspectorTest.log(JSON.stringify(remoteObject, remoteObjectJSONFilter, "  "));
-                afterStep();
-            });
-        }
-
-        for (var step of steps)
-            runStep(step);
-    }
-
-    runSteps();
-}
-
-function runInBrowserTest()
-{
-    if (window.testRunner)
-        return;
-    test(); // get steps.
-
-    for (var step of steps) {
-        console.info("EXPRESSION", step.expression);
-        try {
-            console.log(eval(step.expression));
-        } catch (e) {
-            console.log("EXCEPTION: " + e);
-        }
-    }
+    runSteps(steps);
 }
 </script>
 </head>
diff --git a/LayoutTests/inspector/model/resources/remote-object-utilities.js b/LayoutTests/inspector/model/resources/remote-object-utilities.js
new file mode 100644 (file)
index 0000000..d1e9d11
--- /dev/null
@@ -0,0 +1,50 @@
+function runInBrowserTest() {
+    if (window.testRunner)
+        return;
+    test(); // Populate window.steps.
+
+    for (let step of steps) {
+        console.info("EXPRESSION", step.expression);
+        try {
+            console.log(eval(step.expression));
+        } catch (e) {
+            console.log("EXCEPTION: " + e);
+        }
+    }
+}
+
+TestPage.registerInitializer(() => {
+    function remoteObjectJSONFilter(key, value) {
+        if (key === "_target" || key === "_hasChildren" || key === "_listeners")
+            return undefined;
+        if (key === "_objectId" || key === "_stackTrace")
+            return "<filtered>";
+        return value;
+    }
+
+    window.runSteps = function(steps) {
+        let currentStepIndex = 0;
+
+        function checkComplete() {
+            if (++currentStepIndex >= steps.length)
+                InspectorTest.completeTest();
+        }
+
+        for (let {expression, browserOnly} of steps) {
+            if (browserOnly) {
+                checkComplete();
+                return;
+            }
+
+            WebInspector.runtimeManager.evaluateInInspectedWindow(expression, {objectGroup: "test", doNotPauseOnExceptionsAndMuteConsole: true, generatePreview: true}, (remoteObject, wasThrown) => {
+                InspectorTest.log("");
+                InspectorTest.log("-----------------------------------------------------");
+                InspectorTest.log("EXPRESSION: " + expression);
+                InspectorTest.assert(remoteObject instanceof WebInspector.RemoteObject);
+                InspectorTest.log(JSON.stringify(remoteObject, remoteObjectJSONFilter, 2));
+                checkComplete();
+            });
+        }
+    };
+});
index ee385ca..e0df1cd 100644 (file)
@@ -3925,6 +3925,79 @@ EXPRESSION: x = undefined; (function() { x = arguments; })(1, 'two'); x[Symbol.i
 }
 
 -----------------------------------------------------
+EXPRESSION: iter = [1, 2][Symbol.iterator](); iter['return'] = function(){}; iter
+{
+  "_type": "object",
+  "_subtype": "iterator",
+  "_objectId": "<filtered>",
+  "_description": "Array Iterator",
+  "_preview": {
+    "_type": "object",
+    "_subtype": "iterator",
+    "_description": "Array Iterator",
+    "_lossless": true,
+    "_overflow": false,
+    "_properties": [
+      {
+        "_name": "array",
+        "_type": "object",
+        "_subtype": "array",
+        "_valuePreview": {
+          "_type": "object",
+          "_subtype": "array",
+          "_description": "Array",
+          "_lossless": true,
+          "_overflow": false,
+          "_size": 2,
+          "_properties": [
+            {
+              "_name": "0",
+              "_type": "number",
+              "_value": "1"
+            },
+            {
+              "_name": "1",
+              "_type": "number",
+              "_value": "2"
+            }
+          ],
+          "_entries": null
+        },
+        "_internal": true
+      },
+      {
+        "_name": "kind",
+        "_type": "string",
+        "_value": "value",
+        "_internal": true
+      }
+    ],
+    "_entries": [
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "1",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      },
+      {
+        "_value": {
+          "_type": "number",
+          "_description": "2",
+          "_lossless": true,
+          "_overflow": false,
+          "_properties": null,
+          "_entries": null
+        }
+      }
+    ]
+  }
+}
+
+-----------------------------------------------------
 EXPRESSION: new Promise(function(){})
 {
   "_type": "object",
index 7caffdc..5d5a4b7 100644 (file)
@@ -1,3 +1,58 @@
+2017-06-27  Joseph Pecoraro  <pecoraro@apple.com>
+
+        Web Inspector: Crash generating object preview for ArrayIterator
+        https://bugs.webkit.org/show_bug.cgi?id=173754
+        <rdar://problem/32859012>
+
+        Reviewed by Saam Barati.
+
+        When Inspector generates an object preview for an ArrayIterator instance it made
+        a "clone" of the original ArrayIterator instance by constructing a new object with
+        the instance's structure. However, user code could have modified that instance's
+        structure, such as adding / removing properties. The `return` property had special
+        meaning, and our clone did not fill that slot. This approach is brittle in that
+        we weren't satisfying the expectations of an object with a particular Structure,
+        and the original goal of having Web Inspector peek values of built-in Iterators
+        was to avoid observable behavior.
+
+        This tightens Web Inspector's Iterator preview to only peek values if the
+        Iterators would actually be non-observable. It also builds an ArrayIterator
+        clone like a regular object construction.
+
+        * inspector/JSInjectedScriptHost.cpp:
+        (Inspector::cloneArrayIteratorObject):
+        Build up the Object from scratch with a new ArrayIterator prototype.
+
+        (Inspector::JSInjectedScriptHost::iteratorEntries):
+        Only clone and peek iterators if it would not be observable.
+        Also update iteration to be more in line with IterationOperations, such as when
+        we call iteratorClose.
+
+        * runtime/JSGlobalObject.cpp:
+        (JSC::JSGlobalObject::JSGlobalObject):
+        (JSC::JSGlobalObject::init):
+        * runtime/JSGlobalObject.h:
+        (JSC::JSGlobalObject::stringIteratorProtocolWatchpoint):
+        * runtime/JSGlobalObjectInlines.h:
+        (JSC::JSGlobalObject::isStringPrototypeIteratorProtocolFastAndNonObservable):
+        Add a StringIterator WatchPoint in line with the Array/Map/Set iterator watchpoints.
+
+        * runtime/JSMap.cpp:
+        (JSC::JSMap::isIteratorProtocolFastAndNonObservable):
+        (JSC::JSMap::canCloneFastAndNonObservable):
+        * runtime/JSMap.h:
+        * runtime/JSSet.cpp:
+        (JSC::JSSet::isIteratorProtocolFastAndNonObservable):
+        (JSC::JSSet::canCloneFastAndNonObservable):
+        * runtime/JSSet.h:
+        Promote isIteratorProtocolFastAndNonObservable to a method.
+
+        * runtime/JSObject.cpp:
+        (JSC::canDoFastPutDirectIndex):
+        * runtime/JSTypeInfo.h:
+        (JSC::TypeInfo::isArgumentsType):
+        Helper to detect if an Object is an Arguments type.
+
 2017-06-26  Saam Barati  <sbarati@apple.com>
 
         RegExpPrototype.js builtin uses for-of iteration which is almost certainly incorrect
index ec376e3..c812101 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSInjectedScriptHost.h"
 
+#include "ArrayIteratorPrototype.h"
 #include "BuiltinNames.h"
 #include "Completion.h"
 #include "DateInstance.h"
@@ -33,6 +34,7 @@
 #include "Error.h"
 #include "InjectedScriptHost.h"
 #include "IteratorOperations.h"
+#include "IteratorPrototype.h"
 #include "JSArray.h"
 #include "JSBoundFunction.h"
 #include "JSCInlines.h"
@@ -357,7 +359,7 @@ JSValue JSInjectedScriptHost::getInternalProperties(ExecState* exec)
         array->putDirectIndex(exec, index++, constructInternalProperty(exec, "kind", jsNontrivialString(exec, kind)));
         return array;
     }
-        
+
     if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(vm, value)) {
         String kind;
         switch (setIterator->kind()) {
@@ -508,14 +510,15 @@ JSValue JSInjectedScriptHost::weakSetEntries(ExecState* exec)
     return array;
 }
 
-static JSObject* cloneArrayIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSValue nextIndex)
+static JSObject* cloneArrayIteratorObject(ExecState* exec, VM& vm, JSObject* iteratorObject, JSGlobalObject* globalObject, JSValue nextIndex, JSValue iteratedObject)
 {
     ASSERT(iteratorObject->type() == FinalObjectType);
-    JSObject* clone = constructEmptyObject(exec, iteratorObject->structure());
+    JSObject* clone = constructEmptyObject(exec, ArrayIteratorPrototype::create(vm, globalObject, ArrayIteratorPrototype::createStructure(vm, globalObject, globalObject->iteratorPrototype())));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratedObject);
+    clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorKindPrivateName()));
     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName(), nextIndex);
-    clone->putDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName()));
-    clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName()));
     clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextPrivateName()));
+    clone->putDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName(), iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorIsDonePrivateName()));
     return clone;
 }
 
@@ -526,54 +529,65 @@ JSValue JSInjectedScriptHost::iteratorEntries(ExecState* exec)
 
     VM& vm = exec->vm();
     auto scope = DECLARE_THROW_SCOPE(vm);
+
     JSValue iterator;
+    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
     JSValue value = exec->uncheckedArgument(0);
-    if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(vm, value))
-        iterator = mapIterator->clone(exec);
-    else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(vm, value))
-        iterator = setIterator->clone(exec);
-    else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value))
-        iterator = stringIterator->clone(exec);
-    else {
-        if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
-            // Array Iterators are created in JS for performance reasons. Thus the only way to know we have one is to
-            // look for a property that is unique to them.
-            if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName()))
-                iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, nextIndex);
+    if (JSMapIterator* mapIterator = jsDynamicCast<JSMapIterator*>(vm, value)) {
+        if (jsCast<JSMap*>(mapIterator->iteratedValue())->isIteratorProtocolFastAndNonObservable())
+            iterator = mapIterator->clone(exec);
+    } else if (JSSetIterator* setIterator = jsDynamicCast<JSSetIterator*>(vm, value)) {
+        if (jsCast<JSSet*>(setIterator->iteratedValue())->isIteratorProtocolFastAndNonObservable())
+            iterator = setIterator->clone(exec);
+    } else if (JSStringIterator* stringIterator = jsDynamicCast<JSStringIterator*>(vm, value)) {
+        if (globalObject->isStringPrototypeIteratorProtocolFastAndNonObservable())
+            iterator = stringIterator->clone(exec);
+    } else if (JSObject* iteratorObject = jsDynamicCast<JSObject*>(vm, value)) {
+        // Detect an ArrayIterator by checking for one of its unique private properties.
+        if (JSValue nextIndex = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().arrayIteratorNextIndexPrivateName())) {
+            JSValue iteratedObject = iteratorObject->getDirect(vm, vm.propertyNames->builtinNames().iteratedObjectPrivateName());
+            if (isJSArray(iteratedObject)) {
+                JSArray* array = jsCast<JSArray*>(iteratedObject);
+                if (array->isIteratorProtocolFastAndNonObservable())
+                    iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
+            } else if (iteratedObject.isObject() && TypeInfo::isArgumentsType(asObject(iteratedObject)->type())) {
+                if (globalObject->isArrayPrototypeIteratorProtocolFastAndNonObservable())
+                    iterator = cloneArrayIteratorObject(exec, vm, iteratorObject, globalObject, nextIndex, iteratedObject);
+            }
         }
     }
+    RETURN_IF_EXCEPTION(scope, { });
     if (!iterator)
         return jsUndefined();
 
     unsigned numberToFetch = 5;
     JSValue numberToFetchArg = exec->argument(1);
     double fetchDouble = numberToFetchArg.toInteger(exec);
+    RETURN_IF_EXCEPTION(scope, { });
     if (fetchDouble >= 0)
         numberToFetch = static_cast<unsigned>(fetchDouble);
 
     JSArray* array = constructEmptyArray(exec, nullptr);
-    RETURN_IF_EXCEPTION(scope, JSValue());
+    RETURN_IF_EXCEPTION(scope, { });
 
     for (unsigned i = 0; i < numberToFetch; ++i) {
         JSValue next = iteratorStep(exec, iterator);
-        if (UNLIKELY(scope.exception()))
-            break;
-        if (next.isFalse())
+        if (UNLIKELY(scope.exception()) || next.isFalse())
             break;
 
         JSValue nextValue = iteratorValue(exec, next);
-        if (UNLIKELY(scope.exception()))
-            break;
+        RETURN_IF_EXCEPTION(scope, { });
 
         JSObject* entry = constructEmptyObject(exec);
         entry->putDirect(exec->vm(), Identifier::fromString(exec, "value"), nextValue);
         array->putDirectIndex(exec, i, entry);
-        if (UNLIKELY(scope.exception()))
+        if (UNLIKELY(scope.exception())) {
+            scope.release();
+            iteratorClose(exec, iterator);
             break;
+        }
     }
 
-    iteratorClose(exec, iterator);
-
     return array;
 }
 
index 5b2cc7c..4fb490e 100644 (file)
@@ -326,6 +326,7 @@ JSGlobalObject::JSGlobalObject(VM& vm, Structure* structure, const GlobalObjectM
     , m_arrayIteratorProtocolWatchpoint(IsWatched)
     , m_mapIteratorProtocolWatchpoint(IsWatched)
     , m_setIteratorProtocolWatchpoint(IsWatched)
+    , m_stringIteratorProtocolWatchpoint(IsWatched)
     , m_mapSetWatchpoint(IsWatched)
     , m_setAddWatchpoint(IsWatched)
     , m_arraySpeciesWatchpoint(ClearWatchpoint)
@@ -937,7 +938,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
             m_arrayIteratorPrototypeNext = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_arrayIteratorProtocolWatchpoint);
             m_arrayIteratorPrototypeNext->install();
         }
-
         {
             ObjectPropertyCondition condition = setupAdaptiveWatchpoint(this->arrayPrototype(), m_vm.propertyNames->iteratorSymbol);
             m_arrayPrototypeSymbolIteratorWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_arrayIteratorProtocolWatchpoint);
@@ -949,7 +949,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
             m_mapIteratorPrototypeNextWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_mapIteratorProtocolWatchpoint);
             m_mapIteratorPrototypeNextWatchpoint->install();
         }
-
         {
             ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_mapPrototype.get(), m_vm.propertyNames->iteratorSymbol);
             m_mapPrototypeSymbolIteratorWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_mapIteratorProtocolWatchpoint);
@@ -961,7 +960,6 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
             m_setIteratorPrototypeNextWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_setIteratorProtocolWatchpoint);
             m_setIteratorPrototypeNextWatchpoint->install();
         }
-
         {
             ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_setPrototype.get(), m_vm.propertyNames->iteratorSymbol);
             m_setPrototypeSymbolIteratorWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_setIteratorProtocolWatchpoint);
@@ -969,6 +967,17 @@ putDirectWithoutTransition(vm, vm.propertyNames-> jsName, lowerName ## Construct
         }
 
         {
+            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_stringIteratorPrototype.get(), m_vm.propertyNames->next);
+            m_stringIteratorPrototypeNextWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_stringIteratorProtocolWatchpoint);
+            m_stringIteratorPrototypeNextWatchpoint->install();
+        }
+        {
+            ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_stringPrototype.get(), m_vm.propertyNames->iteratorSymbol);
+            m_stringPrototypeSymbolIteratorWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_stringIteratorProtocolWatchpoint);
+            m_stringPrototypeSymbolIteratorWatchpoint->install();
+        }
+
+        {
             ObjectPropertyCondition condition = setupAdaptiveWatchpoint(m_mapPrototype.get(), m_vm.propertyNames->set);
             m_mapPrototypeSetWatchpoint = std::make_unique<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>>(vm, condition, m_mapSetWatchpoint);
             m_mapPrototypeSetWatchpoint->install();
index 075b05e..ad800b0 100644 (file)
@@ -408,6 +408,7 @@ public:
     InlineWatchpointSet& arrayIteratorProtocolWatchpoint() { return m_arrayIteratorProtocolWatchpoint; }
     InlineWatchpointSet& mapIteratorProtocolWatchpoint() { return m_mapIteratorProtocolWatchpoint; }
     InlineWatchpointSet& setIteratorProtocolWatchpoint() { return m_setIteratorProtocolWatchpoint; }
+    InlineWatchpointSet& stringIteratorProtocolWatchpoint() { return m_stringIteratorProtocolWatchpoint; }
     InlineWatchpointSet& mapSetWatchpoint() { return m_mapSetWatchpoint; }
     InlineWatchpointSet& setAddWatchpoint() { return m_setAddWatchpoint; }
     InlineWatchpointSet& arraySpeciesWatchpoint() { return m_arraySpeciesWatchpoint; }
@@ -416,6 +417,7 @@ public:
     InlineWatchpointSet m_arrayIteratorProtocolWatchpoint;
     InlineWatchpointSet m_mapIteratorProtocolWatchpoint;
     InlineWatchpointSet m_setIteratorProtocolWatchpoint;
+    InlineWatchpointSet m_stringIteratorProtocolWatchpoint;
     InlineWatchpointSet m_mapSetWatchpoint;
     InlineWatchpointSet m_setAddWatchpoint;
     InlineWatchpointSet m_arraySpeciesWatchpoint;
@@ -425,12 +427,15 @@ public:
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapIteratorPrototypeNextWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_setPrototypeSymbolIteratorWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_setIteratorPrototypeNextWatchpoint;
+    std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_stringPrototypeSymbolIteratorWatchpoint;
+    std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_stringIteratorPrototypeNextWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_mapPrototypeSetWatchpoint;
     std::unique_ptr<ObjectPropertyChangeAdaptiveWatchpoint<InlineWatchpointSet>> m_setPrototypeAddWatchpoint;
 
     bool isArrayPrototypeIteratorProtocolFastAndNonObservable();
     bool isMapPrototypeIteratorProtocolFastAndNonObservable();
     bool isSetPrototypeIteratorProtocolFastAndNonObservable();
+    bool isStringPrototypeIteratorProtocolFastAndNonObservable();
     bool isMapPrototypeSetFastAndNonObservable();
     bool isSetPrototypeAddFastAndNonObservable();
 
index a5d06da..52e9376 100644 (file)
@@ -82,6 +82,11 @@ ALWAYS_INLINE bool JSGlobalObject::isSetPrototypeIteratorProtocolFastAndNonObser
     return setIteratorProtocolWatchpoint().isStillValid();
 }
 
+ALWAYS_INLINE bool JSGlobalObject::isStringPrototypeIteratorProtocolFastAndNonObservable()
+{
+    return stringIteratorProtocolWatchpoint().isStillValid();
+}
+
 ALWAYS_INLINE bool JSGlobalObject::isMapPrototypeSetFastAndNonObservable()
 {
     return mapSetWatchpoint().isStillValid();
index 70441d5..8aca572 100644 (file)
@@ -45,27 +45,28 @@ JSMap* JSMap::clone(ExecState* exec, VM& vm, Structure* structure)
     return instance;
 }
 
-bool JSMap::canCloneFastAndNonObservable(Structure* structure)
+bool JSMap::isIteratorProtocolFastAndNonObservable()
 {
-    auto isIteratorProtocolFastAndNonObservable = [&] () {
-        JSGlobalObject* globalObject = this->globalObject();
-        if (!globalObject->isMapPrototypeIteratorProtocolFastAndNonObservable())
-            return false;
+    JSGlobalObject* globalObject = this->globalObject();
+    if (!globalObject->isMapPrototypeIteratorProtocolFastAndNonObservable())
+        return false;
 
-        Structure* structure = this->structure();
-        // This is the fast case. Many maps will be an original map.
-        if (structure == globalObject->mapStructure())
-            return true;
+    Structure* structure = this->structure();
+    // This is the fast case. Many maps will be an original map.
+    if (structure == globalObject->mapStructure())
+        return true;
 
-        if (structure->storedPrototype() != globalObject->mapPrototype())
-            return false;
+    if (structure->storedPrototype() != globalObject->mapPrototype())
+        return false;
 
-        if (getDirectOffset(globalObject->vm(), globalObject->vm().propertyNames->iteratorSymbol) != invalidOffset)
-            return false;
+    if (getDirectOffset(globalObject->vm(), globalObject->vm().propertyNames->iteratorSymbol) != invalidOffset)
+        return false;
 
-        return true;
-    };
+    return true;
+}
 
+bool JSMap::canCloneFastAndNonObservable(Structure* structure)
+{
     auto setFastAndNonObservable = [&] (Structure* structure) {
         JSGlobalObject* globalObject = structure->globalObject();
         if (!globalObject->isMapPrototypeSetFastAndNonObservable())
index e749ac5..81e326d 100644 (file)
@@ -56,6 +56,7 @@ public:
         add(exec, key, value);
     }
 
+    bool isIteratorProtocolFastAndNonObservable();
     bool canCloneFastAndNonObservable(Structure*);
     JSMap* clone(ExecState*, VM&, Structure*);
 
index 0c99844..5eb8543 100644 (file)
@@ -2362,9 +2362,7 @@ ALWAYS_INLINE static bool canDoFastPutDirectIndex(JSObject* object)
 {
     return isJSArray(object)
         || isJSFinalObject(object)
-        || object->type() == DirectArgumentsType
-        || object->type() == ScopedArgumentsType
-        || object->type() == ClonedArgumentsType;
+        || TypeInfo::isArgumentsType(object->type());
 }
 
 // Defined in ES5.1 8.12.9
index b4c6489..e4f4a16 100644 (file)
@@ -45,27 +45,28 @@ JSSet* JSSet::clone(ExecState* exec, VM& vm, Structure* structure)
     return instance;
 }
 
-bool JSSet::canCloneFastAndNonObservable(Structure* structure)
+bool JSSet::isIteratorProtocolFastAndNonObservable()
 {
-    auto isIteratorProtocolFastAndNonObservable = [&] () {
-        JSGlobalObject* globalObject = this->globalObject();
-        if (!globalObject->isSetPrototypeIteratorProtocolFastAndNonObservable())
-            return false;
+    JSGlobalObject* globalObject = this->globalObject();
+    if (!globalObject->isSetPrototypeIteratorProtocolFastAndNonObservable())
+        return false;
 
-        Structure* structure = this->structure();
-        // This is the fast case. Many sets will be an original set.
-        if (structure == globalObject->setStructure())
-            return true;
+    Structure* structure = this->structure();
+    // This is the fast case. Many sets will be an original set.
+    if (structure == globalObject->setStructure())
+        return true;
 
-        if (structure->storedPrototype() != globalObject->jsSetPrototype())
-            return false;
+    if (structure->storedPrototype() != globalObject->jsSetPrototype())
+        return false;
 
-        if (getDirectOffset(globalObject->vm(), globalObject->vm().propertyNames->iteratorSymbol) != invalidOffset)
-            return false;
+    if (getDirectOffset(globalObject->vm(), globalObject->vm().propertyNames->iteratorSymbol) != invalidOffset)
+        return false;
 
-        return true;
-    };
+    return true;
+}
 
+bool JSSet::canCloneFastAndNonObservable(Structure* structure)
+{
     auto addFastAndNonObservable = [&] (Structure* structure) {
         JSGlobalObject* globalObject = structure->globalObject();
         if (!globalObject->isSetPrototypeAddFastAndNonObservable())
index b6b7453..e676407 100644 (file)
@@ -52,6 +52,7 @@ public:
         return instance;
     }
 
+    bool isIteratorProtocolFastAndNonObservable();
     bool canCloneFastAndNonObservable(Structure*);
     JSSet* clone(ExecState*, VM&, Structure*);
 
index 16ed4be..94df229 100644 (file)
@@ -94,6 +94,13 @@ public:
     bool isImmutablePrototypeExoticObject() const { return isSetOnFlags2(IsImmutablePrototypeExoticObject); }
     bool interceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero() const { return isSetOnFlags2(InterceptsGetOwnPropertySlotByIndexEvenWhenLengthIsNotZero); }
 
+    static bool isArgumentsType(JSType type)
+    {
+        return type == DirectArgumentsType
+            || type == ScopedArgumentsType
+            || type == ClonedArgumentsType;
+    }
+
     static ptrdiff_t flagsOffset()
     {
         return OBJECT_OFFSETOF(TypeInfo, m_flags);