1// Copyright (C) 2018 Crimson AS <info@crimson.no>
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qv4mapobject_p.h"
5#include "qv4mapiterator_p.h"
6#include "qv4estable_p.h"
7#include "qv4symbol_p.h"
8
9using namespace QV4;
10
11DEFINE_OBJECT_VTABLE(WeakMapCtor);
12DEFINE_OBJECT_VTABLE(MapCtor);
13DEFINE_OBJECT_VTABLE(MapObject);
14
15void Heap::WeakMapCtor::init(QV4::ExecutionContext *scope)
16{
17 Heap::FunctionObject::init(scope, QStringLiteral("WeakMap"));
18}
19
20void Heap::MapCtor::init(QV4::ExecutionContext *scope)
21{
22 Heap::FunctionObject::init(scope, QStringLiteral("Map"));
23}
24
25ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool weakMap)
26{
27 Scope scope(f);
28 Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
29 bool protoSet = false;
30 if (newTarget)
31 protoSet = a->setProtoFromNewTarget(newTarget);
32 if (!protoSet && weakMap) {
33 a->setPrototypeOf(scope.engine->weakMapPrototype());
34 scope.engine->memoryManager->registerWeakMap(map: a->d());
35 }
36 a->d()->isWeakMap = weakMap;
37
38 if (argc > 0) {
39 ScopedValue iterable(scope, argv[0]);
40
41 if (!iterable->isNullOrUndefined()) {
42 ScopedFunctionObject adder(scope, a->get(name: ScopedString(scope, scope.engine->newString(s: QString::fromLatin1(ba: "set")))));
43 if (!adder)
44 return scope.engine->throwTypeError();
45
46 ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
47 if (scope.hasException())
48 return Encode::undefined();
49 Q_ASSERT(iter);
50
51 ScopedValue obj(scope);
52 Value *arguments = scope.alloc(nValues: 2);
53 ScopedValue done(scope);
54 forever {
55 done = Runtime::IteratorNext::call(scope.engine, iter, obj);
56 if (scope.hasException())
57 break;
58 if (done->toBoolean())
59 return a->asReturnedValue();
60 const Object *o = obj->objectValue();
61 if (!o) {
62 scope.engine->throwTypeError();
63 break;
64 }
65
66 arguments[0] = o->get(id: PropertyKey::fromArrayIndex(idx: 0));
67 if (scope.hasException())
68 break;
69 arguments[1] = o->get(id: PropertyKey::fromArrayIndex(idx: 1));
70 if (scope.hasException())
71 break;
72
73 adder->call(thisObject: a, argv: arguments, argc: 2);
74 if (scope.hasException())
75 break;
76 }
77 ScopedValue falsey(scope, Encode(false));
78 return Runtime::IteratorClose::call(scope.engine, iter, falsey);
79 }
80 }
81 return a->asReturnedValue();
82
83}
84
85ReturnedValue WeakMapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
86{
87 return construct(f, argv, argc, newTarget, weakMap: true);
88}
89
90ReturnedValue WeakMapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
91{
92 Scope scope(f);
93 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "(Weak)Map requires new"));
94}
95
96
97ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
98{
99 return construct(f, argv, argc, newTarget, weakMap: false);
100}
101
102void WeakMapPrototype::init(ExecutionEngine *engine, Object *ctor)
103{
104 Scope scope(engine);
105 ScopedObject o(scope);
106 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
107 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
108 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
109
110 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
111 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
112 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
113 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
114
115 ScopedString val(scope, engine->newString(s: QLatin1String("WeakMap")));
116 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
117}
118
119
120void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
121{
122 Scope scope(engine);
123 ScopedObject o(scope);
124 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
125 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
126 ctor->addSymbolSpecies();
127 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
128
129 defineDefaultProperty(QStringLiteral("clear"), code: method_clear, argumentCount: 0);
130 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
131 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
132 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
133 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
134 defineDefaultProperty(QStringLiteral("keys"), code: method_keys, argumentCount: 0);
135 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
136 defineAccessorProperty(QStringLiteral("size"), getter: method_get_size, setter: nullptr);
137 defineDefaultProperty(QStringLiteral("values"), code: method_values, argumentCount: 0);
138
139 // Per the spec, the value for entries/@@iterator is the same
140 ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
141 ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valString, code: MapPrototype::method_entries, argumentCount: 0));
142 defineDefaultProperty(QStringLiteral("entries"), value: entriesFn);
143 defineDefaultProperty(name: engine->symbol_iterator(), value: entriesFn);
144
145 ScopedString val(scope, engine->newString(s: QLatin1String("Map")));
146 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
147}
148
149void Heap::MapObject::init()
150{
151 Object::init();
152 esTable = new ESTable();
153}
154
155void Heap::MapObject::destroy()
156{
157 delete esTable;
158 esTable = nullptr;
159}
160
161void Heap::MapObject::removeUnmarkedKeys()
162{
163 esTable->removeUnmarkedKeys();
164}
165
166void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
167{
168 MapObject *m = static_cast<MapObject *>(that);
169 m->esTable->markObjects(s: markStack, isWeakMap: m->isWeakMap);
170 Object::markObjects(base: that, stack: markStack);
171}
172
173ReturnedValue WeakMapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
174{
175 Scope scope(b);
176 Scoped<MapObject> that(scope, thisObject);
177 if (!that || !that->d()->isWeakMap)
178 return scope.engine->throwTypeError();
179 if (!argc || !argv[0].isObject())
180 return Encode(false);
181
182 return Encode(that->d()->esTable->remove(k: argv[0]));
183
184}
185
186ReturnedValue WeakMapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
187{
188 Scope scope(b);
189 Scoped<MapObject> that(scope, thisObject);
190 if (!that || !that->d()->isWeakMap)
191 return scope.engine->throwTypeError();
192 if (!argc || !argv[0].isObject())
193 return Encode::undefined();
194
195 return that->d()->esTable->get(k: argv[0]);
196}
197
198ReturnedValue WeakMapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
199{
200 Scope scope(b);
201 Scoped<MapObject> that(scope, thisObject);
202 if (!that || !that->d()->isWeakMap)
203 return scope.engine->throwTypeError();
204 if (!argc || !argv[0].isObject())
205 return Encode(false);
206
207 return Encode(that->d()->esTable->has(k: argv[0]));
208}
209
210ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
211{
212 Scope scope(b);
213 Scoped<MapObject> that(scope, thisObject);
214 if ((!that || !that->d()->isWeakMap) ||
215 (!argc || !argv[0].isObject()))
216 return scope.engine->throwTypeError();
217
218 that->d()->esTable->set(k: argv[0], v: argc > 1 ? argv[1] : Value::undefinedValue());
219 return that.asReturnedValue();
220}
221
222
223ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
224{
225 Scope scope(b);
226 Scoped<MapObject> that(scope, thisObject);
227 if (!that || that->d()->isWeakMap)
228 return scope.engine->throwTypeError();
229
230 that->d()->esTable->clear();
231 return Encode::undefined();
232}
233
234ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
235{
236 Scope scope(b);
237 Scoped<MapObject> that(scope, thisObject);
238 if (!that || that->d()->isWeakMap)
239 return scope.engine->throwTypeError();
240
241 return Encode(that->d()->esTable->remove(k: argc ? argv[0] : Value::undefinedValue()));
242}
243
244ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
245{
246 Scope scope(b);
247 Scoped<MapObject> that(scope, thisObject);
248 if (!that || that->d()->isWeakMap)
249 return scope.engine->throwTypeError();
250
251 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
252 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
253 return ao->asReturnedValue();
254}
255
256ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
257{
258 Scope scope(b);
259 Scoped<MapObject> that(scope, thisObject);
260 if (!that || that->d()->isWeakMap)
261 return scope.engine->throwTypeError();
262
263 ScopedFunctionObject callbackfn(scope, argv[0]);
264 if (!callbackfn)
265 return scope.engine->throwTypeError();
266
267 ScopedValue thisArg(scope, Value::undefinedValue());
268 if (argc > 1)
269 thisArg = ScopedValue(scope, argv[1]);
270
271 Value *arguments = scope.alloc(nValues: 3);
272 arguments[2] = that;
273 for (uint i = 0; i < that->d()->esTable->size(); ++i) {
274 that->d()->esTable->iterate(idx: i, k: &arguments[1], v: &arguments[0]); // fill in key (0), value (1)
275
276 callbackfn->call(thisObject: thisArg, argv: arguments, argc: 3);
277 CHECK_EXCEPTION();
278 }
279 return Encode::undefined();
280}
281
282ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
283{
284 Scope scope(b);
285 Scoped<MapObject> that(scope, thisObject);
286 if (!that || that->d()->isWeakMap)
287 return scope.engine->throwTypeError();
288
289 return that->d()->esTable->get(k: argc ? argv[0] : Value::undefinedValue());
290}
291
292ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
293{
294 Scope scope(b);
295 Scoped<MapObject> that(scope, thisObject);
296 if (!that || that->d()->isWeakMap)
297 return scope.engine->throwTypeError();
298
299 return Encode(that->d()->esTable->has(k: argc ? argv[0] : Value::undefinedValue()));
300}
301
302ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
303{
304 Scope scope(b);
305 Scoped<MapObject> that(scope, thisObject);
306 if (!that || that->d()->isWeakMap)
307 return scope.engine->throwTypeError();
308
309 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
310 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
311 return ao->asReturnedValue();
312}
313
314ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
315{
316 Scope scope(b);
317 Scoped<MapObject> that(scope, thisObject);
318 if (!that || that->d()->isWeakMap)
319 return scope.engine->throwTypeError();
320
321 that->d()->esTable->set(k: argc ? argv[0] : Value::undefinedValue(), v: argc > 1 ? argv[1] : Value::undefinedValue());
322 return that.asReturnedValue();
323}
324
325ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
326{
327 Scope scope(b);
328 Scoped<MapObject> that(scope, thisObject);
329 if (!that || that->d()->isWeakMap)
330 return scope.engine->throwTypeError();
331
332 return Encode(that->d()->esTable->size());
333}
334
335ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
336{
337 Scope scope(b);
338 Scoped<MapObject> that(scope, thisObject);
339 if (!that || that->d()->isWeakMap)
340 return scope.engine->throwTypeError();
341
342 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
343 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
344 return ao->asReturnedValue();
345}
346

source code of qtdeclarative/src/qml/jsruntime/qv4mapobject.cpp