Revision: 21950
Author:   [email protected]
Date:     Mon Jun 23 18:05:57 2014 UTC
Log:      Map/Set: Implement constructor parameter handling

When an iterable object is passed in as the argument to the Map and Set
constructor the elements of the iterable object are used to populate the
Map and Set.

http://people.mozilla.org/~jorendorff/es6-draft.html#sec-map-iterable
http://people.mozilla.org/~jorendorff/es6-draft.html#sec-set-iterable

BUG=v8:3398
LOG=Y
[email protected]

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

Patch from Erik Arvidsson <[email protected]>.
http://code.google.com/p/v8/source/detail?r=21950

Modified:
 /branches/bleeding_edge/src/collection.js
 /branches/bleeding_edge/src/messages.js
 /branches/bleeding_edge/test/mjsunit/harmony/collections.js
 /branches/bleeding_edge/tools/generate-runtime-tests.py

=======================================
--- /branches/bleeding_edge/src/collection.js   Mon Jun 23 07:10:25 2014 UTC
+++ /branches/bleeding_edge/src/collection.js   Mon Jun 23 18:05:57 2014 UTC
@@ -10,17 +10,66 @@

 var $Set = global.Set;
 var $Map = global.Map;
+
+
+// TODO(arv): Move these general functions to v8natives.js when Map and Set are
+// no longer experimental.
+
+
+// 7.4.1 CheckIterable ( obj )
+function ToIterable(obj) {
+  if (!IS_SPEC_OBJECT(obj)) {
+    return UNDEFINED;
+  }
+  return obj[symbolIterator];
+}
+
+
+// 7.4.2 GetIterator ( obj, method )
+function GetIterator(obj, method) {
+  if (IS_UNDEFINED(method)) {
+    method = ToIterable(obj);
+  }
+  if (!IS_SPEC_FUNCTION(method)) {
+    throw MakeTypeError('not_iterable', [obj]);
+  }
+  var iterator = %_CallFunction(obj, method);
+  if (!IS_SPEC_OBJECT(iterator)) {
+    throw MakeTypeError('not_an_iterator', [iterator]);
+  }
+  return iterator;
+}


 // -------------------------------------------------------------------
 // Harmony Set

