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

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

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