Add debug mirror support for ES6 Map/Set iterators.
authoraandrey@chromium.org <aandrey@chromium.org>
Tue, 4 Nov 2014 10:02:25 +0000 (10:02 +0000)
committeraandrey@chromium.org <aandrey@chromium.org>
Tue, 4 Nov 2014 10:02:43 +0000 (10:02 +0000)
This is to show values preview of an iterator in DevTools console.

API=v8::Value::IsMapIterator, v8::Value::IsSetIterator
BUG=chromium:427868
R=arv@chromium.org, yangguo@chromium.org, adamk@chromium.org
LOG=Y

Review URL: https://codereview.chromium.org/693813002

Cr-Commit-Position: refs/heads/master@{#25100}
git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@25100 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

include/v8.h
src/api.cc
src/factory.cc
src/factory.h
src/mirror-debugger.js
src/runtime/runtime-collections.cc
src/runtime/runtime.h
test/mjsunit/es6/mirror-iterators.js [new file with mode: 0644]

index 37e0ddc..d5433a6 100644 (file)
@@ -1626,6 +1626,18 @@ class V8_EXPORT Value : public Data {
   bool IsSet() const;
 
   /**
+   * Returns true if this value is a Map Iterator.
+   * This is an experimental feature.
+   */
+  bool IsMapIterator() const;
+
+  /**
+   * Returns true if this value is a Set Iterator.
+   * This is an experimental feature.
+   */
+  bool IsSetIterator() const;
+
+  /**
    * Returns true if this value is a WeakMap.
    * This is an experimental feature.
    */
index c9231a3..2c8009e 100644 (file)
@@ -2571,6 +2571,16 @@ bool Value::IsGeneratorObject() const {
 }
 
 
+bool Value::IsMapIterator() const {
+  return Utils::OpenHandle(this)->IsJSMapIterator();
+}
+
+
+bool Value::IsSetIterator() const {
+  return Utils::OpenHandle(this)->IsJSSetIterator();
+}
+
+
 Local<String> Value::ToString(Isolate* v8_isolate) const {
   i::Handle<i::Object> obj = Utils::OpenHandle(this);
   i::Handle<i::Object> str;
index 5713f73..72974a3 100644 (file)
@@ -1722,6 +1722,22 @@ Handle<JSDataView> Factory::NewJSDataView() {
 }
 
 
+Handle<JSMapIterator> Factory::NewJSMapIterator() {
+  Handle<Map> map(isolate()->native_context()->map_iterator_map());
+  CALL_HEAP_FUNCTION(isolate(),
+                     isolate()->heap()->AllocateJSObjectFromMap(*map),
+                     JSMapIterator);
+}
+
+
+Handle<JSSetIterator> Factory::NewJSSetIterator() {
+  Handle<Map> map(isolate()->native_context()->set_iterator_map());
+  CALL_HEAP_FUNCTION(isolate(),
+                     isolate()->heap()->AllocateJSObjectFromMap(*map),
+                     JSSetIterator);
+}
+
+
 namespace {
 
 ElementsKind GetExternalArrayElementsKind(ExternalArrayType type) {
index e9972cc..9f9813c 100644 (file)
@@ -445,6 +445,10 @@ class Factory FINAL {
   Handle<JSDataView> NewJSDataView(Handle<JSArrayBuffer> buffer,
                                    size_t byte_offset, size_t byte_length);
 
+  // TODO(aandrey): Maybe these should take table, index and kind arguments.
+  Handle<JSMapIterator> NewJSMapIterator();
+  Handle<JSSetIterator> NewJSSetIterator();
+
   // Allocates a Harmony proxy.
   Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
 
index c36d6fd..da031d3 100644 (file)
@@ -85,6 +85,8 @@ function MakeMirror(value, opt_transient) {
     mirror = new MapMirror(value);
   } else if (IS_SET(value) || IS_WEAKSET(value)) {
     mirror = new SetMirror(value);
+  } else if (IS_MAP_ITERATOR(value) || IS_SET_ITERATOR(value)) {
+    mirror = new IteratorMirror(value);
   } else if (ObjectIsPromise(value)) {
     mirror = new PromiseMirror(value);
   } else if (IS_GENERATOR(value)) {
@@ -163,6 +165,7 @@ var SCOPE_TYPE = 'scope';
 var PROMISE_TYPE = 'promise';
 var MAP_TYPE = 'map';
 var SET_TYPE = 'set';
+var ITERATOR_TYPE = 'iterator';
 var GENERATOR_TYPE = 'generator';
 
 // Maximum length when sending strings through the JSON protocol.
@@ -217,6 +220,7 @@ var ScopeType = { Global: 0,
 //         - PromiseMirror
 //         - MapMirror
 //         - SetMirror
+//         - IteratorMirror
 //         - GeneratorMirror
 //     - PropertyMirror
 //     - InternalPropertyMirror
@@ -456,6 +460,15 @@ Mirror.prototype.isSet = function() {
 
 
 /**
+ * Check whether the mirror reflects an iterator.
+ * @returns {boolean} True if the mirror reflects an iterator
+ */
+Mirror.prototype.isIterator = function() {
+  return this instanceof IteratorMirror;
+};
+
+
+/**
  * Allocate a handle id for this object.
  */
 Mirror.prototype.allocateHandle_ = function() {
@@ -1343,6 +1356,16 @@ function SetMirror(value) {
 inherits(SetMirror, ObjectMirror);
 
 
+function IteratorGetValues_(iter, next_function) {
+  var result = [];
+  var next;
+  while (!(next = %_CallFunction(iter, next_function)).done) {
+    result.push(next.value);
+  }
+  return result;
+}
+
+
 /**
  * Returns an array of elements of a set.
  * This will keep elements alive for WeakSets.
@@ -1354,13 +1377,31 @@ SetMirror.prototype.values = function() {
     return %GetWeakSetValues(this.value_);
   }
 
-  var result = [];
   var iter = %_CallFunction(this.value_, builtins.SetValues);
-  var next;
-  while (!(next = iter.next()).done) {
-    result.push(next.value);
+  return IteratorGetValues_(iter, builtins.SetIteratorNextJS);
+};
+
+
+function IteratorMirror(value) {
+  %_CallFunction(this, value, ITERATOR_TYPE, ObjectMirror);
+}
+inherits(IteratorMirror, ObjectMirror);
+
+
+/**
+ * Returns a preview of elements of an iterator.
+ * Does not change the backing iterator state.
+ *
+ * @returns {Array.<Object>} Array of elements of an iterator.
+ */
+IteratorMirror.prototype.preview = function() {
+  if (IS_MAP_ITERATOR(this.value_)) {
+    return IteratorGetValues_(%MapIteratorClone(this.value_),
+                              builtins.MapIteratorNextJS);
+  } else if (IS_SET_ITERATOR(this.value_)) {
+    return IteratorGetValues_(%SetIteratorClone(this.value_),
+                              builtins.SetIteratorNextJS);
   }
-  return result;
 };
 
 
index c1a63dc..45ac41c 100644 (file)
@@ -92,6 +92,20 @@ RUNTIME_FUNCTION(Runtime_SetIteratorInitialize) {
 }
 
 
+RUNTIME_FUNCTION(Runtime_SetIteratorClone) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSSetIterator, holder, 0);
+
+  Handle<JSSetIterator> result = isolate->factory()->NewJSSetIterator();
+  result->set_table(holder->table());
+  result->set_index(Smi::FromInt(Smi::cast(holder->index())->value()));
+  result->set_kind(Smi::FromInt(Smi::cast(holder->kind())->value()));
+
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(Runtime_SetIteratorNext) {
   SealHandleScope shs(isolate);
   DCHECK(args.length() == 2);
@@ -197,6 +211,20 @@ RUNTIME_FUNCTION(Runtime_MapIteratorInitialize) {
 }
 
 
+RUNTIME_FUNCTION(Runtime_MapIteratorClone) {
+  HandleScope scope(isolate);
+  DCHECK(args.length() == 1);
+  CONVERT_ARG_HANDLE_CHECKED(JSMapIterator, holder, 0);
+
+  Handle<JSMapIterator> result = isolate->factory()->NewJSMapIterator();
+  result->set_table(holder->table());
+  result->set_index(Smi::FromInt(Smi::cast(holder->index())->value()));
+  result->set_kind(Smi::FromInt(Smi::cast(holder->kind())->value()));
+
+  return *result;
+}
+
+
 RUNTIME_FUNCTION(Runtime_GetWeakMapEntries) {
   HandleScope scope(isolate);
   DCHECK(args.length() == 1);
index 599a024..448010a 100644 (file)
@@ -310,6 +310,7 @@ namespace internal {
   F(SetGetSize, 1, 1)                                  \
                                                        \
   F(SetIteratorInitialize, 3, 1)                       \
+  F(SetIteratorClone, 1, 1)                            \
   F(SetIteratorNext, 2, 1)                             \
                                                        \
   /* Harmony maps */                                   \
@@ -322,6 +323,7 @@ namespace internal {
   F(MapGetSize, 1, 1)                                  \
                                                        \
   F(MapIteratorInitialize, 3, 1)                       \
+  F(MapIteratorClone, 1, 1)                            \
   F(MapIteratorNext, 2, 1)                             \
                                                        \
   /* Harmony weak maps and sets */                     \
diff --git a/test/mjsunit/es6/mirror-iterators.js b/test/mjsunit/es6/mirror-iterators.js
new file mode 100644 (file)
index 0000000..02fe7ff
--- /dev/null
@@ -0,0 +1,62 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// Flags: --expose-debug-as debug
+// Test the mirror object for collection iterators.
+
+function testIteratorMirror(iter, offset, expected) {
+  while (offset-- > 0) iter.next();
+
+  var mirror = debug.MakeMirror(iter);
+  assertTrue(mirror.isIterator());
+
+  var preview = mirror.preview();
+  assertArrayEquals(expected, preview);
+
+  // Check that iterator has not changed after taking preview.
+  var values = [];
+  for (var i of iter) values.push(i);
+  assertArrayEquals(expected, values);
+}
+
+var o1 = { foo: 1 };
+var o2 = { foo: 2 };
+
+var map = new Map();
+map.set(41, 42);
+map.set(o1, o2);
+
+testIteratorMirror(map.keys(), 0, [41, o1]);
+testIteratorMirror(map.values(), 0, [42, o2]);
+testIteratorMirror(map.entries(), 0, [[41, 42], [o1, o2]]);
+
+testIteratorMirror(map.keys(), 1, [o1]);
+testIteratorMirror(map.values(), 1, [o2]);
+testIteratorMirror(map.entries(), 1, [[o1, o2]]);
+
+testIteratorMirror(map.keys(), 2, []);
+testIteratorMirror(map.values(), 2, []);
+testIteratorMirror(map.entries(), 2, []);
+
+var set = new Set();
+set.add(41);
+set.add(42);
+set.add(o1);
+set.add(o2);
+
+testIteratorMirror(set.keys(), 0, [41, 42, o1, o2]);
+testIteratorMirror(set.values(), 0, [41, 42, o1, o2]);
+testIteratorMirror(set.entries(), 0, [[41, 41], [42, 42], [o1, o1], [o2, o2]]);
+
+testIteratorMirror(set.keys(), 1, [42, o1, o2]);
+testIteratorMirror(set.values(), 1, [42, o1, o2]);
+testIteratorMirror(set.entries(), 1, [[42, 42], [o1, o1], [o2, o2]]);
+
+testIteratorMirror(set.keys(), 3, [o2]);
+testIteratorMirror(set.values(), 3, [o2]);
+testIteratorMirror(set.entries(), 3, [[o2, o2]]);
+
+testIteratorMirror(set.keys(), 5, []);
+testIteratorMirror(set.values(), 5, []);
+testIteratorMirror(set.entries(), 5, []);