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
41#include "qv4setobject_p.h"
42#include "qv4setiterator_p.h"
43#include "qv4estable_p.h"
44#include "qv4symbol_p.h"
45
46using namespace QV4;
47
48DEFINE_OBJECT_VTABLE(SetCtor);
49DEFINE_OBJECT_VTABLE(WeakSetCtor);
50DEFINE_OBJECT_VTABLE(SetObject);
51
52void Heap::WeakSetCtor::init(QV4::ExecutionContext *scope)
53{
54 Heap::FunctionObject::init(scope, QStringLiteral("WeakSet"));
55}
56
57void Heap::SetCtor::init(QV4::ExecutionContext *scope)
58{
59 Heap::FunctionObject::init(scope, QStringLiteral("Set"));
60}
61
62ReturnedValue WeakSetCtor::construct(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget, bool isWeak)
63{
64 Scope scope(f);
65 Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>());
66 bool protoSet = false;
67 if (newTarget)
68 protoSet = a->setProtoFromNewTarget(newTarget);
69 if (!protoSet && isWeak)
70 a->setPrototypeOf(scope.engine->weakSetPrototype());
71 a->d()->isWeakSet = isWeak;
72
73 if (argc > 0) {
74 ScopedValue iterable(scope, argv[0]);
75 if (!iterable->isUndefined() && !iterable->isNull()) {
76 ScopedFunctionObject adder(scope, a->get(name: ScopedString(scope, scope.engine->newString(s: QString::fromLatin1(str: "add")))));
77 if (!adder)
78 return scope.engine->throwTypeError();
79 ScopedObject iter(scope, Runtime::GetIterator::call(scope.engine, iterable, true));
80 CHECK_EXCEPTION();
81 if (!iter)
82 return a.asReturnedValue();
83
84 Value *nextValue = scope.alloc(nValues: 1);
85 ScopedValue done(scope);
86 forever {
87 done = Runtime::IteratorNext::call(scope.engine, iter, nextValue);
88 CHECK_EXCEPTION();
89 if (done->toBoolean())
90 return a.asReturnedValue();
91
92 adder->call(thisObject: a, argv: nextValue, argc: 1);
93 if (scope.engine->hasException) {
94 ScopedValue falsey(scope, Encode(false));
95 return Runtime::IteratorClose::call(scope.engine, iter, falsey);
96 }
97 }
98 }
99 }
100
101 return a.asReturnedValue();
102}
103
104ReturnedValue WeakSetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
105{
106 return construct(f, argv, argc, newTarget, isWeak: true);
107}
108
109ReturnedValue WeakSetCtor::virtualCall(const FunctionObject *f, const Value *, const Value *, int)
110{
111 Scope scope(f);
112 return scope.engine->throwTypeError(message: QString::fromLatin1(str: "Set requires new"));
113}
114
115ReturnedValue SetCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget)
116{
117 return construct(f, argv, argc, newTarget, isWeak: false);
118}
119
120void WeakSetPrototype::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 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
127
128 defineDefaultProperty(QStringLiteral("add"), code: method_add, argumentCount: 1);
129 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
130 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
131
132 ScopedString val(scope, engine->newString(s: QLatin1String("WeakSet")));
133 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
134}
135
136ReturnedValue WeakSetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
137{
138 Scope scope(b);
139 Scoped<SetObject> that(scope, thisObject);
140 if ((!that || !that->d()->isWeakSet) ||
141 (!argc || !argv[0].isObject()))
142 return scope.engine->throwTypeError();
143
144 that->d()->esTable->set(k: argv[0], v: Value::undefinedValue());
145 return that.asReturnedValue();
146}
147
148ReturnedValue WeakSetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
149{
150 Scope scope(b);
151 Scoped<SetObject> that(scope, thisObject);
152 if (!that || !that->d()->isWeakSet)
153 return scope.engine->throwTypeError();
154 if (!argc || !argv[0].isObject())
155 return Encode(false);
156
157 return Encode(that->d()->esTable->remove(k: argv[0]));
158}
159
160ReturnedValue WeakSetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
161{
162 Scope scope(b);
163 Scoped<SetObject> that(scope, thisObject);
164 if (!that || !that->d()->isWeakSet)
165 return scope.engine->throwTypeError();
166 if (!argc || !argv[0].isObject())
167 return Encode(false);
168
169 return Encode(that->d()->esTable->has(k: argv[0]));
170}
171
172void SetPrototype::init(ExecutionEngine *engine, Object *ctor)
173{
174 Scope scope(engine);
175 ScopedObject o(scope);
176 ctor->defineReadonlyConfigurableProperty(name: engine->id_length(), value: Value::fromInt32(i: 0));
177 ctor->defineReadonlyProperty(name: engine->id_prototype(), value: (o = this));
178 ctor->addSymbolSpecies();
179 defineDefaultProperty(name: engine->id_constructor(), value: (o = ctor));
180
181 defineDefaultProperty(QStringLiteral("add"), code: method_add, argumentCount: 1);
182 defineDefaultProperty(QStringLiteral("clear"), code: method_clear, argumentCount: 0);
183 defineDefaultProperty(QStringLiteral("delete"), code: method_delete, argumentCount: 1);
184 defineDefaultProperty(QStringLiteral("entries"), code: method_entries, argumentCount: 0);
185 defineDefaultProperty(QStringLiteral("forEach"), code: method_forEach, argumentCount: 1);
186 defineDefaultProperty(QStringLiteral("has"), code: method_has, argumentCount: 1);
187 defineAccessorProperty(QStringLiteral("size"), getter: method_get_size, setter: nullptr);
188
189 // Per the spec, the value for 'keys' is the same as 'values'.
190 ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values")));
191 ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, nameOrSymbol: valString, code: SetPrototype::method_values, argumentCount: 0));
192 defineDefaultProperty(QStringLiteral("keys"), value: valuesFn);
193 defineDefaultProperty(QStringLiteral("values"), value: valuesFn);
194
195 defineDefaultProperty(name: engine->symbol_iterator(), value: valuesFn);
196
197 ScopedString val(scope, engine->newString(s: QLatin1String("Set")));
198 defineReadonlyConfigurableProperty(name: engine->symbol_toStringTag(), value: val);
199}
200
201void Heap::SetObject::init()
202{
203 Object::init();
204 esTable = new ESTable();
205}
206
207void Heap::SetObject::destroy()
208{
209 delete esTable;
210 esTable = 0;
211}
212
213void Heap::SetObject::removeUnmarkedKeys()
214{
215 esTable->removeUnmarkedKeys();
216}
217
218void Heap::SetObject::markObjects(Heap::Base *that, MarkStack *markStack)
219{
220 SetObject *s = static_cast<SetObject *>(that);
221 s->esTable->markObjects(s: markStack, isWeakMap: s->isWeakSet);
222 Object::markObjects(base: that, stack: markStack);
223}
224
225ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
226{
227 Scope scope(b);
228 Scoped<SetObject> that(scope, thisObject);
229 if (!that || that->d()->isWeakSet)
230 return scope.engine->throwTypeError();
231
232 that->d()->esTable->set(k: argv[0], v: Value::undefinedValue());
233 return that.asReturnedValue();
234}
235
236ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
237{
238 Scope scope(b);
239 Scoped<SetObject> that(scope, thisObject);
240 if (!that || that->d()->isWeakSet)
241 return scope.engine->throwTypeError();
242
243 that->d()->esTable->clear();
244 return Encode::undefined();
245}
246
247ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
248{
249 Scope scope(b);
250 Scoped<SetObject> that(scope, thisObject);
251 if (!that || that->d()->isWeakSet)
252 return scope.engine->throwTypeError();
253
254 return Encode(that->d()->esTable->remove(k: argv[0]));
255}
256
257ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
258{
259 Scope scope(b);
260 Scoped<SetObject> that(scope, thisObject);
261 if (!that || that->d()->isWeakSet)
262 return scope.engine->throwTypeError();
263
264 Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(o: that));
265 ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
266 return ao->asReturnedValue();
267}
268
269ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
270{
271 Scope scope(b);
272 Scoped<SetObject> that(scope, thisObject);
273 if (!that || that->d()->isWeakSet)
274 return scope.engine->throwTypeError();
275
276 ScopedFunctionObject callbackfn(scope, argv[0]);
277 if (!callbackfn)
278 return scope.engine->throwTypeError();
279
280 ScopedValue thisArg(scope, Value::undefinedValue());
281 if (argc > 1)
282 thisArg = ScopedValue(scope, argv[1]);
283
284 Value *arguments = scope.alloc(nValues: 3);
285 for (uint i = 0; i < that->d()->esTable->size(); ++i) {
286 that->d()->esTable->iterate(idx: i, k: &arguments[0], v: &arguments[1]); // fill in key (0), value (1)
287 arguments[1] = arguments[0]; // but for set, we want to return the key twice; value is always undefined.
288
289 arguments[2] = that;
290 callbackfn->call(thisObject: thisArg, argv: arguments, argc: 3);
291 CHECK_EXCEPTION();
292 }
293 return Encode::undefined();
294}
295
296ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
297{
298 Scope scope(b);
299 Scoped<SetObject> that(scope, thisObject);
300 if (!that || that->d()->isWeakSet)
301 return scope.engine->throwTypeError();
302
303 return Encode(that->d()->esTable->has(k: argv[0]));
304}
305
306ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
307{
308 Scope scope(b);
309 Scoped<SetObject> that(scope, thisObject);
310 if (!that || that->d()->isWeakSet)
311 return scope.engine->throwTypeError();
312
313 return Encode(that->d()->esTable->size());
314}
315
316ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
317{
318 Scope scope(b);
319 Scoped<SetObject> that(scope, thisObject);
320 if (!that || that->d()->isWeakSet)
321 return scope.engine->throwTypeError();
322
323 Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(o: that));
324 ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
325 return ao->asReturnedValue();
326}
327

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