Fix Object::getArrayHeadRoom()
[qt:qtdeclarative.git] / src / qml / jsruntime / qv4object_p.h
1 /****************************************************************************
2 **
3 ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
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 Digia.  For licensing terms and
14 ** conditions see http://qt.digia.com/licensing.  For further information
15 ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file.  Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights.  These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file.  Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 #ifndef QMLJS_OBJECTS_H
42 #define QMLJS_OBJECTS_H
43
44 #include "qv4global_p.h"
45 #include "qv4runtime_p.h"
46 #include "qv4engine_p.h"
47 #include "qv4context_p.h"
48 #include "qv4string_p.h"
49 #include "qv4managed_p.h"
50 #include "qv4property_p.h"
51 #include "qv4internalclass_p.h"
52 #include "qv4sparsearray_p.h"
53
54 #include <QtCore/QString>
55 #include <QtCore/QHash>
56 #include <QtCore/QScopedPointer>
57 #include <cstdio>
58 #include <cassert>
59
60 #ifdef _WIN32_WCE
61 #undef assert
62 #define assert(x)
63 #endif // _WIN32_WCE
64
65 QT_BEGIN_NAMESPACE
66
67 namespace QV4 {
68
69 struct Function;
70 struct Lookup;
71 struct Object;
72 struct ObjectIterator;
73 struct BooleanObject;
74 struct NumberObject;
75 struct StringObject;
76 struct ArrayObject;
77 struct DateObject;
78 struct FunctionObject;
79 struct RegExpObject;
80 struct ErrorObject;
81 struct ArgumentsObject;
82 struct ExecutionContext;
83 struct CallContext;
84 struct ExecutionEngine;
85 class MemoryManager;
86
87 struct ObjectPrototype;
88 struct StringPrototype;
89 struct NumberPrototype;
90 struct BooleanPrototype;
91 struct ArrayPrototype;
92 struct FunctionPrototype;
93 struct DatePrototype;
94 struct RegExpPrototype;
95 struct ErrorPrototype;
96 struct EvalErrorPrototype;
97 struct RangeErrorPrototype;
98 struct ReferenceErrorPrototype;
99 struct SyntaxErrorPrototype;
100 struct TypeErrorPrototype;
101 struct URIErrorPrototype;
102
103 struct Q_QML_EXPORT Object: Managed {
104     Q_MANAGED
105     uint memberDataAlloc;
106     Property *memberData;
107
108     union {
109         uint arrayFreeList;
110         uint arrayOffset;
111     };
112     uint arrayDataLen;
113     uint arrayAlloc;
114     PropertyAttributes *arrayAttributes;
115     Property *arrayData;
116     SparseArray *sparseArray;
117
118     enum {
119         InlinePropertySize = 4
120     };
121     Property inlineProperties[InlinePropertySize];
122
123     Object(ExecutionEngine *engine);
124     Object(InternalClass *internalClass);
125     ~Object();
126
127     Object *prototype() const { return internalClass->prototype; }
128     bool setPrototype(Object *proto);
129
130     Property *__getOwnProperty__(const StringRef name, PropertyAttributes *attrs = 0);
131     Property *__getOwnProperty__(uint index, PropertyAttributes *attrs = 0);
132
133     Property *__getPropertyDescriptor__(const StringRef name, PropertyAttributes *attrs = 0) const;
134     Property *__getPropertyDescriptor__(uint index, PropertyAttributes *attrs = 0) const;
135
136     bool __hasProperty__(const StringRef name) const;
137     bool __hasProperty__(uint index) const;
138
139     bool __defineOwnProperty__(ExecutionContext *ctx, Property *current, const StringRef member, const Property &p, PropertyAttributes attrs);
140     bool __defineOwnProperty__(ExecutionContext *ctx, const StringRef name, const Property &p, PropertyAttributes attrs);
141     bool __defineOwnProperty__(ExecutionContext *ctx, uint index, const Property &p, PropertyAttributes attrs);
142     bool __defineOwnProperty__(ExecutionContext *ctx, const QString &name, const Property &p, PropertyAttributes attrs);
143
144     //
145     // helpers
146     //
147     void put(ExecutionContext *ctx, const QString &name, const ValueRef value);
148
149     static ReturnedValue getValue(const ValueRef thisObject, const Property *p, PropertyAttributes attrs);
150     ReturnedValue getValue(const Property *p, PropertyAttributes attrs) const {
151         Scope scope(this->engine());
152         ScopedValue t(scope, const_cast<Object *>(this));
153         return getValue(t, p, attrs);
154     }
155
156     void putValue(Property *pd, PropertyAttributes attrs, const ValueRef value);
157
158     /* The spec default: Writable: true, Enumerable: false, Configurable: true */
159     void defineDefaultProperty(const StringRef name, ValueRef value);
160     void defineDefaultProperty(const QString &name, ValueRef value);
161     void defineDefaultProperty(const QString &name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
162     void defineDefaultProperty(const StringRef name, ReturnedValue (*code)(CallContext *), int argumentCount = 0);
163     void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
164     void defineAccessorProperty(const StringRef name, ReturnedValue (*getter)(CallContext *), ReturnedValue (*setter)(CallContext *));
165     /* Fixed: Writable: false, Enumerable: false, Configurable: false */
166     void defineReadonlyProperty(const QString &name, ValueRef value);
167     void defineReadonlyProperty(const StringRef name, ValueRef value);
168
169     Property *insertMember(const StringRef s, PropertyAttributes attributes);
170
171     inline ExecutionEngine *engine() const { return internalClass->engine; }
172
173     // Array handling
174
175     uint allocArrayValue() {
176         uint idx = arrayFreeList;
177         if (arrayAlloc <= arrayFreeList)
178             arrayReserve(arrayAlloc + 1);
179         arrayFreeList = arrayData[arrayFreeList].value.uint_32;
180         if (arrayAttributes)
181             arrayAttributes[idx].setType(PropertyAttributes::Data);
182         return idx;
183     }
184
185     uint allocArrayValue(const ValueRef v) {
186         uint idx = allocArrayValue();
187         Property *pd = &arrayData[idx];
188         pd->value = *v;
189         return idx;
190     }
191     void freeArrayValue(int idx) {
192         Property &pd = arrayData[idx];
193         pd.value.tag = Value::Empty_Type;
194         pd.value.int_32 = arrayFreeList;
195         arrayFreeList = idx;
196         if (arrayAttributes)
197             arrayAttributes[idx].clear();
198     }
199
200     void getArrayHeadRoom() {
201         assert(!sparseArray && !arrayOffset);
202         arrayOffset = qMax(arrayDataLen >> 2, (uint)16);
203         Property *newArray = new Property[arrayOffset + arrayAlloc];
204         memcpy(newArray + arrayOffset, arrayData, arrayDataLen*sizeof(Property));
205         delete [] arrayData;
206         arrayData = newArray + arrayOffset;
207         if (arrayAttributes) {
208             PropertyAttributes *newAttrs = new PropertyAttributes[arrayOffset + arrayAlloc];
209             memcpy(newAttrs + arrayOffset, arrayAttributes, arrayDataLen*sizeof(PropertyAttributes));
210             delete [] arrayAttributes;
211             arrayAttributes = newAttrs + arrayOffset;
212         }
213         arrayAlloc += arrayOffset;
214     }
215
216 public:
217     void copyArrayData(Object *other);
218     void initSparse();
219
220     uint arrayLength() const;
221     bool setArrayLength(uint newLen);
222
223     void setArrayLengthUnchecked(uint l);
224
225     Property *arrayInsert(uint index, PropertyAttributes attributes = Attr_Data);
226
227     void arraySet(uint index, const Property *pd);
228     void arraySet(uint index, ValueRef value);
229
230     uint propertyIndexFromArrayIndex(uint index) const
231     {
232         if (!sparseArray) {
233             if (index >= arrayDataLen)
234                 return UINT_MAX;
235             return index;
236         } else {
237             SparseArrayNode *n = sparseArray->findNode(index);
238             if (!n)
239                 return UINT_MAX;
240             return n->value;
241         }
242     }
243
244     Property *arrayAt(uint index) const {
245         uint pidx = propertyIndexFromArrayIndex(index);
246         if (pidx == UINT_MAX)
247             return 0;
248         return arrayData + pidx;
249     }
250
251     Property *nonSparseArrayAt(uint index) const {
252         if (sparseArray)
253             return 0;
254         if (index >= arrayDataLen)
255             return 0;
256         return arrayData + index;
257     }
258
259     void push_back(const ValueRef v);
260
261     SparseArrayNode *sparseArrayBegin() { return sparseArray ? sparseArray->begin() : 0; }
262     SparseArrayNode *sparseArrayEnd() { return sparseArray ? sparseArray->end() : 0; }
263
264     void arrayConcat(const ArrayObject *other);
265     void arraySort(ExecutionContext *context, ObjectRef thisObject, const ValueRef comparefn, uint arrayDataLen);
266     ReturnedValue arrayIndexOf(const ValueRef v, uint fromIndex, uint arrayDataLen, ExecutionContext *ctx, Object *o);
267
268     void arrayReserve(uint n);
269     void ensureArrayAttributes();
270
271     inline bool protoHasArray() {
272         Scope scope(engine());
273         Scoped<Object> p(scope, this);
274
275         while ((p = p->prototype()))
276             if (p->arrayDataLen)
277                 return true;
278
279         return false;
280     }
281     void ensureMemberIndex(uint idx);
282
283     inline ReturnedValue get(const StringRef name, bool *hasProperty = 0)
284     { return vtbl->get(this, name, hasProperty); }
285     inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0)
286     { return vtbl->getIndexed(this, idx, hasProperty); }
287     inline void put(const StringRef name, const ValueRef v)
288     { vtbl->put(this, name, v); }
289     inline void putIndexed(uint idx, const ValueRef v)
290     { vtbl->putIndexed(this, idx, v); }
291     using Managed::get;
292     using Managed::getIndexed;
293     using Managed::put;
294     using Managed::putIndexed;
295     using Managed::query;
296     using Managed::queryIndexed;
297     using Managed::deleteProperty;
298     using Managed::deleteIndexedProperty;
299     using Managed::getLookup;
300     using Managed::setLookup;
301     using Managed::advanceIterator;
302 protected:
303     static void destroy(Managed *that);
304     static void markObjects(Managed *that, ExecutionEngine *e);
305     static ReturnedValue get(Managed *m, const StringRef name, bool *hasProperty);
306     static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty);
307     static void put(Managed *m, const StringRef name, const ValueRef value);
308     static void putIndexed(Managed *m, uint index, const ValueRef value);
309     static PropertyAttributes query(const Managed *m, StringRef name);
310     static PropertyAttributes queryIndexed(const Managed *m, uint index);
311     static bool deleteProperty(Managed *m, const StringRef name);
312     static bool deleteIndexedProperty(Managed *m, uint index);
313     static ReturnedValue getLookup(Managed *m, Lookup *l);
314     static void setLookup(Managed *m, Lookup *l, const ValueRef v);
315     static Property *advanceIterator(Managed *m, ObjectIterator *it, StringRef name, uint *index, PropertyAttributes *attributes);
316
317
318 private:
319     ReturnedValue internalGet(const StringRef name, bool *hasProperty);
320     ReturnedValue internalGetIndexed(uint index, bool *hasProperty);
321     void internalPut(const StringRef name, const ValueRef value);
322     void internalPutIndexed(uint index, const ValueRef value);
323     bool internalDeleteProperty(const StringRef name);
324     bool internalDeleteIndexedProperty(uint index);
325
326     friend struct ObjectIterator;
327     friend struct ObjectPrototype;
328 };
329
330 struct BooleanObject: Object {
331     Q_MANAGED
332     SafeValue value;
333     BooleanObject(ExecutionEngine *engine, const ValueRef val)
334         : Object(engine->booleanClass) {
335         vtbl = &static_vtbl;
336         type = Type_BooleanObject;
337         value = val;
338     }
339 protected:
340     BooleanObject(InternalClass *ic)
341         : Object(ic) {
342         vtbl = &static_vtbl;
343         type = Type_BooleanObject;
344         value = Encode(false);
345     }
346 };
347
348 struct NumberObject: Object {
349     Q_MANAGED
350     SafeValue value;
351     NumberObject(ExecutionEngine *engine, const ValueRef val)
352         : Object(engine->numberClass) {
353         vtbl = &static_vtbl;
354         type = Type_NumberObject;
355         value = val;
356     }
357 protected:
358     NumberObject(InternalClass *ic)
359         : Object(ic) {
360         vtbl = &static_vtbl;
361         type = Type_NumberObject;
362         value = Encode((int)0);
363     }
364 };
365
366 struct ArrayObject: Object {
367     Q_MANAGED
368     enum {
369         LengthPropertyIndex = 0
370     };
371
372     ArrayObject(ExecutionEngine *engine) : Object(engine->arrayClass) { init(engine); }
373     ArrayObject(ExecutionEngine *engine, const QStringList &list);
374     ArrayObject(InternalClass *ic) : Object(ic) { init(ic->engine); }
375
376     void init(ExecutionEngine *engine);
377
378     QStringList toQStringList() const;
379 };
380
381 inline uint Object::arrayLength() const
382 {
383     if (isArrayObject()) {
384         if (memberData[ArrayObject::LengthPropertyIndex].value.isInteger())
385             return memberData[ArrayObject::LengthPropertyIndex].value.integerValue();
386         return Primitive::toUInt32(memberData[ArrayObject::LengthPropertyIndex].value.doubleValue());
387     }
388     return 0;
389 }
390
391 inline void Object::setArrayLengthUnchecked(uint l)
392 {
393     if (isArrayObject()) {
394         // length is always the first property of an array
395         Property &lengthProperty = memberData[ArrayObject::LengthPropertyIndex];
396         lengthProperty.value = Primitive::fromUInt32(l);
397     }
398 }
399
400 inline void Object::push_back(const ValueRef v)
401 {
402     uint idx = arrayLength();
403     if (!sparseArray) {
404         if (idx >= arrayAlloc)
405             arrayReserve(idx + 1);
406         arrayData[idx].value = *v;
407         arrayDataLen = idx + 1;
408     } else {
409         uint idx = allocArrayValue(v);
410         sparseArray->push_back(idx, arrayLength());
411     }
412     setArrayLengthUnchecked(idx + 1);
413 }
414
415 inline Property *Object::arrayInsert(uint index, PropertyAttributes attributes) {
416     if (attributes.isAccessor())
417         hasAccessorProperty = 1;
418
419     Property *pd;
420     if (!sparseArray && (index < 0x1000 || index < arrayDataLen + (arrayDataLen >> 2))) {
421         if (index >= arrayAlloc)
422             arrayReserve(index + 1);
423         if (index >= arrayDataLen) {
424             // mark possible hole in the array
425             for (uint i = arrayDataLen; i < index; ++i) {
426                 arrayData[i].value = Primitive::emptyValue();
427                 if (arrayAttributes)
428                     arrayAttributes[i].clear();
429             }
430             arrayDataLen = index + 1;
431         }
432         pd = arrayData + index;
433     } else {
434         initSparse();
435         SparseArrayNode *n = sparseArray->insert(index);
436         if (n->value == UINT_MAX)
437             n->value = allocArrayValue();
438         pd = arrayData + n->value;
439     }
440     if (index >= arrayLength())
441         setArrayLengthUnchecked(index + 1);
442     if (arrayAttributes || attributes != Attr_Data) {
443         if (!arrayAttributes)
444             ensureArrayAttributes();
445         attributes.resolve();
446         arrayAttributes[pd - arrayData] = attributes;
447     }
448     return pd;
449 }
450
451 inline void Object::arraySet(uint index, ValueRef value)
452 {
453     Property *pd = arrayInsert(index);
454     pd->value = *value;
455 }
456
457 inline void Object::arraySet(uint index, const Property *pd)
458 {
459     *arrayInsert(index) = *pd;
460 }
461
462 template<>
463 inline Object *value_cast(const Value &v) {
464     return v.asObject();
465 }
466
467 template<>
468 inline ArrayObject *value_cast(const Value &v) {
469     return v.asArrayObject();
470 }
471
472 template<>
473 inline ReturnedValue value_convert<Object>(ExecutionContext *ctx, const Value &v)
474 {
475     return v.toObject(ctx)->asReturnedValue();
476 }
477
478 }
479
480 QT_END_NAMESPACE
481
482 #endif // QMLJS_OBJECTS_H