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