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