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 // 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 Loading... | |
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 Loading... | |
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 Loading... | |
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 })(); | |
OLD | NEW |