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