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