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

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: Moved more to JS 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
« no previous file with comments | « no previous file | src/hydrogen.h » ('j') | src/runtime/runtime-collections.cc » ('J')
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 (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
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
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
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 })();
OLDNEW
« no previous file with comments | « no previous file | src/hydrogen.h » ('j') | src/runtime/runtime-collections.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698