Commit f7c49baf authored by rossberg@chromium.org's avatar rossberg@chromium.org

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
parent 8627bd53
......@@ -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
......
......@@ -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;
}
......@@ -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);
}
}
......
......@@ -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'));
})();
// Allows testing iterator-based constructors easily.
var oneAndTwo = new Set();
oneAndTwo.add(1);
oneAndTwo.add(2);
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);
(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);
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment