Map.js (6712B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 // ES2017 draft rev 0e10c9f29fca1385980c08a7d5e7bb3eb775e2e4 6 // 23.1.1.1 Map, steps 6-8 7 function MapConstructorInit(iterable) { 8 var map = this; 9 10 // Step 6.a. 11 var adder = map.set; 12 13 // Step 6.b. 14 if (!IsCallable(adder)) { 15 ThrowTypeError(JSMSG_NOT_FUNCTION, typeof adder); 16 } 17 18 // Steps 6.c-8. 19 for (var nextItem of allowContentIter(iterable)) { 20 // Step 8.d. 21 if (!IsObject(nextItem)) { 22 ThrowTypeError(JSMSG_INVALID_MAP_ITERABLE, "Map"); 23 } 24 25 // Steps 8.e-j. 26 callContentFunction(adder, map, nextItem[0], nextItem[1]); 27 } 28 } 29 30 // ES2018 draft rev f83aa38282c2a60c6916ebc410bfdf105a0f6a54 31 // 23.1.3.5 Map.prototype.forEach ( callbackfn [ , thisArg ] ) 32 function MapForEach(callbackfn, thisArg = undefined) { 33 // Step 1. 34 var M = this; 35 36 // Steps 2-3. 37 if (!IsObject(M) || (M = GuardToMapObject(M)) === null) { 38 return callFunction( 39 CallMapMethodIfWrapped, 40 this, 41 callbackfn, 42 thisArg, 43 "MapForEach" 44 ); 45 } 46 47 // Step 4. 48 if (!IsCallable(callbackfn)) { 49 ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn)); 50 } 51 52 // Steps 5-8. 53 var entries = callFunction(std_Map_entries, M); 54 55 // Inlined: MapIteratorNext 56 var mapIterationResultPair = globalMapIterationResultPair; 57 58 while (true) { 59 var done = GetNextMapEntryForIterator(entries, mapIterationResultPair); 60 if (done) { 61 break; 62 } 63 64 var key = mapIterationResultPair[0]; 65 var value = mapIterationResultPair[1]; 66 mapIterationResultPair[0] = null; 67 mapIterationResultPair[1] = null; 68 69 callContentFunction(callbackfn, thisArg, value, key, M); 70 } 71 } 72 73 var globalMapIterationResultPair = CreateMapIterationResultPair(); 74 75 function MapIteratorNext() { 76 // Step 1. 77 var O = this; 78 79 // Steps 2-3. 80 if (!IsObject(O) || (O = GuardToMapIterator(O)) === null) { 81 return callFunction( 82 CallMapIteratorMethodIfWrapped, 83 this, 84 "MapIteratorNext" 85 ); 86 } 87 88 // Steps 4-5 (implemented in GetNextMapEntryForIterator). 89 // Steps 8-9 (omitted). 90 91 var mapIterationResultPair = globalMapIterationResultPair; 92 93 var retVal = { value: undefined, done: true }; 94 95 // Step 10.a, 11. 96 var done = GetNextMapEntryForIterator(O, mapIterationResultPair); 97 if (!done) { 98 // Steps 10.b-c (omitted). 99 100 // Step 6. 101 var itemKind = UnsafeGetInt32FromReservedSlot(O, MAP_SET_ITERATOR_SLOT_ITEM_KIND); 102 103 var result; 104 if (itemKind === ITEM_KIND_KEY) { 105 // Step 10.d.i. 106 result = mapIterationResultPair[0]; 107 } else if (itemKind === ITEM_KIND_VALUE) { 108 // Step 10.d.ii. 109 result = mapIterationResultPair[1]; 110 } else { 111 // Step 10.d.iii. 112 assert(itemKind === ITEM_KIND_KEY_AND_VALUE, itemKind); 113 result = [mapIterationResultPair[0], mapIterationResultPair[1]]; 114 } 115 116 mapIterationResultPair[0] = null; 117 mapIterationResultPair[1] = null; 118 retVal.value = result; 119 retVal.done = false; 120 } 121 122 // Steps 7, 12. 123 return retVal; 124 } 125 126 // ES6 final draft 23.1.2.2. 127 // Uncloned functions with `$` prefix are allocated as extended function 128 // to store the original name in `SetCanonicalName`. 129 function $MapSpecies() { 130 // Step 1. 131 return this; 132 } 133 SetCanonicalName($MapSpecies, "get [Symbol.species]"); 134 135 // Array Grouping proposal 136 // 137 // Map.groupBy ( items, callbackfn ) 138 // 139 // https://tc39.es/proposal-array-grouping/#sec-map.groupby 140 function MapGroupBy(items, callbackfn) { 141 // Step 1. (Call to GroupBy is inlined.) 142 143 // GroupBy, step 1. 144 if (IsNullOrUndefined(items)) { 145 ThrowTypeError( 146 JSMSG_UNEXPECTED_TYPE, 147 DecompileArg(0, items), 148 items === null ? "null" : "undefined" 149 ); 150 } 151 152 // GroupBy, step 2. 153 if (!IsCallable(callbackfn)) { 154 ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); 155 } 156 157 // Step 2. 158 var C = GetBuiltinConstructor("Map"); 159 var map = new C(); 160 161 // GroupBy, step 3. (Not applicable in our implementation.) 162 163 // GroupBy, step 4. 164 var k = 0; 165 166 // GroupBy, steps 4 and 6. 167 for (var value of allowContentIter(items)) { 168 // GroupBy, step 6.a. (Not applicable) 169 assert(k < 2 ** 53 - 1, "out-of-memory happens before k exceeds 2^53 - 1"); 170 171 // GroupBy, steps 6.b-d. (Implicit through for-of loop.) 172 173 // GroupBy, step 6.e. 174 var key = callContentFunction(callbackfn, undefined, value, k); 175 176 // GroupBy, step 6.f. (Implicit through for-of loop.) 177 178 // GroupBy, step 6.g. (Not applicable) 179 180 // GroupBy, step 6.h. (Implicit through std_Map_get.) 181 182 // GroupBy, step 6.i. (Inlined call to AddValueToKeyedGroup.) 183 var elements = callFunction(std_Map_get, map, key); 184 if (elements === undefined) { 185 callFunction(std_Map_set, map, key, [value]); 186 } else { 187 DefineDataProperty(elements, elements.length, value); 188 } 189 190 // GroupBy, step 6.j. 191 k += 1; 192 } 193 194 // Step 3. (Result map already populated in the previous loop.) 195 196 // Step 4. 197 return map; 198 } 199 200 /** 201 * Upsert proposal 202 * 203 * Map.prototype.getOrInsertComputed ( key, callbackfn ) 204 * 205 * https://tc39.es/proposal-upsert/ 206 */ 207 function MapGetOrInsertComputed(key, callbackfn) { 208 // Step 1. Let M be the this value. 209 var M = this; 210 211 // Step 2. Perform ? RequireInternalSlot(M, [[MapData]]). 212 if (!IsObject(M) || (M = GuardToMapObject(M)) === null) { 213 return callFunction( 214 CallMapMethodIfWrapped, 215 this, 216 key, 217 callbackfn, 218 "MapGetOrInsertComputed" 219 ); 220 } 221 222 // Step 3. If IsCallable(callbackfn) is false, throw a TypeError exception. 223 if (!IsCallable(callbackfn)) { 224 ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(1, callbackfn)); 225 } 226 227 // Step 4. Set key to CanonicalizeKeyedCollectionKey(key). 228 // key === 0 is true for both -0 and +0 229 if (key === 0) { 230 key = 0; 231 } 232 // Step 5. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do 233 // Step 5.a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, return p.[[Value]]. 234 if (callFunction(std_Map_has, M, key)) { 235 return callFunction(std_Map_get, M, key); 236 } 237 238 // Step 6. Let value be ? Call(callbackfn, undefined, « key »). 239 var value = callContentFunction(callbackfn, undefined, key); 240 241 // Step 7. For each Record { [[Key]], [[Value]] } p of M.[[MapData]], do 242 // Step 7.a. If p.[[Key]] is not empty and SameValue(p.[[Key]], key) is true, then 243 // Step 7.a.i. Set p.[[Value]] to value. 244 // Step 8. Let p be the Record { [[Key]]: key, [[Value]]: value }. 245 // Step 9. Append p to M.[[MapData]]. 246 callFunction(std_Map_set, M, key, value); 247 248 // Step 7.a.ii, 10. Return value. 249 return value; 250 }