ES6: Implement WeakMap and WeakSet constructor logic

Now that iterators are enabled by default we need to correctly
handle the parameter for WeakMap and WeakSet. If provided then the
argument is iterated over to add entries to the WeakMap and WeakSet.

BUG=v8:3399
LOG=Y
R=adamk@chromium.org, rossberg@chromium.org

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

Patch from Erik Arvidsson <arv@chromium.org>.

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@22999 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
rossberg@chromium.org 2014-08-08 13:39:13 +00:00
parent 8627bd5347
commit f7c49bafb0
4 changed files with 228 additions and 146 deletions

View File

@ -12,35 +12,6 @@ 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

View File

@ -1875,3 +1875,33 @@ function SetUpFunction() {
}
SetUpFunction();
// ----------------------------------------------------------------------------
// Iterator related spec functions.
// ES6 rev 26, 2014-07-18
// 7.4.1 CheckIterable ( obj )
function ToIterable(obj) {
if (!IS_SPEC_OBJECT(obj)) {
return UNDEFINED;
}
return obj[symbolIterator];
}
// ES6 rev 26, 2014-07-18
// 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;
}

View File

@ -15,12 +15,36 @@ var $WeakSet = global.WeakSet;
// -------------------------------------------------------------------
// Harmony WeakMap
function WeakMapConstructor() {
if (%_IsConstructCall()) {
%WeakCollectionInitialize(this);
} else {
function WeakMapConstructor(iterable) {
if (!%_IsConstructCall()) {
throw MakeTypeError('constructor_not_function', ['WeakMap']);
}
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]);
}
}
%WeakCollectionInitialize(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);
}
}
@ -107,12 +131,32 @@ SetUpWeakMap();
// -------------------------------------------------------------------
// Harmony WeakSet
function WeakSetConstructor() {
if (%_IsConstructCall()) {
%WeakCollectionInitialize(this);
} else {
function WeakSetConstructor(iterable) {
if (!%_IsConstructCall()) {
throw MakeTypeError('constructor_not_function', ['WeakSet']);
}
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]);
}
}
%WeakCollectionInitialize(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);
}
}

View File

