1// Copyright (C) 2018 The Qt Company Ltd.
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 "qv4reflect_p.h"
5#include "qv4runtimeapi_p.h"
6#include "qv4objectproto_p.h"
7#include "qv4propertykey_p.h"
8#include "qv4objectiterator_p.h"
9
10using namespace QV4;
11
12DEFINE_OBJECT_VTABLE(Reflect);
13
14void Heap::Reflect::init()
15{
16 Object::init();
17 Scope scope(internalClass->engine);
18 ScopedObject r(scope, this);
19
20 r->defineDefaultProperty(QStringLiteral("apply"), code: QV4::Reflect::method_apply, argumentCount: 3);
21 r->defineDefaultProperty(QStringLiteral("construct"), code: QV4::Reflect::method_construct, argumentCount: 2);
22 r->defineDefaultProperty(QStringLiteral("defineProperty"), code: QV4::Reflect::method_defineProperty, argumentCount: 3);
23 r->defineDefaultProperty(QStringLiteral("deleteProperty"), code: QV4::Reflect::method_deleteProperty, argumentCount: 2);
24 r->defineDefaultProperty(QStringLiteral("get"), code: QV4::Reflect::method_get, argumentCount: 2);
25 r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), code: QV4::Reflect::method_getOwnPropertyDescriptor, argumentCount: 2);
26 r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), code: QV4::Reflect::method_getPrototypeOf, argumentCount: 1);
27 r->defineDefaultProperty(QStringLiteral("has"), code: QV4::Reflect::method_has, argumentCount: 2);
28 r->defineDefaultProperty(QStringLiteral("isExtensible"), code: QV4::Reflect::method_isExtensible, argumentCount: 1);
29 r->defineDefaultProperty(QStringLiteral("ownKeys"), code: QV4::Reflect::method_ownKeys, argumentCount: 1);
30 r->defineDefaultProperty(QStringLiteral("preventExtensions"), code: QV4::Reflect::method_preventExtensions, argumentCount: 1);
31 r->defineDefaultProperty(QStringLiteral("set"), code: QV4::Reflect::method_set, argumentCount: 3);
32 r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), code: QV4::Reflect::method_setPrototypeOf, argumentCount: 2);
33}
34
35struct CallArgs {
36 Value *argv;
37 int argc;
38};
39
40static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
41{
42 int len = scope.engine->safeForAllocLength(len64: o->getLength());
43 if (scope.engine->hasException)
44 return {.argv: nullptr, .argc: 0};
45
46 Value *arguments = scope.alloc(nValues: len);
47
48 for (int i = 0; i < len; ++i) {
49 arguments[i] = o->get(idx: i);
50 if (scope.hasException())
51 return { .argv: nullptr, .argc: 0 };
52 }
53 return { .argv: arguments, .argc: len };
54}
55
56ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc)
57{
58 Scope scope(f);
59 if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject())
60 return scope.engine->throwTypeError();
61
62 const Object *o = static_cast<const Object *>(argv + 2);
63 CallArgs arguments = createListFromArrayLike(scope, o);
64 if (scope.hasException())
65 return Encode::undefined();
66
67 return checkedResult(v4: scope.engine, result: static_cast<const FunctionObject &>(argv[0]).call(
68 thisObject: &argv[1], argv: arguments.argv, argc: arguments.argc));
69}
70
71ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc)
72{
73 Scope scope(f);
74 if (argc < 2 || !argv[1].isObject())
75 return scope.engine->throwTypeError();
76 const FunctionObject *target = argv[0].as<FunctionObject>();
77 const FunctionObject *newTarget = argc == 3 ? argv[2].as<FunctionObject>() : target;
78 if (!target || !target->isConstructor() || !newTarget || !newTarget->isConstructor())
79 return scope.engine->throwTypeError();
80
81 const Object *o = static_cast<const Object *>(argv + 1);
82 CallArgs arguments = createListFromArrayLike(scope, o);
83 if (scope.hasException())
84 return Encode::undefined();
85
86 return target->callAsConstructor(argv: arguments.argv, argc: arguments.argc, newTarget);
87}
88
89ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
90{
91 Scope scope(f);
92 if (!argc || !argv[0].isObject())
93 return scope.engine->throwTypeError();
94
95 ScopedObject O(scope, argv[0]);
96 ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(e: scope.engine));
97 if (scope.hasException())
98 return QV4::Encode::undefined();
99
100 ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue());
101 ScopedProperty pd(scope);
102 PropertyAttributes attrs;
103 ObjectPrototype::toPropertyDescriptor(engine: scope.engine, v: attributes, desc: pd, attrs: &attrs);
104 if (scope.hasException())
105 return QV4::Encode::undefined();
106
107 bool result = O->defineOwnProperty(id: name, p: pd, attrs);
108
109 return Encode(result);
110}
111
112ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
113{
114 ExecutionEngine *e = f->engine();
115 if (!argc || !argv[0].isObject())
116 return e->throwTypeError();
117
118 bool result = Runtime::DeleteProperty_NoThrow::call(e, argv[0], argc > 1 ? argv[1] : Value::undefinedValue());
119 return Encode(result);
120}
121
122ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc)
123{
124 Scope scope(f);
125 if (!argc || !argv[0].isObject())
126 return scope.engine->throwTypeError();
127
128 ScopedObject o(scope, static_cast<const Object *>(argv));
129 Value undef = Value::undefinedValue();
130 const Value *index = argc > 1 ? &argv[1] : &undef;
131 ScopedPropertyKey name(scope, index->toPropertyKey(e: scope.engine));
132 if (scope.hasException())
133 return Encode::undefined();
134 ScopedValue receiver(scope, argc > 2 ? argv[2] : *o);
135
136 return Encode(o->get(id: name, receiver));
137}
138
139ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
140{
141 if (!argc || !argv[0].isObject())
142 return f->engine()->throwTypeError();
143
144 return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc);
145}
146
147ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
148{
149 if (!argc || !argv[0].isObject())
150 return f->engine()->throwTypeError();
151
152 const Object *o = static_cast<const Object *>(argv);
153 Heap::Object *p = o->getPrototypeOf();
154 return (p ? p->asReturnedValue() : Encode::null());
155}
156
157ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc)
158{
159 Scope scope(f);
160 if (!argc || !argv[0].isObject())
161 return scope.engine->throwTypeError();
162
163 ScopedObject o(scope, static_cast<const Object *>(argv));
164 Value undef = Value::undefinedValue();
165 const Value *index = argc > 1 ? &argv[1] : &undef;
166
167 ScopedPropertyKey name(scope, index->toPropertyKey(e: scope.engine));
168 if (scope.hasException())
169 return false;
170
171 return Encode(o->hasProperty(id: name));
172}
173
174ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc)
175{
176 if (!argc || !argv[0].isObject())
177 return f->engine()->throwTypeError();
178
179 const Object *o = static_cast<const Object *>(argv);
180 return Encode(o->isExtensible());
181}
182
183
184ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *, const Value *argv, int argc)
185{
186 if (!argc || !argv[0].isObject())
187 return f->engine()->throwTypeError();
188
189 Scope scope(f);
190 if (!argc)
191 return scope.engine->throwTypeError();
192
193 ScopedObject O(scope, argv[0].toObject(e: scope.engine));
194 if (!O)
195 return Encode::undefined();
196
197 ScopedArrayObject keys(scope, scope.engine->newArrayObject());
198
199 ObjectIterator it(scope, O, ObjectIterator::WithSymbols);
200 ScopedPropertyKey key(scope);
201 ScopedValue v(scope);
202 while (1) {
203 key = it.next();
204 if (!key->isValid())
205 break;
206 v = key->toStringOrSymbol(e: scope.engine);
207 keys->push_back(v);
208 }
209
210 return keys->asReturnedValue();
211
212}
213
214ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc)
215{
216 Scope scope(f);
217 if (!argc || !argv[0].isObject())
218 return scope.engine->throwTypeError();
219
220 ScopedObject o(scope, static_cast<const Object *>(argv));
221 return Encode(o->preventExtensions());
222}
223
224ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc)
225{
226 Scope scope(f);
227 if (!argc || !argv[0].isObject())
228 return scope.engine->throwTypeError();
229
230 ScopedObject o(scope, static_cast<const Object *>(argv));
231 Value undef = Value::undefinedValue();
232 const Value *index = argc > 1 ? &argv[1] : &undef;
233 const Value &val = argc > 2 ? argv[2] : undef;
234 ScopedValue receiver(scope, argc >3 ? argv[3] : argv[0]);
235
236 ScopedPropertyKey propertyKey(scope, index->toPropertyKey(e: scope.engine));
237 if (scope.hasException())
238 return false;
239 bool result = o->put(id: propertyKey, v: val, receiver);
240 return Encode(result);
241}
242
243ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
244{
245 if (argc < 2 || !argv[0].isObject() || (!argv[1].isNull() && !argv[1].isObject()))
246 return f->engine()->throwTypeError();
247
248 Scope scope(f);
249 ScopedObject o(scope, static_cast<const Object *>(argv));
250 const Object *proto = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1);
251 return Encode(o->setPrototypeOf(proto));
252}
253

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