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#ifndef QV4_OBJECT_H
4#define QV4_OBJECT_H
5
6//
7// W A R N I N G
8// -------------
9//
10// This file is not part of the Qt API. It exists purely as an
11// implementation detail. This header file may change from version to
12// version without notice, or even be removed.
13//
14// We mean it.
15//
16
17#include "qv4managed_p.h"
18#include "qv4memberdata_p.h"
19#include "qv4arraydata_p.h"
20#include "qv4engine_p.h"
21#include "qv4scopedvalue_p.h"
22#include "qv4value_p.h"
23#include "qv4internalclass_p.h"
24
25QT_BEGIN_NAMESPACE
26
27
28namespace QV4 {
29
30namespace Heap {
31
32#define ObjectMembers(class, Member) \
33 Member(class, Pointer, MemberData *, memberData) \
34 Member(class, Pointer, ArrayData *, arrayData)
35
36DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
37 static void markObjects(Heap::Base *base, MarkStack *stack);
38 void init() { Base::init(); }
39
40 const VTable *vtable() const {
41 return internalClass->vtable;
42 }
43
44 const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
45 Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
46 return reinterpret_cast<const Value *>(this) + indexWithOffset;
47 }
48 const Value *inlinePropertyData(uint index) const {
49 Q_ASSERT(index < vtable()->nInlineProperties);
50 return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
51 }
52 void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Value v) {
53 Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
54 Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset;
55 WriteBarrier::write(engine: e, base: this, slot: prop->data_ptr(), value: v.asReturnedValue());
56 }
57 void setInlinePropertyWithOffset(ExecutionEngine *e, uint indexWithOffset, Heap::Base *b) {
58 Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < uint(vtable()->inlinePropertyOffset + vtable()->nInlineProperties));
59 Value *prop = reinterpret_cast<Value *>(this) + indexWithOffset;
60 WriteBarrier::write(engine: e, base: this, slot: prop->data_ptr(), value: Value::fromHeapObject(m: b).asReturnedValue());
61 }
62
63 PropertyIndex writablePropertyData(uint index) {
64 uint nInline = vtable()->nInlineProperties;
65 if (index < nInline)
66 return PropertyIndex{ .base: this, .slot: reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
67 index -= nInline;
68 return PropertyIndex{ .base: memberData, .slot: memberData->values.values + index };
69 }
70
71 const Value *propertyData(uint index) const {
72 uint nInline = vtable()->nInlineProperties;
73 if (index < nInline)
74 return reinterpret_cast<const Value *>(this) + vtable()->inlinePropertyOffset + index;
75 index -= nInline;
76 return memberData->values.data() + index;
77 }
78 void setProperty(ExecutionEngine *e, uint index, Value v) {
79 uint nInline = vtable()->nInlineProperties;
80 if (index < nInline) {
81 setInlinePropertyWithOffset(e, indexWithOffset: index + vtable()->inlinePropertyOffset, v);
82 return;
83 }
84 index -= nInline;
85 memberData->values.set(e, index, v);
86 }
87 void setProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
88 uint nInline = vtable()->nInlineProperties;
89 if (index < nInline) {
90 setInlinePropertyWithOffset(e, indexWithOffset: index + vtable()->inlinePropertyOffset, b);
91 return;
92 }
93 index -= nInline;
94 memberData->values.set(e, index, b);
95 }
96
97 void setUsedAsProto();
98
99 Heap::Object *prototype() const { return internalClass->prototype; }
100};
101
102}
103
104struct Q_QML_EXPORT Object: Managed {
105 V4_OBJECT2(Object, Object)
106 Q_MANAGED_TYPE(Object)
107 V4_INTERNALCLASS(Object)
108 V4_PROTOTYPE(objectPrototype)
109
110 enum { NInlineProperties = 2 };
111
112 enum {
113 IsObject = true,
114 GetterOffset = 0,
115 SetterOffset = 1
116 };
117
118 void setInternalClass(Heap::InternalClass *ic);
119
120 const Value *propertyData(uint index) const { return d()->propertyData(index); }
121
122 Heap::ArrayData *arrayData() const { return d()->arrayData; }
123 void setArrayData(ArrayData *a) { d()->arrayData.set(e: engine(), newVal: a ? a->d() : nullptr); }
124
125 void getProperty(const InternalClassEntry &entry, Property *p) const;
126 void setProperty(const InternalClassEntry &entry, const Property *p);
127 void setProperty(uint index, Value v) const { d()->setProperty(e: engine(), index, v); }
128 void setProperty(uint index, Heap::Base *b) const { d()->setProperty(e: engine(), index, b); }
129 void setProperty(ExecutionEngine *engine, uint index, Value v) const { d()->setProperty(e: engine, index, v); }
130 void setProperty(ExecutionEngine *engine, uint index, Heap::Base *b) const { d()->setProperty(e: engine, index, b); }
131
132 const VTable *vtable() const { return d()->vtable(); }
133
134 PropertyAttributes getOwnProperty(PropertyKey id, Property *p = nullptr) const {
135 return vtable()->getOwnProperty(this, id, p);
136 }
137
138 PropertyIndex getValueOrSetter(PropertyKey id, PropertyAttributes *attrs);
139
140 bool hasProperty(PropertyKey id) const {
141 return vtable()->hasProperty(this, id);
142 }
143
144 bool defineOwnProperty(PropertyKey id, const Property *p, PropertyAttributes attrs) {
145 return vtable()->defineOwnProperty(this, id, p, attrs);
146 }
147
148 //
149 // helpers
150 //
151 static ReturnedValue getValue(const Value *thisObject, const Value &v, PropertyAttributes attrs) {
152 if (attrs.isData())
153 return v.asReturnedValue();
154 return getValueAccessor(thisObject, v, attrs);
155 }
156 ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const {
157 return getValue(thisObject: this, v, attrs);
158 }
159 ReturnedValue getValueByIndex(uint propertyIndex) const {
160 PropertyAttributes attrs = internalClass()->propertyData.at(i: propertyIndex);
161 const Value *v = propertyData(index: propertyIndex);
162 if (!attrs.isAccessor())
163 return v->asReturnedValue();
164 return getValueAccessor(thisObject: this, v: *v, attrs);
165 }
166 static ReturnedValue getValueAccessor(const Value *thisObject, const Value &v, PropertyAttributes attrs);
167
168 bool putValue(uint memberIndex, PropertyAttributes attrs, const Value &value);
169
170 /* The spec default: Writable: true, Enumerable: false, Configurable: true */
171 void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) {
172 insertMember(s: name, v: value, attributes);
173 }
174 void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
175 void defineDefaultProperty(const QString &name, VTable::Call code,
176 int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
177 void defineDefaultProperty(StringOrSymbol *name, VTable::Call code,
178 int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
179 void defineAccessorProperty(const QString &name, VTable::Call getter, VTable::Call setter);
180 void defineAccessorProperty(StringOrSymbol *name, VTable::Call getter, VTable::Call setter);
181 /* Fixed: Writable: false, Enumerable: false, Configurable: false */
182 void defineReadonlyProperty(const QString &name, const Value &value);
183 void defineReadonlyProperty(String *name, const Value &value);
184
185 /* Fixed: Writable: false, Enumerable: false, Configurable: true */
186 void defineReadonlyConfigurableProperty(const QString &name, const Value &value);
187 void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value);
188
189 void addSymbolSpecies();
190
191 void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
192 Scope scope(engine());
193 ScopedProperty p(scope);
194 p->value = v;
195 insertMember(s, p, attributes);
196 }
197 void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes);
198
199 bool isExtensible() const { return vtable()->isExtensible(this); }
200 bool preventExtensions() { return vtable()->preventExtensions(this); }
201 Heap::Object *getPrototypeOf() const { return vtable()->getPrototypeOf(this); }
202 bool setPrototypeOf(const Object *p) { return vtable()->setPrototypeOf(this, p); }
203 void setPrototypeUnchecked(const Object *p);
204
205 // Array handling
206
207public:
208 void copyArrayData(Object *other);
209
210 bool setArrayLength(uint newLen);
211 void setArrayLengthUnchecked(uint l);
212
213 void arraySet(uint index, const Property *p, PropertyAttributes attributes = Attr_Data);
214 void arraySet(uint index, const Value &value);
215
216 bool arrayPut(uint index, const Value &value) {
217 return arrayData()->vtable()->put(this, index, value);
218 }
219 bool arrayPut(uint index, const Value *values, uint n) {
220 return arrayData()->vtable()->putArray(this, index, values, n);
221 }
222 void setArrayAttributes(uint i, PropertyAttributes a) {
223 Q_ASSERT(arrayData());
224 if (d()->arrayData->attrs || a != Attr_Data) {
225 ArrayData::ensureAttributes(o: this);
226 a.resolve();
227 arrayData()->vtable()->setAttribute(this, i, a);
228 }
229 }
230
231 void push_back(const Value &v);
232
233 ArrayData::Type arrayType() const {
234 return arrayData() ? static_cast<ArrayData::Type>(d()->arrayData->type) : Heap::ArrayData::Simple;
235 }
236 // ### remove me
237 void setArrayType(ArrayData::Type t) {
238 Q_ASSERT(t != Heap::ArrayData::Simple && t != Heap::ArrayData::Sparse);
239 arrayCreate();
240 d()->arrayData->type = t;
241 }
242
243 inline void arrayReserve(uint n) {
244 ArrayData::realloc(o: this, newType: Heap::ArrayData::Simple, alloc: n, enforceAttributes: false);
245 }
246
247 void arrayCreate() {
248 if (!arrayData())
249 ArrayData::realloc(o: this, newType: Heap::ArrayData::Simple, alloc: 0, enforceAttributes: false);
250#ifdef CHECK_SPARSE_ARRAYS
251 initSparseArray();
252#endif
253 }
254
255 void initSparseArray();
256 SparseArrayNode *sparseBegin() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->begin() : nullptr; }
257 SparseArrayNode *sparseEnd() const { return arrayType() == Heap::ArrayData::Sparse ? d()->arrayData->sparse->end() : nullptr; }
258
259 inline bool protoHasArray() {
260 Scope scope(engine());
261 ScopedObject p(scope, this);
262
263 while ((p = p->getPrototypeOf()))
264 if (p->arrayData())
265 return true;
266
267 return false;
268 }
269
270 inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
271 { if (!receiver) receiver = this; return vtable()->get(this, name->toPropertyKey(), receiver, hasProperty); }
272 inline ReturnedValue get(uint idx, bool *hasProperty = nullptr, const Value *receiver = nullptr) const
273 { if (!receiver) receiver = this; return vtable()->get(this, PropertyKey::fromArrayIndex(idx), receiver, hasProperty); }
274 QT_DEPRECATED inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
275 { return get(idx, hasProperty); }
276 inline ReturnedValue get(PropertyKey id, const Value *receiver = nullptr, bool *hasProperty = nullptr) const
277 { if (!receiver) receiver = this; return vtable()->get(this, id, receiver, hasProperty); }
278
279 // use the set variants instead, to customize throw behavior
280 inline bool put(StringOrSymbol *name, const Value &v, Value *receiver = nullptr)
281 { if (!receiver) receiver = this; return vtable()->put(this, name->toPropertyKey(), v, receiver); }
282 inline bool put(uint idx, const Value &v, Value *receiver = nullptr)
283 { if (!receiver) receiver = this; return vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, receiver); }
284 QT_DEPRECATED inline bool putIndexed(uint idx, const Value &v)
285 { return put(idx, v); }
286 inline bool put(PropertyKey id, const Value &v, Value *receiver = nullptr)
287 { if (!receiver) receiver = this; return vtable()->put(this, id, v, receiver); }
288
289 enum ThrowOnFailure {
290 DoThrowOnRejection,
291 DoNotThrow
292 };
293
294 // This is the same as set(), but it doesn't require creating a string key,
295 // which is much more efficient for the array case.
296 inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow)
297 {
298 bool ret = vtable()->put(this, PropertyKey::fromArrayIndex(idx), v, this);
299 // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
300 if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
301 ExecutionEngine *e = engine();
302 if (!e->hasException) { // allow a custom set impl to throw itself
303 QString message = QLatin1String("Cannot assign to read-only property \"") +
304 QString::number(idx) + QLatin1Char('\"');
305 e->throwTypeError(message);
306 }
307 }
308 return ret;
309 }
310
311 // ES6: 7.3.3 Set (O, P, V, Throw)
312 inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow)
313 {
314 bool ret = vtable()->put(this, name->toPropertyKey(), v, this);
315 // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
316 if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
317 ExecutionEngine *e = engine();
318 if (!e->hasException) { // allow a custom set impl to throw itself
319 QString message = QLatin1String("Cannot assign to read-only property \"") +
320 name->toQString() + QLatin1Char('\"');
321 e->throwTypeError(message);
322 }
323 }
324 return ret;
325 }
326
327 bool deleteProperty(PropertyKey id)
328 { return vtable()->deleteProperty(this, id); }
329 OwnPropertyKeyIterator *ownPropertyKeys(Value *target) const
330 { return vtable()->ownPropertyKeys(this, target); }
331 qint64 getLength() const { return vtable()->getLength(this); }
332 ReturnedValue instanceOf(const Value &var) const
333 { return vtable()->instanceOf(this, var); }
334
335 bool isConcatSpreadable() const;
336 bool isArray() const;
337 const FunctionObject *speciesConstructor(Scope &scope, const FunctionObject *defaultConstructor) const;
338
339 bool setProtoFromNewTarget(const Value *newTarget);
340
341 ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
342 { return vtable()->resolveLookupGetter(this, engine, lookup); }
343 ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
344 { return vtable()->resolveLookupSetter(this, engine, lookup, value); }
345
346 int metacall(QMetaObject::Call call, int index, void **a)
347 { return vtable()->metacall(this, call, index, a); }
348
349protected:
350 static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
351 static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
352 static bool virtualDeleteProperty(Managed *m, PropertyKey id);
353 static bool virtualHasProperty(const Managed *m, PropertyKey id);
354 static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p);
355 static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
356 static bool virtualIsExtensible(const Managed *m);
357 static bool virtualPreventExtensions(Managed *);
358 static Heap::Object *virtualGetPrototypeOf(const Managed *);
359 static bool virtualSetPrototypeOf(Managed *, const Object *);
360 static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
361 static qint64 virtualGetLength(const Managed *m);
362 static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
363 static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
364 static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
365 static int virtualMetacall(Object *object, QMetaObject::Call call, int index, void **a);
366public:
367 // qv4runtime uses this directly
368 static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
369
370private:
371 bool internalDefineOwnProperty(ExecutionEngine *engine, uint index, const InternalClassEntry *memberEntry, const Property *p, PropertyAttributes attrs);
372 ReturnedValue internalGet(PropertyKey id, const Value *receiver, bool *hasProperty) const;
373 bool internalPut(PropertyKey id, const Value &value, Value *receiver);
374 bool internalDeleteProperty(PropertyKey id);
375
376 friend struct ObjectIterator;
377 friend struct ObjectPrototype;
378};
379
380struct Q_QML_PRIVATE_EXPORT ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
381{
382 uint arrayIndex = 0;
383 uint memberIndex = 0;
384 bool iterateOverSymbols = false;
385 ~ObjectOwnPropertyKeyIterator() override = default;
386 PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
387
388};
389
390namespace Heap {
391
392struct BooleanObject : Object {
393 void init() { Object::init(); }
394 void init(bool b) {
395 Object::init();
396 this->b = b;
397 }
398
399 bool b;
400};
401
402struct NumberObject : Object {
403 void init() { Object::init(); }
404 void init(double val) {
405 Object::init();
406 value = val;
407 }
408
409 double value;
410};
411
412struct ArrayObject : Object {
413 enum {
414 LengthPropertyIndex = 0
415 };
416
417 void init() {
418 Object::init();
419 commonInit();
420 }
421
422 void init(const QStringList &list);
423
424private:
425 void commonInit()
426 { setProperty(e: internalClass->engine, index: LengthPropertyIndex, v: Value::fromInt32(i: 0)); }
427};
428
429}
430
431struct BooleanObject: Object {
432 V4_OBJECT2(BooleanObject, Object)
433 Q_MANAGED_TYPE(BooleanObject)
434 V4_PROTOTYPE(booleanPrototype)
435
436 bool value() const { return d()->b; }
437
438};
439
440struct NumberObject: Object {
441 V4_OBJECT2(NumberObject, Object)
442 Q_MANAGED_TYPE(NumberObject)
443 V4_PROTOTYPE(numberPrototype)
444
445 double value() const { return d()->value; }
446};
447
448struct ArrayObject: Object {
449 V4_OBJECT2(ArrayObject, Object)
450 Q_MANAGED_TYPE(ArrayObject)
451 V4_INTERNALCLASS(ArrayObject)
452 V4_PROTOTYPE(arrayPrototype)
453
454 void init(ExecutionEngine *engine);
455
456 static qint64 virtualGetLength(const Managed *m);
457
458 QStringList toQStringList() const;
459protected:
460 static bool virtualDefineOwnProperty(Managed *m, PropertyKey id, const Property *p, PropertyAttributes attrs);
461
462};
463
464inline void Object::setArrayLengthUnchecked(uint l)
465{
466 if (isArrayObject())
467 setProperty(index: Heap::ArrayObject::LengthPropertyIndex, v: Value::fromUInt32(i: l));
468}
469
470inline void Object::push_back(const Value &v)
471{
472 arrayCreate();
473
474 const auto length = getLength();
475 if (Q_UNLIKELY(length == std::numeric_limits<uint>::max())) {
476 engine()->throwRangeError(message: QLatin1String("Too many elements."));
477 return;
478 }
479 uint idx = uint(length);
480 arrayReserve(n: idx + 1);
481 arrayPut(index: idx, value: v);
482 setArrayLengthUnchecked(idx + 1);
483}
484
485inline void Object::arraySet(uint index, const Property *p, PropertyAttributes attributes)
486{
487 // ### Clean up
488 arrayCreate();
489 if (attributes.isAccessor() || (index > 0x1000 && index > 2*d()->arrayData->values.alloc)) {
490 initSparseArray();
491 } else {
492 arrayData()->vtable()->reallocate(this, index + 1, false);
493 }
494 setArrayAttributes(i: index, a: attributes);
495 ArrayData::insert(o: this, index, v: &p->value, isAccessor: attributes.isAccessor());
496 if (isArrayObject() && index >= getLength())
497 setArrayLengthUnchecked(index + 1);
498}
499
500
501inline void Object::arraySet(uint index, const Value &value)
502{
503 arrayCreate();
504 if (index > 0x1000 && index > 2*d()->arrayData->values.alloc) {
505 initSparseArray();
506 }
507 ArrayData::insert(o: this, index, v: &value);
508 if (isArrayObject() && index >= getLength())
509 setArrayLengthUnchecked(index + 1);
510}
511
512
513template<>
514inline const ArrayObject *Value::as() const {
515 return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
516}
517
518template<>
519inline ReturnedValue value_convert<Object>(ExecutionEngine *e, const Value &v)
520{
521 return v.toObject(e)->asReturnedValue();
522}
523
524}
525
526QT_END_NAMESPACE
527
528#endif // QMLJS_OBJECTS_H
529

source code of qtdeclarative/src/qml/jsruntime/qv4object_p.h