@ -28,6 +28,13 @@
// Flags: --expose-gc --allow-natives-syntax
function assertSize(expected, collection) {
if (collection instanceof Map || collection instanceof Set) {
assertEquals(expected, collection.size);
}
}
// Test valid getter and setter calls on Sets and WeakSets
function TestValidSetCalls(m) {
assertDoesNotThrow(function () { m.add(new Object) });
@ -309,6 +316,7 @@ function TestConstructor(C) {
assertFalse(C === Object.prototype.constructor);
assertSame(C, C.prototype.constructor);
assertSame(C, (new C).__proto__.constructor);
assertEquals(1, C.length);
}
TestConstructor(Set);
TestConstructor(Map);
@ -987,23 +995,32 @@ for (var i = 9; i >= 0; i--) {
})();
(function TestSetConstructor() {
var s = new Set(null);
assertEquals(s.size, 0);
// Allows testing iterator-based constructors easily.
var oneAndTwo = new Map();
var k0 = {key: 0};
var k1 = {key: 1};
var k2 = {key: 2};
oneAndTwo.set(k1, 1);
oneAndTwo.set(k2, 2);
s = new Set(undefined);
assertEquals(s.size, 0);
function TestSetConstructor(ctor) {
var s = new ctor(null);
assertSize(0, s);
s = new ctor(undefined);
assertSize(0, s);
// No @@iterator
assertThrows(function() {
new Set({});
new ctor({});
}, TypeError);
// @@iterator not callable
assertThrows(function() {
var object = {};
object[Symbol.iterator] = 42;
new Set(object);
new ctor(object);
}, TypeError);
// @@iterator result not object
@ -1012,72 +1029,74 @@ for (var i = 9; i >= 0; i--) {
object[Symbol.iterator] = function() {
return 42;
};
new Set(object);
new ctor(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'));
})();
s2.add(k0);
s2.add(k1);
s2.add(k2);
s = new ctor(s2.values());
assertSize(3, s);
assertTrue(s.has(k0));
assertTrue(s.has(k1));
assertTrue(s.has(k2));
}
TestSetConstructor(Set);
TestSetConstructor(WeakSet);
// Allows testing iterator-based constructors easily.
var oneAndTwo = new Set();
oneAndTwo.add(1);
oneAndTwo.add(2);
(function TestSetConstructorAddNotCallable() {
var originalSetPrototypeAdd = Set.prototype.add;
function TestSetConstructorAddNotCallable(ctor) {
var originalPrototypeAdd = ctor.prototype.add;
assertThrows(function() {
Set.prototype.add = 42;
new Set(oneAndTwo.values());
ctor.prototype.add = 42;
new ctor(oneAndTwo.values());
}, TypeError);
Set.prototype.add = originalSetPrototypeAdd;
})();
ctor.prototype.add = originalPrototypeAdd;
}
TestSetConstructorAddNotCallable(Set);
TestSetConstructorAddNotCallable(WeakSet);
(function TestSetConstructorGetAddOnce() {
var originalSetPrototypeAdd = Set.prototype.add;
function TestSetConstructorGetAddOnce(ctor) {
var originalPrototypeAdd = ctor.prototype.add;
var getAddCount = 0;
Object.defineProperty(Set.prototype, 'add', {
Object.defineProperty(ctor.prototype, 'add', {
get: function() {
getAddCount++;
return function() {};
}
});
var s = new Set(oneAndTwo.values());
assertEquals(getAddCount, 1);
assertEquals(s.size, 0);
Object.defineProperty(Set.prototype, 'add', {
value: originalSetPrototypeAdd,
var s = new ctor(oneAndTwo.values());
assertEquals(1, getAddCount);
assertSize(0, s);
Object.defineProperty(ctor.prototype, 'add', {
value: originalPrototypeAdd,
writable: true
});
})();
}
TestSetConstructorGetAddOnce(Set);
TestSetConstructorGetAddOnce(WeakSet);
(function TestSetConstructorAddReplaced() {
var originalSetPrototypeAdd = Set.prototype.add;
function TestSetConstructorAddReplaced(ctor) {
var originalPrototypeAdd = ctor.prototype.add;
var addCount = 0;
Set.prototype.add = function(value) {
ctor.prototype.add = function(value) {
addCount++;
originalSetPrototypeAdd.call(this, value);
Set.prototype.add = null;
originalPrototypeAdd.call(this, value);
ctor.prototype.add = null;
};
var s = new Set(oneAndTwo.values());
assertEquals(addCount, 2);
assertEquals(s.size, 2);
Set.prototype.add = originalSetPrototypeAdd;
})();
var s = new ctor(oneAndTwo.keys());
assertEquals(2, addCount);
assertSize(2, s);
ctor.prototype.add = originalPrototypeAdd;
}
TestSetConstructorAddReplaced(Set);
TestSetConstructorAddReplaced(WeakSet);
(function TestSetConstructorOrderOfDoneValue() {
function TestSetConstructorOrderOfDoneValue(ctor) {
var valueCount = 0, doneCount = 0;
var iterator = {
next: function() {
@ -1096,14 +1115,16 @@ oneAndTwo.add(2);
return this;
};
assertThrows(function() {
new Set(iterator);
new ctor(iterator);
});
assertEquals(doneCount, 1);
assertEquals(valueCount, 0);
})();
assertEquals(1, doneCount);
assertEquals(0, valueCount);
}
TestSetConstructorOrderOfDoneValue(Set);
TestSetConstructorOrderOfDoneValue(WeakSet);
(function TestSetConstructorNextNotAnObject() {
function TestSetConstructorNextNotAnObject(ctor) {
var iterator = {
next: function() {
return 'abc';
@ -1113,28 +1134,30 @@ oneAndTwo.add(2);
return this;
};
assertThrows(function() {
new Set(iterator);
new ctor(iterator);
}, TypeError);
})();
}
TestSetConstructorNextNotAnObject(Set);
TestSetConstructorNextNotAnObject(WeakSet);
(function TestMapConstructor() {
var m = new Map(null);
assertEquals(m.size, 0);
function TestMapConstructor(ctor) {
var m = new ctor(null);
assertSize(0, m);
m = new Map(undefined);
assertEquals(m.size, 0);
m = new ctor(undefined);
assertSize(0, m);
// No @@iterator
assertThrows(function() {
new Map({});
new ctor({});
}, TypeError);
// @@iterator not callable
assertThrows(function() {
var object = {};
object[Symbol.iterator] = 42;
new Map(object);
new ctor(object);
}, TypeError);
// @@iterator result not object
@ -1143,66 +1166,74 @@ oneAndTwo.add(2);
object[Symbol.iterator] = function() {
return 42;
};
new Map(object);
new ctor(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');
})();
m2.set(k0, 'a');
m2.set(k1, 'b');
m2.set(k2, 'c');
m = new ctor(m2.entries());
assertSize(3, m);
assertEquals('a', m.get(k0));
assertEquals('b', m.get(k1));
assertEquals('c', m.get(k2));
}
TestMapConstructor(Map);
TestMapConstructor(WeakMap);
(function TestMapConstructorSetNotCallable() {
var originalMapPrototypeSet = Map.prototype.set;
function TestMapConstructorSetNotCallable(ctor) {
var originalPrototypeSet = ctor.prototype.set;
assertThrows(function() {
Map.prototype.set = 42;
new Map(oneAndTwo.entries());
ctor.prototype.set = 42;
new ctor(oneAndTwo.entries());
}, TypeError);
Map.prototype.set = originalMapPrototypeSet;
})();
ctor.prototype.set = originalPrototypeSet;
}
TestMapConstructorSetNotCallable(Map);
TestMapConstructorSetNotCallable(WeakMap);
(function TestMapConstructorGetAddOnce() {
var originalMapPrototypeSet = Map.prototype.set;
function TestMapConstructorGetAddOnce(ctor) {
var originalPrototypeSet = ctor.prototype.set;
var getSetCount = 0;
Object.defineProperty(Map.prototype, 'set', {
Object.defineProperty(ctor.prototype, 'set', {
get: function() {
getSetCount++;
return function() {};
}
});
var m = new Map(oneAndTwo.entries());
assertEquals(getSetCount, 1);
assertEquals(m.size, 0);
Object.defineProperty(Map.prototype, 'set', {
value: originalMapPrototypeSet,
var m = new ctor(oneAndTwo.entries());
assertEquals(1, getSetCount);
assertSize(0, m);
Object.defineProperty(ctor.prototype, 'set', {
value: originalPrototypeSet,
writable: true
});
})();
}
TestMapConstructorGetAddOnce(Map);
TestMapConstructorGetAddOnce(WeakMap);
(function TestMapConstructorSetReplaced() {
var originalMapPrototypeSet = Map.prototype.set;
function TestMapConstructorSetReplaced(ctor) {
var originalPrototypeSet = ctor.prototype.set;
var setCount = 0;
Map.prototype.set = function(key, value) {
ctor.prototype.set = function(key, value) {
setCount++;
originalMapPrototypeSet.call(this, key, value);
Map.prototype.set = null;
originalPrototypeSet.call(this, key, value);
ctor.prototype.set = null;
};
var m = new Map(oneAndTwo.entries());
assertEquals(setCount, 2);
assertEquals(m.size, 2);
Map.prototype.set = originalMapPrototypeSet;
})();
var m = new ctor(oneAndTwo.entries());
assertEquals(2, setCount);
assertSize(2, m);
ctor.prototype.set = originalPrototypeSet;
}
TestMapConstructorSetReplaced(Map);
TestMapConstructorSetReplaced(WeakMap);
(function TestMapConstructorOrderOfDoneValue() {
function TestMapConstructorOrderOfDoneValue(ctor) {
var valueCount = 0, doneCount = 0;
function FakeError() {}
var iterator = {
@ -1222,14 +1253,16 @@ oneAndTwo.add(2);
return this;
};
assertThrows(function() {
new Map(iterator);
new ctor(iterator);
}, FakeError);
assertEquals(doneCount, 1);
assertEquals(valueCount, 0);
})();
assertEquals(1, doneCount);
assertEquals(0, valueCount);
}
TestMapConstructorOrderOfDoneValue(Map);
TestMapConstructorOrderOfDoneValue(WeakMap);
(function TestMapConstructorNextNotAnObject() {
function TestMapConstructorNextNotAnObject(ctor) {
var iterator = {
next: function() {
return 'abc';
@ -1239,13 +1272,17 @@ oneAndTwo.add(2);
return this;
};
assertThrows(function() {
new Map(iterator);
new ctor(iterator);
}, TypeError);
})();
}
TestMapConstructorNextNotAnObject(Map);
TestMapConstructorNextNotAnObject(WeakMap);
(function TestMapConstructorIteratorNotObjectValues() {
function TestMapConstructorIteratorNotObjectValues(ctor) {
assertThrows(function() {
new Map(oneAndTwo.values());
new ctor(oneAndTwo.values());
}, TypeError);
})();
}
TestMapConstructorIteratorNotObjectValues(Map);
TestMapConstructorIteratorNotObjectValues(WeakMap);