1package goja 2 3type mapObject struct { 4 baseObject 5 m *orderedMap 6} 7 8type mapIterObject struct { 9 baseObject 10 iter *orderedMapIter 11 kind iterationKind 12} 13 14func (o *mapIterObject) next() Value { 15 if o.iter == nil { 16 return o.val.runtime.createIterResultObject(_undefined, true) 17 } 18 19 entry := o.iter.next() 20 if entry == nil { 21 o.iter = nil 22 return o.val.runtime.createIterResultObject(_undefined, true) 23 } 24 25 var result Value 26 switch o.kind { 27 case iterationKindKey: 28 result = entry.key 29 case iterationKindValue: 30 result = entry.value 31 default: 32 result = o.val.runtime.newArrayValues([]Value{entry.key, entry.value}) 33 } 34 35 return o.val.runtime.createIterResultObject(result, false) 36} 37 38func (mo *mapObject) init() { 39 mo.baseObject.init() 40 mo.m = newOrderedMap(mo.val.runtime.getHash()) 41} 42 43func (r *Runtime) mapProto_clear(call FunctionCall) Value { 44 thisObj := r.toObject(call.This) 45 mo, ok := thisObj.self.(*mapObject) 46 if !ok { 47 panic(r.NewTypeError("Method Map.prototype.clear called on incompatible receiver %s", thisObj.String())) 48 } 49 50 mo.m.clear() 51 52 return _undefined 53} 54 55func (r *Runtime) mapProto_delete(call FunctionCall) Value { 56 thisObj := r.toObject(call.This) 57 mo, ok := thisObj.self.(*mapObject) 58 if !ok { 59 panic(r.NewTypeError("Method Map.prototype.delete called on incompatible receiver %s", thisObj.String())) 60 } 61 62 return r.toBoolean(mo.m.remove(call.Argument(0))) 63} 64 65func (r *Runtime) mapProto_get(call FunctionCall) Value { 66 thisObj := r.toObject(call.This) 67 mo, ok := thisObj.self.(*mapObject) 68 if !ok { 69 panic(r.NewTypeError("Method Map.prototype.get called on incompatible receiver %s", thisObj.String())) 70 } 71 72 return nilSafe(mo.m.get(call.Argument(0))) 73} 74 75func (r *Runtime) mapProto_has(call FunctionCall) Value { 76 thisObj := r.toObject(call.This) 77 mo, ok := thisObj.self.(*mapObject) 78 if !ok { 79 panic(r.NewTypeError("Method Map.prototype.has called on incompatible receiver %s", thisObj.String())) 80 } 81 if mo.m.has(call.Argument(0)) { 82 return valueTrue 83 } 84 return valueFalse 85} 86 87func (r *Runtime) mapProto_set(call FunctionCall) Value { 88 thisObj := r.toObject(call.This) 89 mo, ok := thisObj.self.(*mapObject) 90 if !ok { 91 panic(r.NewTypeError("Method Map.prototype.set called on incompatible receiver %s", thisObj.String())) 92 } 93 mo.m.set(call.Argument(0), call.Argument(1)) 94 return call.This 95} 96 97func (r *Runtime) mapProto_entries(call FunctionCall) Value { 98 return r.createMapIterator(call.This, iterationKindKeyValue) 99} 100 101func (r *Runtime) mapProto_forEach(call FunctionCall) Value { 102 thisObj := r.toObject(call.This) 103 mo, ok := thisObj.self.(*mapObject) 104 if !ok { 105 panic(r.NewTypeError("Method Map.prototype.forEach called on incompatible receiver %s", thisObj.String())) 106 } 107 callbackFn, ok := r.toObject(call.Argument(0)).self.assertCallable() 108 if !ok { 109 panic(r.NewTypeError("object is not a function %s")) 110 } 111 t := call.Argument(1) 112 iter := mo.m.newIter() 113 for { 114 entry := iter.next() 115 if entry == nil { 116 break 117 } 118 callbackFn(FunctionCall{This: t, Arguments: []Value{entry.value, entry.key, thisObj}}) 119 } 120 121 return _undefined 122} 123 124func (r *Runtime) mapProto_keys(call FunctionCall) Value { 125 return r.createMapIterator(call.This, iterationKindKey) 126} 127 128func (r *Runtime) mapProto_values(call FunctionCall) Value { 129 return r.createMapIterator(call.This, iterationKindValue) 130} 131 132func (r *Runtime) mapProto_getSize(call FunctionCall) Value { 133 thisObj := r.toObject(call.This) 134 mo, ok := thisObj.self.(*mapObject) 135 if !ok { 136 panic(r.NewTypeError("Method get Map.prototype.size called on incompatible receiver %s", thisObj.String())) 137 } 138 return intToValue(int64(mo.m.size)) 139} 140 141func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object { 142 if newTarget == nil { 143 panic(r.needNew("Map")) 144 } 145 proto := r.getPrototypeFromCtor(newTarget, r.global.Map, r.global.MapPrototype) 146 o := &Object{runtime: r} 147 148 mo := &mapObject{} 149 mo.class = classMap 150 mo.val = o 151 mo.extensible = true 152 o.self = mo 153 mo.prototype = proto 154 mo.init() 155 if len(args) > 0 { 156 if arg := args[0]; arg != nil && arg != _undefined && arg != _null { 157 adder := mo.getStr("set", nil) 158 iter := r.getIterator(arg, nil) 159 i0 := valueInt(0) 160 i1 := valueInt(1) 161 if adder == r.global.mapAdder { 162 r.iterate(iter, func(item Value) { 163 itemObj := r.toObject(item) 164 k := nilSafe(itemObj.self.getIdx(i0, nil)) 165 v := nilSafe(itemObj.self.getIdx(i1, nil)) 166 mo.m.set(k, v) 167 }) 168 } else { 169 adderFn := toMethod(adder) 170 if adderFn == nil { 171 panic(r.NewTypeError("Map.set in missing")) 172 } 173 r.iterate(iter, func(item Value) { 174 itemObj := r.toObject(item) 175 k := itemObj.self.getIdx(i0, nil) 176 v := itemObj.self.getIdx(i1, nil) 177 adderFn(FunctionCall{This: o, Arguments: []Value{k, v}}) 178 }) 179 } 180 } 181 } 182 return o 183} 184 185func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value { 186 obj := r.toObject(mapValue) 187 mapObj, ok := obj.self.(*mapObject) 188 if !ok { 189 panic(r.NewTypeError("Object is not a Map")) 190 } 191 192 o := &Object{runtime: r} 193 194 mi := &mapIterObject{ 195 iter: mapObj.m.newIter(), 196 kind: kind, 197 } 198 mi.class = classMapIterator 199 mi.val = o 200 mi.extensible = true 201 o.self = mi 202 mi.prototype = r.global.MapIteratorPrototype 203 mi.init() 204 205 return o 206} 207 208func (r *Runtime) mapIterProto_next(call FunctionCall) Value { 209 thisObj := r.toObject(call.This) 210 if iter, ok := thisObj.self.(*mapIterObject); ok { 211 return iter.next() 212 } 213 panic(r.NewTypeError("Method Map Iterator.prototype.next called on incompatible receiver %s", thisObj.String())) 214} 215 216func (r *Runtime) createMapProto(val *Object) objectImpl { 217 o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) 218 219 o._putProp("constructor", r.global.Map, true, false, true) 220 o._putProp("clear", r.newNativeFunc(r.mapProto_clear, nil, "clear", nil, 0), true, false, true) 221 r.global.mapAdder = r.newNativeFunc(r.mapProto_set, nil, "set", nil, 2) 222 o._putProp("set", r.global.mapAdder, true, false, true) 223 o._putProp("delete", r.newNativeFunc(r.mapProto_delete, nil, "delete", nil, 1), true, false, true) 224 o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, nil, "forEach", nil, 1), true, false, true) 225 o._putProp("has", r.newNativeFunc(r.mapProto_has, nil, "has", nil, 1), true, false, true) 226 o._putProp("get", r.newNativeFunc(r.mapProto_get, nil, "get", nil, 1), true, false, true) 227 o.setOwnStr("size", &valueProperty{ 228 getterFunc: r.newNativeFunc(r.mapProto_getSize, nil, "get size", nil, 0), 229 accessor: true, 230 writable: true, 231 configurable: true, 232 }, true) 233 o._putProp("keys", r.newNativeFunc(r.mapProto_keys, nil, "keys", nil, 0), true, false, true) 234 o._putProp("values", r.newNativeFunc(r.mapProto_values, nil, "values", nil, 0), true, false, true) 235 236 entriesFunc := r.newNativeFunc(r.mapProto_entries, nil, "entries", nil, 0) 237 o._putProp("entries", entriesFunc, true, false, true) 238 o._putSym(SymIterator, valueProp(entriesFunc, true, false, true)) 239 o._putSym(SymToStringTag, valueProp(asciiString(classMap), false, false, true)) 240 241 return o 242} 243 244func (r *Runtime) createMap(val *Object) objectImpl { 245 o := r.newNativeConstructOnly(val, r.builtin_newMap, r.global.MapPrototype, "Map", 0) 246 o._putSym(SymSpecies, &valueProperty{ 247 getterFunc: r.newNativeFunc(r.returnThis, nil, "get [Symbol.species]", nil, 0), 248 accessor: true, 249 configurable: true, 250 }) 251 252 return o 253} 254 255func (r *Runtime) createMapIterProto(val *Object) objectImpl { 256 o := newBaseObjectObj(val, r.global.IteratorPrototype, classObject) 257 258 o._putProp("next", r.newNativeFunc(r.mapIterProto_next, nil, "next", nil, 0), true, false, true) 259 o._putSym(SymToStringTag, valueProp(asciiString(classMapIterator), false, false, true)) 260 261 return o 262} 263 264func (r *Runtime) initMap() { 265 r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto) 266 267 r.global.MapPrototype = r.newLazyObject(r.createMapProto) 268 r.global.Map = r.newLazyObject(r.createMap) 269 270 r.addToGlobal("Map", r.global.Map) 271} 272