-function SetConstructor() {
-  if (%_IsConstructCall()) {
-    %SetInitialize(this);
-  } else {
+function SetConstructor(iterable) {
+  if (!%_IsConstructCall()) {
     throw MakeTypeError('constructor_not_function', ['Set']);
   }
+
+  var iter, adder;
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    iter = GetIterator(iterable);
+    adder = this.add;
+    if (!IS_SPEC_FUNCTION(adder)) {
+      throw MakeTypeError('property_not_function', ['add', this]);
+    }
+  }
+
+  %SetInitialize(this);
+
+  if (IS_UNDEFINED(iter)) return;
+
+  var next, done;
+  while (!(next = iter.next()).done) {
+    if (!IS_SPEC_OBJECT(next)) {
+      throw MakeTypeError('iterator_result_not_an_object', [next]);
+    }
+    %_CallFunction(this, next.value, adder);
+  }
 }


@@ -117,12 +166,36 @@
 // -------------------------------------------------------------------
 // Harmony Map

-function MapConstructor() {
-  if (%_IsConstructCall()) {
-    %MapInitialize(this);
-  } else {
+function MapConstructor(iterable) {
+  if (!%_IsConstructCall()) {
     throw MakeTypeError('constructor_not_function', ['Map']);
   }
+
+  var iter, adder;
+
+  if (!IS_NULL_OR_UNDEFINED(iterable)) {
+    iter = GetIterator(iterable);
+    adder = this.set;
+    if (!IS_SPEC_FUNCTION(adder)) {
+      throw MakeTypeError('property_not_function', ['set', this]);
+    }
+  }
+
+  %MapInitialize(this);
+
+  if (IS_UNDEFINED(iter)) return;
+
+  var next, done, nextItem;
+  while (!(next = iter.next()).done) {
+    if (!IS_SPEC_OBJECT(next)) {
+      throw MakeTypeError('iterator_result_not_an_object', [next]);
+    }
+    nextItem = next.value;
+    if (!IS_SPEC_OBJECT(nextItem)) {
+      throw MakeTypeError('iterator_value_not_an_object', [nextItem]);
+    }
+    %_CallFunction(this, nextItem[0], nextItem[1], adder);
+  }
 }


=======================================
--- /branches/bleeding_edge/src/messages.js     Thu May 22 15:27:57 2014 UTC
+++ /branches/bleeding_edge/src/messages.js     Mon Jun 23 18:05:57 2014 UTC
@@ -89,6 +89,10 @@
   array_functions_on_frozen:     ["Cannot modify frozen array elements"],
array_functions_change_sealed: ["Cannot add/remove sealed array elements"], first_argument_not_regexp: ["First argument to ", "%0", " must not be a regular expression"],
+  not_iterable:                  ["%0", " is not iterable"],
+  not_an_iterator:               ["%0", " is not an iterator"],
+ iterator_result_not_an_object: ["Iterator result ", "%0", " is not an object"], + iterator_value_not_an_object: ["Iterator value ", "%0", " is not an entry object"],
   // RangeError
   invalid_array_length:          ["Invalid array length"],
   invalid_array_buffer_length:   ["Invalid array buffer length"],
=======================================
--- /branches/bleeding_edge/test/mjsunit/harmony/collections.js Tue May 20 14:22:05 2014 UTC +++ /branches/bleeding_edge/test/mjsunit/harmony/collections.js Mon Jun 23 18:05:57 2014 UTC
@@ -25,7 +25,7 @@
 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

-// Flags: --harmony-collections
+// Flags: --harmony-collections --harmony-iteration
 // Flags: --expose-gc --allow-natives-syntax


@@ -987,3 +987,261 @@

   assertArrayEquals([0, 1, 2, 3, 4], buffer);
 })();
+
+
+(function TestSetConstructor() {
+  var s = new Set(null);
+  assertEquals(s.size, 0);
+
+  s = new Set(undefined);
+  assertEquals(s.size, 0);
+
+  // No @@iterator
+  assertThrows(function() {
+    new Set({});
+  }, TypeError);
+
+  // @@iterator not callable
+  assertThrows(function() {
+    var object = {};
+    object[Symbol.iterator] = 42;
+    new Set(object);
+  }, TypeError);
+
+  // @@iterator result not object
+  assertThrows(function() {
+    var object = {};
+    object[Symbol.iterator] = function() {
+      return 42;
+    };
+    new Set(object);
+  }, TypeError);
+
+  var s2 = new Set();
+  s2.add('a');
+  s2.add('b');
+  s2.add('c');
+  s = new Set(s2.values());
+  assertEquals(s.size, 3);
+  assertTrue(s.has('a'));
+  assertTrue(s.has('b'));
+  assertTrue(s.has('c'));
+})();
+
+
+(function TestSetConstructorAddNotCallable() {
+  var originalSetPrototypeAdd = Set.prototype.add;
+  assertThrows(function() {
+    Set.prototype.add = 42;
+    new Set([1, 2].values());
+  }, TypeError);
+  Set.prototype.add = originalSetPrototypeAdd;
+})();
+
+
+(function TestSetConstructorGetAddOnce() {
+  var originalSetPrototypeAdd = Set.prototype.add;
+  var getAddCount = 0;
+  Object.defineProperty(Set.prototype, 'add', {
+    get: function() {
+      getAddCount++;
+      return function() {};
+    }
+  });
+  var s = new Set([1, 2].values());
+  assertEquals(getAddCount, 1);
+  assertEquals(s.size, 0);
+  Object.defineProperty(Set.prototype, 'add', {
+    value: originalSetPrototypeAdd,
+    writable: true
+  });
+})();
+
+
+(function TestSetConstructorAddReplaced() {
+  var originalSetPrototypeAdd = Set.prototype.add;
+  var addCount = 0;
+  Set.prototype.add = function(value) {
+    addCount++;
+    originalSetPrototypeAdd.call(this, value);
+    Set.prototype.add = null;
+  };
+  var s = new Set([1, 2].values());
+  assertEquals(addCount, 2);
+  assertEquals(s.size, 2);
+  Set.prototype.add = originalSetPrototypeAdd;
+})();
+
+
+(function TestSetConstructorOrderOfDoneValue() {
+  var valueCount = 0, doneCount = 0;
+  var iterator = {
+    next: function() {
+      return {
+        get value() {
+          valueCount++;
+        },
+        get done() {
+          doneCount++;
+          throw new Error();
+        }
+      };
+    }
+  };
+  iterator[Symbol.iterator] = function() {
+    return this;
+  };
+  assertThrows(function() {
+    new Set(iterator);
+  });
+  assertEquals(doneCount, 1);
+  assertEquals(valueCount, 0);
+})();
+
+
+(function TestSetConstructorNextNotAnObject() {
+  var iterator = {
+    next: function() {
+      return 'abc';
+    }
+  };
+  iterator[Symbol.iterator] = function() {
+    return this;
+  };
+  assertThrows(function() {
+    new Set(iterator);
+  }, TypeError);
+})();
+
+
+(function TestMapConstructor() {
+  var m = new Map(null);
+  assertEquals(m.size, 0);
+
+  m = new Map(undefined);
+  assertEquals(m.size, 0);
+
+  // No @@iterator
+  assertThrows(function() {
+    new Map({});
+  }, TypeError);
+
+  // @@iterator not callable
+  assertThrows(function() {
+    var object = {};
+    object[Symbol.iterator] = 42;
+    new Map(object);
+  }, TypeError);
+
+  // @@iterator result not object
+  assertThrows(function() {
+    var object = {};
+    object[Symbol.iterator] = function() {
+      return 42;
+    };
+    new Map(object);
+  }, TypeError);
+
+  var m2 = new Map();
+  m2.set(0, 'a');
+  m2.set(1, 'b');
+  m2.set(2, 'c');
+  m = new Map(m2.entries());
+  assertEquals(m.size, 3);
+  assertEquals(m.get(0), 'a');
+  assertEquals(m.get(1), 'b');
+  assertEquals(m.get(2), 'c');
+})();
+
+
+(function TestMapConstructorSetNotCallable() {
+  var originalMapPrototypeSet = Map.prototype.set;
+  assertThrows(function() {
+    Map.prototype.set = 42;
+    new Map([1, 2].entries());
+  }, TypeError);
+  Map.prototype.set = originalMapPrototypeSet;
+})();
+
+
+(function TestMapConstructorGetAddOnce() {
+  var originalMapPrototypeSet = Map.prototype.set;
+  var getSetCount = 0;
+  Object.defineProperty(Map.prototype, 'set', {
+    get: function() {
+      getSetCount++;
+      return function() {};
+    }
+  });
+  var m = new Map([1, 2].entries());
+  assertEquals(getSetCount, 1);
+  assertEquals(m.size, 0);
+  Object.defineProperty(Map.prototype, 'set', {
+    value: originalMapPrototypeSet,
+    writable: true
+  });
+})();
+
+
+(function TestMapConstructorSetReplaced() {
+  var originalMapPrototypeSet = Map.prototype.set;
+  var setCount = 0;
+  Map.prototype.set = function(key, value) {
+    setCount++;
+    originalMapPrototypeSet.call(this, key, value);
+    Map.prototype.set = null;
+  };
+  var m = new Map([1, 2].entries());
+  assertEquals(setCount, 2);
+  assertEquals(m.size, 2);
+  Map.prototype.set = originalMapPrototypeSet;
+})();
+
+
+(function TestMapConstructorOrderOfDoneValue() {
+  var valueCount = 0, doneCount = 0;
+  function FakeError() {}
+  var iterator = {
+    next: function() {
+      return {
+        get value() {
+          valueCount++;
+        },
+        get done() {
+          doneCount++;
+          throw new FakeError();
+        }
+      };
+    }
+  };
+  iterator[Symbol.iterator] = function() {
+    return this;
+  };
+  assertThrows(function() {
+    new Map(iterator);
+  }, FakeError);
+  assertEquals(doneCount, 1);
+  assertEquals(valueCount, 0);
+})();
+
+
+(function TestMapConstructorNextNotAnObject() {
+  var iterator = {
+    next: function() {
+      return 'abc';
+    }
+  };
+  iterator[Symbol.iterator] = function() {
+    return this;
+  };
+  assertThrows(function() {
+    new Map(iterator);
+  }, TypeError);
+})();
+
+
+(function TestMapConstructorIteratorNotObjectValues() {
+  assertThrows(function() {
+    new Map([1, 2].values());
+  }, TypeError);
+})();
=======================================
--- /branches/bleeding_edge/tools/generate-runtime-tests.py Mon Jun 23 09:02:16 2014 UTC +++ /branches/bleeding_edge/tools/generate-runtime-tests.py Mon Jun 23 18:05:57 2014 UTC
@@ -51,7 +51,7 @@
 EXPECTED_FUZZABLE_COUNT = 326
 EXPECTED_CCTEST_COUNT = 6
 EXPECTED_UNKNOWN_COUNT = 4
-EXPECTED_BUILTINS_COUNT = 798
+EXPECTED_BUILTINS_COUNT = 800


 # Don't call these at all.

--
--
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
--- You received this message because you are subscribed to the Google Groups "v8-dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.

Reply via email to