github.com/nuvolaris/goja@v0.0.0-20230825100449-967811910c6d/builtin_map.go (about) 1 package goja 2 3 import ( 4 "reflect" 5 ) 6 7 var mapExportType = reflect.TypeOf([][2]interface{}{}) 8 9 type mapObject struct { 10 baseObject 11 m *orderedMap 12 } 13 14 type mapIterObject struct { 15 baseObject 16 iter *orderedMapIter 17 kind iterationKind 18 } 19 20 func (o *mapIterObject) next() Value { 21 if o.iter == nil { 22 return o.val.runtime.createIterResultObject(_undefined, true) 23 } 24 25 entry := o.iter.next() 26 if entry == nil { 27 o.iter = nil 28 return o.val.runtime.createIterResultObject(_undefined, true) 29 } 30 31 var result Value 32 switch o.kind { 33 case iterationKindKey: 34 result = entry.key 35 case iterationKindValue: 36 result = entry.value 37 default: 38 result = o.val.runtime.newArrayValues([]Value{entry.key, entry.value}) 39 } 40 41 return o.val.runtime.createIterResultObject(result, false) 42 } 43 44 func (mo *mapObject) init() { 45 mo.baseObject.init() 46 mo.m = newOrderedMap(mo.val.runtime.getHash()) 47 } 48 49 func (mo *mapObject) exportType() reflect.Type { 50 return mapExportType 51 } 52 53 func (mo *mapObject) export(ctx *objectExportCtx) interface{} { 54 m := make([][2]interface{}, mo.m.size) 55 ctx.put(mo.val, m) 56 57 iter := mo.m.newIter() 58 for i := 0; i < len(m); i++ { 59 entry := iter.next() 60 if entry == nil { 61 break 62 } 63 m[i][0] = exportValue(entry.key, ctx) 64 m[i][1] = exportValue(entry.value, ctx) 65 } 66 67 return m 68 } 69 70 func (mo *mapObject) exportToMap(dst reflect.Value, typ reflect.Type, ctx *objectExportCtx) error { 71 dst.Set(reflect.MakeMap(typ)) 72 ctx.putTyped(mo.val, typ, dst.Interface()) 73 keyTyp := typ.Key() 74 elemTyp := typ.Elem() 75 iter := mo.m.newIter() 76 r := mo.val.runtime 77 for { 78 entry := iter.next() 79 if entry == nil { 80 break 81 } 82 keyVal := reflect.New(keyTyp).Elem() 83 err := r.toReflectValue(entry.key, keyVal, ctx) 84 if err != nil { 85 return err 86 } 87 elemVal := reflect.New(elemTyp).Elem() 88 err = r.toReflectValue(entry.value, elemVal, ctx) 89 if err != nil { 90 return err 91 } 92 dst.SetMapIndex(keyVal, elemVal) 93 } 94 return nil 95 } 96 97 func (r *Runtime) mapProto_clear(call FunctionCall) Value { 98 thisObj := r.toObject(call.This) 99 mo, ok := thisObj.self.(*mapObject) 100 if !ok { 101 panic(r.NewTypeError("Method Map.prototype.clear called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 102 } 103 104 mo.m.clear() 105 106 return _undefined 107 } 108 109 func (r *Runtime) mapProto_delete(call FunctionCall) Value { 110 thisObj := r.toObject(call.This) 111 mo, ok := thisObj.self.(*mapObject) 112 if !ok { 113 panic(r.NewTypeError("Method Map.prototype.delete called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 114 } 115 116 return r.toBoolean(mo.m.remove(call.Argument(0))) 117 } 118 119 func (r *Runtime) mapProto_get(call FunctionCall) Value { 120 thisObj := r.toObject(call.This) 121 mo, ok := thisObj.self.(*mapObject) 122 if !ok { 123 panic(r.NewTypeError("Method Map.prototype.get called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 124 } 125 126 return nilSafe(mo.m.get(call.Argument(0))) 127 } 128 129 func (r *Runtime) mapProto_has(call FunctionCall) Value { 130 thisObj := r.toObject(call.This) 131 mo, ok := thisObj.self.(*mapObject) 132 if !ok { 133 panic(r.NewTypeError("Method Map.prototype.has called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 134 } 135 if mo.m.has(call.Argument(0)) { 136 return valueTrue 137 } 138 return valueFalse 139 } 140 141 func (r *Runtime) mapProto_set(call FunctionCall) Value { 142 thisObj := r.toObject(call.This) 143 mo, ok := thisObj.self.(*mapObject) 144 if !ok { 145 panic(r.NewTypeError("Method Map.prototype.set called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 146 } 147 mo.m.set(call.Argument(0), call.Argument(1)) 148 return call.This 149 } 150 151 func (r *Runtime) mapProto_entries(call FunctionCall) Value { 152 return r.createMapIterator(call.This, iterationKindKeyValue) 153 } 154 155 func (r *Runtime) mapProto_forEach(call FunctionCall) Value { 156 thisObj := r.toObject(call.This) 157 mo, ok := thisObj.self.(*mapObject) 158 if !ok { 159 panic(r.NewTypeError("Method Map.prototype.forEach called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 160 } 161 callbackFn, ok := r.toObject(call.Argument(0)).self.assertCallable() 162 if !ok { 163 panic(r.NewTypeError("object is not a function %s")) 164 } 165 t := call.Argument(1) 166 iter := mo.m.newIter() 167 for { 168 entry := iter.next() 169 if entry == nil { 170 break 171 } 172 callbackFn(FunctionCall{This: t, Arguments: []Value{entry.value, entry.key, thisObj}}) 173 } 174 175 return _undefined 176 } 177 178 func (r *Runtime) mapProto_keys(call FunctionCall) Value { 179 return r.createMapIterator(call.This, iterationKindKey) 180 } 181 182 func (r *Runtime) mapProto_values(call FunctionCall) Value { 183 return r.createMapIterator(call.This, iterationKindValue) 184 } 185 186 func (r *Runtime) mapProto_getSize(call FunctionCall) Value { 187 thisObj := r.toObject(call.This) 188 mo, ok := thisObj.self.(*mapObject) 189 if !ok { 190 panic(r.NewTypeError("Method get Map.prototype.size called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 191 } 192 return intToValue(int64(mo.m.size)) 193 } 194 195 func (r *Runtime) builtin_newMap(args []Value, newTarget *Object) *Object { 196 if newTarget == nil { 197 panic(r.needNew("Map")) 198 } 199 proto := r.getPrototypeFromCtor(newTarget, r.global.Map, r.global.MapPrototype) 200 o := &Object{runtime: r} 201 202 mo := &mapObject{} 203 mo.class = classObject 204 mo.val = o 205 mo.extensible = true 206 o.self = mo 207 mo.prototype = proto 208 mo.init() 209 if len(args) > 0 { 210 if arg := args[0]; arg != nil && arg != _undefined && arg != _null { 211 adder := mo.getStr("set", nil) 212 adderFn := toMethod(adder) 213 if adderFn == nil { 214 panic(r.NewTypeError("Map.set in missing")) 215 } 216 iter := r.getIterator(arg, nil) 217 i0 := valueInt(0) 218 i1 := valueInt(1) 219 if adder == r.global.mapAdder { 220 iter.iterate(func(item Value) { 221 itemObj := r.toObject(item) 222 k := nilSafe(itemObj.self.getIdx(i0, nil)) 223 v := nilSafe(itemObj.self.getIdx(i1, nil)) 224 mo.m.set(k, v) 225 }) 226 } else { 227 iter.iterate(func(item Value) { 228 itemObj := r.toObject(item) 229 k := itemObj.self.getIdx(i0, nil) 230 v := itemObj.self.getIdx(i1, nil) 231 adderFn(FunctionCall{This: o, Arguments: []Value{k, v}}) 232 }) 233 } 234 } 235 } 236 return o 237 } 238 239 func (r *Runtime) createMapIterator(mapValue Value, kind iterationKind) Value { 240 obj := r.toObject(mapValue) 241 mapObj, ok := obj.self.(*mapObject) 242 if !ok { 243 panic(r.NewTypeError("Object is not a Map")) 244 } 245 246 o := &Object{runtime: r} 247 248 mi := &mapIterObject{ 249 iter: mapObj.m.newIter(), 250 kind: kind, 251 } 252 mi.class = classObject 253 mi.val = o 254 mi.extensible = true 255 o.self = mi 256 mi.prototype = r.getMapIteratorPrototype() 257 mi.init() 258 259 return o 260 } 261 262 func (r *Runtime) mapIterProto_next(call FunctionCall) Value { 263 thisObj := r.toObject(call.This) 264 if iter, ok := thisObj.self.(*mapIterObject); ok { 265 return iter.next() 266 } 267 panic(r.NewTypeError("Method Map Iterator.prototype.next called on incompatible receiver %s", r.objectproto_toString(FunctionCall{This: thisObj}))) 268 } 269 270 func (r *Runtime) createMapProto(val *Object) objectImpl { 271 o := newBaseObjectObj(val, r.global.ObjectPrototype, classObject) 272 273 o._putProp("constructor", r.global.Map, true, false, true) 274 o._putProp("clear", r.newNativeFunc(r.mapProto_clear, nil, "clear", nil, 0), true, false, true) 275 r.global.mapAdder = r.newNativeFunc(r.mapProto_set, nil, "set", nil, 2) 276 o._putProp("set", r.global.mapAdder, true, false, true) 277 o._putProp("delete", r.newNativeFunc(r.mapProto_delete, nil, "delete", nil, 1), true, false, true) 278 o._putProp("forEach", r.newNativeFunc(r.mapProto_forEach, nil, "forEach", nil, 1), true, false, true) 279 o._putProp("has", r.newNativeFunc(r.mapProto_has, nil, "has", nil, 1), true, false, true) 280 o._putProp("get", r.newNativeFunc(r.mapProto_get, nil, "get", nil, 1), true, false, true) 281 o.setOwnStr("size", &valueProperty{ 282 getterFunc: r.newNativeFunc(r.mapProto_getSize, nil, "get size", nil, 0), 283 accessor: true, 284 writable: true, 285 configurable: true, 286 }, true) 287 o._putProp("keys", r.newNativeFunc(r.mapProto_keys, nil, "keys", nil, 0), true, false, true) 288 o._putProp("values", r.newNativeFunc(r.mapProto_values, nil, "values", nil, 0), true, false, true) 289 290 entriesFunc := r.newNativeFunc(r.mapProto_entries, nil, "entries", nil, 0) 291 o._putProp("entries", entriesFunc, true, false, true) 292 o._putSym(SymIterator, valueProp(entriesFunc, true, false, true)) 293 o._putSym(SymToStringTag, valueProp(asciiString(classMap), false, false, true)) 294 295 return o 296 } 297 298 func (r *Runtime) createMap(val *Object) objectImpl { 299 o := r.newNativeConstructOnly(val, r.builtin_newMap, r.global.MapPrototype, "Map", 0) 300 r.putSpeciesReturnThis(o) 301 302 return o 303 } 304 305 func (r *Runtime) createMapIterProto(val *Object) objectImpl { 306 o := newBaseObjectObj(val, r.getIteratorPrototype(), classObject) 307 308 o._putProp("next", r.newNativeFunc(r.mapIterProto_next, nil, "next", nil, 0), true, false, true) 309 o._putSym(SymToStringTag, valueProp(asciiString(classMapIterator), false, false, true)) 310 311 return o 312 } 313 314 func (r *Runtime) getMapIteratorPrototype() *Object { 315 var o *Object 316 if o = r.global.MapIteratorPrototype; o == nil { 317 o = &Object{runtime: r} 318 r.global.MapIteratorPrototype = o 319 o.self = r.createMapIterProto(o) 320 } 321 return o 322 } 323 324 func (r *Runtime) initMap() { 325 r.global.MapIteratorPrototype = r.newLazyObject(r.createMapIterProto) 326 327 r.global.MapPrototype = r.newLazyObject(r.createMapProto) 328 r.global.Map = r.newLazyObject(r.createMap) 329 330 r.addToGlobal("Map", r.global.Map) 331 }