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

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