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