Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qv4object.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#include "qv4object_p.h"
6
7#include <private/qv4argumentsobject_p.h>
8#include <private/qv4identifiertable_p.h>
9#include <private/qv4jscall_p.h>
10#include <private/qv4lookup_p.h>
11#include <private/qv4memberdata_p.h>
12#include <private/qv4mm_p.h>
13#include <private/qv4proxy_p.h>
14#include <private/qv4scopedvalue_p.h>
15#include <private/qv4stackframe_p.h>
16#include <private/qv4stringobject_p.h>
17#include <private/qv4symbol_p.h>
18
19#include <QtCore/qloggingcategory.h>
20
21#include <stdint.h>
22
23using namespace QV4;
24using namespace Qt::Literals::StringLiterals;
25
26Q_STATIC_LOGGING_CATEGORY(lcJavaScriptGlobals, "qt.qml.js.globals")
27
28DEFINE_OBJECT_VTABLE(Object);
29
30void Object::setInternalClass(Heap::InternalClass *ic)
31{
32 Q_ASSERT(ic && ic->vtable);
33 Heap::Object *p = d();
34
35 if (ic->numRedundantTransitions < p->internalClass.get()->numRedundantTransitions) {
36 // IC was rebuilt. The indices are different now. We need to move everything.
37
38 Scope scope(engine());
39
40 // We allocate before setting the new IC. Protect it from GC.
41 Scoped<InternalClass> newIC(scope, ic);
42
43 // Pick the members of the old IC that are still valid in the new IC.
44 // Order them by index in memberData (or inline data).
45 Scoped<MemberData> newMembers(scope, MemberData::allocate(scope.engine, ic->size));
46 for (uint i = 0; i < ic->size; ++i) {
47 // Note that some members might have been deleted. The key may be invalid.
48 const PropertyKey key = ic->nameMap.at(i);
49 // fromReturnedValue is safe, we directly write it through the WriteBarrier
50 newMembers->set(scope.engine, i,
51 QV4::Value::fromReturnedValue(key.isValid() ? get(key) : Encode::undefined()));
52 }
53
54 p->internalClass.set(scope.engine, ic);
55 const uint nInline = p->vtable()->nInlineProperties;
56
57 if (ic->size > nInline)
58 p->memberData.set(scope.engine, MemberData::allocate(ic->engine, ic->size - nInline));
59 else
60 p->memberData.set(scope.engine, nullptr);
61
62 const auto &memberValues = newMembers->d()->values;
63 for (uint i = 0; i < ic->size; ++i)
64 setProperty(i, memberValues[i]);
65 } else {
66 // The old indices are still the same. No need to move any values.
67 // We may need to re-allocate, though.
68
69 p->internalClass.set(ic->engine, ic);
70 const uint nInline = p->vtable()->nInlineProperties;
71 if (ic->size > nInline) {
72 const uint requiredSize = ic->size - nInline;
73 if ((p->memberData ? p->memberData->values.size : 0) < requiredSize) {
74 p->memberData.set(ic->engine, MemberData::allocate(
75 ic->engine, requiredSize, p->memberData));
76 }
77 }
78 }
79
80 // Before the engine is done initializing, we cannot have any lookups.
81 // Therefore, there is no point in updating the proto IDs.
82 if (ic->engine->isInitialized && ic->isUsedAsProto())
83 ic->updateProtoUsage(p);
84
85}
86
87void Object::getProperty(const InternalClassEntry &entry, Property *p) const
88{
89 p->value = *propertyData(entry.index);
90 if (entry.attributes.isAccessor())
91 p->set = *propertyData(entry.setterIndex);
92}
93
94void Object::setProperty(const InternalClassEntry &entry, const Property *p)
95{
96 setProperty(entry.index, p->value);
97 if (entry.attributes.isAccessor())
98 setProperty(entry.setterIndex, p->set);
99}
100
101void Heap::Object::setUsedAsProto()
102{
103 internalClass.set(internalClass->engine, internalClass->asProtoClass());
104}
105
106ReturnedValue Object::getValueAccessor(const Value *thisObject, const Value &v, PropertyAttributes attrs)
107{
108 if (!attrs.isAccessor())
109 return v.asReturnedValue();
110 const QV4::FunctionObject *f = v.as<FunctionObject>();
111 if (!f)
112 return Encode::undefined();
113
114 Scope scope(f->engine());
115 JSCallArguments jsCallData(scope);
116 if (thisObject)
117 *jsCallData.thisObject = *thisObject;
118 return checkedResult(scope.engine, f->call(jsCallData));
119}
120
121bool Object::putValue(uint memberIndex, PropertyAttributes attrs, const Value &value)
122{
123 Heap::InternalClass *ic = internalClass();
124 if (ic->engine->hasException)
125 return false;
126
127 if (attrs.isAccessor()) {
128 const FunctionObject *set = propertyData(memberIndex)->as<FunctionObject>();
129 if (set) {
130 Scope scope(ic->engine);
131 ScopedFunctionObject setter(scope, set);
132 JSCallArguments jsCallData(scope, 1);
133 jsCallData.args[0] = value;
134 *jsCallData.thisObject = this;
135 setter->call(jsCallData);
136 return !ic->engine->hasException;
137 }
138 return false;
139 }
140
141 if (!attrs.isWritable())
142 return false;
143
144 setProperty(memberIndex, value);
145 return true;
146}
147
148void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes)
149{
150 ExecutionEngine *e = engine();
151 Scope scope(e);
152 ScopedString s(scope, e->newIdentifier(name));
153 defineDefaultProperty(s, value, attributes);
154}
155
156void Object::defineDefaultProperty(const QString &name, VTable::Call code,
157 int argumentCount, PropertyAttributes attributes)
158{
159 ExecutionEngine *e = engine();
160 Scope scope(e);
161 ScopedString s(scope, e->newIdentifier(name));
162 ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount));
163 defineDefaultProperty(s, function, attributes);
164}
165
166void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code,
167 int argumentCount, PropertyAttributes attributes)
168{
169 ExecutionEngine *e = engine();
170 Scope scope(e);
171 ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount));
172 defineDefaultProperty(nameOrSymbol, function, attributes);
173}
174
175void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter)
176{
177 ExecutionEngine *e = engine();
178 Scope scope(e);
179 ScopedString s(scope, e->newIdentifier(name));
180 defineAccessorProperty(s, getter, setter);
181}
182
183void Object::defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter)
184{
185 ExecutionEngine *v4 = engine();
186 QV4::Scope scope(v4);
187 ScopedProperty p(scope);
188 QString n = name->toQString();
189 if (!n.isEmpty() && n.at(0) == '@'_L1)
190 n = '['_L1 + QStringView{n}.mid(1) + ']'_L1;
191 if (getter) {
192 ScopedString getName(scope, v4->newString("get "_L1 + n));
193 p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0)));
194 } else {
195 p->setGetter(nullptr);
196 }
197 if (setter) {
198 ScopedString setName(scope, v4->newString("set "_L1 + n));
199 p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0)));
200 } else {
201 p->setSetter(nullptr);
202 }
203 insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable);
204}
205
206
207
208void Object::defineReadonlyProperty(const QString &name, const Value &value)
209{
210 QV4::ExecutionEngine *e = engine();
211 Scope scope(e);
212 ScopedString s(scope, e->newIdentifier(name));
213 defineReadonlyProperty(s, value);
214}
215
216void Object::defineReadonlyProperty(String *name, const Value &value)
217{
218 insertMember(name, value, Attr_ReadOnly);
219}
220
221void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value)
222{
223 QV4::ExecutionEngine *e = engine();
224 Scope scope(e);
225 ScopedString s(scope, e->newIdentifier(name));
226 defineReadonlyConfigurableProperty(s, value);
227}
228
229void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value)
230{
231 insertMember(name, value, Attr_ReadOnly_ButConfigurable);
232}
233
234void Object::addSymbolSpecies()
235{
236 Scope scope(engine());
237 ScopedProperty p(scope);
238 p->setGetter(scope.engine->getSymbolSpecies());
239 p->setSetter(nullptr);
240 insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable);
241}
242
243void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
244{
245 Base::markObjects(b, stack);
246 Object *o = static_cast<Object *>(b);
247 if (o->memberData)
248 o->memberData->mark(stack);
249 if (o->arrayData)
250 o->arrayData->mark(stack);
251 uint nInline = o->vtable()->nInlineProperties;
252 Value *v = reinterpret_cast<Value *>(o) + o->vtable()->inlinePropertyOffset;
253 const Value *end = v + nInline;
254 while (v < end) {
255 v->mark(stack);
256 ++v;
257 }
258}
259
260void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes)
261{
262 InternalClassEntry idx;
263 PropertyKey key = s->toPropertyKey();
264 Heap::InternalClass::addMember(this, key, attributes, &idx);
265
266 setProperty(idx.index, p->value);
267 if (attributes.isAccessor())
268 setProperty(idx.setterIndex, p->set);
269}
270
271void Object::setPrototypeUnchecked(const Object *p)
272{
273 setInternalClass(internalClass()->changePrototype(p ? p->d() : nullptr));
274}
275
276// Section 8.12.2
277PropertyIndex Object::getValueOrSetter(PropertyKey id, PropertyAttributes *attrs)
278{
279 if (id.isArrayIndex()) {
280 uint index = id.asArrayIndex();
281 Heap::Object *o = d();
282 while (o) {
283 if (o->arrayData) {
284 uint idx = o->arrayData->mappedIndex(index);
285 if (idx != UINT_MAX) {
286 *attrs = o->arrayData->attributes(index);
287 return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) };
288 }
289 }
290 if (o->vtable()->type == Type_StringObject) {
291 if (index < static_cast<const Heap::StringObject *>(o)->length()) {
292 // this is an evil hack, but it works, as the method is only ever called from put,
293 // where we don't use the returned pointer there for non writable attributes
294 *attrs = (Attr_NotWritable|Attr_NotConfigurable);
295 return { reinterpret_cast<Heap::ArrayData *>(0x1), nullptr };
296 }
297 }
298 o = o->prototype();
299 }
300 } else {
301 Heap::Object *o = d();
302 while (o) {
303 auto idx = o->internalClass->findValueOrSetter(id);
304 if (idx.isValid()) {
305 *attrs = idx.attrs;
306 return o->writablePropertyData(idx.index);
307 }
308
309 o = o->prototype();
310 }
311 }
312 *attrs = Attr_Invalid;
313 return { nullptr, nullptr };
314}
315
316ReturnedValue Object::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty)
317{
318 return static_cast<const Object *>(m)->internalGet(id, receiver, hasProperty);
319}
320
321bool Object::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver)
322{
323 return static_cast<Object *>(m)->internalPut(id, value, receiver);
324}
325
326bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
327{
328 return static_cast<Object *>(m)->internalDeleteProperty(id);
329}
330
331PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
332{
333 if (arrayIndex != UINT_MAX && o->arrayData()) {
334 SparseArrayNode *arrayNode = nullptr;
335 if (o->arrayType() == Heap::ArrayData::Sparse) {
336 SparseArray *sparse = o->arrayData()->sparse;
337 arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
338 }
339
340 // sparse arrays
341 if (arrayNode) {
342 while (arrayNode != o->sparseEnd()) {
343 uint k = arrayNode->key();
344 uint pidx = arrayNode->value;
345 Heap::SparseArrayData *sa = o->d()->arrayData.cast<Heap::SparseArrayData>();
346 const Property *p = reinterpret_cast<const Property *>(sa->values.data() + pidx);
347 arrayNode = arrayNode->nextNode();
348 PropertyAttributes a = sa->attrs ? sa->attrs[pidx] : Attr_Data;
349 arrayIndex = k + 1;
350 if (pd)
351 pd->copy(p, a);
352 if (attrs)
353 *attrs = a;
354 return PropertyKey::fromArrayIndex(k);
355 }
356 arrayIndex = UINT_MAX;
357 }
358 // dense arrays
359 while (arrayIndex < o->d()->arrayData->values.size) {
360 Heap::SimpleArrayData *sa = o->d()->arrayData.cast<Heap::SimpleArrayData>();
361 const Value &val = sa->data(arrayIndex);
362 PropertyAttributes a = o->arrayData()->attributes(arrayIndex);
363 int index = arrayIndex;
364 ++arrayIndex;
365 if (!val.isEmpty()) {
366 if (pd)
367 pd->value = val;
368 if (attrs)
369 *attrs = a;
370 return PropertyKey::fromArrayIndex(index);
371 }
372 }
373 arrayIndex = UINT_MAX;
374 }
375
376 while (true) {
377 while (memberIndex < o->internalClass()->size) {
378 PropertyKey n = o->internalClass()->nameMap.at(memberIndex);
379 ++memberIndex;
380 if (!n.isStringOrSymbol())
381 // accessor properties have a dummy entry with n == 0
382 continue;
383 if (!iterateOverSymbols && n.isSymbol())
384 continue;
385 if (iterateOverSymbols && !n.isSymbol())
386 continue;
387
388 InternalClassEntry e = o->internalClass()->find(n);
389 if (!e.isValid())
390 continue;
391 if (pd) {
392 pd->value = *o->propertyData(e.index);
393 if (e.attributes.isAccessor())
394 pd->set = *o->propertyData(e.setterIndex);
395 }
396 if (attrs)
397 *attrs = e.attributes;
398 return n;
399 }
400 if (iterateOverSymbols)
401 break;
402 iterateOverSymbols = true;
403 memberIndex = 0;
404 }
405
406 return PropertyKey::invalid();
407}
408
409OwnPropertyKeyIterator *Object::virtualOwnPropertyKeys(const Object *o, Value *target)
410{
411 *target = *o;
412 return new ObjectOwnPropertyKeyIterator;
413}
414
415// Section 8.12.3
416ReturnedValue Object::internalGet(PropertyKey id, const Value *receiver, bool *hasProperty) const
417{
418 Heap::Object *o = d();
419
420 if (id.isArrayIndex()) {
421 const uint index = id.asArrayIndex();
422 Scope scope(this);
423 PropertyAttributes attrs;
424 ScopedProperty pd(scope);
425 while (1) {
426 if (o->arrayData && o->arrayData->getProperty(index, pd, &attrs)) {
427 if (hasProperty)
428 *hasProperty = true;
429 return Object::getValue(receiver, pd->value, attrs);
430 }
431 if (o->internalClass->vtable->type == Type_StringObject) {
432 ScopedString str(scope, static_cast<Heap::StringObject *>(o)->getIndex(index));
433 if (str) {
434 attrs = (Attr_NotWritable|Attr_NotConfigurable);
435 if (hasProperty)
436 *hasProperty = true;
437 return str.asReturnedValue();
438 }
439 }
440 o = o->prototype();
441 if (!o || o->internalClass->vtable->get != Object::virtualGet)
442 break;
443 }
444 } else {
445 while (1) {
446 auto idx = o->internalClass->findValueOrGetter(id);
447 if (idx.isValid()) {
448 if (hasProperty)
449 *hasProperty = true;
450 return Object::getValue(receiver, *o->propertyData(idx.index), idx.attrs);
451 }
452 o = o->prototype();
453 if (!o || o->internalClass->vtable->get != Object::virtualGet)
454 break;
455 }
456 }
457
458 if (o) {
459 const Value v = Value::fromHeapObject(o);
460 const Object &obj = static_cast<const Object &>(v);
461 return obj.get(id, receiver, hasProperty);
462 }
463
464 if (hasProperty)
465 *hasProperty = false;
466 return Encode::undefined();
467}
468
469// Section 8.12.5
470bool Object::internalPut(PropertyKey id, const Value &value, Value *receiver)
471{
472 Scope scope(this);
473 if (scope.hasException())
474 return false;
475
476 Object *r = receiver->objectValue();
477 if (r && r->d() == d()) {
478 // receiver and this object are the same
479 if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) {
480 // This object standard methods in the vtable, so we can take a shortcut
481 // and avoid the calls to getOwnProperty and defineOwnProperty
482
483 PropertyAttributes attrs;
484 PropertyIndex propertyIndex{nullptr, nullptr};
485
486 if (id.isArrayIndex()) {
487 if (arrayData())
488 propertyIndex = arrayData()->getValueOrSetter(id.asArrayIndex(), &attrs);
489 } else {
490 auto member = internalClass()->findValueOrSetter(id);
491 if (member.isValid()) {
492 attrs = member.attrs;
493 propertyIndex = d()->writablePropertyData(member.index);
494 }
495 }
496
497 if (!propertyIndex.isNull() && !attrs.isAccessor()) {
498 if (!attrs.isWritable())
499 return false;
500 else if (isArrayObject() && id == scope.engine->id_length()->propertyKey()) {
501 bool ok;
502 uint l = value.asArrayLength(&ok);
503 if (!ok) {
504 scope.engine->throwRangeError(value);
505 return false;
506 }
507 ok = setArrayLength(l);
508 if (!ok)
509 return false;
510 } else {
511 propertyIndex.set(scope.engine, value);
512 }
513 return true;
514 }
515 }
516 }
517
518 ScopedProperty p(scope);
519 PropertyAttributes attrs;
520 attrs = getOwnProperty(id, p);
521 if (attrs == Attr_Invalid) {
522 ScopedObject p(scope, getPrototypeOf());
523 if (p)
524 return p->put(id, value, receiver);
525 attrs = Attr_Data;
526 }
527
528 if (attrs.isAccessor()) {
529 ScopedFunctionObject setter(scope, p->setter());
530 if (!setter)
531 return false;
532 JSCallArguments jsCallData(scope, 1);
533 jsCallData.args[0] = value;
534 *jsCallData.thisObject = *receiver;
535 setter->call(jsCallData);
536 return !scope.hasException();
537 }
538
539 // Data property
540 if (!attrs.isWritable())
541 return false;
542 if (!r)
543 return false;
544 attrs = r->getOwnProperty(id, p);
545
546 if (attrs != Attr_Invalid) {
547 if (attrs.isAccessor() || !attrs.isWritable())
548 return false;
549 } else {
550 if (!r->isExtensible())
551 return false;
552 attrs = Attr_Data;
553 }
554
555 if (r->internalClass()->vtable->defineOwnProperty == virtualDefineOwnProperty) {
556 // standard object, we can avoid some more checks
557 if (id.isArrayIndex()) {
558 r->arraySet(id.asArrayIndex(), value);
559 } else {
560 ScopedStringOrSymbol s(scope, id.asStringOrSymbol());
561 r->insertMember(s, value);
562 }
563 return true;
564 }
565
566 p->value = value;
567 return r->defineOwnProperty(id, p, attrs);
568}
569
570// Section 8.12.7
571bool Object::internalDeleteProperty(PropertyKey id)
572{
573 if (internalClass()->engine->hasException)
574 return false;
575
576 if (id.isArrayIndex()) {
577 uint index = id.asArrayIndex();
578 Scope scope(engine());
579 if (scope.hasException())
580 return false;
581
582 Scoped<ArrayData> ad(scope, arrayData());
583 if (!ad || ad->vtable()->del(this, index))
584 return true;
585
586 return false;
587 }
588
589 auto memberIdx = internalClass()->findValueOrGetter(id);
590 if (memberIdx.isValid()) {
591 if (memberIdx.attrs.isConfigurable()) {
592 Heap::InternalClass::removeMember(this, id);
593 return true;
594 }
595 return false;
596 }
597
598 return true;
599}
600
601bool Object::internalDefineOwnProperty(ExecutionEngine *engine, uint index, const InternalClassEntry *memberEntry, const Property *p, PropertyAttributes attrs)
602{
603 // clause 5
604 if (attrs.isEmpty())
605 return true;
606
607 Scope scope(engine);
608 ScopedProperty current(scope);
609 PropertyAttributes cattrs;
610 if (memberEntry) {
611 getProperty(*memberEntry, current);
612 cattrs = memberEntry->attributes;
613 } else if (arrayData()) {
614 arrayData()->getProperty(index, current, &cattrs);
615 cattrs = arrayData()->attributes(index);
616 }
617
618 // clause 6
619 if (p->isSubset(attrs, current, cattrs))
620 return true;
621
622 // clause 7
623 if (!cattrs.isConfigurable()) {
624 if (attrs.isConfigurable())
625 return false;
626 if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable())
627 return false;
628 }
629
630 // clause 8
631 if (attrs.isGeneric() || current->value.isEmpty())
632 goto accept;
633
634 // clause 9
635 if (cattrs.isData() != attrs.isData()) {
636 // 9a
637 if (!cattrs.isConfigurable())
638 return false;
639 if (cattrs.isData()) {
640 // 9b
641 cattrs.setType(PropertyAttributes::Accessor);
642 cattrs.clearWritable();
643 if (!memberEntry) {
644 // need to convert the array and the slot
645 initSparseArray();
646 Q_ASSERT(arrayData());
647 setArrayAttributes(index, cattrs);
648 }
649 current->setGetter(nullptr);
650 current->setSetter(nullptr);
651 } else {
652 // 9c
653 cattrs.setType(PropertyAttributes::Data);
654 cattrs.setWritable(false);
655 if (!memberEntry) {
656 // need to convert the array and the slot
657 setArrayAttributes(index, cattrs);
658 }
659 current->value = Value::undefinedValue();
660 }
661 } else if (cattrs.isData() && attrs.isData()) { // clause 10
662 if (!cattrs.isConfigurable() && !cattrs.isWritable()) {
663 if (attrs.isWritable() || !current->value.sameValue(p->value))
664 return false;
665 }
666 } else { // clause 10
667 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor());
668 if (!cattrs.isConfigurable()) {
669 if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue())
670 return false;
671 if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue())
672 return false;
673 }
674 }
675
676 accept:
677
678 current->merge(cattrs, p, attrs);
679 if (memberEntry) {
680 PropertyKey key = internalClass()->nameMap.at(memberEntry->index);
681 InternalClassEntry e;
682 Heap::InternalClass::changeMember(this, key, cattrs, &e);
683 setProperty(e, current);
684 } else {
685 setArrayAttributes(index, cattrs);
686 arrayData()->setProperty(scope.engine, index, current);
687 }
688 return true;
689}
690
691void Object::copyArrayData(Object *other)
692{
693 Q_ASSERT(isArrayObject());
694 Scope scope(engine());
695
696 if (other->protoHasArray() || ArgumentsObject::isNonStrictArgumentsObject(other) ||
697 (other->arrayType() == Heap::ArrayData::Sparse && other->arrayData()->attrs)) {
698 uint len = other->getLength();
699 Q_ASSERT(len);
700
701 ScopedValue v(scope);
702 for (uint i = 0; i < len; ++i) {
703 arraySet(i, (v = other->get(i)));
704 }
705 } else if (!other->arrayData()) {
706 ;
707 } else {
708 Q_ASSERT(!arrayData() && other->arrayData());
709 ArrayData::realloc(this, static_cast<ArrayData::Type>(other->d()->arrayData->type),
710 other->d()->arrayData->values.alloc, false);
711 if (other->arrayType() == Heap::ArrayData::Sparse) {
712 Heap::ArrayData *od = other->d()->arrayData;
713 Heap::ArrayData *dd = d()->arrayData;
714 dd->sparse = new SparseArray(*od->sparse);
715 } else {
716 Heap::ArrayData *dd = d()->arrayData;
717 dd->values.size = other->d()->arrayData->values.size;
718 dd->offset = other->d()->arrayData->offset;
719 }
720 // ### need a write barrier
721 memcpy(d()->arrayData->values.values, other->d()->arrayData->values.values, other->d()->arrayData->values.alloc*sizeof(Value));
722 }
723 setArrayLengthUnchecked(other->getLength());
724}
725
726qint64 Object::virtualGetLength(const Managed *m)
727{
728 Scope scope(static_cast<const Object *>(m)->engine());
729 ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length()));
730 return v->toLength();
731}
732
733// 'var' is 'V' in 15.3.5.3.
734ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var)
735{
736 QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
737
738 // 15.3.5.3, Assume F is a Function object.
739 const FunctionObject *function = typeObject->as<FunctionObject>();
740 if (!function)
741 return engine->throwTypeError();
742
743 return checkedInstanceOf(engine, function, var);
744}
745
746ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
747{
748 // Otherwise we cannot trust the protoIds
749 Q_ASSERT(engine->isInitialized);
750
751 Heap::Object *obj = object->d();
752 PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
753 if (object->as<QV4::ProxyObject>()) {
754 // proxies invalidate assumptions that we normally maek in lookups
755 // so we always need to use the fallback path
756 lookup->call = Lookup::Call::GetterQObjectPropertyFallback;
757 return lookup->getter(engine, *object);
758 }
759 if (name.isArrayIndex()) {
760 lookup->indexedLookup.index = name.asArrayIndex();
761 lookup->call = Lookup::Call::GetterIndexed;
762 return lookup->getter(engine, *object);
763 }
764
765 auto index = obj->internalClass->findValueOrGetter(name);
766 if (index.isValid()) {
767 PropertyAttributes attrs = index.attrs;
768 uint nInline = obj->vtable()->nInlineProperties;
769 if (attrs.isData()) {
770 if (index.index < obj->vtable()->nInlineProperties) {
771 index.index += obj->vtable()->inlinePropertyOffset;
772 lookup->call = Lookup::Call::Getter0Inline;
773 } else {
774 index.index -= nInline;
775 lookup->call = Lookup::Call::Getter0MemberData;
776 }
777 } else {
778 lookup->call = Lookup::Call::GetterAccessor;
779 }
780 lookup->objectLookup.ic.set(engine, obj->internalClass.get());
781 lookup->objectLookup.offset = index.index;
782 return lookup->getter(engine, *object);
783 }
784
785 lookup->protoLookup.protoId = obj->internalClass->protoId;
786 lookup->resolveProtoGetter(name, obj->prototype());
787 return lookup->getter(engine, *object);
788}
789
790bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
791{
792 // Otherwise we cannot trust the protoIds
793 Q_ASSERT(engine->isInitialized);
794
795 Scope scope(engine);
796 ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
797
798 Heap::InternalClass *c = object->internalClass();
799 PropertyKey key = name->toPropertyKey();
800 auto idx = c->findValueOrSetter(key);
801 if (idx.isValid()) {
802 if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
803 Q_ASSERT(!idx.attrs.isAccessor());
804 lookup->call = Lookup::Call::SetterArrayLength;
805 return lookup->setter(engine, *object, value);
806 } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
807 lookup->objectLookup.ic.set(engine, object->internalClass());
808 lookup->objectLookup.index = idx.index;
809 const auto nInline = object->d()->vtable()->nInlineProperties;
810 if (idx.index < nInline) {
811 lookup->call = Lookup::Call::Setter0Inline;
812 lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
813 } else {
814 lookup->call = Lookup::Call::Setter0MemberData;
815 lookup->objectLookup.offset = idx.index - nInline;
816 }
817 return lookup->setter(engine, *object, value);
818 } else {
819 // ### handle setter
820 lookup->call = Lookup::Call::SetterQObjectPropertyFallback;
821 }
822 return lookup->setter(engine, *object, value);
823 }
824
825 lookup->insertionLookup.protoId = c->protoId;
826 if (!object->put(key, value)) {
827 lookup->call = Lookup::Call::SetterQObjectPropertyFallback;
828 return false;
829 }
830
831 if (object->internalClass() == c) {
832 // ### setter in the prototype, should handle this
833 lookup->call = Lookup::Call::SetterQObjectPropertyFallback;
834 return true;
835 }
836 idx = object->internalClass()->findValueOrSetter(key);
837 if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
838 lookup->call = Lookup::Call::SetterQObjectPropertyFallback;
839 return false;
840 }
841 lookup->insertionLookup.newClass.set(engine, object->internalClass());
842 lookup->insertionLookup.offset = idx.index;
843 lookup->call = Lookup::Call::SetterInsert;
844 return true;
845}
846
847/*!
848 * \internal
849 *
850 * This method is modeled after \l{QMetaObject::metacall}. It takes a JavaScript
851 * \a object and performs \a call on it, using \a index as identifier for the
852 * property/method/etc to be used and \a a as arguments. Like
853 * \l{QMetaObject::metacall} this method is overly generic in order to reduce the
854 * API surface. On a plain Object it does nothing and returns 0. Specific
855 * objects can override it and do some meaningful work. If the metacall succeeds
856 * they should return a non-0 value. Otherwise they should return 0.
857 *
858 * Most prominently, \l QMetaObject::ReadProperty and \l QMetaObject::WriteProperty
859 * calls can be used to manipulate properties of QObjects and Q_GADGETs wrapped
860 * by QV4::QObjectWrapper, QV4::QQmlTypeWrapper and QV4::QQmlValueTypeWrapper.
861 * They can also be used to manipulate elements in QV4::Sequence.
862 */
863int Object::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
864{
865 Q_UNUSED(object);
866 Q_UNUSED(call);
867 Q_UNUSED(index);
868 Q_UNUSED(a);
869 return 0;
870}
871
872ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
873{
874 Scope scope(engine);
875 if (f->isBoundFunction()) {
876 ScopedValue v(scope, static_cast<const BoundFunction *>(f)->target());
877 f = v->as<FunctionObject>();
878 }
879
880 // 15.3.5.3, 1: HasInstance can only be used on an object
881 const Object *lhs = var.as<Object>();
882 if (!lhs)
883 return Encode(false);
884
885 // 15.3.5.3, 2
886 Value p = Value::fromReturnedValue(f->protoProperty());
887 const Object *o = p.objectValue();
888 if (!o) // 15.3.5.3, 3
889 return f->engine()->throwTypeError();
890
891 Heap::Object *v = lhs->d();
892
893 // 15.3.5.3, 4
894 while (v) {
895 // 15.3.5.3, 4, a
896 v = v->prototype();
897
898 // 15.3.5.3, 4, b
899 if (!v)
900 break; // will return false
901
902 // 15.3.5.3, 4, c
903 else if (o->d() == v)
904 return Encode(true);
905 }
906
907 return Encode(false);
908}
909
910bool Object::virtualHasProperty(const Managed *m, PropertyKey id)
911{
912 Scope scope(m->engine());
913 ScopedObject o(scope, m);
914 ScopedProperty p(scope);
915
916 if (o->getOwnProperty(id, p) != Attr_Invalid)
917 return true;
918
919 o = o->getPrototypeOf();
920 if (o)
921 return o->hasProperty(id);
922
923 return false;
924}
925
926PropertyAttributes Object::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
927{
928 PropertyAttributes attrs;
929 const Object *o = static_cast<const Object *>(m);
930 if (id.isArrayIndex()) {
931 uint index = id.asArrayIndex();
932 if (o->arrayData()) {
933 if (o->arrayData()->getProperty(index, p, &attrs))
934 return attrs;
935 }
936 } else {
937 Q_ASSERT(id.asStringOrSymbol());
938
939 auto member = o->internalClass()->find(id);
940 if (member.isValid()) {
941 attrs = member.attributes;
942 if (p) {
943 p->value = *o->propertyData(member.index);
944 if (attrs.isAccessor())
945 p->set = *o->propertyData(member.setterIndex);
946 }
947 return attrs;
948 }
949 }
950
951 return Attr_Invalid;
952}
953
954bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
955{
956 Object *o = static_cast<Object *>(m);
957 Scope scope(o);
958
959 if (id.isArrayIndex()) {
960 uint index = id.asArrayIndex();
961
962 bool hasProperty = false;
963
964 if (o->arrayData()) {
965 hasProperty = o->arrayData()->mappedIndex(index) != UINT_MAX;
966 if (!hasProperty && o->isStringObject())
967 hasProperty = (index < static_cast<StringObject *>(o)->length());
968 }
969
970 if (!hasProperty) {
971 if (!o->isExtensible())
972 return false;
973
974 ScopedProperty pp(scope);
975 pp->copy(p, attrs);
976 pp->fullyPopulated(&attrs);
977 if (attrs == Attr_Data) {
978 ScopedValue v(scope, pp->value);
979 o->arraySet(index, v);
980 } else {
981 o->arraySet(index, pp, attrs);
982 }
983 return true;
984 }
985
986 return o->internalDefineOwnProperty(scope.engine, index, nullptr, p, attrs);
987 }
988
989 Scoped<InternalClass> ic(scope, o->internalClass());
990 auto memberIndex = ic->d()->find(id);
991
992 if (!memberIndex.isValid()) {
993 if (!o->isExtensible())
994 return false;
995
996 // If the IC is locked, you're not allowed to shadow any unconfigurable properties.
997 if (ic->d()->isLocked()) {
998 while (Heap::Object *prototype = ic->d()->prototype) {
999 ic = prototype->internalClass;
1000 const auto entry = ic->d()->find(id);
1001 if (entry.isValid()) {
1002 if (entry.attributes.isConfigurable())
1003 break;
1004 qCWarning(lcJavaScriptGlobals).noquote()
1005 << QStringLiteral("You cannot shadow the locked property "
1006 "'%1' in QML.").arg(id.toQString());
1007 return false;
1008 }
1009 }
1010 }
1011
1012 Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
1013 ScopedProperty pd(scope);
1014 pd->copy(p, attrs);
1015 pd->fullyPopulated(&attrs);
1016 o->insertMember(name, pd, attrs);
1017 return true;
1018 }
1019
1020 return o->internalDefineOwnProperty(scope.engine, UINT_MAX, &memberIndex, p, attrs);
1021}
1022
1023bool Object::virtualIsExtensible(const Managed *m)
1024{
1025 return m->d()->internalClass->isExtensible();
1026}
1027
1028bool Object::virtualPreventExtensions(Managed *m)
1029{
1030 Q_ASSERT(m->isObject());
1031 Object *o = static_cast<Object *>(m);
1032 o->setInternalClass(o->internalClass()->nonExtensible());
1033 return true;
1034}
1035
1036Heap::Object *Object::virtualGetPrototypeOf(const Managed *m)
1037{
1038 return m->internalClass()->prototype;
1039}
1040
1041bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
1042{
1043 Q_ASSERT(m->isObject());
1044 Object *o = static_cast<Object *>(m);
1045 Heap::InternalClass *ic = o->internalClass();
1046 Heap::Object *current = ic->prototype;
1047 Heap::Object *protod = proto ? proto->d() : nullptr;
1048 if (current == protod)
1049 return true;
1050 if (!ic->isExtensible() || ic->isLocked())
1051 return false;
1052 Heap::Object *p = protod;
1053 while (p) {
1054 if (p == o->d())
1055 return false;
1056 if (p->vtable()->getPrototypeOf != Object::staticVTable()->getPrototypeOf)
1057 break;
1058 p = p->prototype();
1059 }
1060 o->setInternalClass(ic->changePrototype(protod));
1061 return true;
1062}
1063
1064bool Object::setArrayLength(uint newLen)
1065{
1066 Q_ASSERT(isArrayObject());
1067 if (!internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
1068 return false;
1069 uint oldLen = getLength();
1070 bool ok = true;
1071 if (newLen < oldLen) {
1072 if (arrayData()) {
1073 uint l = arrayData()->vtable()->truncate(this, newLen);
1074 if (l != newLen)
1075 ok = false;
1076 newLen = l;
1077 }
1078 } else {
1079 if (newLen >= 0x100000)
1080 initSparseArray();
1081 else
1082 ArrayData::realloc(this, arrayType(), newLen, false);
1083 }
1084 setArrayLengthUnchecked(newLen);
1085 return ok;
1086}
1087
1088void Object::initSparseArray()
1089{
1090 if (arrayType() == Heap::ArrayData::Sparse)
1091 return;
1092
1093 ArrayData::realloc(this, Heap::ArrayData::Sparse, 0, false);
1094}
1095
1096bool Object::isConcatSpreadable() const
1097{
1098 Scope scope(this);
1099 ScopedValue spreadable(scope, get(scope.engine->symbol_isConcatSpreadable()));
1100 if (!spreadable->isUndefined())
1101 return spreadable->toBoolean();
1102 return isArray();
1103}
1104
1105bool Object::isArray() const
1106{
1107 if (isArrayObject())
1108 return true;
1109 if (vtable() == ProxyObject::staticVTable()) {
1110 const ProxyObject *p = static_cast<const ProxyObject *>(this);
1111 Scope scope(this);
1112 if (!p->d()->handler) {
1113 scope.engine->throwTypeError();
1114 return false;
1115 }
1116 ScopedObject o(scope, p->d()->target);
1117 return o->isArray();
1118 }
1119 return false;
1120}
1121
1122const FunctionObject *Object::speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const
1123{
1124 ScopedValue C(scope, get(scope.engine->id_constructor()));
1125 if (C->isUndefined())
1126 return defaultConstructor;
1127 const Object *c = C->objectValue();
1128 if (!c) {
1129 scope.engine->throwTypeError();
1130 return nullptr;
1131 }
1132 ScopedValue S(scope, c->get(scope.engine->symbol_species()));
1133 if (S->isNullOrUndefined())
1134 return defaultConstructor;
1135 const FunctionObject *f = S->as<FunctionObject>();
1136 if (!f || !f->isConstructor()) {
1137 scope.engine->throwTypeError();
1138 return nullptr;
1139 }
1140 Q_ASSERT(f->isFunctionObject());
1141 return static_cast<const FunctionObject *>(f);
1142}
1143
1144bool Object::setProtoFromNewTarget(const Value *newTarget)
1145{
1146 if (!newTarget || newTarget->isUndefined())
1147 return false;
1148
1149 Q_ASSERT(newTarget->isFunctionObject());
1150 Scope scope(this);
1151 ScopedObject proto(scope, static_cast<const FunctionObject *>(newTarget)->protoProperty());
1152 if (proto) {
1153 setPrototypeOf(proto);
1154 return true;
1155 }
1156 return false;
1157}
1158
1159
1161
1162void Heap::ArrayObject::init(const QStringList &list)
1163{
1164 Object::init();
1165 commonInit();
1166 Scope scope(internalClass->engine);
1167 ScopedObject a(scope, this);
1168
1169 // Converts a QStringList to JS.
1170 // The result is a new Array object with length equal to the length
1171 // of the QStringList, and the elements being the QStringList's
1172 // elements converted to JS Strings.
1173 int len = list.size();
1174 a->arrayReserve(len);
1175 ScopedValue v(scope);
1176 for (int ii = 0; ii < len; ++ii)
1177 a->arrayPut(ii, (v = scope.engine->newString(list.at(ii))));
1178 a->setArrayLengthUnchecked(len);
1179}
1180
1181qint64 ArrayObject::virtualGetLength(const Managed *m)
1182{
1183 const ArrayObject *a = static_cast<const ArrayObject *>(m);
1184 return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength();
1185}
1186
1187QStringList ArrayObject::toQStringList() const
1188{
1189 QStringList result;
1190
1191 QV4::ExecutionEngine *engine = internalClass()->engine;
1192 Scope scope(engine);
1193 ScopedValue v(scope);
1194
1195 uint length = getLength();
1196 result.reserve(length);
1197 for (uint i = 0; i < length; ++i) {
1198 v = const_cast<ArrayObject *>(this)->get(i);
1199 result.append(v->toQStringNoThrow());
1200 }
1201 return result;
1202}
1203
1204bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
1205{
1206 Q_ASSERT(m->isArrayObject());
1207 ArrayObject *a = static_cast<ArrayObject *>(m);
1208
1209 if (id.isArrayIndex()) {
1210 uint index = id.asArrayIndex();
1211 uint len = a->getLength();
1212 if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
1213 return false;
1214
1215 bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs);
1216 if (!succeeded)
1217 return false;
1218
1219 if (index >= len)
1220 a->setArrayLengthUnchecked(index + 1);
1221
1222 return true;
1223 }
1224
1225 ExecutionEngine *engine = m->engine();
1226 if (id == engine->id_length()->propertyKey()) {
1227 Scope scope(engine);
1228 Q_ASSERT(a->internalClass()->verifyIndex(engine->id_length()->propertyKey(), Heap::ArrayObject::LengthPropertyIndex));
1229 ScopedProperty lp(scope);
1230 InternalClassEntry e = a->internalClass()->find(scope.engine->id_length()->propertyKey());
1231 a->getProperty(e, lp);
1232 if (attrs.isEmpty() || p->isSubset(attrs, lp, e.attributes))
1233 return true;
1234 if (!e.attributes.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
1235 return false;
1236 bool succeeded = true;
1237 if (attrs.type() == PropertyAttributes::Data) {
1238 bool ok;
1239 uint l = p->value.asArrayLength(&ok);
1240 if (!ok) {
1241 ScopedValue v(scope, p->value);
1242 engine->throwRangeError(v);
1243 return false;
1244 }
1245 succeeded = a->setArrayLength(l);
1246 }
1247 if (attrs.hasWritable() && !attrs.isWritable()) {
1248 e.attributes.setWritable(false);
1249 Heap::InternalClass::changeMember(a, engine->id_length()->propertyKey(), e.attributes);
1250 }
1251 if (!succeeded)
1252 return false;
1253 return true;
1254 }
1255 return Object::virtualDefineOwnProperty(m, id, p, attrs);
1256}
Definition qjsvalue.h:23
Q_STATIC_LOGGING_CATEGORY(lcAccessibilityCore, "qt.accessibility.core")
DEFINE_OBJECT_VTABLE(ArrayObject)