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
26DEFINE_OBJECT_VTABLE(Object);
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(e: scope.engine, n: ic->size));
44 for (uint i = 0; i < ic->size; ++i)
45 newMembers->set(e: scope.engine, index: i, v: get(id: ic->nameMap.at(i)));
46
47 p->internalClass.set(e: scope.engine, newVal: ic);
48 const uint nInline = p->vtable()->nInlineProperties;
49
50 if (ic->size > nInline)
51 p->memberData.set(e: scope.engine, newVal: MemberData::allocate(e: ic->engine, n: ic->size - nInline));
52 else
53 p->memberData.set(e: scope.engine, newVal: nullptr);
54
55 const auto &memberValues = newMembers->d()->values;
56 for (uint i = 0; i < ic->size; ++i)
57 setProperty(index: i, v: 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(e: ic->engine, newVal: 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(e: ic->engine, newVal: MemberData::allocate(
68 e: ic->engine, n: requiredSize, old: 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(o: p);
77
78}
79
80void Object::getProperty(const InternalClassEntry &entry, Property *p) const
81{
82 p->value = *propertyData(index: entry.index);
83 if (entry.attributes.isAccessor())
84 p->set = *propertyData(index: entry.setterIndex);
85}
86
87void Object::setProperty(const InternalClassEntry &entry, const Property *p)
88{
89 setProperty(index: entry.index, v: p->value);
90 if (entry.attributes.isAccessor())
91 setProperty(index: entry.setterIndex, v: p->set);
92}
93
94void Heap::Object::setUsedAsProto()
95{
96 internalClass.set(e: internalClass->engine, newVal: internalClass->asProtoClass());
97}
98
99ReturnedValue Object::getValueAccessor(const Value *thisObject, const Value &v, PropertyAttributes attrs)
100{
101 if (!attrs.isAccessor())
102 return v.asReturnedValue();
103 const QV4::FunctionObject *f = v.as<FunctionObject>();
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(v4: scope.engine, result: f->call(data: 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(index: 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(data: jsCallData);
129 return !ic->engine->hasException;
130 }
131 return false;
132 }
133
134 if (!attrs.isWritable())
135 return false;
136
137 setProperty(index: memberIndex, v: value);
138 return true;
139}
140
141void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes)
142{
143 ExecutionEngine *e = engine();
144 Scope scope(e);
145 ScopedString s(scope, e->newIdentifier(text: name));
146 defineDefaultProperty(name: s, value, attributes);
147}
148
149void Object::defineDefaultProperty(const QString &name, VTable::Call code,
150 int argumentCount, PropertyAttributes attributes)
151{
152 ExecutionEngine *e = engine();
153 Scope scope(e);
154 ScopedString s(scope, e->newIdentifier(text: name));
155 ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(engine: e, nameOrSymbol: s, code, argumentCount));
156 defineDefaultProperty(name: s, value: function, attributes);
157}
158
159void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, VTable::Call code,
160 int argumentCount, PropertyAttributes attributes)
161{
162 ExecutionEngine *e = engine();
163 Scope scope(e);
164 ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(engine: e, nameOrSymbol, code, argumentCount));
165 defineDefaultProperty(name: nameOrSymbol, value: function, attributes);
166}
167
168void Object::defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter)
169{
170 ExecutionEngine *e = engine();
171 Scope scope(e);
172 ScopedString s(scope, e->newIdentifier(text: name));
173 defineAccessorProperty(name: 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(i: 0) == QLatin1Char('@'))
183 n = QChar::fromLatin1(c: '[') + QStringView{n}.mid(pos: 1) + QChar::fromLatin1(c: ']');
184 if (getter) {
185 ScopedString getName(scope, v4->newString(s: QString::fromLatin1(ba: "get ") + n));
186 p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(engine: v4, nameOrSymbol: getName, code: getter, argumentCount: 0)));
187 } else {
188 p->setGetter(nullptr);
189 }
190 if (setter) {
191 ScopedString setName(scope, v4->newString(s: QString::fromLatin1(ba: "set ") + n));
192 p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(engine: v4, nameOrSymbol: setName, code: setter, argumentCount: 0)));
193 } else {
194 p->setSetter(nullptr);
195 }
196 insertMember(s: name, p, attributes: QV4::Attr_Accessor|QV4::Attr_NotEnumerable);
197}
198
199
200
201void Object::defineReadonlyProperty(const QString &name, const Value &value)
202{
203 QV4::ExecutionEngine *e = engine();
204 Scope scope(e);
205 ScopedString s(scope, e->newIdentifier(text: name));
206 defineReadonlyProperty(name: s, value);
207}
208
209void Object::defineReadonlyProperty(String *name, const Value &value)
210{
211 insertMember(s: name, v: value, attributes: Attr_ReadOnly);
212}
213
214void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value)
215{
216 QV4::ExecutionEngine *e = engine();
217 Scope scope(e);
218 ScopedString s(scope, e->newIdentifier(text: name));
219 defineReadonlyConfigurableProperty(name: s, value);
220}
221
222void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value)
223{
224 insertMember(s: name, v: value, attributes: Attr_ReadOnly_ButConfigurable);
225}
226
227void Object::addSymbolSpecies()
228{
229 Scope scope(engine());
230 ScopedProperty p(scope);
231 p->setGetter(scope.engine->getSymbolSpecies());
232 p->setSetter(nullptr);
233 insertMember(s: scope.engine->symbol_species(), p, attributes: QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable);
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(markStack: stack);
242 if (o->arrayData)
243 o->arrayData->mark(markStack: 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(markStack: stack);
249 ++v;
250 }
251}
252
253void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes)
254{
255 InternalClassEntry idx;
256 PropertyKey key = s->toPropertyKey();
257 Heap::InternalClass::addMember(object: this, id: key, data: attributes, entry: &idx);
258
259 setProperty(index: idx.index, v: p->value);
260 if (attributes.isAccessor())
261 setProperty(index: idx.setterIndex, v: p->set);
262}
263
264void Object::setPrototypeUnchecked(const Object *p)
265{
266 setInternalClass(internalClass()->changePrototype(proto: 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(i: index);
280 return { .base: o->arrayData , .slot: 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
287 *attrs = (Attr_NotWritable|Attr_NotConfigurable);
288 return { .base: reinterpret_cast<Heap::ArrayData *>(0x1), .slot: 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(index: idx.index);
300 }
301
302 o = o->prototype();
303 }
304 }
305 *attrs = Attr_Invalid;
306 return { .base: nullptr, .slot: 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
324PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
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(akey: 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(other: p, attrs: a);
345 if (attrs)
346 *attrs = a;
347 return PropertyKey::fromArrayIndex(idx: k);
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(index: arrayIndex);
355 PropertyAttributes a = o->arrayData()->attributes(i: arrayIndex);
356 int index = arrayIndex;
357 ++arrayIndex;
358 if (!val.isEmpty()) {
359 if (pd)
360 pd->value = val;
361 if (attrs)
362 *attrs = a;
363 return PropertyKey::fromArrayIndex(idx: index);
364 }
365 }
366 arrayIndex = UINT_MAX;
367 }
368
369 while (true) {
370 while (memberIndex < o->internalClass()->size) {
371 PropertyKey n = o->internalClass()->nameMap.at(i: 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(id: n);
382 if (!e.isValid())
383 continue;
384 if (pd) {
385 pd->value = *o->propertyData(index: e.index);
386 if (e.attributes.isAccessor())
387 pd->set = *o->propertyData(index: e.setterIndex);
388 }
389 if (attrs)
390 *attrs = e.attributes;
391 return n;
392 }
393 if (iterateOverSymbols)
394 break;
395 iterateOverSymbols = true;
396 memberIndex = 0;
397 }
398
399 return PropertyKey::invalid();
400}
401
402OwnPropertyKeyIterator *Object::virtualOwnPropertyKeys(const Object *o, Value *target)
403{
404 *target = *o;
405 return new ObjectOwnPropertyKeyIterator;
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);
416 PropertyAttributes attrs;
417 ScopedProperty pd(scope);
418 while (1) {
419 if (o->arrayData && o->arrayData->getProperty(index, p: pd, attrs: &attrs)) {
420 if (hasProperty)
421 *hasProperty = true;
422 return Object::getValue(thisObject: receiver, v: 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) {
427 attrs = (Attr_NotWritable|Attr_NotConfigurable);
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(thisObject: receiver, v: *o->propertyData(index: idx.index), attrs: idx.attrs);
444 }
445 o = o->prototype();
446 if (!o || o->internalClass->vtable->get != Object::virtualGet)
447 break;
448 }
449 }
450
451 if (o) {
452 const Value v = Value::fromHeapObject(m: 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
472 if (d()->internalClass->vtable->getOwnProperty == Object::virtualGetOwnProperty) {
473 // This object standard methods in the vtable, so we can take a shortcut
474 // and avoid the calls to getOwnProperty and defineOwnProperty
475
476 PropertyAttributes attrs;
477 PropertyIndex propertyIndex{.base: nullptr, .slot: nullptr};
478
479 if (id.isArrayIndex()) {
480 if (arrayData())
481 propertyIndex = arrayData()->getValueOrSetter(index: id.asArrayIndex(), attrs: &attrs);
482 } else {
483 auto member = internalClass()->findValueOrSetter(id);
484 if (member.isValid()) {
485 attrs = member.attrs;
486 propertyIndex = d()->writablePropertyData(index: 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: &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(e: scope.engine, newVal: value);
505 }
506 return true;
507 }
508 }
509 }
510
511 ScopedProperty p(scope);
512 PropertyAttributes attrs;
513 attrs = getOwnProperty(id, p);
514 if (attrs == Attr_Invalid) {
515 ScopedObject p(scope, getPrototypeOf());
516 if (p)
517 return p->put(id, v: value, receiver);
518 attrs = Attr_Data;
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(data: 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;
545 attrs = Attr_Data;
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(index: id.asArrayIndex(), value);
552 } else {
553 ScopedStringOrSymbol s(scope, id.asStringOrSymbol());
554 r->insertMember(s, v: 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{
566 if (internalClass()->engine->hasException)
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()) {
585 Heap::InternalClass::removeMember(object: this, identifier: id);
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(entry: *memberEntry, p: current);
605 cattrs = memberEntry->attributes;
606 } else if (arrayData()) {
607 arrayData()->getProperty(index, p: current, attrs: &cattrs);
608 cattrs = arrayData()->attributes(i: index);
609 }
610
611 // clause 6
612 if (p->isSubset(attrs, other: current, otherAttrs: 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
634 cattrs.setType(PropertyAttributes::Accessor);
635 cattrs.clearWritable();
636 if (!memberEntry) {
637 // need to convert the array and the slot
638 initSparseArray();
639 Q_ASSERT(arrayData());
640 setArrayAttributes(i: index, a: cattrs);
641 }
642 current->setGetter(nullptr);
643 current->setSetter(nullptr);
644 } else {
645 // 9c
646 cattrs.setType(PropertyAttributes::Data);
647 cattrs.setWritable(false);
648 if (!memberEntry) {
649 // need to convert the array and the slot
650 setArrayAttributes(i: index, a: 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(other: 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(attrs&: cattrs, other: p, otherAttrs: attrs);
672 if (memberEntry) {
673 PropertyKey key = internalClass()->nameMap.at(i: memberEntry->index);
674 InternalClassEntry e;
675 Heap::InternalClass::changeMember(object: this, id: key, data: cattrs, entry: &e);
676 setProperty(entry: e, p: current);
677 } else {
678 setArrayAttributes(i: index, a: cattrs);
679 arrayData()->setProperty(e: scope.engine, index, p: current);
680 }
681 return true;
682}
683
684void Object::copyArrayData(Object *other)
685{
686 Q_ASSERT(isArrayObject());
687 Scope scope(engine());
688
689 if (other->protoHasArray() || ArgumentsObject::isNonStrictArgumentsObject(m: other) ||
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(index: i, value: (v = other->get(idx: i)));
697 }
698 } else if (!other->arrayData()) {
699 ;
700 } else {
701 Q_ASSERT(!arrayData() && other->arrayData());
702 ArrayData::realloc(o: this, newType: static_cast<ArrayData::Type>(other->d()->arrayData->type),
703 alloc: other->d()->arrayData->values.alloc, enforceAttributes: 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(dest: d()->arrayData->values.values, src: other->d()->arrayData->values.values, n: other->d()->arrayData->values.alloc*sizeof(Value));
715 }
716 setArrayLengthUnchecked(other->getLength());
717}
718
719qint64 Object::virtualGetLength(const Managed *m)
720{
721 Scope scope(static_cast<const Object *>(m)->engine());
722 ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(name: scope.engine->id_length()));
723 return v->toLength();
724}
725
726// 'var' is 'V' in 15.3.5.3.
727ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &var)
728{
729 QV4::ExecutionEngine *engine = typeObject->internalClass()->engine;
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
736 return checkedInstanceOf(engine, typeObject: function, var);
737}
738
739ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
740{
741 // Otherwise we cannot trust the protoIds
742 Q_ASSERT(engine->isInitialized);
743
744 Heap::Object *obj = object->d();
745 PropertyKey name = engine->identifierTable->asPropertyKey(str: engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
746 if (name.isArrayIndex()) {
747 lookup->indexedLookup.index = name.asArrayIndex();
748 lookup->getter = Lookup::getterIndexed;
749 return lookup->getter(lookup, engine, *object);
750 }
751
752 auto index = obj->internalClass->findValueOrGetter(id: name);
753 if (index.isValid()) {
754 PropertyAttributes attrs = index.attrs;
755 uint nInline = obj->vtable()->nInlineProperties;
756 if (attrs.isData()) {
757 if (index.index < obj->vtable()->nInlineProperties) {
758 index.index += obj->vtable()->inlinePropertyOffset;
759 lookup->getter = Lookup::getter0Inline;
760 } else {
761 index.index -= nInline;
762 lookup->getter = Lookup::getter0MemberData;
763 }
764 } else {
765 lookup->getter = Lookup::getterAccessor;
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, proto: obj->prototype());
774 return lookup->getter(lookup, engine, *object);
775}
776
777bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
778{
779 // Otherwise we cannot trust the protoIds
780 Q_ASSERT(engine->isInitialized);
781
782 Scope scope(engine);
783 ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
784
785 Heap::InternalClass *c = object->internalClass();
786 PropertyKey key = name->toPropertyKey();
787 auto idx = c->findValueOrSetter(id: key);
788 if (idx.isValid()) {
789 if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
790 Q_ASSERT(!idx.attrs.isAccessor());
791 lookup->setter = Lookup::arrayLengthSetter;
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) {
798 lookup->setter = Lookup::setter0Inline;
799 lookup->objectLookup.offset = idx.index + object->d()->vtable()->inlinePropertyOffset;
800 } else {
801 lookup->setter = Lookup::setter0MemberData;
802 lookup->objectLookup.offset = idx.index - nInline;
803 }
804 return lookup->setter(lookup, engine, *object, value);
805 } else {
806 // ### handle setter
807 lookup->setter = Lookup::setterFallback;
808 }
809 return lookup->setter(lookup, engine, *object, value);
810 }
811
812 lookup->insertionLookup.protoId = c->protoId;
813 if (!object->put(id: key, v: value)) {
814 lookup->setter = Lookup::setterFallback;
815 return false;
816 }
817
818 if (object->internalClass() == c) {
819 // ### setter in the prototype, should handle this
820 lookup->setter = Lookup::setterFallback;
821 return true;
822 }
823 idx = object->internalClass()->findValueOrSetter(id: key);
824 if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
825 lookup->setter = Lookup::setterFallback;
826 return false;
827 }
828 lookup->insertionLookup.newClass = object->internalClass();
829 lookup->insertionLookup.offset = idx.index;
830 lookup->setter = Lookup::setterInsert;
831 return true;
832}
833
834/*!
835 * \internal
836 *
837 * This method is modeled after \l{QMetaObject::metacall}. It takes a JavaScript
838 * \a object and performs \a call on it, using \a index as identifier for the
839 * property/method/etc to be used and \a a as arguments. Like
840 * \l{QMetaObject::metacall} this method is overly generic in order to reduce the
841 * API surface. On a plain Object it does nothing and returns 0. Specific
842 * objects can override it and do some meaningful work. If the metacall succeeds
843 * they should return a non-0 value. Otherwise they should return 0.
844 *
845 * Most prominently, \l QMetaObject::ReadProperty and \l QMetaObject::WriteProperty
846 * calls can be used to manipulate properties of QObjects and Q_GADGETs wrapped
847 * by QV4::QObjectWrapper, QV4::QQmlTypeWrapper and QV4::QQmlValueTypeWrapper.
848 * They can also be used to manipulate elements in QV4::Sequence.
849 */
850int Object::virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a)
851{
852 Q_UNUSED(object);
853 Q_UNUSED(call);
854 Q_UNUSED(index);
855 Q_UNUSED(a);
856 return 0;
857}
858
859ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
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(val: 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
897bool Object::virtualHasProperty(const Managed *m, PropertyKey id)
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
913PropertyAttributes Object::virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p)
914{
915 PropertyAttributes attrs;
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: &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(index: member.index);
931 if (attrs.isAccessor())
932 p->set = *o->propertyData(index: member.setterIndex);
933 }
934 return attrs;
935 }
936 }
937
938 return Attr_Invalid;
939}
940
941bool Object::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
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(other: p, attrs);
963 pp->fullyPopulated(attrs: &attrs);
964 if (attrs == Attr_Data) {
965 ScopedValue v(scope, pp->value);
966 o->arraySet(index, value: v);
967 } else {
968 o->arraySet(index, p: pp, attributes: attrs);
969 }
970 return true;
971 }
972
973 return o->internalDefineOwnProperty(engine: scope.engine, index, memberEntry: 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(a: id.toQString());
994 return false;
995 }
996 }
997 }
998
999 Scoped<StringOrSymbol> name(scope, id.asStringOrSymbol());
1000 ScopedProperty pd(scope);
1001 pd->copy(other: p, attrs);
1002 pd->fullyPopulated(attrs: &attrs);
1003 o->insertMember(s: name, p: pd, attributes: attrs);
1004 return true;
1005 }
1006
1007 return o->internalDefineOwnProperty(engine: scope.engine, UINT_MAX, memberEntry: &memberIndex, p, attrs);
1008}
1009
1010bool Object::virtualIsExtensible(const Managed *m)
1011{
1012 return m->d()->internalClass->isExtensible();
1013}
1014
1015bool Object::virtualPreventExtensions(Managed *m)
1016{
1017 Q_ASSERT(m->isObject());
1018 Object *o = static_cast<Object *>(m);
1019 o->setInternalClass(o->internalClass()->nonExtensible());
1020 return true;
1021}
1022
1023Heap::Object *Object::virtualGetPrototypeOf(const Managed *m)
1024{
1025 return m->internalClass()->prototype;
1026}
1027
1028bool Object::virtualSetPrototypeOf(Managed *m, const Object *proto)
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(proto: protod));
1048 return true;
1049}
1050
1051bool Object::setArrayLength(uint newLen)
1052{
1053 Q_ASSERT(isArrayObject());
1054 if (!internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
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)
1067 initSparseArray();
1068 else
1069 ArrayData::realloc(o: this, newType: arrayType(), alloc: newLen, enforceAttributes: false);
1070 }
1071 setArrayLengthUnchecked(newLen);
1072 return ok;
1073}
1074
1075void Object::initSparseArray()
1076{
1077 if (arrayType() == Heap::ArrayData::Sparse)
1078 return;
1079
1080 ArrayData::realloc(o: this, newType: Heap::ArrayData::Sparse, alloc: 0, enforceAttributes: false);
1081}
1082
1083bool Object::isConcatSpreadable() const
1084{
1085 Scope scope(this);
1086 ScopedValue spreadable(scope, get(name: scope.engine->symbol_isConcatSpreadable()));
1087 if (!spreadable->isUndefined())
1088 return spreadable->toBoolean();
1089 return isArray();
1090}
1091
1092bool Object::isArray() const
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(name: 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(name: 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
1131bool Object::setProtoFromNewTarget(const Value *newTarget)
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
1147DEFINE_OBJECT_VTABLE(ArrayObject);
1148
1149void Heap::ArrayObject::init(const QStringList &list)
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(n: len);
1162 ScopedValue v(scope);
1163 for (int ii = 0; ii < len; ++ii)
1164 a->arrayPut(index: ii, value: (v = scope.engine->newString(s: list.at(i: ii))));
1165 a->setArrayLengthUnchecked(len);
1166}
1167
1168qint64 ArrayObject::virtualGetLength(const Managed *m)
1169{
1170 const ArrayObject *a = static_cast<const ArrayObject *>(m);
1171 return a->propertyData(index: Heap::ArrayObject::LengthPropertyIndex)->toLength();
1172}
1173
1174QStringList ArrayObject::toQStringList() const
1175{
1176 QStringList result;
1177
1178 QV4::ExecutionEngine *engine = internalClass()->engine;
1179 Scope scope(engine);
1180 ScopedValue v(scope);
1181
1182 uint length = getLength();
1183 for (uint i = 0; i < length; ++i) {
1184 v = const_cast<ArrayObject *>(this)->get(idx: i);
1185 result.append(t: v->toQStringNoThrow());
1186 }
1187 return result;
1188}
1189
1190bool ArrayObject::virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs)
1191{
1192 Q_ASSERT(m->isArrayObject());
1193 ArrayObject *a = static_cast<ArrayObject *>(m);
1194
1195 if (id.isArrayIndex()) {
1196 uint index = id.asArrayIndex();
1197 uint len = a->getLength();
1198 if (index >= len && !a->internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable())
1199 return false;
1200
1201 bool succeeded = Object::virtualDefineOwnProperty(m, id, p, attrs);
1202 if (!succeeded)
1203 return false;
1204
1205 if (index >= len)
1206 a->setArrayLengthUnchecked(index + 1);
1207
1208 return true;
1209 }
1210
1211 ExecutionEngine *engine = m->engine();
1212 if (id == engine->id_length()->propertyKey()) {
1213 Scope scope(engine);
1214 Q_ASSERT(a->internalClass()->verifyIndex(engine->id_length()->propertyKey(), Heap::ArrayObject::LengthPropertyIndex));
1215 ScopedProperty lp(scope);
1216 InternalClassEntry e = a->internalClass()->find(id: scope.engine->id_length()->propertyKey());
1217 a->getProperty(entry: e, p: lp);
1218 if (attrs.isEmpty() || p->isSubset(attrs, other: lp, otherAttrs: e.attributes))
1219 return true;
1220 if (!e.attributes.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable())
1221 return false;
1222 bool succeeded = true;
1223 if (attrs.type() == PropertyAttributes::Data) {
1224 bool ok;
1225 uint l = p->value.asArrayLength(ok: &ok);
1226 if (!ok) {
1227 ScopedValue v(scope, p->value);
1228 engine->throwRangeError(value: v);
1229 return false;
1230 }
1231 succeeded = a->setArrayLength(l);
1232 }
1233 if (attrs.hasWritable() && !attrs.isWritable()) {
1234 e.attributes.setWritable(false);
1235 Heap::InternalClass::changeMember(object: a, id: engine->id_length()->propertyKey(), data: e.attributes);
1236 }
1237 if (!succeeded)
1238 return false;
1239 return true;
1240 }
1241 return Object::virtualDefineOwnProperty(m, id, p, attrs);
1242}
1243

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