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 |