| Index: src/collection.js
|
| diff --git a/src/collection.js b/src/collection.js
|
| index c8cf639f97d96c3396f463a97ad5181ac4ccc347..537dbd69cbb5ecb1ef0905d05f6636484cbce88b 100644
|
| --- a/src/collection.js
|
| +++ b/src/collection.js
|
| @@ -12,6 +12,86 @@ var $Set = global.Set;
|
| var $Map = global.Map;
|
|
|
|
|
| +// Used by harmony-templates.js
|
| +var $MapGet;
|
| +var $MapSet;
|
| +
|
| +
|
| +(function() {
|
| +
|
| +
|
| +function HashToEntry(table, hash, numBuckets) {
|
| + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
|
| + return ORDERED_HASH_TABLE_BUCKET_AT(table, 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 = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets)) {
|
| + var candidate = ORDERED_HASH_SET_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(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 = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets)) {
|
| + var candidate = ORDERED_HASH_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 ComputeIntegerHash(key, seed) {
|
| + var hash = key;
|
| + hash = hash ^ seed;
|
| + hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
|
| + hash = hash ^ (hash >>> 12);
|
| + hash = hash + (hash << 2);
|
| + hash = hash ^ (hash >>> 4);
|
| + hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11);
|
| + hash = hash ^ (hash >>> 16);
|
| + return hash;
|
| +}
|
| +%SetInlineBuiltinFlag(ComputeIntegerHash);
|
| +
|
| +
|
| +function GetHash(key) {
|
| + if (%_IsSmi(key)) {
|
| + return ComputeIntegerHash(key, 0);
|
| + }
|
| + if (IS_STRING(key)) {
|
| + var field = %_StringGetRawHashField(key);
|
| + if ((field & 1 /* Name::kHashNotComputedMask */) === 0) {
|
| + return field >>> 2 /* Name::kHashShift */;
|
| + }
|
| + }
|
| + return %GenericHash(key);
|
| +}
|
| +%SetInlineBuiltinFlag(GetHash);
|
| +
|
| +
|
| // -------------------------------------------------------------------
|
| // Harmony Set
|
|
|
| @@ -35,7 +115,7 @@ function SetConstructor(iterable) {
|
| }
|
|
|
|
|
| -function SetAddJS(key) {
|
| +function SetAdd(key) {
|
| if (!IS_SET(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Set.prototype.add', this]);
|
| @@ -47,34 +127,76 @@ function SetAddJS(key) {
|
| if (key === 0) {
|
| key = 0;
|
| }
|
| - return %_SetAdd(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
|
| +
|
| + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
|
| + var capacity = numBuckets << 1;
|
| + if ((nof + nod) >= capacity) {
|
| + // Need to grow, bail out to runtime.
|
| + %SetGrow(this);
|
| + // Re-load state from the grown backing store.
|
| + table = %_JSCollectionGetTable(this);
|
| + numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| + nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
|
| + }
|
| + var entry = nof + nod;
|
| + var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
|
| + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
|
| + var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
|
| + ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
|
| + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
|
| + FIXED_ARRAY_SET(table, index, key);
|
| + FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry);
|
| + return this;
|
| }
|
|
|
|
|
| -function SetHasJS(key) {
|
| +function SetHas(key) {
|
| if (!IS_SET(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Set.prototype.has', this]);
|
| }
|
| - return %_SetHas(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
|
| }
|
|
|
|
|
| -function SetDeleteJS(key) {
|
| +function SetDelete(key) {
|
| if (!IS_SET(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Set.prototype.delete', this]);
|
| }
|
| - return %_SetDelete(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + var entry = SetFindEntry(table, numBuckets, key, hash);
|
| + if (entry === NOT_FOUND) return false;
|
| +
|
| + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
|
| + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
|
| + var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
|
| + FIXED_ARRAY_SET(table, index, %_TheHole());
|
| + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
|
| + ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
|
| + if (nof < (numBuckets >>> 1)) %SetShrink(this);
|
| + return true;
|
| }
|
|
|
|
|
| -function SetGetSizeJS() {
|
| +function SetGetSize() {
|
| if (!IS_SET(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Set.prototype.size', this]);
|
| }
|
| - return %_SetGetSize(this);
|
| + var table = %_JSCollectionGetTable(this);
|
| + return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| }
|
|
|
|
|
| @@ -130,11 +252,11 @@ function SetUpSet() {
|
| %FunctionSetLength(SetForEach, 1);
|
|
|
| // Set up the non-enumerable functions on the Set prototype object.
|
| - InstallGetter($Set.prototype, "size", SetGetSizeJS);
|
| + InstallGetter($Set.prototype, "size", SetGetSize);
|
| InstallFunctions($Set.prototype, DONT_ENUM, $Array(
|
| - "add", SetAddJS,
|
| - "has", SetHasJS,
|
| - "delete", SetDeleteJS,
|
| + "add", SetAdd,
|
| + "has", SetHas,
|
| + "delete", SetDelete,
|
| "clear", SetClearJS,
|
| "forEach", SetForEach
|
| ));
|
| @@ -169,16 +291,21 @@ function MapConstructor(iterable) {
|
| }
|
|
|
|
|
| -function MapGetJS(key) {
|
| +function MapGet(key) {
|
| if (!IS_MAP(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Map.prototype.get', this]);
|
| }
|
| - return %_MapGet(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + var entry = MapFindEntry(table, numBuckets, key, hash);
|
| + if (entry === NOT_FOUND) return UNDEFINED;
|
| + return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets);
|
| }
|
|
|
|
|
| -function MapSetJS(key, value) {
|
| +function MapSet(key, value) {
|
| if (!IS_MAP(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Map.prototype.set', this]);
|
| @@ -190,34 +317,84 @@ function MapSetJS(key, value) {
|
| if (key === 0) {
|
| key = 0;
|
| }
|
| - return %_MapSet(this, key, value);
|
| +
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + var entry = MapFindEntry(table, numBuckets, key, hash);
|
| + if (entry !== NOT_FOUND) {
|
| + var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
|
| + FIXED_ARRAY_SET(table, existingIndex + 1, value);
|
| + return this;
|
| + }
|
| +
|
| + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
|
| + var capacity = numBuckets << 1;
|
| + if ((nof + nod) >= capacity) {
|
| + // Need to grow, bail out to runtime.
|
| + %MapGrow(this);
|
| + // Re-load state from the grown backing store.
|
| + table = %_JSCollectionGetTable(this);
|
| + numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| + nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
|
| + }
|
| + entry = nof + nod;
|
| + var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
|
| + var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
|
| + var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
|
| + ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
|
| + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
|
| + FIXED_ARRAY_SET(table, index, key);
|
| + FIXED_ARRAY_SET(table, index + 1, value);
|
| + FIXED_ARRAY_SET(table, index + 2, chainEntry);
|
| + return this;
|
| }
|
|
|
|
|
| -function MapHasJS(key) {
|
| +function MapHas(key) {
|
| if (!IS_MAP(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Map.prototype.has', this]);
|
| }
|
| - return %_MapHas(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
|
| }
|
|
|
|
|
| -function MapDeleteJS(key) {
|
| +function MapDelete(key) {
|
| if (!IS_MAP(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Map.prototype.delete', this]);
|
| }
|
| - return %_MapDelete(this, key);
|
| + var table = %_JSCollectionGetTable(this);
|
| + var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
|
| + var hash = GetHash(key);
|
| + var entry = MapFindEntry(table, numBuckets, key, hash);
|
| + if (entry === NOT_FOUND) return false;
|
| +
|
| + var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
|
| + var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
|
| + var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
|
| + FIXED_ARRAY_SET(table, index, %_TheHole());
|
| + FIXED_ARRAY_SET(table, index + 1, %_TheHole());
|
| + ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
|
| + ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
|
| + if (nof < (numBuckets >>> 1)) %MapShrink(this);
|
| + return true;
|
| }
|
|
|
|
|
| -function MapGetSizeJS() {
|
| +function MapGetSize() {
|
| if (!IS_MAP(this)) {
|
| throw MakeTypeError('incompatible_method_receiver',
|
| ['Map.prototype.size', this]);
|
| }
|
| - return %_MapGetSize(this);
|
| + var table = %_JSCollectionGetTable(this);
|
| + return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
|
| }
|
|
|
|
|
| @@ -271,15 +448,20 @@ function SetUpMap() {
|
| %FunctionSetLength(MapForEach, 1);
|
|
|
| // Set up the non-enumerable functions on the Map prototype object.
|
| - InstallGetter($Map.prototype, "size", MapGetSizeJS);
|
| + InstallGetter($Map.prototype, "size", MapGetSize);
|
| InstallFunctions($Map.prototype, DONT_ENUM, $Array(
|
| - "get", MapGetJS,
|
| - "set", MapSetJS,
|
| - "has", MapHasJS,
|
| - "delete", MapDeleteJS,
|
| + "get", MapGet,
|
| + "set", MapSet,
|
| + "has", MapHas,
|
| + "delete", MapDelete,
|
| "clear", MapClearJS,
|
| "forEach", MapForEach
|
| ));
|
| +
|
| + $MapGet = MapGet;
|
| + $MapSet = MapSet;
|
| }
|
|
|
| SetUpMap();
|
| +
|
| +})();
|
|
|