Commit 257adcf0 authored by adamk@chromium.org's avatar adamk@chromium.org

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
R=rossberg@chromium.org

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

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

git-svn-id: https://v8.googlecode.com/svn/branches/bleeding_edge@21950 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
parent 6b264de7
......@@ -12,15 +12,64 @@ 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 @@ SetUpSet();
// -------------------------------------------------------------------
// 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);
}
}
......
......@@ -89,6 +89,10 @@ var kMessages = {
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"],
......
......@@ -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 @@ for (var i = 9; i >= 0; i--) {
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);
})();
......@@ -51,7 +51,7 @@ EXPECTED_FUNCTION_COUNT = 358
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.
......
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