Chromium Code Reviews| OLD | NEW | 
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 the V8 project authors. All rights reserved. | 
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 "use strict"; | |
| 6 | |
| 7 // This file relies on the fact that the following declaration has been made | 5 // This file relies on the fact that the following declaration has been made | 
| 8 // in runtime.js: | 6 // in runtime.js: | 
| 7 // var $Object = global.Object; | |
| 9 // var $Array = global.Array; | 8 // var $Array = global.Array; | 
| 10 | 9 | 
| 11 var $Set = global.Set; | 10 var $Set = global.Set; | 
| 12 var $Map = global.Map; | 11 var $Map = global.Map; | 
| 13 | 12 | 
| 14 | 13 | 
| 15 // ------------------------------------------------------------------- | 14 (function() { | 
| 16 // Harmony Set | 15 "use strict"; | 
| 17 | 16 | 
| 18 function SetConstructor(iterable) { | 17 | 
| 19 if (!%_IsConstructCall()) { | 18 function HashToBucket(hash, numBuckets) { | 
| 20 throw MakeTypeError('constructor_not_function', ['Set']); | 19 return hash & (numBuckets - 1); | 
| 21 } | 20 } | 
| 22 | 21 %SetInlineBuiltinFlag(HashToBucket); | 
| 23 var iter, adder; | 22 | 
| 24 | 23 | 
| 25 if (!IS_NULL_OR_UNDEFINED(iterable)) { | 24 function HashToEntry(table, hash, numBuckets) { | 
| 26 iter = GetIterator(iterable); | 25 var bucket = HashToBucket(hash, numBuckets); | 
| 27 adder = this.add; | 26 return %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); | 
| 28 if (!IS_SPEC_FUNCTION(adder)) { | 27 } | 
| 29 throw MakeTypeError('property_not_function', ['add', this]); | 28 %SetInlineBuiltinFlag(HashToEntry); | 
| 30 } | 29 | 
| 31 } | 30 | 
| 32 | 31 function SetFindEntry(table, numBuckets, key, hash) { | 
| 33 %_SetInitialize(this); | 32 var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); | 
| 34 | 33 for (var entry = HashToEntry(table, hash, numBuckets); | 
| 35 if (IS_UNDEFINED(iter)) return; | 34 entry !== NOT_FOUND; | 
| 36 | 35 entry = SET_CHAIN_AT(table, entry, numBuckets)) { | 
| 37 var next, done; | 36 var candidate = SET_KEY_AT(table, entry, numBuckets); | 
| 38 while (!(next = iter.next()).done) { | 37 if (key === candidate) { | 
| 39 if (!IS_SPEC_OBJECT(next)) { | 38 return entry; | 
| 40 throw MakeTypeError('iterator_result_not_an_object', [next]); | 39 } | 
| 41 } | 40 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
 
 | |
| 42 %_CallFunction(this, next.value, adder); | 41 return entry; | 
| 43 } | 42 } | 
| 44 } | 43 } | 
| 45 | 44 return NOT_FOUND; | 
| 46 | 45 } | 
| 47 function SetAddJS(key) { | 46 %SetInlineBuiltinFlag(SetFindEntry); | 
| 48 if (!IS_SET(this)) { | 47 | 
| 49 throw MakeTypeError('incompatible_method_receiver', | 48 | 
| 50 ['Set.prototype.add', this]); | 49 function MapFindEntry(table, numBuckets, key, hash) { | 
| 51 } | 50 var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key); | 
| 52 // Normalize -0 to +0 as required by the spec. | 51 for (var entry = HashToEntry(table, hash, numBuckets); | 
| 53 // Even though we use SameValueZero as the comparison for the keys we don't | 52 entry !== NOT_FOUND; | 
| 54 // want to ever store -0 as the key since the key is directly exposed when | 53 entry = MAP_CHAIN_AT(table, entry, numBuckets)) { | 
| 55 // doing iteration. | 54 var candidate = MAP_KEY_AT(table, entry, numBuckets); | 
| 56 if (key === 0) { | 55 if (key === candidate) { | 
| 57 key = 0; | 56 return entry; | 
| 58 } | 57 } | 
| 59 return %_SetAdd(this, key); | 58 if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) { | 
| 60 } | 59 return entry; | 
| 61 | 60 } | 
| 62 | 61 } | 
| 63 function SetHasJS(key) { | 62 return NOT_FOUND; | 
| 64 if (!IS_SET(this)) { | 63 } | 
| 65 throw MakeTypeError('incompatible_method_receiver', | 64 %SetInlineBuiltinFlag(MapFindEntry); | 
| 66 ['Set.prototype.has', this]); | 65 | 
| 67 } | 66 | 
| 68 return %_SetHas(this, key); | 67 function GetHash(key) { | 
| 69 } | 68 if (%_IsSmi(key)) return %_SmiHash(key); | 
| 70 | 69 if (IS_STRING(key)) return %_StringHash(key); | 
| 71 | 70 return %GenericHash(key); | 
| 72 function SetDeleteJS(key) { | 71 } | 
| 73 if (!IS_SET(this)) { | 72 %SetInlineBuiltinFlag(GetHash); | 
| 74 throw MakeTypeError('incompatible_method_receiver', | 73 | 
| 75 ['Set.prototype.delete', this]); | 74 | 
| 76 } | 75 // ------------------------------------------------------------------- | 
| 77 return %_SetDelete(this, key); | 76 // Harmony Set | 
| 78 } | 77 | 
| 79 | 78 function SetConstructor(iterable) { | 
| 80 | 79 if (!%_IsConstructCall()) { | 
| 81 function SetGetSizeJS() { | 80 throw MakeTypeError('constructor_not_function', ['Set']); | 
| 82 if (!IS_SET(this)) { | 81 } | 
| 83 throw MakeTypeError('incompatible_method_receiver', | 82 | 
| 84 ['Set.prototype.size', this]); | 83 var iter, adder; | 
| 85 } | 84 | 
| 86 return %_SetGetSize(this); | 85 if (!IS_NULL_OR_UNDEFINED(iterable)) { | 
| 87 } | 86 iter = GetIterator(iterable); | 
| 88 | 87 adder = this.add; | 
| 89 | 88 if (!IS_SPEC_FUNCTION(adder)) { | 
| 90 function SetClearJS() { | 89 throw MakeTypeError('property_not_function', ['add', this]); | 
| 91 if (!IS_SET(this)) { | 90 } | 
| 92 throw MakeTypeError('incompatible_method_receiver', | 91 } | 
| 93 ['Set.prototype.clear', this]); | 92 | 
| 94 } | 93 %_SetInitialize(this); | 
| 95 %_SetClear(this); | 94 | 
| 96 } | 95 if (IS_UNDEFINED(iter)) return; | 
| 97 | 96 | 
| 98 | 97 var next, done; | 
| 99 function SetForEach(f, receiver) { | 98 while (!(next = iter.next()).done) { | 
| 100 if (!IS_SET(this)) { | 99 if (!IS_SPEC_OBJECT(next)) { | 
| 101 throw MakeTypeError('incompatible_method_receiver', | 100 throw MakeTypeError('iterator_result_not_an_object', [next]); | 
| 102 ['Set.prototype.forEach', this]); | 101 } | 
| 103 } | 102 %_CallFunction(this, next.value, adder); | 
| 104 | 103 } | 
| 105 if (!IS_SPEC_FUNCTION(f)) { | 104 } | 
| 106 throw MakeTypeError('called_non_callable', [f]); | 105 | 
| 107 } | 106 | 
| 108 var needs_wrapper = false; | 107 function SetAdd(key) { | 
| 109 if (IS_NULL_OR_UNDEFINED(receiver)) { | 108 if (!IS_SET(this)) { | 
| 110 receiver = %GetDefaultReceiver(f) || receiver; | 109 throw MakeTypeError('incompatible_method_receiver', | 
| 111 } else { | 110 ['Set.prototype.add', this]); | 
| 112 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); | 111 } | 
| 113 } | 112 // Normalize -0 to +0 as required by the spec. | 
| 114 | 113 // Even though we use SameValueZero as the comparison for the keys we don't | 
| 115 var iterator = new SetIterator(this, ITERATOR_KIND_VALUES); | 114 // want to ever store -0 as the key since the key is directly exposed when | 
| 116 var key; | 115 // doing iteration. | 
| 117 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); | 116 if (IS_NUMBER(key) && key === 0) { | 
| 118 var value_array = [UNDEFINED]; | 117 key = 0; | 
| 119 while (%SetIteratorNext(iterator, value_array)) { | 118 } | 
| 120 if (stepping) %DebugPrepareStepInIfStepping(f); | 119 var table = %_JSCollectionGetTable(this); | 
| 121 key = value_array[0]; | 120 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | 
| 122 var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; | 121 var hash = GetHash(key); | 
| 123 %_CallFunction(new_receiver, key, key, this, f); | 122 if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this; | 
| 124 } | 123 | 
| 125 } | 124 var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); | 
| 126 | 125 var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX); | 
| 127 | 126 var capacity = numBuckets << 1; | 
| 128 // ------------------------------------------------------------------- | 127 if ((nof + nod) >= capacity) { | 
| 129 | 128 // Need to grow, bail out to runtime. | 
| 130 function SetUpSet() { | 129 %SetGrow(this); | 
| 131 %CheckIsBootstrapping(); | 130 // Re-enter...TODO(adamk) Fix this | 
| 132 | 131 return %_CallFunction(this, key, SetAdd); | 
| 133 %SetCode($Set, SetConstructor); | 132 } | 
| 134 %FunctionSetPrototype($Set, new $Object()); | 133 var entry = nof + nod; | 
| 135 %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); | 134 var index = SET_ENTRY_TO_INDEX(entry, numBuckets); | 
| 136 %AddNamedProperty( | 135 var bucket = HashToBucket(hash, numBuckets); | 
| 137 $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY); | 136 var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); | 
| 138 | 137 %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry); | 
| 139 %FunctionSetLength(SetForEach, 1); | 138 %_FixedArraySet(table, index + SET_CHAIN_OFFSET, chainEntry); | 
| 140 | 139 %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof + 1); | 
| 141 // Set up the non-enumerable functions on the Set prototype object. | 140 %_FixedArraySet(table, index, key); | 
| 142 InstallGetter($Set.prototype, "size", SetGetSizeJS); | 141 return this; | 
| 143 InstallFunctions($Set.prototype, DONT_ENUM, $Array( | 142 } | 
| 144 "add", SetAddJS, | 143 | 
| 145 "has", SetHasJS, | 144 | 
| 146 "delete", SetDeleteJS, | 145 function SetHas(key) { | 
| 147 "clear", SetClearJS, | 146 if (!IS_SET(this)) { | 
| 148 "forEach", SetForEach | 147 throw MakeTypeError('incompatible_method_receiver', | 
| 149 )); | 148 ['Set.prototype.has', this]); | 
| 150 } | 149 } | 
| 151 | 150 var table = %_JSCollectionGetTable(this); | 
| 152 SetUpSet(); | 151 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | 
| 153 | 152 var hash = GetHash(key); | 
| 154 | 153 return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; | 
| 155 // ------------------------------------------------------------------- | 154 } | 
| 156 // Harmony Map | 155 | 
| 157 | 156 | 
| 158 function MapConstructor(iterable) { | 157 function SetDelete(key) { | 
| 159 if (!%_IsConstructCall()) { | 158 if (!IS_SET(this)) { | 
| 160 throw MakeTypeError('constructor_not_function', ['Map']); | 159 throw MakeTypeError('incompatible_method_receiver', | 
| 161 } | 160 ['Set.prototype.delete', this]); | 
| 162 | 161 } | 
| 163 var iter, adder; | 162 var table = %_JSCollectionGetTable(this); | 
| 164 | 163 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | 
| 165 if (!IS_NULL_OR_UNDEFINED(iterable)) { | 164 var hash = GetHash(key); | 
| 166 iter = GetIterator(iterable); | 165 var entry = SetFindEntry(table, numBuckets, key, hash); | 
| 167 adder = this.set; | 166 if (entry === NOT_FOUND) return false; | 
| 168 if (!IS_SPEC_FUNCTION(adder)) { | 167 | 
| 169 throw MakeTypeError('property_not_function', ['set', this]); | 168 var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1; | 
| 170 } | 169 var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1; | 
| 171 } | 170 var index = SET_ENTRY_TO_INDEX(entry, numBuckets); | 
| 172 | 171 %_FixedArraySetTheHole(table, index); | 
| 173 %_MapInitialize(this); | 172 %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof); | 
| 174 | 173 %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod); | 
| 175 if (IS_UNDEFINED(iter)) return; | 174 if (nof < (numBuckets >> 1)) %SetShrink(this); | 
| 176 | 175 return true; | 
| 177 var next, done, nextItem; | 176 } | 
| 178 while (!(next = iter.next()).done) { | 177 | 
| 179 if (!IS_SPEC_OBJECT(next)) { | 178 | 
| 180 throw MakeTypeError('iterator_result_not_an_object', [next]); | 179 function SetGetSize() { | 
| 181 } | 180 if (!IS_SET(this)) { | 
| 182 nextItem = next.value; | 181 throw MakeTypeError('incompatible_method_receiver', | 
| 183 if (!IS_SPEC_OBJECT(nextItem)) { | 182 ['Set.prototype.size', this]); | 
| 184 throw MakeTypeError('iterator_value_not_an_object', [nextItem]); | 183 } | 
| 185 } | 184 var table = %_JSCollectionGetTable(this); | 
| 186 %_CallFunction(this, nextItem[0], nextItem[1], adder); | 185 return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); | 
| 187 } | 186 } | 
| 188 } | 187 | 
| 189 | 188 | 
| 190 | 189 function SetClearJS() { | 
| 191 function MapGetJS(key) { | 190 if (!IS_SET(this)) { | 
| 192 if (!IS_MAP(this)) { | 191 throw MakeTypeError('incompatible_method_receiver', | 
| 193 throw MakeTypeError('incompatible_method_receiver', | 192 ['Set.prototype.clear', this]); | 
| 194 ['Map.prototype.get', this]); | 193 } | 
| 195 } | 194 %_SetClear(this); | 
| 196 return %_MapGet(this, key); | 195 } | 
| 197 } | 196 | 
| 198 | 197 | 
| 199 | 198 function SetForEach(f, receiver) { | 
| 200 function MapSetJS(key, value) { | 199 if (!IS_SET(this)) { | 
| 201 if (!IS_MAP(this)) { | 200 throw MakeTypeError('incompatible_method_receiver', | 
| 202 throw MakeTypeError('incompatible_method_receiver', | 201 ['Set.prototype.forEach', this]); | 
| 203 ['Map.prototype.set', this]); | 202 } | 
| 204 } | 203 | 
| 205 // Normalize -0 to +0 as required by the spec. | 204 if (!IS_SPEC_FUNCTION(f)) { | 
| 206 // Even though we use SameValueZero as the comparison for the keys we don't | 205 throw MakeTypeError('called_non_callable', [f]); | 
| 207 // want to ever store -0 as the key since the key is directly exposed when | 206 } | 
| 208 // doing iteration. | 207 var needs_wrapper = false; | 
| 209 if (key === 0) { | 208 if (IS_NULL_OR_UNDEFINED(receiver)) { | 
| 210 key = 0; | 209 receiver = %GetDefaultReceiver(f) || receiver; | 
| 211 } | 210 } else { | 
| 212 return %_MapSet(this, key, value); | 211 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); | 
| 213 } | 212 } | 
| 214 | 213 | 
| 215 | 214 var iterator = new SetIterator(this, ITERATOR_KIND_VALUES); | 
| 216 function MapHasJS(key) { | 215 var key; | 
| 217 if (!IS_MAP(this)) { | 216 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); | 
| 218 throw MakeTypeError('incompatible_method_receiver', | 217 var value_array = [UNDEFINED]; | 
| 219 ['Map.prototype.has', this]); | 218 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
 
 | |
| 220 } | 219 if (stepping) %DebugPrepareStepInIfStepping(f); | 
| 221 return %_MapHas(this, key); | 220 key = value_array[0]; | 
| 222 } | 221 var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; | 
| 223 | 222 %_CallFunction(new_receiver, key, key, this, f); | 
| 224 | 223 } | 
| 225 function MapDeleteJS(key) { | 224 } | 
| 226 if (!IS_MAP(this)) { | 225 | 
| 227 throw MakeTypeError('incompatible_method_receiver', | 226 | 
| 228 ['Map.prototype.delete', this]); | 227 // ------------------------------------------------------------------- | 
| 229 } | 228 | 
| 230 return %_MapDelete(this, key); | 229 function SetUpSet() { | 
| 231 } | 230 %CheckIsBootstrapping(); | 
| 232 | 231 | 
| 233 | 232 %SetCode($Set, SetConstructor); | 
| 234 function MapGetSizeJS() { | 233 %FunctionSetPrototype($Set, new $Object()); | 
| 235 if (!IS_MAP(this)) { | 234 %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); | 
| 236 throw MakeTypeError('incompatible_method_receiver', | 235 %AddNamedProperty( | 
| 237 ['Map.prototype.size', this]); | 236 $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY); | 
| 238 } | 237 | 
| 239 return %_MapGetSize(this); | 238 %FunctionSetLength(SetForEach, 1); | 
| 240 } | 239 | 
| 241 | 240 // Set up the non-enumerable functions on the Set prototype object. | 
| 242 | 241 InstallGetter($Set.prototype, "size", SetGetSize); | 
| 243 function MapClearJS() { | 242 InstallFunctions($Set.prototype, DONT_ENUM, $Array( | 
| 244 if (!IS_MAP(this)) { | 243 "add", SetAdd, | 
| 245 throw MakeTypeError('incompatible_method_receiver', | 244 "has", SetHas, | 
| 246 ['Map.prototype.clear', this]); | 245 "delete", SetDelete, | 
| 247 } | 246 "clear", SetClearJS, | 
| 248 %_MapClear(this); | 247 "forEach", SetForEach | 
| 249 } | 248 )); | 
| 250 | 249 } | 
| 251 | 250 | 
| 252 function MapForEach(f, receiver) { | 251 SetUpSet(); | 
| 253 if (!IS_MAP(this)) { | 252 | 
| 254 throw MakeTypeError('incompatible_method_receiver', | 253 | 
| 255 ['Map.prototype.forEach', this]); | 254 // ------------------------------------------------------------------- | 
| 256 } | 255 // Harmony Map | 
| 257 | 256 | 
| 258 if (!IS_SPEC_FUNCTION(f)) { | 257 function MapConstructor(iterable) { | 
| 259 throw MakeTypeError('called_non_callable', [f]); | 258 if (!%_IsConstructCall()) { | 
| 260 } | 259 throw MakeTypeError('constructor_not_function', ['Map']); | 
| 261 var needs_wrapper = false; | 260 } | 
| 262 if (IS_NULL_OR_UNDEFINED(receiver)) { | 261 | 
| 263 receiver = %GetDefaultReceiver(f) || receiver; | 262 var iter, adder; | 
| 264 } else { | 263 | 
| 265 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); | 264 if (!IS_NULL_OR_UNDEFINED(iterable)) { | 
| 266 } | 265 iter = GetIterator(iterable); | 
| 267 | 266 adder = this.set; | 
| 268 var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES); | 267 if (!IS_SPEC_FUNCTION(adder)) { | 
| 269 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); | 268 throw MakeTypeError('property_not_function', ['set', this]); | 
| 270 var value_array = [UNDEFINED, UNDEFINED]; | 269 } | 
| 271 while (%MapIteratorNext(iterator, value_array)) { | 270 } | 
| 272 if (stepping) %DebugPrepareStepInIfStepping(f); | 271 | 
| 273 var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; | 272 %_MapInitialize(this); | 
| 274 %_CallFunction(new_receiver, value_array[1], value_array[0], this, f); | 273 | 
| 275 } | 274 if (IS_UNDEFINED(iter)) return; | 
| 276 } | 275 | 
| 277 | 276 var next, done, nextItem; | 
| 278 | 277 while (!(next = iter.next()).done) { | 
| 279 // ------------------------------------------------------------------- | 278 if (!IS_SPEC_OBJECT(next)) { | 
| 280 | 279 throw MakeTypeError('iterator_result_not_an_object', [next]); | 
| 281 function SetUpMap() { | 280 } | 
| 282 %CheckIsBootstrapping(); | 281 nextItem = next.value; | 
| 283 | 282 if (!IS_SPEC_OBJECT(nextItem)) { | 
| 284 %SetCode($Map, MapConstructor); | 283 throw MakeTypeError('iterator_value_not_an_object', [nextItem]); | 
| 285 %FunctionSetPrototype($Map, new $Object()); | 284 } | 
| 286 %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); | 285 %_CallFunction(this, nextItem[0], nextItem[1], adder); | 
| 287 %AddNamedProperty( | 286 } | 
| 288 $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY); | 287 } | 
| 289 | 288 | 
| 290 %FunctionSetLength(MapForEach, 1); | 289 | 
| 291 | 290 function MapGet(key) { | 
| 292 // Set up the non-enumerable functions on the Map prototype object. | 291 if (!IS_MAP(this)) { | 
| 293 InstallGetter($Map.prototype, "size", MapGetSizeJS); | 292 throw MakeTypeError('incompatible_method_receiver', | 
| 294 InstallFunctions($Map.prototype, DONT_ENUM, $Array( | 293 ['Map.prototype.get', this]); | 
| 295 "get", MapGetJS, | 294 } | 
| 296 "set", MapSetJS, | 295 var table = %_JSCollectionGetTable(this); | 
| 297 "has", MapHasJS, | 296 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | 
| 298 "delete", MapDeleteJS, | 297 var hash = GetHash(key); | 
| 299 "clear", MapClearJS, | 298 var entry = MapFindEntry(table, numBuckets, key, hash); | 
| 300 "forEach", MapForEach | 299 if (entry === NOT_FOUND) return UNDEFINED; | 
| 301 )); | 300 return MAP_VALUE_AT(table, entry, numBuckets); | 
| 302 } | 301 } | 
| 303 | 302 | 
| 304 SetUpMap(); | 303 | 
| 304 function MapSet(key, value) { | |
| 305 if (!IS_MAP(this)) { | |
| 306 throw MakeTypeError('incompatible_method_receiver', | |
| 307 ['Map.prototype.set', this]); | |
| 308 } | |
| 309 // Normalize -0 to +0 as required by the spec. | |
| 310 // Even though we use SameValueZero as the comparison for the keys we don't | |
| 311 // want to ever store -0 as the key since the key is directly exposed when | |
| 312 // doing iteration. | |
| 313 if (IS_NUMBER(key) && key === 0) { | |
| 314 key = 0; | |
| 315 } | |
| 316 | |
| 317 var table = %_JSCollectionGetTable(this); | |
| 318 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | |
| 319 var hash = GetHash(key); | |
| 320 var entry = MapFindEntry(table, numBuckets, key, hash); | |
| 321 if (entry !== NOT_FOUND) { | |
| 322 var existingIndex = MAP_ENTRY_TO_INDEX(entry, numBuckets); | |
| 323 %_FixedArraySet(table, existingIndex + 1, value); | |
| 324 return this; | |
| 325 } | |
| 326 | |
| 327 var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); | |
| 328 var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX); | |
| 329 var capacity = numBuckets << 1; | |
| 330 if ((nof + nod) >= capacity) { | |
| 331 // Need to grow, bail out to runtime. | |
| 332 %MapGrow(this); | |
| 333 // Re-enter...TODO(adamk) Fix this | |
| 334 return %_CallFunction(this, key, value, MapSet); | |
| 335 } | |
| 336 entry = nof + nod; | |
| 337 var index = MAP_ENTRY_TO_INDEX(entry, numBuckets); | |
| 338 var bucket = HashToBucket(hash, numBuckets); | |
| 339 var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket); | |
| 340 %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry); | |
| 341 %_FixedArraySet(table, index + MAP_CHAIN_OFFSET, chainEntry); | |
| 342 %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof + 1); | |
| 343 %_FixedArraySet(table, index, key); | |
| 344 %_FixedArraySet(table, index + 1, value); | |
| 345 return this; | |
| 346 } | |
| 347 | |
| 348 | |
| 349 function MapHas(key) { | |
| 350 if (!IS_MAP(this)) { | |
| 351 throw MakeTypeError('incompatible_method_receiver', | |
| 352 ['Map.prototype.has', this]); | |
| 353 } | |
| 354 var table = %_JSCollectionGetTable(this); | |
| 355 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | |
| 356 var hash = GetHash(key); | |
| 357 return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND; | |
| 358 } | |
| 359 | |
| 360 | |
| 361 function MapDelete(key) { | |
| 362 if (!IS_MAP(this)) { | |
| 363 throw MakeTypeError('incompatible_method_receiver', | |
| 364 ['Map.prototype.delete', this]); | |
| 365 } | |
| 366 var table = %_JSCollectionGetTable(this); | |
| 367 var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX); | |
| 368 var hash = GetHash(key); | |
| 369 var entry = MapFindEntry(table, numBuckets, key, hash); | |
| 370 if (entry === NOT_FOUND) return false; | |
| 371 | |
| 372 var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1; | |
| 373 var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1; | |
| 374 var index = MAP_ENTRY_TO_INDEX(entry, numBuckets); | |
| 375 %_FixedArraySetTheHole(table, index); | |
| 376 %_FixedArraySetTheHole(table, index + 1); | |
| 377 %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof); | |
| 378 %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod); | |
| 379 if (nof < (numBuckets >> 1)) %MapShrink(this); | |
| 380 return true; | |
| 381 } | |
| 382 | |
| 383 | |
| 384 function MapGetSize() { | |
| 385 if (!IS_MAP(this)) { | |
| 386 throw MakeTypeError('incompatible_method_receiver', | |
| 387 ['Map.prototype.size', this]); | |
| 388 } | |
| 389 var table = %_JSCollectionGetTable(this); | |
| 390 return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX); | |
| 391 } | |
| 392 | |
| 393 | |
| 394 function MapClearJS() { | |
| 395 if (!IS_MAP(this)) { | |
| 396 throw MakeTypeError('incompatible_method_receiver', | |
| 397 ['Map.prototype.clear', this]); | |
| 398 } | |
| 399 %_MapClear(this); | |
| 400 } | |
| 401 | |
| 402 | |
| 403 function MapForEach(f, receiver) { | |
| 404 if (!IS_MAP(this)) { | |
| 405 throw MakeTypeError('incompatible_method_receiver', | |
| 406 ['Map.prototype.forEach', this]); | |
| 407 } | |
| 408 | |
| 409 if (!IS_SPEC_FUNCTION(f)) { | |
| 410 throw MakeTypeError('called_non_callable', [f]); | |
| 411 } | |
| 412 var needs_wrapper = false; | |
| 413 if (IS_NULL_OR_UNDEFINED(receiver)) { | |
| 414 receiver = %GetDefaultReceiver(f) || receiver; | |
| 415 } else { | |
| 416 needs_wrapper = SHOULD_CREATE_WRAPPER(f, receiver); | |
| 417 } | |
| 418 | |
| 419 var iterator = new MapIterator(this, ITERATOR_KIND_ENTRIES); | |
| 420 var stepping = DEBUG_IS_ACTIVE && %DebugCallbackSupportsStepping(f); | |
| 421 var value_array = [UNDEFINED, UNDEFINED]; | |
| 422 while (%MapIteratorNext(iterator, value_array)) { | |
| 423 if (stepping) %DebugPrepareStepInIfStepping(f); | |
| 424 var new_receiver = needs_wrapper ? ToObject(receiver) : receiver; | |
| 425 %_CallFunction(new_receiver, value_array[1], value_array[0], this, f); | |
| 426 } | |
| 427 } | |
| 428 | |
| 429 | |
| 430 // ------------------------------------------------------------------- | |
| 431 | |
| 432 function SetUpMap() { | |
| 433 %CheckIsBootstrapping(); | |
| 434 | |
| 435 %SetCode($Map, MapConstructor); | |
| 436 %FunctionSetPrototype($Map, new $Object()); | |
| 437 %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); | |
| 438 %AddNamedProperty( | |
| 439 $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY); | |
| 440 | |
| 441 %FunctionSetLength(MapForEach, 1); | |
| 442 | |
| 443 // Set up the non-enumerable functions on the Map prototype object. | |
| 444 InstallGetter($Map.prototype, "size", MapGetSize); | |
| 445 InstallFunctions($Map.prototype, DONT_ENUM, $Array( | |
| 446 "get", MapGet, | |
| 447 "set", MapSet, | |
| 448 "has", MapHas, | |
| 449 "delete", MapDelete, | |
| 450 "clear", MapClearJS, | |
| 451 "forEach", MapForEach | |
| 452 )); | |
| 453 } | |
| 454 | |
| 455 SetUpMap(); | |
| 456 | |
| 457 })(); | |
| OLD | NEW |