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::ExecutionEngine *engine)
16{
17 Heap::FunctionObject::init(engine, QStringLiteral("WeakMap"));
18}
19
20void Heap::MapCtor::init(QV4::ExecutionEngine *engine)
21{
22 Heap::FunctionObject::init(engine, 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 return Runtime::IteratorClose::call(scope.engine, iter);
78 }
79 }
80 return a->asReturnedValue();
81
82}
83
84ReturnedValue WeakMapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
85{
86 return construct(f, argv, argc, newTarget, weakMap: true);
87}
88
89ReturnedValue WeakMapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
90{
91 Scope scope(f);
92 return scope.engine->throwTypeError(message: QString::fromLatin1(ba: "(Weak)Map requires new"));
93}
94
95
96ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
97{
98 return construct(f, argv, argc, newTarget, weakMap: false);
99}
100
101void WeakMapPrototype::init(ExecutionEngine *engine, Object *ctor)
102{
103 Scope scope(engine);
104 ScopedObject o(scope);
105 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
106 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
107 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
108
109 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
110 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
111 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
112 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
113
114 ScopedString val(scope, engine->newString(s: QLatin1String("WeakMap")));
115 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
116}
117
118
119void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
120{
121 Scope scope(engine);
122 ScopedObject o(scope);
123 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
124 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
125 ctor->addSymbolSpecies();
126 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
127
128 defineDefaultProperty(QStringLiteral("clear"), code: method_clear, argumentCount: 0);
129 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
130 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
131 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
132 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
133 defineDefaultProperty(QStringLiteral("keys"), code: method_keys, argumentCount: 0);
134 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
135 defineAccessorProperty(QStringLiteral("size"), getter: method_get_size, setter: nullptr);
136 defineDefaultProperty(QStringLiteral("values"), code: method_values, argumentCount: 0);
137
138 // Per the spec, the value for entries/@@iterator is the same
139 ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
140 ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valString, code: MapPrototype::method_entries, argumentCount: 0));
141 defineDefaultProperty(QStringLiteral("entries"), value: entriesFn);
142 defineDefaultProperty(name: engine->symbol_iterator(), value: entriesFn);
143
144 ScopedString val(scope, engine->newString(s: QLatin1String("Map")));
145 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
146}
147
148void Heap::MapObject::init()
149{
150 Object::init();
151 esTable = new ESTable();
152}
153
154void Heap::MapObject::destroy()
155{
156 delete esTable;
157 esTable = nullptr;
158}
159
160void Heap::MapObject::removeUnmarkedKeys()
161{
162 esTable->removeUnmarkedKeys();
163}
164
165void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
166{
167 MapObject *m = static_cast<MapObject *>(that);
168 m->esTable->markObjects(s: markStack, isWeakMap: m->isWeakMap);
169 Object::markObjects(base: that, stack: markStack);
170}
171
172ReturnedValue WeakMapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
173{
174 Scope scope(b);
175 Scoped<MapObject> that(scope, thisObject);
176 if (!that || !that->d()->isWeakMap)
177 return scope.engine->throwTypeError();
178 if (!argc || !argv[0].isObject())
179 return Encode(false);
180
181 return Encode(that->d()->esTable->remove(k: argv[0]));
182
183}
184
185ReturnedValue WeakMapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
186{
187 Scope scope(b);
188 Scoped<MapObject> that(scope, thisObject);
189 if (!that || !that->d()->isWeakMap)
190 return scope.engine->throwTypeError();
191 if (!argc || !argv[0].isObject())
192 return Encode::undefined();
193
194 return that->d()->esTable->get(k: argv[0]);
195}
196
197ReturnedValue WeakMapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
198{
199 Scope scope(b);
200 Scoped<MapObject> that(scope, thisObject);
201 if (!that || !that->d()->isWeakMap)
202 return scope.engine->throwTypeError();
203 if (!argc || !argv[0].isObject())
204 return Encode(false);
205
206 return Encode(that->d()->esTable->has(k: argv[0]));
207}
208
209ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
210{
211 Scope scope(b);
212 Scoped<MapObject> that(scope, thisObject);
213 if ((!that || !that->d()->isWeakMap) ||
214 (!argc || !argv[0].isObject()))
215 return scope.engine->throwTypeError();
216
217 QV4::WriteBarrier::markCustom(engine: scope.engine, markFunction: [&](QV4::MarkStack *ms) {
218 if (scope.engine->memoryManager->gcStateMachine->state <= GCState::FreeWeakMaps)
219 return;
220 Q_ASSERT(argv[0].heapObject());
221 argv[0].heapObject()->mark(markStack: ms);
222 if (argc > 1) {
223 if (auto *h = argv[1].heapObject())
224 h->mark(markStack: ms);
225 }
226 });
227
228 that->d()->esTable->set(k: argv[0], v: argc > 1 ? argv[1] : Value::undefinedValue());
229 return that.asReturnedValue();
230}
231
232
233ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
234{
235 Scope scope(b);
236 Scoped<MapObject> that(scope, thisObject);
237 if (!that || that->d()->isWeakMap)
238 return scope.engine->throwTypeError();
239
240 that->d()->esTable->clear();
241 return Encode::undefined();
242}
243
244ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
245{
246 Scope scope(b);
247 Scoped<MapObject> that(scope, thisObject);
248 if (!that || that->d()->isWeakMap)
249 return scope.engine->throwTypeError();
250
251 return Encode(that->d()->esTable->remove(k: argc ? argv[0] : Value::undefinedValue()));
252}
253
254ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
255{
256 Scope scope(b);
257 Scoped<MapObject> that(scope, thisObject);
258 if (!that || that->d()->isWeakMap)
259 return scope.engine->throwTypeError();
260
261 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
262 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
263 return ao->asReturnedValue();
264}
265
266ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
267{
268 Scope scope(b);
269 Scoped<MapObject> that(scope, thisObject);
270 if (!that || that->d()->isWeakMap)
271 return scope.engine->throwTypeError();
272
273 ScopedFunctionObject callbackfn(scope, argv[0]);
274 if (!callbackfn)
275 return scope.engine->throwTypeError();
276
277 ScopedValue thisArg(scope, Value::undefinedValue());
278 if (argc > 1)
279 thisArg = ScopedValue(scope, argv[1]);
280
281 Value *arguments = scope.alloc(nValues: 3);
282 arguments[2] = that;
283
284 ESTable::ShiftObserver observer{};
285 that->d()->esTable->observeShifts(observer);
286
287 while (observer.pivot < that->d()->esTable->size()) {
288 that->d()->esTable->iterate(idx: observer.pivot, k: &arguments[1], v: &arguments[0]); // fill in key (0), value (1)
289
290 callbackfn->call(thisObject: thisArg, argv: arguments, argc: 3);
291 CHECK_EXCEPTION();
292
293 observer.next();
294 }
295
296 that->d()->esTable->stopObservingShifts(observer);
297
298 return Encode::undefined();
299}
300
301ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
302{
303 Scope scope(b);
304 Scoped<MapObject> that(scope, thisObject);
305 if (!that || that->d()->isWeakMap)
306 return scope.engine->throwTypeError();
307
308 return that->d()->esTable->get(k: argc ? argv[0] : Value::undefinedValue());
309}
310
311ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
312{
313 Scope scope(b);
314 Scoped<MapObject> that(scope, thisObject);
315 if (!that || that->d()->isWeakMap)
316 return scope.engine->throwTypeError();
317
318 return Encode(that->d()->esTable->has(k: argc ? argv[0] : Value::undefinedValue()));
319}
320
321ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
322{
323 Scope scope(b);
324 Scoped<MapObject> that(scope, thisObject);
325 if (!that || that->d()->isWeakMap)
326 return scope.engine->throwTypeError();
327
328 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
329 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
330 return ao->asReturnedValue();
331}
332
333ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
334{
335 Scope scope(b);
336 Scoped<MapObject> that(scope, thisObject);
337 if (!that || that->d()->isWeakMap)
338 return scope.engine->throwTypeError();
339
340 QV4::WriteBarrier::markCustom(engine: scope.engine, markFunction: [&](QV4::MarkStack *ms) {
341 if (auto *h = argv[0].heapObject())
342 h->mark(markStack: ms);
343 if (argc > 1) {
344 if (auto *h = argv[1].heapObject())
345 h->mark(markStack: ms);
346 }
347 });
348
349 that->d()->esTable->set(k: argc ? argv[0] : Value::undefinedValue(), v: argc > 1 ? argv[1] : Value::undefinedValue());
350 return that.asReturnedValue();
351}
352
353ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
354{
355 Scope scope(b);
356 Scoped<MapObject> that(scope, thisObject);
357 if (!that || that->d()->isWeakMap)
358 return scope.engine->throwTypeError();
359
360 return Encode(that->d()->esTable->size());
361}
362
363ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
364{
365 Scope scope(b);
366 Scoped<MapObject> that(scope, thisObject);
367 if (!that || that->d()->isWeakMap)
368 return scope.engine->throwTypeError();
369
370 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
371 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
372 return ao->asReturnedValue();
373}
374

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

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