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 | |
10 | using namespace QV4; |
11 | |
12 | DEFINE_OBJECT_VTABLE(Reflect); |
13 | |
14 | void 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 | |
35 | struct CallArgs { |
36 | Value *argv; |
37 | int argc; |
38 | }; |
39 | |
40 | static 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 | |
56 | ReturnedValue 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 | |
71 | ReturnedValue 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 | |
89 | ReturnedValue 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 | |
112 | ReturnedValue 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 | |
122 | ReturnedValue 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 | |
139 | ReturnedValue 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 | |
147 | ReturnedValue 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 | |
157 | ReturnedValue 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 | |
174 | ReturnedValue 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 | |
184 | ReturnedValue 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 | |
214 | ReturnedValue 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 | |
224 | ReturnedValue 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 | |
243 | ReturnedValue 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 | |