aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime')
-rw-r--r--src/qml/jsruntime/jsruntime.pri34
-rw-r--r--src/qml/jsruntime/qv4argumentsobject.cpp25
-rw-r--r--src/qml/jsruntime/qv4argumentsobject_p.h11
-rw-r--r--src/qml/jsruntime/qv4arraybuffer.cpp10
-rw-r--r--src/qml/jsruntime/qv4arraydata.cpp70
-rw-r--r--src/qml/jsruntime/qv4arraydata_p.h30
-rw-r--r--src/qml/jsruntime/qv4arrayiterator.cpp106
-rw-r--r--src/qml/jsruntime/qv4arrayiterator_p.h106
-rw-r--r--src/qml/jsruntime/qv4arrayobject.cpp442
-rw-r--r--src/qml/jsruntime/qv4arrayobject_p.h12
-rw-r--r--src/qml/jsruntime/qv4context.cpp103
-rw-r--r--src/qml/jsruntime/qv4context_p.h46
-rw-r--r--src/qml/jsruntime/qv4dataview.cpp54
-rw-r--r--src/qml/jsruntime/qv4dateobject.cpp125
-rw-r--r--src/qml/jsruntime/qv4dateobject_p.h5
-rw-r--r--src/qml/jsruntime/qv4engine.cpp425
-rw-r--r--src/qml/jsruntime/qv4engine_p.h120
-rw-r--r--src/qml/jsruntime/qv4enginebase_p.h11
-rw-r--r--src/qml/jsruntime/qv4errorobject.cpp8
-rw-r--r--src/qml/jsruntime/qv4errorobject_p.h21
-rw-r--r--src/qml/jsruntime/qv4function.cpp19
-rw-r--r--src/qml/jsruntime/qv4function_p.h11
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp133
-rw-r--r--src/qml/jsruntime/qv4functionobject_p.h30
-rw-r--r--src/qml/jsruntime/qv4generatorobject.cpp248
-rw-r--r--src/qml/jsruntime/qv4generatorobject_p.h136
-rw-r--r--src/qml/jsruntime/qv4global_p.h21
-rw-r--r--src/qml/jsruntime/qv4globalobject.cpp4
-rw-r--r--src/qml/jsruntime/qv4identifier.cpp92
-rw-r--r--src/qml/jsruntime/qv4identifier_p.h61
-rw-r--r--src/qml/jsruntime/qv4identifiertable.cpp194
-rw-r--r--src/qml/jsruntime/qv4identifiertable_p.h42
-rw-r--r--src/qml/jsruntime/qv4internalclass.cpp560
-rw-r--r--src/qml/jsruntime/qv4internalclass_p.h126
-rw-r--r--src/qml/jsruntime/qv4iterator.cpp64
-rw-r--r--src/qml/jsruntime/qv4iterator_p.h82
-rw-r--r--src/qml/jsruntime/qv4jscall_p.h5
-rw-r--r--src/qml/jsruntime/qv4jsonobject.cpp5
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp85
-rw-r--r--src/qml/jsruntime/qv4lookup_p.h50
-rw-r--r--src/qml/jsruntime/qv4managed.cpp41
-rw-r--r--src/qml/jsruntime/qv4managed_p.h60
-rw-r--r--src/qml/jsruntime/qv4mapiterator.cpp109
-rw-r--r--src/qml/jsruntime/qv4mapiterator_p.h104
-rw-r--r--src/qml/jsruntime/qv4mapobject.cpp354
-rw-r--r--src/qml/jsruntime/qv4mapobject_p.h117
-rw-r--r--src/qml/jsruntime/qv4mathobject.cpp235
-rw-r--r--src/qml/jsruntime/qv4mathobject_p.h16
-rw-r--r--src/qml/jsruntime/qv4memberdata_p.h12
-rw-r--r--src/qml/jsruntime/qv4object.cpp152
-rw-r--r--src/qml/jsruntime/qv4object_p.h116
-rw-r--r--src/qml/jsruntime/qv4objectiterator.cpp26
-rw-r--r--src/qml/jsruntime/qv4objectiterator_p.h25
-rw-r--r--src/qml/jsruntime/qv4objectproto.cpp127
-rw-r--r--src/qml/jsruntime/qv4objectproto_p.h19
-rw-r--r--src/qml/jsruntime/qv4persistent.cpp6
-rw-r--r--src/qml/jsruntime/qv4profiling.cpp9
-rw-r--r--src/qml/jsruntime/qv4profiling_p.h2
-rw-r--r--src/qml/jsruntime/qv4property_p.h13
-rw-r--r--src/qml/jsruntime/qv4qmlcontext.cpp43
-rw-r--r--src/qml/jsruntime/qv4qmlcontext_p.h4
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper.cpp45
-rw-r--r--src/qml/jsruntime/qv4qobjectwrapper_p.h6
-rw-r--r--src/qml/jsruntime/qv4reflect.cpp270
-rw-r--r--src/qml/jsruntime/qv4reflect_p.h89
-rw-r--r--src/qml/jsruntime/qv4regexpobject.cpp9
-rw-r--r--src/qml/jsruntime/qv4regexpobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp574
-rw-r--r--src/qml/jsruntime/qv4runtime_p.h1
-rw-r--r--src/qml/jsruntime/qv4runtimeapi_p.h25
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen.cpp9
-rw-r--r--src/qml/jsruntime/qv4scopedvalue_p.h73
-rw-r--r--src/qml/jsruntime/qv4script.cpp17
-rw-r--r--src/qml/jsruntime/qv4script_p.h17
-rw-r--r--src/qml/jsruntime/qv4sequenceobject.cpp35
-rw-r--r--src/qml/jsruntime/qv4sequenceobject_p.h4
-rw-r--r--src/qml/jsruntime/qv4serialize.cpp18
-rw-r--r--src/qml/jsruntime/qv4setiterator.cpp100
-rw-r--r--src/qml/jsruntime/qv4setiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4setobject.cpp297
-rw-r--r--src/qml/jsruntime/qv4setobject_p.h113
-rw-r--r--src/qml/jsruntime/qv4sparsearray.cpp2
-rw-r--r--src/qml/jsruntime/qv4sparsearray_p.h3
-rw-r--r--src/qml/jsruntime/qv4string.cpp26
-rw-r--r--src/qml/jsruntime/qv4string_p.h102
-rw-r--r--src/qml/jsruntime/qv4stringiterator.cpp95
-rw-r--r--src/qml/jsruntime/qv4stringiterator_p.h103
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp265
-rw-r--r--src/qml/jsruntime/qv4stringobject_p.h11
-rw-r--r--src/qml/jsruntime/qv4symbol.cpp182
-rw-r--r--src/qml/jsruntime/qv4symbol_p.h129
-rw-r--r--src/qml/jsruntime/qv4typedarray.cpp132
-rw-r--r--src/qml/jsruntime/qv4typedarray_p.h35
-rw-r--r--src/qml/jsruntime/qv4value.cpp47
-rw-r--r--src/qml/jsruntime/qv4value_p.h142
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp373
-rw-r--r--src/qml/jsruntime/qv4vme_moth_p.h1
97 files changed, 7043 insertions, 1736 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri
index 4bc877bd9d..a2408d7d2a 100644
--- a/src/qml/jsruntime/jsruntime.pri
+++ b/src/qml/jsruntime/jsruntime.pri
@@ -14,13 +14,16 @@ SOURCES += \
$$PWD/qv4sparsearray.cpp \
$$PWD/qv4arraydata.cpp \
$$PWD/qv4arrayobject.cpp \
+ $$PWD/qv4arrayiterator.cpp \
$$PWD/qv4argumentsobject.cpp \
$$PWD/qv4booleanobject.cpp \
$$PWD/qv4dateobject.cpp \
$$PWD/qv4errorobject.cpp \
$$PWD/qv4function.cpp \
$$PWD/qv4functionobject.cpp \
+ $$PWD/qv4generatorobject.cpp \
$$PWD/qv4globalobject.cpp \
+ $$PWD/qv4iterator.cpp \
$$PWD/qv4jsonobject.cpp \
$$PWD/qv4mathobject.cpp \
$$PWD/qv4memberdata.cpp \
@@ -28,7 +31,9 @@ SOURCES += \
$$PWD/qv4object.cpp \
$$PWD/qv4objectproto.cpp \
$$PWD/qv4qmlcontext.cpp \
+ $$PWD/qv4reflect.cpp \
$$PWD/qv4regexpobject.cpp \
+ $$PWD/qv4stringiterator.cpp \
$$PWD/qv4stringobject.cpp \
$$PWD/qv4variantobject.cpp \
$$PWD/qv4objectiterator.cpp \
@@ -36,13 +41,17 @@ SOURCES += \
$$PWD/qv4runtimecodegen.cpp \
$$PWD/qv4serialize.cpp \
$$PWD/qv4script.cpp \
- $$PWD/qv4sequenceobject.cpp \
+ $$PWD/qv4symbol.cpp \
+ $$PWD/qv4setobject.cpp \
+ $$PWD/qv4setiterator.cpp \
$$PWD/qv4include.cpp \
$$PWD/qv4qobjectwrapper.cpp \
$$PWD/qv4arraybuffer.cpp \
$$PWD/qv4typedarray.cpp \
$$PWD/qv4dataview.cpp \
- $$PWD/qv4vme_moth.cpp
+ $$PWD/qv4vme_moth.cpp \
+ $$PWD/qv4mapobject.cpp \
+ $$PWD/qv4mapiterator.cpp
qtConfig(qml-debug): SOURCES += $$PWD/qv4profiling.cpp
@@ -64,13 +73,16 @@ HEADERS += \
$$PWD/qv4sparsearray_p.h \
$$PWD/qv4arraydata_p.h \
$$PWD/qv4arrayobject_p.h \
+ $$PWD/qv4arrayiterator_p.h \
$$PWD/qv4argumentsobject_p.h \
$$PWD/qv4booleanobject_p.h \
$$PWD/qv4dateobject_p.h \
$$PWD/qv4errorobject_p.h \
$$PWD/qv4function_p.h \
$$PWD/qv4functionobject_p.h \
+ $$PWD/qv4generatorobject_p.h \
$$PWD/qv4globalobject_p.h \
+ $$PWD/qv4iterator_p.h \
$$PWD/qv4jsonobject_p.h \
$$PWD/qv4mathobject_p.h \
$$PWD/qv4memberdata_p.h \
@@ -78,8 +90,10 @@ HEADERS += \
$$PWD/qv4object_p.h \
$$PWD/qv4objectproto_p.h \
$$PWD/qv4qmlcontext_p.h \
+ $$PWD/qv4reflect_p.h \
$$PWD/qv4regexpobject_p.h \
$$PWD/qv4runtimecodegen_p.h \
+ $$PWD/qv4stringiterator_p.h \
$$PWD/qv4stringobject_p.h \
$$PWD/qv4variantobject_p.h \
$$PWD/qv4property_p.h \
@@ -87,16 +101,28 @@ HEADERS += \
$$PWD/qv4regexp_p.h \
$$PWD/qv4serialize_p.h \
$$PWD/qv4script_p.h \
+ $$PWD/qv4symbol_p.h \
+ $$PWD/qv4setobject_p.h \
+ $$PWD/qv4setiterator_p.h \
$$PWD/qv4scopedvalue_p.h \
$$PWD/qv4executableallocator_p.h \
- $$PWD/qv4sequenceobject_p.h \
$$PWD/qv4include_p.h \
$$PWD/qv4qobjectwrapper_p.h \
$$PWD/qv4profiling_p.h \
$$PWD/qv4arraybuffer_p.h \
$$PWD/qv4typedarray_p.h \
$$PWD/qv4dataview_p.h \
- $$PWD/qv4vme_moth_p.h
+ $$PWD/qv4vme_moth_p.h \
+ $$PWD/qv4mapobject_p.h \
+ $$PWD/qv4mapiterator_p.h
+
+qtConfig(qml-sequence-object) {
+ HEADERS += \
+ $$PWD/qv4sequenceobject_p.h
+
+ SOURCES += \
+ $$PWD/qv4sequenceobject.cpp
+}
}
diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp
index 075e7afd8a..58951d043c 100644
--- a/src/qml/jsruntime/qv4argumentsobject.cpp
+++ b/src/qml/jsruntime/qv4argumentsobject.cpp
@@ -37,11 +37,13 @@
**
****************************************************************************/
#include <qv4argumentsobject_p.h>
+#include <qv4arrayobject_p.h>
#include <qv4alloca_p.h>
#include <qv4scopedvalue_p.h>
#include <qv4string_p.h>
#include <qv4function_p.h>
#include <qv4jscall_p.h>
+#include <qv4symbol_p.h>
using namespace QV4;
@@ -61,10 +63,12 @@ void Heap::ArgumentsObject::init(QV4::CppStackFrame *frame)
this->context.set(v4, context->d());
Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable());
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->identifier()));
setProperty(v4, CalleePropertyIndex, context->d()->function);
- Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()));
+ Q_ASSERT(LengthPropertyIndex == internalClass->find(v4->id_length()->identifier()));
setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(context->argc()));
+ Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->identifier()));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
}
void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
@@ -74,19 +78,18 @@ void Heap::StrictArgumentsObject::init(QV4::CppStackFrame *frame)
Object::init();
- Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()));
- Q_ASSERT(CallerPropertyIndex == internalClass->find(v4->id_caller()));
+ Q_ASSERT(CalleePropertyIndex == internalClass->find(v4->id_callee()->identifier()));
+ Q_ASSERT(SymbolIteratorPropertyIndex == internalClass->find(v4->symbol_iterator()->identifier()));
+ setProperty(v4, SymbolIteratorPropertyIndex, *v4->arrayProtoValues());
setProperty(v4, CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
setProperty(v4, CalleePropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::GetterOffset, *v4->thrower());
- setProperty(v4, CallerPropertyIndex + QV4::Object::SetterOffset, *v4->thrower());
Scope scope(v4);
Scoped<QV4::StrictArgumentsObject> args(scope, this);
args->arrayReserve(frame->originalArgumentsCount);
args->arrayPut(0, frame->originalArguments, frame->originalArgumentsCount);
- Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()));
+ Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length()->identifier()));
setProperty(v4, LengthPropertyIndex, Primitive::fromInt32(frame->originalArgumentsCount));
}
@@ -136,7 +139,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con
mapAttrs = arrayData()->attributes(index);
arrayData()->getProperty(index, map, &mapAttrs);
setArrayAttributes(index, Attr_Data);
- ArrayData::Index arrayIndex{ arrayData(), arrayData()->mappedIndex(index) };
+ PropertyIndex arrayIndex{ arrayData(), arrayData()->values.values + arrayData()->mappedIndex(index) };
arrayIndex.set(scope.engine, d()->mappedArguments->values[index]);
}
@@ -245,10 +248,8 @@ ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const
return Encode::undefined();
}
-uint ArgumentsObject::getLength(const Managed *m)
+qint64 ArgumentsObject::getLength(const Managed *m)
{
const ArgumentsObject *a = static_cast<const ArgumentsObject *>(m);
- if (a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->doubleValue());
+ return a->propertyData(Heap::ArgumentsObject::LengthPropertyIndex)->toLength();
}
diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h
index ac281f555a..f246f66019 100644
--- a/src/qml/jsruntime/qv4argumentsobject_p.h
+++ b/src/qml/jsruntime/qv4argumentsobject_p.h
@@ -85,7 +85,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_MARKOBJECTS(ArgumentsObject);
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2
};
void init(CppStackFrame *frame);
};
@@ -95,8 +96,8 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) {
DECLARE_HEAP_OBJECT(StrictArgumentsObject, Object) {
enum {
LengthPropertyIndex = 0,
- CalleePropertyIndex = 1,
- CallerPropertyIndex = 3
+ SymbolIteratorPropertyIndex = 1,
+ CalleePropertyIndex = 2
};
void init(CppStackFrame *frame);
};
@@ -142,7 +143,7 @@ struct ArgumentsObject: Object {
bool fullyCreated() const { return d()->fullyCreated; }
static bool isNonStrictArgumentsObject(Managed *m) {
- return m->d()->vtable() == staticVTable();
+ return m->vtable() == staticVTable();
}
bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs);
@@ -150,7 +151,7 @@ struct ArgumentsObject: Object {
static bool putIndexed(Managed *m, uint index, const Value &value);
static bool deleteIndexedProperty(Managed *m, uint index);
static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static uint getLength(const Managed *m);
+ static qint64 getLength(const Managed *m);
void fullyCreate();
diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp
index 59a2b9d913..b0c2ba597a 100644
--- a/src/qml/jsruntime/qv4arraybuffer.cpp
+++ b/src/qml/jsruntime/qv4arraybuffer.cpp
@@ -41,6 +41,7 @@
#include "qv4dataview_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
using namespace QV4;
@@ -94,7 +95,8 @@ ReturnedValue ArrayBufferCtor::method_isView(const FunctionObject *, const Value
void Heap::ArrayBuffer::init(size_t length)
{
Object::init();
- data = QTypedArrayData<char>::allocate(length + 1);
+ if (length < UINT_MAX)
+ data = QTypedArrayData<char>::allocate(length + 1);
if (!data) {
internalClass->engine->throwRangeError(QStringLiteral("ArrayBuffer: out of memory"));
return;
@@ -147,13 +149,17 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineDefaultProperty(QStringLiteral("isView"), ArrayBufferCtor::method_isView, 1);
+ ctor->addSymbolSpecies();
+
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineDefaultProperty(QStringLiteral("slice"), method_slice, 2);
defineDefaultProperty(QStringLiteral("toString"), method_toString, 0);
+ ScopedString name(scope, engine->newString(QStringLiteral("ArrayBuffer")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
ReturnedValue ArrayBufferPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp
index 855407e6f7..ecc0b138c0 100644
--- a/src/qml/jsruntime/qv4arraydata.cpp
+++ b/src/qml/jsruntime/qv4arraydata.cpp
@@ -59,8 +59,9 @@ const QV4::VTable QV4::ArrayData::static_vtbl = {
QV4::ArrayData::IsFunctionObject,
QV4::ArrayData::IsErrorObject,
QV4::ArrayData::IsArrayData,
- 0,
+ QV4::ArrayData::IsStringOrSymbol,
QV4::ArrayData::MyType,
+ { 0, 0, 0, 0 },
"ArrayData",
Q_VTABLE_FUNCTION(QV4::ArrayData, destroy),
ArrayData::Data::markObjects,
@@ -104,13 +105,6 @@ QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SimpleArrayData));
Q_STATIC_ASSERT(sizeof(Heap::ArrayData) == sizeof(Heap::SparseArrayData));
-static Q_ALWAYS_INLINE void storeValue(ReturnedValue *target, uint value)
-{
- Value v;
- v.setEmpty(value);
- *target = v.asReturnedValue();
-}
-
void Heap::ArrayData::markObjects(Heap::Base *base, MarkStack *stack)
{
ArrayData *a = static_cast<ArrayData *>(base);
@@ -195,7 +189,7 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
Heap::SparseArrayData *sparse = static_cast<Heap::SparseArrayData *>(newData->d());
- ReturnedValue *lastFree;
+ Value *lastFree;
if (d && d->type() == Heap::ArrayData::Sparse) {
Heap::SparseArrayData *old = static_cast<Heap::SparseArrayData *>(d->d());
sparse->sparse = old->sparse;
@@ -204,29 +198,29 @@ void ArrayData::realloc(Object *o, Type newType, uint requested, bool enforceAtt
} else {
sparse->sparse = new SparseArray;
lastFree = &sparse->sparse->freeList;
- storeValue(lastFree, 0);
+ *lastFree = Encode(0);
for (uint i = 0; i < toCopy; ++i) {
if (!sparse->values[i].isEmpty()) {
SparseArrayNode *n = sparse->sparse->insert(i);
n->value = i;
} else {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
}
if (toCopy < sparse->values.alloc) {
for (uint i = toCopy; i < sparse->values.alloc; ++i) {
- storeValue(lastFree, i);
+ *lastFree = Encode(i);
sparse->values.values[i].setEmpty();
- lastFree = &sparse->values.values[i].rawValueRef();
+ lastFree = &sparse->values.values[i];
}
}
- storeValue(lastFree, UINT_MAX);
+ *lastFree = Encode(-1);
- Q_ASSERT(Value::fromReturnedValue(sparse->sparse->freeList).isEmpty());
+ Q_ASSERT(sparse->sparse->freeList.isInteger());
// ### Could explicitly free the old data
}
@@ -368,12 +362,12 @@ void SparseArrayData::free(Heap::ArrayData *d, uint idx)
Value *v = d->values.values + idx;
if (d->attrs && d->attrs[idx].isAccessor()) {
// double slot, free both. Order is important, so we have a double slot for allocation again afterwards.
- v[1].setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
- v[0].setEmpty(idx + 1);
+ v[1] = d->sparse->freeList;
+ v[0] = Encode(idx + 1);
} else {
- v->setEmpty(Value::fromReturnedValue(d->sparse->freeList).emptyValue());
+ *v = d->sparse->freeList;
}
- d->sparse->freeList = Primitive::emptyValue(idx).asReturnedValue();
+ d->sparse->freeList = Encode(idx);
if (d->attrs)
d->attrs[idx].clear();
}
@@ -390,36 +384,34 @@ uint SparseArrayData::allocate(Object *o, bool doubleSlot)
Q_ASSERT(o->d()->arrayData->type == Heap::ArrayData::Sparse);
Heap::SimpleArrayData *dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
if (doubleSlot) {
- ReturnedValue *last = &dd->sparse->freeList;
+ Value *last = &dd->sparse->freeList;
while (1) {
- if (Value::fromReturnedValue(*last).value() == UINT_MAX) {
+ if (last->int_32() == -1) {
reallocate(o, dd->values.alloc + 2, true);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
last = &dd->sparse->freeList;
- Q_ASSERT(Value::fromReturnedValue(*last).value() != UINT_MAX);
+ Q_ASSERT(last->int_32() != -1);
}
- Q_ASSERT(dd->values[Value::fromReturnedValue(*last).value()].value() != Value::fromReturnedValue(*last).value());
- if (dd->values[Value::fromReturnedValue(*last).value()].value() == (Value::fromReturnedValue(*last).value() + 1)) {
+ Q_ASSERT(dd->values[static_cast<uint>(last->int_32())].int_32() != last->int_32());
+ if (dd->values[static_cast<uint>(last->int_32())].int_32() == last->int_32() + 1) {
// found two slots in a row
- uint idx = Value::fromReturnedValue(*last).emptyValue();
- Value lastV = Value::fromReturnedValue(*last);
- lastV.setEmpty(dd->values[lastV.emptyValue() + 1].value());
- *last = lastV.rawValue();
+ uint idx = static_cast<uint>(last->int_32());
+ *last = Encode(dd->values[static_cast<uint>(last->int_32()) + 1].int_32());
dd->attrs[idx] = Attr_Accessor;
return idx;
}
- last = &dd->values.values[Value::fromReturnedValue(*last).value()].rawValueRef();
+ last = &dd->values.values[last->int_32()];
}
} else {
- if (Value::fromReturnedValue(dd->sparse->freeList).value() == UINT_MAX) {
+ if (dd->sparse->freeList.int_32() == -1) {
reallocate(o, dd->values.alloc + 1, false);
dd = o->d()->arrayData.cast<Heap::SimpleArrayData>();
}
- uint idx = Value::fromReturnedValue(dd->sparse->freeList).value();
- Q_ASSERT(idx != UINT_MAX);
- dd->sparse->freeList = dd->values[idx].asReturnedValue();
- Q_ASSERT(Value::fromReturnedValue(dd->sparse->freeList).isEmpty());
+ Q_ASSERT(dd->sparse->freeList.int_32() != -1);
+ uint idx = static_cast<uint>(dd->sparse->freeList.int_32());
+ dd->sparse->freeList = dd->values[idx];
+ Q_ASSERT(dd->sparse->freeList.isInteger());
if (dd->attrs)
dd->attrs[idx] = Attr_Data;
return idx;
@@ -474,14 +466,14 @@ bool SparseArrayData::del(Object *o, uint index)
if (isAccessor) {
// free up both indices
- dd->values.values[pidx + 1].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
- dd->values.values[pidx].setEmpty(pidx + 1);
+ dd->values.values[pidx + 1] = dd->sparse->freeList;
+ dd->values.values[pidx] = Encode(pidx + 1);
} else {
Q_ASSERT(dd->type == Heap::ArrayData::Sparse);
- dd->values.values[pidx].setEmpty(Value::fromReturnedValue(dd->sparse->freeList).emptyValue());
+ dd->values.values[pidx] = dd->sparse->freeList;
}
- dd->sparse->freeList = Primitive::emptyValue(pidx).asReturnedValue();
+ dd->sparse->freeList = Encode(pidx);
dd->sparse->erase(n);
return true;
}
diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h
index 7ec060f9c6..ac5b430356 100644
--- a/src/qml/jsruntime/qv4arraydata_p.h
+++ b/src/qml/jsruntime/qv4arraydata_p.h
@@ -103,28 +103,16 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) {
enum Type { Simple = 0, Complex = 1, Sparse = 2, Custom = 3 };
- struct Index {
- Heap::ArrayData *arrayData;
- uint index;
-
- void set(EngineBase *e, Value newVal) {
- arrayData->values.set(e, index, newVal);
- }
- const Value *operator->() const { return &arrayData->values[index]; }
- const Value &operator*() const { return arrayData->values[index]; }
- bool isNull() const { return !arrayData; }
- };
-
bool isSparse() const { return type == Sparse; }
- const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(Base::vtable()); }
+ const ArrayVTable *vtable() const { return reinterpret_cast<const ArrayVTable *>(internalClass->vtable); }
inline ReturnedValue get(uint i) const {
return vtable()->get(this, i);
}
inline bool getProperty(uint index, Property *p, PropertyAttributes *attrs);
inline void setProperty(EngineBase *e, uint index, const Property *p);
- inline Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ inline PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs);
inline PropertyAttributes attributes(uint i) const;
bool isEmpty(uint i) const {
@@ -187,8 +175,6 @@ struct Q_QML_EXPORT ArrayData : public Managed
IsArrayData = true
};
- typedef Heap::ArrayData::Index Index;
-
uint alloc() const { return d()->values.alloc; }
uint &alloc() { return d()->values.alloc; }
void setAlloc(uint a) { d()->values.alloc = a; }
@@ -303,9 +289,9 @@ bool ArrayData::getProperty(uint index, Property *p, PropertyAttributes *attrs)
*attrs = attributes(index);
if (p) {
- p->value = *(Index{ this, mapped });
+ p->value = *(PropertyIndex{ this, values.values + mapped });
if (attrs->isAccessor())
- p->set = *(Index{ this, mapped + 1 /*Object::SetterOffset*/ });
+ p->set = *(PropertyIndex{ this, values.values + mapped + 1 /*Object::SetterOffset*/ });
}
return true;
}
@@ -326,16 +312,18 @@ inline PropertyAttributes ArrayData::attributes(uint i) const
return static_cast<const SimpleArrayData *>(this)->attributes(i);
}
-ArrayData::Index ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
+PropertyIndex ArrayData::getValueOrSetter(uint index, PropertyAttributes *attrs)
{
uint idx = mappedIndex(index);
if (idx == UINT_MAX) {
*attrs = Attr_Invalid;
- return { nullptr, 0 };
+ return { nullptr, nullptr };
}
*attrs = attributes(index);
- return { this, attrs->isAccessor() ? idx + 1 /* QV4::Object::SetterOffset*/ : idx };
+ if (attrs->isAccessor())
+ ++idx;
+ return { this, values.values + idx };
}
diff --git a/src/qml/jsruntime/qv4arrayiterator.cpp b/src/qml/jsruntime/qv4arrayiterator.cpp
new file mode 100644
index 0000000000..d6f787c01d
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator.cpp
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4arrayiterator_p.h>
+#include <private/qv4typedarray_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(ArrayIteratorObject);
+
+void ArrayIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Array Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue ArrayIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const ArrayIteratorObject *thisObject = that->as<ArrayIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an Array Iterator instance"));
+
+ ScopedObject a(scope, thisObject->d()->iteratedObject);
+ if (!a) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ Scoped<TypedArray> ta(scope, a->as<TypedArray>());
+ quint32 len = a->getLength();
+
+ if (index >= len) {
+ thisObject->d()->iteratedObject.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ thisObject->d()->nextIndex = index + 1;
+ if (itemKind == KeyIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Primitive::fromInt32(index), false);
+ }
+
+ ReturnedValue elementValue = a->getIndexed(index);
+ CHECK_EXCEPTION();
+
+ if (itemKind == ValueIteratorKind) {
+ return IteratorPrototype::createIterResultObject(scope.engine, Value::fromReturnedValue(elementValue), false);
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, Primitive::fromInt32(index));
+ resultArray->arrayPut(1, Value::fromReturnedValue(elementValue));
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+}
+
diff --git a/src/qml/jsruntime/qv4arrayiterator_p.h b/src/qml/jsruntime/qv4arrayiterator_p.h
new file mode 100644
index 0000000000..6d6bb466f1
--- /dev/null
+++ b/src/qml/jsruntime/qv4arrayiterator_p.h
@@ -0,0 +1,106 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ARRAYITERATOR_P_H
+#define QV4ARRAYITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define ArrayIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedObject) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(ArrayIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(ArrayIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedObject.set(engine, obj);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct ArrayIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ArrayIteratorObject : Object
+{
+ V4_OBJECT2(ArrayIteratorObject, Object)
+ Q_MANAGED_TYPE(ArrayIteratorObject)
+ V4_PROTOTYPE(arrayIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp
index bd019d3bcb..434f6781a8 100644
--- a/src/qml/jsruntime/qv4arrayobject.cpp
+++ b/src/qml/jsruntime/qv4arrayobject.cpp
@@ -1,5 +1,6 @@
/****************************************************************************
**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
@@ -38,12 +39,15 @@
****************************************************************************/
#include "qv4arrayobject_p.h"
+#include "qv4objectiterator_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4sparsearray_p.h"
#include "qv4objectproto_p.h"
#include "qv4jscall_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4runtime_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qscopedvaluerollback.h>
using namespace QV4;
@@ -89,13 +93,19 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineDefaultProperty(QStringLiteral("isArray"), method_isArray, 1);
+ ctor->defineDefaultProperty(QStringLiteral("of"), method_of, 0);
+ ctor->defineDefaultProperty(QStringLiteral("from"), method_from, 1);
+ ctor->addSymbolSpecies();
+
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString, 0);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
+ defineDefaultProperty(QStringLiteral("copyWithin"), method_copyWithin, 2);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
defineDefaultProperty(QStringLiteral("find"), method_find, 1);
defineDefaultProperty(QStringLiteral("findIndex"), method_findIndex, 1);
defineDefaultProperty(QStringLiteral("join"), method_join, 1);
@@ -107,15 +117,23 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("sort"), method_sort, 1);
defineDefaultProperty(QStringLiteral("splice"), method_splice, 2);
defineDefaultProperty(QStringLiteral("unshift"), method_unshift, 1);
+ defineDefaultProperty(QStringLiteral("includes"), method_includes, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("every"), method_every, 1);
+ defineDefaultProperty(QStringLiteral("fill"), method_fill, 1);
defineDefaultProperty(QStringLiteral("some"), method_some, 1);
defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
defineDefaultProperty(QStringLiteral("map"), method_map, 1);
defineDefaultProperty(QStringLiteral("filter"), method_filter, 1);
defineDefaultProperty(QStringLiteral("reduce"), method_reduce, 1);
defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1);
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ engine->jsObjects[ExecutionEngine::ArrayProtoValues] = values;
+ defineDefaultProperty(QStringLiteral("values"), values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
}
ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -124,6 +142,200 @@ ReturnedValue ArrayPrototype::method_isArray(const FunctionObject *, const Value
return Encode(isArray);
}
+ScopedObject createObjectFromCtorOrArray(Scope &scope, ScopedFunctionObject ctor, bool useLen, int len)
+{
+ ScopedObject a(scope, Primitive::undefinedValue());
+
+ if (ctor) {
+ // ### the spec says that we should only call constructors if
+ // IsConstructor(that), but we have no way of knowing if a builtin is a
+ // constructor. so for the time being, just try call it, and silence any
+ // exceptions-- this is not ideal, as the spec also says that we should
+ // return on exception.
+ //
+ // this also isn't completely kosher. for instance:
+ // Array.from.call(Object, []).constructor == Object
+ // is expected by the tests, but naturally, we get Number.
+ ScopedValue argument(scope, useLen ? QV4::Encode(len) : Primitive::undefinedValue());
+ a = ctor->callAsConstructor(argument, useLen ? 1 : 0);
+ if (scope.engine->hasException)
+ scope.engine->catchException(); // probably not a constructor, then.
+ }
+
+ if (!a) {
+ a = scope.engine->newArrayObject(len);
+ }
+
+ return a;
+}
+
+ReturnedValue ArrayPrototype::method_from(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject thatCtor(scope, thisObject);
+ ScopedObject itemsObject(scope, argv[0]);
+ bool usingIterator = false;
+
+ if (itemsObject) {
+ // If the object claims to support iterators, then let's try use them.
+ ScopedValue it(scope, itemsObject->get(scope.engine->symbol_iterator()));
+ if (!it->isNullOrUndefined()) {
+ ScopedFunctionObject itfunc(scope, it);
+ if (!itfunc)
+ return scope.engine->throwTypeError();
+ usingIterator = true;
+ }
+ }
+
+ ScopedFunctionObject mapfn(scope, Primitive::undefinedValue());
+ Value *mapArguments = nullptr;
+ if (argc > 1) {
+ mapfn = ScopedFunctionObject(scope, argv[1]);
+ if (!mapfn)
+ return scope.engine->throwTypeError(QString::fromLatin1("%1 is not a function").arg(argv[1].toQStringNoThrow()));
+ mapArguments = scope.alloc(2);
+ }
+
+ ScopedValue thisArg(scope);
+ if (argc > 2)
+ thisArg = argv[2];
+
+ if (usingIterator) {
+ // Item iteration supported, so let's go ahead and try use that.
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, false, 0));
+ CHECK_EXCEPTION();
+ ScopedObject iterator(scope, Runtime::method_getIterator(scope.engine, itemsObject, true));
+ CHECK_EXCEPTION(); // symbol_iterator threw; whoops.
+ if (!iterator) {
+ return scope.engine->throwTypeError(); // symbol_iterator wasn't an object.
+ }
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope);
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+
+ // The loop below pulls out all the properties using the iterator, and
+ // sets them into the created array.
+ forever {
+ if (k > (static_cast<qint64>(1) << 53) - 1) {
+ ScopedValue falsey(scope, Encode(false));
+ ScopedValue error(scope, scope.engine->throwTypeError());
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ // Retrieve the next value. If the iteration ends, we're done here.
+ done = Value::fromReturnedValue(Runtime::method_iteratorNext(scope.engine, iterator, nextValue));
+ CHECK_EXCEPTION();
+ if (done->toBoolean()) {
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+ if (mapfn) {
+ mapArguments[0] = *nextValue;
+ mapArguments[1] = Primitive::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ if (scope.engine->hasException)
+ return Runtime::method_iteratorClose(scope.engine, iterator, Primitive::fromBoolean(false));
+ } else {
+ mappedValue = *nextValue;
+ }
+
+ if (!a->hasOwnProperty(k)) {
+ a->arraySet(k, mappedValue);
+ } else {
+ // Don't return: we need to close the iterator.
+ scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iterator, falsey);
+ }
+
+ k++;
+ }
+
+ // the return is hidden up in the loop above, when iteration finishes.
+ } else {
+ // Array-like fallback. We request properties by index, and set them on
+ // the return object.
+ ScopedObject arrayLike(scope, argv[0].toObject(scope.engine));
+ if (!arrayLike)
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot convert %1 to object").arg(argv[0].toQStringNoThrow()));
+ qint64 len = arrayLike->getLength();
+ ScopedObject a(createObjectFromCtorOrArray(scope, thatCtor, true, len));
+ CHECK_EXCEPTION();
+
+ qint64 k = 0;
+ ScopedValue mappedValue(scope, Primitive::undefinedValue());
+ ScopedValue kValue(scope);
+ while (k < len) {
+ kValue = arrayLike->getIndexed(k);
+ CHECK_EXCEPTION();
+
+ if (mapfn) {
+ mapArguments[0] = kValue;
+ mapArguments[1] = Primitive::fromDouble(k);
+ mappedValue = mapfn->call(thisArg, mapArguments, 2);
+ CHECK_EXCEPTION();
+ } else {
+ mappedValue = kValue;
+ }
+
+ if (a->hasOwnProperty(k))
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+
+ a->arraySet(k, mappedValue);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ if (ArrayObject *ao = a->as<ArrayObject>()) {
+ ao->setArrayLengthUnchecked(k);
+ } else {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(k), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+ return a.asReturnedValue();
+ }
+
+}
+
+ReturnedValue ArrayPrototype::method_of(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(builtin);
+ ScopedFunctionObject that(scope, thisObject);
+ ScopedObject a(createObjectFromCtorOrArray(scope, that, true, argc));
+ CHECK_EXCEPTION();
+
+ int k = 0;
+ while (k < argc) {
+ if (a->hasOwnProperty(k)) {
+ return scope.engine->throwTypeError(QString::fromLatin1("Cannot redefine property: %1").arg(k));
+ }
+ a->arraySet(k, argv[k]);
+ CHECK_EXCEPTION();
+
+ k++;
+ }
+
+ // ArrayObject updates its own length, and will throw if we try touch it.
+ if (!a->as<ArrayObject>()) {
+ a->set(scope.engine->id_length(), Primitive::fromDouble(argc), QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ }
+
+ return a.asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_toString(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(builtin);
@@ -183,6 +395,88 @@ ReturnedValue ArrayPrototype::method_concat(const FunctionObject *b, const Value
return result.asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_copyWithin(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ double len = instance->getLength();
+ double target = argv[0].toInteger();
+ double start = argv[1].toInteger();
+ double end = len;
+
+ if (argc > 2 && !argv[2].isUndefined()) {
+ end = argv[2].toInteger();
+ }
+
+ double relativeTarget = target;
+ double relativeStart = start;
+ double relativeEnd = end;
+ double from = 0;
+ double to = 0;
+
+ if (relativeTarget < 0) {
+ to = std::max(len+relativeTarget, 0.0);
+ } else {
+ to = std::min(relativeTarget, len);
+ }
+ if (relativeStart < 0) {
+ from = std::max(len+relativeStart, 0.0);
+ } else {
+ from = std::min(relativeStart, len);
+ }
+
+ double fin = 0;
+ if (relativeEnd < 0) {
+ fin = std::max(len+relativeEnd, 0.0);
+ } else {
+ fin = std::min(relativeEnd, len);
+ }
+ double count = std::min(fin-from, len-to);
+ double direction = 1;
+ if (from < to && to < from+count) {
+ direction = -1;
+ from = from + count - 1;
+ to = to + count - 1;
+ }
+
+ while (count > 0) {
+ bool fromPresent = false;
+ ScopedValue fromVal(scope, instance->getIndexed(from, &fromPresent));
+
+ if (fromPresent) {
+ instance->setIndexed(to, fromVal, QV4::Object::DoThrowOnRejection);
+ CHECK_EXCEPTION();
+ } else {
+ bool didDelete = instance->deleteIndexedProperty(to);
+ CHECK_EXCEPTION();
+ if (!didDelete) {
+ return scope.engine->throwTypeError();
+ }
+ }
+
+ from = from + direction;
+ to = to + direction;
+ count = count - 1;
+ }
+
+ return instance.asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_find(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -352,9 +646,9 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
instance->arrayCreate();
Q_ASSERT(instance->arrayData());
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
- if (len + argc < len) {
+ if (len + quint64(argc) >= UINT_MAX) {
// ughh... this goes beyond UINT_MAX
double l = len;
ScopedString s(scope);
@@ -393,7 +687,7 @@ ReturnedValue ArrayPrototype::method_push(const FunctionObject *b, const Value *
return scope.engine->throwTypeError();
}
- return Encode(len);
+ return Encode(uint(len));
}
ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -403,7 +697,10 @@ ReturnedValue ArrayPrototype::method_reverse(const FunctionObject *b, const Valu
if (!instance)
RETURN_UNDEFINED();
- uint length = instance->getLength();
+ qint64 length = instance->getLength();
+ // ### FIXME
+ if (length >= UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Array.prototype.reverse: Length out of range."));
int lo = 0, hi = length - 1;
@@ -548,19 +845,31 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
-
- ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
+ qint64 len = instance->getLength();
double rs = (argc ? argv[0] : Primitive::undefinedValue()).toInteger();
- uint start;
+ qint64 start;
if (rs < 0)
- start = (uint) qMax(0., len + rs);
+ start = static_cast<qint64>(qMax(0., len + rs));
else
- start = (uint) qMin(rs, (double)len);
+ start = static_cast<qint64>(qMin(rs, static_cast<double>(len)));
+
+ qint64 deleteCount = 0;
+ qint64 itemCount = 0;
+ if (argc == 1) {
+ deleteCount = len - start;
+ } else if (argc > 1){
+ itemCount = argc - 2;
+ double dc = argv[1].toInteger();
+ deleteCount = static_cast<qint64>(qMin(qMax(dc, 0.), double(len - start)));
+ }
- uint deleteCount = (uint)qMin(qMax((argc > 1 ? argv[1] : Primitive::undefinedValue()).toInteger(), 0.), (double)(len - start));
+ if (len + itemCount - deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwTypeError();
+ if (deleteCount > /*(static_cast<qint64>(1) << 53) - 1*/ UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+ ScopedArrayObject newArray(scope, scope.engine->newArrayObject());
newArray->arrayReserve(deleteCount);
ScopedValue v(scope);
for (uint i = 0; i < deleteCount; ++i) {
@@ -572,7 +881,6 @@ ReturnedValue ArrayPrototype::method_splice(const FunctionObject *b, const Value
}
newArray->setArrayLengthUnchecked(deleteCount);
- uint itemCount = argc < 2 ? 0 : argc - 2;
if (itemCount < deleteCount) {
for (uint k = start; k < len - deleteCount; ++k) {
@@ -663,6 +971,44 @@ ReturnedValue ArrayPrototype::method_unshift(const FunctionObject *b, const Valu
return Encode(newLen);
}
+ReturnedValue ArrayPrototype::method_includes(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ qint64 len = instance->getLength();
+ if (len == 0) {
+ return Encode(false);
+ }
+
+ double n = 0;
+ if (argc > 1 && !argv[1].isUndefined()) {
+ n = argv[1].toInteger();
+ }
+
+ double k = 0;
+ if (n >= 0) {
+ k = n;
+ } else {
+ k = len + n;
+ if (k < 0) {
+ k = 0;
+ }
+ }
+
+ while (k < len) {
+ ScopedValue val(scope, instance->getIndexed(k));
+ if (val->sameValueZero(argv[0])) {
+ return Encode(true);
+ }
+ k++;
+ }
+
+ return Encode(false);
+}
+
ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -729,6 +1075,18 @@ ReturnedValue ArrayPrototype::method_indexOf(const FunctionObject *b, const Valu
return Encode(-1);
}
+ReturnedValue ArrayPrototype::method_keys(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_lastIndexOf(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -805,6 +1163,42 @@ ReturnedValue ArrayPrototype::method_every(const FunctionObject *b, const Value
return Encode(ok);
}
+ReturnedValue ArrayPrototype::method_fill(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ ScopedObject instance(scope, thisObject->toObject(scope.engine));
+ if (!instance)
+ RETURN_UNDEFINED();
+
+ uint len = instance->getLength();
+ int relativeStart = argc > 1 ? argv[1].toInteger() : 0;
+ int relativeEnd = len;
+ if (argc > 2 && !argv[2].isUndefined()) {
+ relativeEnd = argv[2].toInteger();
+ }
+ uint k = 0;
+ uint fin = 0;
+
+ if (relativeStart < 0) {
+ k = std::max(len+relativeStart, uint(0));
+ } else {
+ k = std::min(uint(relativeStart), len);
+ }
+
+ if (relativeEnd < 0) {
+ fin = std::max(len + relativeEnd, uint(0));
+ } else {
+ fin = std::min(uint(relativeEnd), len);
+ }
+
+ while (k < fin) {
+ instance->setIndexed(k, argv[0], QV4::Object::DoThrowOnRejection);
+ k++;
+ }
+
+ return instance.asReturnedValue();
+}
+
ReturnedValue ArrayPrototype::method_some(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
@@ -873,12 +1267,15 @@ ReturnedValue ArrayPrototype::method_map(const FunctionObject *b, const Value *t
if (!instance)
RETURN_UNDEFINED();
- uint len = instance->getLength();
+ qint64 len = instance->getLength();
if (!argc || !argv->isFunctionObject())
THROW_TYPE_ERROR();
const FunctionObject *callback = static_cast<const FunctionObject *>(argv);
+ if (len > UINT_MAX - 1)
+ return scope.engine->throwRangeError(QString::fromLatin1("Array length out of range."));
+
ScopedArrayObject a(scope, scope.engine->newArrayObject());
a->arrayReserve(len);
a->setArrayLengthUnchecked(len);
@@ -1041,3 +1438,20 @@ ReturnedValue ArrayPrototype::method_reduceRight(const FunctionObject *b, const
return acc->asReturnedValue();
}
+ReturnedValue ArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedObject O(scope, thisObject->toObject(scope.engine));
+ if (!O)
+ RETURN_UNDEFINED();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue ArrayPrototype::method_get_species(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h
index 3825a600a2..f1acfed7e7 100644
--- a/src/qml/jsruntime/qv4arrayobject_p.h
+++ b/src/qml/jsruntime/qv4arrayobject_p.h
@@ -79,9 +79,13 @@ struct ArrayPrototype: ArrayObject
void init(ExecutionEngine *engine, Object *ctor);
static ReturnedValue method_isArray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_from(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_of(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_copyWithin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_find(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_findIndex(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_join(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -93,15 +97,23 @@ struct ArrayPrototype: ArrayObject
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_splice(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_unshift(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_includes(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_every(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fill(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_some(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_map(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_filter(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduce(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_reduceRight(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ // while this function is implemented here, it's the same for many other JS classes, so the corresponding JS function
+ // is instantiated in the engine, and it can be added to any JS object through Object::addSymbolSpecies()
+ static ReturnedValue method_get_species(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp
index 020e519e74..78cca8d525 100644
--- a/src/qml/jsruntime/qv4context.cpp
+++ b/src/qml/jsruntime/qv4context.cpp
@@ -53,7 +53,42 @@ using namespace QV4;
DEFINE_MANAGED_VTABLE(ExecutionContext);
DEFINE_MANAGED_VTABLE(CallContext);
-DEFINE_MANAGED_VTABLE(CatchContext);
+
+Heap::CallContext *ExecutionContext::newBlockContext(CppStackFrame *frame, int blockIndex)
+{
+ Function *function = frame->v4Function;
+
+ Heap::InternalClass *ic = function->compilationUnit->runtimeBlocks.at(blockIndex);
+ uint nLocals = ic->size;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = function->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, ic);
+ c->init();
+ c->type = Heap::ExecutionContext::Type_BlockContext;
+
+ Heap::ExecutionContext *outer = static_cast<Heap::ExecutionContext *>(frame->context()->m());
+ c->outer.set(v4, outer);
+ c->function.set(v4, static_cast<Heap::FunctionObject *>(frame->jsFrame->function.m()));
+
+ c->locals.size = nLocals;
+ c->locals.alloc = nLocals;
+
+ return c;
+}
+
+Heap::CallContext *ExecutionContext::cloneBlockContext(Heap::CallContext *context)
+{
+ uint nLocals = context->locals.alloc;
+ size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * nLocals;
+
+ ExecutionEngine *v4 = context->internalClass->engine;
+ Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, context->internalClass);
+ memcpy(c, context, requiredMemory);
+
+ return c;
+
+}
Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
{
@@ -75,7 +110,7 @@ Heap::CallContext *ExecutionContext::newCallContext(CppStackFrame *frame)
uint nLocals = compiledFunction->nLocals;
c->locals.size = nLocals;
c->locals.alloc = localsAndFormals;
- // memory allocated from the JS heap is 0 initialized, so check if undefined is 0
+ // memory allocated from the JS heap is 0 initialized, so check if empty is 0
Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0);
Value *args = c->locals.values + nLocals;
@@ -96,11 +131,14 @@ Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with)
return c;
}
-Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue)
+Heap::ExecutionContext *ExecutionContext::newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName)
{
- Scope scope(this);
- ScopedValue e(scope, exceptionValue);
- return engine()->memoryManager->alloc<CatchContext>(d(), exceptionVarName, e);
+ Scope scope(frame->context());
+ ScopedString name(scope, exceptionVarName);
+ ScopedValue val(scope, scope.engine->catchException(nullptr));
+ ScopedContext ctx(scope, newBlockContext(frame, blockIndex));
+ ctx->setProperty(name, val);
+ return ctx->d();
}
void ExecutionContext::createMutableBinding(String *name, bool deletable)
@@ -132,6 +170,8 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
activation = ctx->d()->activation;
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
+ // never create activation records on block contexts
default:
break;
}
@@ -143,33 +183,19 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable)
ScopedProperty desc(scope);
PropertyAttributes attrs(Attr_Data);
attrs.setConfigurable(deletable);
- activation->__defineOwnProperty__(scope.engine, name, desc, attrs);
-}
-
-void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName,
- const Value &exceptionValue)
-{
- Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext);
- outer.set(internalClass->engine, outerContext);
-
- this->exceptionVarName.set(internalClass->engine, exceptionVarName);
- this->exceptionValue.set(internalClass->engine, exceptionValue);
+ if (!activation->__defineOwnProperty__(scope.engine, name, desc, attrs))
+ scope.engine->throwTypeError();
}
bool ExecutionContext::deleteProperty(String *name)
{
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return false;
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -200,21 +226,13 @@ bool ExecutionContext::deleteProperty(String *name)
ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value)
{
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
QV4::ExecutionEngine *v4 = engine();
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d())) {
- c->exceptionValue.set(v4, value);
- return NoError;
- }
- break;
- }
case Heap::ExecutionContext::Type_WithContext: {
Scope scope(v4);
ScopedObject w(scope, ctx->activation);
@@ -225,6 +243,7 @@ ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value
}
break;
}
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
uint index = c->internalClass->find(id);
@@ -267,15 +286,10 @@ ReturnedValue ExecutionContext::getProperty(String *name)
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
uint index = c->internalClass->find(id);
if (index < UINT_MAX)
@@ -308,16 +322,11 @@ ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base)
Heap::ExecutionContext *ctx = d();
for (; ctx; ctx = ctx->outer) {
switch (ctx->type) {
- case Heap::ExecutionContext::Type_CatchContext: {
- Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx);
- if (c->exceptionVarName->isEqualTo(name->d()))
- return c->exceptionValue.asReturnedValue();
- break;
- }
+ case Heap::ExecutionContext::Type_BlockContext:
case Heap::ExecutionContext::Type_CallContext: {
Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
uint index = c->internalClass->find(id);
if (index < UINT_MAX)
diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h
index 512bfa06d8..26e64728dc 100644
--- a/src/qml/jsruntime/qv4context_p.h
+++ b/src/qml/jsruntime/qv4context_p.h
@@ -55,23 +55,8 @@
QT_BEGIN_NAMESPACE
-class QObject;
-class QQmlContextData;
-
namespace QV4 {
-namespace CompiledData {
-struct CompilationUnitBase;
-struct Function;
-}
-
-struct Function;
-struct Identifier;
-struct CallContext;
-struct CatchContext;
-struct QmlContext;
-struct QQmlContextWrapper;
-
struct CallData
{
enum Offsets {
@@ -113,8 +98,6 @@ Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value));
namespace Heap {
-struct QmlContext;
-
#define ExecutionContextMembers(class, Member) \
Member(class, Pointer, ExecutionContext *, outer) \
Member(class, Pointer, Object *, activation)
@@ -124,9 +107,9 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
enum ContextType {
Type_GlobalContext = 0x1,
- Type_CatchContext = 0x2,
- Type_WithContext = 0x3,
- Type_QmlContext = 0x4,
+ Type_WithContext = 0x2,
+ Type_QmlContext = 0x3,
+ Type_BlockContext = 0x4,
Type_CallContext = 0x5
};
@@ -137,6 +120,10 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) {
type = t;
}
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
quint32 type : 8;
quint32 nArgs : 24;
#if QT_POINTER_SIZE == 8
@@ -181,16 +168,6 @@ Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0);
//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData));
//#endif
-#define CatchContextMembers(class, Member) \
- Member(class, Pointer, String *, exceptionVarName) \
- Member(class, HeapValue, HeapValue, exceptionValue)
-
-DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) {
- DECLARE_MARKOBJECTS(CatchContext);
-
- void init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue);
-};
-Q_STATIC_ASSERT(std::is_trivial< CatchContext >::value);
}
@@ -204,9 +181,11 @@ struct Q_QML_EXPORT ExecutionContext : public Managed
Q_MANAGED_TYPE(ExecutionContext)
V4_INTERNALCLASS(ExecutionContext)
+ static Heap::CallContext *newBlockContext(QV4::CppStackFrame *frame, int blockIndex);
+ static Heap::CallContext *cloneBlockContext(Heap::CallContext *context);
static Heap::CallContext *newCallContext(QV4::CppStackFrame *frame);
Heap::ExecutionContext *newWithContext(Heap::Object *with);
- Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue);
+ static Heap::ExecutionContext *newCatchContext(CppStackFrame *frame, int blockIndex, Heap::String *exceptionVarName);
void createMutableBinding(String *name, bool deletable);
@@ -239,11 +218,6 @@ struct Q_QML_EXPORT CallContext : public ExecutionContext
}
};
-struct CatchContext : public ExecutionContext
-{
- V4_MANAGED(CatchContext, ExecutionContext)
-};
-
inline CallContext *ExecutionContext::asCallContext()
{
return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : nullptr;
diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp
index d894d909ff..354eaad7dc 100644
--- a/src/qml/jsruntime/qv4dataview.cpp
+++ b/src/qml/jsruntime/qv4dataview.cpp
@@ -40,6 +40,7 @@
#include "qv4dataview_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/private/qnumeric_p.h>
#include "qendian.h"
@@ -69,7 +70,7 @@ ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Val
if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength)
return scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range"));
- Scoped<DataView> a(scope, scope.engine->memoryManager->allocObject<DataView>());
+ Scoped<DataView> a(scope, scope.engine->memoryManager->allocate<DataView>());
a->d()->buffer.set(scope.engine, buffer->d());
a->d()->byteLength = byteLength;
a->d()->byteOffset = byteOffset;
@@ -85,38 +86,41 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
defineDefaultProperty(engine->id_constructor(), (o = ctor));
defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 0);
- defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 0);
- defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 0);
-
- defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 0);
- defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 0);
- defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 0);
- defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 0);
- defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 0);
+ defineDefaultProperty(QStringLiteral("getInt8"), method_getChar<signed char>, 1);
+ defineDefaultProperty(QStringLiteral("getUint8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getInt16"), method_get<short>, 1);
+ defineDefaultProperty(QStringLiteral("getUint16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getInt32"), method_get<int>, 1);
+ defineDefaultProperty(QStringLiteral("getUint32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat32"), method_getFloat<float>, 1);
+ defineDefaultProperty(QStringLiteral("getFloat64"), method_getFloat<double>, 1);
+
+ defineDefaultProperty(QStringLiteral("setInt8"), method_setChar<signed char>, 2);
+ defineDefaultProperty(QStringLiteral("setUint8"), method_setChar<unsigned char>, 2);
+ defineDefaultProperty(QStringLiteral("setInt16"), method_set<short>, 2);
+ defineDefaultProperty(QStringLiteral("setUint16"), method_set<unsigned short>, 2);
+ defineDefaultProperty(QStringLiteral("setInt32"), method_set<int>, 2);
+ defineDefaultProperty(QStringLiteral("setUint32"), method_set<unsigned int>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat32"), method_setFloat<float>, 2);
+ defineDefaultProperty(QStringLiteral("setFloat64"), method_setFloat<double>, 2);
+
+ ScopedString name(scope, engine->newString(QStringLiteral("DataView")));
+ defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
// For backword compatibility
- defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 0);
- defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 0);
- defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 0);
- defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0);
+ defineDefaultProperty(QStringLiteral("getUInt8"), method_getChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt16"), method_get<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("getUInt32"), method_get<unsigned int>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt8"), method_setChar<unsigned char>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt16"), method_set<unsigned short>, 1);
+ defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 1);
}
ReturnedValue DataViewPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp
index 5bbe312146..092c36e52a 100644
--- a/src/qml/jsruntime/qv4dateobject.cpp
+++ b/src/qml/jsruntime/qv4dateobject.cpp
@@ -44,6 +44,7 @@
#include "qv4runtime_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/QDebug>
#include <QtCore/QDateTime>
@@ -89,9 +90,6 @@ static const double msPerMinute = 60000.0;
static const double msPerHour = 3600000.0;
static const double msPerDay = 86400000.0;
-// The current *standard* time offset, regardless of DST:
-static double LocalTZA = 0.0; // initialized at startup
-
static inline double TimeWithinDay(double t)
{
double r = ::fmod(t, msPerDay);
@@ -318,14 +316,14 @@ static inline double MakeDate(double day, double time)
against the ECMAScript spec is https://github.com/tc39/ecma262/issues/725
*/
-static inline double DaylightSavingTA(double t) // t is a UTC time
+static inline double DaylightSavingTA(double t, double localTZA) // t is a UTC time
{
return QTimeZone::systemTimeZone().offsetFromUtc(
- QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - LocalTZA;
+ QDateTime::fromMSecsSinceEpoch(qint64(t), Qt::UTC)) * 1e3 - localTZA;
}
#else
// This implementation fails to take account of past changes in standard offset.
-static inline double DaylightSavingTA(double t)
+static inline double DaylightSavingTA(double t, double /*localTZA*/)
{
struct tm tmtm;
#if defined(Q_CC_MSVC)
@@ -348,19 +346,19 @@ static inline double DaylightSavingTA(double t)
}
#endif // USE_QTZ_SYSTEM_ZONE
-static inline double LocalTime(double t)
+static inline double LocalTime(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t + LocalTZA + DaylightSavingTA(t);
+ return t + localTZA + DaylightSavingTA(t, localTZA);
}
// The spec does note [*] that UTC and LocalTime are not quite mutually inverse.
// [*] http://www.ecma-international.org/ecma-262/7.0/index.html#sec-utc-t
-static inline double UTC(double t)
+static inline double UTC(double t, double localTZA)
{
// Flawed, yet verbatim from the spec:
- return t - LocalTZA - DaylightSavingTA(t - LocalTZA);
+ return t - localTZA - DaylightSavingTA(t - localTZA, localTZA);
}
static inline double currentTime()
@@ -377,7 +375,7 @@ static inline double TimeClip(double t)
return Primitive::toInteger(t) + 0;
}
-static inline double ParseString(const QString &s)
+static inline double ParseString(const QString &s, double localTZA)
{
/*
First, try the format defined in ECMA 262's "Date Time String Format";
@@ -533,7 +531,7 @@ static inline double ParseString(const QString &s)
if (seenZ)
t -= offset * offsetSign * 60 * 1000;
else if (seenT) // No zone specified, treat date-time as local time
- t = UTC(t);
+ t = UTC(t, localTZA);
// else: treat plain date as already in UTC
return t;
}
@@ -621,12 +619,12 @@ static inline QDateTime ToDateTime(double t, Qt::TimeSpec spec)
return QDateTime::fromMSecsSinceEpoch(t, Qt::UTC).toTimeSpec(spec);
}
-static inline QString ToString(double t)
+static inline QString ToString(double t, double localTZA)
{
if (std::isnan(t))
return QStringLiteral("Invalid Date");
QString str = ToDateTime(t, Qt::LocalTime).toString() + QLatin1String(" GMT");
- double tzoffset = LocalTZA + DaylightSavingTA(t);
+ double tzoffset = localTZA + DaylightSavingTA(t, localTZA);
if (tzoffset) {
int hours = static_cast<int>(::fabs(tzoffset) / 1000 / 60 / 60);
int mins = int(::fabs(tzoffset) / 1000 / 60) % 60;
@@ -730,7 +728,7 @@ void Heap::DateObject::init(const QTime &time)
*/
static const double d = MakeDay(1925, 5, 8);
double t = MakeTime(time.hour(), time.minute(), time.second(), time.msec());
- date = TimeClip(UTC(MakeDate(d, t)));
+ date = TimeClip(UTC(MakeDate(d, t), internalClass->engine->localTZA));
}
QDateTime DateObject::toQDateTime() const
@@ -747,13 +745,14 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope)
ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc)
{
+ ExecutionEngine *e = that->engine();
double t = 0;
if (argc == 0)
t = currentTime();
else if (argc == 1) {
- Scope scope(that->engine());
+ Scope scope(e);
ScopedValue arg(scope, argv[0]);
if (DateObject *d = arg->as<DateObject>()) {
t = d->date();
@@ -761,7 +760,7 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
arg = RuntimeHelpers::toPrimitive(arg, PREFERREDTYPE_HINT);
if (String *s = arg->stringValue())
- t = ParseString(s->toQString());
+ t = ParseString(s->toQString(), e->localTZA);
else
t = TimeClip(arg->toNumber());
}
@@ -778,16 +777,17 @@ ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Valu
if (year >= 0 && year <= 99)
year += 1900;
t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms));
- t = TimeClip(UTC(t));
+ t = TimeClip(UTC(t, e->localTZA));
}
- return Encode(that->engine()->newDateObject(Primitive::fromDouble(t)));
+ return Encode(e->newDateObject(Primitive::fromDouble(t)));
}
ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int)
{
+ ExecutionEngine *e = m->engine();
double t = currentTime();
- return m->engine()->newString(ToString(t))->asReturnedValue();
+ return e->newString(ToString(t, e->localTZA))->asReturnedValue();
}
void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -796,7 +796,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7));
- LocalTZA = getLocalTZA();
+ engine->localTZA = getLocalTZA();
ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1);
ctor->defineDefaultProperty(QStringLiteral("UTC"), method_UTC, 7);
@@ -853,15 +853,14 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor)
QString toGmtString(QStringLiteral("toGMTString"));
ScopedString us(scope, engine->newIdentifier(toUtcString));
ScopedString gs(scope, engine->newIdentifier(toGmtString));
- ExecutionContext *global = engine->rootContext();
- ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(global, us, method_toUTCString));
- toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ScopedFunctionObject toUtcGmtStringFn(scope, FunctionObject::createBuiltinFunction(engine, us, method_toUTCString, 0));
defineDefaultProperty(us, toUtcGmtStringFn);
defineDefaultProperty(gs, toUtcGmtStringFn);
}
defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0);
defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
}
double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
@@ -872,12 +871,12 @@ double DatePrototype::getThisDate(ExecutionEngine *v4, const Value *thisObject)
return 0;
}
-ReturnedValue DatePrototype::method_parse(const FunctionObject *, const Value *, const Value *argv, int argc)
+ReturnedValue DatePrototype::method_parse(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
if (!argc)
return Encode(qt_qnan());
else
- return Encode(ParseString(argv[0].toQString()));
+ return Encode(ParseString(argv[0].toQString(), f->engine()->localTZA));
}
ReturnedValue DatePrototype::method_UTC(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -909,7 +908,7 @@ ReturnedValue DatePrototype::method_toString(const FunctionObject *b, const Valu
{
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
- return Encode(v4->newString(ToString(t)));
+ return Encode(v4->newString(ToString(t, v4->localTZA)));
}
ReturnedValue DatePrototype::method_toDateString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -966,7 +965,7 @@ ReturnedValue DatePrototype::method_getYear(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t)) - 1900;
+ t = YearFromTime(LocalTime(t, v4->localTZA)) - 1900;
return Encode(t);
}
@@ -975,7 +974,7 @@ ReturnedValue DatePrototype::method_getFullYear(const FunctionObject *b, const V
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = YearFromTime(LocalTime(t));
+ t = YearFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -993,7 +992,7 @@ ReturnedValue DatePrototype::method_getMonth(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MonthFromTime(LocalTime(t));
+ t = MonthFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1011,7 +1010,7 @@ ReturnedValue DatePrototype::method_getDate(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = DateFromTime(LocalTime(t));
+ t = DateFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1029,7 +1028,7 @@ ReturnedValue DatePrototype::method_getDay(const FunctionObject *b, const Value
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = WeekDay(LocalTime(t));
+ t = WeekDay(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1047,7 +1046,7 @@ ReturnedValue DatePrototype::method_getHours(const FunctionObject *b, const Valu
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = HourFromTime(LocalTime(t));
+ t = HourFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1065,7 +1064,7 @@ ReturnedValue DatePrototype::method_getMinutes(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = MinFromTime(LocalTime(t));
+ t = MinFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1083,7 +1082,7 @@ ReturnedValue DatePrototype::method_getSeconds(const FunctionObject *b, const Va
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = SecFromTime(LocalTime(t));
+ t = SecFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1101,7 +1100,7 @@ ReturnedValue DatePrototype::method_getMilliseconds(const FunctionObject *b, con
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = msFromTime(LocalTime(t));
+ t = msFromTime(LocalTime(t, v4->localTZA));
return Encode(t);
}
@@ -1119,7 +1118,7 @@ ReturnedValue DatePrototype::method_getTimezoneOffset(const FunctionObject *b, c
ExecutionEngine *v4 = b->engine();
double t = getThisDate(v4, thisObject);
if (!std::isnan(t))
- t = (t - LocalTime(t)) / msPerMinute;
+ t = (t - LocalTime(t, v4->localTZA)) / msPerMinute;
return Encode(t);
}
@@ -1144,13 +1143,13 @@ ReturnedValue DatePrototype::method_setMilliseconds(const FunctionObject *b, con
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double ms = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))));
+ self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)), v4->localTZA)));
return Encode(self->date());
}
@@ -1178,7 +1177,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double sec = argc ? argv[0].toNumber() : qt_qnan();
@@ -1187,7 +1186,7 @@ ReturnedValue DatePrototype::method_setSeconds(const FunctionObject *b, const Va
double ms = (argc < 2) ? msFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1214,7 +1213,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double min = argc ? argv[0].toNumber() : qt_qnan();
@@ -1226,7 +1225,7 @@ ReturnedValue DatePrototype::method_setMinutes(const FunctionObject *b, const Va
double ms = (argc < 3) ? msFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1254,7 +1253,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double hour = argc ? argv[0].toNumber() : qt_qnan();
@@ -1269,7 +1268,7 @@ ReturnedValue DatePrototype::method_setHours(const FunctionObject *b, const Valu
double ms = (argc < 4) ? msFromTime(t) : argv[3].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms))));
+ t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1298,13 +1297,13 @@ ReturnedValue DatePrototype::method_setDate(const FunctionObject *b, const Value
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double date = argc ? argv[0].toNumber() : qt_qnan();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1334,7 +1333,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
double month = argc ? argv[0].toNumber() : qt_qnan();
@@ -1343,7 +1342,7 @@ ReturnedValue DatePrototype::method_setMonth(const FunctionObject *b, const Valu
double date = (argc < 2) ? DateFromTime(t) : argv[1].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1374,7 +1373,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value
if (std::isnan(t))
t = 0;
else
- t = LocalTime(t);
+ t = LocalTime(t, v4->localTZA);
double year = argc ? argv[0].toNumber() : qt_qnan();
double r;
if (std::isnan(year)) {
@@ -1383,7 +1382,7 @@ ReturnedValue DatePrototype::method_setYear(const FunctionObject *b, const Value
if ((Primitive::toInteger(year) >= 0) && (Primitive::toInteger(year) <= 99))
year += 1900;
r = MakeDay(year, MonthFromTime(t), DateFromTime(t));
- r = UTC(MakeDate(r, TimeWithinDay(t)));
+ r = UTC(MakeDate(r, TimeWithinDay(t)), v4->localTZA);
r = TimeClip(r);
}
self->setDate(r);
@@ -1413,7 +1412,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
if (!self)
return v4->throwTypeError();
- double t = LocalTime(self->date());
+ double t = LocalTime(self->date(), v4->localTZA);
if (v4->hasException)
return QV4::Encode::undefined();
if (std::isnan(t))
@@ -1427,7 +1426,7 @@ ReturnedValue DatePrototype::method_setFullYear(const FunctionObject *b, const V
double date = (argc < 3) ? DateFromTime(t) : argv[2].toNumber();
if (v4->hasException)
return QV4::Encode::undefined();
- t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))));
+ t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)), v4->localTZA));
self->setDate(t);
return Encode(self->date());
}
@@ -1518,7 +1517,23 @@ ReturnedValue DatePrototype::method_toJSON(const FunctionObject *b, const Value
return toIso->call(O, nullptr, 0);
}
-void DatePrototype::timezoneUpdated()
+ReturnedValue DatePrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!thisObject->isObject() || !argc || !argv->isString())
+ return e->throwTypeError();
+
+ String *hint = argv->stringValue();
+ Identifier id = hint->identifier();
+ if (id == e->id_default()->identifier())
+ hint = e->id_string();
+ else if (id != e->id_string()->identifier() && id != e->id_number()->identifier())
+ return e->throwTypeError();
+
+ return RuntimeHelpers::ordinaryToPrimitive(e, static_cast<const Object *>(thisObject), hint);
+}
+
+void DatePrototype::timezoneUpdated(ExecutionEngine *e)
{
- LocalTZA = getLocalTZA();
+ e->localTZA = getLocalTZA();
}
diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h
index 2b9a580288..0a5a3954d1 100644
--- a/src/qml/jsruntime/qv4dateobject_p.h
+++ b/src/qml/jsruntime/qv4dateobject_p.h
@@ -101,7 +101,7 @@ struct DateObject: Object {
template<>
inline const DateObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_DateObject ? static_cast<const DateObject *>(this) : nullptr;
}
struct DateCtor: FunctionObject
@@ -169,8 +169,9 @@ struct DatePrototype: Object
static ReturnedValue method_toUTCString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toISOString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toJSON(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int);
- static void timezoneUpdated();
+ static void timezoneUpdated(ExecutionEngine *e);
};
}
diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp
index 0ed0df89a9..1073a2abab 100644
--- a/src/qml/jsruntime/qv4engine.cpp
+++ b/src/qml/jsruntime/qv4engine.cpp
@@ -42,6 +42,9 @@
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <qv4objectiterator_p.h>
+#include <qv4setiterator_p.h>
+#include <qv4mapiterator_p.h>
+#include <qv4arrayiterator_p.h>
#include <qv4arrayobject_p.h>
#include <qv4booleanobject_p.h>
#include <qv4globalobject_p.h>
@@ -52,6 +55,9 @@
#include <qv4numberobject_p.h>
#include <qv4regexpobject_p.h>
#include <qv4regexp_p.h>
+#include "qv4symbol_p.h"
+#include "qv4setobject_p.h"
+#include "qv4mapobject_p.h"
#include <qv4variantobject_p.h>
#include <qv4runtime_p.h>
#include <private/qv4mm_p.h>
@@ -63,7 +69,15 @@
#include "qv4debugging_p.h"
#include "qv4profiling_p.h"
#include "qv4executableallocator_p.h"
+#include "qv4iterator_p.h"
+#include "qv4stringiterator_p.h"
+#include "qv4generatorobject_p.h"
+#include "qv4reflect_p.h"
+
+#if QT_CONFIG(qml_sequence_object)
#include "qv4sequenceobject_p.h"
+#endif
+
#include "qv4qobjectwrapper_p.h"
#include "qv4memberdata_p.h"
#include "qv4arraybuffer_p.h"
@@ -76,7 +90,9 @@
#include <private/qqmlvaluetype_p.h>
#include <private/qqmllistwrapper_p.h>
#include <private/qqmllist_p.h>
+#if QT_CONFIG(qml_locale)
#include <private/qqmllocale_p.h>
+#endif
#include <QtCore/QTextStream>
#include <QDateTime>
@@ -110,7 +126,7 @@ ReturnedValue throwTypeError(const FunctionObject *b, const QV4::Value *, const
#ifdef V4_BOOTSTRAP
QJSEngine *ExecutionEngine::jsEngine() const
{
- return v8Engine->publicEngine();
+ return publicEngine;
}
QQmlEngine *ExecutionEngine::qmlEngine() const
@@ -121,7 +137,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const
qint32 ExecutionEngine::maxCallDepth = -1;
-ExecutionEngine::ExecutionEngine()
+ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine)
: executableAllocator(new QV4::ExecutableAllocator)
, regExpAllocator(new QV4::ExecutableAllocator)
, bumperPointerAllocator(new WTF::BumpPointerAllocator)
@@ -129,6 +145,7 @@ ExecutionEngine::ExecutionEngine()
, gcStack(new WTF::PageAllocation)
, globalCode(nullptr)
, v8Engine(nullptr)
+ , publicEngine(jsEngine)
, argumentsAccessors(nullptr)
, nArgumentsAccessors(0)
, m_engineId(engineSerial.fetchAndAddOrdered(1))
@@ -181,27 +198,44 @@ ExecutionEngine::ExecutionEngine()
}
exceptionValue = jsAlloca(1);
+ *exceptionValue = Encode::undefined();
globalObject = static_cast<Object *>(jsAlloca(1));
jsObjects = jsAlloca(NJSObjects);
typedArrayPrototype = static_cast<Object *>(jsAlloca(NTypedArrayTypes));
typedArrayCtors = static_cast<FunctionObject *>(jsAlloca(NTypedArrayTypes));
jsStrings = jsAlloca(NJSStrings);
+ jsSymbols = jsAlloca(NJSSymbols);
// set up stack limits
jsStackLimit = jsStackBase + JSStackLimit/sizeof(Value);
identifierTable = new IdentifierTable(this);
- classPool = new InternalClassPool;
+ memset(classes, 0, sizeof(classes));
+ classes[Class_Empty] = memoryManager->allocIC<InternalClass>();
+ classes[Class_Empty]->init(this);
- internalClasses[Class_Empty] = new (classPool) InternalClass(this);
- internalClasses[Class_String] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::String::staticVTable());
- internalClasses[Class_MemberData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
- internalClasses[Class_SimpleArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
- internalClasses[Class_SparseArrayData] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
- internalClasses[Class_ExecutionContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
- internalClasses[Class_QmlContext] = internalClasses[EngineBase::Class_ExecutionContext]->changeVTable(QV4::QmlContext::staticVTable());
- internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ classes[Class_MemberData] = classes[Class_Empty]->changeVTable(QV4::MemberData::staticVTable());
+ classes[Class_SimpleArrayData] = classes[Class_Empty]->changeVTable(QV4::SimpleArrayData::staticVTable());
+ classes[Class_SparseArrayData] = classes[Class_Empty]->changeVTable(QV4::SparseArrayData::staticVTable());
+ classes[Class_ExecutionContext] = classes[Class_Empty]->changeVTable(QV4::ExecutionContext::staticVTable());
+ classes[Class_CallContext] = classes[Class_Empty]->changeVTable(QV4::CallContext::staticVTable());
+ classes[Class_QmlContext] = classes[Class_Empty]->changeVTable(QV4::QmlContext::staticVTable());
+
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope);
+ ic = classes[Class_Empty]->changeVTable(QV4::Object::staticVTable());
+ jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic->d());
+ classes[Class_Object] = ic->changePrototype(objectPrototype()->d());
+ classes[Class_QmlContextWrapper] = classes[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+
+ ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
+ jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic->d(), /*init =*/ false);
+ classes[Class_String] = classes[Class_Empty]->changeVTable(QV4::String::staticVTable())->changePrototype(stringPrototype()->d());
+ Q_ASSERT(stringPrototype()->d() && classes[Class_String]->prototype);
+
+ jsObjects[SymbolProto] = memoryManager->allocate<SymbolPrototype>();
+ classes[Class_Symbol] = classes[EngineBase::Class_Empty]->changeVTable(QV4::Symbol::staticVTable())->changePrototype(symbolPrototype()->d());
jsStrings[String_Empty] = newIdentifier(QString());
jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined"));
@@ -211,6 +245,8 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_boolean] = newIdentifier(QStringLiteral("boolean"));
jsStrings[String_number] = newIdentifier(QStringLiteral("number"));
jsStrings[String_string] = newIdentifier(QStringLiteral("string"));
+ jsStrings[String_default] = newIdentifier(QStringLiteral("default"));
+ jsStrings[String_symbol] = newIdentifier(QStringLiteral("symbol"));
jsStrings[String_object] = newIdentifier(QStringLiteral("object"));
jsStrings[String_function] = newIdentifier(QStringLiteral("function"));
jsStrings[String_length] = newIdentifier(QStringLiteral("length"));
@@ -239,141 +275,175 @@ ExecutionEngine::ExecutionEngine()
jsStrings[String_byteOffset] = newIdentifier(QStringLiteral("byteOffset"));
jsStrings[String_buffer] = newIdentifier(QStringLiteral("buffer"));
jsStrings[String_lastIndex] = newIdentifier(QStringLiteral("lastIndex"));
-
- InternalClass *ic = internalClasses[Class_Empty]->changeVTable(QV4::Object::staticVTable());
- jsObjects[ObjectProto] = memoryManager->allocObject<ObjectPrototype>(ic);
- internalClasses[Class_Object] = ic->changePrototype(objectPrototype()->d());
- internalClasses[EngineBase::Class_QmlContextWrapper] = internalClasses[Class_Object]->changeVTable(QV4::QQmlContextWrapper::staticVTable());
+ jsStrings[String_next] = newIdentifier(QStringLiteral("next"));
+ jsStrings[String_done] = newIdentifier(QStringLiteral("done"));
+ jsStrings[String_return] = newIdentifier(QStringLiteral("return"));
+
+ jsSymbols[Symbol_hasInstance] = Symbol::create(this, QStringLiteral("@Symbol.hasInstance"));
+ jsSymbols[Symbol_isConcatSpreadable] = Symbol::create(this, QStringLiteral("@Symbol.isConcatSpreadable"));
+ jsSymbols[Symbol_iterator] = Symbol::create(this, QStringLiteral("@Symbol.iterator"));
+ jsSymbols[Symbol_match] = Symbol::create(this, QStringLiteral("@Symbol.match"));
+ jsSymbols[Symbol_replace] = Symbol::create(this, QStringLiteral("@Symbol.replace"));
+ jsSymbols[Symbol_search] = Symbol::create(this, QStringLiteral("@Symbol.search"));
+ jsSymbols[Symbol_species] = Symbol::create(this, QStringLiteral("@Symbol.species"));
+ jsSymbols[Symbol_split] = Symbol::create(this, QStringLiteral("@Symbol.split"));
+ jsSymbols[Symbol_toPrimitive] = Symbol::create(this, QStringLiteral("@Symbol.toPrimitive"));
+ jsSymbols[Symbol_toStringTag] = Symbol::create(this, QStringLiteral("@Symbol.toStringTag"));
+ jsSymbols[Symbol_unscopables] = Symbol::create(this, QStringLiteral("@Symbol.unscopables"));
ic = newInternalClass(ArrayPrototype::staticVTable(), objectPrototype());
- Q_ASSERT(ic->prototype);
- ic = ic->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable);
- Q_ASSERT(ic->prototype);
- jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic, objectPrototype());
- internalClasses[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
- jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>();
-
- InternalClass *argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- internalClasses[EngineBase::Class_ArgumentsObject] = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ ic = ic->addMember(id_length()->identifier(), Attr_NotConfigurable|Attr_NotEnumerable);
+ Q_ASSERT(ic->d()->prototype);
+ jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(ic->d());
+ classes[Class_ArrayObject] = ic->changePrototype(arrayPrototype()->d());
+ jsObjects[PropertyListProto] = memoryManager->allocate<PropertyListPrototype>();
+
+ Scoped<InternalClass> argsClass(scope);
+ argsClass = newInternalClass(ArgumentsObject::staticVTable(), objectPrototype());
+ argsClass = argsClass->addMember(id_length()->identifier(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->identifier(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_ArgumentsObject] = argsClass->addMember(id_callee()->identifier(), Attr_Data|Attr_NotEnumerable);
argsClass = newInternalClass(StrictArgumentsObject::staticVTable(), objectPrototype());
- argsClass = argsClass->addMember(id_length(), Attr_NotEnumerable);
- argsClass = argsClass->addMember(id_callee(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- internalClasses[EngineBase::Class_StrictArgumentsObject] = argsClass->addMember(id_caller(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
+ argsClass = argsClass->addMember(id_length()->identifier(), Attr_NotEnumerable);
+ argsClass = argsClass->addMember(symbol_iterator()->identifier(), Attr_Data|Attr_NotEnumerable);
+ classes[Class_StrictArgumentsObject] = argsClass->addMember(id_callee()->identifier(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
*static_cast<Value *>(globalObject) = newObject();
Q_ASSERT(globalObject->d()->vtable());
initRootContext();
ic = newInternalClass(QV4::StringObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_length(), Attr_ReadOnly);
- jsObjects[StringProto] = memoryManager->allocObject<StringPrototype>(ic);
- internalClasses[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
- Q_ASSERT(internalClasses[EngineBase::Class_StringObject]->find(id_length()) == Heap::StringObject::LengthPropertyIndex);
+ ic = ic->addMember(id_length()->identifier(), Attr_ReadOnly);
+ classes[Class_StringObject] = ic->changePrototype(stringPrototype()->d());
+ Q_ASSERT(classes[Class_StringObject]->find(id_length()->identifier()) == Heap::StringObject::LengthPropertyIndex);
- jsObjects[NumberProto] = memoryManager->allocObject<NumberPrototype>();
- jsObjects[BooleanProto] = memoryManager->allocObject<BooleanPrototype>();
- jsObjects[DateProto] = memoryManager->allocObject<DatePrototype>();
+ classes[Class_SymbolObject] = newInternalClass(QV4::SymbolObject::staticVTable(), symbolPrototype());
+
+ jsObjects[NumberProto] = memoryManager->allocate<NumberPrototype>();
+ jsObjects[BooleanProto] = memoryManager->allocate<BooleanPrototype>();
+ jsObjects[DateProto] = memoryManager->allocate<DatePrototype>();
uint index;
ic = newInternalClass(QV4::FunctionPrototype::staticVTable(), objectPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable, &index);
+ ic = ic->addMember(id_prototype()->identifier(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic, objectPrototype());
+ jsObjects[FunctionProto] = memoryManager->allocObject<FunctionPrototype>(ic->d());
ic = newInternalClass(FunctionObject::staticVTable(), functionPrototype());
- ic = ic->addMember(id_prototype(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ ic = ic->addMember(id_prototype()->identifier(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_Prototype);
- internalClasses[EngineBase::Class_FunctionObject] = ic;
- ic = ic->addMember(id_name(), Attr_ReadOnly, &index);
+ classes[Class_FunctionObject] = ic->d();
+ ic = ic->addMember(id_name()->identifier(), Attr_ReadOnly, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Name);
ic = ic->changeVTable(ScriptFunction::staticVTable());
- internalClasses[EngineBase::Class_ScriptFunction] = ic->addMember(id_length(), Attr_ReadOnly, &index);
+ ic = ic->addMember(id_length()->identifier(), Attr_ReadOnly_ButConfigurable, &index);
Q_ASSERT(index == Heap::ScriptFunction::Index_Length);
- internalClasses[EngineBase::Class_ObjectProto] = internalClasses[Class_Object]->addMember(id_constructor(), Attr_NotEnumerable, &index);
+ classes[Class_ScriptFunction] = ic->d();
+ ic = ic->changeVTable(GeneratorFunction::staticVTable());
+ classes[Class_GeneratorFunction] = ic->d();
+ classes[Class_ObjectProto] = classes[Class_Object]->addMember(id_constructor()->identifier(), Attr_NotEnumerable, &index);
Q_ASSERT(index == Heap::FunctionObject::Index_ProtoConstructor);
- Scope scope(this);
+ jsObjects[GeneratorProto] = memoryManager->allocObject<GeneratorPrototype>(classes[Class_Object]);
+ classes[Class_GeneratorObject] = newInternalClass(QV4::GeneratorObject::staticVTable(), generatorPrototype());
+
ScopedString str(scope);
- internalClasses[Class_RegExp] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
+ classes[Class_RegExp] = classes[Class_Empty]->changeVTable(QV4::RegExp::staticVTable());
ic = newInternalClass(QV4::RegExpObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_lastIndex(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
+ ic = ic->addMember(id_lastIndex()->identifier(), Attr_NotEnumerable|Attr_NotConfigurable, &index);
Q_ASSERT(index == RegExpObject::Index_LastIndex);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("source"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("source")))->identifier(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Source);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("global"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("global")))->identifier(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Global);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("ignoreCase")))->identifier(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_IgnoreCase);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline"))), Attr_ReadOnly, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("multiline")))->identifier(), Attr_ReadOnly, &index);
Q_ASSERT(index == RegExpObject::Index_Multiline);
- jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic, objectPrototype());
- internalClasses[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
+ jsObjects[RegExpProto] = memoryManager->allocObject<RegExpPrototype>(ic->d());
+ classes[Class_RegExpObject] = ic->changePrototype(regExpPrototype()->d());
- ic = internalClasses[Class_ArrayObject]->addMember(id_index(), Attr_Data, &index);
+ ic = classes[Class_ArrayObject]->addMember(id_index()->identifier(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayIndex);
- internalClasses[EngineBase::Class_RegExpExecArray] = ic->addMember(id_input(), Attr_Data, &index);
+ classes[Class_RegExpExecArray] = ic->addMember(id_input()->identifier(), Attr_Data, &index);
Q_ASSERT(index == RegExpObject::Index_ArrayInput);
ic = newInternalClass(ErrorObject::staticVTable(), nullptr);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("stack"))), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("stack")))->identifier(), Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Stack);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("fileName")))->identifier(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_FileName);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber"))), Attr_Data|Attr_NotEnumerable, &index);
- internalClasses[EngineBase::Class_ErrorObject] = ic;
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("lineNumber")))->identifier(), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorObject] = ic->d();
Q_ASSERT(index == ErrorObject::Index_LineNumber);
- internalClasses[EngineBase::Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorObjectWithMessage] = ic->addMember((str = newIdentifier(QStringLiteral("message")))->identifier(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorObject::Index_Message);
ic = newInternalClass(ErrorObject::staticVTable(), objectPrototype());
- ic = ic->addMember(id_constructor(), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember(id_constructor()->identifier(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Constructor);
- ic = ic->addMember((str = newIdentifier(QStringLiteral("message"))), Attr_Data|Attr_NotEnumerable, &index);
+ ic = ic->addMember((str = newIdentifier(QStringLiteral("message")))->identifier(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Message);
- internalClasses[EngineBase::Class_ErrorProto] = ic->addMember(id_name(), Attr_Data|Attr_NotEnumerable, &index);
+ classes[Class_ErrorProto] = ic->addMember(id_name()->identifier(), Attr_Data|Attr_NotEnumerable, &index);
Q_ASSERT(index == ErrorPrototype::Index_Name);
- jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(rootContext(), str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack);
- getStackFunction()->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
+ jsObjects[GetStack_Function] = FunctionObject::createBuiltinFunction(this, str = newIdentifier(QStringLiteral("stack")), ErrorObject::method_get_stack, 0);
- jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto], objectPrototype());
- jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
- jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(internalClasses[EngineBase::Class_ErrorProto]->changePrototype(errorPrototype()->d()), errorPrototype());
+ jsObjects[ErrorProto] = memoryManager->allocObject<ErrorPrototype>(classes[Class_ErrorProto]);
+ ic = classes[Class_ErrorProto]->changePrototype(errorPrototype()->d());
+ jsObjects[EvalErrorProto] = memoryManager->allocObject<EvalErrorPrototype>(ic->d());
+ jsObjects[RangeErrorProto] = memoryManager->allocObject<RangeErrorPrototype>(ic->d());
+ jsObjects[ReferenceErrorProto] = memoryManager->allocObject<ReferenceErrorPrototype>(ic->d());
+ jsObjects[SyntaxErrorProto] = memoryManager->allocObject<SyntaxErrorPrototype>(ic->d());
+ jsObjects[TypeErrorProto] = memoryManager->allocObject<TypeErrorPrototype>(ic->d());
+ jsObjects[URIErrorProto] = memoryManager->allocObject<URIErrorPrototype>(ic->d());
- jsObjects[VariantProto] = memoryManager->allocObject<VariantPrototype>();
+ jsObjects[VariantProto] = memoryManager->allocate<VariantPrototype>();
Q_ASSERT(variantPrototype()->prototype() == objectPrototype()->d());
+#if QT_CONFIG(qml_sequence_object)
ic = newInternalClass(SequencePrototype::staticVTable(), SequencePrototype::defaultPrototype(this));
- jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic, SequencePrototype::defaultPrototype(this)));
+ jsObjects[SequenceProto] = ScopedValue(scope, memoryManager->allocObject<SequencePrototype>(ic->d()));
+#endif
ExecutionContext *global = rootContext();
- jsObjects[Object_Ctor] = memoryManager->allocObject<ObjectCtor>(global);
- jsObjects[String_Ctor] = memoryManager->allocObject<StringCtor>(global);
- jsObjects[Number_Ctor] = memoryManager->allocObject<NumberCtor>(global);
- jsObjects[Boolean_Ctor] = memoryManager->allocObject<BooleanCtor>(global);
- jsObjects[Array_Ctor] = memoryManager->allocObject<ArrayCtor>(global);
- jsObjects[Function_Ctor] = memoryManager->allocObject<FunctionCtor>(global);
- jsObjects[Date_Ctor] = memoryManager->allocObject<DateCtor>(global);
- jsObjects[RegExp_Ctor] = memoryManager->allocObject<RegExpCtor>(global);
- jsObjects[Error_Ctor] = memoryManager->allocObject<ErrorCtor>(global);
- jsObjects[EvalError_Ctor] = memoryManager->allocObject<EvalErrorCtor>(global);
- jsObjects[RangeError_Ctor] = memoryManager->allocObject<RangeErrorCtor>(global);
- jsObjects[ReferenceError_Ctor] = memoryManager->allocObject<ReferenceErrorCtor>(global);
- jsObjects[SyntaxError_Ctor] = memoryManager->allocObject<SyntaxErrorCtor>(global);
- jsObjects[TypeError_Ctor] = memoryManager->allocObject<TypeErrorCtor>(global);
- jsObjects[URIError_Ctor] = memoryManager->allocObject<URIErrorCtor>(global);
+
+ jsObjects[Object_Ctor] = memoryManager->allocate<ObjectCtor>(global);
+ jsObjects[String_Ctor] = memoryManager->allocate<StringCtor>(global);
+ jsObjects[Symbol_Ctor] = memoryManager->allocate<SymbolCtor>(global);
+ jsObjects[Number_Ctor] = memoryManager->allocate<NumberCtor>(global);
+ jsObjects[Boolean_Ctor] = memoryManager->allocate<BooleanCtor>(global);
+ jsObjects[Array_Ctor] = memoryManager->allocate<ArrayCtor>(global);
+ jsObjects[Function_Ctor] = memoryManager->allocate<FunctionCtor>(global);
+ jsObjects[GeneratorFunction_Ctor] = memoryManager->allocate<GeneratorFunctionCtor>(global);
+ jsObjects[Date_Ctor] = memoryManager->allocate<DateCtor>(global);
+ jsObjects[RegExp_Ctor] = memoryManager->allocate<RegExpCtor>(global);
+ jsObjects[Error_Ctor] = memoryManager->allocate<ErrorCtor>(global);
+ jsObjects[EvalError_Ctor] = memoryManager->allocate<EvalErrorCtor>(global);
+ jsObjects[RangeError_Ctor] = memoryManager->allocate<RangeErrorCtor>(global);
+ jsObjects[ReferenceError_Ctor] = memoryManager->allocate<ReferenceErrorCtor>(global);
+ jsObjects[SyntaxError_Ctor] = memoryManager->allocate<SyntaxErrorCtor>(global);
+ jsObjects[TypeError_Ctor] = memoryManager->allocate<TypeErrorCtor>(global);
+ jsObjects[URIError_Ctor] = memoryManager->allocate<URIErrorCtor>(global);
+ jsObjects[IteratorProto] = memoryManager->allocate<IteratorPrototype>();
+ jsObjects[ForInIteratorProto] = memoryManager->allocObject<ForInIteratorPrototype>(newInternalClass(ForInIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[MapIteratorProto] = memoryManager->allocObject<MapIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[SetIteratorProto] = memoryManager->allocObject<SetIteratorPrototype>(newInternalClass(SetIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[ArrayIteratorProto] = memoryManager->allocObject<ArrayIteratorPrototype>(newInternalClass(ArrayIteratorPrototype::staticVTable(), iteratorPrototype()));
+ jsObjects[StringIteratorProto] = memoryManager->allocObject<StringIteratorPrototype>(newInternalClass(StringIteratorPrototype::staticVTable(), iteratorPrototype()));
+
+ str = newString(QStringLiteral("get [Symbol.species]"));
+ jsObjects[GetSymbolSpecies] = FunctionObject::createBuiltinFunction(this, str, ArrayPrototype::method_get_species, 0);
static_cast<ObjectPrototype *>(objectPrototype())->init(this, objectCtor());
static_cast<StringPrototype *>(stringPrototype())->init(this, stringCtor());
+ static_cast<SymbolPrototype *>(symbolPrototype())->init(this, symbolCtor());
static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor());
static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor());
static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor());
static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this);
static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor());
static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor());
+ static_cast<GeneratorPrototype *>(generatorPrototype())->init(this, generatorFunctionCtor());
static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor());
static_cast<ErrorPrototype *>(errorPrototype())->init(this, errorCtor());
static_cast<EvalErrorPrototype *>(evalErrorPrototype())->init(this, evalErrorCtor());
@@ -383,25 +453,47 @@ ExecutionEngine::ExecutionEngine()
static_cast<TypeErrorPrototype *>(typeErrorPrototype())->init(this, typeErrorCtor());
static_cast<URIErrorPrototype *>(uRIErrorPrototype())->init(this, uRIErrorCtor());
+ static_cast<IteratorPrototype *>(iteratorPrototype())->init(this);
+ static_cast<ForInIteratorPrototype *>(forInIteratorPrototype())->init(this);
+ static_cast<MapIteratorPrototype *>(mapIteratorPrototype())->init(this);
+ static_cast<SetIteratorPrototype *>(setIteratorPrototype())->init(this);
+ static_cast<ArrayIteratorPrototype *>(arrayIteratorPrototype())->init(this);
+ static_cast<StringIteratorPrototype *>(stringIteratorPrototype())->init(this);
+
static_cast<VariantPrototype *>(variantPrototype())->init();
+
+#if QT_CONFIG(qml_sequence_object)
sequencePrototype()->cast<SequencePrototype>()->init();
+#endif
+
+ jsObjects[Map_Ctor] = memoryManager->allocate<MapCtor>(global);
+ jsObjects[MapProto] = memoryManager->allocate<MapPrototype>();
+ static_cast<MapPrototype *>(mapPrototype())->init(this, mapCtor());
+ jsObjects[Set_Ctor] = memoryManager->allocate<SetCtor>(global);
+ jsObjects[SetProto] = memoryManager->allocate<SetPrototype>();
+ static_cast<SetPrototype *>(setPrototype())->init(this, setCtor());
// typed arrays
- jsObjects[ArrayBuffer_Ctor] = memoryManager->allocObject<ArrayBufferCtor>(global);
- jsObjects[ArrayBufferProto] = memoryManager->allocObject<ArrayBufferPrototype>();
+ jsObjects[ArrayBuffer_Ctor] = memoryManager->allocate<ArrayBufferCtor>(global);
+ jsObjects[ArrayBufferProto] = memoryManager->allocate<ArrayBufferPrototype>();
static_cast<ArrayBufferPrototype *>(arrayBufferPrototype())->init(this, arrayBufferCtor());
- jsObjects[DataView_Ctor] = memoryManager->allocObject<DataViewCtor>(global);
- jsObjects[DataViewProto] = memoryManager->allocObject<DataViewPrototype>();
+ jsObjects[DataView_Ctor] = memoryManager->allocate<DataViewCtor>(global);
+ jsObjects[DataViewProto] = memoryManager->allocate<DataViewPrototype>();
static_cast<DataViewPrototype *>(dataViewPrototype())->init(this, dataViewCtor());
jsObjects[ValueTypeProto] = (Heap::Base *) nullptr;
jsObjects[SignalHandlerProto] = (Heap::Base *) nullptr;
+ jsObjects[IntrinsicTypedArray_Ctor] = memoryManager->allocate<IntrinsicTypedArrayCtor>(global);
+ jsObjects[IntrinsicTypedArrayProto] = memoryManager->allocate<IntrinsicTypedArrayPrototype>();
+ static_cast<IntrinsicTypedArrayPrototype *>(intrinsicTypedArrayPrototype())
+ ->init(this, static_cast<IntrinsicTypedArrayCtor *>(intrinsicTypedArrayCtor()));
+
for (int i = 0; i < Heap::TypedArray::NTypes; ++i) {
- static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocObject<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
- static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocObject<TypedArrayPrototype>(Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayCtors[i]) = memoryManager->allocate<TypedArrayCtor>(global, Heap::TypedArray::Type(i));
+ static_cast<Value &>(typedArrayPrototype[i]) = memoryManager->allocate<TypedArrayPrototype>(Heap::TypedArray::Type(i));
typedArrayPrototype[i].as<TypedArrayPrototype>()->init(this, static_cast<TypedArrayCtor *>(typedArrayCtors[i].as<Object>()));
}
@@ -413,6 +505,7 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor());
globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Symbol"), *symbolCtor());
FunctionObject *numberObject = numberCtor();
globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject);
globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor());
@@ -430,18 +523,22 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("ArrayBuffer"), *arrayBufferCtor());
globalObject->defineDefaultProperty(QStringLiteral("DataView"), *dataViewCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Set"), *setCtor());
+ globalObject->defineDefaultProperty(QStringLiteral("Map"), *mapCtor());
+
for (int i = 0; i < Heap::TypedArray::NTypes; ++i)
globalObject->defineDefaultProperty((str = typedArrayCtors[i].as<FunctionObject>()->name())->toQString(), typedArrayCtors[i]);
ScopedObject o(scope);
- globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocObject<MathObject>()));
- globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocObject<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Math"), (o = memoryManager->allocate<MathObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("JSON"), (o = memoryManager->allocate<JsonObject>()));
+ globalObject->defineDefaultProperty(QStringLiteral("Reflect"), (o = memoryManager->allocate<Reflect>()));
globalObject->defineReadonlyProperty(QStringLiteral("undefined"), Primitive::undefinedValue());
globalObject->defineReadonlyProperty(QStringLiteral("NaN"), Primitive::fromDouble(std::numeric_limits<double>::quiet_NaN()));
globalObject->defineReadonlyProperty(QStringLiteral("Infinity"), Primitive::fromDouble(Q_INFINITY));
- jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global);
+ jsObjects[Eval_Function] = memoryManager->allocate<EvalFunction>(global);
globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction());
// ES6: 20.1.2.12 & 20.1.2.13:
@@ -453,11 +550,8 @@ ExecutionEngine::ExecutionEngine()
Scope scope(this);
ScopedString pi(scope, newIdentifier(piString));
ScopedString pf(scope, newIdentifier(pfString));
- ExecutionContext *global = rootContext();
- ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(global, pi, GlobalFunctions::method_parseInt));
- ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(global, pf, GlobalFunctions::method_parseFloat));
- parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2));
- parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1));
+ ScopedFunctionObject parseIntFn(scope, FunctionObject::createBuiltinFunction(this, pi, GlobalFunctions::method_parseInt, 2));
+ ScopedFunctionObject parseFloatFn(scope, FunctionObject::createBuiltinFunction(this, pf, GlobalFunctions::method_parseFloat, 1));
globalObject->defineDefaultProperty(piString, parseIntFn);
globalObject->defineDefaultProperty(pfString, parseFloatFn);
numberObject->defineDefaultProperty(piString, parseIntFn);
@@ -473,8 +567,16 @@ ExecutionEngine::ExecutionEngine()
globalObject->defineDefaultProperty(QStringLiteral("escape"), GlobalFunctions::method_escape, 1);
globalObject->defineDefaultProperty(QStringLiteral("unescape"), GlobalFunctions::method_unescape, 1);
- ScopedString name(scope, newString(QStringLiteral("thrower")));
- jsObjects[ThrowerObject] = FunctionObject::createBuiltinFunction(global, name, ::throwTypeError);
+ ScopedFunctionObject t(scope, memoryManager->allocate<FunctionObject>(rootContext(), nullptr, ::throwTypeError));
+ t->defineReadonlyProperty(id_length(), Primitive::fromInt32(0));
+ t->setInternalClass(t->internalClass()->frozen());
+ jsObjects[ThrowerObject] = t;
+
+ ScopedProperty pd(scope);
+ pd->value = thrower();
+ pd->set = thrower();
+ functionPrototype()->insertMember(id_caller(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
+ functionPrototype()->insertMember(id_arguments(), pd, Attr_Accessor|Attr_ReadOnly_ButConfigurable);
}
ExecutionEngine::~ExecutionEngine()
@@ -487,8 +589,6 @@ ExecutionEngine::~ExecutionEngine()
while (!compilationUnits.isEmpty())
(*compilationUnits.begin())->unlink();
- internalClasses[Class_Empty]->destroy();
- delete classPool;
delete bumperPointerAllocator;
delete regExpCache;
delete regExpAllocator;
@@ -521,27 +621,32 @@ void ExecutionEngine::initRootContext()
r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext);
r->d()->activation.set(this, globalObject->d());
jsObjects[RootContext] = r;
+ jsObjects[ScriptContext] = r;
jsObjects[IntegerNull] = Encode((int)0);
}
-InternalClass *ExecutionEngine::newClass(const InternalClass &other)
+Heap::InternalClass *ExecutionEngine::newClass(Heap::InternalClass *other)
{
- return new (classPool) InternalClass(other);
+ Heap::InternalClass *ic = memoryManager->allocIC<InternalClass>();
+ ic->init(other);
+ return ic;
}
-InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
+Heap::InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype)
{
- return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : nullptr);
+ Scope scope(this);
+ Scoped<InternalClass> ic(scope, internalClasses(Class_Empty)->changeVTable(vtable));
+ return ic->changePrototype(prototype ? prototype->d() : nullptr);
}
Heap::Object *ExecutionEngine::newObject()
{
- return memoryManager->allocObject<Object>();
+ return memoryManager->allocate<Object>();
}
-Heap::Object *ExecutionEngine::newObject(InternalClass *internalClass, QV4::Object *prototype)
+Heap::Object *ExecutionEngine::newObject(Heap::InternalClass *internalClass)
{
- return memoryManager->allocObject<Object>(internalClass, prototype);
+ return memoryManager->allocObject<Object>(internalClass);
}
Heap::String *ExecutionEngine::newString(const QString &s)
@@ -557,23 +662,28 @@ Heap::String *ExecutionEngine::newIdentifier(const QString &text)
Heap::Object *ExecutionEngine::newStringObject(const String *string)
{
- return memoryManager->allocObject<StringObject>(string);
+ return memoryManager->allocate<StringObject>(string);
+}
+
+Heap::Object *ExecutionEngine::newSymbolObject(const Symbol *symbol)
+{
+ return memoryManager->allocObject<SymbolObject>(classes[Class_SymbolObject], symbol);
}
Heap::Object *ExecutionEngine::newNumberObject(double value)
{
- return memoryManager->allocObject<NumberObject>(value);
+ return memoryManager->allocate<NumberObject>(value);
}
Heap::Object *ExecutionEngine::newBooleanObject(bool b)
{
- return memoryManager->allocObject<BooleanObject>(b);
+ return memoryManager->allocate<BooleanObject>(b);
}
Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
{
Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject object(scope, memoryManager->allocate<ArrayObject>());
if (count) {
if (count < 0x1000)
@@ -586,7 +696,7 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(int count)
Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int length)
{
Scope scope(this);
- ScopedArrayObject a(scope, memoryManager->allocObject<ArrayObject>());
+ ScopedArrayObject a(scope, memoryManager->allocate<ArrayObject>());
if (length) {
size_t size = sizeof(Heap::ArrayData) + (length-1)*sizeof(Value);
@@ -607,45 +717,41 @@ Heap::ArrayObject *ExecutionEngine::newArrayObject(const Value *values, int leng
Heap::ArrayObject *ExecutionEngine::newArrayObject(const QStringList &list)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(list));
- return object->d();
+ return memoryManager->allocate<ArrayObject>(list);
}
-Heap::ArrayObject *ExecutionEngine::newArrayObject(InternalClass *internalClass, Object *prototype)
+Heap::ArrayObject *ExecutionEngine::newArrayObject(Heap::InternalClass *internalClass)
{
- Scope scope(this);
- ScopedArrayObject object(scope, memoryManager->allocObject<ArrayObject>(internalClass, prototype));
- return object->d();
+ return memoryManager->allocObject<ArrayObject>(internalClass);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(const QByteArray &array)
{
- return memoryManager->allocObject<ArrayBuffer>(array);
+ return memoryManager->allocate<ArrayBuffer>(array);
}
Heap::ArrayBuffer *ExecutionEngine::newArrayBuffer(size_t length)
{
- return memoryManager->allocObject<ArrayBuffer>(length);
+ return memoryManager->allocate<ArrayBuffer>(length);
}
Heap::DateObject *ExecutionEngine::newDateObject(const Value &value)
{
- return memoryManager->allocObject<DateObject>(value);
+ return memoryManager->allocate<DateObject>(value);
}
Heap::DateObject *ExecutionEngine::newDateObject(const QDateTime &dt)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(dt));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(dt));
return object->d();
}
Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t)
{
Scope scope(this);
- Scoped<DateObject> object(scope, memoryManager->allocObject<DateObject>(t));
+ Scoped<DateObject> object(scope, memoryManager->allocate<DateObject>(t));
return object->d();
}
@@ -662,12 +768,12 @@ Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int
Heap::RegExpObject *ExecutionEngine::newRegExpObject(RegExp *re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QRegExp &re)
{
- return memoryManager->allocObject<RegExpObject>(re);
+ return memoryManager->allocate<RegExpObject>(re);
}
Heap::Object *ExecutionEngine::newErrorObject(const Value &value)
@@ -714,16 +820,31 @@ Heap::Object *ExecutionEngine::newURIErrorObject(const Value &message)
Heap::Object *ExecutionEngine::newVariantObject(const QVariant &v)
{
- return memoryManager->allocObject<VariantObject>(v);
+ return memoryManager->allocate<VariantObject>(v);
}
-Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o)
+Heap::Object *ExecutionEngine::newForInIteratorObject(Object *o)
{
Scope scope(this);
- ScopedObject obj(scope, memoryManager->allocObject<ForEachIteratorObject>(o));
+ ScopedObject obj(scope, memoryManager->allocate<ForInIteratorObject>(o));
return obj->d();
}
+Heap::Object *ExecutionEngine::newMapIteratorObject(Object *o)
+{
+ return memoryManager->allocate<MapIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newSetIteratorObject(Object *o)
+{
+ return memoryManager->allocate<SetIteratorObject>(o->d(), this);
+}
+
+Heap::Object *ExecutionEngine::newArrayIteratorObject(Object *o)
+{
+ return memoryManager->allocate<ArrayIteratorObject>(o->d(), this);
+}
+
Heap::QmlContext *ExecutionEngine::qmlContext() const
{
if (!currentStackFrame)
@@ -891,16 +1012,14 @@ void ExecutionEngine::requireArgumentsAccessors(int n)
}
ExecutionContext *global = rootContext();
for (int i = oldSize; i < nArgumentsAccessors; ++i) {
- argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocObject<ArgumentsGetterFunction>(global, i));
- argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocObject<ArgumentsSetterFunction>(global, i));
+ argumentsAccessors[i].value = ScopedValue(scope, memoryManager->allocate<ArgumentsGetterFunction>(global, i));
+ argumentsAccessors[i].set = ScopedValue(scope, memoryManager->allocate<ArgumentsSetterFunction>(global, i));
}
}
}
void ExecutionEngine::markObjects(MarkStack *markStack)
{
- identifierTable->mark(markStack);
-
for (int i = 0; i < nArgumentsAccessors; ++i) {
const Property &pd = argumentsAccessors[i];
if (Heap::FunctionObject *getter = pd.getter())
@@ -909,9 +1028,13 @@ void ExecutionEngine::markObjects(MarkStack *markStack)
setter->mark(markStack);
}
- classPool->markObjects(markStack);
+ for (int i = 0; i < NClasses; ++i)
+ if (classes[i])
+ classes[i]->mark(markStack);
markStack->drain();
+ identifierTable->markObjects(markStack);
+
for (auto compilationUnit: compilationUnits) {
compilationUnit->markObjects(markStack);
markStack->drain();
@@ -1053,12 +1176,7 @@ QQmlError ExecutionEngine::catchExceptionAsQmlError()
error.setColumn(frame.column);
}
QV4::Scoped<QV4::ErrorObject> errorObj(scope, exception);
- if (!!errorObj && errorObj->asSyntaxError()) {
- QV4::ScopedString m(scope, newString(QStringLiteral("message")));
- QV4::ScopedValue v(scope, errorObj->get(m));
- error.setDescription(v->toQStringNoThrow());
- } else
- error.setDescription(exception->toQStringNoThrow());
+ error.setDescription(exception->toQStringNoThrow());
return error;
}
@@ -1117,8 +1235,11 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return v->toVariant();
} else if (QV4::QmlListWrapper *l = object->as<QV4::QmlListWrapper>()) {
return l->toVariant();
- } else if (object->isListType())
+#if QT_CONFIG(qml_sequence_object)
+ } else if (object->isListType()) {
return QV4::SequencePrototype::toVariant(object);
+#endif
+ }
}
if (value.as<ArrayObject>()) {
@@ -1141,10 +1262,12 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return QVariant::fromValue(QV4::JsonObject::toJsonArray(a));
}
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QVariant retn = QV4::SequencePrototype::toVariant(value, typeHint, &succeeded);
if (succeeded)
return retn;
+#endif
}
if (value.isUndefined())
@@ -1164,8 +1287,10 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int
return str.at(0);
return str;
}
+#if QT_CONFIG(qml_locale)
if (const QV4::QQmlLocaleData *ld = value.as<QV4::QQmlLocaleData>())
return *ld->d()->locale;
+#endif
if (const QV4::DateObject *d = value.as<DateObject>())
return d->toQDateTime();
if (const ArrayBuffer *d = value.as<ArrayBuffer>())
@@ -1321,6 +1446,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::Encode(newRegExpObject(*reinterpret_cast<const QRegExp *>(ptr)));
case QMetaType::QObjectStar:
return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(ptr));
+#if QT_CONFIG(qml_sequence_object)
case QMetaType::QStringList:
{
bool succeeded = false;
@@ -1330,6 +1456,7 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return retn->asReturnedValue();
return QV4::Encode(newArrayObject(*reinterpret_cast<const QStringList *>(ptr)));
}
+#endif
case QMetaType::QVariantList:
return arrayFromVariantList(this, *reinterpret_cast<const QVariantList *>(ptr));
case QMetaType::QVariantMap:
@@ -1340,8 +1467,10 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
return QV4::JsonObject::fromJsonObject(this, *reinterpret_cast<const QJsonObject *>(ptr));
case QMetaType::QJsonArray:
return QV4::JsonObject::fromJsonArray(this, *reinterpret_cast<const QJsonArray *>(ptr));
+#if QT_CONFIG(qml_locale)
case QMetaType::QLocale:
return QQmlLocale::wrap(this, *reinterpret_cast<const QLocale*>(ptr));
+#endif
default:
break;
}
@@ -1381,10 +1510,12 @@ QV4::ReturnedValue QV4::ExecutionEngine::fromVariant(const QVariant &variant)
if (objOk)
return QV4::QObjectWrapper::wrap(this, obj);
+#if QT_CONFIG(qml_sequence_object)
bool succeeded = false;
QV4::ScopedValue retn(scope, QV4::SequencePrototype::fromVariant(this, variant, &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
if (const QMetaObject *vtmo = QQmlValueTypeFactory::metaObjectForMetaType(type))
return QV4::QQmlValueTypeWrapper::create(this, variant, vtmo, type);
diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h
index c7fb743088..b007e65c4b 100644
--- a/src/qml/jsruntime/qv4engine_p.h
+++ b/src/qml/jsruntime/qv4engine_p.h
@@ -88,8 +88,6 @@ struct CompilationUnit;
}
struct Function;
-struct InternalClass;
-struct InternalClassPool;
struct Q_QML_EXPORT CppStackFrame {
CppStackFrame *parent;
@@ -98,6 +96,10 @@ struct Q_QML_EXPORT CppStackFrame {
const Value *originalArguments;
int originalArgumentsCount;
int instructionPointer;
+ const char *yield;
+ const char *unwindHandler;
+ const char *unwindLabel;
+ int unwindLevel;
QString source() const;
QString function() const;
@@ -142,8 +144,6 @@ public:
QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) {
Value *ptr = jsStackTop;
jsStackTop = ptr + nValues;
- for (int i = 0; i < nValues; ++i)
- ptr[i] = Primitive::undefinedValue();
return ptr;
}
@@ -153,22 +153,27 @@ public:
QJSEngine *jsEngine() const;
QQmlEngine *qmlEngine() const;
#else // !V4_BOOTSTRAP
- QJSEngine *jsEngine() const { return v8Engine->publicEngine(); }
+ QJSEngine *jsEngine() const { return publicEngine; }
QQmlEngine *qmlEngine() const { return v8Engine ? v8Engine->engine() : nullptr; }
#endif // V4_BOOTSTRAP
QV8Engine *v8Engine;
+ QJSEngine *publicEngine;
enum JSObjects {
RootContext,
+ ScriptContext,
IntegerNull, // Has to come after the RootContext to make the context stack safe
ObjectProto,
+ SymbolProto,
ArrayProto,
+ ArrayProtoValues,
PropertyListProto,
StringProto,
NumberProto,
BooleanProto,
DateProto,
FunctionProto,
+ GeneratorProto,
RegExpProto,
ErrorProto,
EvalErrorProto,
@@ -178,18 +183,31 @@ public:
TypeErrorProto,
URIErrorProto,
VariantProto,
+#if QT_CONFIG(qml_sequence_object)
SequenceProto,
+#endif
ArrayBufferProto,
DataViewProto,
+ SetProto,
+ MapProto,
+ IntrinsicTypedArrayProto,
ValueTypeProto,
SignalHandlerProto,
+ IteratorProto,
+ ForInIteratorProto,
+ SetIteratorProto,
+ MapIteratorProto,
+ ArrayIteratorProto,
+ StringIteratorProto,
Object_Ctor,
String_Ctor,
+ Symbol_Ctor,
Number_Ctor,
Boolean_Ctor,
Array_Ctor,
Function_Ctor,
+ GeneratorFunction_Ctor,
Date_Ctor,
RegExp_Ctor,
Error_Ctor,
@@ -201,6 +219,11 @@ public:
URIError_Ctor,
ArrayBuffer_Ctor,
DataView_Ctor,
+ Set_Ctor,
+ Map_Ctor,
+ IntrinsicTypedArray_Ctor,
+
+ GetSymbolSpecies,
Eval_Function,
GetStack_Function,
@@ -211,12 +234,16 @@ public:
enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency
ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); }
+ ExecutionContext *scriptContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + ScriptContext); }
+ void setScriptContext(ReturnedValue c) { jsObjects[ScriptContext] = c; }
FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); }
FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); }
+ FunctionObject *symbolCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Symbol_Ctor); }
FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); }
FunctionObject *booleanCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Boolean_Ctor); }
FunctionObject *arrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Array_Ctor); }
FunctionObject *functionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Function_Ctor); }
+ FunctionObject *generatorFunctionCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + GeneratorFunction_Ctor); }
FunctionObject *dateCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Date_Ctor); }
FunctionObject *regExpCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + RegExp_Ctor); }
FunctionObject *errorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Error_Ctor); }
@@ -228,16 +255,24 @@ public:
FunctionObject *uRIErrorCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + URIError_Ctor); }
FunctionObject *arrayBufferCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + ArrayBuffer_Ctor); }
FunctionObject *dataViewCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + DataView_Ctor); }
+ FunctionObject *setCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Set_Ctor); }
+ FunctionObject *mapCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Map_Ctor); }
+ FunctionObject *intrinsicTypedArrayCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + IntrinsicTypedArray_Ctor); }
FunctionObject *typedArrayCtors;
+ FunctionObject *getSymbolSpecies() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetSymbolSpecies); }
+
Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); }
+ Object *symbolPrototype() const { return reinterpret_cast<Object *>(jsObjects + SymbolProto); }
Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); }
+ Object *arrayProtoValues() const { return reinterpret_cast<Object *>(jsObjects + ArrayProtoValues); }
Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); }
Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); }
Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); }
Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); }
Object *datePrototype() const { return reinterpret_cast<Object *>(jsObjects + DateProto); }
Object *functionPrototype() const { return reinterpret_cast<Object *>(jsObjects + FunctionProto); }
+ Object *generatorPrototype() const { return reinterpret_cast<Object *>(jsObjects + GeneratorProto); }
Object *regExpPrototype() const { return reinterpret_cast<Object *>(jsObjects + RegExpProto); }
Object *errorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ErrorProto); }
Object *evalErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + EvalErrorProto); }
@@ -247,16 +282,26 @@ public:
Object *typeErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + TypeErrorProto); }
Object *uRIErrorPrototype() const { return reinterpret_cast<Object *>(jsObjects + URIErrorProto); }
Object *variantPrototype() const { return reinterpret_cast<Object *>(jsObjects + VariantProto); }
+#if QT_CONFIG(qml_sequence_object)
Object *sequencePrototype() const { return reinterpret_cast<Object *>(jsObjects + SequenceProto); }
+#endif
Object *arrayBufferPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayBufferProto); }
Object *dataViewPrototype() const { return reinterpret_cast<Object *>(jsObjects + DataViewProto); }
+ Object *setPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetProto); }
+ Object *mapPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapProto); }
+ Object *intrinsicTypedArrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + IntrinsicTypedArrayProto); }
Object *typedArrayPrototype;
Object *valueTypeWrapperPrototype() const { return reinterpret_cast<Object *>(jsObjects + ValueTypeProto); }
Object *signalHandlerPrototype() const { return reinterpret_cast<Object *>(jsObjects + SignalHandlerProto); }
+ Object *iteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + IteratorProto); }
+ Object *forInIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ForInIteratorProto); }
+ Object *setIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + SetIteratorProto); }
+ Object *mapIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + MapIteratorProto); }
+ Object *arrayIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayIteratorProto); }
+ Object *stringIteratorPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringIteratorProto); }
- InternalClassPool *classPool;
EvalFunction *evalFunction() const { return reinterpret_cast<EvalFunction *>(jsObjects + Eval_Function); }
FunctionObject *getStackFunction() const { return reinterpret_cast<FunctionObject *>(jsObjects + GetStack_Function); }
FunctionObject *thrower() const { return reinterpret_cast<FunctionObject *>(jsObjects + ThrowerObject); }
@@ -273,6 +318,8 @@ public:
String_boolean,
String_number,
String_string,
+ String_default,
+ String_symbol,
String_object,
String_function,
String_length,
@@ -301,10 +348,30 @@ public:
String_byteOffset,
String_buffer,
String_lastIndex,
+ String_next,
+ String_done,
+ String_return,
+
NJSStrings
};
Value *jsStrings;
+ enum JSSymbols {
+ Symbol_hasInstance,
+ Symbol_isConcatSpreadable,
+ Symbol_iterator,
+ Symbol_match,
+ Symbol_replace,
+ Symbol_search,
+ Symbol_species,
+ Symbol_split,
+ Symbol_toPrimitive,
+ Symbol_toStringTag,
+ Symbol_unscopables,
+ NJSSymbols
+ };
+ Value *jsSymbols;
+
String *id_empty() const { return reinterpret_cast<String *>(jsStrings + String_Empty); }
String *id_undefined() const { return reinterpret_cast<String *>(jsStrings + String_undefined); }
String *id_null() const { return reinterpret_cast<String *>(jsStrings + String_null); }
@@ -313,6 +380,8 @@ public:
String *id_boolean() const { return reinterpret_cast<String *>(jsStrings + String_boolean); }
String *id_number() const { return reinterpret_cast<String *>(jsStrings + String_number); }
String *id_string() const { return reinterpret_cast<String *>(jsStrings + String_string); }
+ String *id_default() const { return reinterpret_cast<String *>(jsStrings + String_default); }
+ String *id_symbol() const { return reinterpret_cast<String *>(jsStrings + String_symbol); }
String *id_object() const { return reinterpret_cast<String *>(jsStrings + String_object); }
String *id_function() const { return reinterpret_cast<String *>(jsStrings + String_function); }
String *id_length() const { return reinterpret_cast<String *>(jsStrings + String_length); }
@@ -341,6 +410,21 @@ public:
String *id_byteOffset() const { return reinterpret_cast<String *>(jsStrings + String_byteOffset); }
String *id_buffer() const { return reinterpret_cast<String *>(jsStrings + String_buffer); }
String *id_lastIndex() const { return reinterpret_cast<String *>(jsStrings + String_lastIndex); }
+ String *id_next() const { return reinterpret_cast<String *>(jsStrings + String_next); }
+ String *id_done() const { return reinterpret_cast<String *>(jsStrings + String_done); }
+ String *id_return() const { return reinterpret_cast<String *>(jsStrings + String_return); }
+
+ Symbol *symbol_hasInstance() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_hasInstance); }
+ Symbol *symbol_isConcatSpreadable() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_isConcatSpreadable); }
+ Symbol *symbol_iterator() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_iterator); }
+ Symbol *symbol_match() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_match); }
+ Symbol *symbol_replace() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_replace); }
+ Symbol *symbol_search() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_search); }
+ Symbol *symbol_species() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_species); }
+ Symbol *symbol_split() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_split); }
+ Symbol *symbol_toPrimitive() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toPrimitive); }
+ Symbol *symbol_toStringTag() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_toStringTag); }
+ Symbol *symbol_unscopables() const { return reinterpret_cast<Symbol *>(jsSymbols + Symbol_unscopables); }
#ifndef V4_BOOTSTRAP
QIntrusiveList<CompiledData::CompilationUnit, &CompiledData::CompilationUnit::nextCompilationUnit> compilationUnits;
@@ -372,9 +456,9 @@ public:
const bool m_canAllocateExecutableMemory;
#endif
- int internalClassIdCount = 0;
+ quintptr protoIdCount = 1;
- ExecutionEngine();
+ ExecutionEngine(QJSEngine *jsEngine = nullptr);
~ExecutionEngine();
#if !QT_CONFIG(qml_debug)
@@ -396,24 +480,26 @@ public:
return static_cast<ExecutionContext *>(&currentStackFrame->jsFrame->context);
}
- int newInternalClassId() { return ++internalClassIdCount; }
+ // ensure we always get odd prototype IDs. This helps make marking in QV4::Lookup fast
+ quintptr newProtoId() { return (protoIdCount += 2); }
- InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
+ Heap::InternalClass *newInternalClass(const VTable *vtable, Object *prototype);
Heap::Object *newObject();
- Heap::Object *newObject(InternalClass *internalClass, Object *prototype);
+ Heap::Object *newObject(Heap::InternalClass *internalClass);
Heap::String *newString(const QString &s = QString());
Heap::String *newIdentifier(const QString &text);
Heap::Object *newStringObject(const String *string);
+ Heap::Object *newSymbolObject(const Symbol *symbol);
Heap::Object *newNumberObject(double value);
Heap::Object *newBooleanObject(bool b);
Heap::ArrayObject *newArrayObject(int count = 0);
Heap::ArrayObject *newArrayObject(const Value *values, int length);
Heap::ArrayObject *newArrayObject(const QStringList &list);
- Heap::ArrayObject *newArrayObject(InternalClass *ic, Object *prototype);
+ Heap::ArrayObject *newArrayObject(Heap::InternalClass *ic);
Heap::ArrayBuffer *newArrayBuffer(const QByteArray &array);
Heap::ArrayBuffer *newArrayBuffer(size_t length);
@@ -437,7 +523,10 @@ public:
Heap::Object *newVariantObject(const QVariant &v);
- Heap::Object *newForEachIteratorObject(Object *o);
+ Heap::Object *newForInIteratorObject(Object *o);
+ Heap::Object *newSetIteratorObject(Object *o);
+ Heap::Object *newMapIteratorObject(Object *o);
+ Heap::Object *newArrayIteratorObject(Object *o);
Heap::QmlContext *qmlContext() const;
QObject *qmlScopeObject() const;
@@ -453,7 +542,7 @@ public:
void initRootContext();
- InternalClass *newClass(const InternalClass &other);
+ Heap::InternalClass *newClass(Heap::InternalClass *other);
StackTrace exceptionStackTrace;
@@ -492,7 +581,7 @@ public:
if (!m_canAllocateExecutableMemory)
return false;
if (f)
- return f->interpreterCallCount >= jitCallCountThreshold;
+ return !f->isGenerator() && f->interpreterCallCount >= jitCallCountThreshold;
return true;
#else
Q_UNUSED(f);
@@ -502,6 +591,7 @@ public:
QV4::ReturnedValue global();
+ double localTZA = 0.0; // local timezone, initialized at startup
private:
#if QT_CONFIG(qml_debug)
QScopedPointer<QV4::Debugging::Debugger> m_debugger;
diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h
index 59fb4a564a..03ff25d5b5 100644
--- a/src/qml/jsruntime/qv4enginebase_p.h
+++ b/src/qml/jsruntime/qv4enginebase_p.h
@@ -88,7 +88,7 @@ struct Q_QML_EXPORT EngineBase {
// Exception handling
Value *exceptionValue = nullptr;
- enum {
+ enum InternalClassType {
Class_Empty,
Class_String,
Class_MemberData,
@@ -96,10 +96,14 @@ struct Q_QML_EXPORT EngineBase {
Class_SparseArrayData,
Class_ExecutionContext,
Class_CallContext,
+ Class_QmlContext,
Class_Object,
Class_ArrayObject,
Class_FunctionObject,
+ Class_GeneratorFunction,
+ Class_GeneratorObject,
Class_StringObject,
+ Class_SymbolObject,
Class_ScriptFunction,
Class_ObjectProto,
Class_RegExp,
@@ -111,10 +115,11 @@ struct Q_QML_EXPORT EngineBase {
Class_ErrorObjectWithMessage,
Class_ErrorProto,
Class_QmlContextWrapper,
- Class_QmlContext,
+ Class_Symbol,
NClasses
};
- InternalClass *internalClasses[NClasses];
+ Heap::InternalClass *classes[NClasses];
+ Heap::InternalClass *internalClasses(InternalClassType icType) { return classes[icType]; }
};
#if defined(Q_CC_MSVC) || defined(Q_CC_GNU)
#pragma pack(pop)
diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp
index 90e158ba37..6541f00c8a 100644
--- a/src/qml/jsruntime/qv4errorobject.cpp
+++ b/src/qml/jsruntime/qv4errorobject.cpp
@@ -47,10 +47,6 @@
#include "qv4string_p.h"
#include <private/qv4mm_p.h>
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include <qv4codegen_p.h>
#ifndef Q_OS_WIN
@@ -74,7 +70,7 @@ void Heap::ErrorObject::init()
Scope scope(internalClass->engine);
Scoped<QV4::ErrorObject> e(scope, this);
- if (internalClass == scope.engine->internalClasses[EngineBase::Class_ErrorProto])
+ if (internalClass == scope.engine->internalClasses(EngineBase::Class_ErrorProto))
return;
setProperty(scope.engine, QV4::ErrorObject::Index_Stack, scope.engine->getStackFunction()->d());
@@ -316,7 +312,7 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He
ScopedString s(scope);
ScopedObject o(scope);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = obj));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
obj->setProperty(Index_Constructor, ctor->d());
obj->setProperty(Index_Message, engine->id_empty()->d());
obj->setProperty(Index_Name, engine->newString(QString::fromLatin1(ErrorObject::className(t))));
diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h
index 6b578e8c38..44b88f0d31 100644
--- a/src/qml/jsruntime/qv4errorobject_p.h
+++ b/src/qml/jsruntime/qv4errorobject_p.h
@@ -180,7 +180,7 @@ struct ErrorObject: Object {
template<>
inline const ErrorObject *Value::as() const {
- return isManaged() && m()->vtable()->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isErrorObject ? reinterpret_cast<const ErrorObject *>(this) : nullptr;
}
struct EvalErrorObject: ErrorObject {
@@ -328,25 +328,26 @@ inline SyntaxErrorObject *ErrorObject::asSyntaxError()
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const Value &message) {
- InternalClass *ic = e->internalClasses[message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), message);
+ EngineBase::InternalClassType klass = message.isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scope scope(e);
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), message);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v);
}
template <typename T>
Heap::Object *ErrorObject::create(ExecutionEngine *e, const QString &message, const QString &filename, int line, int column) {
Scope scope(e);
ScopedValue v(scope, message.isEmpty() ? Encode::undefined() : e->newString(message)->asReturnedValue());
- InternalClass *ic = e->internalClasses[v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage];
- ic = ic->changePrototype(T::defaultPrototype(e)->d());
- return e->memoryManager->allocObject<T>(ic, T::defaultPrototype(e), v, filename, line, column);
+ EngineBase::InternalClassType klass = v->isUndefined() ? EngineBase::Class_ErrorObject : EngineBase::Class_ErrorObjectWithMessage;
+ Scoped<InternalClass> ic(scope, e->internalClasses(klass)->changePrototype(T::defaultPrototype(e)->d()));
+ return e->memoryManager->allocObject<T>(ic->d(), v, filename, line, column);
}
diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp
index 6fca9ecd45..131d3406d2 100644
--- a/src/qml/jsruntime/qv4function.cpp
+++ b/src/qml/jsruntime/qv4function.cpp
@@ -52,27 +52,26 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr)
+Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function)
: compiledFunction(function)
, compilationUnit(unit)
- , code(codePtr)
, codeData(function->code())
, jittedCode(nullptr)
, codeRef(nullptr)
, hasQmlDependencies(function->hasQmlDependencies())
{
- Q_UNUSED(engine);
-
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ Scope scope(engine);
+ Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext));
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
for (quint32 i = 0; i < compiledFunction->nLocals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[localsIndices[i]]), Attr_NotConfigurable);
const quint32_le *formalsIndices = compiledFunction->formalsTable();
for (quint32 i = 0; i < compiledFunction->nFormals; ++i)
- internalClass = internalClass->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ ic = ic->addMember(engine->identifierTable->identifier(compilationUnit->runtimeStrings[formalsIndices[i]]), Attr_NotConfigurable);
+ internalClass = ic->d();
nFormals = compiledFunction->nFormals;
}
@@ -110,7 +109,7 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
}
- internalClass = engine->internalClasses[EngineBase::Class_CallContext];
+ internalClass = engine->internalClasses(EngineBase::Class_CallContext);
// first locals
const quint32_le *localsIndices = compiledFunction->localsTable();
@@ -120,8 +119,8 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr
Scope scope(engine);
ScopedString arg(scope);
for (const QString &parameterName : parameterNames) {
- arg = engine->newString(parameterName);
- internalClass = internalClass->addMember(arg, Attr_NotConfigurable);
+ arg = engine->newIdentifier(parameterName);
+ internalClass = internalClass->addMember(arg->identifier(), Attr_NotConfigurable);
}
nFormals = parameters.size();
}
diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h
index 59a94e5dde..ff2d86b89f 100644
--- a/src/qml/jsruntime/qv4function_p.h
+++ b/src/qml/jsruntime/qv4function_p.h
@@ -72,21 +72,19 @@ struct Q_QML_EXPORT Function {
return Moth::VME::exec(this, thisObject, argv, argc, context);
}
- typedef ReturnedValue (*Code)(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
- Code code;
- const uchar *codeData;
+ const char *codeData;
typedef ReturnedValue (*JittedCode)(CppStackFrame *, ExecutionEngine *);
JittedCode jittedCode;
JSC::MacroAssemblerCodeRef *codeRef;
// first nArguments names in internalClass are the actual arguments
- InternalClass *internalClass;
+ Heap::InternalClass *internalClass;
uint nFormals;
int interpreterCallCount = 0;
bool hasQmlDependencies;
- Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr);
+ Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function);
~Function();
// used when dynamically assigning signal handlers (QQmlConnection)
@@ -98,8 +96,9 @@ struct Q_QML_EXPORT Function {
inline QString sourceFile() const { return compilationUnit->fileName(); }
inline QUrl finalUrl() const { return compilationUnit->finalUrl(); }
- inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; }
inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; }
+ inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; }
+ inline bool isGenerator() const { return compiledFunction->flags & CompiledData::Function::IsGenerator; }
QQmlSourceLocation sourceLocation() const
{
diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 83608070ec..a9aaa344cb 100644
--- a/src/qml/jsruntime/qv4functionobject.cpp
+++ b/src/qml/jsruntime/qv4functionobject.cpp
@@ -41,6 +41,7 @@
#include "qv4objectproto_p.h"
#include "qv4stringobject_p.h"
#include "qv4function_p.h"
+#include "qv4symbol_p.h"
#include <private/qv4mm_p.h>
#include "qv4arrayobject_p.h"
@@ -121,7 +122,7 @@ void Heap::FunctionObject::init()
Object::init();
this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d());
- Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()) == Index_Prototype);
+ Q_ASSERT(internalClass && internalClass->find(internalClass->engine->id_prototype()->identifier()) == Index_Prototype);
setProperty(internalClass->engine, Index_Prototype, Primitive::undefinedValue());
}
@@ -144,10 +145,10 @@ void FunctionObject::init(String *n, bool createProto)
Scope s(internalClass()->engine);
ScopedValue protectThis(s, this);
- Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()) == Heap::FunctionObject::Index_Prototype);
+ Q_ASSERT(internalClass() && internalClass()->find(s.engine->id_prototype()->identifier()) == Heap::FunctionObject::Index_Prototype);
if (createProto) {
- ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses[EngineBase::Class_ObjectProto], s.engine->objectPrototype()));
- Q_ASSERT(s.engine->internalClasses[EngineBase::Class_ObjectProto]->find(s.engine->id_constructor()) == Heap::FunctionObject::Index_ProtoConstructor);
+ ScopedObject proto(s, s.engine->newObject(s.engine->internalClasses(EngineBase::Class_ObjectProto)));
+ Q_ASSERT(s.engine->internalClasses(EngineBase::Class_ObjectProto)->find(s.engine->id_constructor()->identifier()) == Heap::FunctionObject::Index_ProtoConstructor);
proto->setProperty(Heap::FunctionObject::Index_ProtoConstructor, d());
setProperty(Heap::FunctionObject::Index_Prototype, proto);
} else {
@@ -175,7 +176,19 @@ ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const
Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function)
{
- return scope->engine()->memoryManager->allocObject<ScriptFunction>(scope, function);
+ return scope->engine()->memoryManager->allocate<ScriptFunction>(scope, function);
+}
+
+Heap::FunctionObject *FunctionObject::createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount)
+{
+ Scope scope(engine);
+ ScopedString name(scope, nameOrSymbol);
+ if (!name)
+ name = engine->newString(QChar::fromLatin1('[') + nameOrSymbol->toQString().midRef(1) + QChar::fromLatin1(']'));
+
+ ScopedFunctionObject function(scope, engine->memoryManager->allocate<FunctionObject>(engine->rootContext(), name, code));
+ function->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(argumentCount));
+ return function->d();
}
bool FunctionObject::isBinding() const
@@ -201,10 +214,8 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope)
}
// 15.3.2
-ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+QQmlRefPointer<CompiledData::CompilationUnit> FunctionCtor::parse(ExecutionEngine *engine, const Value *argv, int argc, Type t)
{
- Scope scope(f->engine());
-
QString arguments;
QString body;
if (argc > 0) {
@@ -215,35 +226,51 @@ ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Val
}
body = argv[argc - 1].toQString();
}
- if (scope.engine->hasException)
- return Encode::undefined();
+ if (engine->hasException)
+ return nullptr;
- QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}');
+ QString function = (t == Type_Function ? QLatin1String("function anonymous(") : QLatin1String("function* anonymous(")) + arguments + QLatin1String("\n){") + body + QLatin1String("\n}");
- QQmlJS::Engine ee, *engine = &ee;
- QQmlJS::Lexer lexer(engine);
+ QQmlJS::Engine ee;
+ QQmlJS::Lexer lexer(&ee);
lexer.setCode(function, 1, false);
- QQmlJS::Parser parser(engine);
+ QQmlJS::Parser parser(&ee);
const bool parsed = parser.parseExpression();
- if (!parsed)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!parsed) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode());
- if (!fe)
- return scope.engine->throwSyntaxError(QLatin1String("Parse error"));
+ if (!fe) {
+ engine->throwSyntaxError(QLatin1String("Parse error"));
+ return nullptr;
+ }
- Compiler::Module module(scope.engine->debugger() != nullptr);
+ Compiler::Module module(engine->debugger() != nullptr);
Compiler::JSUnitGenerator jsGenerator(&module);
- RuntimeCodegen cg(scope.engine, &jsGenerator, false);
+ RuntimeCodegen cg(engine, &jsGenerator, false);
cg.generateFromFunctionExpression(QString(), function, fe, &module);
- QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit();
- Function *vmf = compilationUnit->linkToEngine(scope.engine);
+ if (engine->hasException)
+ return nullptr;
- ExecutionContext *global = scope.engine->rootContext();
+ return cg.generateCompilationUnit();
+}
+
+ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Function);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
return Encode(FunctionObject::createScriptFunction(global, vmf));
}
@@ -268,13 +295,14 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor)
ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ defineReadonlyConfigurableProperty(engine->id_name(), *engine->id_empty());
defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString, 0);
defineDefaultProperty(QStringLiteral("apply"), method_apply, 2);
defineDefaultProperty(QStringLiteral("call"), method_call, 1);
defineDefaultProperty(QStringLiteral("bind"), method_bind, 1);
-
+ defineDefaultProperty(engine->symbol_hasInstance(), method_hasInstance, 1, Attr_ReadOnly);
}
ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -284,7 +312,19 @@ ReturnedValue FunctionPrototype::method_toString(const FunctionObject *b, const
if (!fun)
return v4->throwTypeError();
- return Encode(v4->newString(QStringLiteral("function() { [code] }")));
+ const Scope scope(fun->engine());
+ const ScopedString scopedFunctionName(scope, fun->name());
+ const QString functionName(scopedFunctionName ? scopedFunctionName->toQString() : QString());
+ QString functionAsString = QStringLiteral("function");
+
+ // If fun->name() is empty, then there is no function name
+ // to append because the function is anonymous.
+ if (!functionName.isEmpty())
+ functionAsString.append(QLatin1Char(' ') + functionName);
+
+ functionAsString.append(QStringLiteral("() { [code] }"));
+
+ return Encode(v4->newString(functionAsString));
}
ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -304,7 +344,7 @@ ReturnedValue FunctionPrototype::method_apply(const QV4::FunctionObject *b, cons
uint len = arr->getLength();
Scope scope(v4);
- Value *arguments = v4->jsAlloca(len);
+ Value *arguments = scope.alloc(len);
if (len) {
if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) {
QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>();
@@ -383,6 +423,17 @@ ReturnedValue FunctionPrototype::method_bind(const FunctionObject *b, const Valu
return bound->asReturnedValue();
}
+ReturnedValue FunctionPrototype::method_hasInstance(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc)
+ return false;
+ const Object *o = thisObject->as<Object>();
+ if (!o)
+ return f->engine()->throwTypeError();
+
+ return Object::instanceOf(o, argv[0]);
+}
+
DEFINE_OBJECT_VTABLE(ScriptFunction);
ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc)
@@ -391,8 +442,7 @@ ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const
const ScriptFunction *f = static_cast<const ScriptFunction *>(fo);
Scope scope(v4);
- InternalClass *ic = f->classForConstructor();
- ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(ic));
+ ScopedValue thisObject(scope, v4->memoryManager->allocObject<Object>(f->classForConstructor()));
ReturnedValue result = Moth::VME::exec(fo, thisObject, argv, argc);
@@ -415,38 +465,29 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function
setFunction(function);
Q_ASSERT(function);
- Q_ASSERT(function->code);
Scope s(scope);
ScopedFunctionObject f(s, this);
ScopedString name(s, function->name());
f->init(name, true);
- Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length);
- setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount()));
-
- if (function->isStrict()) {
- ScopedProperty pd(s);
- pd->value = s.engine->thrower();
- pd->set = s.engine->thrower();
- f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- f->insertMember(s.engine->id_arguments(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable);
- }
+ Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()->identifier()) == Index_Length);
+ setProperty(s.engine, Index_Length, Primitive::fromInt32(int(function->compiledFunction->length)));
}
-InternalClass *ScriptFunction::classForConstructor() const
+Heap::InternalClass *ScriptFunction::classForConstructor() const
{
const Object *o = d()->protoProperty();
- InternalClass *ic = d()->cachedClassForConstructor;
- if (ic && ic->prototype == o->d())
- return ic;
+ if (d()->cachedClassForConstructor && d()->cachedClassForConstructor->prototype == o->d())
+ return d()->cachedClassForConstructor;
- ic = engine()->internalClasses[EngineBase::Class_Object];
+ Scope scope(engine());
+ Scoped<InternalClass> ic(scope, engine()->internalClasses(EngineBase::Class_Object));
if (o)
ic = ic->changePrototype(o->d());
- d()->cachedClassForConstructor = ic;
+ d()->cachedClassForConstructor.set(scope.engine, ic->d());
- return ic;
+ return ic->d();
}
DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction);
diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h
index 32e71a175b..e8bd574161 100644
--- a/src/qml/jsruntime/qv4functionobject_p.h
+++ b/src/qml/jsruntime/qv4functionobject_p.h
@@ -64,9 +64,6 @@ namespace QV4 {
struct IndexedBuiltinFunction;
struct JSCallData;
-typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
-typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc);
-
namespace Heap {
@@ -111,14 +108,16 @@ struct IndexedBuiltinFunction : FunctionObject {
uint index;
};
-struct ScriptFunction : FunctionObject {
+#define ScriptFunctionMembers(class, Member) \
+ Member(class, Pointer, InternalClass *, cachedClassForConstructor)
+
+DECLARE_HEAP_OBJECT(ScriptFunction, FunctionObject) {
+ DECLARE_MARKOBJECTS(ScriptFunction)
enum {
Index_Name = FunctionObject::Index_Prototype + 1,
Index_Length
};
void init(QV4::ExecutionContext *scope, Function *function);
-
- QV4::InternalClass *cachedClassForConstructor;
};
#define BoundFunctionMembers(class, Member) \
@@ -166,11 +165,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function);
- static Heap::FunctionObject *createBuiltinFunction(ExecutionContext *scope, String *name,
- ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc))
- {
- return scope->engine()->memoryManager->allocObject<FunctionObject>(scope, name, code);
- }
+ static Heap::FunctionObject *createBuiltinFunction(ExecutionEngine *engine, StringOrSymbol *nameOrSymbol, jsCallFunction code, int argumentCount);
bool strictMode() const { return d()->function ? d()->function->isStrict() : false; }
bool isBinding() const;
@@ -181,7 +176,7 @@ struct Q_QML_EXPORT FunctionObject: Object {
template<>
inline const FunctionObject *Value::as() const {
- return isManaged() && m()->vtable()->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isFunctionObject ? reinterpret_cast<const FunctionObject *>(this) : nullptr;
}
@@ -191,6 +186,12 @@ struct FunctionCtor: FunctionObject
static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+protected:
+ enum Type {
+ Type_Function,
+ Type_Generator
+ };
+ static QQmlRefPointer<CompiledData::CompilationUnit> parse(ExecutionEngine *engine, const Value *argv, int argc, Type t = Type_Function);
};
struct FunctionPrototype: FunctionObject
@@ -203,6 +204,7 @@ struct FunctionPrototype: FunctionObject
static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_bind(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hasInstance(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct IndexedBuiltinFunction : FunctionObject
@@ -227,7 +229,7 @@ struct ScriptFunction : FunctionObject {
static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc);
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
- InternalClass *classForConstructor() const;
+ Heap::InternalClass *classForConstructor() const;
};
@@ -236,7 +238,7 @@ struct BoundFunction: FunctionObject {
static Heap::BoundFunction *create(ExecutionContext *scope, FunctionObject *target, const Value &boundThis, QV4::MemberData *boundArgs)
{
- return scope->engine()->memoryManager->allocObject<BoundFunction>(scope, target, boundThis, boundArgs);
+ return scope->engine()->memoryManager->allocate<BoundFunction>(scope, target, boundThis, boundArgs);
}
Heap::FunctionObject *target() const { return d()->target; }
diff --git a/src/qml/jsruntime/qv4generatorobject.cpp b/src/qml/jsruntime/qv4generatorobject.cpp
new file mode 100644
index 0000000000..a29eef513c
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject.cpp
@@ -0,0 +1,248 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4generatorobject_p.h>
+#include <qv4symbol_p.h>
+#include <qv4iterator_p.h>
+#include <qv4jscall_p.h>
+#include <qv4vme_moth_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(GeneratorFunctionCtor);
+DEFINE_OBJECT_VTABLE(GeneratorFunction);
+DEFINE_OBJECT_VTABLE(GeneratorObject);
+
+void Heap::GeneratorFunctionCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("GeneratorFunction"));
+}
+
+ReturnedValue GeneratorFunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+
+ QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = parse(engine, argv, argc, Type_Generator);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ Function *vmf = compilationUnit->linkToEngine(engine);
+ ExecutionContext *global = engine->scriptContext();
+ return Encode(GeneratorFunction::create(global, vmf));
+}
+
+// 15.3.1: This is equivalent to new Function(...)
+ReturnedValue GeneratorFunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ return callAsConstructor(f, argv, argc);
+}
+
+Heap::FunctionObject *GeneratorFunction::create(ExecutionContext *context, Function *function)
+{
+ Scope scope(context);
+ Scoped<GeneratorFunction> g(scope, context->engine()->memoryManager->allocate<GeneratorFunction>(context, function));
+ ScopedObject proto(scope, scope.engine->newObject());
+ proto->setPrototype(scope.engine->generatorPrototype());
+ g->defineDefaultProperty(scope.engine->id_prototype(), proto, Attr_NotConfigurable|Attr_NotEnumerable);
+ g->setPrototype(ScopedObject(scope, scope.engine->generatorFunctionCtor()->get(scope.engine->id_prototype())));
+ return g->d();
+}
+
+ReturnedValue GeneratorFunction::callAsConstructor(const FunctionObject *f, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue GeneratorFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ const GeneratorFunction *gf = static_cast<const GeneratorFunction *>(f);
+ Function *function = gf->function();
+ ExecutionEngine *engine = gf->engine();
+
+ // We need to set up a separate stack for the generator, as it's being re-entered
+ uint stackSize = argc; // space for the original arguments
+ int jsStackFrameSize = offsetof(CallData, args)/sizeof(Value) + function->compiledFunction->nRegisters;
+ stackSize += jsStackFrameSize;
+
+ size_t requiredMemory = sizeof(GeneratorObject::Data) - sizeof(Value) + sizeof(Value) * stackSize;
+
+ Scope scope(gf);
+ Scoped<GeneratorObject> g(scope, scope.engine->memoryManager->allocManaged<GeneratorObject>(requiredMemory, scope.engine->classes[EngineBase::Class_GeneratorObject]));
+ g->setPrototype(ScopedObject(scope, gf->get(scope.engine->id_prototype())));
+
+ Heap::GeneratorObject *gp = g->d();
+ gp->stack.size = stackSize;
+ gp->stack.alloc = stackSize;
+
+ // copy original arguments
+ memcpy(gp->stack.values, argv, argc*sizeof(Value));
+ gp->cppFrame.originalArguments = gp->stack.values;
+ gp->cppFrame.originalArgumentsCount = argc;
+
+ // setup JS stack frame
+ CallData *callData = reinterpret_cast<CallData *>(&gp->stack.values[argc]);
+ callData->function = *gf;
+ callData->context = gf->scope();
+ callData->accumulator = Encode::undefined();
+ callData->thisObject = thisObject ? *thisObject : Primitive::undefinedValue();
+ if (argc > int(function->nFormals))
+ argc = int(function->nFormals);
+ callData->setArgc(argc);
+ memcpy(callData->args, argv, argc*sizeof(Value));
+
+ gp->cppFrame.v4Function = function;
+ gp->cppFrame.instructionPointer = 0;
+ gp->cppFrame.jsFrame = callData;
+ gp->cppFrame.parent = engine->currentStackFrame;
+ engine->currentStackFrame = &gp->cppFrame;
+
+ Moth::VME::interpret(gp->cppFrame, function->codeData);
+ gp->state = GeneratorState::SuspendedStart;
+
+ engine->currentStackFrame = gp->cppFrame.parent;
+ return g->asReturnedValue();
+}
+
+
+void Heap::GeneratorPrototype::init()
+{
+ Heap::FunctionObject::init();
+}
+
+
+void GeneratorPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+
+ ScopedObject ctorProto(scope, engine->newObject(engine->newInternalClass(Object::staticVTable(), engine->functionPrototype())));
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1));
+ ctor->defineReadonlyProperty(engine->id_prototype(), ctorProto);
+
+ ctorProto->defineDefaultProperty(QStringLiteral("constructor"), (v = ctor), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newIdentifier(QStringLiteral("GeneratorFunction"))), Attr_ReadOnly_ButConfigurable);
+ ctorProto->defineDefaultProperty(engine->id_prototype(), (v = this), Attr_ReadOnly_ButConfigurable);
+
+ setPrototype(engine->iteratorPrototype());
+ defineDefaultProperty(QStringLiteral("constructor"), ctorProto, Attr_ReadOnly_ButConfigurable);
+ defineDefaultProperty(QStringLiteral("next"), method_next, 1);
+ defineDefaultProperty(QStringLiteral("return"), method_return, 1);
+ defineDefaultProperty(QStringLiteral("throw"), method_throw, 1);
+ defineDefaultProperty(engine->symbol_toStringTag(), (v = engine->newString(QStringLiteral("Generator"))), Attr_ReadOnly_ButConfigurable);
+}
+
+ReturnedValue GeneratorPrototype::method_next(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, Primitive::undefinedValue(), true);
+
+ return g->resume(engine, argc ? argv[0] : Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_return(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ if (gp->state == GeneratorState::SuspendedStart)
+ gp->state = GeneratorState::Completed;
+
+ if (gp->state == GeneratorState::Completed)
+ return IteratorPrototype::createIterResultObject(engine, argc ? argv[0] : Primitive::undefinedValue(), true);
+
+ // the bytecode interpreter interprets an exception with empty value as
+ // a yield called with return()
+ engine->throwError(Primitive::emptyValue());
+
+ return g->resume(engine, argc ? argv[0]: Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorPrototype::method_throw(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *engine = f->engine();
+ const GeneratorObject *g = thisObject->as<GeneratorObject>();
+ if (!g || g->d()->state == GeneratorState::Executing)
+ return engine->throwTypeError();
+
+ Heap::GeneratorObject *gp = g->d();
+
+ engine->throwError(argc ? argv[0]: Primitive::undefinedValue());
+
+ if (gp->state == GeneratorState::SuspendedStart || gp->state == GeneratorState::Completed) {
+ gp->state = GeneratorState::Completed;
+ return Encode::undefined();
+ }
+
+ return g->resume(engine, Primitive::undefinedValue());
+}
+
+ReturnedValue GeneratorObject::resume(ExecutionEngine *engine, const Value &arg) const
+{
+ Heap::GeneratorObject *gp = d();
+ gp->state = GeneratorState::Executing;
+ gp->cppFrame.parent = engine->currentStackFrame;
+ engine->currentStackFrame = &gp->cppFrame;
+
+ Q_ASSERT(gp->cppFrame.yield != nullptr);
+ const char *code = gp->cppFrame.yield;
+ gp->cppFrame.yield = nullptr;
+ gp->cppFrame.jsFrame->accumulator = arg;
+
+ Scope scope(engine);
+ ScopedValue result(scope, Moth::VME::interpret(gp->cppFrame, code));
+
+ engine->currentStackFrame = gp->cppFrame.parent;
+
+ bool done = (gp->cppFrame.yield == nullptr);
+ gp->state = done ? GeneratorState::Completed : GeneratorState::SuspendedYield;
+ if (engine->hasException)
+ return Encode::undefined();
+ return IteratorPrototype::createIterResultObject(engine, result, done);
+}
diff --git a/src/qml/jsruntime/qv4generatorobject_p.h b/src/qml/jsruntime/qv4generatorobject_p.h
new file mode 100644
index 0000000000..62ffcbbad1
--- /dev/null
+++ b/src/qml/jsruntime/qv4generatorobject_p.h
@@ -0,0 +1,136 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4GENERATOROBJECT_P_H
+#define QV4GENERATOROBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+enum class GeneratorState {
+ Undefined,
+ SuspendedStart,
+ SuspendedYield,
+ Executing,
+ Completed
+};
+
+namespace Heap {
+
+struct GeneratorFunctionCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct GeneratorFunction : ScriptFunction {
+};
+
+struct GeneratorPrototype : FunctionObject {
+ void init();
+};
+
+#define GeneratorObjectMembers(class, Member) \
+ Member(class, Pointer, ExecutionContext *, context) \
+ Member(class, Pointer, GeneratorFunction *, function) \
+ Member(class, NoMark, GeneratorState, state) \
+ Member(class, NoMark, CppStackFrame, cppFrame) \
+ Member(class, ValueArray, ValueArray, stack)
+
+DECLARE_HEAP_OBJECT(GeneratorObject, Object) {
+ DECLARE_MARKOBJECTS(GeneratorObject);
+};
+
+}
+
+struct GeneratorFunctionCtor : FunctionCtor
+{
+ V4_OBJECT2(GeneratorFunctionCtor, FunctionCtor)
+
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct GeneratorFunction : ScriptFunction
+{
+ V4_OBJECT2(GeneratorFunction, ScriptFunction)
+ V4_INTERNALCLASS(GeneratorFunction)
+
+ static Heap::FunctionObject *create(ExecutionContext *scope, Function *function);
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct GeneratorPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_next(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_return(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_throw(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+struct GeneratorObject : Object {
+ V4_OBJECT2(GeneratorObject, Object)
+ Q_MANAGED_TYPE(GeneratorObject)
+ V4_INTERNALCLASS(GeneratorObject)
+ V4_PROTOTYPE(generatorPrototype)
+
+ ReturnedValue resume(ExecutionEngine *engine, const Value &arg) const;
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4GENERATORFUNCTION_P_H
+
diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h
index 1fa4bae049..607f8b4d28 100644
--- a/src/qml/jsruntime/qv4global_p.h
+++ b/src/qml/jsruntime/qv4global_p.h
@@ -153,6 +153,11 @@ namespace Compiler {
struct Module;
struct Context;
struct JSUnitGenerator;
+ class Codegen;
+}
+
+namespace Moth {
+ class BytecodeGenerator;
}
namespace Heap {
@@ -160,13 +165,17 @@ namespace Heap {
struct MemberData;
struct ArrayData;
+ struct StringOrSymbol;
struct String;
+ struct Symbol;
struct Object;
struct ObjectPrototype;
struct ExecutionContext;
struct CallContext;
+ struct QmlContext;
struct ScriptFunction;
+ struct InternalClass;
struct BooleanObject;
struct NumberObject;
@@ -188,14 +197,18 @@ namespace Heap {
template <typename T, size_t> struct Pointer;
}
+struct CppStackFrame;
class MemoryManager;
class ExecutableAllocator;
+struct StringOrSymbol;
struct String;
+struct Symbol;
struct Object;
struct ObjectPrototype;
struct ObjectIterator;
struct ExecutionContext;
struct CallContext;
+struct QmlContext;
struct ScriptFunction;
struct InternalClass;
struct Property;
@@ -236,6 +249,7 @@ struct Scope;
struct ScopedValue;
template<typename T> struct Scoped;
typedef Scoped<String> ScopedString;
+typedef Scoped<StringOrSymbol> ScopedStringOrSymbol;
typedef Scoped<Object> ScopedObject;
typedef Scoped<ArrayObject> ScopedArrayObject;
typedef Scoped<FunctionObject> ScopedFunctionObject;
@@ -244,6 +258,7 @@ typedef Scoped<ExecutionContext> ScopedContext;
struct PersistentValueStorage;
class PersistentValue;
class WeakValue;
+struct MarkStack;
struct IdentifierTable;
class RegExpCache;
@@ -357,6 +372,12 @@ struct Q_QML_EXPORT StackFrame {
};
typedef QVector<StackFrame> StackTrace;
+enum class ObjectLiteralArgument {
+ Value,
+ Getter,
+ Setter
+};
+
}
Q_DECLARE_TYPEINFO(QV4::PropertyAttributes, Q_PRIMITIVE_TYPE);
diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp
index f419ab53fe..47a6734eda 100644
--- a/src/qml/jsruntime/qv4globalobject.cpp
+++ b/src/qml/jsruntime/qv4globalobject.cpp
@@ -351,7 +351,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
if (!directCall) {
// the context for eval should be the global scope
- ctx = v4->rootContext();
+ ctx = v4->scriptContext();
}
String *scode = argv[0].stringValue();
@@ -361,7 +361,7 @@ ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc,
const QString code = scode->toQString();
bool inheritContext = !isStrict;
- Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code"));
+ Script script(ctx, QV4::Compiler::ContextType::Eval, code, QStringLiteral("eval code"));
script.strictMode = (directCall && isStrict);
script.inheritContext = inheritContext;
script.parse();
diff --git a/src/qml/jsruntime/qv4identifier.cpp b/src/qml/jsruntime/qv4identifier.cpp
index c122bcb51a..4bfae14aec 100644
--- a/src/qml/jsruntime/qv4identifier.cpp
+++ b/src/qml/jsruntime/qv4identifier.cpp
@@ -38,11 +38,22 @@
****************************************************************************/
#include "qv4identifier_p.h"
#include "qv4identifiertable_p.h"
+#include "qv4string_p.h"
QT_BEGIN_NAMESPACE
namespace QV4 {
+QString Identifier::toQString() const
+{
+ if (isArrayIndex())
+ return QString::number(asArrayIndex());
+ Heap::Base *b = asHeapObject();
+ Q_ASSERT(b->internalClass->vtable->isStringOrSymbol);
+ Heap::StringOrSymbol *s = static_cast<Heap::StringOrSymbol *>(b);
+ return s->toQString();
+}
+
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
1, 21, 3, 21, 7, 15, 9, 5, 3, 29, 15, 0, 0, 0, 0, 0
@@ -54,14 +65,16 @@ static inline int primeForNumBits(int numBits)
}
-IdentifierHashData::IdentifierHashData(int numBits)
+IdentifierHashData::IdentifierHashData(IdentifierTable *table, int numBits)
: size(0)
, numBits(numBits)
+ , identifierTable(table)
{
refCount.store(1);
alloc = primeForNumBits(numBits);
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memset(entries, 0, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
}
IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
@@ -73,12 +86,18 @@ IdentifierHashData::IdentifierHashData(IdentifierHashData *other)
alloc = other->alloc;
entries = (IdentifierHashEntry *)malloc(alloc*sizeof(IdentifierHashEntry));
memcpy(entries, other->entries, alloc*sizeof(IdentifierHashEntry));
+ identifierTable->addIdentifierHash(this);
+}
+
+IdentifierHashData::~IdentifierHashData() {
+ free(entries);
+ if (identifierTable)
+ identifierTable->removeIdentifierHash(this);
}
IdentifierHash::IdentifierHash(ExecutionEngine *engine)
{
- d = new IdentifierHashData(3);
- d->identifierTable = engine->identifierTable;
+ d = new IdentifierHashData(engine->identifierTable, 3);
}
void IdentifierHash::detach()
@@ -92,8 +111,10 @@ void IdentifierHash::detach()
}
-IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
+IdentifierHashEntry *IdentifierHash::addEntry(Identifier identifier)
{
+ Q_ASSERT(identifier.isValid());
+
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
@@ -104,10 +125,10 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
memset(newEntries, 0, newAlloc*sizeof(IdentifierHashEntry));
for (int i = 0; i < d->alloc; ++i) {
const IdentifierHashEntry &e = d->entries[i];
- if (!e.identifier)
+ if (!e.identifier.isValid())
continue;
- uint idx = e.identifier->hashValue % newAlloc;
- while (newEntries[idx].identifier) {
+ uint idx = e.identifier.id % newAlloc;
+ while (newEntries[idx].identifier.isValid()) {
++idx;
idx %= newAlloc;
}
@@ -118,8 +139,8 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
d->alloc = newAlloc;
}
- uint idx = identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = identifier.id % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
Q_ASSERT(d->entries[idx].identifier != identifier);
++idx;
idx %= d->alloc;
@@ -129,15 +150,15 @@ IdentifierHashEntry *IdentifierHash::addEntry(const Identifier *identifier)
return d->entries + idx;
}
-const IdentifierHashEntry *IdentifierHash::lookup(const Identifier *identifier) const
+const IdentifierHashEntry *IdentifierHash::lookup(Identifier identifier) const
{
- if (!d)
+ if (!d || !identifier.isValid())
return nullptr;
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id % d->alloc;
while (1) {
- if (!d->entries[idx].identifier)
+ if (!d->entries[idx].identifier.isValid())
return nullptr;
if (d->entries[idx].identifier == identifier)
return d->entries + idx;
@@ -150,41 +171,56 @@ const IdentifierHashEntry *IdentifierHash::lookup(const QString &str) const
{
if (!d)
return nullptr;
- Q_ASSERT(d->entries);
- uint hash = String::createHashValue(str.constData(), str.length(), nullptr);
- uint idx = hash % d->alloc;
- while (1) {
- if (!d->entries[idx].identifier)
- return nullptr;
- if (d->entries[idx].identifier->string == str)
- return d->entries + idx;
- ++idx;
- idx %= d->alloc;
- }
+ Identifier id = d->identifierTable->identifier(str);
+ return lookup(id);
}
const IdentifierHashEntry *IdentifierHash::lookup(String *str) const
{
if (!d)
return nullptr;
- if (str->d()->identifier)
- return lookup(str->d()->identifier);
+ Identifier id = d->identifierTable->identifier(str);
+ if (id.isValid())
+ return lookup(id);
return lookup(str->toQString());
}
-const Identifier *IdentifierHash::toIdentifier(const QString &str) const
+const Identifier IdentifierHash::toIdentifier(const QString &str) const
{
Q_ASSERT(d);
return d->identifierTable->identifier(str);
}
-const Identifier *IdentifierHash::toIdentifier(Heap::String *str) const
+const Identifier IdentifierHash::toIdentifier(Heap::String *str) const
{
Q_ASSERT(d);
return d->identifierTable->identifier(str);
}
+QString QV4::IdentifierHash::findId(int value) const
+{
+ IdentifierHashEntry *e = d->entries;
+ IdentifierHashEntry *end = e + d->alloc;
+ while (e < end) {
+ if (e->identifier.isValid() && e->value == value)
+ return e->identifier.toQString();
+ ++e;
+ }
+ return QString();
+}
+
+void IdentifierHashData::markObjects(MarkStack *markStack) const
+{
+ IdentifierHashEntry *e = entries;
+ IdentifierHashEntry *end = e + alloc;
+ while (e < end) {
+ if (Heap::Base *o = e->identifier.asHeapObject())
+ o->mark(markStack);
+ ++e;
+ }
+}
+
}
diff --git a/src/qml/jsruntime/qv4identifier_p.h b/src/qml/jsruntime/qv4identifier_p.h
index 82346d5f68..b167a149a2 100644
--- a/src/qml/jsruntime/qv4identifier_p.h
+++ b/src/qml/jsruntime/qv4identifier_p.h
@@ -51,38 +51,47 @@
//
#include <qstring.h>
+#include <private/qv4global_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-namespace Heap {
- struct String;
-}
-
-struct String;
-struct IdentifierTable;
-struct ExecutionEngine;
-
struct Identifier
{
- QString string;
- uint hashValue;
+ // id's are either pointers to Heap::String or Heap::Symbol from the Identifier table.
+ // For Symbol is can simply point to itself.
+ // This gives us automative GC'ing of identifiers
+ // In addition, an identifier can have the lowest bit set, and then indicates an array index
+ quint64 id;
+
+ static Identifier invalid() { return Identifier{ 0 }; }
+ static Identifier fromArrayIndex(uint idx) { return Identifier{ (quint64(idx) << 1) | 1 }; }
+ bool isValid() const { return id && !(id & 1); }
+ uint asArrayIndex() const { return (id & 1) ? (id >> 1) : std::numeric_limits<uint>::max(); }
+ uint isArrayIndex() const { return (id & 1); }
+ static Identifier fromHeapObject(Heap::Base *b) { return Identifier{ reinterpret_cast<quintptr>(b) }; }
+ Heap::Base *asHeapObject() const { return (id & 1) ? nullptr : reinterpret_cast<Heap::Base *>(id); }
+
+ Q_QML_EXPORT QString toQString() const;
+
+ bool operator ==(const Identifier &other) const { return id == other.id; }
+ bool operator !=(const Identifier &other) const { return id != other.id; }
+ bool operator <(const Identifier &other) const { return id < other.id; }
};
struct IdentifierHashEntry {
- const Identifier *identifier;
+ Identifier identifier;
int value;
};
struct IdentifierHashData
{
- IdentifierHashData(int numBits);
+ IdentifierHashData(IdentifierTable *table, int numBits);
explicit IdentifierHashData(IdentifierHashData *other);
- ~IdentifierHashData() {
- free(entries);
- }
+ ~IdentifierHashData();
+ void markObjects(MarkStack *markStack) const;
QBasicAtomicInt refCount;
int alloc;
@@ -117,12 +126,12 @@ struct IdentifierHash
QString findId(int value) const;
protected:
- IdentifierHashEntry *addEntry(const Identifier *i);
- const IdentifierHashEntry *lookup(const Identifier *identifier) const;
+ IdentifierHashEntry *addEntry(Identifier i);
+ const IdentifierHashEntry *lookup(Identifier identifier) const;
const IdentifierHashEntry *lookup(const QString &str) const;
const IdentifierHashEntry *lookup(String *str) const;
- const Identifier *toIdentifier(const QString &str) const;
- const Identifier *toIdentifier(Heap::String *str) const;
+ const Identifier toIdentifier(const QString &str) const;
+ const Identifier toIdentifier(Heap::String *str) const;
};
@@ -180,20 +189,6 @@ inline int IdentifierHash::value(String *str) const
return e ? e->value : -1;
}
-
-inline
-QString IdentifierHash::findId(int value) const
-{
- IdentifierHashEntry *e = d->entries;
- IdentifierHashEntry *end = e + d->alloc;
- while (e < end) {
- if (e->identifier && e->value == value)
- return e->identifier->string;
- ++e;
- }
- return QString();
-}
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp
index b77f9478d3..c045ac3008 100644
--- a/src/qml/jsruntime/qv4identifiertable.cpp
+++ b/src/qml/jsruntime/qv4identifiertable.cpp
@@ -37,6 +37,7 @@
**
****************************************************************************/
#include "qv4identifiertable_p.h"
+#include "qv4symbol_p.h"
QT_BEGIN_NAMESPACE
@@ -59,38 +60,38 @@ IdentifierTable::IdentifierTable(ExecutionEngine *engine)
, numBits(8)
{
alloc = primeForNumBits(numBits);
- entries = (Heap::String **)malloc(alloc*sizeof(Heap::String *));
- memset(entries, 0, alloc*sizeof(Heap::String *));
+ entriesByHash = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ entriesById = (Heap::StringOrSymbol **)malloc(alloc*sizeof(Heap::StringOrSymbol *));
+ memset(entriesByHash, 0, alloc*sizeof(Heap::String *));
+ memset(entriesById, 0, alloc*sizeof(Heap::String *));
}
IdentifierTable::~IdentifierTable()
{
- for (int i = 0; i < alloc; ++i)
- if (entries[i])
- delete entries[i]->identifier;
- free(entries);
+ free(entriesByHash);
+ free(entriesById);
+ for (auto &h : idHashes)
+ h->identifierTable = nullptr;
}
-void IdentifierTable::addEntry(Heap::String *str)
+void IdentifierTable::addEntry(Heap::StringOrSymbol *str)
{
uint hash = str->hashValue();
if (str->subtype == Heap::String::StringType_ArrayIndex)
return;
- str->identifier = new Identifier;
- str->identifier->string = str->toQString();
- str->identifier->hashValue = hash;
+ str->identifier = Identifier::fromHeapObject(str);
bool grow = (alloc <= size*2);
if (grow) {
++numBits;
int newAlloc = primeForNumBits(numBits);
- Heap::String **newEntries = (Heap::String **)malloc(newAlloc*sizeof(Heap::String *));
- memset(newEntries, 0, newAlloc*sizeof(Heap::String *));
+ Heap::StringOrSymbol **newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
for (int i = 0; i < alloc; ++i) {
- Heap::String *e = entries[i];
+ Heap::StringOrSymbol *e = entriesByHash[i];
if (!e)
continue;
uint idx = e->stringHash % newAlloc;
@@ -100,17 +101,42 @@ void IdentifierTable::addEntry(Heap::String *str)
}
newEntries[idx] = e;
}
- free(entries);
- entries = newEntries;
+ free(entriesByHash);
+ entriesByHash = newEntries;
+
+ newEntries = (Heap::StringOrSymbol **)malloc(newAlloc*sizeof(Heap::String *));
+ memset(newEntries, 0, newAlloc*sizeof(Heap::StringOrSymbol *));
+ for (int i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *e = entriesById[i];
+ if (!e)
+ continue;
+ uint idx = e->identifier.id % newAlloc;
+ while (newEntries[idx]) {
+ ++idx;
+ idx %= newAlloc;
+ }
+ newEntries[idx] = e;
+ }
+ free(entriesById);
+ entriesById = newEntries;
+
alloc = newAlloc;
}
uint idx = hash % alloc;
- while (entries[idx]) {
+ while (entriesByHash[idx]) {
+ ++idx;
+ idx %= alloc;
+ }
+ entriesByHash[idx] = str;
+
+ idx = str->identifier.id % alloc;
+ while (entriesById[idx]) {
++idx;
idx %= alloc;
}
- entries[idx] = str;
+ entriesById[idx] = str;
+
++size;
}
@@ -121,9 +147,9 @@ Heap::String *IdentifierTable::insertString(const QString &s)
uint subtype;
uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == s)
- return e;
+ return static_cast<Heap::String *>(e);
++idx;
idx %= alloc;
}
@@ -135,18 +161,42 @@ Heap::String *IdentifierTable::insertString(const QString &s)
return str;
}
+Heap::Symbol *IdentifierTable::insertSymbol(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+
+ uint subtype;
+ uint hash = String::createHashValue(s.constData(), s.length(), &subtype);
+ uint idx = hash % alloc;
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == s)
+ return static_cast<Heap::Symbol *>(e);
+ ++idx;
+ idx %= alloc;
+ }
+
+ Heap::Symbol *str = Symbol::create(engine, s);
+ str->stringHash = hash;
+ str->subtype = subtype;
+ addEntry(str);
+ return str;
+
+}
+
-Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
+Identifier IdentifierTable::identifierImpl(const Heap::String *str)
{
- if (str->identifier)
+ if (str->identifier.isValid())
return str->identifier;
uint hash = str->hashValue();
- if (str->subtype == Heap::String::StringType_ArrayIndex)
- return nullptr;
+ if (str->subtype == Heap::String::StringType_ArrayIndex) {
+ str->identifier = Identifier::fromArrayIndex(hash);
+ return str->identifier;
+ }
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
- if (e->stringHash == hash && e->isEqualTo(str)) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
+ if (e->stringHash == hash && e->toQString() == str->toQString()) {
str->identifier = e->identifier;
return e->identifier;
}
@@ -158,28 +208,104 @@ Identifier *IdentifierTable::identifierImpl(const Heap::String *str)
return str->identifier;
}
-Heap::String *IdentifierTable::stringFromIdentifier(Identifier *i)
+Heap::StringOrSymbol *IdentifierTable::resolveId(Identifier i) const
{
- if (!i)
+ uint arrayIdx = i.asArrayIndex();
+ if (arrayIdx < UINT_MAX)
+ return engine->newString(QString::number(arrayIdx));
+ if (!i.isValid())
return nullptr;
- uint idx = i->hashValue % alloc;
+ uint idx = i.id % alloc;
while (1) {
- Heap::String *e = entries[idx];
- Q_ASSERT(e);
- if (e->identifier == i)
+ Heap::StringOrSymbol *e = entriesById[idx];
+ if (!e || e->identifier == i)
return e;
++idx;
idx %= alloc;
}
}
-Identifier *IdentifierTable::identifier(const QString &s)
+Heap::String *IdentifierTable::stringForId(Identifier i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(s && s->internalClass->vtable->isString);
+ return static_cast<Heap::String *>(s);
+}
+
+Heap::Symbol *IdentifierTable::symbolForId(Identifier i) const
+{
+ Heap::StringOrSymbol *s = resolveId(i);
+ Q_ASSERT(!s || !s->internalClass->vtable->isString);
+ return static_cast<Heap::Symbol *>(s);
+}
+
+void IdentifierTable::markObjects(MarkStack *markStack)
+{
+ for (const auto &h : idHashes)
+ h->markObjects(markStack);
+}
+
+template <typename Key>
+int sweepTable(Heap::StringOrSymbol **table, int alloc, std::function<Key(Heap::StringOrSymbol *)> f) {
+ int freed = 0;
+ Key lastKey = 0;
+ int lastEntry = -1;
+ int start = 0;
+ // start at an empty entry so we compress properly
+ for (; start < alloc; ++start) {
+ if (!table[start])
+ break;
+ }
+
+ for (int i = 0; i < alloc; ++i) {
+ int idx = (i + start) % alloc;
+ Heap::StringOrSymbol *entry = table[idx];
+ if (!entry) {
+ lastEntry = -1;
+ continue;
+ }
+ if (entry->isMarked()) {
+ if (lastEntry >= 0 && lastKey == f(entry)) {
+ Q_ASSERT(table[lastEntry] == nullptr);
+ table[lastEntry] = entry;
+ table[idx] = nullptr;
+ lastEntry = (lastEntry + 1) % alloc;
+ Q_ASSERT(table[lastEntry] == nullptr);
+ }
+ continue;
+ }
+ if (lastEntry == -1) {
+ lastEntry = idx;
+ lastKey = f(entry);
+ }
+ table[idx] = nullptr;
+ ++freed;
+ }
+ for (int i = 0; i < alloc; ++i) {
+ Heap::StringOrSymbol *entry = table[i];
+ if (!entry)
+ continue;
+ Q_ASSERT(entry->isMarked());
+ }
+ return freed;
+}
+
+void IdentifierTable::sweep()
+{
+ int f = sweepTable<int>(entriesByHash, alloc, [](Heap::StringOrSymbol *entry) {return entry->hashValue(); });
+ int freed = sweepTable<quint64>(entriesById, alloc, [](Heap::StringOrSymbol *entry) {return entry->identifier.id; });
+ Q_UNUSED(f);
+ Q_ASSERT(f == freed);
+ size -= freed;
+}
+
+Identifier IdentifierTable::identifier(const QString &s)
{
return insertString(s)->identifier;
}
-Identifier *IdentifierTable::identifier(const char *s, int len)
+Identifier IdentifierTable::identifier(const char *s, int len)
{
uint subtype;
uint hash = String::createHashValue(s, len, &subtype);
@@ -188,7 +314,7 @@ Identifier *IdentifierTable::identifier(const char *s, int len)
QLatin1String latin(s, len);
uint idx = hash % alloc;
- while (Heap::String *e = entries[idx]) {
+ while (Heap::StringOrSymbol *e = entriesByHash[idx]) {
if (e->stringHash == hash && e->toQString() == latin)
return e->identifier;
++idx;
diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h
index b0b08f1e54..6e6600d055 100644
--- a/src/qml/jsruntime/qv4identifiertable_p.h
+++ b/src/qml/jsruntime/qv4identifiertable_p.h
@@ -53,6 +53,7 @@
#include "qv4identifier_p.h"
#include "qv4string_p.h"
#include "qv4engine_p.h"
+#include <qset.h>
#include <limits.h>
QT_BEGIN_NAMESPACE
@@ -66,9 +67,12 @@ struct IdentifierTable
int alloc;
int size;
int numBits;
- Heap::String **entries;
+ Heap::StringOrSymbol **entriesByHash;
+ Heap::StringOrSymbol **entriesById;
- void addEntry(Heap::String *str);
+ QSet<IdentifierHashData *> idHashes;
+
+ void addEntry(Heap::StringOrSymbol *str);
public:
@@ -76,32 +80,34 @@ public:
~IdentifierTable();
Heap::String *insertString(const QString &s);
+ Heap::Symbol *insertSymbol(const QString &s);
- Identifier *identifier(const Heap::String *str) {
- if (str->identifier)
+ Identifier identifier(const Heap::String *str) {
+ if (str->identifier.isValid())
return str->identifier;
return identifierImpl(str);
}
- Identifier *identifier(const QV4::String *str) {
+ Identifier identifier(const QV4::String *str) {
return identifier(str->d());
}
- Identifier *identifier(const QString &s);
- Identifier *identifier(const char *s, int len);
+ Identifier identifier(const QString &s);
+ Identifier identifier(const char *s, int len);
+
+ Identifier identifierImpl(const Heap::String *str);
- Identifier *identifierImpl(const Heap::String *str);
+ Heap::StringOrSymbol *resolveId(Identifier i) const;
+ Q_QML_PRIVATE_EXPORT Heap::String *stringForId(Identifier i) const;
+ Q_QML_PRIVATE_EXPORT Heap::Symbol *symbolForId(Identifier i) const;
- Heap::String *stringFromIdentifier(Identifier *i);
+ void markObjects(MarkStack *markStack);
+ void sweep();
- void mark(MarkStack *markStack) {
- for (int i = 0; i < alloc; ++i) {
- Heap::String *entry = entries[i];
- if (!entry || entry->isMarked())
- continue;
- entry->setMarkBit();
- Q_ASSERT(entry->vtable()->markObjects);
- entry->vtable()->markObjects(entry, markStack);
- }
+ void addIdentifierHash(IdentifierHashData *h) {
+ idHashes.insert(h);
+ }
+ void removeIdentifierHash(IdentifierHashData *h) {
+ idHashes.remove(h);
}
};
diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp
index 3bfcf358bf..a913d5ca75 100644
--- a/src/qml/jsruntime/qv4internalclass.cpp
+++ b/src/qml/jsruntime/qv4internalclass.cpp
@@ -47,7 +47,7 @@
QT_BEGIN_NAMESPACE
-using namespace QV4;
+namespace QV4 {
static const uchar prime_deltas[] = {
0, 0, 1, 3, 1, 5, 3, 3, 1, 9, 7, 5, 3, 9, 25, 3,
@@ -74,27 +74,11 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
// fill up to max 50%
bool grow = (d->alloc <= d->size*2);
- if (classSize < d->size || grow) {
- PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
- for (int i = 0; i < d->alloc; ++i) {
- const Entry &e = d->entries[i];
- if (!e.identifier || e.index >= static_cast<unsigned>(classSize))
- continue;
- uint idx = e.identifier->hashValue % dd->alloc;
- while (dd->entries[idx].identifier) {
- ++idx;
- idx %= dd->alloc;
- }
- dd->entries[idx] = e;
- }
- dd->size = classSize;
- Q_ASSERT(d->refCount > 1);
- --d->refCount;
- d = dd;
- }
+ if (classSize < d->size || grow)
+ detach(grow, classSize);
- uint idx = entry.identifier->hashValue % d->alloc;
- while (d->entries[idx].identifier) {
+ uint idx = entry.identifier.id % d->alloc;
+ while (d->entries[idx].identifier.isValid()) {
++idx;
idx %= d->alloc;
}
@@ -102,38 +86,125 @@ void PropertyHash::addEntry(const PropertyHash::Entry &entry, int classSize)
++d->size;
}
+int PropertyHash::removeIdentifier(Identifier identifier, int classSize)
+{
+ int val = -1;
+ PropertyHashData *dd = new PropertyHashData(d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ if (e.identifier == identifier) {
+ val = e.index;
+ continue;
+ }
+ uint idx = e.identifier.id % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
+
+ Q_ASSERT(val != -1);
+ return val;
+}
+
+void PropertyHash::detach(bool grow, int classSize)
+{
+ if (d->refCount == 1 && !grow)
+ return;
-InternalClass::InternalClass(ExecutionEngine *engine)
- : engine(engine)
- , vtable(nullptr)
- , prototype(nullptr)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(0)
- , extensible(true)
+ PropertyHashData *dd = new PropertyHashData(grow ? d->numBits + 1 : d->numBits);
+ for (int i = 0; i < d->alloc; ++i) {
+ const Entry &e = d->entries[i];
+ if (!e.identifier.isValid() || e.index >= static_cast<unsigned>(classSize))
+ continue;
+ uint idx = e.identifier.id % dd->alloc;
+ while (dd->entries[idx].identifier.isValid()) {
+ ++idx;
+ idx %= dd->alloc;
+ }
+ dd->entries[idx] = e;
+ }
+ dd->size = classSize;
+ if (!--d->refCount)
+ delete d;
+ d = dd;
+}
+
+namespace Heap {
+
+void InternalClass::init(ExecutionEngine *engine)
{
- id = engine->newInternalClassId();
+ Base::init();
+ new (&propertyTable) PropertyHash();
+ new (&nameMap) SharedInternalClassData<Identifier>();
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>();
+ new (&transitions) std::vector<Transition>();
+
+ this->engine = engine;
+ vtable = QV4::InternalClass::staticVTable();
+// prototype = nullptr;
+// parent = nullptr;
+// size = 0;
+ extensible = true;
+ isFrozen = false;
+ isSealed = false;
+ isUsedAsProto = false;
+ protoId = engine->newProtoId();
+
+ // Also internal classes need an internal class pointer. Simply make it point to itself
+ internalClass.set(engine, this);
}
-InternalClass::InternalClass(const QV4::InternalClass &other)
- : QQmlJS::Managed()
- , engine(other.engine)
- , vtable(other.vtable)
- , prototype(other.prototype)
- , propertyTable(other.propertyTable)
- , nameMap(other.nameMap)
- , propertyData(other.propertyData)
- , m_sealed(nullptr)
- , m_frozen(nullptr)
- , size(other.size)
- , extensible(other.extensible)
- , isUsedAsProto(other.isUsedAsProto)
+void InternalClass::init(Heap::InternalClass *other)
+{
+ Base::init();
+ Q_ASSERT(!other->isFrozen);
+ new (&propertyTable) PropertyHash(other->propertyTable);
+ new (&nameMap) SharedInternalClassData<Identifier>(other->nameMap);
+ new (&propertyData) SharedInternalClassData<PropertyAttributes>(other->propertyData);
+ new (&transitions) std::vector<Transition>();
+
+ engine = other->engine;
+ vtable = other->vtable;
+ prototype = other->prototype;
+ parent = other;
+ size = other->size;
+ extensible = other->extensible;
+ isSealed = other->isSealed;
+ isFrozen = other->isFrozen;
+ isUsedAsProto = other->isUsedAsProto;
+ protoId = engine->newProtoId();
+
+ internalClass.set(engine, other->internalClass);
+}
+
+void InternalClass::destroy()
{
- id = engine->newInternalClassId();
+#ifndef QT_NO_DEBUG
+ for (const auto &t : transitions) {
+ Q_ASSERT(!t.lookup || !t.lookup->isMarked());
+ }
+#endif
+ if (parent && parent->engine && parent->isMarked())
+ parent->removeChildEntry(this);
+
+ propertyTable.~PropertyHash();
+ nameMap.~SharedInternalClassData<Identifier>();
+ propertyData.~SharedInternalClassData<PropertyAttributes>();
+ transitions.~vector<Transition>();
+ engine = nullptr;
+ Base::destroy();
}
-static void insertHoleIntoPropertyData(Object *object, int idx)
+static void insertHoleIntoPropertyData(QV4::Object *object, int idx)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@@ -142,7 +213,7 @@ static void insertHoleIntoPropertyData(Object *object, int idx)
o->setProperty(v4, i, *o->propertyData(i - 1));
}
-static void removeFromPropertyData(Object *object, int idx, bool accessor = false)
+static void removeFromPropertyData(QV4::Object *object, int idx, bool accessor = false)
{
Heap::Object *o = object->d();
ExecutionEngine *v4 = o->internalClass->engine;
@@ -154,20 +225,23 @@ static void removeFromPropertyData(Object *object, int idx, bool accessor = fals
o->setProperty(v4, size + 1, Primitive::undefinedValue());
}
-void InternalClass::changeMember(Object *object, String *string, PropertyAttributes data, uint *index)
+void InternalClass::changeMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(id.isValid());
uint idx;
- InternalClass *oldClass = object->internalClass();
- InternalClass *newClass = oldClass->changeMember(string->identifier(), data, &idx);
+ Heap::InternalClass *oldClass = object->internalClass();
+ Heap::InternalClass *newClass = oldClass->changeMember(id, data, &idx);
if (index)
*index = idx;
+ uint oldSize = oldClass->size;
object->setInternalClass(newClass);
- if (newClass->size > oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size + 1);
+ // don't use oldClass anymore, it could be GC'ed
+ if (newClass->size > oldSize) {
+ Q_ASSERT(newClass->size == oldSize + 1);
insertHoleIntoPropertyData(object, idx);
- } else if (newClass->size < oldClass->size) {
- Q_ASSERT(newClass->size == oldClass->size - 1);
+ } else if (newClass->size < oldSize) {
+ Q_ASSERT(newClass->size == oldSize - 1);
removeFromPropertyData(object, idx + 1);
}
}
@@ -183,7 +257,16 @@ InternalClassTransition &InternalClass::lookupOrInsertTransition(const InternalC
}
}
-InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttributes data, uint *index)
+static void addDummyEntry(InternalClass *newClass, PropertyHash::Entry e)
+{
+ // add a dummy entry, since we need two entries for accessors
+ newClass->propertyTable.addEntry(e, newClass->size);
+ newClass->nameMap.add(newClass->size, Identifier::invalid());
+ newClass->propertyData.add(newClass->size, PropertyAttributes());
+ ++newClass->size;
+}
+
+Heap::InternalClass *InternalClass::changeMember(Identifier identifier, PropertyAttributes data, uint *index)
{
data.resolve();
uint idx = find(identifier);
@@ -193,7 +276,7 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
*index = idx;
if (data == propertyData.at(idx))
- return this;
+ return static_cast<Heap::InternalClass *>(this);
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@@ -201,14 +284,34 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (i == idx) {
- newClass = newClass->addMember(nameMap.at(i), data);
- } else if (!propertyData.at(i).isEmpty()) {
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
+ Heap::InternalClass *newClass = engine->newClass(this);
+ if (data.isAccessor() != propertyData.at(idx).isAccessor()) {
+ // this changes the layout of the class, so we need to rebuild the data
+ newClass->propertyTable = PropertyHash();
+ newClass->nameMap = SharedInternalClassData<Identifier>();
+ newClass->propertyData = SharedInternalClassData<PropertyAttributes>();
+ newClass->size = 0;
+ for (uint i = 0; i < size; ++i) {
+ Identifier identifier = nameMap.at(i);
+ PropertyHash::Entry e = { identifier, newClass->size };
+ if (i && !identifier.isValid())
+ e.identifier = nameMap.at(i - 1);
+ newClass->propertyTable.addEntry(e, newClass->size);
+ newClass->nameMap.add(newClass->size, identifier);
+ if (i == idx) {
+ newClass->propertyData.add(newClass->size, data);
+ ++newClass->size;
+ if (data.isAccessor())
+ addDummyEntry(newClass, e);
+ else
+ ++i;
+ } else {
+ newClass->propertyData.add(newClass->size, propertyData.at(i));
+ ++newClass->size;
+ }
}
+ } else {
+ newClass->propertyData.set(idx, data);
}
t.lookup = newClass;
@@ -216,14 +319,16 @@ InternalClass *InternalClass::changeMember(Identifier *identifier, PropertyAttri
return newClass;
}
-InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
+Heap::InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
{
+ Scope scope(engine);
+ ScopedValue protectThis(scope, this);
if (proto)
proto->setUsedAsProto();
Q_ASSERT(prototype != proto);
Q_ASSERT(!proto || proto->internalClass->isUsedAsProto);
- Transition temp = { { nullptr }, nullptr, Transition::PrototypeChange };
+ Transition temp = { { Identifier::invalid() }, nullptr, Transition::PrototypeChange };
temp.prototype = proto;
Transition &t = lookupOrInsertTransition(temp);
@@ -231,29 +336,19 @@ InternalClass *InternalClass::changePrototypeImpl(Heap::Object *proto)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (!size && !prototype) {
- newClass = engine->newClass(*this);
- newClass->prototype = proto;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- newClass = newClass->changePrototype(proto);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->prototype = proto;
t.lookup = newClass;
return newClass;
}
-InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
+Heap::InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
{
Q_ASSERT(vtable != vt);
- Transition temp = { { nullptr }, nullptr, Transition::VTableChange };
+ Transition temp = { { Identifier::invalid() }, nullptr, Transition::VTableChange };
temp.vtable = vt;
Transition &t = lookupOrInsertTransition(temp);
@@ -261,18 +356,8 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass;
- if (this == engine->internalClasses[EngineBase::Class_Empty]) {
- newClass = engine->newClass(*this);
- newClass->vtable = vt;
- } else {
- newClass = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vt);
- newClass = newClass->changePrototype(prototype);
- for (uint i = 0; i < size; ++i) {
- if (!propertyData.at(i).isEmpty())
- newClass = newClass->addMember(nameMap.at(i), propertyData.at(i));
- }
- }
+ Heap::InternalClass *newClass = engine->newClass(this);
+ newClass->vtable = vt;
t.lookup = newClass;
Q_ASSERT(t.lookup);
@@ -280,17 +365,17 @@ InternalClass *InternalClass::changeVTableImpl(const VTable *vt)
return newClass;
}
-InternalClass *InternalClass::nonExtensible()
+Heap::InternalClass *InternalClass::nonExtensible()
{
if (!extensible)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::NotExtensible};
+ Transition temp = { { Identifier::invalid() }, nullptr, Transition::NotExtensible};
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->extensible = false;
t.lookup = newClass;
@@ -298,31 +383,26 @@ InternalClass *InternalClass::nonExtensible()
return newClass;
}
-void InternalClass::addMember(Object *object, String *string, PropertyAttributes data, uint *index)
+void InternalClass::addMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(id.isValid());
data.resolve();
- object->internalClass()->engine->identifierTable->identifier(string);
- if (object->internalClass()->propertyTable.lookup(string->d()->identifier) < object->internalClass()->size) {
- changeMember(object, string, data, index);
+ if (object->internalClass()->propertyTable.lookup(id) < object->internalClass()->size) {
+ changeMember(object, id, data, index);
return;
}
uint idx;
- InternalClass *newClass = object->internalClass()->addMemberImpl(string->identifier(), data, &idx);
+ Heap::InternalClass *newClass = object->internalClass()->addMemberImpl(id, data, &idx);
if (index)
*index = idx;
object->setInternalClass(newClass);
}
-InternalClass *InternalClass::addMember(String *string, PropertyAttributes data, uint *index)
-{
- engine->identifierTable->identifier(string);
- return addMember(string->identifier(), data, index);
-}
-
-InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMember(Identifier identifier, PropertyAttributes data, uint *index)
{
+ Q_ASSERT(identifier.isValid());
data.resolve();
if (propertyTable.lookup(identifier) < size)
@@ -331,7 +411,7 @@ InternalClass *InternalClass::addMember(Identifier *identifier, PropertyAttribut
return addMemberImpl(identifier, data, index);
}
-InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index)
+Heap::InternalClass *InternalClass::addMemberImpl(Identifier identifier, PropertyAttributes data, uint *index)
{
Transition temp = { { identifier }, nullptr, (int)data.flags() };
Transition &t = lookupOrInsertTransition(temp);
@@ -343,109 +423,156 @@ InternalClass *InternalClass::addMemberImpl(Identifier *identifier, PropertyAttr
return t.lookup;
// create a new class and add it to the tree
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
PropertyHash::Entry e = { identifier, newClass->size };
newClass->propertyTable.addEntry(e, newClass->size);
newClass->nameMap.add(newClass->size, identifier);
newClass->propertyData.add(newClass->size, data);
++newClass->size;
- if (data.isAccessor()) {
- // add a dummy entry, since we need two entries for accessors
- newClass->propertyTable.addEntry(e, newClass->size);
- newClass->nameMap.add(newClass->size, 0);
- newClass->propertyData.add(newClass->size, PropertyAttributes());
- ++newClass->size;
- }
+ if (data.isAccessor())
+ addDummyEntry(newClass, e);
t.lookup = newClass;
Q_ASSERT(t.lookup);
return newClass;
}
-void InternalClass::removeMember(Object *object, Identifier *id)
+void InternalClass::removeChildEntry(InternalClass *child)
{
- InternalClass *oldClass = object->internalClass();
- uint propIdx = oldClass->propertyTable.lookup(id);
- Q_ASSERT(propIdx < oldClass->size);
+ Q_ASSERT(engine);
+ for (auto &t : transitions) {
+ if (t.lookup == child) {
+ t.lookup = nullptr;
+ return;
+ }
+ }
+ Q_UNREACHABLE();
- Transition temp = { { id }, nullptr, -1 };
- Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
+}
- bool accessor = oldClass->propertyData.at(propIdx).isAccessor();
+void InternalClass::removeMember(QV4::Object *object, Identifier identifier)
+{
+ Heap::InternalClass *oldClass = object->internalClass();
+ Q_ASSERT(oldClass->propertyTable.lookup(identifier) < oldClass->size);
- if (t.lookup) {
- object->setInternalClass(t.lookup);
- } else {
+ Transition temp = { { identifier }, nullptr, Transition::RemoveMember };
+ Transition &t = object->internalClass()->lookupOrInsertTransition(temp);
+
+ if (!t.lookup) {
// create a new class and add it to the tree
- InternalClass *newClass = oldClass->engine->internalClasses[EngineBase::Class_Empty]->changeVTable(oldClass->vtable);
- newClass = newClass->changePrototype(oldClass->prototype);
- for (uint i = 0; i < oldClass->size; ++i) {
- if (i == propIdx)
- continue;
- if (!oldClass->propertyData.at(i).isEmpty())
- newClass = newClass->addMember(oldClass->nameMap.at(i), oldClass->propertyData.at(i));
- }
- object->setInternalClass(newClass);
+ Heap::InternalClass *newClass = oldClass->engine->newClass(oldClass);
+ // simply make the entry inaccessible
+ int idx = newClass->propertyTable.removeIdentifier(identifier, oldClass->size);
+ newClass->nameMap.set(idx, Identifier::invalid());
+ newClass->propertyData.set(idx, PropertyAttributes());
+ t.lookup = newClass;
+ Q_ASSERT(t.lookup);
}
+ object->setInternalClass(t.lookup);
- Q_ASSERT(object->internalClass()->size == oldClass->size - (accessor ? 2 : 1));
-
- // remove the entry in the property data
- removeFromPropertyData(object, propIdx, accessor);
-
- t.lookup = object->internalClass();
- Q_ASSERT(t.lookup);
+ // we didn't remove the data slot, just made it inaccessible
+ Q_ASSERT(object->internalClass()->size == oldClass->size);
}
-uint InternalClass::find(const String *string)
+Heap::InternalClass *InternalClass::sealed()
{
- engine->identifierTable->identifier(string);
- const Identifier *id = string->d()->identifier;
+ if (isSealed)
+ return this;
- uint index = propertyTable.lookup(id);
- if (index < size)
- return index;
+ bool alreadySealed = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isConfigurable()) {
+ alreadySealed = false;
+ break;
+ }
+ }
- return UINT_MAX;
-}
+ if (alreadySealed) {
+ isSealed = true;
+ return this;
+ }
-InternalClass *InternalClass::sealed()
-{
- if (m_sealed)
- return m_sealed;
+ Transition temp = { { Identifier::invalid() }, nullptr, InternalClassTransition::Sealed };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed);
+ return t.lookup;
+ }
+
+ Heap::InternalClass *s = engine->newClass(this);
- m_sealed = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
- m_sealed = m_sealed->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
if (attrs.isEmpty())
continue;
attrs.setConfigurable(false);
- m_sealed = m_sealed->addMember(nameMap.at(i), attrs);
+ s->propertyData.set(i, attrs);
}
- m_sealed = m_sealed->nonExtensible();
+ s->extensible = false;
+ s->isSealed = true;
- m_sealed->m_sealed = m_sealed;
- return m_sealed;
+ t.lookup = s;
+ return s;
}
-InternalClass *InternalClass::frozen()
+Heap::InternalClass *InternalClass::frozen()
{
- if (m_frozen)
- return m_frozen;
+ if (isFrozen)
+ return this;
+
+ bool alreadyFrozen = !extensible;
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if ((attrs.isData() && attrs.isWritable()) || attrs.isConfigurable()) {
+ alreadyFrozen = false;
+ break;
+ }
+ }
+
+ if (alreadyFrozen) {
+ isSealed = true;
+ isFrozen = true;
+ return this;
+ }
+
+ Transition temp = { { Identifier::invalid() }, nullptr, InternalClassTransition::Frozen };
+ Transition &t = lookupOrInsertTransition(temp);
+
+ if (t.lookup) {
+ Q_ASSERT(t.lookup && t.lookup->isSealed && t.lookup->isFrozen);
+ return t.lookup;
+ }
+
+ Heap::InternalClass *f = engine->newClass(this);
- m_frozen = propertiesFrozen();
- m_frozen = m_frozen->nonExtensible();
+ for (uint i = 0; i < size; ++i) {
+ PropertyAttributes attrs = propertyData.at(i);
+ if (attrs.isEmpty())
+ continue;
+ if (attrs.isData())
+ attrs.setWritable(false);
+ attrs.setConfigurable(false);
+ f->propertyData.set(i, attrs);
+ }
+ f->extensible = false;
+ f->isSealed = true;
+ f->isFrozen = true;
- m_frozen->m_frozen = m_frozen;
- m_frozen->m_sealed = m_frozen;
- return m_frozen;
+ t.lookup = f;
+ return f;
}
-InternalClass *InternalClass::propertiesFrozen() const
+Heap::InternalClass *InternalClass::propertiesFrozen() const
{
- InternalClass *frozen = engine->internalClasses[EngineBase::Class_Empty]->changeVTable(vtable);
+ Scope scope(engine);
+ Scoped<QV4::InternalClass> frozen(scope, engine->internalClasses(EngineBase::Class_Empty)->changeVTable(vtable));
frozen = frozen->changePrototype(prototype);
for (uint i = 0; i < size; ++i) {
PropertyAttributes attrs = propertyData.at(i);
@@ -455,20 +582,20 @@ InternalClass *InternalClass::propertiesFrozen() const
attrs.setConfigurable(false);
frozen = frozen->addMember(nameMap.at(i), attrs);
}
- return frozen;
+ return frozen->d();
}
-InternalClass *InternalClass::asProtoClass()
+Heap::InternalClass *InternalClass::asProtoClass()
{
if (isUsedAsProto)
return this;
- Transition temp = { { nullptr }, nullptr, Transition::ProtoClass };
+ Transition temp = { { Identifier::invalid() }, nullptr, Transition::ProtoClass };
Transition &t = lookupOrInsertTransition(temp);
if (t.lookup)
return t.lookup;
- InternalClass *newClass = engine->newClass(*this);
+ Heap::InternalClass *newClass = engine->newClass(this);
newClass->isUsedAsProto = true;
t.lookup = newClass;
@@ -476,90 +603,43 @@ InternalClass *InternalClass::asProtoClass()
return newClass;
}
-void InternalClass::destroy()
+static void updateProtoUsage(Heap::Object *o, Heap::InternalClass *ic)
{
- std::vector<InternalClass *> destroyStack;
- destroyStack.reserve(64);
- destroyStack.push_back(this);
-
- while (!destroyStack.empty()) {
- InternalClass *next = destroyStack.back();
- destroyStack.pop_back();
- if (!next->engine)
- continue;
- next->engine = nullptr;
- next->propertyTable.~PropertyHash();
- next->nameMap.~SharedInternalClassData<Identifier *>();
- next->propertyData.~SharedInternalClassData<PropertyAttributes>();
- if (next->m_sealed)
- destroyStack.push_back(next->m_sealed);
- if (next->m_frozen)
- destroyStack.push_back(next->m_frozen);
-
- for (size_t i = 0; i < next->transitions.size(); ++i) {
- Q_ASSERT(next->transitions.at(i).lookup);
- destroyStack.push_back(next->transitions.at(i).lookup);
- }
-
- next->transitions.~vector<Transition>();
+ if (ic->prototype == o)
+ ic->protoId = ic->engine->newProtoId();
+ for (auto &t : ic->transitions) {
+ if (t.lookup)
+ updateProtoUsage(o, t.lookup);
}
}
+
void InternalClass::updateProtoUsage(Heap::Object *o)
{
Q_ASSERT(isUsedAsProto);
- InternalClass *ic = engine->internalClasses[EngineBase::Class_Empty];
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_Empty);
Q_ASSERT(!ic->prototype);
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange &&
- t2.lookup->prototype == o)
- ic2->updateInternalClassIdRecursive();
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange && t.lookup->prototype == o) {
- ic->updateInternalClassIdRecursive();
- }
- }
+ Heap::updateProtoUsage(o, ic);
}
-void InternalClass::updateInternalClassIdRecursive()
+void InternalClass::markObjects(Heap::Base *b, MarkStack *stack)
{
- id = engine->newInternalClassId();
- for (auto &t : transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange || t.flags == InternalClassTransition::PrototypeChange)
- continue;
- t.lookup->updateInternalClassIdRecursive();
+ Heap::InternalClass *ic = static_cast<Heap::InternalClass *>(b);
+ if (ic->prototype)
+ ic->prototype->mark(stack);
+ if (ic->parent)
+ ic->parent->mark(stack);
+
+ for (uint i = 0; i < ic->size; ++i) {
+ Identifier id = ic->nameMap.at(i);
+ if (Heap::Base *b = id.asHeapObject())
+ b->mark(stack);
}
}
+}
-
-void InternalClassPool::markObjects(MarkStack *markStack)
-{
- InternalClass *ic = markStack->engine->internalClasses[EngineBase::Class_Empty];
- Q_ASSERT(!ic->prototype);
-
- // only need to go two levels into the IC hierarchy, as prototype changes
- // can only happen there
- for (auto &t : ic->transitions) {
- Q_ASSERT(t.lookup);
- if (t.flags == InternalClassTransition::VTableChange) {
- InternalClass *ic2 = t.lookup;
- for (auto &t2 : ic2->transitions) {
- if (t2.flags == InternalClassTransition::PrototypeChange)
- t2.lookup->prototype->mark(markStack);
- }
- } else if (t.flags == InternalClassTransition::PrototypeChange) {
- t.lookup->prototype->mark(markStack);
- }
- }
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h
index b689272006..290251f4ba 100644
--- a/src/qml/jsruntime/qv4internalclass_p.h
+++ b/src/qml/jsruntime/qv4internalclass_p.h
@@ -53,16 +53,13 @@
#include "qv4global_p.h"
#include <QHash>
-#include <private/qqmljsmemorypool_p.h>
#include <private/qv4identifier_p.h>
+#include <private/qv4heap_p.h>
QT_BEGIN_NAMESPACE
namespace QV4 {
-struct String;
-struct Object;
-struct Identifier;
struct VTable;
struct MarkStack;
@@ -70,7 +67,7 @@ struct PropertyHashData;
struct PropertyHash
{
struct Entry {
- const Identifier *identifier;
+ Identifier identifier;
uint index;
};
@@ -79,12 +76,12 @@ struct PropertyHash
inline PropertyHash();
inline PropertyHash(const PropertyHash &other);
inline ~PropertyHash();
+ PropertyHash &operator=(const PropertyHash &other);
void addEntry(const Entry &entry, int classSize);
- uint lookup(const Identifier *identifier) const;
-
-private:
- PropertyHash &operator=(const PropertyHash &other);
+ uint lookup(Identifier identifier) const;
+ int removeIdentifier(Identifier identifier, int classSize);
+ void detach(bool grow, int classSize);
};
struct PropertyHashData
@@ -118,15 +115,26 @@ inline PropertyHash::~PropertyHash()
delete d;
}
-inline uint PropertyHash::lookup(const Identifier *identifier) const
+inline PropertyHash &PropertyHash::operator=(const PropertyHash &other)
+{
+ ++other.d->refCount;
+ if (!--d->refCount)
+ delete d;
+ d = other.d;
+ return *this;
+}
+
+
+
+inline uint PropertyHash::lookup(Identifier identifier) const
{
Q_ASSERT(d->entries);
- uint idx = identifier->hashValue % d->alloc;
+ uint idx = identifier.id % d->alloc;
while (1) {
if (d->entries[idx].identifier == identifier)
return d->entries[idx].index;
- if (!d->entries[idx].identifier)
+ if (!d->entries[idx].identifier.isValid())
return UINT_MAX;
++idx;
idx %= d->alloc;
@@ -163,6 +171,13 @@ struct SharedInternalClassData {
if (!--d->refcount)
delete d;
}
+ SharedInternalClassData &operator=(const SharedInternalClassData &other) {
+ ++other.d->refcount;
+ if (!--d->refcount)
+ delete d;
+ d = other.d;
+ return *this;
+ }
void add(uint pos, T value) {
if (pos < d->size) {
@@ -214,26 +229,26 @@ struct SharedInternalClassData {
Q_ASSERT(i < d->size);
return d->data[i];
}
-
-private:
- SharedInternalClassData &operator=(const SharedInternalClassData &other);
};
struct InternalClassTransition
{
union {
- Identifier *id;
+ Identifier id;
const VTable *vtable;
Heap::Object *prototype;
};
- InternalClass *lookup;
+ Heap::InternalClass *lookup;
int flags;
enum {
// range 0-0xff is reserved for attribute changes
NotExtensible = 0x100,
VTableChange = 0x200,
PrototypeChange = 0x201,
- ProtoClass = 0x202
+ ProtoClass = 0x202,
+ Sealed = 0x203,
+ Frozen = 0x204,
+ RemoveMember = -1
};
bool operator==(const InternalClassTransition &other) const
@@ -243,48 +258,44 @@ struct InternalClassTransition
{ return id < other.id || (id == other.id && flags < other.flags); }
};
-struct InternalClass : public QQmlJS::Managed {
- int id = 0; // unique across the engine, gets changed also when proto chain changes
+namespace Heap {
+
+struct InternalClass : Base {
ExecutionEngine *engine;
const VTable *vtable;
+ quintptr protoId; // unique across the engine, gets changed whenever the proto chain changes
Heap::Object *prototype;
+ InternalClass *parent;
PropertyHash propertyTable; // id to valueIndex
- SharedInternalClassData<Identifier *> nameMap;
+ SharedInternalClassData<Identifier> nameMap;
SharedInternalClassData<PropertyAttributes> propertyData;
typedef InternalClassTransition Transition;
std::vector<Transition> transitions;
InternalClassTransition &lookupOrInsertTransition(const InternalClassTransition &t);
- InternalClass *m_sealed;
- InternalClass *m_frozen;
-
uint size;
bool extensible;
- bool isUsedAsProto = false;
+ bool isSealed;
+ bool isFrozen;
+ bool isUsedAsProto;
+
+ void init(ExecutionEngine *engine);
+ void init(InternalClass *other);
+ void destroy();
Q_REQUIRED_RESULT InternalClass *nonExtensible();
- Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
- if (vtable == vt)
- return this;
- return changeVTableImpl(vt);
- }
- Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
- if (prototype == proto)
- return this;
- return changePrototypeImpl(proto);
- }
- static void addMember(Object *object, String *string, PropertyAttributes data, uint *index);
- Q_REQUIRED_RESULT InternalClass *addMember(String *string, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *addMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- Q_REQUIRED_RESULT InternalClass *changeMember(Identifier *identifier, PropertyAttributes data, uint *index = nullptr);
- static void changeMember(Object *object, String *string, PropertyAttributes data, uint *index = nullptr);
- static void removeMember(Object *object, Identifier *id);
- uint find(const String *string);
- uint find(const Identifier *id)
+ static void addMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index);
+ Q_REQUIRED_RESULT InternalClass *addMember(Identifier identifier, PropertyAttributes data, uint *index = nullptr);
+ Q_REQUIRED_RESULT InternalClass *changeMember(Identifier identifier, PropertyAttributes data, uint *index = nullptr);
+ static void changeMember(QV4::Object *object, Identifier id, PropertyAttributes data, uint *index = nullptr);
+ static void removeMember(QV4::Object *object, Identifier identifier);
+ uint find(const Identifier id)
{
+ Q_ASSERT(id.isValid());
+
uint index = propertyTable.lookup(id);
if (index < size)
return index;
@@ -298,24 +309,37 @@ struct InternalClass : public QQmlJS::Managed {
Q_REQUIRED_RESULT InternalClass *asProtoClass();
- void destroy();
+ Q_REQUIRED_RESULT InternalClass *changeVTable(const VTable *vt) {
+ if (vtable == vt)
+ return this;
+ return changeVTableImpl(vt);
+ }
+ Q_REQUIRED_RESULT InternalClass *changePrototype(Heap::Object *proto) {
+ if (prototype == proto)
+ return this;
+ return changePrototypeImpl(proto);
+ }
void updateProtoUsage(Heap::Object *o);
+ static void markObjects(Heap::Base *ic, MarkStack *stack);
+
private:
Q_QML_EXPORT InternalClass *changeVTableImpl(const VTable *vt);
Q_QML_EXPORT InternalClass *changePrototypeImpl(Heap::Object *proto);
- InternalClass *addMemberImpl(Identifier *identifier, PropertyAttributes data, uint *index);
- void updateInternalClassIdRecursive();
+ InternalClass *addMemberImpl(Identifier identifier, PropertyAttributes data, uint *index);
+
+ void removeChildEntry(InternalClass *child);
friend struct ExecutionEngine;
- InternalClass(ExecutionEngine *engine);
- InternalClass(const InternalClass &other);
};
-struct InternalClassPool : public QQmlJS::MemoryPool
+inline
+void Base::markObjects(Base *b, MarkStack *stack)
{
- void markObjects(MarkStack *markStack);
-};
+ b->internalClass->mark(stack);
+}
+
+}
}
diff --git a/src/qml/jsruntime/qv4iterator.cpp b/src/qml/jsruntime/qv4iterator.cpp
new file mode 100644
index 0000000000..df8000a8f7
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator.cpp
@@ -0,0 +1,64 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include <qv4iterator_p.h>
+#include <qv4symbol_p.h>
+#include <qv4engine_p.h>
+
+using namespace QV4;
+
+void IteratorPrototype::init(ExecutionEngine *engine)
+{
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator, 0);
+}
+
+ReturnedValue IteratorPrototype::method_iterator(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ return thisObject->asReturnedValue();
+}
+
+
+ReturnedValue IteratorPrototype::createIterResultObject(ExecutionEngine *engine, const Value &value, bool done)
+{
+ Scope scope(engine);
+ ScopedObject obj(scope, engine->newObject());
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("value"))), value, Object::DoNotThrow);
+ obj->set(ScopedString(scope, engine->newString(QStringLiteral("done"))), Primitive::fromBoolean(done), Object::DoNotThrow);
+ return obj->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4iterator_p.h b/src/qml/jsruntime/qv4iterator_p.h
new file mode 100644
index 0000000000..28e337d21b
--- /dev/null
+++ b/src/qml/jsruntime/qv4iterator_p.h
@@ -0,0 +1,82 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4ITERATOR_P_H
+#define QV4ITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4arraydata_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+enum IteratorKind {
+ KeyIteratorKind,
+ ValueIteratorKind,
+ KeyValueIteratorKind
+};
+
+struct IteratorPrototype : Object
+{
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_iterator(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue createIterResultObject(ExecutionEngine *engine, const Value &value, bool done);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h
index c676b57c51..e186285025 100644
--- a/src/qml/jsruntime/qv4jscall_p.h
+++ b/src/qml/jsruntime/qv4jscall_p.h
@@ -67,7 +67,7 @@ struct JSCallData {
if (thisObject)
this->thisObject = const_cast<Value *>(thisObject);
else
- this->thisObject = scope.alloc(1);
+ this->thisObject = scope.alloc();
if (argv)
this->args = const_cast<Value *>(argv);
else
@@ -80,8 +80,7 @@ struct JSCallData {
CallData *callData(const FunctionObject *f = nullptr) const {
int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + argc;
- CallData *ptr = reinterpret_cast<CallData *>(scope.engine->jsStackTop);
- scope.engine->jsStackTop += size;
+ CallData *ptr = reinterpret_cast<CallData *>(scope.alloc<Scope::Uninitialized>(size));
ptr->function = Encode::undefined();
ptr->context = Encode::undefined();
ptr->accumulator = Encode::undefined();
diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp
index c3569c29d2..75a9bf4111 100644
--- a/src/qml/jsruntime/qv4jsonobject.cpp
+++ b/src/qml/jsruntime/qv4jsonobject.cpp
@@ -47,6 +47,7 @@
#include <qv4variantobject_p.h>
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include <qv4symbol_p.h>
#include <qstack.h>
#include <qstringlist.h>
@@ -743,7 +744,7 @@ QString Stringify::Str(const QString &key, const Value &v)
o = value->asReturnedValue();
if (o) {
if (!o->as<FunctionObject>()) {
- if (o->as<ArrayObject>() || o->isListType()) {
+ if (o->isArrayLike()) {
return JA(o.getPointer());
} else {
return JO(o);
@@ -881,6 +882,8 @@ void Heap::JsonObject::init()
o->defineDefaultProperty(QStringLiteral("parse"), QV4::JsonObject::method_parse, 2);
o->defineDefaultProperty(QStringLiteral("stringify"), QV4::JsonObject::method_stringify, 3);
+ ScopedString json(scope, scope.engine->newString(QStringLiteral("JSON")));
+ o->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), json);
}
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 52ab03cd94..67b4b9dc38 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -47,7 +47,7 @@ QT_BEGIN_NAMESPACE
using namespace QV4;
-void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
+void Lookup::resolveProtoGetter(Identifier name, const Heap::Object *proto)
{
while (proto) {
uint index = proto->internalClass->find(name);
@@ -70,7 +70,12 @@ void Lookup::resolveProtoGetter(Identifier *name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
Heap::Object *obj = object->d();
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ if (name.isArrayIndex()) {
+ indexedLookup.index = name.asArrayIndex();
+ getter = getterIndexed;
+ return getter(this, engine, *object);
+ }
uint index = obj->internalClass->find(name);
if (index != UINT_MAX) {
@@ -92,7 +97,7 @@ ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *objec
return getter(this, engine, *object);
}
- protoLookup.icIdentifier = obj->internalClass->id;
+ protoLookup.protoId = obj->internalClass->protoId;
resolveProtoGetter(name, obj->prototype());
return getter(this, engine, *object);
}
@@ -109,11 +114,12 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
break;
case Value::Managed_Type: {
// ### Should move this over to the Object path, as strings also have an internalClass
- Q_ASSERT(object.isString());
- primitiveLookup.proto = engine->stringPrototype()->d();
+ Q_ASSERT(object.isStringOrSymbol());
+ primitiveLookup.proto = static_cast<const Managed &>(object).internalClass()->prototype;
+ Q_ASSERT(primitiveLookup.proto);
Scope scope(engine);
ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name->equals(engine->id_length())) {
+ if (object.isString() && name->equals(engine->id_length())) {
// special case, as the property is on the object itself
getter = stringLengthGetter;
return stringLengthGetter(this, engine, object);
@@ -125,8 +131,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
primitiveLookup.proto = engine->numberPrototype()->d();
}
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = primitiveLookup.proto->internalClass->id;
+ Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = primitiveLookup.proto->internalClass->protoId;
resolveProtoGetter(name, primitiveLookup.proto);
if (getter == getterProto)
@@ -139,8 +145,8 @@ ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Valu
ReturnedValue Lookup::resolveGlobalGetter(ExecutionEngine *engine)
{
Object *o = engine->globalObject;
- Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- protoLookup.icIdentifier = o->internalClass()->id;
+ Identifier name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ protoLookup.protoId = o->internalClass()->protoId;
resolveProtoGetter(name, o->d());
if (getter == getterProto)
@@ -188,16 +194,16 @@ ReturnedValue Lookup::getterTwoClasses(Lookup *l, ExecutionEngine *engine, const
return result;
}
if (first.getter == getterProto && second.getter == getterProto) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoTwoClasses;
return result;
}
if (first.getter == getterProtoAccessor && second.getter == getterProtoAccessor) {
- l->protoLookupTwoClasses.icIdentifier = first.protoLookup.icIdentifier;
- l->protoLookupTwoClasses.icIdentifier2 = second.protoLookup.icIdentifier;
+ l->protoLookupTwoClasses.protoId = first.protoLookup.protoId;
+ l->protoLookupTwoClasses.protoId2 = second.protoLookup.protoId;
l->protoLookupTwoClasses.data = first.protoLookup.data;
l->protoLookupTwoClasses.data2 = second.protoLookup.data;
l->getter = getterProtoAccessorTwoClasses;
@@ -250,7 +256,7 @@ ReturnedValue Lookup::getterProto(Lookup *l, ExecutionEngine *engine, const Valu
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
}
return getterTwoClasses(l, engine, object);
@@ -307,9 +313,9 @@ ReturnedValue Lookup::getterProtoTwoClasses(Lookup *l, ExecutionEngine *engine,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
return l->protoLookupTwoClasses.data->asReturnedValue();
- if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
return l->protoLookupTwoClasses.data2->asReturnedValue();
return getterFallback(l, engine, object);
}
@@ -340,14 +346,13 @@ ReturnedValue Lookup::getterProtoAccessor(Lookup *l, ExecutionEngine *engine, co
// we can safely cast to a QV4::Object here. If object is actually a string,
// the internal class won't match
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
- if (o && l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (o && l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
return static_cast<const FunctionObject *>(getter)->call(&object, nullptr, 0);
}
- l->getter = getterTwoClasses;
return getterTwoClasses(l, engine, object);
}
@@ -358,9 +363,9 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
Heap::Object *o = static_cast<Heap::Object *>(object.heapObject());
if (o) {
const Value *getter = nullptr;
- if (l->protoLookupTwoClasses.icIdentifier == o->internalClass->id)
+ if (l->protoLookupTwoClasses.protoId == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data;
- else if (l->protoLookupTwoClasses.icIdentifier2 == o->internalClass->id)
+ else if (l->protoLookupTwoClasses.protoId2 == o->internalClass->protoId)
getter = l->protoLookupTwoClasses.data2;
if (getter) {
if (!getter->isFunctionObject()) // ### catch at resolve time
@@ -373,11 +378,29 @@ ReturnedValue Lookup::getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *
return getterFallback(l, engine, object);
}
+ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object)
+{
+ Object *o = object.objectValue();
+ if (o) {
+ Heap::Object *ho = o->d();
+ if (ho->arrayData && ho->arrayData->type == Heap::ArrayData::Simple) {
+ Heap::SimpleArrayData *s = ho->arrayData.cast<Heap::SimpleArrayData>();
+ if (l->indexedLookup.index < s->values.size)
+ if (!s->data(l->indexedLookup.index).isEmpty())
+ return s->data(l->indexedLookup.index).asReturnedValue();
+ }
+ return o->getIndexed(l->indexedLookup.index);
+ }
+ l->getter = getterFallback;
+ return getterFallback(l, engine, object);
+
+}
+
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id)
+ if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
}
l->getter = getterGeneric;
@@ -388,7 +411,7 @@ ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine
{
if (object.type() == l->primitiveLookup.type) {
Heap::Object *o = l->primitiveLookup.proto;
- if (l->primitiveLookup.icIdentifier == o->internalClass->id) {
+ if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -417,7 +440,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id)
+ if (l->protoLookup.protoId == o->internalClass->protoId)
return l->protoLookup.data->asReturnedValue();
l->globalGetter = globalGetterGeneric;
return globalGetterGeneric(l, engine);
@@ -426,7 +449,7 @@ ReturnedValue Lookup::globalGetterProto(Lookup *l, ExecutionEngine *engine)
ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engine)
{
Heap::Object *o = engine->globalObject->d();
- if (l->protoLookup.icIdentifier == o->internalClass->id) {
+ if (l->protoLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->protoLookup.data;
if (!getter->isFunctionObject()) // ### catch at resolve time
return Encode::undefined();
@@ -442,8 +465,9 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
Scope scope(engine);
ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- InternalClass *c = object->internalClass();
- uint idx = c->find(name);
+ Heap::InternalClass *c = object->internalClass();
+ name->makeIdentifier();
+ uint idx = c->find(name->identifier());
if (idx != UINT_MAX) {
if (object->isArrayObject() && idx == Heap::ArrayObject::LengthPropertyIndex) {
setter = arrayLengthSetter;
@@ -460,7 +484,7 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
return setter(this, engine, *object, value);
}
- insertionLookup.icIdentifier = c->id;
+ insertionLookup.protoId = c->protoId;
if (!object->put(name, value)) {
setter = Lookup::setterFallback;
return false;
@@ -471,7 +495,8 @@ bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value
setter = setterFallback;
return true;
}
- idx = object->internalClass()->find(name);
+ name->makeIdentifier();
+ idx = object->internalClass()->find(name->identifier());
if (idx == UINT_MAX) { // ### can this even happen?
setter = setterFallback;
return false;
@@ -574,7 +599,7 @@ bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, c
bool Lookup::setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
{
Object *o = static_cast<Object *>(object.managed());
- if (o && o->internalClass()->id == l->insertionLookup.icIdentifier) {
+ if (o && o->internalClass()->protoId == l->insertionLookup.protoId) {
o->setInternalClass(l->insertionLookup.newClass);
o->d()->setProperty(engine, l->insertionLookup.offset, value);
return true;
diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h
index 5f507733fd..7fb9976135 100644
--- a/src/qml/jsruntime/qv4lookup_p.h
+++ b/src/qml/jsruntime/qv4lookup_p.h
@@ -65,7 +65,6 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
struct Lookup {
- enum { Size = 4 };
union {
ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object);
ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine);
@@ -73,44 +72,57 @@ struct Lookup {
};
union {
struct {
- InternalClass *ic;
+ Heap::Base *h1;
+ Heap::Base *h2;
+ quintptr unused;
+ quintptr unused2;
+ } markDef;
+ struct {
+ Heap::InternalClass *ic;
+ quintptr _unused;
int offset;
} objectLookup;
struct {
+ quintptr protoId;
+ quintptr _unused;
const Value *data;
- int icIdentifier;
} protoLookup;
struct {
- InternalClass *ic;
- InternalClass *ic2;
+ Heap::InternalClass *ic;
+ Heap::InternalClass *ic2;
int offset;
int offset2;
} objectLookupTwoClasses;
struct {
+ quintptr protoId;
+ quintptr protoId2;
const Value *data;
const Value *data2;
- int icIdentifier;
- int icIdentifier2;
} protoLookupTwoClasses;
struct {
// Make sure the next two values are in sync with protoLookup
- const Value *data;
- int icIdentifier;
- unsigned type;
+ quintptr protoId;
Heap::Object *proto;
+ const Value *data;
+ quintptr type;
} primitiveLookup;
struct {
- InternalClass *newClass;
- int icIdentifier;
+ Heap::InternalClass *newClass;
+ quintptr protoId;
int offset;
} insertionLookup;
+ struct {
+ quintptr _unused;
+ quintptr _unused2;
+ uint index;
+ } indexedLookup;
};
uint nameIndex;
ReturnedValue resolveGetter(ExecutionEngine *engine, const Object *object);
ReturnedValue resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object);
ReturnedValue resolveGlobalGetter(ExecutionEngine *engine);
- void resolveProtoGetter(Identifier *name, const Heap::Object *proto);
+ void resolveProtoGetter(Identifier name, const Heap::Object *proto);
static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -126,6 +138,7 @@ struct Lookup {
static ReturnedValue getterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue getterProtoAccessorTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object);
+ static ReturnedValue getterIndexed(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object);
static ReturnedValue primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object);
@@ -144,6 +157,17 @@ struct Lookup {
static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool setterInsert(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
static bool arrayLengthSetter(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value);
+
+ void markObjects(MarkStack *stack) {
+ if (markDef.h1 && !(reinterpret_cast<quintptr>(markDef.h1) & 1))
+ markDef.h1->mark(stack);
+ if (markDef.h2 && !(reinterpret_cast<quintptr>(markDef.h2) & 1))
+ markDef.h2->mark(stack);
+ }
+
+ void clear() {
+ memset(&markDef, 0, sizeof(markDef));
+ }
};
Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value);
diff --git a/src/qml/jsruntime/qv4managed.cpp b/src/qml/jsruntime/qv4managed.cpp
index b50e5f0355..58f5b0051f 100644
--- a/src/qml/jsruntime/qv4managed.cpp
+++ b/src/qml/jsruntime/qv4managed.cpp
@@ -55,22 +55,30 @@ const VTable Managed::static_vtbl =
Managed::IsFunctionObject,
Managed::IsErrorObject,
Managed::IsArrayData,
- 0,
+ Managed::IsStringOrSymbol,
Managed::MyType,
+ { 0, 0, 0, 0 },
"Managed",
nullptr,
nullptr /*markObjects*/,
isEqualTo
};
+DEFINE_MANAGED_VTABLE(InternalClass);
+
QString Managed::className() const
{
const char *s = nullptr;
- switch (Type(d()->vtable()->type)) {
+ switch (Type(vtable()->type)) {
case Type_Invalid:
- case Type_String:
return QString();
+ case Type_String:
+ s = "String";
+ break;
+ case Type_Symbol:
+ s = "Symbol";
+ break;
case Type_Object:
s = "Object";
break;
@@ -80,6 +88,9 @@ QString Managed::className() const
case Type_FunctionObject:
s = "Function";
break;
+ case Type_GeneratorObject:
+ s = "Generator";
+ break;
case Type_BooleanObject:
s = "Boolean";
break;
@@ -89,6 +100,9 @@ QString Managed::className() const
case Type_StringObject:
s = "String";
break;
+ case Type_SymbolObject:
+ s = "Symbol";
+ break;
case Type_DateObject:
s = "Date";
break;
@@ -96,7 +110,7 @@ QString Managed::className() const
s = "RegExp";
break;
case Type_ErrorObject:
- s = ErrorObject::className(static_cast<Heap::ErrorObject *>(d())->errorType);
+ s = "Error";
break;
case Type_ArgumentsObject:
s = "Arguments";
@@ -111,8 +125,23 @@ QString Managed::className() const
case Type_ExecutionContext:
s = "__ExecutionContext";
break;
- case Type_ForeachIteratorObject:
- s = "__ForeachIterator";
+ case Type_MapIteratorObject:
+ s = "Map Iterator";
+ break;
+ case Type_SetIteratorObject:
+ s = "Set Iterator";
+ break;
+ case Type_ArrayIteratorObject:
+ s = "Array Iterator";
+ break;
+ case Type_StringIteratorObject:
+ s = "String Iterator";
+ break;
+ case Type_ForInIterator:
+ s = "__ForIn Iterator";
+ break;
+ case Type_InternalClass:
+ s = "__InternalClass";
break;
case Type_RegExp:
s = "__RegExp";
diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h
index 092c61b81c..f16bf3dc58 100644
--- a/src/qml/jsruntime/qv4managed_p.h
+++ b/src/qml/jsruntime/qv4managed_p.h
@@ -92,14 +92,14 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
QV4::Heap::DataClass *dptr = d_unchecked(); \
dptr->_checkIsInitialized(); \
return dptr; \
- } \
- Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
+ }
#define V4_MANAGED(DataClass, superClass) \
private: \
DataClass() Q_DECL_EQ_DELETE; \
Q_DISABLE_COPY(DataClass) \
- V4_MANAGED_ITSELF(DataClass, superClass)
+ V4_MANAGED_ITSELF(DataClass, superClass) \
+ Q_STATIC_ASSERT(std::is_trivial< QV4::Heap::DataClass >::value);
#define Q_MANAGED_TYPE(type) \
public: \
@@ -140,8 +140,9 @@ inline void qYouForgotTheQ_MANAGED_Macro(T1, T2) {}
classname::IsFunctionObject, \
classname::IsErrorObject, \
classname::IsArrayData, \
- 0, \
+ classname::IsStringOrSymbol, \
classname::MyType, \
+ { 0, 0, 0, 0 }, \
#classname, \
Q_VTABLE_FUNCTION(classname, destroy), \
classname::Data::markObjects, \
@@ -154,8 +155,8 @@ const QV4::VTable classname::static_vtbl = DEFINE_MANAGED_VTABLE_INT(classname,
QT_WARNING_SUPPRESS_GCC_TAUTOLOGICAL_COMPARE_OFF
#define V4_INTERNALCLASS(c) \
- static QV4::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
- { return e->internalClasses[QV4::EngineBase::Class_##c]; }
+ static Heap::InternalClass *defaultInternalClass(QV4::EngineBase *e) \
+ { return e->internalClasses(QV4::EngineBase::Class_##c); }
struct Q_QML_PRIVATE_EXPORT Managed : Value
{
@@ -163,6 +164,7 @@ struct Q_QML_PRIVATE_EXPORT Managed : Value
enum {
IsExecutionContext = false,
IsString = false,
+ IsStringOrSymbol = false,
IsObject = false,
IsFunctionObject = false,
IsErrorObject = false,
@@ -180,11 +182,14 @@ public:
Type_Invalid,
Type_String,
Type_Object,
+ Type_Symbol,
Type_ArrayObject,
Type_FunctionObject,
+ Type_GeneratorObject,
Type_BooleanObject,
Type_NumberObject,
Type_StringObject,
+ Type_SymbolObject,
Type_DateObject,
Type_RegExpObject,
Type_ErrorObject,
@@ -193,25 +198,33 @@ public:
Type_MathObject,
Type_ExecutionContext,
- Type_ForeachIteratorObject,
+ Type_InternalClass,
+ Type_SetIteratorObject,
+ Type_MapIteratorObject,
+ Type_ArrayIteratorObject,
+ Type_StringIteratorObject,
+ Type_ForInIterator,
Type_RegExp,
Type_QmlSequence
};
Q_MANAGED_TYPE(Invalid)
- InternalClass *internalClass() const { return d()->internalClass; }
+ Heap::InternalClass *internalClass() const { return d()->internalClass; }
+ const VTable *vtable() const { return d()->internalClass->vtable; }
inline ExecutionEngine *engine() const { return internalClass()->engine; }
- bool isListType() const { return d()->vtable()->type == Type_QmlSequence; }
+ bool isListType() const { return d()->internalClass->vtable->type == Type_QmlSequence; }
+ bool isArrayLike() const { return isArrayObject() || isListType(); }
- bool isArrayObject() const { return d()->vtable()->type == Type_ArrayObject; }
- bool isStringObject() const { return d()->vtable()->type == Type_StringObject; }
+ bool isArrayObject() const { return d()->internalClass->vtable->type == Type_ArrayObject; }
+ bool isStringObject() const { return d()->internalClass->vtable->type == Type_StringObject; }
+ bool isSymbolObject() const { return d()->internalClass->vtable->type == Type_SymbolObject; }
QString className() const;
bool isEqualTo(const Managed *other) const
- { return d()->vtable()->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
+ { return d()->internalClass->vtable->isEqualTo(const_cast<Managed *>(this), const_cast<Managed *>(other)); }
static bool isEqualTo(Managed *m, Managed *other);
@@ -254,6 +267,29 @@ inline const Object *Value::as() const {
return objectValue();
}
+
+struct InternalClass : Managed
+{
+ V4_MANAGED_ITSELF(InternalClass, Managed)
+ Q_MANAGED_TYPE(InternalClass)
+ V4_INTERNALCLASS(Empty)
+ V4_NEEDS_DESTROY
+
+ Q_REQUIRED_RESULT Heap::InternalClass *changeVTable(const VTable *vt) {
+ return d()->changeVTable(vt);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *changePrototype(Heap::Object *proto) {
+ return d()->changePrototype(proto);
+ }
+ Q_REQUIRED_RESULT Heap::InternalClass *addMember(Identifier identifier, PropertyAttributes data, uint *index = 0) {
+ return d()->addMember(identifier, data, index);
+ }
+
+ void operator =(Heap::InternalClass *ic) {
+ Value::operator=(ic);
+ }
+};
+
}
diff --git a/src/qml/jsruntime/qv4mapiterator.cpp b/src/qml/jsruntime/qv4mapiterator.cpp
new file mode 100644
index 0000000000..74b0dda791
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator.cpp
@@ -0,0 +1,109 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4mapiterator_p.h>
+#include <private/qv4mapobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(MapIteratorObject);
+
+void MapIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Map Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue MapIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const MapIteratorObject *thisObject = that->as<MapIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Map Iterator instance"));
+
+ Scoped<MapObject> s(scope, thisObject->d()->iteratedMap);
+ quint32 index = thisObject->d()->mapNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Scoped<ArrayObject> keys(scope, s->d()->mapKeys);
+ Scoped<ArrayObject> values(scope, s->d()->mapValues);
+
+ while (index < keys->getLength()) {
+ ScopedValue sk(scope, keys->getIndexed(index));
+ ScopedValue sv(scope, values->getIndexed(index));
+ index += 1;
+ thisObject->d()->mapNextIndex = index;
+
+ ScopedValue result(scope);
+
+ if (itemKind == KeyIteratorKind) {
+ result = sk;
+ } else if (itemKind == ValueIteratorKind) {
+ result = sv;
+ } else {
+ Q_ASSERT(itemKind == KeyValueIteratorKind);
+
+ result = scope.engine->newArrayObject();
+
+ Scoped<ArrayObject> resultArray(scope, result);
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, sk);
+ resultArray->arrayPut(1, sv);
+ resultArray->setArrayLengthUnchecked(2);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, result, false);
+ }
+
+ thisObject->d()->iteratedMap.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
+
diff --git a/src/qml/jsruntime/qv4mapiterator_p.h b/src/qml/jsruntime/qv4mapiterator_p.h
new file mode 100644
index 0000000000..836ba14663
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapiterator_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MAPITERATOR_P_H
+#define QV4MAPITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define MapIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedMap) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, mapNextIndex)
+
+DECLARE_HEAP_OBJECT(MapIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(MapIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedMap.set(engine, obj);
+ this->mapNextIndex = 0;
+ }
+};
+
+}
+
+struct MapIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapIteratorObject : Object
+{
+ V4_OBJECT2(MapIteratorObject, Object)
+ Q_MANAGED_TYPE(MapIteratorObject)
+ V4_PROTOTYPE(mapIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPITERATOR_P_H
+
+
diff --git a/src/qml/jsruntime/qv4mapobject.cpp b/src/qml/jsruntime/qv4mapobject.cpp
new file mode 100644
index 0000000000..e3ca75b951
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject.cpp
@@ -0,0 +1,354 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4setobject_p.h" // ### temporary
+#include "qv4mapobject_p.h"
+#include "qv4mapiterator_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(MapCtor);
+DEFINE_OBJECT_VTABLE(MapObject);
+
+void Heap::MapCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Map"));
+}
+
+ReturnedValue MapCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+{
+ Scope scope(f);
+ Scoped<MapObject> a(scope, scope.engine->memoryManager->allocate<MapObject>());
+ a->d()->mapKeys.set(scope.engine, scope.engine->newArrayObject());
+ a->d()->mapValues.set(scope.engine, scope.engine->newArrayObject());
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+
+ // ### beware, hack alert!
+ // Object iteration seems broken right now. if we allow any object to
+ // iterate, it endlessly loops in the Map/prototype tests in test262...
+ // disable these for now until Object iteration is fixed, just so we can
+ // test this.
+ Scoped<MapObject> mapObjectCheck(scope, argv[0]);
+ Scoped<SetObject> setObjectCheck(scope, argv[0]);
+
+ if (!iterable->isUndefined() && !iterable->isNull() && (mapObjectCheck || setObjectCheck)) {
+
+
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("set")))));
+ if (!adder) {
+ return scope.engine->throwTypeError();
+ }
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+
+ CHECK_EXCEPTION();
+ if (!iter) {
+ return a.asReturnedValue();
+ }
+
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ CHECK_EXCEPTION();
+ if (done->toBoolean()) {
+ return a.asReturnedValue();
+ }
+
+ adder->call(a, nextValue, 1);
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ }
+ }
+ return a.asReturnedValue();
+}
+
+ReturnedValue MapCtor::call(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("Map requires new"));
+}
+
+void MapPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("get"), method_get, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 0);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+ defineDefaultProperty(QStringLiteral("values"), method_values, 0);
+
+ // Per the spec, the value for entries/@@iterator is the same
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("entries")));
+ ScopedFunctionObject entriesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, MapPrototype::method_entries, 0));
+ defineDefaultProperty(QStringLiteral("entries"), entriesFn);
+ defineDefaultProperty(engine->symbol_iterator(), entriesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Map")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+ReturnedValue MapPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ that->d()->mapKeys.set(scope.engine, scope.engine->newArrayObject());
+ that->d()->mapValues.set(scope.engine, scope.engine->newArrayObject());
+ return Encode::undefined();
+}
+
+// delete value
+ReturnedValue MapPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ qint64 len = keys->getLength();
+
+ bool found = false;
+ int idx = 0;
+ ScopedValue sk(scope);
+
+ for (; idx < len; ++idx) {
+ sk = keys->getIndexed(idx);
+ if (sk->sameValueZero(argv[0])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == true) {
+ Scoped<ArrayObject> values(scope, that->d()->mapValues);
+ Scoped<ArrayObject> newKeys(scope, scope.engine->newArrayObject());
+ Scoped<ArrayObject> newValues(scope, scope.engine->newArrayObject());
+ for (int j = 0, newIdx = 0; j < len; ++j, newIdx++) {
+ if (j == idx) {
+ newIdx--; // skip the entry
+ continue;
+ }
+ newKeys->putIndexed(newIdx, ScopedValue(scope, keys->getIndexed(j)));
+ newValues->putIndexed(newIdx, ScopedValue(scope, values->getIndexed(j)));
+ }
+
+ that->d()->mapKeys.set(scope.engine, newKeys->d());
+ that->d()->mapValues.set(scope.engine, newValues->d());
+ return Encode(true);
+ } else {
+ return Encode(false);
+ }
+}
+
+ReturnedValue MapPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Primitive::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ Scoped<ArrayObject> values(scope, that->d()->mapKeys);
+ qint64 len = keys->getLength();
+
+ Value *arguments = scope.alloc(3);
+ ScopedValue sk(scope);
+ ScopedValue sv(scope);
+ for (int i = 0; i < len; ++i) {
+ sk = keys->getIndexed(i);
+ sv = values->getIndexed(i);
+
+ arguments[0] = sv;
+ arguments[1] = sk;
+ arguments[2] = that;
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_get(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ ScopedValue sk(scope);
+ qint64 len = keys->getLength();
+
+ for (int i = 0; i < len; ++i) {
+ sk = keys->getIndexed(i);
+ if (sk->sameValueZero(argv[0])) {
+ Scoped<ArrayObject> values(scope, that->d()->mapValues);
+ return values->getIndexed(i);
+ }
+ }
+
+ return Encode::undefined();
+}
+
+ReturnedValue MapPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ ScopedValue sk(scope);
+ qint64 len = keys->getLength();
+
+ for (int i = 0; i < len; ++i) {
+ sk = keys->getIndexed(i);
+ if (sk->sameValueZero(argv[0]))
+ return Encode(true);
+ }
+
+ return Encode(false);
+}
+
+ReturnedValue MapPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ Scoped<ArrayObject> values(scope, that->d()->mapValues);
+ ScopedValue sk(scope, argv[1]);
+ qint64 len = keys->getLength();
+
+ for (int i = 0; i < len; ++i) {
+ sk = keys->getIndexed(i);
+ if (sk->sameValueZero(argv[0])) {
+ values->putIndexed(len, argv[1]);
+ return that.asReturnedValue();
+ }
+ }
+
+ sk = argv[0];
+ if (sk->isDouble()) {
+ if (sk->doubleValue() == 0 && std::signbit(sk->doubleValue()))
+ sk = Primitive::fromDouble(+0);
+ }
+
+ keys->putIndexed(len, sk);
+ values->putIndexed(len, argv[1]);
+ return that.asReturnedValue();
+}
+
+ReturnedValue MapPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> keys(scope, that->d()->mapKeys);
+ qint64 len = keys->getLength();
+ return Encode((uint)len);
+}
+
+ReturnedValue MapPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<MapObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<MapIteratorObject> ao(scope, scope.engine->newMapIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+
diff --git a/src/qml/jsruntime/qv4mapobject_p.h b/src/qml/jsruntime/qv4mapobject_p.h
new file mode 100644
index 0000000000..9c64e25c3d
--- /dev/null
+++ b/src/qml/jsruntime/qv4mapobject_p.h
@@ -0,0 +1,117 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4MAPOBJECT_P_H
+#define QV4MAPOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct MapCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+#define MapObjectMembers(class, Member) \
+ Member(class, Pointer, ArrayObject *, mapKeys) \
+ Member(class, Pointer, ArrayObject *, mapValues)
+
+DECLARE_HEAP_OBJECT(MapObject, Object) {
+ DECLARE_MARKOBJECTS(MapObject);
+ void init() { Object::init(); }
+};
+
+}
+
+struct MapCtor: FunctionObject
+{
+ V4_OBJECT2(MapCtor, FunctionObject)
+
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct MapObject : Object
+{
+ V4_OBJECT2(MapObject, Object)
+ V4_PROTOTYPE(mapPrototype)
+};
+
+struct MapPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4MAPOBJECT_P_H
+
diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp
index 0c18d908de..652f6c603e 100644
--- a/src/qml/jsruntime/qv4mathobject.cpp
+++ b/src/qml/jsruntime/qv4mathobject.cpp
@@ -39,6 +39,7 @@
#include "qv4mathobject_p.h"
#include "qv4objectproto_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/qdatetime.h>
#include <QtCore/qmath.h>
@@ -70,14 +71,27 @@ void Heap::MathObject::init()
m->defineDefaultProperty(QStringLiteral("abs"), QV4::MathObject::method_abs, 1);
m->defineDefaultProperty(QStringLiteral("acos"), QV4::MathObject::method_acos, 1);
- m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 0);
+ m->defineDefaultProperty(QStringLiteral("acosh"), QV4::MathObject::method_acosh, 1);
+ m->defineDefaultProperty(QStringLiteral("asin"), QV4::MathObject::method_asin, 1);
+ m->defineDefaultProperty(QStringLiteral("asinh"), QV4::MathObject::method_asinh, 1);
m->defineDefaultProperty(QStringLiteral("atan"), QV4::MathObject::method_atan, 1);
+ m->defineDefaultProperty(QStringLiteral("atanh"), QV4::MathObject::method_atanh, 1);
m->defineDefaultProperty(QStringLiteral("atan2"), QV4::MathObject::method_atan2, 2);
+ m->defineDefaultProperty(QStringLiteral("cbrt"), QV4::MathObject::method_cbrt, 1);
m->defineDefaultProperty(QStringLiteral("ceil"), QV4::MathObject::method_ceil, 1);
+ m->defineDefaultProperty(QStringLiteral("clz32"), QV4::MathObject::method_clz32, 1);
m->defineDefaultProperty(QStringLiteral("cos"), QV4::MathObject::method_cos, 1);
+ m->defineDefaultProperty(QStringLiteral("cosh"), QV4::MathObject::method_cosh, 1);
m->defineDefaultProperty(QStringLiteral("exp"), QV4::MathObject::method_exp, 1);
+ m->defineDefaultProperty(QStringLiteral("expm1"), QV4::MathObject::method_expm1, 1);
m->defineDefaultProperty(QStringLiteral("floor"), QV4::MathObject::method_floor, 1);
+ m->defineDefaultProperty(QStringLiteral("fround"), QV4::MathObject::method_fround, 1);
+ m->defineDefaultProperty(QStringLiteral("hypot"), QV4::MathObject::method_hypot, 2);
+ m->defineDefaultProperty(QStringLiteral("imul"), QV4::MathObject::method_imul, 2);
m->defineDefaultProperty(QStringLiteral("log"), QV4::MathObject::method_log, 1);
+ m->defineDefaultProperty(QStringLiteral("log10"), QV4::MathObject::method_log10, 1);
+ m->defineDefaultProperty(QStringLiteral("log1p"), QV4::MathObject::method_log1p, 1);
+ m->defineDefaultProperty(QStringLiteral("log2"), QV4::MathObject::method_log2, 1);
m->defineDefaultProperty(QStringLiteral("max"), QV4::MathObject::method_max, 2);
m->defineDefaultProperty(QStringLiteral("min"), QV4::MathObject::method_min, 2);
m->defineDefaultProperty(QStringLiteral("pow"), QV4::MathObject::method_pow, 2);
@@ -85,8 +99,14 @@ void Heap::MathObject::init()
m->defineDefaultProperty(QStringLiteral("round"), QV4::MathObject::method_round, 1);
m->defineDefaultProperty(QStringLiteral("sign"), QV4::MathObject::method_sign, 1);
m->defineDefaultProperty(QStringLiteral("sin"), QV4::MathObject::method_sin, 1);
+ m->defineDefaultProperty(QStringLiteral("sinh"), QV4::MathObject::method_sinh, 1);
m->defineDefaultProperty(QStringLiteral("sqrt"), QV4::MathObject::method_sqrt, 1);
m->defineDefaultProperty(QStringLiteral("tan"), QV4::MathObject::method_tan, 1);
+ m->defineDefaultProperty(QStringLiteral("tanh"), QV4::MathObject::method_tanh, 1);
+ m->defineDefaultProperty(QStringLiteral("trunc"), QV4::MathObject::method_trunc, 1);
+
+ ScopedString name(scope, scope.engine->newString(QStringLiteral("Math")));
+ m->defineReadonlyConfigurableProperty(scope.engine->symbol_toStringTag(), name);
}
static Q_ALWAYS_INLINE double copySign(double x, double y)
@@ -120,6 +140,19 @@ ReturnedValue MathObject::method_acos(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::acos(v)));
}
+ReturnedValue MathObject::method_acosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v < 1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(v + 1) * std::sqrt(v - 1))));
+#else
+ RETURN_RESULT(Encode(std::acosh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : 2;
@@ -129,6 +162,19 @@ ReturnedValue MathObject::method_asin(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::asin(v)));
}
+ReturnedValue MathObject::method_asinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : 2;
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::log(v +std::sqrt(1 + v * v))));
+#else
+ RETURN_RESULT(Encode(std::asinh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -138,6 +184,25 @@ ReturnedValue MathObject::method_atan(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::atan(v)));
}
+ReturnedValue MathObject::method_atanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (-1 < v && v < 1)
+ RETURN_RESULT(Encode(0.5 * (std::log(v + 1) - std::log(v - 1))));
+
+ if (v > 1 || v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+
+ RETURN_RESULT(Encode(copySign(qt_inf(), v)));
+#else
+ RETURN_RESULT(Encode(std::atanh(v)));
+#endif
+}
+
ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v1 = argc ? argv[0].toNumber() : qt_qnan();
@@ -156,6 +221,16 @@ ReturnedValue MathObject::method_atan2(const FunctionObject *, const Value *, co
RETURN_RESULT(Encode(std::atan2(v1, v2)));
}
+ReturnedValue MathObject::method_cbrt(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(copySign(std::exp(std::log(std::abs(v)) / 3), v)));
+#else
+ RETURN_RESULT(Encode(std::cbrt(v))); // cube root
+#endif
+}
+
ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -165,12 +240,24 @@ ReturnedValue MathObject::method_ceil(const FunctionObject *, const Value *, con
RETURN_RESULT(Encode(std::ceil(v)));
}
+ReturnedValue MathObject::method_clz32(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 v = argc ? argv[0].toUInt32() : 0;
+ RETURN_RESULT(Encode(qint32(qCountLeadingZeroBits(v))));
+}
+
ReturnedValue MathObject::method_cos(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
RETURN_RESULT(Encode(std::cos(v)));
}
+ReturnedValue MathObject::method_cosh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ RETURN_RESULT(Encode(std::cosh(v)));
+}
+
ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -184,6 +271,25 @@ ReturnedValue MathObject::method_exp(const FunctionObject *, const Value *, cons
}
}
+ReturnedValue MathObject::method_expm1(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qIsNull(v)) {
+ RETURN_RESULT(Encode(v));
+ } else if (qt_is_inf(v)) {
+ if (copySign(1.0, v) == -1.0)
+ RETURN_RESULT(Encode(-1.0));
+ else
+ RETURN_RESULT(Encode(qt_inf()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ RETURN_RESULT(Encode(std::exp(v) - 1));
+#else
+ RETURN_RESULT(Encode(std::expm1(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -192,6 +298,53 @@ ReturnedValue MathObject::method_floor(const FunctionObject *, const Value *, co
RETURN_RESULT(result);
}
+ReturnedValue MathObject::method_fround(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ else // convert to 32-bit float using roundTiesToEven, then convert back to 64-bit double
+ RETURN_RESULT(Encode(double(float(v))));
+}
+
+ReturnedValue MathObject::method_hypot(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ // ES6 Math.hypot(v1, ..., vn) -> sqrt(sum(vi**2)) but "should take care to
+ // avoid the loss of precision from overflows and underflows" (as std::hypot does).
+ double v = argc ? argv[0].toNumber() : 0;
+ // Spec mandates +0 on no args; and says nothing about what to do if toNumber() signals ...
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ bool big = qt_is_inf(v), bad = std::isnan(v);
+ v *= v;
+ for (int i = 1; !big && i < argc; i++) {
+ double u = argv[i].toNumber();
+ if (qt_is_inf(u))
+ big = true;
+ if (std::isnan(u))
+ bad = true;
+ v += u * u;
+ }
+ if (big)
+ RETURN_RESULT(Encode(qt_inf()));
+ if (bad)
+ RETURN_RESULT(Encode(qt_qnan()));
+ // Should actually check for {und,ov}erflow, but too fiddly !
+ RETURN_RESULT(Primitive::fromDouble(sqrt(v)));
+#else
+ for (int i = 1; i < argc; i++)
+ v = std::hypot(v, argv[i].toNumber());
+#endif
+ RETURN_RESULT(Primitive::fromDouble(v));
+}
+
+ReturnedValue MathObject::method_imul(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ quint32 a = argc ? argv[0].toUInt32() : 0;
+ quint32 b = argc > 0 ? argv[1].toUInt32() : 0;
+ qint32 product = a * b;
+ RETURN_RESULT(Encode(product));
+}
+
ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
@@ -201,6 +354,43 @@ ReturnedValue MathObject::method_log(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::log(v)));
}
+ReturnedValue MathObject::method_log10(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(std::log10(v)));
+}
+
+ReturnedValue MathObject::method_log1p(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+#if !defined(__ANDROID__)
+ using std::log1p;
+#endif
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < -1)
+ RETURN_RESULT(Encode(qt_qnan()));
+ else
+ RETURN_RESULT(Encode(log1p(v)));
+}
+
+ReturnedValue MathObject::method_log2(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v < 0) {
+ RETURN_RESULT(Encode(qt_qnan()));
+ } else {
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ // Android ndk r10e doesn't have std::log2, so fall back.
+ const double ln2 = std::log(2.0);
+ RETURN_RESULT(Encode(std::log(v) / ln2));
+#else
+ RETURN_RESULT(Encode(std::log2(v)));
+#endif
+ }
+}
+
ReturnedValue MathObject::method_max(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double mx = -qt_inf();
@@ -283,8 +473,11 @@ ReturnedValue MathObject::method_random(const FunctionObject *, const Value *, c
ReturnedValue MathObject::method_round(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- v = copySign(std::floor(v + 0.5), v);
- RETURN_RESULT(Encode(v));
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+
+ v = copySign(std::floor(v + 0.5), v);
+ RETURN_RESULT(Encode(v));
}
ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -303,7 +496,19 @@ ReturnedValue MathObject::method_sign(const FunctionObject *, const Value *, con
ReturnedValue MathObject::method_sin(const FunctionObject *, const Value *, const Value *argv, int argc)
{
double v = argc ? argv[0].toNumber() : qt_qnan();
- RETURN_RESULT(Encode(std::sin(v)));
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sin(v)));
+}
+
+ReturnedValue MathObject::method_sinh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::sinh(v)));
}
ReturnedValue MathObject::method_sqrt(const FunctionObject *, const Value *, const Value *argv, int argc)
@@ -321,3 +526,25 @@ ReturnedValue MathObject::method_tan(const FunctionObject *, const Value *, cons
RETURN_RESULT(Encode(std::tan(v)));
}
+ReturnedValue MathObject::method_tanh(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+ if (v == 0.0)
+ RETURN_RESULT(Encode(v));
+ else
+ RETURN_RESULT(Encode(std::tanh(v)));
+}
+
+ReturnedValue MathObject::method_trunc(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ double v = argc ? argv[0].toNumber() : qt_qnan();
+#ifdef Q_OS_ANDROID // incomplete std :-(
+ if (std::isnan(v) || qt_is_inf(v) || qIsNull(v))
+ RETURN_RESULT(Encode(v));
+ // Nearest integer not greater in magnitude:
+ quint64 whole = std::abs(v);
+ RETURN_RESULT(Encode(copySign(whole, v)));
+#else
+ RETURN_RESULT(Encode(std::trunc(v)));
+#endif
+}
diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h
index 0bf5da9404..2658e25438 100644
--- a/src/qml/jsruntime/qv4mathobject_p.h
+++ b/src/qml/jsruntime/qv4mathobject_p.h
@@ -71,14 +71,27 @@ struct MathObject: Object
static ReturnedValue method_abs(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_acos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_acosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_asin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_asinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_atanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_atan2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cbrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_ceil(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clz32(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_cos(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_cosh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_exp(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_expm1(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_floor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fround(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_hypot(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_imul(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_log(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log10(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log1p(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_log2(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_max(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_min(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_pow(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -86,8 +99,11 @@ struct MathObject: Object
static ReturnedValue method_round(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sin(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_sinh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_sqrt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_tan(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_tanh(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_trunc(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4memberdata_p.h b/src/qml/jsruntime/qv4memberdata_p.h
index ac9671254d..186083b83a 100644
--- a/src/qml/jsruntime/qv4memberdata_p.h
+++ b/src/qml/jsruntime/qv4memberdata_p.h
@@ -74,18 +74,6 @@ struct MemberData : Managed
V4_MANAGED(MemberData, Managed)
V4_INTERNALCLASS(MemberData)
- struct Index {
- Heap::Base *base;
- Value *slot;
-
- void set(EngineBase *e, Value newVal) {
- WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
- }
- const Value *operator->() const { return slot; }
- const Value &operator*() const { return *slot; }
- bool isNull() const { return !slot; }
- };
-
const Value &operator[] (uint idx) const { return d()->values[idx]; }
const Value *data() const { return d()->values.data(); }
void set(EngineBase *e, uint index, Value v) { d()->values.set(e, index, v); }
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 0c6cde84ad..79a63d1ee6 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -50,6 +50,7 @@
#include "qv4string_p.h"
#include "qv4identifiertable_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <stdint.h>
@@ -57,9 +58,9 @@ using namespace QV4;
DEFINE_OBJECT_VTABLE(Object);
-void Object::setInternalClass(InternalClass *ic)
+void Object::setInternalClass(Heap::InternalClass *ic)
{
- d()->internalClass = ic;
+ d()->internalClass.set(engine(), ic);
if (ic->isUsedAsProto)
ic->updateProtoUsage(d());
Q_ASSERT(ic && ic->vtable);
@@ -89,7 +90,7 @@ void Object::setProperty(uint index, const Property *p)
void Heap::Object::setUsedAsProto()
{
- internalClass = internalClass->asProtoClass();
+ internalClass.set(internalClass->engine, internalClass->asProtoClass());
}
bool Object::setPrototype(Object *proto)
@@ -121,7 +122,7 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property
bool Object::putValue(uint memberIndex, const Value &value)
{
- QV4::InternalClass *ic = internalClass();
+ Heap::InternalClass *ic = internalClass();
if (ic->engine->hasException)
return false;
@@ -148,37 +149,34 @@ bool Object::putValue(uint memberIndex, const Value &value)
return true;
}
-void Object::defineDefaultProperty(const QString &name, const Value &value)
+void Object::defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- defineDefaultProperty(s, value);
+ defineDefaultProperty(s, value, attributes);
}
-void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(const QString &name, jsCallFunction code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
ScopedString s(scope, e->newIdentifier(name));
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, s, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(s, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, s, code, argumentCount));
+ defineDefaultProperty(s, function, attributes);
}
-void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount)
+void Object::defineDefaultProperty(StringOrSymbol *nameOrSymbol, jsCallFunction code,
+ int argumentCount, PropertyAttributes attributes)
{
ExecutionEngine *e = engine();
Scope scope(e);
- ExecutionContext *global = e->rootContext();
- ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(global, name, code));
- function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount));
- defineDefaultProperty(name, function);
+ ScopedFunctionObject function(scope, FunctionObject::createBuiltinFunction(e, nameOrSymbol, code, argumentCount));
+ defineDefaultProperty(nameOrSymbol, function, attributes);
}
-void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(const QString &name, jsCallFunction getter, jsCallFunction setter)
{
ExecutionEngine *e = engine();
Scope scope(e);
@@ -186,16 +184,27 @@ void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)
defineAccessorProperty(s, getter, setter);
}
-void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int))
+void Object::defineAccessorProperty(StringOrSymbol *name, jsCallFunction getter, jsCallFunction setter)
{
ExecutionEngine *v4 = engine();
QV4::Scope scope(v4);
ScopedProperty p(scope);
- ExecutionContext *global = v4->rootContext();
- p->setGetter(ScopedFunctionObject(scope, (getter ? FunctionObject::createBuiltinFunction(global, name, getter) : nullptr)));
- p->setSetter(ScopedFunctionObject(scope, (setter ? FunctionObject::createBuiltinFunction(global, name, setter) : nullptr)));
- insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotConfigurable|QV4::Attr_NotEnumerable);
+ QString n = name->toQString();
+ if (n.at(0) == QLatin1Char('@'))
+ n = QChar::fromLatin1('[') + n.midRef(1) + QChar::fromLatin1(']');
+ if (getter) {
+ ScopedString getName(scope, v4->newString(QString::fromLatin1("get ") + n));
+ p->setGetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, getName, getter, 0)));
+ } else {
+ p->setGetter(nullptr);
+ }
+ if (setter) {
+ ScopedString setName(scope, v4->newString(QString::fromLatin1("set ") + n));
+ p->setSetter(ScopedFunctionObject(scope, FunctionObject::createBuiltinFunction(v4, setName, setter, 0)));
+ } else {
+ p->setSetter(nullptr);
+ }
+ insertMember(name, p, QV4::Attr_Accessor|QV4::Attr_NotEnumerable);
}
@@ -221,13 +230,23 @@ void Object::defineReadonlyConfigurableProperty(const QString &name, const Value
defineReadonlyConfigurableProperty(s, value);
}
-void Object::defineReadonlyConfigurableProperty(String *name, const Value &value)
+void Object::defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value)
{
insertMember(name, value, Attr_ReadOnly_ButConfigurable);
}
+void Object::addSymbolSpecies()
+{
+ Scope scope(engine());
+ ScopedProperty p(scope);
+ p->setGetter(scope.engine->getSymbolSpecies());
+ p->setSetter(nullptr);
+ insertMember(scope.engine->symbol_species(), p, QV4::Attr_Accessor|QV4::Attr_NotWritable|QV4::Attr_NotEnumerable);
+}
+
void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
{
+ Base::markObjects(b, stack);
Object *o = static_cast<Object *>(b);
if (o->memberData)
o->memberData->mark(stack);
@@ -242,10 +261,11 @@ void Heap::Object::markObjects(Heap::Base *b, MarkStack *stack)
}
}
-void Object::insertMember(String *s, const Property *p, PropertyAttributes attributes)
+void Object::insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes)
{
uint idx;
- InternalClass::addMember(this, s, attributes, &idx);
+ s->makeIdentifier();
+ Heap::InternalClass::addMember(this, s->identifier(), attributes, &idx);
if (attributes.isAccessor()) {
setProperty(idx + GetterOffset, p->value);
@@ -256,14 +276,14 @@ void Object::insertMember(String *s, const Property *p, PropertyAttributes attri
}
// Section 8.12.1
-void Object::getOwnProperty(String *name, PropertyAttributes *attrs, Property *p)
+void Object::getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p)
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return getOwnProperty(idx, attrs, p);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
uint member = internalClass()->find(id);
if (member < UINT_MAX) {
@@ -300,12 +320,12 @@ void Object::getOwnProperty(uint index, PropertyAttributes *attrs, Property *p)
}
// Section 8.12.2
-MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *attrs)
+PropertyIndex Object::getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs)
{
Q_ASSERT(name->asArrayIndex() == UINT_MAX);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
Heap::Object *o = d();
while (o) {
@@ -321,7 +341,7 @@ MemberData::Index Object::getValueOrSetter(String *name, PropertyAttributes *att
return { nullptr, nullptr };
}
-ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
+PropertyIndex Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
{
Heap::Object *o = d();
while (o) {
@@ -329,7 +349,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
uint idx = o->arrayData->mappedIndex(index);
if (idx != UINT_MAX) {
*attrs = o->arrayData->attributes(index);
- return { o->arrayData , attrs->isAccessor() ? idx + SetterOffset : idx };
+ return { o->arrayData , o->arrayData->values.values + (attrs->isAccessor() ? idx + SetterOffset : idx) };
}
}
if (o->vtable()->type == Type_StringObject) {
@@ -346,7 +366,7 @@ ArrayData::Index Object::getValueOrSetter(uint index, PropertyAttributes *attrs)
return { nullptr, 0 };
}
-bool Object::hasProperty(String *name) const
+bool Object::hasProperty(StringOrSymbol *name) const
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
@@ -378,14 +398,14 @@ bool Object::hasProperty(uint index) const
return false;
}
-bool Object::hasOwnProperty(String *name) const
+bool Object::hasOwnProperty(StringOrSymbol *name) const
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return hasOwnProperty(idx);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
if (internalClass()->find(id) < UINT_MAX)
return true;
@@ -418,7 +438,7 @@ ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *
return f->engine()->throwTypeError();
}
-ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue Object::get(const Managed *m, StringOrSymbol *name, bool *hasProperty)
{
return static_cast<const Object *>(m)->internalGet(name, hasProperty);
}
@@ -428,7 +448,7 @@ ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty
return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty);
}
-bool Object::put(Managed *m, String *name, const Value &value)
+bool Object::put(Managed *m, StringOrSymbol *name, const Value &value)
{
return static_cast<Object *>(m)->internalPut(name, value);
}
@@ -438,14 +458,14 @@ bool Object::putIndexed(Managed *m, uint index, const Value &value)
return static_cast<Object *>(m)->internalPutIndexed(index, value);
}
-PropertyAttributes Object::query(const Managed *m, String *name)
+PropertyAttributes Object::query(const Managed *m, StringOrSymbol *name)
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return queryIndexed(m, idx);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
const Object *o = static_cast<const Object *>(m);
idx = o->internalClass()->find(id);
@@ -468,7 +488,7 @@ PropertyAttributes Object::queryIndexed(const Managed *m, uint index)
return Attr_Invalid;
}
-bool Object::deleteProperty(Managed *m, String *name)
+bool Object::deleteProperty(Managed *m, StringOrSymbol *name)
{
return static_cast<Object *>(m)->internalDeleteProperty(name);
}
@@ -525,9 +545,10 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
}
while (it->memberIndex < o->internalClass()->size) {
- Identifier *n = o->internalClass()->nameMap.at(it->memberIndex);
- if (!n) {
+ Identifier n = o->internalClass()->nameMap.at(it->memberIndex);
+ if (!n.isValid() || !n.asHeapObject()->internalClass->vtable->isString) {
// accessor properties have a dummy entry with n == 0
+ // symbol entries are supposed to be skipped
++it->memberIndex;
continue;
}
@@ -536,7 +557,7 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
PropertyAttributes a = o->internalClass()->propertyData[it->memberIndex];
++it->memberIndex;
if (!(it->flags & ObjectIterator::EnumerableOnly) || a.isEnumerable()) {
- name->setM(o->engine()->identifierTable->stringFromIdentifier(n));
+ name->setM(n.asHeapObject());
*attrs = a;
pd->value = *o->propertyData(idx);
if (a.isAccessor())
@@ -549,14 +570,14 @@ void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *
}
// Section 8.12.3
-ReturnedValue Object::internalGet(String *name, bool *hasProperty) const
+ReturnedValue Object::internalGet(StringOrSymbol *name, bool *hasProperty) const
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
return getIndexed(idx, hasProperty);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
Heap::Object *o = d();
while (o) {
@@ -612,7 +633,7 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const
// Section 8.12.5
-bool Object::internalPut(String *name, const Value &value)
+bool Object::internalPut(StringOrSymbol *name, const Value &value)
{
ExecutionEngine *engine = this->engine();
if (engine->hasException)
@@ -623,9 +644,9 @@ bool Object::internalPut(String *name, const Value &value)
return putIndexed(idx, value);
name->makeIdentifier();
- Identifier *id = name->identifier();
+ Identifier id = name->identifier();
- MemberData::Index memberIndex{nullptr, nullptr};
+ PropertyIndex memberIndex{nullptr, nullptr};
uint member = internalClass()->find(id);
PropertyAttributes attrs;
if (member < UINT_MAX) {
@@ -641,7 +662,7 @@ bool Object::internalPut(String *name, const Value &value)
return false;
} else if (!attrs.isWritable())
return false;
- else if (isArrayObject() && name->equals(engine->id_length())) {
+ else if (isArrayObject() && id == engine->id_length()->identifier()) {
bool ok;
uint l = value.asArrayLength(&ok);
if (!ok) {
@@ -701,7 +722,7 @@ bool Object::internalPutIndexed(uint index, const Value &value)
PropertyAttributes attrs;
- ArrayData::Index arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : ArrayData::Index{ nullptr, 0 };
+ PropertyIndex arrayIndex = arrayData() ? arrayData()->getValueOrSetter(index, &attrs) : PropertyIndex{ nullptr, 0 };
if (arrayIndex.isNull() && isStringObject()) {
if (index < static_cast<StringObject *>(this)->length())
@@ -759,7 +780,7 @@ bool Object::internalPutIndexed(uint index, const Value &value)
}
// Section 8.12.7
-bool Object::internalDeleteProperty(String *name)
+bool Object::internalDeleteProperty(StringOrSymbol *name)
{
if (internalClass()->engine->hasException)
return false;
@@ -769,11 +790,12 @@ bool Object::internalDeleteProperty(String *name)
return deleteIndexedProperty(idx);
name->makeIdentifier();
+ Identifier id = name->identifier();
- uint memberIdx = internalClass()->find(name->identifier());
+ uint memberIdx = internalClass()->find(id);
if (memberIdx != UINT_MAX) {
if (internalClass()->propertyData[memberIdx].isConfigurable()) {
- InternalClass::removeMember(this, name->identifier());
+ Heap::InternalClass::removeMember(this, id);
return true;
}
return false;
@@ -796,7 +818,7 @@ bool Object::internalDeleteIndexedProperty(uint index)
}
// Section 8.12.9
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs)
+bool Object::__defineOwnProperty__(ExecutionEngine *engine, StringOrSymbol *name, const Property *p, PropertyAttributes attrs)
{
uint idx = name->asArrayIndex();
if (idx != UINT_MAX)
@@ -807,8 +829,8 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
uint memberIndex;
- if (isArrayObject() && name->equals(engine->id_length())) {
- Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length()));
+ if (isArrayObject() && name->identifier() == engine->id_length()->identifier()) {
+ Q_ASSERT(Heap::ArrayObject::LengthPropertyIndex == internalClass()->find(engine->id_length()->identifier()));
ScopedProperty lp(scope);
PropertyAttributes cattrs;
getProperty(Heap::ArrayObject::LengthPropertyIndex, lp, &cattrs);
@@ -829,7 +851,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const
}
if (attrs.hasWritable() && !attrs.isWritable()) {
cattrs.setWritable(false);
- InternalClass::changeMember(this, engine->id_length(), cattrs);
+ Heap::InternalClass::changeMember(this, engine->id_length()->identifier(), cattrs);
}
if (!succeeded)
return false;
@@ -898,7 +920,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope
return __defineOwnProperty__(engine, index, nullptr, p, attrs);
}
-bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs)
+bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs)
{
// clause 5
if (attrs.isEmpty())
@@ -977,7 +999,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *
current->merge(cattrs, p, attrs);
if (member) {
- InternalClass::changeMember(this, member, cattrs);
+ Heap::InternalClass::changeMember(this, member->identifier(), cattrs);
setProperty(index, current);
} else {
setArrayAttributes(index, cattrs);
@@ -1030,11 +1052,11 @@ void Object::copyArrayData(Object *other)
setArrayLengthUnchecked(other->getLength());
}
-uint Object::getLength(const Managed *m)
+qint64 Object::getLength(const Managed *m)
{
Scope scope(static_cast<const Object *>(m)->engine());
ScopedValue v(scope, static_cast<Object *>(const_cast<Managed *>(m))->get(scope.engine->id_length()));
- return v->toUInt32();
+ return v->toLength();
}
// 'var' is 'V' in 15.3.5.3.
@@ -1132,12 +1154,10 @@ void Heap::ArrayObject::init(const QStringList &list)
a->setArrayLengthUnchecked(len);
}
-uint ArrayObject::getLength(const Managed *m)
+qint64 ArrayObject::getLength(const Managed *m)
{
const ArrayObject *a = static_cast<const ArrayObject *>(m);
- if (a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->isInteger())
- return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->integerValue();
- return Primitive::toUInt32(a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->doubleValue());
+ return a->propertyData(Heap::ArrayObject::LengthPropertyIndex)->toLength();
}
QStringList ArrayObject::toQStringList() const
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index 1731ae3c76..5ad67635db 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -64,6 +64,9 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+typedef ReturnedValue (*jsCallFunction)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+typedef ReturnedValue (*jsConstructFunction)(const FunctionObject *, const Value *argv, int argc);
+
namespace Heap {
#define ObjectMembers(class, Member) \
@@ -74,6 +77,10 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
static void markObjects(Heap::Base *base, MarkStack *stack);
void init() { Base::init(); }
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
const Value *inlinePropertyDataWithOffset(uint indexWithOffset) const {
Q_ASSERT(indexWithOffset >= vtable()->inlinePropertyOffset && indexWithOffset < vtable()->inlinePropertyOffset + vtable()->nInlineProperties);
return reinterpret_cast<const Value *>(this) + indexWithOffset;
@@ -90,15 +97,15 @@ DECLARE_EXPORTED_HEAP_OBJECT(Object, Base) {
void setInlineProperty(ExecutionEngine *e, uint index, Heap::Base *b) {
Q_ASSERT(index < vtable()->nInlineProperties);
Value *prop = reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index;
- WriteBarrier::write(e, this, prop->data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, this, prop->data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
- QV4::MemberData::Index writablePropertyData(uint index) {
+ PropertyIndex writablePropertyData(uint index) {
uint nInline = vtable()->nInlineProperties;
if (index < nInline)
- return { this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
+ return PropertyIndex{ this, reinterpret_cast<Value *>(this) + vtable()->inlinePropertyOffset + index};
index -= nInline;
- return { memberData, memberData->values.values + index };
+ return PropertyIndex{ memberData, memberData->values.values + index };
}
const Value *propertyData(uint index) const {
@@ -162,15 +169,15 @@ struct ObjectVTable
VTable vTable;
ReturnedValue (*call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
ReturnedValue (*callAsConstructor)(const FunctionObject *, const Value *argv, int argc);
- ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty);
+ ReturnedValue (*get)(const Managed *, StringOrSymbol *name, bool *hasProperty);
ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty);
- bool (*put)(Managed *, String *name, const Value &value);
+ bool (*put)(Managed *, StringOrSymbol *name, const Value &value);
bool (*putIndexed)(Managed *, uint index, const Value &value);
- PropertyAttributes (*query)(const Managed *, String *name);
+ PropertyAttributes (*query)(const Managed *, StringOrSymbol *name);
PropertyAttributes (*queryIndexed)(const Managed *, uint index);
- bool (*deleteProperty)(Managed *m, String *name);
+ bool (*deleteProperty)(Managed *m, StringOrSymbol *name);
bool (*deleteIndexedProperty)(Managed *m, uint index);
- uint (*getLength)(const Managed *m);
+ qint64 (*getLength)(const Managed *m);
void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var);
};
@@ -218,7 +225,7 @@ struct Q_QML_EXPORT Object: Managed {
SetterOffset = 1
};
- void setInternalClass(InternalClass *ic);
+ void setInternalClass(Heap::InternalClass *ic);
const Value *propertyData(uint index) const { return d()->propertyData(index); }
@@ -236,20 +243,20 @@ struct Q_QML_EXPORT Object: Managed {
Heap::Object *prototype() const { return d()->prototype(); }
bool setPrototype(Object *proto);
- void getOwnProperty(String *name, PropertyAttributes *attrs, Property *p = nullptr);
+ void getOwnProperty(StringOrSymbol *name, PropertyAttributes *attrs, Property *p = nullptr);
void getOwnProperty(uint index, PropertyAttributes *attrs, Property *p = nullptr);
- MemberData::Index getValueOrSetter(String *name, PropertyAttributes *attrs);
- ArrayData::Index getValueOrSetter(uint index, PropertyAttributes *attrs);
+ PropertyIndex getValueOrSetter(StringOrSymbol *name, PropertyAttributes *attrs);
+ PropertyIndex getValueOrSetter(uint index, PropertyAttributes *attrs);
- bool hasProperty(String *name) const;
+ bool hasProperty(StringOrSymbol *name) const;
bool hasProperty(uint index) const;
- bool hasOwnProperty(String *name) const;
+ bool hasOwnProperty(StringOrSymbol *name) const;
bool hasOwnProperty(uint index) const;
- bool __defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs);
- bool __defineOwnProperty__(ExecutionEngine *engine, String *name, const Property *p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionEngine *engine, uint index, StringOrSymbol *member, const Property *p, PropertyAttributes attrs);
+ bool __defineOwnProperty__(ExecutionEngine *engine, StringOrSymbol *name, const Property *p, PropertyAttributes attrs);
bool __defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
bool __defineOwnProperty__(ExecutionEngine *engine, const QString &name, const Property *p, PropertyAttributes attrs);
bool defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs);
@@ -267,31 +274,33 @@ struct Q_QML_EXPORT Object: Managed {
bool putValue(uint memberIndex, const Value &value);
/* The spec default: Writable: true, Enumerable: false, Configurable: true */
- void defineDefaultProperty(String *name, const Value &value) {
- insertMember(name, value, Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(StringOrSymbol *name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable) {
+ insertMember(name, value, attributes);
}
- void defineDefaultProperty(const QString &name, const Value &value);
- void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineDefaultProperty(String *name, ReturnedValue (*code)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc), int argumentCount = 0);
- void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
- void defineAccessorProperty(String *name, ReturnedValue (*getter)(const FunctionObject *, const Value *, const Value *, int),
- ReturnedValue (*setter)(const FunctionObject *, const Value *, const Value *, int));
+ void defineDefaultProperty(const QString &name, const Value &value, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(const QString &name, jsCallFunction code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineDefaultProperty(StringOrSymbol *name, jsCallFunction code,
+ int argumentCount = 0, PropertyAttributes attributes = Attr_Data|Attr_NotEnumerable);
+ void defineAccessorProperty(const QString &name, jsCallFunction getter, jsCallFunction setter);
+ void defineAccessorProperty(StringOrSymbol *name, jsCallFunction getter, jsCallFunction setter);
/* Fixed: Writable: false, Enumerable: false, Configurable: false */
void defineReadonlyProperty(const QString &name, const Value &value);
void defineReadonlyProperty(String *name, const Value &value);
/* Fixed: Writable: false, Enumerable: false, Configurable: true */
void defineReadonlyConfigurableProperty(const QString &name, const Value &value);
- void defineReadonlyConfigurableProperty(String *name, const Value &value);
+ void defineReadonlyConfigurableProperty(StringOrSymbol *name, const Value &value);
+
+ void addSymbolSpecies();
- void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
+ void insertMember(StringOrSymbol *s, const Value &v, PropertyAttributes attributes = Attr_Data) {
Scope scope(engine());
ScopedProperty p(scope);
p->value = v;
insertMember(s, p, attributes);
}
- void insertMember(String *s, const Property *p, PropertyAttributes attributes);
+ void insertMember(StringOrSymbol *s, const Property *p, PropertyAttributes attributes);
bool isExtensible() const { return d()->internalClass->extensible; }
@@ -360,13 +369,13 @@ public:
return false;
}
- inline ReturnedValue get(String *name, bool *hasProperty = nullptr) const
+ inline ReturnedValue get(StringOrSymbol *name, bool *hasProperty = nullptr) const
{ return vtable()->get(this, name, hasProperty); }
inline ReturnedValue getIndexed(uint idx, bool *hasProperty = nullptr) const
{ return vtable()->getIndexed(this, idx, hasProperty); }
// use the set variants instead, to customize throw behavior
- inline bool put(String *name, const Value &v)
+ inline bool put(StringOrSymbol *name, const Value &v)
{ return vtable()->put(this, name, v); }
inline bool putIndexed(uint idx, const Value &v)
{ return vtable()->putIndexed(this, idx, v); }
@@ -376,8 +385,25 @@ public:
DoNotThrow
};
+ // This is the same as set(), but it doesn't require creating a string key,
+ // which is much more efficient for the array case.
+ inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow)
+ {
+ bool ret = vtable()->putIndexed(this, idx, v);
+ // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
+ if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) {
+ ExecutionEngine *e = engine();
+ if (!e->hasException) { // allow a custom set impl to throw itself
+ QString message = QLatin1String("Cannot assign to read-only property \"") +
+ QString::number(idx) + QLatin1Char('\"');
+ e->throwTypeError(message);
+ }
+ }
+ return ret;
+ }
+
// ES6: 7.3.3 Set (O, P, V, Throw)
- inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow)
+ inline bool set(StringOrSymbol *name, const Value &v, ThrowOnFailure shouldThrow)
{
bool ret = vtable()->put(this, name, v);
// ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception.
@@ -392,41 +418,41 @@ public:
return ret;
}
- PropertyAttributes query(String *name) const
+ PropertyAttributes query(StringOrSymbol *name) const
{ return vtable()->query(this, name); }
PropertyAttributes queryIndexed(uint index) const
{ return vtable()->queryIndexed(this, index); }
- bool deleteProperty(String *name)
+ bool deleteProperty(StringOrSymbol *name)
{ return vtable()->deleteProperty(this, name); }
bool deleteIndexedProperty(uint index)
{ return vtable()->deleteIndexedProperty(this, index); }
void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes)
{ vtable()->advanceIterator(this, it, name, index, p, attributes); }
- uint getLength() const { return vtable()->getLength(this); }
+ qint64 getLength() const { return vtable()->getLength(this); }
ReturnedValue instanceOf(const Value &var) const
{ return vtable()->instanceOf(this, var); }
protected:
static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
+ static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty);
static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
+ static bool put(Managed *m, StringOrSymbol *name, const Value &value);
static bool putIndexed(Managed *m, uint index, const Value &value);
- static PropertyAttributes query(const Managed *m, String *name);
+ static PropertyAttributes query(const Managed *m, StringOrSymbol *name);
static PropertyAttributes queryIndexed(const Managed *m, uint index);
- static bool deleteProperty(Managed *m, String *name);
+ static bool deleteProperty(Managed *m, StringOrSymbol *name);
static bool deleteIndexedProperty(Managed *m, uint index);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
- static uint getLength(const Managed *m);
+ static qint64 getLength(const Managed *m);
static ReturnedValue instanceOf(const Object *typeObject, const Value &var);
private:
- ReturnedValue internalGet(String *name, bool *hasProperty) const;
+ ReturnedValue internalGet(StringOrSymbol *name, bool *hasProperty) const;
ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const;
- bool internalPut(String *name, const Value &value);
+ bool internalPut(StringOrSymbol *name, const Value &value);
bool internalPutIndexed(uint index, const Value &value);
- bool internalDeleteProperty(String *name);
+ bool internalDeleteProperty(StringOrSymbol *name);
bool internalDeleteIndexedProperty(uint index);
friend struct ObjectIterator;
@@ -500,7 +526,7 @@ struct ArrayObject: Object {
void init(ExecutionEngine *engine);
using Object::getLength;
- static uint getLength(const Managed *m);
+ static qint64 getLength(const Managed *m);
QStringList toQStringList() const;
};
@@ -551,7 +577,7 @@ inline void Object::arraySet(uint index, const Value &value)
template<>
inline const ArrayObject *Value::as() const {
- return isManaged() && m()->vtable()->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->type == Managed::Type_ArrayObject ? static_cast<const ArrayObject *>(this) : nullptr;
}
#ifndef V4_BOOTSTRAP
diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp
index 7bf7e1aa04..1290a2c1b2 100644
--- a/src/qml/jsruntime/qv4objectiterator.cpp
+++ b/src/qml/jsruntime/qv4objectiterator.cpp
@@ -42,9 +42,29 @@
#include "qv4identifier_p.h"
#include "qv4argumentsobject_p.h"
#include "qv4string_p.h"
+#include "qv4iterator_p.h"
using namespace QV4;
+void ForInIteratorPrototype::init(ExecutionEngine *)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+}
+
+ReturnedValue ForInIteratorPrototype::method_next(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ const ForInIteratorObject *forIn = thisObject->as<ForInIteratorObject>();
+ Q_ASSERT(forIn);
+ Scope scope(b->engine());
+ ScopedValue n(scope, forIn->nextPropertyName());
+ bool done = false;
+ if (n->asReturnedValue() == Encode::null()) {
+ done = true;
+ n = Primitive::undefinedValue();
+ }
+ return IteratorPrototype::createIterResultObject(scope.engine, n, done);
+}
+
void ObjectIterator::init(const Object *o)
{
object->setM(o ? o->m() : nullptr);
@@ -175,11 +195,11 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString()
}
-DEFINE_OBJECT_VTABLE(ForEachIteratorObject);
+DEFINE_OBJECT_VTABLE(ForInIteratorObject);
-void Heap::ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
+void Heap::ForInIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack)
{
- ForEachIteratorObject *o = static_cast<ForEachIteratorObject *>(that);
+ ForInIteratorObject *o = static_cast<ForInIteratorObject *>(that);
o->workArea[0].mark(markStack);
o->workArea[1].mark(markStack);
Object::markObjects(that, markStack);
diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h
index 744d16301a..1e7000ad1f 100644
--- a/src/qml/jsruntime/qv4objectiterator_p.h
+++ b/src/qml/jsruntime/qv4objectiterator_p.h
@@ -92,8 +92,8 @@ struct Q_QML_EXPORT ObjectIterator: ObjectIteratorData
ObjectIterator(Scope &scope, const Object *o, uint flags)
{
engine = scope.engine;
- object = scope.alloc(1);
- current = scope.alloc(1);
+ object = scope.alloc();
+ current = scope.alloc();
arrayNode = nullptr;
arrayIndex = 0;
memberIndex = 0;
@@ -111,7 +111,7 @@ private:
};
namespace Heap {
-struct ForEachIteratorObject : Object {
+struct ForInIteratorObject : Object {
void init(QV4::Object *o);
ObjectIterator &it() { return *reinterpret_cast<ObjectIterator*>(&itData); }
Value workArea[2];
@@ -123,15 +123,24 @@ private:
}
-struct ForEachIteratorObject: Object {
- V4_OBJECT2(ForEachIteratorObject, Object)
- Q_MANAGED_TYPE(ForeachIteratorObject)
+struct ForInIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct ForInIteratorObject: Object {
+ V4_OBJECT2(ForInIteratorObject, Object)
+ Q_MANAGED_TYPE(ForInIterator)
+ V4_PROTOTYPE(forInIteratorPrototype)
- ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); }
+ ReturnedValue nextPropertyName() const { return d()->it().nextPropertyNameAsString(); }
};
inline
-void Heap::ForEachIteratorObject::init(QV4::Object *o)
+void Heap::ForInIteratorObject::init(QV4::Object *o)
{
Object::init();
it() = ObjectIterator(internalClass->engine, workArea, workArea + 1, o,
diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp
index b998b78520..a61b5d5136 100644
--- a/src/qml/jsruntime/qv4objectproto.cpp
+++ b/src/qml/jsruntime/qv4objectproto.cpp
@@ -47,6 +47,7 @@
#include "qv4objectiterator_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QStringList>
@@ -97,6 +98,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2);
ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1);
+ ctor->defineDefaultProperty(QStringLiteral("getOwnPropertySymbols"), method_getOwnPropertySymbols, 1);
ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2);
ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2);
ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3);
@@ -104,10 +106,12 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
ctor->defineDefaultProperty(QStringLiteral("seal"), method_seal, 1);
ctor->defineDefaultProperty(QStringLiteral("freeze"), method_freeze, 1);
ctor->defineDefaultProperty(QStringLiteral("preventExtensions"), method_preventExtensions, 1);
+ ctor->defineDefaultProperty(QStringLiteral("is"), method_is, 2);
ctor->defineDefaultProperty(QStringLiteral("isSealed"), method_isSealed, 1);
ctor->defineDefaultProperty(QStringLiteral("isFrozen"), method_isFrozen, 1);
ctor->defineDefaultProperty(QStringLiteral("isExtensible"), method_isExtensible, 1);
ctor->defineDefaultProperty(QStringLiteral("keys"), method_keys, 1);
+ ctor->defineDefaultProperty(QStringLiteral("setPrototypeOf"), method_setPrototypeOf, 2);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(v4->id_toString(), method_toString, 0);
@@ -119,11 +123,7 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor)
defineDefaultProperty(QStringLiteral("__defineGetter__"), method_defineGetter, 2);
defineDefaultProperty(QStringLiteral("__defineSetter__"), method_defineSetter, 2);
- ExecutionContext *global = v4->rootContext();
- ScopedProperty p(scope);
- p->value = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_get_proto);
- p->set = FunctionObject::createBuiltinFunction(global, v4->id___proto__(), method_set_proto);
- insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable);
+ defineAccessorProperty(v4->id___proto__(), method_get_proto, method_set_proto);
}
ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, const Value *, const Value *argv, int argc)
@@ -140,6 +140,15 @@ ReturnedValue ObjectPrototype::method_getPrototypeOf(const FunctionObject *b, co
return (!!p ? p->asReturnedValue() : Encode::null());
}
+ReturnedValue ObjectPrototype::method_is(const FunctionObject *, const Value *, const Value *argv, int argc)
+{
+ if (!argc)
+ return Encode(true);
+ if (argc == 1)
+ return Encode((argv[0].isUndefined() ? true : false));
+ return Encode(argv[0].sameValue(argv[1]));
+}
+
ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
Scope scope(b);
@@ -154,7 +163,7 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const FunctionObj
static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate();
ScopedValue v(scope, argc > 1 ? argv[1] : Primitive::undefinedValue());
- ScopedString name(scope, v->toString(scope.engine));
+ ScopedStringOrSymbol name(scope, v->toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
@@ -177,6 +186,28 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const FunctionObject *
return Encode(getOwnPropertyNames(scope.engine, argv[0]));
}
+ReturnedValue ObjectPrototype::method_getOwnPropertySymbols(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc)
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0].toObject(scope.engine));
+ if (!O)
+ return Encode::undefined();
+ Heap::InternalClass *ic = O->d()->internalClass;
+ ScopedValue n(scope);
+ ScopedArrayObject array(scope, scope.engine->newArrayObject());
+ for (uint i = 0; i < ic->size; ++i) {
+ Identifier id = ic->nameMap.at(i);
+ n = id.asHeapObject();
+ if (!n || !n->isSymbol())
+ continue;
+ array->push_back(n);
+ }
+ return array->asReturnedValue();
+}
+
// 19.1.2.1
ReturnedValue ObjectPrototype::method_assign(const FunctionObject *b, const Value *, const Value *argv, int argc)
{
@@ -255,7 +286,7 @@ ReturnedValue ObjectPrototype::method_defineProperty(const FunctionObject *b, co
return scope.engine->throwTypeError();
ScopedObject O(scope, argv[0]);
- ScopedString name(scope, argc > 1 ? argv[1] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedStringOrSymbol name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
@@ -287,7 +318,7 @@ ReturnedValue ObjectPrototype::method_defineProperties(const FunctionObject *b,
ScopedValue val(scope);
ObjectIterator it(scope, o, ObjectIterator::EnumerableOnly);
- ScopedString name(scope);
+ ScopedStringOrSymbol name(scope);
ScopedProperty pd(scope);
ScopedProperty n(scope);
while (1) {
@@ -477,19 +508,60 @@ ReturnedValue ObjectPrototype::method_keys(const FunctionObject *b, const Value
return a.asReturnedValue();
}
+ReturnedValue ObjectPrototype::method_setPrototypeOf(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f->engine());
+ if (argc < 2 || argv[0].isNullOrUndefined() || !(argv[1].isObject() || argv[1].isNull()))
+ return scope.engine->throwTypeError();
+
+ if (!argv[0].isObject())
+ return argv[0].asReturnedValue();
+
+ ScopedObject o(scope, argv[0]);
+ ScopedObject p(scope, argv[1]);
+ Q_ASSERT(!!o);
+
+ if (o->prototype() != p->d()) {
+ bool ok = false;
+ if (o->isExtensible()) {
+ ok = o->setPrototype(p);
+ }
+ if (!ok)
+ return scope.engine->throwTypeError(QStringLiteral("Object.setPrototypeOf: Could not change prototype"));
+ }
+ return o->asReturnedValue();
+}
+
ReturnedValue ObjectPrototype::method_toString(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
+ QString string;
if (thisObject->isUndefined()) {
- return Encode(v4->newString(QStringLiteral("[object Undefined]")));
+ string = QStringLiteral("[object Undefined]");
} else if (thisObject->isNull()) {
- return Encode(v4->newString(QStringLiteral("[object Null]")));
+ string = QStringLiteral("[object Null]");
} else {
+ const Object *o = thisObject->as<Object>();
+ if (!o) {
+ // primitive, get the proper prototype
+ if (thisObject->isBoolean())
+ o = v4->booleanPrototype();
+ else if (thisObject->isNumber())
+ o = v4->numberPrototype();
+ else if (thisObject->isString())
+ o = v4->stringPrototype();
+ else if (thisObject->isSymbol())
+ o = v4->symbolPrototype();
+ Q_ASSERT(o);
+ }
+ QString name = o->className();
Scope scope(v4);
- ScopedObject obj(scope, thisObject->toObject(scope.engine));
- QString className = obj->className();
- return Encode(v4->newString(QStringLiteral("[object %1]").arg(className)));
+ ScopedString toStringTag(scope, o->get(v4->symbol_toStringTag()));
+ if (toStringTag)
+ name = toStringTag->toQString();
+ string = QStringLiteral("[object %1]").arg(name);
}
+ return Encode(v4->newString(string));
}
ReturnedValue ObjectPrototype::method_toLocaleString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
@@ -514,7 +586,7 @@ ReturnedValue ObjectPrototype::method_valueOf(const FunctionObject *b, const Val
ReturnedValue ObjectPrototype::method_hasOwnProperty(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString P(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedStringOrSymbol P(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
ScopedObject O(scope, thisObject->toObject(scope.engine));
@@ -548,7 +620,7 @@ ReturnedValue ObjectPrototype::method_isPrototypeOf(const FunctionObject *b, con
ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
- ScopedString p(scope, argc ? argv[0] : Primitive::undefinedValue(), ScopedString::Convert);
+ ScopedStringOrSymbol p(scope, (argc ? argv[0] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
if (scope.engine->hasException)
return QV4::Encode::undefined();
@@ -734,29 +806,28 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c
// is the standard built-in constructor with that name.
ScopedObject o(scope, engine->newObject());
ScopedString s(scope);
+ ScopedValue v(scope);
- ScopedProperty pd(scope);
if (attrs.isData()) {
- pd->value = desc->value;
s = engine->newString(QStringLiteral("value"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isWritable());
+ o->put(s, desc->value);
+ v = Primitive::fromBoolean(attrs.isWritable());
s = engine->newString(QStringLiteral("writable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
} else {
- pd->value = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
+ v = desc->getter() ? desc->getter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("get"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
+ o->put(s, v);
+ v = desc->setter() ? desc->setter()->asReturnedValue() : Encode::undefined();
s = engine->newString(QStringLiteral("set"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
}
- pd->value = Primitive::fromBoolean(attrs.isEnumerable());
+ v = Primitive::fromBoolean(attrs.isEnumerable());
s = engine->newString(QStringLiteral("enumerable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
- pd->value = Primitive::fromBoolean(attrs.isConfigurable());
+ o->put(s, v);
+ v = Primitive::fromBoolean(attrs.isConfigurable());
s = engine->newString(QStringLiteral("configurable"));
- o->__defineOwnProperty__(scope.engine, s, pd, Attr_Data);
+ o->put(s, v);
return o.asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h
index 2b231d46ad..1a93685093 100644
--- a/src/qml/jsruntime/qv4objectproto_p.h
+++ b/src/qml/jsruntime/qv4objectproto_p.h
@@ -78,20 +78,23 @@ struct ObjectPrototype: Object
{
void init(ExecutionEngine *engine, Object *ctor);
- static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_assign(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_create(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_defineProperties(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_freeze(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyNames(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertySymbols(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_is(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isFrozen(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isSealed(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_seal(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp
index 7fc74173e3..f8bc28160e 100644
--- a/src/qml/jsruntime/qv4persistent.cpp
+++ b/src/qml/jsruntime/qv4persistent.cpp
@@ -98,9 +98,9 @@ Page *allocatePage(PersistentValueStorage *storage)
p->header.freeList = 0;
insertInFront(storage, p);
for (int i = 0; i < kEntriesPerPage - 1; ++i) {
- p->values[i].setEmpty(i + 1);
+ p->values[i] = Encode(i + 1);
}
- p->values[kEntriesPerPage - 1].setEmpty(-1);
+ p->values[kEntriesPerPage - 1] = Encode(-1);
return p;
}
@@ -226,7 +226,7 @@ void PersistentValueStorage::free(Value *v)
Page *p = getPage(v);
- v->setEmpty(p->header.freeList);
+ *v = Encode(p->header.freeList);
p->header.freeList = v - p->values;
if (!--p->header.refCount)
freePage(p);
diff --git a/src/qml/jsruntime/qv4profiling.cpp b/src/qml/jsruntime/qv4profiling.cpp
index 5fd200efc1..b337243204 100644
--- a/src/qml/jsruntime/qv4profiling.cpp
+++ b/src/qml/jsruntime/qv4profiling.cpp
@@ -78,7 +78,7 @@ Profiler::Profiler(QV4::ExecutionEngine *engine) : featuresEnabled(0), m_engine(
void Profiler::stopProfiling()
{
featuresEnabled = 0;
- reportData(true);
+ reportData();
m_sentLocations.clear();
}
@@ -89,7 +89,7 @@ bool operator<(const FunctionCall &call1, const FunctionCall &call2)
(call1.m_end == call2.m_end && call1.m_function < call2.m_function)));
}
-void Profiler::reportData(bool trackLocations)
+void Profiler::reportData()
{
std::sort(m_data.begin(), m_data.end());
QVector<FunctionCallProperties> properties;
@@ -100,12 +100,11 @@ void Profiler::reportData(bool trackLocations)
properties.append(call.properties());
Function *function = call.function();
SentMarker &marker = m_sentLocations[reinterpret_cast<quintptr>(function)];
- if (!trackLocations || !marker.isValid()) {
+ if (!marker.isValid()) {
FunctionLocation &location = locations[properties.constLast().id];
if (!location.isValid())
location = call.resolveLocation();
- if (trackLocations)
- marker.setFunction(function);
+ marker.setFunction(function);
}
}
diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h
index e8c154e4e7..8461384e9a 100644
--- a/src/qml/jsruntime/qv4profiling_p.h
+++ b/src/qml/jsruntime/qv4profiling_p.h
@@ -251,7 +251,7 @@ public:
void stopProfiling();
void startProfiling(quint64 features);
- void reportData(bool trackLocations);
+ void reportData();
void setTimer(const QElapsedTimer &timer) { m_timer = timer; }
signals:
diff --git a/src/qml/jsruntime/qv4property_p.h b/src/qml/jsruntime/qv4property_p.h
index 7cb106c424..26dc7a83c3 100644
--- a/src/qml/jsruntime/qv4property_p.h
+++ b/src/qml/jsruntime/qv4property_p.h
@@ -142,6 +142,19 @@ inline void Property::merge(PropertyAttributes &attrs, const Property *other, Pr
}
}
+struct PropertyIndex {
+ Heap::Base *base;
+ Value *slot;
+
+ void set(EngineBase *e, Value newVal) {
+ WriteBarrier::write(e, base, slot->data_ptr(), newVal.asReturnedValue());
+ }
+ const Value *operator->() const { return slot; }
+ const Value &operator*() const { return *slot; }
+ bool isNull() const { return !slot; }
+};
+
+
}
Q_DECLARE_TYPEINFO(QV4::Property, Q_MOVABLE_TYPE);
diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp
index 040f060476..4226bf7972 100644
--- a/src/qml/jsruntime/qv4qmlcontext.cpp
+++ b/src/qml/jsruntime/qv4qmlcontext.cpp
@@ -78,30 +78,26 @@ void Heap::QQmlContextWrapper::destroy()
Object::destroy();
}
-ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasProperty)
+ReturnedValue QQmlContextWrapper::get(const Managed *m, StringOrSymbol *n, bool *hasProperty)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (n->isSymbol())
+ return Object::get(m, n, hasProperty);
+ String *name = static_cast<String *>(n);
+
const QQmlContextWrapper *resource = static_cast<const QQmlContextWrapper *>(m);
QV4::ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
- // In V8 the JS global object would come _before_ the QML global object,
- // so simulate that here.
- bool hasProp;
- QV4::ScopedValue result(scope, v4->globalObject->get(name, &hasProp));
- if (hasProp) {
- if (hasProperty)
- *hasProperty = hasProp;
- return result->asReturnedValue();
- }
-
if (resource->d()->isNullWrapper)
return Object::get(m, name, hasProperty);
if (v4->callingQmlContext() != *resource->d()->context)
return Object::get(m, name, hasProperty);
- result = Object::get(m, name, &hasProp);
+ bool hasProp = false;
+ ScopedValue result(scope, Object::get(m, name, &hasProp));
if (hasProp) {
if (hasProperty)
*hasProperty = hasProp;
@@ -219,14 +215,28 @@ ReturnedValue QQmlContextWrapper::get(const Managed *m, String *name, bool *hasP
context = context->parent;
}
+ // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming
+ // true if we access properties of the global object.
+ result = v4->globalObject->get(name, &hasProp);
+ if (hasProp) {
+ if (hasProperty)
+ *hasProperty = hasProp;
+ return result->asReturnedValue();
+ }
+
expressionContext->unresolvedNames = true;
return Encode::undefined();
}
-bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
+bool QQmlContextWrapper::put(Managed *m, StringOrSymbol *n, const Value &value)
{
Q_ASSERT(m->as<QQmlContextWrapper>());
+
+ if (n->isSymbol())
+ return Object::put(m, n, value);
+ String *name = static_cast<String *>(n);
+
QQmlContextWrapper *resource = static_cast<QQmlContextWrapper *>(m);
ExecutionEngine *v4 = resource->engine();
QV4::Scope scope(v4);
@@ -234,7 +244,8 @@ bool QQmlContextWrapper::put(Managed *m, String *name, const Value &value)
return false;
QV4::Scoped<QQmlContextWrapper> wrapper(scope, resource);
- uint member = wrapper->internalClass()->find(name);
+ name->makeIdentifier();
+ uint member = wrapper->internalClass()->find(name->identifier());
if (member < UINT_MAX)
return wrapper->putValue(member, value);
@@ -312,7 +323,7 @@ Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, cons
context->isInternal = true;
context->isJSContext = true;
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, (QObject*)nullptr));
+ Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, (QObject*)nullptr));
qml->d()->isNullWrapper = true;
qml->setReadOnly(false);
@@ -330,7 +341,7 @@ Heap::QmlContext *QmlContext::create(ExecutionContext *parent, QQmlContextData *
{
Scope scope(parent);
- Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocObject<QQmlContextWrapper>(context, scopeObject));
+ Scoped<QQmlContextWrapper> qml(scope, scope.engine->memoryManager->allocate<QQmlContextWrapper>(context, scopeObject));
Heap::QmlContext *c = scope.engine->memoryManager->alloc<QmlContext>(parent, qml);
Q_ASSERT(c->vtable() == staticVTable());
return c;
diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h
index 647bef7fc1..86c5b62da2 100644
--- a/src/qml/jsruntime/qv4qmlcontext_p.h
+++ b/src/qml/jsruntime/qv4qmlcontext_p.h
@@ -98,8 +98,8 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object
void setReadOnly(bool b) { d()->readOnly = b; }
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
+ static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty);
+ static bool put(Managed *m, StringOrSymbol *name, const Value &value);
};
struct Q_QML_EXPORT QmlContext : public ExecutionContext
diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp
index d63d42478a..e17ce55f7b 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper.cpp
+++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp
@@ -56,7 +56,11 @@
#include <private/qv4functionobject_p.h>
#include <private/qv4runtime_p.h>
#include <private/qv4variantobject_p.h>
+
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
+
#include <private/qv4objectproto_p.h>
#include <private/qv4jsonobject_p.h>
#include <private/qv4regexpobject_p.h>
@@ -181,11 +185,13 @@ static QV4::ReturnedValue loadProperty(QV4::ExecutionEngine *v4, QObject *object
if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property.propType()))
return QV4::QQmlValueTypeWrapper::create(v4, object, property.coreIndex(), valueTypeMetaObject, property.propType());
} else {
+#if QT_CONFIG(qml_sequence_object)
// see if it's a sequence type
bool succeeded = false;
- QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), &succeeded));
+ QV4::ScopedValue retn(scope, QV4::SequencePrototype::newSequence(v4, property.propType(), object, property.coreIndex(), !property.isWritable(), &succeeded));
if (succeeded)
return retn->asReturnedValue();
+#endif
}
if (property.propType() == QMetaType::UnknownType) {
@@ -242,7 +248,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje
return QV4::QObjectMethod::create(global, object, property->coreIndex());
} else if (property->isSignalHandler()) {
QmlSignalHandler::initProto(engine);
- return engine->memoryManager->allocObject<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
+ return engine->memoryManager->allocate<QV4::QmlSignalHandler>(object, property->coreIndex())->asReturnedValue();
} else {
ExecutionContext *global = engine->rootContext();
return QV4::QObjectMethod::create(global, object, property->coreIndex());
@@ -684,18 +690,25 @@ ReturnedValue QObjectWrapper::create(ExecutionEngine *engine, QObject *object)
return result;
}
}
- return (engine->memoryManager->allocObject<QV4::QObjectWrapper>(object))->asReturnedValue();
+ return (engine->memoryManager->allocate<QV4::QObjectWrapper>(object))->asReturnedValue();
}
-QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *hasProperty)
+QV4::ReturnedValue QObjectWrapper::get(const Managed *m, StringOrSymbol *name, bool *hasProperty)
{
+ if (name->isSymbol())
+ return Object::get(m, name, hasProperty);
+ String *n = static_cast<String *>(name);
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
QQmlContextData *qmlContext = that->engine()->callingQmlContext();
- return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true);
+ return that->getQmlProperty(qmlContext, n, IgnoreRevision, hasProperty, /*includeImports*/ true);
}
-bool QObjectWrapper::put(Managed *m, String *name, const Value &value)
+bool QObjectWrapper::put(Managed *m, StringOrSymbol *n, const Value &value)
{
+ if (n->isSymbol())
+ return Object::put(m, n, value);
+ String *name = static_cast<String *>(n);
+
QObjectWrapper *that = static_cast<QObjectWrapper*>(m);
ExecutionEngine *v4 = that->engine();
@@ -720,8 +733,12 @@ bool QObjectWrapper::put(Managed *m, String *name, const Value &value)
return true;
}
-PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
+PropertyAttributes QObjectWrapper::query(const Managed *m, StringOrSymbol *name)
{
+ if (name->isSymbol())
+ return QV4::Object::query(m, name);
+ String *n = static_cast<String *>(name);
+
const QObjectWrapper *that = static_cast<const QObjectWrapper*>(m);
const QObject *thatObject = that->d()->object();
if (QQmlData::wasDeleted(thatObject))
@@ -730,8 +747,8 @@ PropertyAttributes QObjectWrapper::query(const Managed *m, String *name)
ExecutionEngine *engine = that->engine();
QQmlContextData *qmlContext = engine->callingQmlContext();
QQmlPropertyData local;
- if (that->findProperty(engine, qmlContext, name, IgnoreRevision, &local)
- || name->equals(engine->id_destroy()) || name->equals(engine->id_toString()))
+ if (that->findProperty(engine, qmlContext, n, IgnoreRevision, &local)
+ || n->equals(engine->id_destroy()) || n->equals(engine->id_toString()))
return QV4::Attr_Data;
else
return QV4::Object::query(m, name);
@@ -1619,6 +1636,7 @@ void CallArgument::initAsType(int callType)
}
}
+#if QT_CONFIG(qml_sequence_object)
template <class T, class M>
void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M CallArgument::*member, bool &queryEngine)
{
@@ -1631,6 +1649,7 @@ void CallArgument::fromContainerValue(const QV4::Object *object, int callType, M
}
}
}
+#endif
void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const QV4::Value &value)
{
@@ -1713,6 +1732,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
type = callType;
} else if (callType == QMetaType::Void) {
*qvariantPtr = QVariant();
+#if QT_CONFIG(qml_sequence_object)
} else if (callType == qMetaTypeId<std::vector<int>>()
|| callType == qMetaTypeId<std::vector<qreal>>()
|| callType == qMetaTypeId<std::vector<bool>>()
@@ -1740,6 +1760,7 @@ void CallArgument::fromValue(int callType, QV4::ExecutionEngine *engine, const Q
stdVectorQModelIndexPtr = nullptr;
fromContainerValue<std::vector<QModelIndex>>(object, callType, &CallArgument::stdVectorQModelIndexPtr, queryEngine);
}
+#endif
} else {
queryEngine = true;
}
@@ -1833,7 +1854,7 @@ QV4::ReturnedValue CallArgument::toValue(QV4::ExecutionEngine *engine)
ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setObject(object);
if (QQmlData *ddata = QQmlData::get(object))
@@ -1846,7 +1867,7 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in
ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index)
{
Scope valueScope(scope);
- Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocObject<QObjectMethod>(scope));
+ Scoped<QObjectMethod> method(valueScope, valueScope.engine->memoryManager->allocate<QObjectMethod>(scope));
method->d()->setPropertyCache(valueType->d()->propertyCache());
method->d()->index = index;
method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d());
@@ -2019,7 +2040,7 @@ void Heap::QMetaObjectWrapper::ensureConstructorsCache() {
ReturnedValue QMetaObjectWrapper::create(ExecutionEngine *engine, const QMetaObject* metaObject) {
QV4::Scope scope(engine);
- Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocObject<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
+ Scoped<QMetaObjectWrapper> mo(scope, engine->memoryManager->allocate<QV4::QMetaObjectWrapper>(metaObject)->asReturnedValue());
mo->init(engine);
return mo->asReturnedValue();
}
diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h
index 1455acc1b3..56e20adbfa 100644
--- a/src/qml/jsruntime/qv4qobjectwrapper_p.h
+++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h
@@ -193,9 +193,9 @@ protected:
static QQmlPropertyData *findProperty(ExecutionEngine *engine, QObject *o, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local);
QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const;
- static ReturnedValue get(const Managed *m, String *name, bool *hasProperty);
- static bool put(Managed *m, String *name, const Value &value);
- static PropertyAttributes query(const Managed *, String *name);
+ static ReturnedValue get(const Managed *m, StringOrSymbol *name, bool *hasProperty);
+ static bool put(Managed *m, StringOrSymbol *name, const Value &value);
+ static PropertyAttributes query(const Managed *, StringOrSymbol *name);
static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes);
static ReturnedValue method_connect(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
diff --git a/src/qml/jsruntime/qv4reflect.cpp b/src/qml/jsruntime/qv4reflect.cpp
new file mode 100644
index 0000000000..69baecd337
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect.cpp
@@ -0,0 +1,270 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qv4reflect_p.h"
+#include "qv4symbol_p.h"
+#include "qv4runtimeapi_p.h"
+#include "qv4objectproto_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(Reflect);
+
+void Heap::Reflect::init()
+{
+ Object::init();
+ Scope scope(internalClass->engine);
+ ScopedObject r(scope, this);
+
+ r->defineDefaultProperty(QStringLiteral("apply"), QV4::Reflect::method_apply, 3);
+ r->defineDefaultProperty(QStringLiteral("construct"), QV4::Reflect::method_construct, 2);
+ r->defineDefaultProperty(QStringLiteral("defineProperty"), QV4::Reflect::method_defineProperty, 3);
+ r->defineDefaultProperty(QStringLiteral("deleteProperty"), QV4::Reflect::method_deleteProperty, 2);
+ r->defineDefaultProperty(QStringLiteral("get"), QV4::Reflect::method_get, 2);
+ r->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), QV4::Reflect::method_getOwnPropertyDescriptor, 2);
+ r->defineDefaultProperty(QStringLiteral("getPrototypeOf"), QV4::Reflect::method_getPrototypeOf, 1);
+ r->defineDefaultProperty(QStringLiteral("has"), QV4::Reflect::method_has, 2);
+ r->defineDefaultProperty(QStringLiteral("isExtensible"), QV4::Reflect::method_isExtensible, 1);
+ r->defineDefaultProperty(QStringLiteral("ownKeys"), QV4::Reflect::method_ownKeys, 1);
+ r->defineDefaultProperty(QStringLiteral("preventExtensions"), QV4::Reflect::method_preventExtensions, 1);
+ r->defineDefaultProperty(QStringLiteral("set"), QV4::Reflect::method_set, 3);
+ r->defineDefaultProperty(QStringLiteral("setPrototypeOf"), QV4::Reflect::method_setPrototypeOf, 2);
+}
+
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createListFromArrayLike(Scope &scope, const Object *o)
+{
+ int len = o->getLength();
+ Value *arguments = scope.alloc(len);
+
+ for (int i = 0; i < len; ++i) {
+ arguments[i] = o->getIndexed(i);
+ if (scope.hasException())
+ return { nullptr, 0 };
+ }
+ return { arguments, len };
+}
+
+ReturnedValue Reflect::method_apply(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 3 || !argv[0].isFunctionObject() || !argv[2].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 2);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(argv[0]).call(&argv[1], arguments.argv, arguments.argc);
+}
+
+ReturnedValue Reflect::method_construct(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (argc < 2 || !argv[0].isFunctionObject() || !argv[1].isObject())
+ return scope.engine->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv + 1);
+ CallArgs arguments = createListFromArrayLike(scope, o);
+ if (scope.hasException())
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(argv[0]).callAsConstructor(arguments.argv, arguments.argc);
+}
+
+ReturnedValue Reflect::method_defineProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject O(scope, argv[0]);
+ ScopedStringOrSymbol name(scope, (argc > 1 ? argv[1] : Primitive::undefinedValue()).toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ ScopedValue attributes(scope, argc > 2 ? argv[2] : Primitive::undefinedValue());
+ ScopedProperty pd(scope);
+ PropertyAttributes attrs;
+ ObjectPrototype::toPropertyDescriptor(scope.engine, attributes, pd, &attrs);
+ if (scope.engine->hasException)
+ return QV4::Encode::undefined();
+
+ bool result = O->__defineOwnProperty__(scope.engine, name, pd, attrs);
+
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_deleteProperty(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isObject())
+ return e->throwTypeError();
+
+ bool result = Runtime::method_deleteProperty(e, argv[0], argc > 1 ? argv[1] : Primitive::undefinedValue());
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_get(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ // ### Fix third receiver argument
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+
+ uint n = index->asArrayIndex();
+ if (n < UINT_MAX)
+ return Encode(o->getIndexed(n));
+
+ ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+ return Encode(o->get(name));
+}
+
+ReturnedValue Reflect::method_getOwnPropertyDescriptor(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getOwnPropertyDescriptor(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_getPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getPrototypeOf(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_has(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+
+ bool hasProperty = false;
+ uint n = index->asArrayIndex();
+ if (n < UINT_MAX) {
+ (void) o->getIndexed(n, &hasProperty);
+ return Encode(hasProperty);
+ }
+
+ ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+ (void) o->get(name, &hasProperty);
+ return Encode(hasProperty);
+}
+
+ReturnedValue Reflect::method_isExtensible(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ const Object *o = static_cast<const Object *>(argv);
+ return Encode(o->isExtensible());
+}
+
+
+ReturnedValue Reflect::method_ownKeys(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (!argc || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_getOwnPropertyNames(f, thisObject, argv, argc);
+}
+
+ReturnedValue Reflect::method_preventExtensions(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ o->setInternalClass(o->internalClass()->nonExtensible());
+ return Encode(true);
+}
+
+ReturnedValue Reflect::method_set(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ // ### Fix third receiver argument
+ Scope scope(f);
+ if (!argc || !argv[0].isObject())
+ return scope.engine->throwTypeError();
+
+ ScopedObject o(scope, static_cast<const Object *>(argv));
+ Value undef = Primitive::undefinedValue();
+ const Value *index = argc > 1 ? &argv[1] : &undef;
+ const Value &val = argc > 2 ? argv[2] : undef;
+
+ uint n = index->asArrayIndex();
+ if (n < UINT_MAX) {
+ bool result = o->putIndexed(n, val);
+ return Encode(result);
+ }
+
+ ScopedStringOrSymbol name(scope, index->toPropertyKey(scope.engine));
+ if (scope.engine->hasException)
+ return false;
+ bool result = o->put(name, val);
+ return Encode(result);
+}
+
+ReturnedValue Reflect::method_setPrototypeOf(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ if (argc < 2 || !argv[0].isObject())
+ return f->engine()->throwTypeError();
+
+ return ObjectPrototype::method_setPrototypeOf(f, thisObject, argv, argc);
+}
diff --git a/src/qml/jsruntime/qv4reflect_p.h b/src/qml/jsruntime/qv4reflect_p.h
new file mode 100644
index 0000000000..73d257e006
--- /dev/null
+++ b/src/qml/jsruntime/qv4reflect_p.h
@@ -0,0 +1,89 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4REFLECT_H
+#define QV4REFLECT_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct Reflect : Object {
+ void init();
+};
+
+}
+
+struct Reflect : Object {
+ V4_OBJECT2(Reflect, Object)
+
+ static ReturnedValue method_apply(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_construct(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_defineProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_deleteProperty(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getOwnPropertyDescriptor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_getPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_isExtensible(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_ownKeys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_preventExtensions(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_setPrototypeOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp
index 000e2c3a7e..4fce63c5cf 100644
--- a/src/qml/jsruntime/qv4regexpobject.cpp
+++ b/src/qml/jsruntime/qv4regexpobject.cpp
@@ -45,10 +45,6 @@
#include "qv4scopedvalue_p.h"
#include "qv4jscall_p.h"
-#include <private/qqmljsengine_p.h>
-#include <private/qqmljslexer_p.h>
-#include <private/qqmljsparser_p.h>
-#include <private/qqmljsast_p.h>
#include "private/qlocale_tools_p.h"
#include <QtCore/QDebug>
@@ -280,7 +276,8 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor)
ScopedObject ctor(scope, constructor);
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(2));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(2));
+ ctor->addSymbolSpecies();
// Properties deprecated in the spec but required by "the web" :(
ctor->defineAccessorProperty(QStringLiteral("lastMatch"), method_get_lastMatch_n<0>, nullptr);
@@ -387,7 +384,7 @@ ReturnedValue RegExpPrototype::method_exec(const FunctionObject *b, const Value
}
// fill in result data
- ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses[EngineBase::Class_RegExpExecArray], scope.engine->arrayPrototype()));
+ ScopedArrayObject array(scope, scope.engine->newArrayObject(scope.engine->internalClasses(EngineBase::Class_RegExpExecArray)));
int len = r->value()->captureCount();
array->arrayReserve(len);
ScopedValue v(scope);
diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h
index 181628241b..a9ebe8384f 100644
--- a/src/qml/jsruntime/qv4regexpobject_p.h
+++ b/src/qml/jsruntime/qv4regexpobject_p.h
@@ -129,11 +129,11 @@ struct RegExpObject: Object {
void initProperties();
int lastIndex() const {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
+ Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->identifier()));
return propertyData(Index_LastIndex)->toInt32();
}
void setLastIndex(int index) {
- Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()));
+ Q_ASSERT(Index_LastIndex == internalClass()->find(engine()->id_lastIndex()->identifier()));
return setProperty(Index_LastIndex, Primitive::fromInt32(index));
}
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp
index 9729228511..8759a72074 100644
--- a/src/qml/jsruntime/qv4runtime.cpp
+++ b/src/qml/jsruntime/qv4runtime.cpp
@@ -61,6 +61,8 @@
#include <private/qqmlengine_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include "qv4qobjectwrapper_p.h"
+#include "qv4symbol_p.h"
+#include "qv4generatorobject_p.h"
#include <private/qv8engine_p.h>
#endif
@@ -314,37 +316,27 @@ ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId)
QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId];
Q_ASSERT(clos);
ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context);
+ if (clos->isGenerator())
+ return GeneratorFunction::create(current, clos)->asReturnedValue();
return FunctionObject::createScriptFunction(current, clos)->asReturnedValue();
}
-bool Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index)
+bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index)
{
Scope scope(engine);
- ScopedObject o(scope, base);
- if (o) {
- uint n = index.asArrayIndex();
- if (n < UINT_MAX)
- return o->deleteIndexedProperty(n);
- }
-
- ScopedString name(scope, index.toString(engine));
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex)
-{
- Scope scope(engine);
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- return method_deleteMemberString(engine, base, name);
-}
-
-bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name)
-{
- Scope scope(engine);
- ScopedObject obj(scope, base.toObject(engine));
+ ScopedObject o(scope, base.toObject(engine));
if (scope.engine->hasException)
return Encode::undefined();
- return obj->deleteProperty(name);
+ Q_ASSERT(o);
+
+ uint n = index.asArrayIndex();
+ if (n < UINT_MAX)
+ return o->deleteIndexedProperty(n);
+
+ ScopedStringOrSymbol name(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
+ return o->deleteProperty(name);
}
bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex)
@@ -361,8 +353,16 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val
if (!rhs)
return engine->throwTypeError();
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- return rhs->instanceOf(lval);
+ Scope scope(engine);
+ ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance()));
+ if (hasInstance->isUndefined())
+ return rhs->instanceOf(lval);
+ FunctionObject *f = hasInstance->as<FunctionObject>();
+ if (!f)
+ return engine->throwTypeError();
+
+ ScopedValue result(scope, f->call(&rval, &lval, 1));
+ return Encode(result->toBoolean());
}
QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right)
@@ -371,7 +371,7 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left
if (!ro)
return engine->throwTypeError();
Scope scope(engine);
- ScopedString s(scope, left.toString(engine));
+ ScopedStringOrSymbol s(scope, left.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
bool r = ro->hasProperty(s);
@@ -408,27 +408,59 @@ Heap::String *RuntimeHelpers::stringFromNumber(ExecutionEngine *engine, double n
ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeHint)
{
- if (typeHint == PREFERREDTYPE_HINT) {
- if (object->as<DateObject>())
- typeHint = STRING_HINT;
- else
- typeHint = NUMBER_HINT;
+ ExecutionEngine *engine = object->internalClass()->engine;
+ if (engine->hasException)
+ return Encode::undefined();
+
+ String *hint;
+ switch (typeHint) {
+ case STRING_HINT:
+ hint = engine->id_string();
+ break;
+ case NUMBER_HINT:
+ hint = engine->id_number();
+ break;
+ default:
+ hint = engine->id_default();
+ break;
}
- ExecutionEngine *engine = object->internalClass()->engine;
+ Scope scope(engine);
+ ScopedFunctionObject toPrimitive(scope, object->get(engine->symbol_toPrimitive()));
if (engine->hasException)
return Encode::undefined();
+ if (toPrimitive) {
+ ScopedValue result(scope, toPrimitive->call(object, hint, 1));
+ if (engine->hasException)
+ return Encode::undefined();
+ if (!result->isPrimitive())
+ return engine->throwTypeError();
+ return result->asReturnedValue();
+ }
+
+ if (hint == engine->id_default())
+ hint = engine->id_number();
+ return ordinaryToPrimitive(engine, object, hint);
+}
+
+
+ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint)
+{
+ Q_ASSERT(!engine->hasException);
String *meth1 = engine->id_toString();
String *meth2 = engine->id_valueOf();
- if (typeHint == NUMBER_HINT)
+ if (typeHint->identifier() == engine->id_number()->identifier()) {
qSwap(meth1, meth2);
+ } else {
+ Q_ASSERT(typeHint->identifier() == engine->id_string()->identifier());
+ }
Scope scope(engine);
ScopedValue result(scope);
- ScopedValue conv(scope, object->get(meth1));
+ ScopedValue conv(scope, object->get(meth1));
if (FunctionObject *o = conv->as<FunctionObject>()) {
result = o->call(object, nullptr, 0);
if (result->isPrimitive())
@@ -449,7 +481,6 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH
}
-
Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Value &value)
{
Q_ASSERT(!value.isObject());
@@ -461,7 +492,9 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val
case Value::Boolean_Type:
return engine->newBooleanObject(value.booleanValue());
case Value::Managed_Type:
- Q_ASSERT(value.isString());
+ Q_ASSERT(value.isStringOrSymbol());
+ if (!value.isString())
+ return engine->newSymbolObject(value.symbolValue());
return engine->newStringObject(value.stringValue());
case Value::Integer_Type:
default: // double
@@ -488,6 +521,10 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val
case Value::Managed_Type: {
if (value.isString())
return static_cast<const String &>(value).d();
+ if (value.isSymbol()) {
+ engine->throwTypeError(QLatin1String("Cannot convert a symbol to a string."));
+ return nullptr;
+ }
value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint));
Q_ASSERT(value.isPrimitive());
if (value.isString())
@@ -598,7 +635,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine,
Q_ASSERT(!!o); // can't fail as null/undefined is covered above
}
- ScopedString name(scope, index.toString(engine));
+ ScopedStringOrSymbol name(scope, index.toPropertyKey(engine));
if (scope.hasException())
return Encode::undefined();
return o->get(name);
@@ -617,7 +654,7 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &
uint idx = 0;
if (index.asArrayIndex(idx)) {
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
@@ -652,7 +689,9 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val
return o->putIndexed(idx, value);
}
- ScopedString name(scope, index.toString(engine));
+ ScopedStringOrSymbol name(scope, index.toPropertyKey(engine));
+ if (engine->hasException)
+ return false;
return o->put(name, value);
}
@@ -661,7 +700,7 @@ bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object,
uint idx = 0;
if (index.asArrayIndex(idx)) {
if (Heap::Base *b = object.heapObject()) {
- if (b->vtable()->isObject) {
+ if (b->internalClass->vtable->isObject) {
Heap::Object *o = static_cast<Heap::Object *>(b);
if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) {
Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>();
@@ -677,25 +716,105 @@ bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object,
return setElementFallback(engine, object, index, value);
}
-ReturnedValue Runtime::method_foreachIterator(ExecutionEngine *engine, const Value &in)
+ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator)
{
Scope scope(engine);
ScopedObject o(scope, (Object *)nullptr);
if (!in.isNullOrUndefined())
o = in.toObject(engine);
- return engine->newForEachIteratorObject(o)->asReturnedValue();
+ if (engine->hasException)
+ return Encode::undefined();
+ if (iterator) {
+ if (!o)
+ return engine->throwTypeError();
+ ScopedFunctionObject f(scope, o->get(engine->symbol_iterator()));
+ if (!f)
+ return engine->throwTypeError();
+ JSCallData cData(scope, 0, nullptr, o);
+ ScopedObject it(scope, f->call(cData));
+ if (!it)
+ return engine->throwTypeError();
+ return it->asReturnedValue();
+ }
+ return engine->newForInIteratorObject(o)->asReturnedValue();
}
-ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_iterator)
+ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value)
{
- Q_ASSERT(foreach_iterator.isObject());
+ Q_ASSERT(iterator.isObject());
+
+ Scope scope(engine);
+ ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_next()));
+ if (!f)
+ return engine->throwTypeError();
+ JSCallData cData(scope, 0, nullptr, &iterator);
+ ScopedObject o(scope, f->call(cData));
+ if (!o)
+ return engine->throwTypeError();
+ ScopedValue d(scope, o->get(engine->id_done()));
+ bool done = d->toBoolean();
+ if (done) {
+ *value = Encode::undefined();
+ } else {
+ *value = o->get(engine->id_value());
+ }
+ return Encode(done);
+}
- ForEachIteratorObject *it = static_cast<ForEachIteratorObject *>(foreach_iterator.objectValue());
- Q_ASSERT(it->as<ForEachIteratorObject>());
+ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done)
+{
+ Q_ASSERT(iterator.isObject());
+ Q_ASSERT(done.isBoolean());
+ if (done.booleanValue())
+ return Encode::undefined();
- return it->nextPropertyName();
+ Scope scope(engine);
+ bool hadException = engine->hasException;
+ ScopedValue e(scope);
+ if (hadException) {
+ e = *engine->exceptionValue;
+ engine->hasException = false;
+ }
+ ScopedFunctionObject f(scope, static_cast<const Object &>(iterator).get(engine->id_return()));
+ ScopedObject o(scope);
+ if (f) {
+ JSCallData cData(scope, 0, nullptr, &iterator);
+ o = f->call(cData);
+ }
+ if (hadException || !f) {
+ *engine->exceptionValue = e;
+ engine->hasException = hadException;
+ return Encode::undefined();
+ }
+ if (engine->hasException)
+ return Encode::undefined();
+
+ if (!o)
+ return engine->throwTypeError();
+ return Encode::undefined();
}
+ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator)
+{
+ Q_ASSERT(iterator.isObject());
+
+ Scope scope(engine);
+ ScopedArrayObject array(scope, engine->newArrayObject());
+ array->arrayCreate();
+ uint index = 0;
+ while (1) {
+ ScopedValue n(scope);
+ ScopedValue done(scope, method_iteratorNext(engine, iterator, n));
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ array->arraySet(index, n);
+ ++index;
+ }
+ return array->asReturnedValue();
+}
void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value)
{
@@ -1033,24 +1152,31 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V
ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)
{
Scope scope(engine);
+ ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
+ ScopedObject lookupObject(scope, base);
- if (!base->isObject()) {
+ if (!lookupObject) {
Q_ASSERT(!base->isEmpty());
if (base->isNullOrUndefined()) {
QString message = QStringLiteral("Cannot call method '%1' of %2")
- .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(),
- base->toQStringNoThrow());
+ .arg(name->toQString(), base->toQStringNoThrow());
return engine->throwTypeError(message);
}
- ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base));
- if (engine->hasException) // type error
- return Encode::undefined();
- base = thisObject;
+ if (base->isManaged()) {
+ Managed *m = static_cast<Managed *>(base);
+ lookupObject = m->internalClass()->prototype;
+ Q_ASSERT(m->internalClass()->prototype);
+ } else {
+ lookupObject = RuntimeHelpers::convertToObject(engine, *base);
+ if (engine->hasException) // type error
+ return Encode::undefined();
+ if (!engine->currentStackFrame->v4Function->isStrict())
+ base = lookupObject;
+ }
}
- ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name));
+ ScopedFunctionObject f(scope, static_cast<Object *>(lookupObject)->get(name));
if (!f) {
QString error = QStringLiteral("Property '%1' of object %2 is not a function")
@@ -1080,7 +1206,7 @@ ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base,
ScopedValue thisObject(scope, base->toObject(engine));
base = thisObject;
- ScopedString str(scope, index.toString(engine));
+ ScopedStringOrSymbol str(scope, index.toPropertyKey(engine));
if (engine->hasException)
return Encode::undefined();
@@ -1130,6 +1256,60 @@ ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engi
return fo->call(qmlContextValue, argv, argc);
}
+struct CallArgs {
+ Value *argv;
+ int argc;
+};
+
+static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc)
+{
+ ScopedValue it(scope);
+ ScopedValue done(scope);
+
+ int argCount = 0;
+
+ Value *v = scope.alloc<Scope::Uninitialized>();
+ Value *arguments = v;
+ for (int i = 0; i < argc; ++i) {
+ if (!argv[i].isEmpty()) {
+ *v = argv[i];
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ continue;
+ }
+ // spread element
+ ++i;
+ it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ while (1) {
+ done = Runtime::method_iteratorNext(scope.engine, it, v);
+ if (scope.engine->hasException)
+ return { nullptr, 0 };
+ Q_ASSERT(done->isBoolean());
+ if (done->booleanValue())
+ break;
+ ++argCount;
+ v = scope.alloc<Scope::Uninitialized>();
+ }
+ }
+ return { arguments, argCount };
+}
+
+ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc)
+{
+ Q_ASSERT(argc >= 1);
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc);
+}
+
ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc)
{
if (!function.isFunctionObject())
@@ -1138,6 +1318,20 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu
return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc);
}
+ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, Value *argv, int argc)
+{
+ Q_UNIMPLEMENTED();
+ if (!function.isFunctionObject())
+ return engine->throwTypeError();
+
+ Scope scope(engine);
+ CallArgs arguments = createSpreadArguments(scope, argv, argc);
+ if (engine->hasException)
+ return Encode::undefined();
+
+ return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc);
+}
+
void Runtime::method_throwException(ExecutionEngine *engine, const Value &value)
{
if (!value.isEmpty())
@@ -1161,6 +1355,8 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &
case Value::Managed_Type:
if (value.isString())
res = engine->id_string();
+ else if (value.isSymbol())
+ res = engine->id_symbol();
else if (value.objectValue()->as<FunctionObject>())
res = engine->id_function();
else
@@ -1183,25 +1379,54 @@ QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameI
return method_typeofValue(engine, prop);
}
-/* The next three methods are a bit tricky. They can't open up a Scope, as that
- * would mess up the pushing of the context.
- *
- * Instead the push/pop pair acts as a non local scope.
- */
-ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o)
+ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame)
{
- Q_ASSERT(o.isObject());
- const Object &obj = static_cast<const Object &>(o);
- return parent->newWithContext(obj.d())->asReturnedValue();
+ QV4::Value &accumulator = jsStackFrame[CallData::Accumulator];
+ accumulator = accumulator.toObject(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(accumulator.isObject());
+ const Object &obj = static_cast<const Object &>(accumulator);
+ ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context);
+ return context->newWithContext(obj.d())->asReturnedValue();
+}
+
+ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)
+{
+ ExecutionEngine *e = parent->engine();
+ return parent->newCatchContext(e->currentStackFrame, blockIndex,
+ e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue();
}
-ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex)
+ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index)
{
ExecutionEngine *e = parent->engine();
- return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex],
- e->catchException(nullptr))->asReturnedValue();
+ return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue();
+}
+
+ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous)
+{
+ return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue();
+}
+
+
+ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index)
+{
+ Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext ||
+ engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext);
+ ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue();
+ engine->setScriptContext(c);
+ return c;
+}
+
+ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine)
+{
+ ReturnedValue root = engine->rootContext()->asReturnedValue();
+ engine->setScriptContext(root);
+ return root;
}
+
void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex)
{
Scope scope(engine);
@@ -1214,61 +1439,71 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value
return engine->newArrayObject(values, length)->asReturnedValue();
}
-ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)
+ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, int argc, const QV4::Value *args)
{
Scope scope(engine);
- QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId];
- ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype()));
+ Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]);
+ ScopedObject o(scope, engine->newObject(klass->d()));
- {
- bool needSparseArray = arrayGetterSetterCountAndFlags >> 30;
- if (needSparseArray)
- o->initSparseArray();
- }
+ Q_ASSERT(uint(argc) >= klass->d()->size);
- for (uint i = 0; i < klass->size; ++i)
+ for (uint i = 0; i < klass->d()->size; ++i)
o->setProperty(i, *args++);
- if (arrayValueCount > 0) {
- ScopedValue entry(scope);
- for (int i = 0; i < arrayValueCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- entry = *args++;
- o->arraySet(idx, entry);
- }
- }
+ Q_ASSERT((argc - klass->d()->size) % 3 == 0);
+ int additionalArgs = (argc - int(klass->d()->size))/3;
+
+ if (!additionalArgs)
+ return o->asReturnedValue();
- uint arrayGetterSetterCount = arrayGetterSetterCountAndFlags & ((1 << 30) - 1);
- if (arrayGetterSetterCount > 0) {
- ScopedProperty pd(scope);
- for (uint i = 0; i < arrayGetterSetterCount; ++i) {
- uint idx = args->toUInt32();
- ++args;
- pd->value = *args;
- ++args;
- pd->set = *args;
- ++args;
- o->arraySet(idx, pd, Attr_Accessor);
+ Scoped<StringOrSymbol> name(scope);
+ ScopedProperty pd(scope);
+ for (int i = 0; i < additionalArgs; ++i) {
+ Q_ASSERT(args->isInteger());
+ ObjectLiteralArgument arg = ObjectLiteralArgument(args->integerValue());
+ name = args[1].toPropertyKey(engine);
+ if (engine->hasException)
+ return Encode::undefined();
+ Q_ASSERT(arg == ObjectLiteralArgument::Value || args[2].isFunctionObject());
+ if (arg == ObjectLiteralArgument::Value || arg == ObjectLiteralArgument::Getter) {
+ pd->value = args[2];
+ pd->set = Primitive::emptyValue();
+ } else {
+ pd->value = Primitive::emptyValue();
+ pd->set = args[2];
}
- }
+ bool ok = o->__defineOwnProperty__(scope.engine, name, pd, (arg == ObjectLiteralArgument::Value ? Attr_Data : Attr_Accessor));
+ if (!ok)
+ return engine->throwTypeError();
+ args += 3;
+ }
return o.asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine)
{
Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext);
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject];
- return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject);
+ return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine)
{
- QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject];
- return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->objectPrototype(), engine->currentStackFrame)->asReturnedValue();
+ Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject);
+ return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue();
}
+QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex)
+{
+ const Value *values = engine->currentStackFrame->originalArguments + argIndex;
+ int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex;
+ if (nValues <= 0)
+ return engine->newArrayObject(0)->asReturnedValue();
+ return engine->newArrayObject(values, nValues)->asReturnedValue();
+}
+
+
ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine)
{
Heap::QmlContext *ctx = engine->qmlContext();
@@ -1485,24 +1720,133 @@ ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right)
return Encode(r);
}
+struct LazyScope
+{
+ ExecutionEngine *engine = nullptr;
+ Value *stackMark = nullptr;
+ ~LazyScope() {
+ if (engine)
+ engine->jsStackTop = stackMark;
+ }
+ template <typename T>
+ void set(Value **scopedValue, T value, ExecutionEngine *e) {
+ if (!engine) {
+ engine = e;
+ stackMark = engine->jsStackTop;
+ }
+ if (!*scopedValue)
+ *scopedValue = e->jsAlloca(1);
+ **scopedValue = value;
+ }
+};
+
Bool Runtime::method_compareEqual(const Value &left, const Value &right)
{
TRACE2(left, right);
- if (left.rawValue() == right.rawValue())
- // NaN != NaN
- return !left.isNaN();
+ Value lhs = left;
+ Value rhs = right;
- if (left.type() == right.type()) {
- if (left.isDouble() && left.doubleValue() == 0 && right.doubleValue() == 0)
- return true; // this takes care of -0 == +0 (which obviously have different raw values)
- if (!left.isManaged())
- return false;
- if (left.isString() == right.isString())
- return left.cast<Managed>()->isEqualTo(right.cast<Managed>());
+#ifndef V4_BOOTSTRAP
+ LazyScope scope;
+ Value *lhsGuard = nullptr;
+ Value *rhsGuard = nullptr;
+#endif
+
+ redo:
+ if (lhs.asReturnedValue() == rhs.asReturnedValue())
+ return !lhs.isNaN();
+
+ int lt = lhs.quickType();
+ int rt = rhs.quickType();
+ if (rt < lt) {
+ qSwap(lhs, rhs);
+ qSwap(lt, rt);
}
- return RuntimeHelpers::equalHelper(left, right);
+ switch (lt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (lhs.isUndefined())
+ return rhs.isNullOrUndefined();
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ // LHS: Managed
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ if (rhs.isUndefined())
+ return false;
+ Q_FALLTHROUGH();
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3: {
+#ifndef V4_BOOTSTRAP
+ // RHS: Managed
+ Heap::Base *l = lhs.m();
+ Heap::Base *r = rhs.m();
+ Q_ASSERT(l);
+ Q_ASSERT(r);
+ if (l->internalClass->vtable->isStringOrSymbol == r->internalClass->vtable->isStringOrSymbol)
+ return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
+ if (l->internalClass->vtable->isStringOrSymbol) {
+ scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine);
+ rhs = rhsGuard->asReturnedValue();
+ break;
+ } else {
+ Q_ASSERT(r->internalClass->vtable->isStringOrSymbol);
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ break;
+ }
+#endif
+ return false;
+ }
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return false;
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ rhs = Primitive::fromDouble(rhs.int_32());
+ // fall through
+ default: // double
+#ifndef V4_BOOTSTRAP
+ if (lhs.m()->internalClass->vtable->isStringOrSymbol) {
+ return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false;
+ } else {
+ scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine);
+ lhs = lhsGuard->asReturnedValue();
+ }
+#else
+ Q_UNIMPLEMENTED();
+#endif
+ }
+ goto redo;
+ case QV4::Value::QT_Empty:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Null:
+ return rhs.isNull();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ switch (rt) {
+ case QV4::Value::QT_ManagedOrUndefined:
+ case QV4::Value::QT_ManagedOrUndefined1:
+ case QV4::Value::QT_ManagedOrUndefined2:
+ case QV4::Value::QT_ManagedOrUndefined3:
+ case QV4::Value::QT_Empty:
+ case QV4::Value::QT_Null:
+ Q_UNREACHABLE();
+ case QV4::Value::QT_Bool:
+ case QV4::Value::QT_Int:
+ return lhs.int_32() == rhs.int_32();
+ default: // double
+ return lhs.int_32() == rhs.doubleValue();
+ }
+ default: // double
+ Q_ASSERT(rhs.isDouble());
+ return lhs.doubleValue() == rhs.doubleValue();
+ }
}
ReturnedValue Runtime::method_equal(const Value &left, const Value &right)
diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h
index 3a26c23990..72af90d1dc 100644
--- a/src/qml/jsruntime/qv4runtime_p.h
+++ b/src/qml/jsruntime/qv4runtime_p.h
@@ -100,6 +100,7 @@ enum TypeHint {
struct Q_QML_PRIVATE_EXPORT RuntimeHelpers {
static ReturnedValue objectDefaultValue(const Object *object, int typeHint);
static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint);
+ static ReturnedValue ordinaryToPrimitive(ExecutionEngine *engine, const Object *object, String *typeHint);
static double stringToNumber(const QString &s);
static Heap::String *stringFromNumber(ExecutionEngine *engine, double number);
diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h
index 91232256a9..09f6a65dde 100644
--- a/src/qml/jsruntime/qv4runtimeapi_p.h
+++ b/src/qml/jsruntime/qv4runtimeapi_p.h
@@ -99,9 +99,11 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, callElement, (ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc)) \
F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
F(ReturnedValue, callPossiblyDirectEval, (ExecutionEngine *engine, Value *argv, int argc)) \
+ F(ReturnedValue, callWithSpread, (ExecutionEngine *engine, const Value &func, const Value &thisObject, Value *argv, int argc)) \
\
/* construct */ \
F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
+ F(ReturnedValue, constructWithSpread, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \
\
/* load & store */ \
F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \
@@ -117,15 +119,17 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \
\
/* delete */ \
- F(bool, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \
- F(bool, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \
- F(bool, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \
+ F(bool, deleteProperty, (ExecutionEngine *engine, const Value &base, const Value &index)) \
F(bool, deleteName, (ExecutionEngine *engine, int nameIndex)) \
\
/* exceptions & scopes */ \
F(void, throwException, (ExecutionEngine *engine, const Value &value)) \
- F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \
- F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createWithContext, (ExecutionEngine *, Value *jsStackFrame)) \
+ F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex)) \
+ F(ReturnedValue, createBlockContext, (ExecutionContext *parent, int index)) \
+ F(ReturnedValue, createScriptContext, (ExecutionEngine *engine, int index)) \
+ F(ReturnedValue, cloneBlockContext, (ExecutionContext *previous)) \
+ F(ReturnedValue, popScriptContext, (ExecutionEngine *engine)) \
\
/* closures */ \
F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \
@@ -134,14 +138,17 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> {
F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \
F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \
F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \
+ F(ReturnedValue, createRestParameter, (ExecutionEngine *engine, int argIndex)) \
\
/* literals */ \
F(ReturnedValue, arrayLiteral, (ExecutionEngine *engine, Value *values, uint length)) \
- F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, const Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags)) \
+ F(ReturnedValue, objectLiteral, (ExecutionEngine *engine, int classId, int argc, const Value *args)) \
\
- /* foreach */ \
- F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \
- F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \
+ /* for-in, for-of and array destructuring */ \
+ F(ReturnedValue, getIterator, (ExecutionEngine *engine, const Value &in, int iterator)) \
+ F(ReturnedValue, iteratorNext, (ExecutionEngine *engine, const Value &iterator, Value *value)) \
+ F(ReturnedValue, iteratorClose, (ExecutionEngine *engine, const Value &iterator, const Value &done)) \
+ F(ReturnedValue, destructureRestElement, (ExecutionEngine *engine, const Value &iterator)) \
\
/* unary operators */ \
F(ReturnedValue, uMinus, (const Value &value)) \
diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp
index fe18ddf9ed..9866966936 100644
--- a/src/qml/jsruntime/qv4runtimecodegen.cpp
+++ b/src/qml/jsruntime/qv4runtimecodegen.cpp
@@ -52,13 +52,16 @@ void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName,
_module->finalUrl = fileName;
_context = nullptr;
- Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode);
+ Compiler::ScanFunctions scan(this, sourceCode, Compiler::ContextType::Global);
// fake a global environment
- scan.enterEnvironment(nullptr, Compiler::FunctionCode);
+ scan.enterEnvironment(nullptr, Compiler::ContextType::Function, QString());
scan(ast);
scan.leaveEnvironment();
- int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : nullptr);
+ if (hasError)
+ return;
+
+ int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body);
_module->rootContext = _module->functions.at(index);
}
diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h
index bb20f384b3..73ee17cd40 100644
--- a/src/qml/jsruntime/qv4scopedvalue_p.h
+++ b/src/qml/jsruntime/qv4scopedvalue_p.h
@@ -117,8 +117,45 @@ struct Scope {
engine->jsStackTop = mark;
}
- QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const {
- return engine->jsAlloca(nValues);
+ enum AllocMode {
+ Undefined,
+ Empty,
+ /* Be careful when using Uninitialized, the stack has to be fully initialized before calling into the memory manager again */
+ Uninitialized
+ };
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc(int nValues) const
+ {
+ Value *ptr = engine->jsAlloca(nValues);
+ switch (mode) {
+ case Undefined:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Primitive::undefinedValue();
+ break;
+ case Empty:
+ for (int i = 0; i < nValues; ++i)
+ ptr[i] = Primitive::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
+ }
+ template <AllocMode mode = Undefined>
+ QML_NEARLY_ALWAYS_INLINE Value *alloc() const
+ {
+ Value *ptr = engine->jsAlloca(1);
+ switch (mode) {
+ case Undefined:
+ *ptr = Primitive::undefinedValue();
+ break;
+ case Empty:
+ *ptr = Primitive::emptyValue();
+ break;
+ case Uninitialized:
+ break;
+ }
+ return ptr;
}
bool hasException() const {
@@ -136,31 +173,31 @@ struct ScopedValue
{
ScopedValue(const Scope &scope)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(0);
}
ScopedValue(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = v;
}
ScopedValue(const Scope &scope, Heap::Base *o)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setM(o);
}
ScopedValue(const Scope &scope, Managed *m)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(m->asReturnedValue());
}
ScopedValue(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsStackTop++;
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(v);
}
@@ -214,66 +251,66 @@ struct Scoped
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Undefined>();
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, Heap::Base *o)
{
Value v;
v = o;
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ScopedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v.ptr->as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, v));
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const Value *v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(v ? v->as<T>() : nullptr);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const T *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(t);
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, typename T::Data *t)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
*ptr = t;
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
setPointer(QV4::Value::fromReturnedValue(v).as<T>());
}
QML_NEARLY_ALWAYS_INLINE Scoped(const Scope &scope, const ReturnedValue &v, ConvertType)
{
- ptr = scope.engine->jsAlloca(1);
+ ptr = scope.alloc<Scope::Uninitialized>();
ptr->setRawValue(value_convert<T>(scope.engine, QV4::Value::fromReturnedValue(v)));
}
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 5cd62c90f1..efd528860f 100644
--- a/src/qml/jsruntime/qv4script.cpp
+++ b/src/qml/jsruntime/qv4script.cpp
@@ -60,7 +60,7 @@
using namespace QV4;
-Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit)
+Script::Script(ExecutionEngine *v4, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit)
: line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false)
, compilationUnit(compilationUnit), vmFunction(nullptr), parseAsBinding(true)
{
@@ -128,7 +128,7 @@ void Script::parse()
RuntimeCodegen cg(v4, &jsGenerator, strictMode);
if (inheritContext)
cg.setUseFastLookups(false);
- cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, compilationMode);
+ cg.generateFromProgram(sourceFile, sourceFile, sourceCode, program, &module, contextType);
if (v4->hasException)
return;
@@ -171,19 +171,16 @@ Function *Script::function()
return vmFunction;
}
-QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors, Directives *directivesCollector)
+ QList<QQmlError> *reportedErrors)
{
using namespace QV4::Compiler;
using namespace QQmlJS::AST;
- Engine ee;
- if (directivesCollector)
- ee.setDirectives(directivesCollector);
- Lexer lexer(&ee);
+ Lexer lexer(jsEngine);
lexer.setCode(source, /*line*/1, /*qml mode*/false);
- Parser parser(&ee);
+ Parser parser(jsEngine);
parser.parseProgram();
@@ -219,7 +216,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compi
Codegen cg(unitGenerator, /*strict mode*/false);
cg.setUseFastLookups(false);
- cg.generateFromProgram(fileName, finalUrl, source, program, module, GlobalCode);
+ cg.generateFromProgram(fileName, finalUrl, source, program, module, ContextType::Global);
errors = cg.qmlErrors();
if (!errors.isEmpty()) {
if (reportedErrors)
diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h
index b4ac150044..e7189664e2 100644
--- a/src/qml/jsruntime/qv4script_p.h
+++ b/src/qml/jsruntime/qv4script_p.h
@@ -62,12 +62,16 @@ QT_BEGIN_NAMESPACE
class QQmlContextData;
+namespace QQmlJS {
+class Engine;
+}
+
namespace QV4 {
struct Q_QML_EXPORT Script {
- Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
+ Script(ExecutionContext *scope, QV4::Compiler::ContextType mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
- , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode)
+ , context(scope), strictMode(false), inheritContext(false), parsed(false), contextType(mode)
, vmFunction(nullptr), parseAsBinding(false) {}
Script(ExecutionEngine *engine, QmlContext *qml, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0)
: sourceFile(source), line(line), column(column), sourceCode(sourceCode)
@@ -76,7 +80,7 @@ struct Q_QML_EXPORT Script {
if (qml)
qmlContext.set(engine, *qml);
}
- Script(ExecutionEngine *engine, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit);
+ Script(ExecutionEngine *engine, QmlContext *qml, const QQmlRefPointer<CompiledData::CompilationUnit> &compilationUnit);
~Script();
QString sourceFile;
int line;
@@ -86,7 +90,7 @@ struct Q_QML_EXPORT Script {
bool strictMode;
bool inheritContext;
bool parsed;
- QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode;
+ QV4::Compiler::ContextType contextType = QV4::Compiler::ContextType::Eval;
QV4::PersistentValue qmlContext;
QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit;
Function *vmFunction;
@@ -97,10 +101,9 @@ struct Q_QML_EXPORT Script {
Function *function();
- static QQmlRefPointer<CompiledData::CompilationUnit> precompile(
- QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator,
+ static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, QQmlJS::Engine *jsEngine, Compiler::JSUnitGenerator *unitGenerator,
const QString &fileName, const QString &finalUrl, const QString &source,
- QList<QQmlError> *reportedErrors = nullptr, QQmlJS::Directives *directivesCollector = nullptr);
+ QList<QQmlError> *reportedErrors = nullptr);
static Script *createFromFileOrCache(ExecutionEngine *engine, QmlContext *qmlContext, const QString &fileName, const QUrl &originalUrl, QString *error);
static ReturnedValue evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext);
diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp
index 7d29d0b517..f0aa39b786 100644
--- a/src/qml/jsruntime/qv4sequenceobject.cpp
+++ b/src/qml/jsruntime/qv4sequenceobject.cpp
@@ -227,7 +227,7 @@ namespace Heap {
template <typename Container>
struct QQmlSequence : Object {
void init(const Container &container);
- void init(QObject *object, int propertyIndex);
+ void init(QObject *object, int propertyIndex, bool readOnly);
void destroy() {
delete container;
object.destroy();
@@ -237,7 +237,8 @@ struct QQmlSequence : Object {
mutable Container *container;
QQmlQPointer<QObject> object;
int propertyIndex;
- bool isReference;
+ bool isReference : 1;
+ bool isReadOnly : 1;
};
}
@@ -294,6 +295,9 @@ public:
return false;
}
+ if (d()->isReadOnly)
+ return false;
+
if (d()->isReference) {
if (!d()->object)
return false;
@@ -366,6 +370,8 @@ public:
/* Qt containers have int (rather than uint) allowable indexes. */
if (index > INT_MAX)
return false;
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
return false;
@@ -432,11 +438,13 @@ public:
const QV4::Value *m_compareFn;
};
- void sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ bool sort(const FunctionObject *f, const Value *, const Value *argv, int argc)
{
+ if (d()->isReadOnly)
+ return false;
if (d()->isReference) {
if (!d()->object)
- return;
+ return false;
loadReference();
}
@@ -450,6 +458,8 @@ public:
if (d()->isReference)
storeReference();
+
+ return true;
}
static QV4::ReturnedValue method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
@@ -480,6 +490,10 @@ public:
generateWarning(scope.engine, QLatin1String("Index out of range during length set"));
RETURN_UNDEFINED();
}
+
+ if (This->d()->isReadOnly)
+ THROW_TYPE_ERROR();
+
/* Read the sequence from the QObject property if we're a reference */
if (This->d()->isReference) {
if (!This->d()->object)
@@ -572,6 +586,7 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
this->container = new Container(container);
propertyIndex = -1;
isReference = false;
+ isReadOnly = false;
object.init();
QV4::Scope scope(internalClass->engine);
@@ -581,12 +596,13 @@ void Heap::QQmlSequence<Container>::init(const Container &container)
}
template <typename Container>
-void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex)
+void Heap::QQmlSequence<Container>::init(QObject *object, int propertyIndex, bool readOnly)
{
Object::init();
this->container = new Container;
this->propertyIndex = propertyIndex;
isReference = true;
+ this->isReadOnly = readOnly;
this->object.init(object);
QV4::Scope scope(internalClass->engine);
QV4::Scoped<QV4::QQmlSequence<Container> > o(scope, this);
@@ -668,7 +684,8 @@ ReturnedValue SequencePrototype::method_sort(const FunctionObject *b, const Valu
#define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \
if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \
- s->sort(b, thisObject, argv, argc); \
+ if (!s->sort(b, thisObject, argv, argc)) \
+ THROW_TYPE_ERROR(); \
} else
FOREACH_QML_SEQUENCE_TYPE(CALL_SORT)
@@ -691,11 +708,11 @@ bool SequencePrototype::isSequenceType(int sequenceTypeId)
#define NEW_REFERENCE_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(object, propertyIndex)); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(object, propertyIndex, readOnly)); \
return obj.asReturnedValue(); \
} else
-ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool *succeeded)
+ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int sequenceType, QObject *object, int propertyIndex, bool readOnly, bool *succeeded)
{
QV4::Scope scope(engine);
// This function is called when the property is a QObject Q_PROPERTY of
@@ -709,7 +726,7 @@ ReturnedValue SequencePrototype::newSequence(QV4::ExecutionEngine *engine, int s
#define NEW_COPY_SEQUENCE(ElementType, ElementTypeName, SequenceType, unused) \
if (sequenceType == qMetaTypeId<SequenceType>()) { \
- QV4::ScopedObject obj(scope, engine->memoryManager->allocObject<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
+ QV4::ScopedObject obj(scope, engine->memoryManager->allocate<QQml##ElementTypeName##List>(v.value<SequenceType >())); \
return obj.asReturnedValue(); \
} else
diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h
index e9bef2f604..da71215bed 100644
--- a/src/qml/jsruntime/qv4sequenceobject_p.h
+++ b/src/qml/jsruntime/qv4sequenceobject_p.h
@@ -59,6 +59,8 @@
#include "qv4context_p.h"
#include "qv4string_p.h"
+QT_REQUIRE_CONFIG(qml_sequence_object);
+
QT_BEGIN_NAMESPACE
namespace QV4 {
@@ -72,7 +74,7 @@ struct SequencePrototype : public QV4::Object
static ReturnedValue method_sort(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static bool isSequenceType(int sequenceTypeId);
- static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded);
+ static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool readOnly, bool *succeeded);
static ReturnedValue fromVariant(QV4::ExecutionEngine *engine, const QVariant& v, bool *succeeded);
static int metaTypeForSequence(const Object *object);
static QVariant toVariant(Object *object);
diff --git a/src/qml/jsruntime/qv4serialize.cpp b/src/qml/jsruntime/qv4serialize.cpp
index 14def49d0a..31b51cbfe3 100644
--- a/src/qml/jsruntime/qv4serialize.cpp
+++ b/src/qml/jsruntime/qv4serialize.cpp
@@ -40,13 +40,17 @@
#include "qv4serialize_p.h"
#include <private/qv8engine_p.h>
+#if QT_CONFIG(qml_list_model)
#include <private/qqmllistmodel_p.h>
#include <private/qqmllistmodelworkeragent_p.h>
+#endif
#include <private/qv4value_p.h>
#include <private/qv4dateobject_p.h>
#include <private/qv4regexpobject_p.h>
+#if QT_CONFIG(qml_sequence_object)
#include <private/qv4sequenceobject_p.h>
+#endif
#include <private/qv4objectproto_p.h>
#include <private/qv4qobjectwrapper_p.h>
@@ -82,8 +86,12 @@ enum Type {
WorkerNumber,
WorkerDate,
WorkerRegexp,
+#if QT_CONFIG(qml_list_model)
WorkerListModel,
+#endif
+#if QT_CONFIG(qml_sequence_object)
WorkerSequence
+#endif
};
static inline quint32 valueheader(Type type, quint32 size = 0)
@@ -228,6 +236,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
} else if (const QObjectWrapper *qobjectWrapper = v.as<QV4::QObjectWrapper>()) {
// XXX TODO: Generalize passing objects between the main thread and worker scripts so
// that others can trivially plug in their elements.
+#if QT_CONFIG(qml_list_model)
QQmlListModel *lm = qobject_cast<QQmlListModel *>(qobjectWrapper->object());
if (lm && lm->agent()) {
QQmlListModelWorkerAgent *agent = lm->agent();
@@ -236,9 +245,13 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
push(data, (void *)agent);
return;
}
+#else
+ Q_UNUSED(qobjectWrapper);
+#endif
// No other QObject's are allowed to be sent
push(data, valueheader(WorkerUndefined));
} else if (const Object *o = v.as<Object>()) {
+#if QT_CONFIG(qml_sequence_object)
if (o->isListType()) {
// valid sequence. we generate a length (sequence length + 1 for the sequence type)
uint seqLength = ScopedValue(scope, o->get(engine->id_length()))->toUInt32();
@@ -256,6 +269,7 @@ void Serialize::serialize(QByteArray &data, const QV4::Value &v, ExecutionEngine
return;
}
+#endif
// regular object
QV4::ScopedValue val(scope, v);
@@ -353,6 +367,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
data += ALIGN(length * sizeof(quint16));
return Encode(engine->newRegExpObject(pattern, flags));
}
+#if QT_CONFIG(qml_list_model)
case WorkerListModel:
{
void *ptr = popPtr(data);
@@ -369,6 +384,8 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
agent->setEngine(engine);
return rv->asReturnedValue();
}
+#endif
+#if QT_CONFIG(qml_sequence_object)
case WorkerSequence:
{
ScopedValue value(scope);
@@ -387,6 +404,7 @@ ReturnedValue Serialize::deserialize(const char *&data, ExecutionEngine *engine)
QVariant seqVariant = QV4::SequencePrototype::toVariant(array, sequenceType, &succeeded);
return QV4::SequencePrototype::fromVariant(engine, seqVariant, &succeeded);
}
+#endif
}
Q_ASSERT(!"Unreachable");
return QV4::Encode::undefined();
diff --git a/src/qml/jsruntime/qv4setiterator.cpp b/src/qml/jsruntime/qv4setiterator.cpp
new file mode 100644
index 0000000000..715a4e30d5
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator.cpp
@@ -0,0 +1,100 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4setiterator_p.h>
+#include <private/qv4setobject_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetIteratorObject);
+
+void SetIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("Set Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue SetIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const SetIteratorObject *thisObject = that->as<SetIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not a Set Iterator instance"));
+
+ Scoped<SetObject> s(scope, thisObject->d()->iteratedSet);
+ quint32 index = thisObject->d()->setNextIndex;
+ IteratorKind itemKind = thisObject->d()->iterationKind;
+
+
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ Scoped<ArrayObject> arr(scope, s->d()->setArray);
+
+ while (index < arr->getLength()) {
+ ScopedValue e(scope, arr->getIndexed(index));
+ index += 1;
+ thisObject->d()->setNextIndex = index;
+
+ if (itemKind == KeyValueIteratorKind) {
+ // Return CreateIterResultObject(CreateArrayFromList(« e, e »), false).
+ ScopedArrayObject resultArray(scope, scope.engine->newArrayObject());
+ resultArray->arrayReserve(2);
+ resultArray->arrayPut(0, e);
+ resultArray->arrayPut(1, e);
+ resultArray->setArrayLengthUnchecked(2);
+
+ return IteratorPrototype::createIterResultObject(scope.engine, resultArray, false);
+ }
+
+ return IteratorPrototype::createIterResultObject(scope.engine, e, false);
+ }
+
+ thisObject->d()->iteratedSet.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+}
+
diff --git a/src/qml/jsruntime/qv4setiterator_p.h b/src/qml/jsruntime/qv4setiterator_p.h
new file mode 100644
index 0000000000..78eda6d57b
--- /dev/null
+++ b/src/qml/jsruntime/qv4setiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SETITERATOR_P_H
+#define QV4SETITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4iterator_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+#define SetIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, Object *, iteratedSet) \
+ Member(class, NoMark, IteratorKind, iterationKind) \
+ Member(class, NoMark, quint32, setNextIndex)
+
+DECLARE_HEAP_OBJECT(SetIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(SetIteratorObject);
+ void init(Object *obj, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedSet.set(engine, obj);
+ this->setNextIndex = 0;
+ }
+};
+
+}
+
+struct SetIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetIteratorObject : Object
+{
+ V4_OBJECT2(SetIteratorObject, Object)
+ Q_MANAGED_TYPE(SetIteratorObject)
+ V4_PROTOTYPE(setIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4SETITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4setobject.cpp b/src/qml/jsruntime/qv4setobject.cpp
new file mode 100644
index 0000000000..4245cd2ea3
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject.cpp
@@ -0,0 +1,297 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+
+#include "qv4setobject_p.h"
+#include "qv4setiterator_p.h"
+#include "qv4symbol_p.h"
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SetCtor);
+DEFINE_OBJECT_VTABLE(SetObject);
+
+void Heap::SetCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Set"));
+}
+
+ReturnedValue SetCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc)
+{
+ Scope scope(f);
+ Scoped<SetObject> a(scope, scope.engine->memoryManager->allocate<SetObject>());
+ Scoped<ArrayObject> arr(scope, scope.engine->newArrayObject());
+ a->d()->setArray.set(scope.engine, arr->d());
+
+ if (argc > 0) {
+ ScopedValue iterable(scope, argv[0]);
+ if (!iterable->isUndefined() && !iterable->isNull()) {
+ ScopedFunctionObject adder(scope, a->get(ScopedString(scope, scope.engine->newString(QString::fromLatin1("add")))));
+ if (!adder) {
+ return scope.engine->throwTypeError();
+ }
+ ScopedObject iter(scope, Runtime::method_getIterator(scope.engine, iterable, true));
+ CHECK_EXCEPTION();
+ if (!iter) {
+ return a.asReturnedValue();
+ }
+
+ Value *nextValue = scope.alloc(1);
+ ScopedValue done(scope);
+ forever {
+ done = Runtime::method_iteratorNext(scope.engine, iter, nextValue);
+ CHECK_EXCEPTION();
+ if (done->toBoolean()) {
+ return a.asReturnedValue();
+ }
+
+ adder->call(a, nextValue, 1);
+ if (scope.engine->hasException) {
+ ScopedValue falsey(scope, Encode(false));
+ return Runtime::method_iteratorClose(scope.engine, iter, falsey);
+ }
+ }
+ }
+ }
+
+ return a.asReturnedValue();
+}
+
+ReturnedValue SetCtor::call(const FunctionObject *f, const Value *, const Value *, int)
+{
+ Scope scope(f);
+ return scope.engine->throwTypeError(QString::fromLatin1("Set requires new"));
+}
+
+void SetPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedObject o(scope);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+ ctor->addSymbolSpecies();
+ defineDefaultProperty(engine->id_constructor(), (o = ctor));
+
+ defineDefaultProperty(QStringLiteral("add"), method_add, 1);
+ defineDefaultProperty(QStringLiteral("clear"), method_clear, 0);
+ defineDefaultProperty(QStringLiteral("delete"), method_delete, 1);
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("forEach"), method_forEach, 1);
+ defineDefaultProperty(QStringLiteral("has"), method_has, 1);
+ defineAccessorProperty(QStringLiteral("size"), method_get_size, nullptr);
+
+ // Per the spec, the value for 'keys' is the same as 'values'.
+ ScopedString valString(scope, scope.engine->newIdentifier(QStringLiteral("values")));
+ ScopedFunctionObject valuesFn(scope, FunctionObject::createBuiltinFunction(engine, valString, SetPrototype::method_values, 0));
+ defineDefaultProperty(QStringLiteral("keys"), valuesFn);
+ defineDefaultProperty(QStringLiteral("values"), valuesFn);
+
+ defineDefaultProperty(engine->symbol_iterator(), valuesFn);
+
+ ScopedString val(scope, engine->newString(QLatin1String("Set")));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), val);
+}
+
+ReturnedValue SetPrototype::method_add(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> arr(scope, that->d()->setArray);
+ ScopedValue sk(scope);
+ qint64 len = arr->getLength();
+
+ for (int i = 0; i < len; ++i) {
+ sk = arr->getIndexed(i);
+ if (sk->sameValueZero(argv[0]))
+ return that.asReturnedValue();
+ }
+
+ sk = argv[0];
+ if (sk->isDouble()) {
+ if (sk->doubleValue() == 0 && std::signbit(sk->doubleValue()))
+ sk = Primitive::fromDouble(+0);
+ }
+
+ arr->putIndexed(len, sk);
+ return that.asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_clear(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> arr(scope, scope.engine->newArrayObject());
+ that->d()->setArray.set(scope.engine, arr->d());
+ return Encode::undefined();
+}
+
+// delete value
+ReturnedValue SetPrototype::method_delete(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> arr(scope, that->d()->setArray);
+ ScopedValue sk(scope);
+ qint64 len = arr->getLength();
+
+ bool found = false;
+ int idx = 0;
+
+ for (; idx < len; ++idx) {
+ sk = arr->getIndexed(idx);
+ if (sk->sameValueZero(argv[0])) {
+ found = true;
+ break;
+ }
+ }
+
+ if (found == true) {
+ Scoped<ArrayObject> newArr(scope, scope.engine->newArrayObject());
+ for (int j = 0, newIdx = 0; j < len; ++j, newIdx++) {
+ if (j == idx) {
+ newIdx--; // skip the entry
+ continue;
+ }
+ newArr->putIndexed(newIdx, ScopedValue(scope, arr->getIndexed(j)));
+ }
+
+ that->d()->setArray.set(scope.engine, newArr->d());
+ return Encode(true);
+ } else {
+ return Encode(false);
+ }
+}
+
+ReturnedValue SetPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue SetPrototype::method_forEach(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ ScopedFunctionObject callbackfn(scope, argv[0]);
+ if (!callbackfn)
+ return scope.engine->throwTypeError();
+
+ ScopedValue thisArg(scope, Primitive::undefinedValue());
+ if (argc > 1)
+ thisArg = ScopedValue(scope, argv[1]);
+
+ Scoped<ArrayObject> arr(scope, that->d()->setArray);
+ ScopedValue sk(scope);
+ qint64 len = arr->getLength();
+
+ Value *arguments = scope.alloc(3);
+ for (int i = 0; i < len; ++i) {
+ sk = arr->getIndexed(i);
+
+ arguments[0] = sk;
+ arguments[1] = sk;
+ arguments[2] = that;
+ callbackfn->call(thisArg, arguments, 3);
+ CHECK_EXCEPTION();
+ }
+ return Encode::undefined();
+}
+
+ReturnedValue SetPrototype::method_has(const FunctionObject *b, const Value *thisObject, const Value *argv, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> arr(scope, that->d()->setArray);
+ ScopedValue sk(scope);
+ qint64 len = arr->getLength();
+
+ for (int i = 0; i < len; ++i) {
+ sk = arr->getIndexed(i);
+ if (sk->sameValueZero(argv[0]))
+ return Encode(true);
+ }
+
+ return Encode(false);
+}
+
+ReturnedValue SetPrototype::method_get_size(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<ArrayObject> arr(scope, that->d()->setArray);
+ qint64 len = arr->getLength();
+ return Encode((uint)len);
+}
+
+ReturnedValue SetPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<SetObject> that(scope, thisObject);
+ if (!that)
+ return scope.engine->throwTypeError();
+
+ Scoped<SetIteratorObject> ao(scope, scope.engine->newSetIteratorObject(that));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
diff --git a/src/qml/jsruntime/qv4setobject_p.h b/src/qml/jsruntime/qv4setobject_p.h
new file mode 100644
index 0000000000..ba0ed6bfe7
--- /dev/null
+++ b/src/qml/jsruntime/qv4setobject_p.h
@@ -0,0 +1,113 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 Crimson AS <info@crimson.no>
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4SETOBJECT_P_H
+#define QV4SETOBJECT_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4objectproto_p.h"
+#include "qv4functionobject_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+namespace Heap {
+
+struct SetCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+#define SetObjectMembers(class, Member) \
+ Member(class, Pointer, ArrayObject *, setArray)
+
+DECLARE_HEAP_OBJECT(SetObject, Object) {
+ DECLARE_MARKOBJECTS(SetObject);
+ void init() { Object::init(); }
+};
+
+}
+
+struct SetCtor: FunctionObject
+{
+ V4_OBJECT2(SetCtor, FunctionObject)
+
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SetObject : Object
+{
+ V4_OBJECT2(SetObject, Object)
+ V4_PROTOTYPE(setPrototype)
+};
+
+struct SetPrototype : Object
+{
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_add(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_clear(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_delete(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_forEach(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_has(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_get_size(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+
+} // namespace QV4
+
+
+QT_END_NAMESPACE
+
+#endif // QV4SETOBJECT_P_H
diff --git a/src/qml/jsruntime/qv4sparsearray.cpp b/src/qml/jsruntime/qv4sparsearray.cpp
index 2a3e28bf63..8930c9a94d 100644
--- a/src/qml/jsruntime/qv4sparsearray.cpp
+++ b/src/qml/jsruntime/qv4sparsearray.cpp
@@ -395,7 +395,7 @@ void SparseArray::freeTree(SparseArrayNode *root, int alignment)
SparseArray::SparseArray()
: numEntries(0)
{
- freeList = Primitive::emptyValue(UINT_MAX).asReturnedValue();
+ freeList = Encode(-1);
header.p = 0;
header.left = nullptr;
header.right = nullptr;
diff --git a/src/qml/jsruntime/qv4sparsearray_p.h b/src/qml/jsruntime/qv4sparsearray_p.h
index 51869b259f..c1e50c8dcf 100644
--- a/src/qml/jsruntime/qv4sparsearray_p.h
+++ b/src/qml/jsruntime/qv4sparsearray_p.h
@@ -52,6 +52,7 @@
//
#include "qv4global_p.h"
+#include "qv4value_p.h"
#include <QtCore/qlist.h>
//#define Q_MAP_DEBUG
@@ -151,7 +152,7 @@ struct Q_QML_EXPORT SparseArray
SparseArray(const SparseArray &other);
- ReturnedValue freeList;
+ Value freeList;
private:
SparseArray &operator=(const SparseArray &other);
diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp
index 447992ebec..27c73a2b77 100644
--- a/src/qml/jsruntime/qv4string.cpp
+++ b/src/qml/jsruntime/qv4string.cpp
@@ -52,8 +52,17 @@ using namespace QV4;
#ifndef V4_BOOTSTRAP
+void Heap::StringOrSymbol::markObjects(Heap::Base *that, MarkStack *markStack)
+{
+ StringOrSymbol *s = static_cast<StringOrSymbol *>(that);
+ Heap::Base *id = s->identifier.asHeapObject();
+ if (id)
+ id->mark(markStack);
+}
+
void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
{
+ StringOrSymbol::markObjects(that, markStack);
String *s = static_cast<String *>(that);
if (s->subtype < StringType_Complex)
return;
@@ -68,6 +77,7 @@ void Heap::String::markObjects(Heap::Base *that, MarkStack *markStack)
}
}
+DEFINE_MANAGED_VTABLE(StringOrSymbol);
DEFINE_MANAGED_VTABLE(String);
@@ -76,7 +86,7 @@ bool String::isEqualTo(Managed *t, Managed *o)
if (t == o)
return true;
- if (!o->d()->vtable()->isString)
+ if (!o->vtable()->isString)
return false;
return static_cast<String *>(t)->isEqualTo(static_cast<String *>(o));
@@ -128,7 +138,8 @@ void Heap::ComplexString::init(Heap::String *ref, int from, int len)
this->len = len;
}
-void Heap::String::destroy() {
+void Heap::StringOrSymbol::destroy()
+{
if (text) {
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(-text->size) * (int)sizeof(QChar));
if (!text->ref.deref())
@@ -174,7 +185,7 @@ void Heap::String::simplifyString() const
text = result.data_ptr();
text->ref.ref();
const ComplexString *cs = static_cast<const ComplexString *>(this);
- identifier = nullptr;
+ identifier = Identifier::invalid();
cs->left = cs->right = nullptr;
internalClass->engine->memoryManager->changeUnmanagedHeapSizeUsage(qptrdiff(text->size) * (qptrdiff)sizeof(QChar));
@@ -227,10 +238,12 @@ void Heap::String::append(const String *data, QChar *ch)
}
}
-void Heap::String::createHashValue() const
+void Heap::StringOrSymbol::createHashValue() const
{
- if (!text)
- simplifyString();
+ if (!text) {
+ Q_ASSERT(internalClass->vtable->isString);
+ static_cast<const Heap::String *>(this)->simplifyString();
+ }
Q_ASSERT(text);
const QChar *ch = reinterpret_cast<const QChar *>(text->data());
const QChar *end = ch + text->size;
@@ -248,4 +261,3 @@ uint String::toArrayIndex(const QString &str)
{
return QV4::String::toArrayIndex(str.constData(), str.constData() + str.length());
}
-
diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h
index 5466cc274d..0deb542ea2 100644
--- a/src/qml/jsruntime/qv4string_p.h
+++ b/src/qml/jsruntime/qv4string_p.h
@@ -64,9 +64,10 @@ struct Identifier;
namespace Heap {
-struct Q_QML_PRIVATE_EXPORT String : Base {
- static void markObjects(Heap::Base *that, MarkStack *markStack);
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : Base
+{
enum StringType {
+ StringType_Symbol,
StringType_Regular,
StringType_ArrayIndex,
StringType_Unknown,
@@ -75,13 +76,20 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
StringType_Complex = StringType_AddedString
};
-#ifndef V4_BOOTSTRAP
- void init(const QString &text);
+ mutable QStringData *text;
+ mutable Identifier identifier;
+ mutable uint subtype;
+ mutable uint stringHash;
+
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
void destroy();
- void simplifyString() const;
- int length() const;
- std::size_t retainedTextSize() const {
- return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+
+ inline QString toQString() const {
+ if (!text)
+ return QString();
+ QStringDataPtr ptr = { text };
+ text->ref.ref();
+ return QString(ptr);
}
void createHashValue() const;
inline unsigned hashValue() const {
@@ -91,6 +99,22 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
return stringHash;
}
+};
+
+struct Q_QML_PRIVATE_EXPORT String : StringOrSymbol {
+ static void markObjects(Heap::Base *that, MarkStack *markStack);
+
+#ifndef V4_BOOTSTRAP
+ const VTable *vtable() const {
+ return internalClass->vtable;
+ }
+
+ void init(const QString &text);
+ void simplifyString() const;
+ int length() const;
+ std::size_t retainedTextSize() const {
+ return subtype >= StringType_Complex ? 0 : (std::size_t(text->size) * sizeof(QChar));
+ }
inline QString toQString() const {
if (subtype >= StringType_Complex)
simplifyString();
@@ -104,7 +128,7 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
if (hashValue() != other->hashValue())
return false;
Q_ASSERT(subtype < StringType_Complex);
- if (identifier && identifier == other->identifier)
+ if (identifier.isValid() && identifier == other->identifier)
return true;
if (subtype == Heap::String::StringType_ArrayIndex && other->subtype == Heap::String::StringType_ArrayIndex)
return true;
@@ -114,10 +138,6 @@ struct Q_QML_PRIVATE_EXPORT String : Base {
bool startsWithUpper() const;
- mutable QStringData *text;
- mutable Identifier *identifier;
- mutable uint subtype;
- mutable uint stringHash;
private:
static void append(const String *data, QChar *ch);
#endif
@@ -146,9 +166,27 @@ int String::length() const {
}
-struct Q_QML_PRIVATE_EXPORT String : public Managed {
+struct Q_QML_PRIVATE_EXPORT StringOrSymbol : public Managed {
#ifndef V4_BOOTSTRAP
- V4_MANAGED(String, Managed)
+ V4_MANAGED(StringOrSymbol, Managed)
+ enum {
+ IsStringOrSymbol = true
+ };
+
+ inline void makeIdentifier() const;
+ Identifier identifier() const { return d()->identifier; }
+
+ uint asArrayIndex() const;
+
+ inline QString toQString() const {
+ return d()->toQString();
+ }
+#endif
+};
+
+struct Q_QML_PRIVATE_EXPORT String : public StringOrSymbol {
+#ifndef V4_BOOTSTRAP
+ V4_MANAGED(String, StringOrSymbol)
Q_MANAGED_TYPE(String)
V4_INTERNALCLASS(String)
V4_NEEDS_DESTROY
@@ -187,12 +225,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
}
uint toUInt(bool *ok) const;
- void makeIdentifier() const {
- if (d()->identifier)
- return;
- makeIdentifierImpl();
- }
-
// slow path
Q_NEVER_INLINE void makeIdentifierImpl() const;
@@ -210,8 +242,6 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed {
bool startsWithUpper() const { return d()->startsWithUpper(); }
- Identifier *identifier() const { return d()->identifier; }
-
protected:
static bool isEqualTo(Managed *that, Managed *o);
static uint getLength(const Managed *m);
@@ -254,7 +284,7 @@ public:
uint h = toArrayIndex(ch, end);
if (h != UINT_MAX) {
if (subtype)
- *subtype = Heap::String::StringType_ArrayIndex;
+ *subtype = Heap::StringOrSymbol::StringType_ArrayIndex;
return h;
}
@@ -264,7 +294,7 @@ public:
}
if (subtype)
- *subtype = Heap::String::StringType_Regular;
+ *subtype = (toUInt(ch) == '@') ? Heap::StringOrSymbol::StringType_Symbol : Heap::StringOrSymbol::StringType_Regular;
return h;
}
};
@@ -280,9 +310,29 @@ struct ComplexString : String {
}
};
+inline
+void StringOrSymbol::makeIdentifier() const {
+ if (d()->identifier.isValid())
+ return;
+ Q_ASSERT(isString());
+ static_cast<const String *>(this)->makeIdentifierImpl();
+}
+
+inline
+uint StringOrSymbol::asArrayIndex() const {
+ if (isString())
+ return static_cast<const String *>(this)->asArrayIndex();
+ return UINT_MAX;
+}
+
+template<>
+inline const StringOrSymbol *Value::as() const {
+ return isManaged() && m()->internalClass->vtable->isStringOrSymbol ? static_cast<const String *>(this) : nullptr;
+}
+
template<>
inline const String *Value::as() const {
- return isManaged() && m()->vtable()->isString ? static_cast<const String *>(this) : nullptr;
+ return isManaged() && m()->internalClass->vtable->isString ? static_cast<const String *>(this) : nullptr;
}
template<>
diff --git a/src/qml/jsruntime/qv4stringiterator.cpp b/src/qml/jsruntime/qv4stringiterator.cpp
new file mode 100644
index 0000000000..810ed333e4
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator.cpp
@@ -0,0 +1,95 @@
+/****************************************************************************
+**
+** Copyright (C) 2017 Crimson AS <info@crimson.no>
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qv4iterator_p.h>
+#include <private/qv4stringiterator_p.h>
+#include <private/qv4symbol_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(StringIteratorObject);
+
+void StringIteratorPrototype::init(ExecutionEngine *e)
+{
+ defineDefaultProperty(QStringLiteral("next"), method_next, 0);
+
+ Scope scope(e);
+ ScopedString val(scope, e->newString(QLatin1String("String Iterator")));
+ defineReadonlyConfigurableProperty(e->symbol_toStringTag(), val);
+}
+
+ReturnedValue StringIteratorPrototype::method_next(const FunctionObject *b, const Value *that, const Value *, int)
+{
+ Scope scope(b);
+ const StringIteratorObject *thisObject = that->as<StringIteratorObject>();
+ if (!thisObject)
+ return scope.engine->throwTypeError(QLatin1String("Not an String Iterator instance"));
+
+ ScopedString s(scope, thisObject->d()->iteratedString);
+ if (!s) {
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ quint32 index = thisObject->d()->nextIndex;
+
+ QString str = s->toQString();
+ quint32 len = str.length();
+
+ if (index >= len) {
+ thisObject->d()->iteratedString.set(scope.engine, nullptr);
+ QV4::Value undefined = Primitive::undefinedValue();
+ return IteratorPrototype::createIterResultObject(scope.engine, undefined, true);
+ }
+
+ QChar ch = str.at(index);
+ int num = 1;
+ if (ch.unicode() >= 0xd800 && ch.unicode() <= 0xdbff && index + 1 != len) {
+ ch = str.at(index + 1);
+ if (ch.unicode() >= 0xdc00 && ch.unicode() <= 0xdfff)
+ num = 2;
+ }
+
+ thisObject->d()->nextIndex += num;
+
+ ScopedString resultString(scope, scope.engine->newString(s->toQString().mid(index, num)));
+ return IteratorPrototype::createIterResultObject(scope.engine, resultString, false);
+}
+
diff --git a/src/qml/jsruntime/qv4stringiterator_p.h b/src/qml/jsruntime/qv4stringiterator_p.h
new file mode 100644
index 0000000000..672ccc9963
--- /dev/null
+++ b/src/qml/jsruntime/qv4stringiterator_p.h
@@ -0,0 +1,103 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QV4STRINGITERATOR_P_H
+#define QV4STRINGITERATOR_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4object_p.h"
+#include "qv4string_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+#define StringIteratorObjectMembers(class, Member) \
+ Member(class, Pointer, String *, iteratedString) \
+ Member(class, NoMark, quint32, nextIndex)
+
+DECLARE_HEAP_OBJECT(StringIteratorObject, Object) {
+ DECLARE_MARKOBJECTS(StringIteratorObject);
+ void init(String *str, QV4::ExecutionEngine *engine)
+ {
+ Object::init();
+ this->iteratedString.set(engine, str);
+ this->nextIndex = 0;
+ }
+};
+
+}
+
+struct StringIteratorPrototype : Object
+{
+ V4_PROTOTYPE(iteratorPrototype)
+ void init(ExecutionEngine *engine);
+
+ static ReturnedValue method_next(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct StringIteratorObject : Object
+{
+ V4_OBJECT2(StringIteratorObject, Object)
+ Q_MANAGED_TYPE(StringIteratorObject)
+ V4_PROTOTYPE(stringIteratorPrototype)
+
+ void init(ExecutionEngine *engine);
+};
+
+
+}
+
+QT_END_NAMESPACE
+
+#endif // QV4ARRAYITERATOR_P_H
+
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 61176b3706..3639edac17 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -44,8 +44,10 @@
#include "qv4objectproto_p.h"
#include <private/qv4mm_p.h>
#include "qv4scopedvalue_p.h"
+#include "qv4symbol_p.h"
#include "qv4alloca_p.h"
#include "qv4jscall_p.h"
+#include "qv4stringiterator_p.h"
#include <QtCore/QDateTime>
#include <QtCore/QDebug>
#include <QtCore/QStringList>
@@ -152,16 +154,58 @@ ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value
value = argv[0].toString(v4);
else
value = v4->newString();
+ CHECK_EXCEPTION();
return Encode(v4->newStringObject(value));
}
ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc)
{
ExecutionEngine *v4 = m->engine();
- if (argc)
- return argv[0].toString(v4)->asReturnedValue();
- else
+ if (!argc)
return v4->newString()->asReturnedValue();
+ if (argv[0].isSymbol())
+ return v4->newString(argv[0].symbolValue()->descriptiveString())->asReturnedValue();
+ return argv[0].toString(v4)->asReturnedValue();
+}
+
+ReturnedValue StringCtor::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
+{
+ QString str(argc, Qt::Uninitialized);
+ QChar *ch = str.data();
+ for (int i = 0, ei = argc; i < ei; ++i) {
+ *ch = QChar(argv[i].toUInt16());
+ ++ch;
+ }
+ *ch = 0;
+ return Encode(b->engine()->newString(str));
+}
+
+
+
+ReturnedValue StringCtor::method_fromCodePoint(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ QString result(argc*2, Qt::Uninitialized); // assume worst case
+ QChar *ch = result.data();
+ for (int i = 0; i < argc; ++i) {
+ double num = argv[i].toNumber();
+ if (e->hasException)
+ return Encode::undefined();
+ int cp = static_cast<int>(num);
+ if (cp != num || cp < 0 || cp > 0x10ffff)
+ return e->throwRangeError(QStringLiteral("String.fromCodePoint: argument out of range."));
+ if (cp > 0xffff) {
+ *ch = QChar::highSurrogate(cp);
+ ++ch;
+ *ch = QChar::lowSurrogate(cp);
+ } else {
+ *ch = cp;
+ }
+ ++ch;
+ }
+ *ch = 0;
+ result.truncate(ch - result.constData());
+ return e->newString(result)->asReturnedValue();
}
void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
@@ -169,15 +213,23 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
Scope scope(engine);
ScopedObject o(scope);
+ // need to set this once again, as these were not fully defined when creating the string proto
+ Heap::InternalClass *ic = scope.engine->classes[ExecutionEngine::Class_StringObject]->changePrototype(scope.engine->objectPrototype()->d());
+ d()->internalClass.set(scope.engine, ic);
+ d()->string.set(scope.engine, scope.engine->id_empty()->d());
+ setProperty(scope.engine, Heap::StringObject::LengthPropertyIndex, Primitive::fromInt32(0));
+
ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1));
- ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), method_fromCharCode, 1);
+ ctor->defineDefaultProperty(QStringLiteral("fromCharCode"), StringCtor::method_fromCharCode, 1);
+ ctor->defineDefaultProperty(QStringLiteral("fromCodePoint"), StringCtor::method_fromCodePoint, 1);
defineDefaultProperty(QStringLiteral("constructor"), (o = ctor));
defineDefaultProperty(engine->id_toString(), method_toString);
defineDefaultProperty(engine->id_valueOf(), method_toString); // valueOf and toString are identical
defineDefaultProperty(QStringLiteral("charAt"), method_charAt, 1);
defineDefaultProperty(QStringLiteral("charCodeAt"), method_charCodeAt, 1);
+ defineDefaultProperty(QStringLiteral("codePointAt"), method_codePointAt, 1);
defineDefaultProperty(QStringLiteral("concat"), method_concat, 1);
defineDefaultProperty(QStringLiteral("endsWith"), method_endsWith, 1);
defineDefaultProperty(QStringLiteral("indexOf"), method_indexOf, 1);
@@ -185,6 +237,9 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1);
defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1);
defineDefaultProperty(QStringLiteral("match"), method_match, 1);
+ defineDefaultProperty(QStringLiteral("normalize"), method_normalize, 0);
+ defineDefaultProperty(QStringLiteral("padEnd"), method_padEnd, 1);
+ defineDefaultProperty(QStringLiteral("padStart"), method_padStart, 1);
defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1);
defineDefaultProperty(QStringLiteral("replace"), method_replace, 2);
defineDefaultProperty(QStringLiteral("search"), method_search, 1);
@@ -198,6 +253,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor)
defineDefaultProperty(QStringLiteral("toUpperCase"), method_toUpperCase);
defineDefaultProperty(QStringLiteral("toLocaleUpperCase"), method_toLocaleUpperCase);
defineDefaultProperty(QStringLiteral("trim"), method_trim);
+ defineDefaultProperty(engine->symbol_iterator(), method_iterator);
}
static Heap::String *thisAsString(ExecutionEngine *v4, const QV4::Value *thisObject)
@@ -270,6 +326,29 @@ ReturnedValue StringPrototype::method_charCodeAt(const FunctionObject *b, const
return Encode(qt_qnan());
}
+ReturnedValue StringPrototype::method_codePointAt(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ int index = argc ? argv[0].toInteger() : 0;
+ if (v4->hasException)
+ return QV4::Encode::undefined();
+
+ if (index < 0 || index >= value.size())
+ return Encode::undefined();
+
+ uint first = value.at(index).unicode();
+ if (QChar::isHighSurrogate(first) && index + 1 < value.size()) {
+ uint second = value.at(index + 1).unicode();
+ if (QChar::isLowSurrogate(second))
+ return Encode(QChar::surrogateToUcs4(first, second));
+ }
+ return Encode(first);
+}
+
ReturnedValue StringPrototype::method_concat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -298,12 +377,11 @@ ReturnedValue StringPrototype::method_endsWith(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = value.length();
if (argc > 1)
@@ -323,9 +401,9 @@ ReturnedValue StringPrototype::method_indexOf(const FunctionObject *b, const Val
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -345,12 +423,11 @@ ReturnedValue StringPrototype::method_includes(const FunctionObject *b, const Va
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1) {
@@ -374,9 +451,9 @@ ReturnedValue StringPrototype::method_lastIndexOf(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc)
- searchString = argv[0].toQString();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
double position = argc > 1 ? RuntimeHelpers::toNumber(argv[1]) : +qInf();
if (std::isnan(position))
@@ -455,6 +532,115 @@ ReturnedValue StringPrototype::method_match(const FunctionObject *b, const Value
return a.asReturnedValue();
}
+ReturnedValue StringPrototype::method_normalize(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ const QString value = getThisString(v4, thisObject);
+ if (v4->hasException)
+ return Encode::undefined();
+
+ QString::NormalizationForm form = QString::NormalizationForm_C;
+ if (argc >= 1 && !argv[0].isUndefined()) {
+ QString f = argv[0].toQString();
+ if (v4->hasException)
+ return Encode::undefined();
+ if (f == QLatin1String("NFC"))
+ form = QString::NormalizationForm_C;
+ else if (f == QLatin1String("NFD"))
+ form = QString::NormalizationForm_D;
+ else if (f == QLatin1String("NFKC"))
+ form = QString::NormalizationForm_KC;
+ else if (f == QLatin1String("NFKD"))
+ form = QString::NormalizationForm_KD;
+ else
+ return v4->throwRangeError(QLatin1String("String.prototype.normalize: Invalid normalization form."));
+ }
+ QString normalized = value.normalized(form);
+ return v4->newString(normalized)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
+
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
+
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
+
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString padded = s->toQString();
+ int oldLength = padded.length();
+ int toFill = maxLen - oldLength;
+ padded.resize(maxLen);
+ QChar *ch = padded.data() + oldLength;
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
+ }
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
+}
+
+ReturnedValue StringPrototype::method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc)
+{
+ ExecutionEngine *v4 = f->engine();
+ if (thisObject->isNullOrUndefined())
+ return v4->throwTypeError();
+
+ Scope scope(v4);
+ ScopedString s(scope, thisAsString(v4, thisObject));
+ if (v4->hasException)
+ return Encode::undefined();
+ if (!argc)
+ return s->asReturnedValue();
+
+ int maxLen = argv[0].toInteger();
+ if (maxLen <= s->d()->length())
+ return s->asReturnedValue();
+ QString fillString = (argc > 1 && !argv[1].isUndefined()) ? argv[1].toQString() : QString::fromLatin1(" ");
+ if (v4->hasException)
+ return Encode::undefined();
+
+ if (fillString.isEmpty())
+ return s->asReturnedValue();
+
+ QString original = s->toQString();
+ int oldLength = original.length();
+ int toFill = maxLen - oldLength;
+ QString padded;
+ padded.resize(maxLen);
+ QChar *ch = padded.data();
+ while (toFill) {
+ int copy = qMin(fillString.length(), toFill);
+ memcpy(ch, fillString.constData(), copy*sizeof(QChar));
+ toFill -= copy;
+ ch += copy;
+ }
+ memcpy(ch, original.constData(), oldLength*sizeof(QChar));
+ ch += oldLength;
+ *ch = 0;
+
+ return v4->newString(padded)->asReturnedValue();
+}
+
+
ReturnedValue StringPrototype::method_repeat(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
ExecutionEngine *v4 = b->engine();
@@ -777,12 +963,11 @@ ReturnedValue StringPrototype::method_startsWith(const FunctionObject *b, const
if (v4->hasException)
return QV4::Encode::undefined();
- QString searchString;
- if (argc) {
- if (argv[0].as<RegExpObject>())
- return v4->throwTypeError();
- searchString = argv[0].toQString();
- }
+ if (argc && argv[0].as<RegExpObject>())
+ return v4->throwTypeError();
+ QString searchString = (argc ? argv[0] : Primitive::undefinedValue()).toQString();
+ if (v4->hasException)
+ return Encode::undefined();
int pos = 0;
if (argc > 1)
@@ -892,17 +1077,6 @@ ReturnedValue StringPrototype::method_toLocaleUpperCase(const FunctionObject *b,
return method_toUpperCase(b, thisObject, argv, argc);
}
-ReturnedValue StringPrototype::method_fromCharCode(const FunctionObject *b, const Value *, const Value *argv, int argc)
-{
- QString str(argc, Qt::Uninitialized);
- QChar *ch = str.data();
- for (int i = 0, ei = argc; i < ei; ++i) {
- *ch = QChar(argv[i].toUInt16());
- ++ch;
- }
- return Encode(b->engine()->newString(str));
-}
-
ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
@@ -923,3 +1097,16 @@ ReturnedValue StringPrototype::method_trim(const FunctionObject *b, const Value
return Encode(v4->newString(QString(chars + start, end - start + 1)));
}
+
+
+
+ReturnedValue StringPrototype::method_iterator(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ ScopedString s(scope, thisObject->toString(scope.engine));
+ if (!s || thisObject->isNullOrUndefined())
+ return scope.engine->throwTypeError();
+
+ Scoped<StringIteratorObject> si(scope, scope.engine->memoryManager->allocate<StringIteratorObject>(s->d(), scope.engine));
+ return si->asReturnedValue();
+}
diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h
index 7d25678b61..407d617d57 100644
--- a/src/qml/jsruntime/qv4stringobject_p.h
+++ b/src/qml/jsruntime/qv4stringobject_p.h
@@ -70,6 +70,8 @@ DECLARE_HEAP_OBJECT(StringObject, Object) {
LengthPropertyIndex = 0
};
+ void init(bool /*don't init*/)
+ { Object::init(); }
void init();
void init(const QV4::String *string);
@@ -108,6 +110,9 @@ struct StringCtor: FunctionObject
static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_fromCodePoint(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
struct StringPrototype: StringObject
@@ -118,6 +123,7 @@ struct StringPrototype: StringObject
static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_charCodeAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_codePointAt(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_concat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_endsWith(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_indexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -125,6 +131,9 @@ struct StringPrototype: StringObject
static ReturnedValue method_lastIndexOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_localeCompare(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_match(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_normalize(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padEnd(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_padStart(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_repeat(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_replace(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_search(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
@@ -137,8 +146,8 @@ struct StringPrototype: StringObject
static ReturnedValue method_toLocaleLowerCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_toLocaleUpperCase(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
- static ReturnedValue method_fromCharCode(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_trim(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_iterator(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
};
}
diff --git a/src/qml/jsruntime/qv4symbol.cpp b/src/qml/jsruntime/qv4symbol.cpp
new file mode 100644
index 0000000000..7bb33dce69
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol.cpp
@@ -0,0 +1,182 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <qv4symbol_p.h>
+#include <qv4functionobject_p.h>
+#include <qv4identifiertable_p.h>
+
+using namespace QV4;
+
+DEFINE_OBJECT_VTABLE(SymbolCtor);
+DEFINE_MANAGED_VTABLE(Symbol);
+DEFINE_OBJECT_VTABLE(SymbolObject);
+
+void Heap::Symbol::init(const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ identifier = Identifier::fromHeapObject(this);
+ QString desc(s);
+ text = desc.data_ptr();
+ text->ref.ref();
+}
+
+void Heap::SymbolCtor::init(QV4::ExecutionContext *scope)
+{
+ Heap::FunctionObject::init(scope, QStringLiteral("Symbol"));
+}
+
+void Heap::SymbolObject::init(const QV4::Symbol *s)
+{
+ Object::init();
+ symbol.set(internalClass->engine, s->d());
+}
+
+ReturnedValue QV4::SymbolCtor::call(const QV4::FunctionObject *f, const QV4::Value *, const QV4::Value *argv, int argc)
+{
+ Scope scope(f);
+ QString desc = QChar::fromLatin1('@');
+ if (argc && !argv[0].isUndefined()) {
+ ScopedString s(scope, argv[0].toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ desc += s->toQString();
+ }
+ return Symbol::create(scope.engine, desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::method_for(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ Scope scope(f);
+ ScopedValue k(scope, argc ? argv[0]: Primitive::undefinedValue());
+ ScopedString key(scope, k->toString(scope.engine));
+ if (scope.hasException())
+ return Encode::undefined();
+ QString desc = QLatin1Char('@') + key->toQString();
+ return scope.engine->identifierTable->insertSymbol(desc)->asReturnedValue();
+}
+
+ReturnedValue SymbolCtor::method_keyFor(const FunctionObject *f, const Value *, const Value *argv, int argc)
+{
+ ExecutionEngine *e = f->engine();
+ if (!argc || !argv[0].isSymbol())
+ return e->throwTypeError(QLatin1String("Symbol.keyFor: Argument is not a symbol."));
+ const Symbol &arg = static_cast<const Symbol &>(argv[0]);
+ Heap::Symbol *s = e->identifierTable->symbolForId(arg.identifier());
+ Q_ASSERT(!s || s == arg.d());
+ if (s)
+ return e->newString(arg.toQString().mid((1)))->asReturnedValue();
+ return Encode::undefined();
+}
+
+void SymbolPrototype::init(ExecutionEngine *engine, Object *ctor)
+{
+ Scope scope(engine);
+ ScopedValue v(scope);
+ ctor->defineReadonlyProperty(engine->id_prototype(), (v = this));
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+
+ ctor->defineDefaultProperty(QStringLiteral("for"), SymbolCtor::method_for, 1);
+ ctor->defineDefaultProperty(QStringLiteral("keyFor"), SymbolCtor::method_keyFor, 1);
+ ctor->defineReadonlyProperty(QStringLiteral("hasInstance"), *engine->symbol_hasInstance());
+ ctor->defineReadonlyProperty(QStringLiteral("isConcatSpreadable"), *engine->symbol_isConcatSpreadable());
+ ctor->defineReadonlyProperty(QStringLiteral("iterator"), *engine->symbol_iterator());
+ ctor->defineReadonlyProperty(QStringLiteral("match"), *engine->symbol_match());
+ ctor->defineReadonlyProperty(QStringLiteral("replace"), *engine->symbol_replace());
+ ctor->defineReadonlyProperty(QStringLiteral("search"), *engine->symbol_search());
+ ctor->defineReadonlyProperty(QStringLiteral("species"), *engine->symbol_species());
+ ctor->defineReadonlyProperty(QStringLiteral("split"), *engine->symbol_split());
+ ctor->defineReadonlyProperty(QStringLiteral("toPrimitive"), *engine->symbol_toPrimitive());
+ ctor->defineReadonlyProperty(QStringLiteral("toStringTag"), *engine->symbol_toStringTag());
+ ctor->defineReadonlyProperty(QStringLiteral("unscopables"), *engine->symbol_unscopables());
+
+ defineDefaultProperty(QStringLiteral("constructor"), (v = ctor));
+ defineDefaultProperty(QStringLiteral("toString"), method_toString);
+ defineDefaultProperty(QStringLiteral("valueOf"), method_valueOf);
+ defineDefaultProperty(engine->symbol_toPrimitive(), method_symbolToPrimitive, 1, Attr_ReadOnly_ButConfigurable);
+
+ v = engine->newString(QStringLiteral("Symbol"));
+ defineReadonlyConfigurableProperty(engine->symbol_toStringTag(), v);
+
+}
+
+ReturnedValue SymbolPrototype::method_toString(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return scope.engine->newString(s->descriptiveString())->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_valueOf(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ Scope scope(f);
+ Scoped<Symbol> s(scope, thisObject->as<Symbol>());
+ if (!s) {
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ s = o->d()->symbol;
+ else
+ return scope.engine->throwTypeError();
+ }
+ return s->asReturnedValue();
+}
+
+ReturnedValue SymbolPrototype::method_symbolToPrimitive(const FunctionObject *f, const Value *thisObject, const Value *, int)
+{
+ if (thisObject->isSymbol())
+ return thisObject->asReturnedValue();
+ if (const SymbolObject *o = thisObject->as<SymbolObject>())
+ return o->d()->symbol->asReturnedValue();
+ return f->engine()->throwTypeError();
+}
+
+Heap::Symbol *Symbol::create(ExecutionEngine *e, const QString &s)
+{
+ Q_ASSERT(s.at(0) == QLatin1Char('@'));
+ return e->memoryManager->alloc<Symbol>(s);
+}
+
+QString Symbol::descriptiveString() const
+{
+ return QLatin1String("Symbol(") + toQString().midRef(1) + QLatin1String(")");
+}
diff --git a/src/qml/jsruntime/qv4symbol_p.h b/src/qml/jsruntime/qv4symbol_p.h
new file mode 100644
index 0000000000..3cf6bc5dde
--- /dev/null
+++ b/src/qml/jsruntime/qv4symbol_p.h
@@ -0,0 +1,129 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4_SYMBOL_H
+#define QV4_SYMBOL_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qv4string_p.h"
+#include "qv4functionobject_p.h"
+
+QT_BEGIN_NAMESPACE
+
+
+namespace QV4 {
+
+namespace Heap {
+
+struct SymbolCtor : FunctionObject {
+ void init(QV4::ExecutionContext *scope);
+};
+
+struct Symbol : StringOrSymbol {
+ void init(const QString &s);
+};
+
+#define SymbolObjectMembers(class, Member) \
+ Member(class, Pointer, Symbol *, symbol)
+
+DECLARE_HEAP_OBJECT(SymbolObject, Object) {
+ DECLARE_MARKOBJECTS(SymbolObject);
+ void init(const QV4::Symbol *s);
+};
+
+}
+
+struct SymbolCtor : FunctionObject
+{
+ V4_OBJECT2(SymbolCtor, FunctionObject)
+
+ static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_for(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keyFor(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct SymbolPrototype : Object
+{
+ V4_PROTOTYPE(objectPrototype)
+ void init(ExecutionEngine *engine, Object *ctor);
+
+ static ReturnedValue method_toString(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_valueOf(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_symbolToPrimitive(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+};
+
+struct Symbol : StringOrSymbol
+{
+ V4_MANAGED(Symbol, StringOrSymbol)
+ Q_MANAGED_TYPE(Symbol)
+ V4_INTERNALCLASS(Symbol)
+ V4_NEEDS_DESTROY
+
+ static Heap::Symbol *create(ExecutionEngine *e, const QString &s);
+
+ QString descriptiveString() const;
+};
+
+struct SymbolObject : Object
+{
+ V4_OBJECT2(SymbolObject, Object)
+ Q_MANAGED_TYPE(SymbolObject)
+ V4_INTERNALCLASS(SymbolObject)
+ V4_PROTOTYPE(symbolPrototype)
+
+ static bool put(Managed *, StringOrSymbol *, const Value &) { return false; }
+ static bool putIndexed(Managed *, uint, const Value &) { return false; }
+
+};
+
+}
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp
index ea1532b8ce..3eadfe04a6 100644
--- a/src/qml/jsruntime/qv4typedarray.cpp
+++ b/src/qml/jsruntime/qv4typedarray.cpp
@@ -36,15 +36,20 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+
#include "qv4typedarray_p.h"
+#include "qv4arrayiterator_p.h"
#include "qv4arraybuffer_p.h"
#include "qv4string_p.h"
#include "qv4jscall_p.h"
+#include "qv4symbol_p.h"
#include <cmath>
using namespace QV4;
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayCtor);
+DEFINE_OBJECT_VTABLE(IntrinsicTypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArrayCtor);
DEFINE_OBJECT_VTABLE(TypedArrayPrototype);
DEFINE_OBJECT_VTABLE(TypedArray);
@@ -216,9 +221,12 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
if (!argc || !argv[0].isObject()) {
// ECMA 6 22.2.1.1
- double l = argc ? argv[0].toNumber() : 0;
+ qint64 l = argc ? argv[0].toIndex() : 0;
if (scope.engine->hasException)
return Encode::undefined();
+ // ### lift UINT_MAX restriction
+ if (l < 0 || l > UINT_MAX)
+ return scope.engine->throwRangeError(QLatin1String("Index out of range."));
uint len = (uint)l;
if (l != len)
scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array."));
@@ -312,7 +320,10 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
return scope.engine->throwTypeError();
uint elementSize = operations[that->d()->type].bytesPerElement;
- Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize));
+ size_t bufferSize;
+ if (mul_overflow(size_t(l), size_t(elementSize), &bufferSize))
+ return scope.engine->throwRangeError(QLatin1String("new TypedArray: invalid length"));
+ Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(bufferSize));
if (scope.engine->hasException)
return Encode::undefined();
@@ -337,9 +348,9 @@ ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const V
return array.asReturnedValue();
}
-ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc)
+ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *, int)
{
- return callAsConstructor(f, argv, argc);
+ return f->engine()->throwTypeError(QStringLiteral("calling a TypedArray constructor without new is invalid"));
}
void Heap::TypedArray::init(Type t)
@@ -351,9 +362,9 @@ void Heap::TypedArray::init(Type t)
Heap::TypedArray *TypedArray::create(ExecutionEngine *e, Heap::TypedArray::Type t)
{
- QV4::InternalClass *ic = e->internalClasses[EngineBase::Class_Empty]->changeVTable(staticVTable());
- ic = ic->changePrototype(e->typedArrayPrototype[t].d());
- return e->memoryManager->allocObject<TypedArray>(ic, e->typedArrayPrototype + t, t);
+ Scope scope(e);
+ Scoped<InternalClass> ic(scope, e->newInternalClass(staticVTable(), e->typedArrayPrototype + t));
+ return e->memoryManager->allocObject<TypedArray>(ic->d(), t);
}
ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProperty)
@@ -395,21 +406,18 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor)
{
Scope scope(engine);
ScopedObject o(scope);
- ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(3));
- ctor->defineReadonlyProperty(engine->id_prototype(), (o = this));
+
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(3));
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
ctor->defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
+ ctor->setPrototype(engine->intrinsicTypedArrayCtor());
+
+ setPrototype(engine->intrinsicTypedArrayPrototype());
defineDefaultProperty(engine->id_constructor(), (o = ctor));
- defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
- defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
- defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
- defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
defineReadonlyProperty(QStringLiteral("BYTES_PER_ELEMENT"), Primitive::fromInt32(operations[ctor->d()->type].bytesPerElement));
-
- defineDefaultProperty(QStringLiteral("set"), method_set, 1);
- defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
}
-ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_buffer(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -419,7 +427,7 @@ ReturnedValue TypedArrayPrototype::method_get_buffer(const FunctionObject *b, co
return v->d()->buffer->asReturnedValue();
}
-ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteLength(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -429,7 +437,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteLength(const FunctionObject *b
return Encode(v->d()->byteLength);
}
-ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_byteOffset(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -439,7 +447,7 @@ ReturnedValue TypedArrayPrototype::method_get_byteOffset(const FunctionObject *b
return Encode(v->d()->byteOffset);
}
-ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_length(const FunctionObject *b, const Value *thisObject, const Value *, int)
{
ExecutionEngine *v4 = b->engine();
const TypedArray *v = thisObject->as<TypedArray>();
@@ -449,7 +457,43 @@ ReturnedValue TypedArrayPrototype::method_get_length(const FunctionObject *b, co
return Encode(v->d()->byteLength/v->d()->type->bytesPerElement);
}
-ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_entries(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_keys(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::KeyIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_values(const FunctionObject *b, const Value *thisObject, const Value *, int)
+{
+ Scope scope(b);
+ Scoped<TypedArray> O(scope, thisObject);
+ if (!O)
+ THROW_TYPE_ERROR();
+
+ Scoped<ArrayIteratorObject> ao(scope, scope.engine->newArrayIteratorObject(O));
+ ao->d()->iterationKind = IteratorKind::ValueIteratorKind;
+ return ao->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_set(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(b);
Scoped<TypedArray> a(scope, *thisObject);
@@ -538,7 +582,7 @@ ReturnedValue TypedArrayPrototype::method_set(const FunctionObject *b, const Val
RETURN_UNDEFINED();
}
-ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
+ReturnedValue IntrinsicTypedArrayPrototype::method_subarray(const FunctionObject *builtin, const Value *thisObject, const Value *argv, int argc)
{
Scope scope(builtin);
Scoped<TypedArray> a(scope, *thisObject);
@@ -578,3 +622,47 @@ ReturnedValue TypedArrayPrototype::method_subarray(const FunctionObject *builtin
arguments[2] = Encode(newLen);
return constructor->callAsConstructor(arguments, 3);
}
+
+ReturnedValue IntrinsicTypedArrayPrototype::method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *, int)
+{
+ const TypedArray *a = thisObject->as<TypedArray>();
+ if (!a)
+ return Encode::undefined();
+
+ return a->engine()->newString(QString::fromLatin1(a->d()->type->name))->asReturnedValue();
+}
+
+ReturnedValue IntrinsicTypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+ReturnedValue IntrinsicTypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *, int)
+{
+ return f->engine()->throwTypeError();
+}
+
+void IntrinsicTypedArrayPrototype::init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor)
+{
+ ctor->defineReadonlyProperty(engine->id_prototype(), *this);
+ ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0));
+ ctor->addSymbolSpecies();
+
+ defineAccessorProperty(QStringLiteral("buffer"), method_get_buffer, nullptr);
+ defineAccessorProperty(QStringLiteral("byteLength"), method_get_byteLength, nullptr);
+ defineAccessorProperty(QStringLiteral("byteOffset"), method_get_byteOffset, nullptr);
+ defineAccessorProperty(QStringLiteral("length"), method_get_length, nullptr);
+
+ defineDefaultProperty(QStringLiteral("entries"), method_entries, 0);
+ defineDefaultProperty(QStringLiteral("keys"), method_keys, 0);
+ defineDefaultProperty(QStringLiteral("set"), method_set, 1);
+ defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0);
+
+ Scope scope(engine);
+ ScopedString valuesString(scope, engine->newIdentifier(QStringLiteral("values")));
+ ScopedObject values(scope, FunctionObject::createBuiltinFunction(engine, valuesString, method_values, 0));
+ defineDefaultProperty(QStringLiteral("values"), values);
+ defineDefaultProperty(engine->symbol_iterator(), values);
+
+ defineAccessorProperty(engine->symbol_toStringTag(), method_get_toStringTag, nullptr);
+}
diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h
index 129c662c97..43ff1ec5b7 100644
--- a/src/qml/jsruntime/qv4typedarray_p.h
+++ b/src/qml/jsruntime/qv4typedarray_p.h
@@ -97,12 +97,18 @@ DECLARE_HEAP_OBJECT(TypedArray, Object) {
void init(Type t);
};
+struct IntrinsicTypedArrayCtor : FunctionObject {
+};
+
struct TypedArrayCtor : FunctionObject {
void init(QV4::ExecutionContext *scope, TypedArray::Type t);
TypedArray::Type type;
};
+struct IntrinsicTypedArrayPrototype : Object {
+};
+
struct TypedArrayPrototype : Object {
inline void init(TypedArray::Type t);
TypedArray::Type type;
@@ -137,6 +143,14 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object
static bool putIndexed(Managed *m, uint index, const Value &value);
};
+struct IntrinsicTypedArrayCtor: FunctionObject
+{
+ V4_OBJECT2(IntrinsicTypedArrayCtor, FunctionObject)
+
+ static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc);
+ static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
+};
+
struct TypedArrayCtor: FunctionObject
{
V4_OBJECT2(TypedArrayCtor, FunctionObject)
@@ -145,21 +159,34 @@ struct TypedArrayCtor: FunctionObject
static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc);
};
-
-struct TypedArrayPrototype : Object
+struct IntrinsicTypedArrayPrototype : Object
{
- V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_OBJECT2(IntrinsicTypedArrayPrototype, Object)
V4_PROTOTYPE(objectPrototype)
- void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
+ void init(ExecutionEngine *engine, IntrinsicTypedArrayCtor *ctor);
static ReturnedValue method_get_buffer(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteLength(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_byteOffset(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_get_length(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_entries(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_keys(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+ static ReturnedValue method_values(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_set(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
static ReturnedValue method_subarray(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+ static ReturnedValue method_get_toStringTag(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
+
+};
+
+struct TypedArrayPrototype : Object
+{
+ V4_OBJECT2(TypedArrayPrototype, Object)
+ V4_PROTOTYPE(objectPrototype)
+
+ void init(ExecutionEngine *engine, TypedArrayCtor *ctor);
};
inline void
diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp
index 0d4711df3c..9febd41a00 100644
--- a/src/qml/jsruntime/qv4value.cpp
+++ b/src/qml/jsruntime/qv4value.cpp
@@ -40,6 +40,7 @@
#include <qv4runtime_p.h>
#include <qv4string_p.h>
#ifndef V4_BOOTSTRAP
+#include <qv4symbol_p.h>
#include <qv4object_p.h>
#include <qv4objectproto_p.h>
#include <private/qv4mm_p.h>
@@ -84,7 +85,7 @@ bool Value::toBooleanImpl(Value val)
#ifdef V4_BOOTSTRAP
Q_UNIMPLEMENTED();
#else
- if (b->vtable()->isString)
+ if (b->internalClass->vtable->isString)
return static_cast<Heap::String *>(b)->length() > 0;
#endif
return true;
@@ -107,6 +108,11 @@ double Value::toNumberImpl(Value val)
#else
if (String *s = val.stringValue())
return RuntimeHelpers::stringToNumber(s->toQString());
+ if (val.isSymbol()) {
+ Managed &m = static_cast<Managed &>(val);
+ m.engine()->throwTypeError();
+ return 0;
+ }
{
Q_ASSERT(val.isObject());
Scope scope(val.objectValue()->engine());
@@ -145,6 +151,8 @@ QString Value::toQStringNoThrow() const
case Value::Managed_Type:
if (String *s = stringValue())
return s->toQString();
+ if (Symbol *s = symbolValue())
+ return s->descriptiveString();
{
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
@@ -197,9 +205,12 @@ QString Value::toQString() const
else
return QStringLiteral("false");
case Value::Managed_Type:
- if (String *s = stringValue())
+ if (String *s = stringValue()) {
return s->toQString();
- {
+ } else if (isSymbol()) {
+ static_cast<const Managed *>(this)->engine()->throwTypeError();
+ return QString();
+ } else {
Q_ASSERT(isObject());
Scope scope(objectValue()->engine());
ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
@@ -217,6 +228,17 @@ QString Value::toQString() const
}
} // switch
}
+
+Heap::StringOrSymbol *Value::toPropertyKey(ExecutionEngine *e) const
+{
+ Scope scope(e);
+ ScopedValue v(scope, RuntimeHelpers::toPrimitive(*this, STRING_HINT));
+ if (!v->isStringOrSymbol())
+ v = v->toString(e);
+ if (e->hasException)
+ return nullptr;
+ return static_cast<Heap::StringOrSymbol *>(v->m());
+}
#endif // V4_BOOTSTRAP
bool Value::sameValue(Value other) const {
@@ -235,6 +257,25 @@ bool Value::sameValue(Value other) const {
return false;
}
+bool Value::sameValueZero(Value other) const {
+ if (_val == other._val)
+ return true;
+ String *s = stringValue();
+ String *os = other.stringValue();
+ if (s && os)
+ return s->isEqualTo(os);
+ if (isInteger() && other.isDouble())
+ return double(int_32()) == other.doubleValue();
+ if (isDouble() && other.isInteger())
+ return other.int_32() == doubleValue();
+ if (isDouble() && other.isDouble()) {
+ if (doubleValue() == 0 && other.doubleValue() == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
#ifndef V4_BOOTSTRAP
Heap::String *Value::toString(ExecutionEngine *e, Value val)
{
diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h
index a5ee6b5373..16bbf241ff 100644
--- a/src/qml/jsruntime/qv4value_p.h
+++ b/src/qml/jsruntime/qv4value_p.h
@@ -56,6 +56,7 @@
#include <QtCore/QString>
#include "qv4global_p.h"
#include <private/qv4heap_p.h>
+#include <private/qv4internalclass_p.h>
#include <private/qnumeric_p.h>
@@ -184,23 +185,7 @@ public:
QML_NEARLY_ALWAYS_INLINE void setEmpty()
{
- setTagValue(quint32(ValueTypeInternal::Empty), value());
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(int i)
- {
- setTagValue(quint32(ValueTypeInternal::Empty), quint32(i));
- }
-
- QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i)
- {
- setTagValue(quint32(ValueTypeInternal::Empty), i);
- }
-
- QML_NEARLY_ALWAYS_INLINE quint32 emptyValue()
- {
- Q_ASSERT(isEmpty());
- return quint32(value());
+ setTagValue(quint32(ValueTypeInternal::Empty), 0);
}
// ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible
@@ -248,7 +233,8 @@ public:
IsManagedOrUndefined_Shift = 64-15,
IsIntegerConvertible_Shift = 64-15,
IsIntegerOrBool_Shift = 64-16,
- QuickType_Shift = 64 - 17
+ QuickType_Shift = 64 - 17,
+ IsPositiveIntShift = 31
};
static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49
@@ -315,6 +301,14 @@ public:
}
inline bool isNaN() const { return (tag() & 0x7ffc0000 ) == 0x00040000; }
+ inline bool isPositiveInt() const {
+#if QT_POINTER_SIZE == 4
+ return isInteger() && int_32() >= 0;
+#else
+ return (_val >> IsPositiveIntShift) == (quint64(ValueTypeInternal::Integer) << 1);
+#endif
+ }
+
QML_NEARLY_ALWAYS_INLINE double doubleValue() const {
Q_ASSERT(isDouble());
double d;
@@ -331,6 +325,8 @@ public:
Q_ASSERT(isDouble());
}
inline bool isString() const;
+ inline bool isStringOrSymbol() const;
+ inline bool isSymbol() const;
inline bool isObject() const;
inline bool isFunctionObject() const;
inline bool isInt32() {
@@ -362,7 +358,17 @@ public:
QML_NEARLY_ALWAYS_INLINE String *stringValue() const {
if (!isString())
return nullptr;
- return reinterpret_cast<String*>(const_cast<Value *>(this));
+ return reinterpret_cast<String *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE StringOrSymbol *stringOrSymbolValue() const {
+ if (!isStringOrSymbol())
+ return nullptr;
+ return reinterpret_cast<StringOrSymbol *>(const_cast<Value *>(this));
+ }
+ QML_NEARLY_ALWAYS_INLINE Symbol *symbolValue() const {
+ if (!isSymbol())
+ return nullptr;
+ return reinterpret_cast<Symbol *>(const_cast<Value *>(this));
}
QML_NEARLY_ALWAYS_INLINE Object *objectValue() const {
if (!isObject())
@@ -388,6 +394,8 @@ public:
int toUInt16() const;
inline int toInt32() const;
inline unsigned int toUInt32() const;
+ qint64 toLength() const;
+ inline qint64 toIndex() const;
bool toBoolean() const {
if (integerCompatible())
@@ -407,6 +415,8 @@ public:
return reinterpret_cast<Heap::String *>(m());
return toString(e, *this);
}
+ Heap::StringOrSymbol *toPropertyKey(ExecutionEngine *e) const;
+
static Heap::String *toString(ExecutionEngine *e, Value val);
Heap::Object *toObject(ExecutionEngine *e) const {
if (isObject())
@@ -428,11 +438,11 @@ public:
if (!isManaged())
return nullptr;
- Q_ASSERT(m()->vtable());
+ Q_ASSERT(m()->internalClass->vtable);
#if !defined(QT_NO_QOBJECT_CHECK)
static_cast<const T *>(this)->qt_check_for_QMANAGED_macro(static_cast<const T *>(this));
#endif
- const VTable *vt = m()->vtable();
+ const VTable *vt = m()->internalClass->vtable;
while (vt) {
if (vt == T::staticVTable())
return static_cast<const T *>(this);
@@ -465,8 +475,9 @@ public:
ReturnedValue asReturnedValue() const { return _val; }
static Value fromReturnedValue(ReturnedValue val) { Value v; v._val = val; return v; }
- // Section 9.12
+ // As per ES specs
bool sameValue(Value other) const;
+ bool sameValueZero(Value other) const;
inline void mark(MarkStack *markStack);
@@ -500,18 +511,32 @@ inline void Value::mark(MarkStack *markStack)
inline bool Value::isString() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isString;
+ return b && b->internalClass->vtable->isString;
+}
+
+bool Value::isStringOrSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol;
}
+
+bool Value::isSymbol() const
+{
+ Heap::Base *b = heapObject();
+ return b && b->internalClass->vtable->isStringOrSymbol && !b->internalClass->vtable->isString;
+}
+
inline bool Value::isObject() const
+
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isObject;
+ return b && b->internalClass->vtable->isObject;
}
inline bool Value::isFunctionObject() const
{
Heap::Base *b = heapObject();
- return b && b->vtable()->isFunctionObject;
+ return b && b->internalClass->vtable->isFunctionObject;
}
inline bool Value::isPrimitive() const
@@ -532,33 +557,26 @@ inline double Value::toNumber() const
#ifndef V4_BOOTSTRAP
inline uint Value::asArrayIndex() const
{
-#if QT_POINTER_SIZE == 8
- if (!isNumber())
- return UINT_MAX;
- if (isInteger())
- return int_32() >= 0 ? (uint)int_32() : UINT_MAX;
-#else
- if (isInteger() && int_32() >= 0)
+ if (Q_LIKELY(isPositiveInt())) {
return (uint)int_32();
- if (!isDouble())
+ }
+ if (Q_UNLIKELY(!isDouble()))
return UINT_MAX;
-#endif
double d = doubleValue();
uint idx = (uint)d;
- if (idx != d)
- return UINT_MAX;
- return idx;
+ if (idx == d)
+ return idx;
+ return UINT_MAX;
}
inline bool Value::asArrayIndex(uint &idx) const
{
- if (Q_LIKELY(!isDouble())) {
- if (Q_LIKELY(isInteger() && int_32() >= 0)) {
- idx = (uint)int_32();
- return true;
- }
- return false;
+ if (Q_LIKELY(isPositiveInt())) {
+ idx = (uint)int_32();
+ return true;
}
+ if (Q_UNLIKELY(!isDouble()))
+ return false;
double d = doubleValue();
idx = (uint)d;
return (idx == d && idx != UINT_MAX);
@@ -576,7 +594,6 @@ ReturnedValue Heap::Base::asReturnedValue() const
struct Q_QML_PRIVATE_EXPORT Primitive : public Value
{
inline static Primitive emptyValue();
- inline static Primitive emptyValue(uint v);
static inline Primitive fromBoolean(bool b);
static inline Primitive fromInt32(int i);
inline static Primitive undefinedValue();
@@ -602,14 +619,7 @@ inline Primitive Primitive::undefinedValue()
inline Primitive Primitive::emptyValue()
{
Primitive v;
- v.setEmpty(0);
- return v;
-}
-
-inline Primitive Primitive::emptyValue(uint e)
-{
- Primitive v;
- v.setEmpty(e);
+ v.setEmpty();
return v;
}
@@ -788,6 +798,31 @@ inline unsigned int Value::toUInt32() const
return static_cast<unsigned int>(toInt32());
}
+inline qint64 Value::toLength() const
+{
+ if (Q_LIKELY(integerCompatible()))
+ return int_32() < 0 ? 0 : int_32();
+ double i = Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl());
+ if (i <= 0)
+ return 0;
+ if (i > (static_cast<qint64>(1) << 53) - 1)
+ return (static_cast<qint64>(1) << 53) - 1;
+ return static_cast<qint64>(i);
+}
+
+inline qint64 Value::toIndex() const
+{
+ qint64 idx;
+ if (Q_LIKELY(integerCompatible())) {
+ idx = int_32();
+ } else {
+ idx = static_cast<qint64>(Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()));
+ }
+ if (idx > (static_cast<qint64>(1) << 53) - 1)
+ idx = -1;
+ return idx;
+}
+
inline double Value::toInteger() const
{
if (integerCompatible())
@@ -831,7 +866,7 @@ struct ValueArray {
WriteBarrier::write(e, base(), values[index].data_ptr(), v.asReturnedValue());
}
void set(EngineBase *e, uint index, Heap::Base *b) {
- WriteBarrier::write(e, base(), values[index].data_ptr(), b->asReturnedValue());
+ WriteBarrier::write(e, base(), values[index].data_ptr(), Value::fromHeapObject(b).asReturnedValue());
}
inline const Value &operator[] (uint index) const {
Q_ASSERT(index < alloc);
@@ -884,7 +919,6 @@ struct ValueArray {
// have wrong offsets between host and target.
Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8);
-
}
QT_END_NAMESPACE
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index a1f5b01fa9..f625368b62 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -55,12 +55,13 @@
#include <private/qv4string_p.h>
#include <private/qv4profiling_p.h>
#include <private/qv4jscall_p.h>
+#include <private/qv4generatorobject_p.h>
#include <private/qqmljavascriptexpression_p.h>
#include <iostream>
#include "qv4alloca_p.h"
-#include <private/qv4jit_p.h>
+#include <private/qv4baselinejit_p.h>
#undef COUNT_INSTRUCTIONS
@@ -343,7 +344,7 @@ static struct InstrCount {
#endif
#define CHECK_EXCEPTION \
if (engine->hasException) \
- goto catchException
+ goto handleUnwind
static inline Heap::CallContext *getScope(QV4::Value *stack, int level)
{
@@ -361,95 +362,6 @@ static inline const QV4::Value &constant(Function *function, int index)
return function->compilationUnit->constants[index];
}
-
-static bool compareEqual(QV4::Value lhs, QV4::Value rhs)
-{
- redo:
- if (lhs.asReturnedValue() == rhs.asReturnedValue())
- return !lhs.isNaN();
-
- int lt = lhs.quickType();
- int rt = rhs.quickType();
- if (rt < lt) {
- qSwap(lhs, rhs);
- qSwap(lt, rt);
- }
-
- switch (lt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (lhs.isUndefined())
- return rhs.isNullOrUndefined();
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- // LHS: Managed
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- if (rhs.isUndefined())
- return false;
- Q_FALLTHROUGH();
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3: {
- // RHS: Managed
- Heap::Base *l = lhs.m();
- Heap::Base *r = rhs.m();
- Q_ASSERT(l);
- Q_ASSERT(r);
- if (l->vtable()->isString == r->vtable()->isString)
- return static_cast<QV4::Managed &>(lhs).isEqualTo(&static_cast<QV4::Managed &>(rhs));
- if (l->vtable()->isString) {
- rhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT));
- break;
- } else {
- Q_ASSERT(r->vtable()->isString);
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- break;
- }
- return false;
- }
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return false;
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- rhs = Primitive::fromDouble(rhs.int_32());
- // fall through
- default: // double
- if (lhs.m()->vtable()->isString)
- return RuntimeHelpers::toNumber(lhs) == rhs.doubleValue();
- else
- lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT));
- }
- goto redo;
- case QV4::Value::QT_Empty:
- Q_UNREACHABLE();
- case QV4::Value::QT_Null:
- return rhs.isNull();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- switch (rt) {
- case QV4::Value::QT_ManagedOrUndefined:
- case QV4::Value::QT_ManagedOrUndefined1:
- case QV4::Value::QT_ManagedOrUndefined2:
- case QV4::Value::QT_ManagedOrUndefined3:
- case QV4::Value::QT_Empty:
- case QV4::Value::QT_Null:
- Q_UNREACHABLE();
- case QV4::Value::QT_Bool:
- case QV4::Value::QT_Int:
- return lhs.int_32() == rhs.int_32();
- default: // double
- return lhs.int_32() == rhs.doubleValue();
- }
- default: // double
- Q_ASSERT(rhs.isDouble());
- return lhs.doubleValue() == rhs.doubleValue();
- }
-}
-
static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
{
redo:
@@ -462,7 +374,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
case QV4::Value::QT_ManagedOrUndefined2:
case QV4::Value::QT_ManagedOrUndefined3:
// LHS: Managed
- if (lhs.m()->vtable()->isString)
+ if (lhs.m()->internalClass->vtable->isString)
return RuntimeHelpers::stringToNumber(static_cast<String &>(lhs).toQString()) == rhs;
accumulator = lhs;
lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(accumulator), PREFERREDTYPE_HINT));
@@ -479,7 +391,7 @@ static bool compareEqualInt(QV4::Value &accumulator, QV4::Value lhs, int rhs)
}
}
-#define STORE_IP() frame.instructionPointer = int(code - codeStart);
+#define STORE_IP() frame.instructionPointer = int(code - function->codeData);
#define STORE_ACC() accumulator = acc;
#define ACC Primitive::fromReturnedValue(acc)
#define VALUE_TO_INT(i, val) \
@@ -508,6 +420,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CppStackFrame frame;
frame.originalArguments = argv;
frame.originalArgumentsCount = argc;
+ frame.yield = nullptr;
+ frame.unwindHandler = nullptr;
+ frame.unwindLabel = nullptr;
+ frame.unwindLevel = 0;
+
Function *function;
{
@@ -553,10 +470,6 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
Profiling::FunctionCallProfiler profiler(engine, function); // start execution profiling
QV4::Debugging::Debugger *debugger = engine->debugger();
- const uchar *exceptionHandler = nullptr;
-
- QV4::Value &accumulator = frame.jsFrame->accumulator;
- QV4::ReturnedValue acc = Encode::undefined();
#ifdef V4_ENABLE_JIT
if (function->jittedCode == nullptr && debugger == nullptr) {
@@ -570,12 +483,29 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
if (debugger)
debugger->enteringFunction();
+ ReturnedValue result;
if (function->jittedCode != nullptr && debugger == nullptr) {
- acc = function->jittedCode(&frame, engine);
+ result = function->jittedCode(&frame, engine);
} else {
// interpreter
- const uchar *code = function->codeData;
- const uchar *codeStart = code;
+ result = interpret(frame, function->codeData);
+ }
+
+ if (QV4::Debugging::Debugger *debugger = engine->debugger())
+ debugger->leavingFunction(result);
+ engine->currentStackFrame = frame.parent;
+ engine->jsStackTop = stack;
+
+ return result;
+}
+
+QV4::ReturnedValue VME::interpret(CppStackFrame &frame, const char *code)
+{
+ QV4::Function *function = frame.v4Function;
+ QV4::Value &accumulator = frame.jsFrame->accumulator;
+ QV4::ReturnedValue acc = accumulator.asReturnedValue();
+ Value *stack = reinterpret_cast<Value *>(frame.jsFrame);
+ ExecutionEngine *engine = function->internalClass->engine;
MOTH_JUMP_TABLE;
@@ -629,12 +559,14 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadLocal)
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
acc = cc->locals[index].asReturnedValue();
MOTH_END_INSTR(LoadLocal)
MOTH_BEGIN_INSTR(StoreLocal)
CHECK_EXCEPTION;
auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m());
+ Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext);
QV4::WriteBarrier::write(engine, cc, cc->locals.values[index].data_ptr(), acc);
MOTH_END_INSTR(StoreLocal)
@@ -689,16 +621,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadElement)
STORE_IP();
- acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index));
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElement)
-
- MOTH_BEGIN_INSTR(LoadElementA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadElementA)
+ MOTH_END_INSTR(LoadElement)
MOTH_BEGIN_INSTR(StoreElement)
STORE_IP();
@@ -710,16 +636,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(LoadProperty)
STORE_IP();
- acc = Runtime::method_loadProperty(engine, STACK_VALUE(base), name);
- CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadProperty)
-
- MOTH_BEGIN_INSTR(LoadPropertyA)
- STORE_IP();
STORE_ACC();
acc = Runtime::method_loadProperty(engine, accumulator, name);
CHECK_EXCEPTION;
- MOTH_END_INSTR(LoadPropertyA)
+ MOTH_END_INSTR(LoadProperty)
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
@@ -784,12 +704,30 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(LoadIdObject)
+ MOTH_BEGIN_INSTR(Yield)
+ frame.yield = code;
+ return acc;
+ MOTH_END_INSTR(Yield)
+
+ MOTH_BEGIN_INSTR(Resume)
+ // check exception, in case the generator was called with throw() or return()
+ if (engine->hasException) {
+ // an empty value indicates that the generator was called with return()
+ if (engine->exceptionValue->asReturnedValue() != Primitive::emptyValue().asReturnedValue())
+ goto handleUnwind;
+ engine->hasException = false;
+ *engine->exceptionValue = Primitive::undefinedValue();
+ } else {
+ code += offset;
+ }
+ MOTH_END_INSTR(Resume)
+
MOTH_BEGIN_INSTR(CallValue)
STORE_IP();
Value func = STACK_VALUE(name);
if (Q_UNLIKELY(!func.isFunctionObject())) {
acc = engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow()));
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<const FunctionObject &>(func).call(nullptr, stack + argv, argc);
CHECK_EXCEPTION;
@@ -809,7 +747,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
if (Q_UNLIKELY(!f.isFunctionObject())) {
acc = engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc);
@@ -852,15 +790,49 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
CHECK_EXCEPTION;
MOTH_END_INSTR(CallContextObjectProperty)
- MOTH_BEGIN_INSTR(SetExceptionHandler)
- exceptionHandler = offset ? code + offset : nullptr;
- MOTH_END_INSTR(SetExceptionHandler)
+ MOTH_BEGIN_INSTR(CallWithSpread)
+ STORE_IP();
+ acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(CallWithSpread)
+
+ MOTH_BEGIN_INSTR(Construct)
+ STORE_IP();
+ acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(Construct)
+
+ MOTH_BEGIN_INSTR(ConstructWithSpread)
+ STORE_IP();
+ acc = Runtime::method_constructWithSpread(engine, STACK_VALUE(func), stack + argv, argc);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(ConstructWithSpread)
+
+ MOTH_BEGIN_INSTR(SetUnwindHandler)
+ frame.unwindHandler = offset ? code + offset : nullptr;
+ MOTH_END_INSTR(SetUnwindHandler)
+
+ MOTH_BEGIN_INSTR(UnwindDispatch)
+ CHECK_EXCEPTION;
+ if (frame.unwindLevel) {
+ --frame.unwindLevel;
+ if (frame.unwindLevel)
+ goto handleUnwind;
+ code = frame.unwindLabel;
+ }
+ MOTH_END_INSTR(UnwindDispatch)
+
+ MOTH_BEGIN_INSTR(UnwindToLabel)
+ frame.unwindLevel = level;
+ frame.unwindLabel = code + offset;
+ goto handleUnwind;
+ MOTH_END_INSTR(UnwindToLabel)
MOTH_BEGIN_INSTR(ThrowException)
STORE_IP();
STORE_ACC();
Runtime::method_throwException(engine, accumulator);
- goto catchException;
+ goto handleUnwind;
MOTH_END_INSTR(ThrowException)
MOTH_BEGIN_INSTR(GetException)
@@ -870,14 +842,15 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(HasException)
MOTH_BEGIN_INSTR(SetException)
- *engine->exceptionValue = acc;
- engine->hasException = true;
+ if (acc != Primitive::emptyValue().asReturnedValue()) {
+ *engine->exceptionValue = acc;
+ engine->hasException = true;
+ }
MOTH_END_INSTR(SetException)
MOTH_BEGIN_INSTR(PushCatchContext)
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, name);
+ STACK_VALUE(CallData::Context) = Runtime::method_createCatchContext(c, index, name);
MOTH_END_INSTR(PushCatchContext)
MOTH_BEGIN_INSTR(CreateCallContext)
@@ -887,54 +860,72 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(PushWithContext)
STORE_IP();
STORE_ACC();
- accumulator = accumulator.toObject(engine);
+ auto ctx = Runtime::method_createWithContext(engine, stack);
CHECK_EXCEPTION;
- STACK_VALUE(reg) = STACK_VALUE(CallData::Context);
- ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
- STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator);
+ STACK_VALUE(CallData::Context) = ctx;
MOTH_END_INSTR(PushWithContext)
+ MOTH_BEGIN_INSTR(PushBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_createBlockContext(c, index);
+ MOTH_END_INSTR(PushBlockContext)
+
+ MOTH_BEGIN_INSTR(CloneBlockContext)
+ STORE_ACC();
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = Runtime::method_cloneBlockContext(c);
+ MOTH_END_INSTR(CloneBlockContext)
+
+ MOTH_BEGIN_INSTR(PushScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_createScriptContext(engine, index);
+ MOTH_END_INSTR(PushScriptContext)
+
+ MOTH_BEGIN_INSTR(PopScriptContext)
+ STACK_VALUE(CallData::Context) = Runtime::method_popScriptContext(engine);
+ MOTH_END_INSTR(PopScriptContext)
+
MOTH_BEGIN_INSTR(PopContext)
- STACK_VALUE(CallData::Context) = STACK_VALUE(reg);
+ ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context);
+ STACK_VALUE(CallData::Context) = c->d()->outer;
MOTH_END_INSTR(PopContext)
- MOTH_BEGIN_INSTR(ForeachIteratorObject)
+ MOTH_BEGIN_INSTR(GetIterator)
STORE_ACC();
- acc = Runtime::method_foreachIterator(engine, accumulator);
+ acc = Runtime::method_getIterator(engine, accumulator, iterator);
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachIteratorObject)
+ MOTH_END_INSTR(GetIterator)
- MOTH_BEGIN_INSTR(ForeachNextPropertyName)
+ MOTH_BEGIN_INSTR(IteratorNext)
STORE_ACC();
- acc = Runtime::method_foreachNextPropertyName(accumulator);
+ acc = Runtime::method_iteratorNext(engine, accumulator, &STACK_VALUE(value));
CHECK_EXCEPTION;
- MOTH_END_INSTR(ForeachNextPropertyName)
+ MOTH_END_INSTR(IteratorNext)
- MOTH_BEGIN_INSTR(DeleteMember)
- if (!Runtime::method_deleteMember(engine, STACK_VALUE(base), member)) {
- if (function->isStrict()) {
- STORE_IP();
- engine->throwTypeError();
- goto catchException;
- }
- acc = Encode(false);
- } else {
- acc = Encode(true);
- }
- MOTH_END_INSTR(DeleteMember)
+ MOTH_BEGIN_INSTR(IteratorClose)
+ STORE_ACC();
+ acc = Runtime::method_iteratorClose(engine, accumulator, STACK_VALUE(done));
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(IteratorClose)
+
+ MOTH_BEGIN_INSTR(DestructureRestElement)
+ STORE_ACC();
+ acc = Runtime::method_destructureRestElement(engine, ACC);
+ CHECK_EXCEPTION;
+ MOTH_END_INSTR(DestructureRestElement)
- MOTH_BEGIN_INSTR(DeleteSubscript)
- if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) {
+ MOTH_BEGIN_INSTR(DeleteProperty)
+ if (!Runtime::method_deleteProperty(engine, STACK_VALUE(base), STACK_VALUE(index))) {
if (function->isStrict()) {
STORE_IP();
engine->throwTypeError();
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
acc = Encode(true);
}
- MOTH_END_INSTR(DeleteSubscript)
+ MOTH_END_INSTR(DeleteProperty)
MOTH_BEGIN_INSTR(DeleteName)
if (!Runtime::method_deleteName(engine, name)) {
@@ -942,7 +933,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STORE_IP();
QString n = function->compilationUnit->runtimeStrings[name]->toQString();
engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n));
- goto catchException;
+ goto handleUnwind;
}
acc = Encode(false);
} else {
@@ -970,7 +961,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_BEGIN_INSTR(DefineObjectLiteral)
QV4::Value *arguments = stack + args;
- acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags);
+ acc = Runtime::method_objectLiteral(engine, internalClassId, argc, arguments);
MOTH_END_INSTR(DefineObjectLiteral)
MOTH_BEGIN_INSTR(CreateMappedArgumentsObject)
@@ -981,6 +972,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Runtime::method_createUnmappedArgumentsObject(engine);
MOTH_END_INSTR(CreateUnmappedArgumentsObject)
+ MOTH_BEGIN_INSTR(CreateRestParameter)
+ acc = Runtime::method_createRestParameter(engine, argIndex);
+ MOTH_END_INSTR(CreateRestParameter)
+
MOTH_BEGIN_INSTR(ConvertThisToObject)
Value *t = &stack[CallData::This];
if (!t->isObject()) {
@@ -993,11 +988,10 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(ConvertThisToObject)
- MOTH_BEGIN_INSTR(Construct)
- STORE_IP();
- acc = Runtime::method_construct(engine, STACK_VALUE(func), stack + argv, argc);
+ MOTH_BEGIN_INSTR(ToObject)
+ acc = ACC.toObject(engine)->asReturnedValue();
CHECK_EXCEPTION;
- MOTH_END_INSTR(Construct)
+ MOTH_END_INSTR(ToObject)
MOTH_BEGIN_INSTR(Jump)
code += offset;
@@ -1023,6 +1017,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(JumpFalse)
+ MOTH_BEGIN_INSTR(JumpNoException)
+ if (!engine->hasException)
+ code += offset;
+ MOTH_END_INSTR(JumpNoException)
+
+ MOTH_BEGIN_INSTR(JumpNotUndefined)
+ if (Q_LIKELY(acc != QV4::Encode::undefined()))
+ code += offset;
+ MOTH_END_INSTR(JumpNotUndefined)
+
MOTH_BEGIN_INSTR(CmpEqNull)
acc = Encode(ACC.isNullOrUndefined());
MOTH_END_INSTR(CmpEqNull)
@@ -1059,7 +1063,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(left.int_32() == ACC.int_32());
} else {
STORE_ACC();
- acc = Encode(compareEqual(left, accumulator));
+ acc = Encode(bool(Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpEq)
@@ -1070,7 +1074,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
acc = Encode(bool(left.int_32() != ACC.int_32()));
} else {
STORE_ACC();
- acc = Encode(!compareEqual(left, accumulator));
+ acc = Encode(bool(!Runtime::method_compareEqual(left, accumulator)));
CHECK_EXCEPTION;
}
MOTH_END_INSTR(CmpNe)
@@ -1154,27 +1158,11 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(CmpIn)
MOTH_BEGIN_INSTR(CmpInstanceOf)
- // 11.8.6, 5: rval must be an Object
- if (Q_UNLIKELY(!Primitive::fromReturnedValue(acc).isObject())) {
- acc = engine->throwTypeError();
- goto catchException;
- }
-
- // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result.
- acc = Primitive::fromReturnedValue(acc).objectValue()->instanceOf(STACK_VALUE(lhs));
+ STORE_ACC();
+ acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), ACC);
CHECK_EXCEPTION;
MOTH_END_INSTR(CmpInstanceOf)
- MOTH_BEGIN_INSTR(JumpStrictNotEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() != rhs || STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
- MOTH_BEGIN_INSTR(JumpStrictEqualStackSlotInt)
- if (STACK_VALUE(lhs).int_32() == rhs && !STACK_VALUE(lhs).isUndefined())
- code += offset;
- MOTH_END_INSTR(JumpStrictNotEqualStackSlotInt)
-
MOTH_BEGIN_INSTR(UNot)
if (ACC.integerCompatible()) {
acc = Encode(!static_cast<bool>(ACC.int_32()));
@@ -1259,6 +1247,16 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
}
MOTH_END_INSTR(Sub)
+ MOTH_BEGIN_INSTR(Exp)
+ const Value left = STACK_VALUE(lhs);
+ double base = left.toNumber();
+ double exp = ACC.toNumber();
+ if (qIsInf(exp) && (base == 1 || base == -1))
+ acc = Encode(qSNaN());
+ else
+ acc = Encode(pow(base,exp));
+ MOTH_END_INSTR(Exp)
+
MOTH_BEGIN_INSTR(Mul)
const Value left = STACK_VALUE(lhs);
if (Q_LIKELY(Value::integerCompatible(left, ACC))) {
@@ -1351,7 +1349,7 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
MOTH_END_INSTR(ShlConst)
MOTH_BEGIN_INSTR(Ret)
- goto functionExit;
+ return acc;
MOTH_END_INSTR(Ret)
MOTH_BEGIN_INSTR(Debug)
@@ -1369,21 +1367,12 @@ QV4::ReturnedValue VME::exec(const FunctionObject *fo, const QV4::Value *thisObj
STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine));
MOTH_END_INSTR(LoadQmlImportedScripts)
- catchException:
- Q_ASSERT(engine->hasException);
- if (!exceptionHandler) {
+ handleUnwind:
+ Q_ASSERT(engine->hasException || frame.unwindLevel);
+ if (!frame.unwindHandler) {
acc = Encode::undefined();
- goto functionExit;
+ return acc;
}
- code = exceptionHandler;
- }
+ code = frame.unwindHandler;
}
-
-functionExit:
- if (QV4::Debugging::Debugger *debugger = engine->debugger())
- debugger->leavingFunction(ACC.asReturnedValue());
- engine->currentStackFrame = frame.parent;
- engine->jsStackTop = stack;
-
- return acc;
}
diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h
index 3b7723ca7e..67bfd537c5 100644
--- a/src/qml/jsruntime/qv4vme_moth_p.h
+++ b/src/qml/jsruntime/qv4vme_moth_p.h
@@ -72,6 +72,7 @@ public:
return exec(reinterpret_cast<const FunctionObject *>(d), thisObject, argv, argc);
}
static QV4::ReturnedValue exec(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc);
+ static QV4::ReturnedValue interpret(CppStackFrame &frame, const char *codeEntry);
};
} // namespace Moth