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 | |
17 | using namespace QV4; |
18 | |
19 | |
20 | DEFINE_OBJECT_VTABLE(ObjectCtor); |
21 | |
22 | void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) |
23 | { |
24 | Heap::FunctionObject::init(scope, QStringLiteral("Object" )); |
25 | } |
26 | |
27 | ReturnedValue 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 | |
43 | ReturnedValue 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 | |
53 | void 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 | |
94 | ReturnedValue 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 | |
108 | ReturnedValue 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 | |
117 | ReturnedValue 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 | |
140 | ReturnedValue 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 | |
169 | ReturnedValue 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 | |
182 | ReturnedValue 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 |
209 | ReturnedValue 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 | |
256 | ReturnedValue 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 | |
278 | ReturnedValue 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 | |
302 | ReturnedValue 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 | |
338 | ReturnedValue 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 | |
377 | ReturnedValue 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 | |
399 | ReturnedValue 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 | |
426 | ReturnedValue 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 | |
440 | ReturnedValue 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 | |
472 | ReturnedValue 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 | |
504 | ReturnedValue 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 | |
517 | ReturnedValue 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 | |
542 | ReturnedValue 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 | |
559 | ReturnedValue 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 | |
587 | ReturnedValue 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 | |
619 | ReturnedValue 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 | |
634 | ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Value *thisObject, const Value *, int) |
635 | { |
636 | return Encode(thisObject->toObject(e: b->engine())); |
637 | } |
638 | |
639 | ReturnedValue 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 | |
652 | ReturnedValue 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 | |
671 | ReturnedValue 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 | |
685 | ReturnedValue 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 | |
715 | ReturnedValue 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 | |
745 | ReturnedValue 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 | |
755 | ReturnedValue 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 | |
770 | void 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 | |
836 | ReturnedValue 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 |
873 | Heap::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 | |