Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: src/collection.js

Issue 947683002: Reimplement Maps and Sets in JS (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Rename all the things, add more macros, and remove unnecessary %_CallFunctions Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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 // Used by harmony-templates.js
16 var $MapGet;
17 var $MapSet;
18
19
20 (function() {
21 "use strict";
arv (Not doing code reviews) 2015/03/30 18:55:22 This extra use strict is not need (it also does no
adamk 2015/03/30 22:31:50 Isn't this in fact the only one that _does_ have a
22
23
24 function HashToEntry(table, hash, numBuckets) {
25 var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
26 return ORDERED_HASH_TABLE_BUCKET_AT(table, 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 = ORDERED_HASH_SET_CHAIN_AT(table, entry, numBuckets)) {
36 var candidate = ORDERED_HASH_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 = ORDERED_HASH_MAP_CHAIN_AT(table, entry, numBuckets)) {
54 var candidate = ORDERED_HASH_MAP_KEY_AT(table, entry, numBuckets);
55 if (key === candidate) {
56 return entry;
57 }
58 if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) {
arv (Not doing code reviews) 2015/03/30 18:55:22 IS_NUMBER(x) && NUMBER_IS_NAN(x) is repeated in a
adamk 2015/03/30 22:31:50 Happy to do that in a followup, these aren't the o
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 & 1 /* Name::kHashNotComputedMask */) === 0) {
88 return (field >>> 2 /* Name::kHashShift */);
arv (Not doing code reviews) 2015/03/30 18:55:22 skip parens here
adamk 2015/03/30 22:31:50 Done.
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) {
arv (Not doing code reviews) 2015/03/25 11:17:01 Is this a bug in crankshaft?
Toon Verwaest 2015/03/30 12:55:42 Why is this necessary?
adamk 2015/03/30 18:44:53 It's not necessary for correctness, but it does he
arv (Not doing code reviews) 2015/03/30 18:55:22 This needs a comment and preferably a bug number.
adamk 2015/03/30 22:31:50 Removed this in the latest version, the perf diffe
48 key = 0; 129 key = 0;
49 } 130 }
50 return %_SetAdd(this, key); 131 var table = %_JSCollectionGetTable(this);
132 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
133 var hash = GetHash(key);
134 if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
135
136 var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
137 var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
138 var capacity = numBuckets << 1;
139 if ((nof + nod) >= capacity) {
140 // Need to grow, bail out to runtime.
141 %SetGrow(this);
142 // Re-load state from the grown backing store.
143 table = %_JSCollectionGetTable(this);
144 numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
145 nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
146 nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
147 }
148 var entry = nof + nod;
149 var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
150 var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
151 var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
152 ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
153 ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
154 FIXED_ARRAY_SET(table, index, key);
155 FIXED_ARRAY_SET_SMI(table, index + 1, chainEntry);
156 return this;
51 } 157 }
52 158
53 159
54 function SetHasJS(key) { 160 function SetHas(key) {
55 if (!IS_SET(this)) { 161 if (!IS_SET(this)) {
56 throw MakeTypeError('incompatible_method_receiver', 162 throw MakeTypeError('incompatible_method_receiver',
57 ['Set.prototype.has', this]); 163 ['Set.prototype.has', this]);
58 } 164 }
59 return %_SetHas(this, key); 165 var table = %_JSCollectionGetTable(this);
166 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
167 var hash = GetHash(key);
168 return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
60 } 169 }
61 170
62 171
63 function SetDeleteJS(key) { 172 function SetDelete(key) {
64 if (!IS_SET(this)) { 173 if (!IS_SET(this)) {
65 throw MakeTypeError('incompatible_method_receiver', 174 throw MakeTypeError('incompatible_method_receiver',
66 ['Set.prototype.delete', this]); 175 ['Set.prototype.delete', this]);
67 } 176 }
68 return %_SetDelete(this, key); 177 var table = %_JSCollectionGetTable(this);
178 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
179 var hash = GetHash(key);
180 var entry = SetFindEntry(table, numBuckets, key, hash);
181 if (entry === NOT_FOUND) return false;
182
183 var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
184 var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
185 var index = ORDERED_HASH_SET_ENTRY_TO_INDEX(entry, numBuckets);
186 FIXED_ARRAY_SET(table, index, %_TheHole());
187 ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
188 ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
189 if (nof < (numBuckets >>> 1)) %SetShrink(this);
190 return true;
69 } 191 }
70 192
71 193
72 function SetGetSizeJS() { 194 function SetGetSize() {
73 if (!IS_SET(this)) { 195 if (!IS_SET(this)) {
74 throw MakeTypeError('incompatible_method_receiver', 196 throw MakeTypeError('incompatible_method_receiver',
75 ['Set.prototype.size', this]); 197 ['Set.prototype.size', this]);
76 } 198 }
77 return %_SetGetSize(this); 199 var table = %_JSCollectionGetTable(this);
200 return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
78 } 201 }
79 202
80 203
81 function SetClearJS() { 204 function SetClearJS() {
82 if (!IS_SET(this)) { 205 if (!IS_SET(this)) {
83 throw MakeTypeError('incompatible_method_receiver', 206 throw MakeTypeError('incompatible_method_receiver',
84 ['Set.prototype.clear', this]); 207 ['Set.prototype.clear', this]);
85 } 208 }
86 %_SetClear(this); 209 %_SetClear(this);
87 } 210 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
123 246
124 %SetCode($Set, SetConstructor); 247 %SetCode($Set, SetConstructor);
125 %FunctionSetPrototype($Set, new $Object()); 248 %FunctionSetPrototype($Set, new $Object());
126 %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM); 249 %AddNamedProperty($Set.prototype, "constructor", $Set, DONT_ENUM);
127 %AddNamedProperty( 250 %AddNamedProperty(
128 $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY); 251 $Set.prototype, symbolToStringTag, "Set", DONT_ENUM | READ_ONLY);
129 252
130 %FunctionSetLength(SetForEach, 1); 253 %FunctionSetLength(SetForEach, 1);
131 254
132 // Set up the non-enumerable functions on the Set prototype object. 255 // Set up the non-enumerable functions on the Set prototype object.
133 InstallGetter($Set.prototype, "size", SetGetSizeJS); 256 InstallGetter($Set.prototype, "size", SetGetSize);
134 InstallFunctions($Set.prototype, DONT_ENUM, $Array( 257 InstallFunctions($Set.prototype, DONT_ENUM, $Array(
135 "add", SetAddJS, 258 "add", SetAdd,
136 "has", SetHasJS, 259 "has", SetHas,
137 "delete", SetDeleteJS, 260 "delete", SetDelete,
138 "clear", SetClearJS, 261 "clear", SetClearJS,
139 "forEach", SetForEach 262 "forEach", SetForEach
140 )); 263 ));
141 } 264 }
142 265
143 SetUpSet(); 266 SetUpSet();
144 267
145 268
146 // ------------------------------------------------------------------- 269 // -------------------------------------------------------------------
147 // Harmony Map 270 // Harmony Map
(...skipping 14 matching lines...) Expand all
162 for (var nextItem of iterable) { 285 for (var nextItem of iterable) {
163 if (!IS_SPEC_OBJECT(nextItem)) { 286 if (!IS_SPEC_OBJECT(nextItem)) {
164 throw MakeTypeError('iterator_value_not_an_object', [nextItem]); 287 throw MakeTypeError('iterator_value_not_an_object', [nextItem]);
165 } 288 }
166 %_CallFunction(this, nextItem[0], nextItem[1], adder); 289 %_CallFunction(this, nextItem[0], nextItem[1], adder);
167 } 290 }
168 } 291 }
169 } 292 }
170 293
171 294
172 function MapGetJS(key) { 295 function MapGet(key) {
173 if (!IS_MAP(this)) { 296 if (!IS_MAP(this)) {
174 throw MakeTypeError('incompatible_method_receiver', 297 throw MakeTypeError('incompatible_method_receiver',
175 ['Map.prototype.get', this]); 298 ['Map.prototype.get', this]);
176 } 299 }
177 return %_MapGet(this, key); 300 var table = %_JSCollectionGetTable(this);
301 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
302 var hash = GetHash(key);
303 var entry = MapFindEntry(table, numBuckets, key, hash);
304 if (entry === NOT_FOUND) return UNDEFINED;
305 return ORDERED_HASH_MAP_VALUE_AT(table, entry, numBuckets);
178 } 306 }
179 307
180 308
181 function MapSetJS(key, value) { 309 function MapSet(key, value) {
182 if (!IS_MAP(this)) { 310 if (!IS_MAP(this)) {
183 throw MakeTypeError('incompatible_method_receiver', 311 throw MakeTypeError('incompatible_method_receiver',
184 ['Map.prototype.set', this]); 312 ['Map.prototype.set', this]);
185 } 313 }
186 // Normalize -0 to +0 as required by the spec. 314 // Normalize -0 to +0 as required by the spec.
187 // Even though we use SameValueZero as the comparison for the keys we don't 315 // 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 316 // want to ever store -0 as the key since the key is directly exposed when
189 // doing iteration. 317 // doing iteration.
190 if (key === 0) { 318 if (IS_NUMBER(key) && key === 0) {
191 key = 0; 319 key = 0;
192 } 320 }
193 return %_MapSet(this, key, value); 321
322 var table = %_JSCollectionGetTable(this);
323 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
324 var hash = GetHash(key);
325 var entry = MapFindEntry(table, numBuckets, key, hash);
326 if (entry !== NOT_FOUND) {
327 var existingIndex = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
328 FIXED_ARRAY_SET(table, existingIndex + 1, value);
329 return this;
330 }
331
332 var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
333 var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
334 var capacity = numBuckets << 1;
335 if ((nof + nod) >= capacity) {
336 // Need to grow, bail out to runtime.
337 %MapGrow(this);
338 // Re-load state from the grown backing store.
339 table = %_JSCollectionGetTable(this);
340 numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
341 nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
342 nod = ORDERED_HASH_TABLE_DELETED_COUNT(table);
343 }
344 entry = nof + nod;
345 var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
346 var bucket = ORDERED_HASH_TABLE_HASH_TO_BUCKET(hash, numBuckets);
347 var chainEntry = ORDERED_HASH_TABLE_BUCKET_AT(table, bucket);
348 ORDERED_HASH_TABLE_SET_BUCKET_AT(table, bucket, entry);
349 ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof + 1);
350 FIXED_ARRAY_SET(table, index, key);
351 FIXED_ARRAY_SET(table, index + 1, value);
352 FIXED_ARRAY_SET(table, index + 2, chainEntry);
353 return this;
194 } 354 }
195 355
196 356
197 function MapHasJS(key) { 357 function MapHas(key) {
198 if (!IS_MAP(this)) { 358 if (!IS_MAP(this)) {
199 throw MakeTypeError('incompatible_method_receiver', 359 throw MakeTypeError('incompatible_method_receiver',
200 ['Map.prototype.has', this]); 360 ['Map.prototype.has', this]);
201 } 361 }
202 return %_MapHas(this, key); 362 var table = %_JSCollectionGetTable(this);
363 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
364 var hash = GetHash(key);
365 return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
203 } 366 }
204 367
205 368
206 function MapDeleteJS(key) { 369 function MapDelete(key) {
207 if (!IS_MAP(this)) { 370 if (!IS_MAP(this)) {
208 throw MakeTypeError('incompatible_method_receiver', 371 throw MakeTypeError('incompatible_method_receiver',
209 ['Map.prototype.delete', this]); 372 ['Map.prototype.delete', this]);
210 } 373 }
211 return %_MapDelete(this, key); 374 var table = %_JSCollectionGetTable(this);
375 var numBuckets = ORDERED_HASH_TABLE_BUCKET_COUNT(table);
376 var hash = GetHash(key);
377 var entry = MapFindEntry(table, numBuckets, key, hash);
378 if (entry === NOT_FOUND) return false;
379
380 var nof = ORDERED_HASH_TABLE_ELEMENT_COUNT(table) - 1;
381 var nod = ORDERED_HASH_TABLE_DELETED_COUNT(table) + 1;
382 var index = ORDERED_HASH_MAP_ENTRY_TO_INDEX(entry, numBuckets);
383 FIXED_ARRAY_SET(table, index, %_TheHole());
384 FIXED_ARRAY_SET(table, index + 1, %_TheHole());
385 ORDERED_HASH_TABLE_SET_ELEMENT_COUNT(table, nof);
386 ORDERED_HASH_TABLE_SET_DELETED_COUNT(table, nod);
387 if (nof < (numBuckets >>> 1)) %MapShrink(this);
388 return true;
212 } 389 }
213 390
214 391
215 function MapGetSizeJS() { 392 function MapGetSize() {
216 if (!IS_MAP(this)) { 393 if (!IS_MAP(this)) {
217 throw MakeTypeError('incompatible_method_receiver', 394 throw MakeTypeError('incompatible_method_receiver',
218 ['Map.prototype.size', this]); 395 ['Map.prototype.size', this]);
219 } 396 }
220 return %_MapGetSize(this); 397 var table = %_JSCollectionGetTable(this);
398 return ORDERED_HASH_TABLE_ELEMENT_COUNT(table);
221 } 399 }
222 400
223 401
224 function MapClearJS() { 402 function MapClearJS() {
225 if (!IS_MAP(this)) { 403 if (!IS_MAP(this)) {
226 throw MakeTypeError('incompatible_method_receiver', 404 throw MakeTypeError('incompatible_method_receiver',
227 ['Map.prototype.clear', this]); 405 ['Map.prototype.clear', this]);
228 } 406 }
229 %_MapClear(this); 407 %_MapClear(this);
230 } 408 }
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
264 442
265 %SetCode($Map, MapConstructor); 443 %SetCode($Map, MapConstructor);
266 %FunctionSetPrototype($Map, new $Object()); 444 %FunctionSetPrototype($Map, new $Object());
267 %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM); 445 %AddNamedProperty($Map.prototype, "constructor", $Map, DONT_ENUM);
268 %AddNamedProperty( 446 %AddNamedProperty(
269 $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY); 447 $Map.prototype, symbolToStringTag, "Map", DONT_ENUM | READ_ONLY);
270 448
271 %FunctionSetLength(MapForEach, 1); 449 %FunctionSetLength(MapForEach, 1);
272 450
273 // Set up the non-enumerable functions on the Map prototype object. 451 // Set up the non-enumerable functions on the Map prototype object.
274 InstallGetter($Map.prototype, "size", MapGetSizeJS); 452 InstallGetter($Map.prototype, "size", MapGetSize);
275 InstallFunctions($Map.prototype, DONT_ENUM, $Array( 453 InstallFunctions($Map.prototype, DONT_ENUM, $Array(
276 "get", MapGetJS, 454 "get", MapGet,
277 "set", MapSetJS, 455 "set", MapSet,
278 "has", MapHasJS, 456 "has", MapHas,
279 "delete", MapDeleteJS, 457 "delete", MapDelete,
280 "clear", MapClearJS, 458 "clear", MapClearJS,
281 "forEach", MapForEach 459 "forEach", MapForEach
282 )); 460 ));
461
462 $MapGet = MapGet;
adamk 2015/03/25 16:13:36 This is where I copy MapGet into the global scope.
463 $MapSet = MapSet;
283 } 464 }
284 465
285 SetUpMap(); 466 SetUpMap();
467
468 })();
OLDNEW
« no previous file with comments | « src/arm/code-stubs-arm.cc ('k') | src/harmony-templates.js » ('j') | src/harmony-templates.js » ('J')

Powered by Google App Engine
This is Rietveld 408576698