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