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

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