1/****************************************************************************
2**
3** Copyright (C) 2018 Crimson AS <info@crimson.no>
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qv4setobject_p.h" // ### temporary
41#include "qv4mapobject_p.h"
42#include "qv4mapiterator_p.h"
43#include "qv4estable_p.h"
44#include "qv4symbol_p.h"
45
46using namespace QV4;
47
48DEFINE_OBJECT_VTABLE(WeakMapCtor);
49DEFINE_OBJECT_VTABLE(MapCtor);
50DEFINE_OBJECT_VTABLE(MapObject);
51
52void Heap::WeakMapCtor::init(QV4::ExecutionContext *scope)
53{
54 Heap::FunctionObject::init(scope, QStringLiteral("WeakMap"));
55}
56
57void Heap::MapCtor::init(QV4::ExecutionContext *scope)
58{
59 Heap::FunctionObject::init(scope, QStringLiteral("Map"));
60}
61
62ReturnedValue WeakMapCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool weakMap)
63{
64 Scope scope(f);
65 Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
66 bool protoSet = false;
67 if (newTarget)
68 protoSet = a->setProtoFromNewTarget(newTarget);
69 if (!protoSet && weakMap) {
70 a->setPrototypeOf(scope.engine->weakMapPrototype());
71 scope.engine->memoryManager->registerWeakMap(map: a->d());
72 }
73 a->d()->isWeakMap = weakMap;
74
75 if (argc > 0) {
76 ScopedValue iterable(scope, argv[0]);
77
78 if (!iterable->isNullOrUndefined()) {
79 ScopedFunctionObject adder(scope, a->get(name: ScopedString(scope, scope.engine->newString(s: QString::fromLatin1(str: "set")))));
80 if (!adder)
81 return scope.engine->throwTypeError();
82
83 ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
84 if (scope.hasException())
85 return Encode::undefined();
86 Q_ASSERT(iter);
87
88 ScopedValue obj(scope);
89 Value *arguments = scope.alloc(nValues: 2);
90 ScopedValue done(scope);
91 forever {
92 done = Runtime::IteratorNext::call(scope.engine, iter, obj);
93 if (scope.hasException())
94 break;
95 if (done->toBoolean())
96 return a->asReturnedValue();
97 const Object *o = obj->objectValue();
98 if (!o) {
99 scope.engine->throwTypeError();
100 break;
101 }
102
103 arguments[0] = o->get(id: PropertyKey::fromArrayIndex(idx: 0));
104 if (scope.hasException())
105 break;
106 arguments[1] = o->get(id: PropertyKey::fromArrayIndex(idx: 1));
107 if (scope.hasException())
108 break;
109
110 adder->call(thisObject: a, argv: arguments, argc: 2);
111 if (scope.hasException())
112 break;
113 }
114 ScopedValue falsey(scope, Encode(false));
115 return Runtime::IteratorClose::call(scope.engine, iter, falsey);
116 }
117 }
118 return a->asReturnedValue();
119
120}
121
122ReturnedValue WeakMapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
123{
124 return construct(f, argv, argc, newTarget, weakMap: true);
125}
126
127ReturnedValue WeakMapCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
128{
129 Scope scope(f);
130 return scope.engine->throwTypeError(message: QString::fromLatin1(str: "(Weak)Map requires new"));
131}
132
133
134ReturnedValue MapCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
135{
136 return construct(f, argv, argc, newTarget, weakMap: false);
137}
138
139void WeakMapPrototype::init(ExecutionEngine *engine, Object *ctor)
140{
141 Scope scope(engine);
142 ScopedObject o(scope);
143 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
144 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
145 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
146
147 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
148 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
149 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
150 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
151
152 ScopedString val(scope, engine->newString(s: QLatin1String("WeakMap")));
153 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
154}
155
156
157void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
158{
159 Scope scope(engine);
160 ScopedObject o(scope);
161 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
162 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
163 ctor->addSymbolSpecies();
164 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
165
166 defineDefaultProperty(QStringLiteral("clear"), code: method_clear, argumentCount: 0);
167 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
168 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
169 defineDefaultProperty(QStringLiteral("get"), code: method_get, argumentCount: 1);
170 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
171 defineDefaultProperty(QStringLiteral("keys"), code: method_keys, argumentCount: 0);
172 defineDefaultProperty(QStringLiteral("set"), code: method_set, argumentCount: 2);
173 defineAccessorProperty(QStringLiteral("size"), getter: method_get_size, setter: nullptr);
174 defineDefaultProperty(QStringLiteral("values"), code: method_values, argumentCount: 0);
175
176 // Per the spec, the value for entries/@@iterator is the same
177 ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
178 ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valString, code: MapPrototype::method_entries, argumentCount: 0));
179 defineDefaultProperty(QStringLiteral("entries"), value: entriesFn);
180 defineDefaultProperty(name: engine->symbol_iterator(), value: entriesFn);
181
182 ScopedString val(scope, engine->newString(s: QLatin1String("Map")));
183 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
184}
185
186void Heap::MapObject::init()
187{
188 Object::init();
189 esTable = new ESTable();
190}
191
192void Heap::MapObject::destroy()
193{
194 delete esTable;
195 esTable = nullptr;
196}
197
198void Heap::MapObject::removeUnmarkedKeys()
199{
200 esTable->removeUnmarkedKeys();
201}
202
203void Heap::MapObject::markObjects(Heap::Base *that, MarkStack *markStack)
204{
205 MapObject *m = static_cast<MapObject *>(that);
206 m->esTable->markObjects(s: markStack, isWeakMap: m->isWeakMap);
207 Object::markObjects(base: that, stack: markStack);
208}
209
210ReturnedValue WeakMapPrototype::method_delete(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 return scope.engine->throwTypeError();
216 if (!argc || !argv[0].isObject())
217 return Encode(false);
218
219 return Encode(that->d()->esTable->remove(k: argv[0]));
220
221}
222
223ReturnedValue WeakMapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
224{
225 Scope scope(b);
226 Scoped<MapObject> that(scope, thisObject);
227 if (!that || !that->d()->isWeakMap)
228 return scope.engine->throwTypeError();
229 if (!argc || !argv[0].isObject())
230 return Encode::undefined();
231
232 return that->d()->esTable->get(k: argv[0]);
233}
234
235ReturnedValue WeakMapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
236{
237 Scope scope(b);
238 Scoped<MapObject> that(scope, thisObject);
239 if (!that || !that->d()->isWeakMap)
240 return scope.engine->throwTypeError();
241 if (!argc || !argv[0].isObject())
242 return Encode(false);
243
244 return Encode(that->d()->esTable->has(k: argv[0]));
245}
246
247ReturnedValue WeakMapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
248{
249 Scope scope(b);
250 Scoped<MapObject> that(scope, thisObject);
251 if ((!that || !that->d()->isWeakMap) ||
252 (!argc || !argv[0].isObject()))
253 return scope.engine->throwTypeError();
254
255 that->d()->esTable->set(k: argv[0], v: argc > 1 ? argv[1] : Value::undefinedValue());
256 return that.asReturnedValue();
257}
258
259
260ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
261{
262 Scope scope(b);
263 Scoped<MapObject> that(scope, thisObject);
264 if (!that || that->d()->isWeakMap)
265 return scope.engine->throwTypeError();
266
267 that->d()->esTable->clear();
268 return Encode::undefined();
269}
270
271ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
272{
273 Scope scope(b);
274 Scoped<MapObject> that(scope, thisObject);
275 if (!that || that->d()->isWeakMap)
276 return scope.engine->throwTypeError();
277
278 return Encode(that->d()->esTable->remove(k: argc ? argv[0] : Value::undefinedValue()));
279}
280
281ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
282{
283 Scope scope(b);
284 Scoped<MapObject> that(scope, thisObject);
285 if (!that || that->d()->isWeakMap)
286 return scope.engine->throwTypeError();
287
288 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
289 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
290 return ao->asReturnedValue();
291}
292
293ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
294{
295 Scope scope(b);
296 Scoped<MapObject> that(scope, thisObject);
297 if (!that || that->d()->isWeakMap)
298 return scope.engine->throwTypeError();
299
300 ScopedFunctionObject callbackfn(scope, argv[0]);
301 if (!callbackfn)
302 return scope.engine->throwTypeError();
303
304 ScopedValue thisArg(scope, Value::undefinedValue());
305 if (argc > 1)
306 thisArg = ScopedValue(scope, argv[1]);
307
308 Value *arguments = scope.alloc(nValues: 3);
309 arguments[2] = that;
310 for (uint i = 0; i < that->d()->esTable->size(); ++i) {
311 that->d()->esTable->iterate(idx: i, k: &arguments[1], v: &arguments[0]); // fill in key (0), value (1)
312
313 callbackfn->call(thisObject: thisArg, argv: arguments, argc: 3);
314 CHECK_EXCEPTION();
315 }
316 return Encode::undefined();
317}
318
319ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
320{
321 Scope scope(b);
322 Scoped<MapObject> that(scope, thisObject);
323 if (!that || that->d()->isWeakMap)
324 return scope.engine->throwTypeError();
325
326 return that->d()->esTable->get(k: argc ? argv[0] : Value::undefinedValue());
327}
328
329ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
330{
331 Scope scope(b);
332 Scoped<MapObject> that(scope, thisObject);
333 if (!that || that->d()->isWeakMap)
334 return scope.engine->throwTypeError();
335
336 return Encode(that->d()->esTable->has(k: argc ? argv[0] : Value::undefinedValue()));
337}
338
339ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
340{
341 Scope scope(b);
342 Scoped<MapObject> that(scope, thisObject);
343 if (!that || that->d()->isWeakMap)
344 return scope.engine->throwTypeError();
345
346 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
347 ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
348 return ao->asReturnedValue();
349}
350
351ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
352{
353 Scope scope(b);
354 Scoped<MapObject> that(scope, thisObject);
355 if (!that || that->d()->isWeakMap)
356 return scope.engine->throwTypeError();
357
358 that->d()->esTable->set(k: argc ? argv[0] : Value::undefinedValue(), v: argc > 1 ? argv[1] : Value::undefinedValue());
359 return that.asReturnedValue();
360}
361
362ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
363{
364 Scope scope(b);
365 Scoped<MapObject> that(scope, thisObject);
366 if (!that || that->d()->isWeakMap)
367 return scope.engine->throwTypeError();
368
369 return Encode(that->d()->esTable->size());
370}
371
372ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
373{
374 Scope scope(b);
375 Scoped<MapObject> that(scope, thisObject);
376 if (!that || that->d()->isWeakMap)
377 return scope.engine->throwTypeError();
378
379 Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(o: that));
380 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
381 return ao->asReturnedValue();
382}
383

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