Chromium Code Reviews| Index: src/collection.js |
| diff --git a/src/collection.js b/src/collection.js |
| index 9b5716687bcc14ed06e628afd15de795b07c1a14..3033907afd8548c3bbc53550922e7166da4703e0 100644 |
| --- a/src/collection.js |
| +++ b/src/collection.js |
| @@ -2,303 +2,456 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| -"use strict"; |
| - |
| // This file relies on the fact that the following declaration has been made |
| // in runtime.js: |
| +// var $Object = global.Object; |
| // var $Array = global.Array; |
| var $Set = global.Set; |
| var $Map = global.Map; |
| -// ------------------------------------------------------------------- |
| -// Harmony Set |
| +(function() { |
| + "use strict"; |
| + |
| -function SetConstructor(iterable) { |
| - if (!%_IsConstructCall()) { |
| - throw MakeTypeError('constructor_not_function', ['Set']); |
| + function HashToBucket(hash, numBuckets) { |
| + return hash & (numBuckets - 1); |
| } |
| + %SetInlineBuiltinFlag(HashToBucket); |
| - 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]); |
| + function HashToEntry(table, hash, numBuckets) { |
| + var bucket = HashToBucket(hash, numBuckets); |
| + return %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); |
| + } |
| + %SetInlineBuiltinFlag(HashToEntry); |
| + |
| + |
| + function SetFindEntry(table, numBuckets, key, hash) { |
| + var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); |
| + for (var entry = HashToEntry(table, hash, numBuckets); |
| + entry !== NOT_FOUND; |
| + entry = SET_CHAIN_AT(table, entry, numBuckets)) { |
| + var candidate = SET_KEY_AT(table, entry, numBuckets); |
| + if (key === candidate) { |
| + return entry; |
| + } |
| + if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) { |
|
arv (Not doing code reviews)
2015/02/21 16:20:16
Nice optimization to get around the NaN SameValueZ
arv (Not doing code reviews)
2015/02/21 16:20:16
Nice optimization to get around the NaN SameValueZ
|
| + return entry; |
| + } |
| } |
| + return NOT_FOUND; |
| + } |
| + %SetInlineBuiltinFlag(SetFindEntry); |
| + |
| + |
| + function MapFindEntry(table, numBuckets, key, hash) { |
| + var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); |
| + for (var entry = HashToEntry(table, hash, numBuckets); |
| + entry !== NOT_FOUND; |
| + entry = MAP_CHAIN_AT(table, entry, numBuckets)) { |
| + var candidate = MAP_KEY_AT(table, entry, numBuckets); |
| + if (key === candidate) { |
| + return entry; |
| + } |
| + if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) { |
| + return entry; |
| + } |
| + } |
| + return NOT_FOUND; |
| + } |
| + %SetInlineBuiltinFlag(MapFindEntry); |
| + |
| + |
| + function GetHash(key) { |
| + if (%_IsSmi(key)) return %_SmiHash(key); |
| + if (IS_STRING(key)) return %_StringHash(key); |
| + return %GenericHash(key); |
| } |
| + %SetInlineBuiltinFlag(GetHash); |
| - %_SetInitialize(this); |
| - if (IS_UNDEFINED(iter)) return; |
| + // ------------------------------------------------------------------- |
| + // Harmony Set |
| - var next, done; |
| - while (!(next = iter.next()).done) { |
| - if (!IS_SPEC_OBJECT(next)) { |
| - throw MakeTypeError('iterator_result_not_an_object', [next]); |
| + function SetConstructor(iterable) { |
| + if (!%_IsConstructCall()) { |
| + throw MakeTypeError('constructor_not_function', ['Set']); |
| } |
| - %_CallFunction(this, next.value, adder); |
| - } |
| -} |
| + var iter, adder; |
| -function SetAddJS(key) { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.add', this]); |
| - } |
| - // Normalize -0 to +0 as required by the spec. |
| - // Even though we use SameValueZero as the comparison for the keys we don't |
| - // want to ever store -0 as the key since the key is directly exposed when |
| - // doing iteration. |
| - if (key === 0) { |
| - key = 0; |
| - } |
| - return %_SetAdd(this, key); |
| -} |
| + 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; |
| -function SetHasJS(key) { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.has', this]); |
| + 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); |
| + } |
| } |
| - return %_SetHas(this, key); |
| -} |
| -function SetDeleteJS(key) { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.delete', this]); |
| + function SetAdd(key) { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.add', this]); |
| + } |
| + // Normalize -0 to +0 as required by the spec. |
| + // Even though we use SameValueZero as the comparison for the keys we don't |
| + // want to ever store -0 as the key since the key is directly exposed when |
| + // doing iteration. |
| + if (IS_NUMBER(key) && key === 0) { |
| + key = 0; |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this; |
| + |
| + var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); |
| + var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX); |
| + var capacity = numBuckets << 1; |
| + if ((nof + nod) >= capacity) { |
| + // Need to grow, bail out to runtime. |
| + %SetGrow(this); |
| + // Re-enter...TODO(adamk) Fix this |
| + return %_CallFunction(this, key, SetAdd); |
| + } |
| + var entry = nof + nod; |
| + var index = SET_ENTRY_TO_INDEX(entry, numBuckets); |
| + var bucket = HashToBucket(hash, numBuckets); |
| + var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); |
| + %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry); |
| + %_FixedArraySet(table, index + SET_CHAIN_OFFSET, chainEntry); |
| + %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof + 1); |
| + %_FixedArraySet(table, index, key); |
| + return this; |
| } |
| - return %_SetDelete(this, key); |
| -} |
| -function SetGetSizeJS() { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.size', this]); |
| + function SetHas(key) { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.has', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; |
| } |
| - return %_SetGetSize(this); |
| -} |
| -function SetClearJS() { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.clear', this]); |
| + function SetDelete(key) { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.delete', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + var entry = SetFindEntry(table, numBuckets, key, hash); |
| + if (entry === NOT_FOUND) return false; |
| + |
| + var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1; |
| + var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1; |
| + var index = SET_ENTRY_TO_INDEX(entry, numBuckets); |
| + %_FixedArraySetTheHole(table, index); |
| + %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof); |
| + %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod); |
| + if (nof < (numBuckets >> 1)) %SetShrink(this); |
| + return true; |
| } |
| - %_SetClear(this); |
| -} |
| -function SetForEach(f, receiver) { |
| - if (!IS_SET(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Set.prototype.forEach', this]); |
| + function SetGetSize() { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.size', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); |
| } |
| - if (!IS_SPEC_FUNCTION(f)) { |
| - throw MakeTypeError('called_non_callable', [f]); |
| - } |
| - var needs_wrapper = false; |
| - if (IS_NULL_OR_UNDEFINED(receiver)) { |
| - receiver = %GetDefaultReceiver(f) || receiver; |
| - } else { |
| - needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); |
| - } |
| - var iterator = new SetIterator(this, ITERATOR_KIND_VALUES); |
| - var key; |
| - var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
| - var value_array = [UNDEFINED]; |
| - while (%SetIteratorNext(iterator, value_array)) { |
| - if (stepping) %DebugPrepareStepInIfStepping(f); |
| - key = value_array[0]; |
| - var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; |
| - %_CallFunction(new_receiver, key, key, this, f); |
| + function SetClearJS() { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.clear', this]); |
| + } |
| + %_SetClear(this); |
| } |
| -} |
| -// ------------------------------------------------------------------- |
| + function SetForEach(f, receiver) { |
| + if (!IS_SET(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Set.prototype.forEach', this]); |
| + } |
| -function SetUpSet() { |
| - %CheckIsBootstrapping(); |
| + if (!IS_SPEC_FUNCTION(f)) { |
| + throw MakeTypeError('called_non_callable', [f]); |
| + } |
| + var needs_wrapper = false; |
| + if (IS_NULL_OR_UNDEFINED(receiver)) { |
| + receiver = %GetDefaultReceiver(f) || receiver; |
| + } else { |
| + needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); |
| + } |
| - %SetCode($Set, SetConstructor); |
| - %FunctionSetPrototype($Set, new $Object()); |
| - %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); |
| - %AddNamedProperty( |
| - $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY); |
| + var iterator = new SetIterator(this, ITERATOR_KIND_VALUES); |
| + var key; |
| + var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
| + var value_array = [UNDEFINED]; |
| + while (%SetIteratorNext(iterator, value_array)) { |
|
arv (Not doing code reviews)
2015/02/21 16:20:16
This one can get much faster now that we have acce
adamk
2015/02/23 18:43:34
Yeah, there are definitely opportunities to speed
|
| + if (stepping) %DebugPrepareStepInIfStepping(f); |
| + key = value_array[0]; |
| + var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; |
| + %_CallFunction(new_receiver, key, key, this, f); |
| + } |
| + } |
| - %FunctionSetLength(SetForEach, 1); |
| - // Set up the non-enumerable functions on the Set prototype object. |
| - InstallGetter($Set.prototype, "size", SetGetSizeJS); |
| - InstallFunctions($Set.prototype, DONT_ENUM, $Array( |
| - "add", SetAddJS, |
| - "has", SetHasJS, |
| - "delete", SetDeleteJS, |
| - "clear", SetClearJS, |
| - "forEach", SetForEach |
| - )); |
| -} |
| + // ------------------------------------------------------------------- |
| -SetUpSet(); |
| + function SetUpSet() { |
| + %CheckIsBootstrapping(); |
| + %SetCode($Set, SetConstructor); |
| + %FunctionSetPrototype($Set, new $Object()); |
| + %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); |
| + %AddNamedProperty( |
| + $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY); |
| -// ------------------------------------------------------------------- |
| -// Harmony Map |
| + %FunctionSetLength(SetForEach, 1); |
| -function MapConstructor(iterable) { |
| - if (!%_IsConstructCall()) { |
| - throw MakeTypeError('constructor_not_function', ['Map']); |
| + // Set up the non-enumerable functions on the Set prototype object. |
| + InstallGetter($Set.prototype, "size", SetGetSize); |
| + InstallFunctions($Set.prototype, DONT_ENUM, $Array( |
| + "add", SetAdd, |
| + "has", SetHas, |
| + "delete", SetDelete, |
| + "clear", SetClearJS, |
| + "forEach", SetForEach |
| + )); |
| } |
| - var iter, adder; |
| + SetUpSet(); |
| - 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); |
| + // ------------------------------------------------------------------- |
| + // Harmony Map |
| + |
| + function MapConstructor(iterable) { |
| + if (!%_IsConstructCall()) { |
| + throw MakeTypeError('constructor_not_function', ['Map']); |
| + } |
| - if (IS_UNDEFINED(iter)) return; |
| + var iter, adder; |
| - var next, done, nextItem; |
| - while (!(next = iter.next()).done) { |
| - if (!IS_SPEC_OBJECT(next)) { |
| - throw MakeTypeError('iterator_result_not_an_object', [next]); |
| + if (!IS_NULL_OR_UNDEFINED(iterable)) { |
| + iter = GetIterator(iterable); |
| + adder = this.set; |
| + if (!IS_SPEC_FUNCTION(adder)) { |
| + throw MakeTypeError('property_not_function', ['set', this]); |
| + } |
| } |
| - nextItem = next.value; |
| - if (!IS_SPEC_OBJECT(nextItem)) { |
| - throw MakeTypeError('iterator_value_not_an_object', [nextItem]); |
| + |
| + %_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); |
| } |
| - %_CallFunction(this, nextItem[0], nextItem[1], adder); |
| } |
| -} |
| -function MapGetJS(key) { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.get', this]); |
| + function MapGet(key) { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.get', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + var entry = MapFindEntry(table, numBuckets, key, hash); |
| + if (entry === NOT_FOUND) return UNDEFINED; |
| + return MAP_VALUE_AT(table, entry, numBuckets); |
| } |
| - return %_MapGet(this, key); |
| -} |
| -function MapSetJS(key, value) { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.set', this]); |
| - } |
| - // Normalize -0 to +0 as required by the spec. |
| - // Even though we use SameValueZero as the comparison for the keys we don't |
| - // want to ever store -0 as the key since the key is directly exposed when |
| - // doing iteration. |
| - if (key === 0) { |
| - key = 0; |
| - } |
| - return %_MapSet(this, key, value); |
| -} |
| + function MapSet(key, value) { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.set', this]); |
| + } |
| + // Normalize -0 to +0 as required by the spec. |
| + // Even though we use SameValueZero as the comparison for the keys we don't |
| + // want to ever store -0 as the key since the key is directly exposed when |
| + // doing iteration. |
| + if (IS_NUMBER(key) && key === 0) { |
| + key = 0; |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + var entry = MapFindEntry(table, numBuckets, key, hash); |
| + if (entry !== NOT_FOUND) { |
| + var existingIndex = MAP_ENTRY_TO_INDEX(entry, numBuckets); |
| + %_FixedArraySet(table, existingIndex + 1, value); |
| + return this; |
| + } |
| -function MapHasJS(key) { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.has', this]); |
| + var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); |
| + var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX); |
| + var capacity = numBuckets << 1; |
| + if ((nof + nod) >= capacity) { |
| + // Need to grow, bail out to runtime. |
| + %MapGrow(this); |
| + // Re-enter...TODO(adamk) Fix this |
| + return %_CallFunction(this, key, value, MapSet); |
| + } |
| + entry = nof + nod; |
| + var index = MAP_ENTRY_TO_INDEX(entry, numBuckets); |
| + var bucket = HashToBucket(hash, numBuckets); |
| + var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); |
| + %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry); |
| + %_FixedArraySet(table, index + MAP_CHAIN_OFFSET, chainEntry); |
| + %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof + 1); |
| + %_FixedArraySet(table, index, key); |
| + %_FixedArraySet(table, index + 1, value); |
| + return this; |
| } |
| - return %_MapHas(this, key); |
| -} |
| -function MapDeleteJS(key) { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.delete', this]); |
| + function MapHas(key) { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.has', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; |
| } |
| - return %_MapDelete(this, key); |
| -} |
| -function MapGetSizeJS() { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.size', this]); |
| + function MapDelete(key) { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.delete', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); |
| + var hash = GetHash(key); |
| + var entry = MapFindEntry(table, numBuckets, key, hash); |
| + if (entry === NOT_FOUND) return false; |
| + |
| + var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1; |
| + var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1; |
| + var index = MAP_ENTRY_TO_INDEX(entry, numBuckets); |
| + %_FixedArraySetTheHole(table, index); |
| + %_FixedArraySetTheHole(table, index + 1); |
| + %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof); |
| + %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod); |
| + if (nof < (numBuckets >> 1)) %MapShrink(this); |
| + return true; |
| } |
| - return %_MapGetSize(this); |
| -} |
| -function MapClearJS() { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.clear', this]); |
| + function MapGetSize() { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.size', this]); |
| + } |
| + var table = %_JSCollectionGetTable(this); |
| + return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); |
| } |
| - %_MapClear(this); |
| -} |
| -function MapForEach(f, receiver) { |
| - if (!IS_MAP(this)) { |
| - throw MakeTypeError('incompatible_method_receiver', |
| - ['Map.prototype.forEach', this]); |
| + function MapClearJS() { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.clear', this]); |
| + } |
| + %_MapClear(this); |
| } |
| - if (!IS_SPEC_FUNCTION(f)) { |
| - throw MakeTypeError('called_non_callable', [f]); |
| - } |
| - var needs_wrapper = false; |
| - if (IS_NULL_OR_UNDEFINED(receiver)) { |
| - receiver = %GetDefaultReceiver(f) || receiver; |
| - } else { |
| - needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); |
| - } |
| - var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES); |
| - var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
| - var value_array = [UNDEFINED, UNDEFINED]; |
| - while (%MapIteratorNext(iterator, value_array)) { |
| - if (stepping) %DebugPrepareStepInIfStepping(f); |
| - var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; |
| - %_CallFunction(new_receiver, value_array[1], value_array[0], this, f); |
| + function MapForEach(f, receiver) { |
| + if (!IS_MAP(this)) { |
| + throw MakeTypeError('incompatible_method_receiver', |
| + ['Map.prototype.forEach', this]); |
| + } |
| + |
| + if (!IS_SPEC_FUNCTION(f)) { |
| + throw MakeTypeError('called_non_callable', [f]); |
| + } |
| + var needs_wrapper = false; |
| + if (IS_NULL_OR_UNDEFINED(receiver)) { |
| + receiver = %GetDefaultReceiver(f) || receiver; |
| + } else { |
| + needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); |
| + } |
| + |
| + var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES); |
| + var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); |
| + var value_array = [UNDEFINED, UNDEFINED]; |
| + while (%MapIteratorNext(iterator, value_array)) { |
| + if (stepping) %DebugPrepareStepInIfStepping(f); |
| + var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; |
| + %_CallFunction(new_receiver, value_array[1], value_array[0], this, f); |
| + } |
| } |
| -} |
| -// ------------------------------------------------------------------- |
| + // ------------------------------------------------------------------- |
| + |
| + function SetUpMap() { |
| + %CheckIsBootstrapping(); |
| -function SetUpMap() { |
| - %CheckIsBootstrapping(); |
| + %SetCode($Map, MapConstructor); |
| + %FunctionSetPrototype($Map, new $Object()); |
| + %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); |
| + %AddNamedProperty( |
| + $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY); |
| - %SetCode($Map, MapConstructor); |
| - %FunctionSetPrototype($Map, new $Object()); |
| - %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); |
| - %AddNamedProperty( |
| - $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY); |
| + %FunctionSetLength(MapForEach, 1); |
| - %FunctionSetLength(MapForEach, 1); |
| + // Set up the non-enumerable functions on the Map prototype object. |
| + InstallGetter($Map.prototype, "size", MapGetSize); |
| + InstallFunctions($Map.prototype, DONT_ENUM, $Array( |
| + "get", MapGet, |
| + "set", MapSet, |
| + "has", MapHas, |
| + "delete", MapDelete, |
| + "clear", MapClearJS, |
| + "forEach", MapForEach |
| + )); |
| + } |
| - // Set up the non-enumerable functions on the Map prototype object. |
| - InstallGetter($Map.prototype, "size", MapGetSizeJS); |
| - InstallFunctions($Map.prototype, DONT_ENUM, $Array( |
| - "get", MapGetJS, |
| - "set", MapSetJS, |
| - "has", MapHasJS, |
| - "delete", MapDeleteJS, |
| - "clear", MapClearJS, |
| - "forEach", MapForEach |
| - )); |
| -} |
| + SetUpMap(); |
| -SetUpMap(); |
| +})(); |