1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2017 Crimson AS <info@crimson.no> |
4 | ** Copyright (C) 2016 The Qt Company Ltd. |
5 | ** Contact: https://www.qt.io/licensing/ |
6 | ** |
7 | ** This file is part of the QtQml module of the Qt Toolkit. |
8 | ** |
9 | ** $QT_BEGIN_LICENSE:LGPL$ |
10 | ** Commercial License Usage |
11 | ** Licensees holding valid commercial Qt licenses may use this file in |
12 | ** accordance with the commercial license agreement provided with the |
13 | ** Software or, alternatively, in accordance with the terms contained in |
14 | ** a written agreement between you and The Qt Company. For licensing terms |
15 | ** and conditions see https://www.qt.io/terms-conditions. For further |
16 | ** information use the contact form at https://www.qt.io/contact-us. |
17 | ** |
18 | ** GNU Lesser General Public License Usage |
19 | ** Alternatively, this file may be used under the terms of the GNU Lesser |
20 | ** General Public License version 3 as published by the Free Software |
21 | ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
22 | ** packaging of this file. Please review the following information to |
23 | ** ensure the GNU Lesser General Public License version 3 requirements |
24 | ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
25 | ** |
26 | ** GNU General Public License Usage |
27 | ** Alternatively, this file may be used under the terms of the GNU |
28 | ** General Public License version 2.0 or (at your option) the GNU General |
29 | ** Public license version 3 or any later version approved by the KDE Free |
30 | ** Qt Foundation. The licenses are as published by the Free Software |
31 | ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
32 | ** included in the packaging of this file. Please review the following |
33 | ** information to ensure the GNU General Public License requirements will |
34 | ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
35 | ** https://www.gnu.org/licenses/gpl-3.0.html. |
36 | ** |
37 | ** $QT_END_LICENSE$ |
38 | ** |
39 | ****************************************************************************/ |
40 | |
41 | |
42 | #include "qv4objectproto_p.h" |
43 | #include "qv4argumentsobject_p.h" |
44 | #include <private/qv4mm_p.h> |
45 | #include "qv4scopedvalue_p.h" |
46 | #include "qv4runtime_p.h" |
47 | #include "qv4objectiterator_p.h" |
48 | #include "qv4string_p.h" |
49 | #include "qv4jscall_p.h" |
50 | #include "qv4symbol_p.h" |
51 | #include "qv4propertykey_p.h" |
52 | |
53 | #include <QtCore/QDateTime> |
54 | #include <QtCore/QStringList> |
55 | |
56 | using namespace QV4; |
57 | |
58 | |
59 | DEFINE_OBJECT_VTABLE(ObjectCtor); |
60 | |
61 | void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) |
62 | { |
63 | Heap::FunctionObject::init(scope, QStringLiteral("Object" )); |
64 | } |
65 | |
66 | ReturnedValue ObjectCtor::virtualCallAsConstructor(const FunctionObject *f, const Value *argv, int argc, const Value *newTarget) |
67 | { |
68 | ExecutionEngine *v4 = f->engine(); |
69 | const ObjectCtor *nt = static_cast<const ObjectCtor *>(newTarget); |
70 | if (!argc || argv[0].isUndefined() || argv[0].isNull()) { |
71 | Scope scope(v4); |
72 | ScopedObject obj(scope, scope.engine->newObject()); |
73 | ScopedObject proto(scope, nt->get(name: scope.engine->id_prototype())); |
74 | if (!!proto) |
75 | obj->setPrototypeOf(proto); |
76 | return obj.asReturnedValue(); |
77 | } else { |
78 | return argv[0].toObject(e: v4)->asReturnedValue(); |
79 | } |
80 | } |
81 | |
82 | ReturnedValue ObjectCtor::virtualCall(const FunctionObject *m, const Value *, const Value *argv, int argc) |
83 | { |
84 | ExecutionEngine *v4 = m->engine(); |
85 | if (!argc || argv[0].isUndefined() || argv[0].isNull()) { |
86 | return v4->newObject()->asReturnedValue(); |
87 | } else { |
88 | return argv[0].toObject(e: v4)->asReturnedValue(); |
89 | } |
90 | } |
91 | |
92 | void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) |
93 | { |
94 | Scope scope(v4); |
95 | ScopedObject o(scope, this); |
96 | |
97 | ctor->defineReadonlyProperty(name: v4->id_prototype(), value: o); |
98 | ctor->defineReadonlyConfigurableProperty(name: v4->id_length(), value: Value::fromInt32(i: 1)); |
99 | ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf" ), code: method_getPrototypeOf, argumentCount: 1); |
100 | ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor" ), code: method_getOwnPropertyDescriptor, argumentCount: 2); |
101 | ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptors" ), code: method_getOwnPropertyDescriptors, argumentCount: 1); |
102 | ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames" ), code: method_getOwnPropertyNames, argumentCount: 1); |
103 | ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols" ), code: method_getOwnPropertySymbols, argumentCount: 1); |
104 | ctor->defineDefaultProperty(QStringLiteral("assign" ), code: method_assign, argumentCount: 2); |
105 | ctor->defineDefaultProperty(QStringLiteral("create" ), code: method_create, argumentCount: 2); |
106 | ctor->defineDefaultProperty(QStringLiteral("defineProperty" ), code: method_defineProperty, argumentCount: 3); |
107 | ctor->defineDefaultProperty(QStringLiteral("defineProperties" ), code: method_defineProperties, argumentCount: 2); |
108 | ctor->defineDefaultProperty(QStringLiteral("entries" ), code: method_entries, argumentCount: 1); |
109 | ctor->defineDefaultProperty(QStringLiteral("seal" ), code: method_seal, argumentCount: 1); |
110 | ctor->defineDefaultProperty(QStringLiteral("freeze" ), code: method_freeze, argumentCount: 1); |
111 | ctor->defineDefaultProperty(QStringLiteral("preventExtensions" ), code: method_preventExtensions, argumentCount: 1); |
112 | ctor->defineDefaultProperty(QStringLiteral("is" ), code: method_is, argumentCount: 2); |
113 | ctor->defineDefaultProperty(QStringLiteral("isSealed" ), code: method_isSealed, argumentCount: 1); |
114 | ctor->defineDefaultProperty(QStringLiteral("isFrozen" ), code: method_isFrozen, argumentCount: 1); |
115 | ctor->defineDefaultProperty(QStringLiteral("isExtensible" ), code: method_isExtensible, argumentCount: 1); |
116 | ctor->defineDefaultProperty(QStringLiteral("keys" ), code: method_keys, argumentCount: 1); |
117 | ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf" ), code: method_setPrototypeOf, argumentCount: 2); |
118 | ctor->defineDefaultProperty(QStringLiteral("values" ), code: method_values, argumentCount: 1); |
119 | |
120 | defineDefaultProperty(QStringLiteral("constructor" ), value: (o = ctor)); |
121 | defineDefaultProperty(name: v4->id_toString(), code: method_toString, argumentCount: 0); |
122 | defineDefaultProperty(name: v4->id_toLocaleString(), code: method_toLocaleString, argumentCount: 0); |
123 | defineDefaultProperty(name: v4->id_valueOf(), code: method_valueOf, argumentCount: 0); |
124 | defineDefaultProperty(QStringLiteral("hasOwnProperty" ), code: method_hasOwnProperty, argumentCount: 1); |
125 | defineDefaultProperty(QStringLiteral("isPrototypeOf" ), code: method_isPrototypeOf, argumentCount: 1); |
126 | defineDefaultProperty(QStringLiteral("propertyIsEnumerable" ), code: method_propertyIsEnumerable, argumentCount: 1); |
127 | defineDefaultProperty(QStringLiteral("__defineGetter__" ), code: method_defineGetter, argumentCount: 2); |
128 | defineDefaultProperty(QStringLiteral("__defineSetter__" ), code: method_defineSetter, argumentCount: 2); |
129 | |
130 | defineAccessorProperty(name: v4->id___proto__(), getter: method_get_proto, setter: method_set_proto); |
131 | } |
132 | |
133 | ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc) |
134 | { |
135 | Scope scope(b); |
136 | if (argc < 1) |
137 | return scope.engine->throwTypeError(); |
138 | |
139 | ScopedObject o(scope, argv[0].toObject(e: scope.engine)); |
140 | if (scope.engine->hasException) |
141 | return QV4::Encode::undefined(); |
142 | |
143 | ScopedObject p(scope, o->getPrototypeOf()); |
144 | return (!!p ? p->asReturnedValue() : Encode::null()); |
145 | } |
146 | |
147 | ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc) |
148 | { |
149 | if (!argc) |
150 | return Encode(true); |
151 | if (argc == 1) |
152 | return Encode((argv[0].isUndefined() ? true : false)); |
153 | return Encode(argv[0].sameValue(other: argv[1])); |
154 | } |
155 | |
156 | ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc) |
157 | { |
158 | Scope scope(b); |
159 | if (argc < 1) |
160 | return scope.engine->throwTypeError(); |
161 | |
162 | ScopedObject O(scope, argv[0].toObject(e: scope.engine)); |
163 | if (scope.engine->hasException) |
164 | return QV4::Encode::undefined(); |
165 | |
166 | if (ArgumentsObject::isNonStrictArgumentsObject(m: O)) |
167 | static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); |
168 | |
169 | ScopedValue v(scope, argc > 1 ? argv[1] : Value::undefinedValue()); |
170 | ScopedPropertyKey name(scope, v->toPropertyKey(e: scope.engine)); |
171 | if (scope.engine->hasException) |
172 | return QV4::Encode::undefined(); |
173 | |
174 | ScopedProperty desc(scope); |
175 | PropertyAttributes attrs = O->getOwnProperty(id: name, p: desc); |
176 | return fromPropertyDescriptor(engine: scope.engine, desc, attrs); |
177 | } |
178 | |
179 | ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptors(const FunctionObject *f, const Value *, const Value *argv, int argc) |
180 | { |
181 | Scope scope(f); |
182 | if (!argc) |
183 | return scope.engine->throwTypeError(); |
184 | |
185 | ScopedObject o(scope, argv[0].toObject(e: scope.engine)); |
186 | if (scope.engine->hasException) |
187 | return Encode::undefined(); |
188 | |
189 | ScopedObject descriptors(scope, scope.engine->newObject()); |
190 | |
191 | ObjectIterator it(scope, o, ObjectIterator::WithSymbols); |
192 | ScopedProperty pd(scope); |
193 | PropertyAttributes attrs; |
194 | ScopedPropertyKey key(scope); |
195 | ScopedObject entry(scope); |
196 | while (1) { |
197 | key = it.next(pd, attributes: &attrs); |
198 | if (!key->isValid()) |
199 | break; |
200 | entry = fromPropertyDescriptor(engine: scope.engine, desc: pd, attrs); |
201 | descriptors->put(id: key, v: entry); |
202 | } |
203 | |
204 | return descriptors.asReturnedValue(); |
205 | |
206 | } |
207 | |
208 | ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *b, const Value *, const Value *argv, int argc) |
209 | { |
210 | Scope scope(b); |
211 | if (argc < 1) |
212 | return scope.engine->throwTypeError(); |
213 | |
214 | ScopedObject O(scope, argv[0].toObject(e: scope.engine)); |
215 | if (scope.engine->hasException) |
216 | return QV4::Encode::undefined(); |
217 | |
218 | return Encode(getOwnPropertyNames(v4: scope.engine, o: argv[0])); |
219 | } |
220 | |
221 | ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc) |
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 array(scope, scope.engine->newArrayObject()); |
232 | if (O) { |
233 | ObjectIterator it(scope, O, ObjectIterator::WithSymbols); |
234 | ScopedValue name(scope); |
235 | while (1) { |
236 | name = it.nextPropertyNameAsString(); |
237 | if (name->isNull()) |
238 | break; |
239 | if (!name->isSymbol()) |
240 | continue; |
241 | array->push_back(v: name); |
242 | } |
243 | } |
244 | return array->asReturnedValue(); |
245 | } |
246 | |
247 | // 19.1.2.1 |
248 | ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc) |
249 | { |
250 | Scope scope(b); |
251 | if (argc < 1) |
252 | return scope.engine->throwTypeError(); |
253 | |
254 | ScopedObject to(scope, argv[0].toObject(e: scope.engine)); |
255 | if (scope.engine->hasException) |
256 | return QV4::Encode::undefined(); |
257 | |
258 | if (argc == 1) |
259 | return to.asReturnedValue(); |
260 | |
261 | for (int i = 1, ei = argc; i < ei; ++i) { |
262 | if (argv[i].isUndefined() || argv[i].isNull()) |
263 | continue; |
264 | |
265 | ScopedObject from(scope, argv[i].toObject(e: scope.engine)); |
266 | if (scope.engine->hasException) |
267 | return QV4::Encode::undefined(); |
268 | QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(v4: scope.engine, o: from)); |
269 | quint32 length = keys->getLength(); |
270 | |
271 | ScopedString nextKey(scope); |
272 | ScopedValue propValue(scope); |
273 | for (quint32 i = 0; i < length; ++i) { |
274 | nextKey = Value::fromReturnedValue(val: keys->get(idx: i)).toString(e: scope.engine); |
275 | |
276 | ScopedProperty prop(scope); |
277 | PropertyAttributes attrs = from->getOwnProperty(id: nextKey->toPropertyKey(), p: prop); |
278 | |
279 | if (attrs == PropertyFlag::Attr_Invalid) |
280 | continue; |
281 | |
282 | if (!attrs.isEnumerable()) |
283 | continue; |
284 | |
285 | propValue = from->get(name: nextKey); |
286 | to->set(name: nextKey, v: propValue, shouldThrow: Object::DoThrowOnRejection); |
287 | if (scope.engine->hasException) |
288 | return QV4::Encode::undefined(); |
289 | } |
290 | } |
291 | |
292 | return to.asReturnedValue(); |
293 | } |
294 | |
295 | ReturnedValue ObjectPrototype::method_create(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc) |
296 | { |
297 | Scope scope(builtin); |
298 | if (!argc || (!argv[0].isObject() && !argv[0].isNull())) |
299 | return scope.engine->throwTypeError(); |
300 | |
301 | ScopedObject O(scope, argv[0]); |
302 | |
303 | ScopedObject newObject(scope, scope.engine->newObject()); |
304 | newObject->setPrototypeOf(O); |
305 | |
306 | |
307 | if (argc > 1 && !argv[1].isUndefined()) { |
308 | Value *arguments = scope.alloc(nValues: argc); |
309 | arguments[0] = newObject; |
310 | memcpy(dest: arguments + 1, src: argv + 1, n: (argc - 1)*sizeof(Value)); |
311 | return method_defineProperties(builtin, thisObject, argv: arguments, argc); |
312 | } |
313 | |
314 | return newObject.asReturnedValue(); |
315 | } |
316 | |
317 | ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, const Value *, const Value *argv, int argc) |
318 | { |
319 | Scope scope(b); |
320 | if (!argc || !argv[0].isObject()) |
321 | return scope.engine->throwTypeError(); |
322 | |
323 | ScopedObject O(scope, argv[0]); |
324 | ScopedPropertyKey name(scope, (argc > 1 ? argv[1] : Value::undefinedValue()).toPropertyKey(e: scope.engine)); |
325 | if (scope.engine->hasException) |
326 | return QV4::Encode::undefined(); |
327 | |
328 | ScopedValue attributes(scope, argc > 2 ? argv[2] : Value::undefinedValue()); |
329 | ScopedProperty pd(scope); |
330 | PropertyAttributes attrs; |
331 | toPropertyDescriptor(engine: scope.engine, v: attributes, desc: pd, attrs: &attrs); |
332 | if (scope.engine->hasException) |
333 | return QV4::Encode::undefined(); |
334 | |
335 | if (!O->defineOwnProperty(id: name, p: pd, attrs)) |
336 | THROW_TYPE_ERROR(); |
337 | |
338 | return O.asReturnedValue(); |
339 | } |
340 | |
341 | ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b, const Value *, const Value *argv, int argc) |
342 | { |
343 | Scope scope(b); |
344 | if (argc < 2 || !argv[0].isObject()) |
345 | return scope.engine->throwTypeError(); |
346 | |
347 | ScopedObject O(scope, argv[0]); |
348 | |
349 | ScopedObject o(scope, argv[1].toObject(e: scope.engine)); |
350 | if (scope.engine->hasException) |
351 | return QV4::Encode::undefined(); |
352 | |
353 | ScopedValue val(scope); |
354 | |
355 | ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
356 | ScopedProperty pd(scope); |
357 | ScopedProperty n(scope); |
358 | ScopedPropertyKey key(scope); |
359 | while (1) { |
360 | PropertyAttributes attrs; |
361 | key = it.next(pd, attributes: &attrs); |
362 | if (!key->isValid()) |
363 | break; |
364 | PropertyAttributes nattrs; |
365 | val = o->getValue(v: pd->value, attrs); |
366 | toPropertyDescriptor(engine: scope.engine, v: val, desc: n, attrs: &nattrs); |
367 | if (scope.engine->hasException) |
368 | return QV4::Encode::undefined(); |
369 | bool ok = O->defineOwnProperty(id: key, p: n, attrs: nattrs); |
370 | if (!ok) |
371 | THROW_TYPE_ERROR(); |
372 | } |
373 | |
374 | return O.asReturnedValue(); |
375 | } |
376 | |
377 | ReturnedValue ObjectPrototype::method_entries(const FunctionObject *f, const Value *, const Value *argv, int argc) |
378 | { |
379 | Scope scope(f); |
380 | if (!argc) |
381 | return scope.engine->throwTypeError(); |
382 | |
383 | ScopedObject o(scope, argv[0].toObject(e: scope.engine)); |
384 | if (scope.engine->hasException) |
385 | return Encode::undefined(); |
386 | |
387 | ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
388 | |
389 | ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
390 | ScopedString name(scope); |
391 | ScopedArrayObject entry(scope); |
392 | while (1) { |
393 | name = it.nextPropertyNameAsString(); |
394 | if (!name) |
395 | break; |
396 | entry = scope.engine->newArrayObject(); |
397 | entry->push_back(v: name); |
398 | a->push_back(v: entry); |
399 | } |
400 | |
401 | // now add values, do this after the loop above as reading out the values can have side effects |
402 | uint len = a->getLength(); |
403 | ScopedValue value(scope); |
404 | for (uint i = 0; i < len; ++i) { |
405 | entry = a->get(id: PropertyKey::fromArrayIndex(idx: i)); |
406 | name = entry->get(id: PropertyKey::fromArrayIndex(idx: 0)); |
407 | value = o->get(id: name->toPropertyKey()); |
408 | if (scope.engine->hasException) |
409 | return Encode::undefined(); |
410 | entry->push_back(v: value); |
411 | } |
412 | |
413 | return a.asReturnedValue(); |
414 | } |
415 | |
416 | ReturnedValue ObjectPrototype::method_seal(const FunctionObject *b, const Value *, const Value *argv, int argc) |
417 | { |
418 | const Value a = argc ? argv[0] : Value::undefinedValue(); |
419 | if (!a.isObject()) |
420 | // 19.1.2.17, 1 |
421 | return a.asReturnedValue(); |
422 | |
423 | Scope scope(b); |
424 | ScopedObject o(scope, a); |
425 | o->setInternalClass(o->internalClass()->canned()); |
426 | |
427 | if (o->arrayData()) { |
428 | ArrayData::ensureAttributes(o); |
429 | for (uint i = 0; i < o->d()->arrayData->values.alloc; ++i) { |
430 | if (!o->arrayData()->isEmpty(i)) |
431 | o->d()->arrayData->attrs[i].setConfigurable(false); |
432 | } |
433 | } |
434 | |
435 | return o.asReturnedValue(); |
436 | } |
437 | |
438 | ReturnedValue ObjectPrototype::method_freeze(const FunctionObject *b, const Value *, const Value *argv, int argc) |
439 | { |
440 | const Value a = argc ? argv[0] : Value::undefinedValue(); |
441 | if (!a.isObject()) |
442 | // 19.1.2.5, 1 |
443 | return a.asReturnedValue(); |
444 | |
445 | Scope scope(b); |
446 | ScopedObject o(scope, a); |
447 | |
448 | if (ArgumentsObject::isNonStrictArgumentsObject(m: o)) |
449 | static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); |
450 | |
451 | o->setInternalClass(o->internalClass()->cryopreserved()); |
452 | |
453 | if (o->arrayData()) { |
454 | ArrayData::ensureAttributes(o); |
455 | for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
456 | if (!o->arrayData()->isEmpty(i)) |
457 | o->arrayData()->attrs[i].setConfigurable(false); |
458 | if (o->arrayData()->attrs[i].isData()) |
459 | o->arrayData()->attrs[i].setWritable(false); |
460 | } |
461 | } |
462 | return o.asReturnedValue(); |
463 | } |
464 | |
465 | ReturnedValue ObjectPrototype::method_preventExtensions(const FunctionObject *b, const Value *, const Value *argv, int argc) |
466 | { |
467 | Scope scope(b); |
468 | if (!argc) |
469 | return Encode::undefined(); |
470 | |
471 | ScopedObject o(scope, argv[0]); |
472 | if (!o) |
473 | return argv[0].asReturnedValue(); |
474 | |
475 | o->preventExtensions(); |
476 | return o.asReturnedValue(); |
477 | } |
478 | |
479 | ReturnedValue ObjectPrototype::method_isSealed(const FunctionObject *b, const Value *, const Value *argv, int argc) |
480 | { |
481 | Scope scope(b); |
482 | if (!argc) |
483 | return Encode(true); |
484 | |
485 | ScopedObject o(scope, argv[0]); |
486 | if (!o) |
487 | return Encode(true); |
488 | |
489 | if (o->isExtensible()) |
490 | return Encode(false); |
491 | |
492 | if (o->internalClass() != o->internalClass()->canned()) |
493 | return Encode(false); |
494 | |
495 | if (!o->arrayData() || !o->arrayData()->length()) |
496 | return Encode(true); |
497 | |
498 | Q_ASSERT(o->arrayData() && o->arrayData()->length()); |
499 | if (!o->arrayData()->attrs) |
500 | return Encode(false); |
501 | |
502 | for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
503 | if (!o->arrayData()->isEmpty(i)) |
504 | if (o->arrayData()->attributes(i).isConfigurable()) |
505 | return Encode(false); |
506 | } |
507 | |
508 | return Encode(true); |
509 | } |
510 | |
511 | ReturnedValue ObjectPrototype::method_isFrozen(const FunctionObject *b, const Value *, const Value *argv, int argc) |
512 | { |
513 | Scope scope(b); |
514 | if (!argc) |
515 | return Encode(true); |
516 | |
517 | ScopedObject o(scope, argv[0]); |
518 | if (!o) |
519 | return Encode(true); |
520 | |
521 | if (o->isExtensible()) |
522 | return Encode(false); |
523 | |
524 | if (!o->internalClass()->isImplicitlyFrozen()) |
525 | return Encode(false); |
526 | |
527 | if (!o->arrayData() || !o->arrayData()->length()) |
528 | return Encode(true); |
529 | |
530 | Q_ASSERT(o->arrayData() && o->arrayData()->length()); |
531 | if (!o->arrayData()->attrs) |
532 | return Encode(false); |
533 | |
534 | for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { |
535 | if (!o->arrayData()->isEmpty(i)) |
536 | if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) |
537 | return Encode(false); |
538 | } |
539 | |
540 | return Encode(true); |
541 | } |
542 | |
543 | ReturnedValue ObjectPrototype::method_isExtensible(const FunctionObject *b, const Value *, const Value *argv, int argc) |
544 | { |
545 | Scope scope(b); |
546 | if (!argc) |
547 | return Encode(false); |
548 | |
549 | ScopedObject o(scope, argv[0]); |
550 | if (!o) |
551 | return Encode(false); |
552 | |
553 | return Encode((bool)o->isExtensible()); |
554 | } |
555 | |
556 | ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value *, const Value *argv, int argc) |
557 | { |
558 | Scope scope(b); |
559 | if (!argc) |
560 | return scope.engine->throwTypeError(); |
561 | |
562 | ScopedObject o(scope, argv[0].toObject(e: scope.engine)); |
563 | if (scope.engine->hasException) |
564 | return QV4::Encode::undefined(); |
565 | |
566 | ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
567 | |
568 | ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
569 | ScopedValue name(scope); |
570 | ScopedValue value(scope); |
571 | while (1) { |
572 | name = it.nextPropertyNameAsString(value); |
573 | if (name->isNull()) |
574 | break; |
575 | a->push_back(v: name); |
576 | } |
577 | |
578 | return a.asReturnedValue(); |
579 | } |
580 | |
581 | ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc) |
582 | { |
583 | Scope scope(f->engine()); |
584 | if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull())) |
585 | return scope.engine->throwTypeError(); |
586 | |
587 | if (!argv[0].isObject()) |
588 | return argv[0].asReturnedValue(); |
589 | |
590 | ScopedObject o(scope, argv[0]); |
591 | const Object *p = argv[1].isNull() ? nullptr : static_cast<const Object *>(argv + 1); |
592 | bool ok = o->setPrototypeOf(p); |
593 | if (!ok) |
594 | return scope.engine->throwTypeError(QStringLiteral("Could not change prototype." )); |
595 | return o->asReturnedValue(); |
596 | } |
597 | |
598 | ReturnedValue ObjectPrototype::method_values(const FunctionObject *f, const Value *, const Value *argv, int argc) |
599 | { |
600 | Scope scope(f); |
601 | if (!argc) |
602 | return scope.engine->throwTypeError(); |
603 | |
604 | ScopedObject o(scope, argv[0].toObject(e: scope.engine)); |
605 | if (scope.engine->hasException) |
606 | return QV4::Encode::undefined(); |
607 | |
608 | ScopedArrayObject a(scope, scope.engine->newArrayObject()); |
609 | |
610 | ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly); |
611 | ScopedPropertyKey key(scope); |
612 | ScopedProperty pd(scope); |
613 | ScopedValue value(scope); |
614 | PropertyAttributes attrs; |
615 | while (1) { |
616 | key = it.next(pd, attributes: &attrs); |
617 | if (!key->isValid()) |
618 | break; |
619 | value = o->getValue(v: pd->value, attrs); |
620 | a->push_back(v: value); |
621 | } |
622 | |
623 | return a.asReturnedValue(); |
624 | } |
625 | |
626 | ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int) |
627 | { |
628 | ExecutionEngine *v4 = b->engine(); |
629 | QString string; |
630 | if (thisObject->isUndefined()) { |
631 | string = QStringLiteral("[object Undefined]" ); |
632 | } else if (thisObject->isNull()) { |
633 | string = QStringLiteral("[object Null]" ); |
634 | } else { |
635 | const Object *o = thisObject->as<Object>(); |
636 | if (!o) { |
637 | // primitive, get the proper prototype |
638 | if (thisObject->isBoolean()) |
639 | o = v4->booleanPrototype(); |
640 | else if (thisObject->isNumber()) |
641 | o = v4->numberPrototype(); |
642 | else if (thisObject->isString()) |
643 | o = v4->stringPrototype(); |
644 | else if (thisObject->isSymbol()) |
645 | o = v4->symbolPrototype(); |
646 | Q_ASSERT(o); |
647 | } |
648 | QString name = o->className(); |
649 | Scope scope(v4); |
650 | ScopedString toStringTag(scope, o->get(name: v4->symbol_toStringTag())); |
651 | if (toStringTag) |
652 | name = toStringTag->toQString(); |
653 | string = QStringLiteral("[object %1]" ).arg(a: name); |
654 | } |
655 | return Encode(v4->newString(s: string)); |
656 | } |
657 | |
658 | ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
659 | { |
660 | Scope scope(b); |
661 | CHECK_STACK_LIMITS(scope.engine) |
662 | ScopedObject o(scope, thisObject->toObject(e: scope.engine)); |
663 | if (!o) |
664 | RETURN_UNDEFINED(); |
665 | |
666 | ScopedFunctionObject f(scope, o->get(name: scope.engine->id_toString())); |
667 | if (!f) |
668 | THROW_TYPE_ERROR(); |
669 | |
670 | return checkedResult(v4: scope.engine, result: f->call(thisObject, argv, argc)); |
671 | } |
672 | |
673 | ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) |
674 | { |
675 | return Encode(thisObject->toObject(e: b->engine())); |
676 | } |
677 | |
678 | ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
679 | { |
680 | Scope scope(b); |
681 | ScopedPropertyKey P(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(e: scope.engine)); |
682 | if (scope.engine->hasException) |
683 | return QV4::Encode::undefined(); |
684 | ScopedObject O(scope, thisObject->toObject(e: scope.engine)); |
685 | if (scope.engine->hasException) |
686 | return QV4::Encode::undefined(); |
687 | bool r = O->getOwnProperty(id: P) != Attr_Invalid; |
688 | return Encode(r); |
689 | } |
690 | |
691 | ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
692 | { |
693 | Scope scope(b); |
694 | if (!argc || !argv[0].isObject()) |
695 | return Encode(false); |
696 | |
697 | ScopedObject V(scope, argv[0]); |
698 | ScopedObject O(scope, thisObject->toObject(e: scope.engine)); |
699 | if (scope.engine->hasException) |
700 | return QV4::Encode::undefined(); |
701 | ScopedObject proto(scope, V->getPrototypeOf()); |
702 | while (proto) { |
703 | if (O->d() == proto->d()) |
704 | return Encode(true); |
705 | proto = proto->getPrototypeOf(); |
706 | } |
707 | return Encode(false); |
708 | } |
709 | |
710 | ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
711 | { |
712 | Scope scope(b); |
713 | ScopedPropertyKey p(scope, (argc ? argv[0] : Value::undefinedValue()).toPropertyKey(e: scope.engine)); |
714 | if (scope.engine->hasException) |
715 | return QV4::Encode::undefined(); |
716 | |
717 | ScopedObject o(scope, thisObject->toObject(e: scope.engine)); |
718 | if (scope.engine->hasException) |
719 | return QV4::Encode::undefined(); |
720 | PropertyAttributes attrs = o->getOwnProperty(id: p); |
721 | return Encode(attrs.isEnumerable()); |
722 | } |
723 | |
724 | ReturnedValue ObjectPrototype::method_defineGetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
725 | { |
726 | Scope scope(b); |
727 | if (argc < 2) |
728 | THROW_TYPE_ERROR(); |
729 | |
730 | ScopedFunctionObject f(scope, argv[1]); |
731 | if (!f) |
732 | THROW_TYPE_ERROR(); |
733 | |
734 | ScopedString prop(scope, argv[0], ScopedString::Convert); |
735 | if (scope.engine->hasException) |
736 | return QV4::Encode::undefined(); |
737 | |
738 | ScopedObject o(scope, thisObject); |
739 | if (!o) { |
740 | if (!thisObject->isUndefined()) |
741 | RETURN_UNDEFINED(); |
742 | o = scope.engine->globalObject; |
743 | } |
744 | |
745 | ScopedProperty pd(scope); |
746 | pd->value = f; |
747 | pd->set = Value::emptyValue(); |
748 | bool ok = o->defineOwnProperty(id: prop->toPropertyKey(), p: pd, attrs: Attr_Accessor); |
749 | if (!ok) |
750 | THROW_TYPE_ERROR(); |
751 | RETURN_UNDEFINED(); |
752 | } |
753 | |
754 | ReturnedValue ObjectPrototype::method_defineSetter(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
755 | { |
756 | Scope scope(b); |
757 | if (argc < 2) |
758 | THROW_TYPE_ERROR(); |
759 | |
760 | ScopedFunctionObject f(scope, argv[1]); |
761 | if (!f) |
762 | THROW_TYPE_ERROR(); |
763 | |
764 | ScopedString prop(scope, argv[0], ScopedString::Convert); |
765 | if (scope.engine->hasException) |
766 | return QV4::Encode::undefined(); |
767 | |
768 | ScopedObject o(scope, thisObject); |
769 | if (!o) { |
770 | if (!thisObject->isUndefined()) |
771 | RETURN_UNDEFINED(); |
772 | o = scope.engine->globalObject; |
773 | } |
774 | |
775 | ScopedProperty pd(scope); |
776 | pd->value = Value::emptyValue(); |
777 | pd->set = f; |
778 | bool ok = o->defineOwnProperty(id: prop->toPropertyKey(), p: pd, attrs: Attr_Accessor); |
779 | if (!ok) |
780 | THROW_TYPE_ERROR(); |
781 | RETURN_UNDEFINED(); |
782 | } |
783 | |
784 | ReturnedValue ObjectPrototype::method_get_proto(const FunctionObject *b, const Value *thisObject, const Value *, int) |
785 | { |
786 | Scope scope(b); |
787 | ScopedObject o(scope, thisObject->as<Object>()); |
788 | if (!o) |
789 | THROW_TYPE_ERROR(); |
790 | |
791 | return Encode(o->getPrototypeOf()); |
792 | } |
793 | |
794 | ReturnedValue ObjectPrototype::method_set_proto(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc) |
795 | { |
796 | Scope scope(b); |
797 | ScopedObject o(scope, thisObject); |
798 | if (!o || !argc || (!argv[0].isObject() && !argv[0].isNull())) |
799 | THROW_TYPE_ERROR(); |
800 | |
801 | const Object *p = argv[0].isNull() ? nullptr : static_cast<const Object *>(argv); |
802 | bool ok = o->setPrototypeOf(p); |
803 | if (!ok) |
804 | return scope.engine->throwTypeError(QStringLiteral("Could not change prototype." )); |
805 | return Encode::undefined(); |
806 | RETURN_UNDEFINED(); |
807 | } |
808 | |
809 | void ObjectPrototype::toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs) |
810 | { |
811 | Scope scope(engine); |
812 | ScopedObject o(scope, v); |
813 | if (!o) { |
814 | engine->throwTypeError(); |
815 | return; |
816 | } |
817 | |
818 | attrs->clear(); |
819 | desc->value = Value::emptyValue(); |
820 | desc->set = Value::emptyValue(); |
821 | ScopedValue tmp(scope); |
822 | |
823 | if (o->hasProperty(id: engine->id_enumerable()->toPropertyKey())) |
824 | attrs->setEnumerable((tmp = o->get(name: engine->id_enumerable()))->toBoolean()); |
825 | |
826 | if (o->hasProperty(id: engine->id_configurable()->toPropertyKey())) |
827 | attrs->setConfigurable((tmp = o->get(name: engine->id_configurable()))->toBoolean()); |
828 | |
829 | if (o->hasProperty(id: engine->id_get()->toPropertyKey())) { |
830 | ScopedValue get(scope, o->get(name: engine->id_get())); |
831 | FunctionObject *f = get->as<FunctionObject>(); |
832 | if (f || get->isUndefined()) { |
833 | desc->value = get; |
834 | } else { |
835 | engine->throwTypeError(); |
836 | return; |
837 | } |
838 | attrs->setType(PropertyAttributes::Accessor); |
839 | } |
840 | |
841 | if (o->hasProperty(id: engine->id_set()->toPropertyKey())) { |
842 | ScopedValue set(scope, o->get(name: engine->id_set())); |
843 | FunctionObject *f = set->as<FunctionObject>(); |
844 | if (f || set->isUndefined()) { |
845 | desc->set = set; |
846 | } else { |
847 | engine->throwTypeError(); |
848 | return; |
849 | } |
850 | attrs->setType(PropertyAttributes::Accessor); |
851 | } |
852 | |
853 | if (o->hasProperty(id: engine->id_writable()->toPropertyKey())) { |
854 | if (attrs->isAccessor()) { |
855 | engine->throwTypeError(); |
856 | return; |
857 | } |
858 | attrs->setWritable((tmp = o->get(name: engine->id_writable()))->toBoolean()); |
859 | } |
860 | |
861 | if (o->hasProperty(id: engine->id_value()->toPropertyKey())) { |
862 | if (attrs->isAccessor()) { |
863 | engine->throwTypeError(); |
864 | return; |
865 | } |
866 | desc->value = o->get(name: engine->id_value()); |
867 | attrs->setType(PropertyAttributes::Data); |
868 | } |
869 | |
870 | if (attrs->isGeneric()) |
871 | desc->value = Value::emptyValue(); |
872 | } |
873 | |
874 | |
875 | ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs) |
876 | { |
877 | if (attrs.isEmpty()) |
878 | return Encode::undefined(); |
879 | |
880 | Scope scope(engine); |
881 | // Let obj be the result of creating a new object as if by the expression new Object() where Object |
882 | // is the standard built-in constructor with that name. |
883 | ScopedObject o(scope, engine->newObject()); |
884 | ScopedString s(scope); |
885 | ScopedValue v(scope); |
886 | |
887 | if (attrs.isData()) { |
888 | s = engine->newString(QStringLiteral("value" )); |
889 | o->put(name: s, v: desc->value); |
890 | v = Value::fromBoolean(b: attrs.isWritable()); |
891 | s = engine->newString(QStringLiteral("writable" )); |
892 | o->put(name: s, v); |
893 | } else { |
894 | v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined(); |
895 | s = engine->newString(QStringLiteral("get" )); |
896 | o->put(name: s, v); |
897 | v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined(); |
898 | s = engine->newString(QStringLiteral("set" )); |
899 | o->put(name: s, v); |
900 | } |
901 | v = Value::fromBoolean(b: attrs.isEnumerable()); |
902 | s = engine->newString(QStringLiteral("enumerable" )); |
903 | o->put(name: s, v); |
904 | v = Value::fromBoolean(b: attrs.isConfigurable()); |
905 | s = engine->newString(QStringLiteral("configurable" )); |
906 | o->put(name: s, v); |
907 | |
908 | return o.asReturnedValue(); |
909 | } |
910 | |
911 | // es6: GetOwnPropertyKeys |
912 | Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) |
913 | { |
914 | Scope scope(v4); |
915 | ScopedArrayObject array(scope, v4->newArrayObject()); |
916 | ScopedObject O(scope, o.toObject(e: v4)); |
917 | if (O) { |
918 | ObjectIterator it(scope, O, ObjectIterator::NoFlags); |
919 | ScopedValue name(scope); |
920 | while (1) { |
921 | name = it.nextPropertyNameAsString(); |
922 | if (name->isNull()) |
923 | break; |
924 | if (name->isSymbol()) |
925 | continue; |
926 | array->push_back(v: name); |
927 | } |
928 | } |
929 | return array->d(); |
930 | } |
931 | |