diff options
Diffstat (limited to 'src/qml/jsruntime')
74 files changed, 4517 insertions, 4727 deletions
diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 9938f60aea..a270cb1aa3 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -33,6 +33,7 @@ SOURCES += \ $$PWD/qv4variantobject.cpp \ $$PWD/qv4objectiterator.cpp \ $$PWD/qv4regexp.cpp \ + $$PWD/qv4runtimecodegen.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ $$PWD/qv4sequenceobject.cpp \ @@ -58,6 +59,7 @@ HEADERS += \ $$PWD/qv4identifiertable_p.h \ $$PWD/qv4managed_p.h \ $$PWD/qv4internalclass_p.h \ + $$PWD/qv4jscall_p.h \ $$PWD/qv4sparsearray_p.h \ $$PWD/qv4arraydata_p.h \ $$PWD/qv4arrayobject_p.h \ @@ -76,6 +78,7 @@ HEADERS += \ $$PWD/qv4objectproto_p.h \ $$PWD/qv4qmlcontext_p.h \ $$PWD/qv4regexpobject_p.h \ + $$PWD/qv4runtimecodegen_p.h \ $$PWD/qv4stringobject_p.h \ $$PWD/qv4variantobject_p.h \ $$PWD/qv4property_p.h \ diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 0905c2828a..5833e39561 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -41,24 +41,26 @@ #include <qv4scopedvalue_p.h> #include <qv4string_p.h> #include <qv4function_p.h> +#include <qv4jscall_p.h> using namespace QV4; DEFINE_OBJECT_VTABLE(ArgumentsObject); -void Heap::ArgumentsObject::init(QV4::CallContext *context) +void Heap::ArgumentsObject::init(QV4::CallContext *context, bool strict) { ExecutionEngine *v4 = internalClass->engine; Object::init(); fullyCreated = false; + isStrict = strict; this->context.set(v4, context->d()); Q_ASSERT(vtable() == QV4::ArgumentsObject::staticVTable()); Scope scope(v4); Scoped<QV4::ArgumentsObject> args(scope, this); - if (context->d()->strictMode) { + if (isStrict) { Q_ASSERT(CalleePropertyIndex == args->internalClass()->find(v4->id_callee())); Q_ASSERT(CallerPropertyIndex == args->internalClass()->find(v4->id_caller())); args->setProperty(CalleePropertyIndex + QV4::Object::GetterOffset, *v4->thrower()); @@ -74,7 +76,7 @@ void Heap::ArgumentsObject::init(QV4::CallContext *context) args->setProperty(CalleePropertyIndex, context->d()->function); } Q_ASSERT(LengthPropertyIndex == args->internalClass()->find(v4->id_length())); - args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->d()->callData->argc)); + args->setProperty(LengthPropertyIndex, Primitive::fromInt32(context->argc())); } void ArgumentsObject::fullyCreate() @@ -84,7 +86,7 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); - uint argCount = context()->callData->argc; + uint argCount = context()->argc(); uint numAccessors = qMin(context()->formalParameterCount(), argCount); ArrayData::realloc(this, Heap::ArrayData::Sparse, argCount, true); scope.engine->requireArgumentsAccessors(numAccessors); @@ -93,11 +95,11 @@ void ArgumentsObject::fullyCreate() if (numAccessors) { d()->mappedArguments.set(scope.engine, md->allocate(scope.engine, numAccessors)); for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->values.set(scope.engine, i, context()->callData->args[i]); + d()->mappedArguments->values.set(scope.engine, i, context()->args()[i]); arraySet(i, scope.engine->argumentsAccessors + i, Attr_Accessor); } } - arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); + arrayPut(numAccessors, context()->args() + numAccessors, argCount - numAccessors); for (uint i = numAccessors; i < argCount; ++i) setArrayAttributes(i, Attr_Data); @@ -111,7 +113,7 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con Scope scope(engine); ScopedProperty map(scope); PropertyAttributes mapAttrs; - uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->callData->argc)); + uint numAccessors = qMin(context()->formalParameterCount(), static_cast<uint>(context()->argc())); bool isMapped = false; if (arrayData() && index < numAccessors && arrayData()->attributes(index).isAccessor() && @@ -127,18 +129,20 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con arrayIndex.set(scope.engine, d()->mappedArguments->values[index]); } - bool strict = engine->current->strictMode; - engine->current->strictMode = false; bool result = Object::defineOwnProperty2(scope.engine, index, desc, attrs); - engine->current->strictMode = strict; + if (!result) { + if (d()->isStrict) + return engine->throwTypeError(); + return false; + } if (isMapped && attrs.isData()) { Q_ASSERT(arrayData()); ScopedFunctionObject setter(scope, map->setter()); - ScopedCallData callData(scope, 1); - callData->thisObject = this->asReturnedValue(); - callData->args[0] = desc->value; - setter->call(scope, callData); + JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = this->asReturnedValue(); + jsCallData->args[0] = desc->value; + setter->call(jsCallData); if (attrs.isWritable()) { setArrayAttributes(index, mapAttrs); @@ -146,8 +150,6 @@ bool ArgumentsObject::defineOwnProperty(ExecutionEngine *engine, uint index, con } } - if (engine->current->strictMode && !result) - return engine->throwTypeError(); return result; } @@ -157,10 +159,10 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha if (args->fullyCreated()) return Object::getIndexed(m, index, hasProperty); - if (index < static_cast<uint>(args->context()->callData->argc)) { + if (index < static_cast<uint>(args->context()->argc())) { if (hasProperty) *hasProperty = true; - return args->context()->callData->args[index].asReturnedValue(); + return args->context()->args()[index].asReturnedValue(); } if (hasProperty) *hasProperty = false; @@ -170,13 +172,13 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); - if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc)) + if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->argc())) args->fullyCreate(); if (args->fullyCreated()) return Object::putIndexed(m, index, value); - args->context()->callData->args[index] = value; + args->context()->setArg(index, value); return true; } @@ -194,8 +196,8 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) if (args->fullyCreated()) return Object::queryIndexed(m, index); - uint numAccessors = qMin((int)args->context()->formalParameterCount(), args->context()->callData->argc); - uint argCount = args->context()->callData->argc; + uint numAccessors = qMin((int)args->context()->formalParameterCount(), args->context()->argc()); + uint argCount = args->context()->argc(); if (index >= argCount) return PropertyAttributes(); if (index >= numAccessors) @@ -205,35 +207,33 @@ PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) DEFINE_OBJECT_VTABLE(ArgumentsGetterFunction); -void ArgumentsGetterFunction::call(const Managed *getter, Scope &scope, CallData *callData) +ReturnedValue ArgumentsGetterFunction::call(const FunctionObject *getter, const Value *thisObject, const Value *, int) { - ExecutionEngine *v4 = static_cast<const ArgumentsGetterFunction *>(getter)->engine(); - Scoped<ArgumentsGetterFunction> g(scope, static_cast<const ArgumentsGetterFunction *>(getter)); - Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) { - scope.result = v4->throwTypeError(); - return; - } + ExecutionEngine *v4 = getter->engine(); + Scope scope(v4); + const ArgumentsGetterFunction *g = static_cast<const ArgumentsGetterFunction *>(getter); + Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>()); + if (!o) + return v4->throwTypeError(); - Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->callData->argc)); - scope.result = o->context()->callData->args[g->index()]; + Q_ASSERT(g->index() < static_cast<unsigned>(o->context()->argc())); + return o->context()->args()[g->index()].asReturnedValue(); } DEFINE_OBJECT_VTABLE(ArgumentsSetterFunction); -void ArgumentsSetterFunction::call(const Managed *setter, Scope &scope, CallData *callData) +ReturnedValue ArgumentsSetterFunction::call(const FunctionObject *setter, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const ArgumentsSetterFunction *>(setter)->engine(); - Scoped<ArgumentsSetterFunction> s(scope, static_cast<const ArgumentsSetterFunction *>(setter)); - Scoped<ArgumentsObject> o(scope, callData->thisObject.as<ArgumentsObject>()); - if (!o) { - scope.result = v4->throwTypeError(); - return; - } + ExecutionEngine *v4 = setter->engine(); + Scope scope(v4); + const ArgumentsSetterFunction *s = static_cast<const ArgumentsSetterFunction *>(setter); + Scoped<ArgumentsObject> o(scope, thisObject->as<ArgumentsObject>()); + if (!o) + return v4->throwTypeError(); - Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->callData->argc)); - o->context()->callData->args[s->index()] = callData->argc ? callData->args[0].asReturnedValue() : Encode::undefined(); - scope.result = Encode::undefined(); + Q_ASSERT(s->index() < static_cast<unsigned>(o->context()->argc())); + o->context()->setArg(s->index(), argc ? argv[0] : Primitive::undefinedValue()); + return Encode::undefined(); } uint ArgumentsObject::getLength(const Managed *m) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 46e1f884e8..cd270e8b47 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -78,7 +78,8 @@ DECLARE_HEAP_OBJECT(ArgumentsSetterFunction, FunctionObject) { #define ArgumentsObjectMembers(class, Member) \ Member(class, Pointer, CallContext *, context) \ Member(class, Pointer, MemberData *, mappedArguments) \ - Member(class, NoMark, bool, fullyCreated) + Member(class, NoMark, bool, fullyCreated) \ + Member(class, NoMark, bool, isStrict) DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { DECLARE_MARK_TABLE(ArgumentsObject); @@ -87,7 +88,7 @@ DECLARE_HEAP_OBJECT(ArgumentsObject, Object) { CalleePropertyIndex = 1, CallerPropertyIndex = 3 }; - void init(QV4::CallContext *context); + void init(QV4::CallContext *context, bool strict); }; } @@ -97,7 +98,7 @@ struct ArgumentsGetterFunction: FunctionObject V4_OBJECT2(ArgumentsGetterFunction, FunctionObject) uint index() const { return d()->index; } - static void call(const Managed *that, Scope &scope, CallData *d); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -112,7 +113,7 @@ struct ArgumentsSetterFunction: FunctionObject V4_OBJECT2(ArgumentsSetterFunction, FunctionObject) uint index() const { return d()->index; } - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; inline void @@ -132,7 +133,7 @@ struct ArgumentsObject: Object { static bool isNonStrictArgumentsObject(Managed *m) { return m->d()->vtable()->type == Type_ArgumentsObject && - !static_cast<ArgumentsObject *>(m)->context()->strictMode; + !static_cast<ArgumentsObject *>(m)->d()->isStrict; } bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); diff --git a/src/qml/jsruntime/qv4arraybuffer.cpp b/src/qml/jsruntime/qv4arraybuffer.cpp index ffe9aa846f..3f06ce8bd5 100644 --- a/src/qml/jsruntime/qv4arraybuffer.cpp +++ b/src/qml/jsruntime/qv4arraybuffer.cpp @@ -40,6 +40,7 @@ #include "qv4typedarray_p.h" #include "qv4dataview_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" using namespace QV4; @@ -51,49 +52,42 @@ void Heap::ArrayBufferCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("ArrayBuffer")); } -void ArrayBufferCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); + ExecutionEngine *v4 = f->engine(); + Scope scope(v4); - ScopedValue l(scope, callData->argument(0)); + ScopedValue l(scope, argc ? argv[0] : Primitive::undefinedValue()); double dl = l->toInteger(); - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } + if (v4->hasException) + return Encode::undefined(); uint len = (uint)qBound(0., dl, (double)UINT_MAX); - if (len != dl) { - scope.result = v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); - return; - } + if (len != dl) + return v4->throwRangeError(QLatin1String("ArrayBuffer constructor: invalid length")); Scoped<ArrayBuffer> a(scope, v4->newArrayBuffer(len)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - } else { - scope.result = a->asReturnedValue(); - } + if (scope.engine->hasException) + return Encode::undefined(); + + return a->asReturnedValue(); } -void ArrayBufferCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } -void ArrayBufferCtor::method_isView(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferCtor::method_isView(const BuiltinFunction *, CallData *callData) { - QV4::Scoped<TypedArray> a(scope, callData->argument(0)); - if (!!a) { - scope.result = Encode(true); - return; - } - QV4::Scoped<DataView> v(scope, callData->argument(0)); - if (!!v) { - scope.result = Encode(true); - return; - } - scope.result = Encode(false); + if (callData->argc() < 1) + return Encode(false); + + if (callData->args[0].as<TypedArray>() || + callData->args[0].as<DataView>()) + return Encode(true); + + return Encode(false); } @@ -163,48 +157,52 @@ void ArrayBufferPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toString"), method_toString, 0); } -void ArrayBufferPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_get_byteLength(const BuiltinFunction *b, CallData *callData) { - Scoped<ArrayBuffer> v(scope, callData->thisObject); - if (!v) - THROW_TYPE_ERROR(); + ArrayBuffer *a = callData->thisObject.as<ArrayBuffer>(); + if (!a) + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->data->size); + return Encode(a->d()->data->size); } -void ArrayBufferPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_slice(const BuiltinFunction *b, CallData *callData) { - Scoped<ArrayBuffer> a(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + ArrayBuffer *a = callData->thisObject.as<ArrayBuffer>(); if (!a) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - double start = callData->argc > 0 ? callData->args[0].toInteger() : 0; - double end = (callData->argc < 2 || callData->args[1].isUndefined()) ? + double start = callData->argc() > 0 ? callData->args[0].toInteger() : 0; + double end = (callData->argc() < 2 || callData->args[1].isUndefined()) ? a->d()->data->size : callData->args[1].toInteger(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); double first = (start < 0) ? qMax(a->d()->data->size + start, 0.) : qMin(start, (double)a->d()->data->size); double final = (end < 0) ? qMax(a->d()->data->size + end, 0.) : qMin(end, (double)a->d()->data->size); + Scope scope(v4); ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - ScopedCallData cData(scope, 1); + JSCallData jsCallData(scope, 1); double newLen = qMax(final - first, 0.); - cData->args[0] = QV4::Encode(newLen); - constructor->construct(scope, cData); - QV4::Scoped<ArrayBuffer> newBuffer(scope, scope.result); + jsCallData->args[0] = QV4::Encode(newLen); + QV4::Scoped<ArrayBuffer> newBuffer(scope, constructor->callAsConstructor(jsCallData)); if (!newBuffer || newBuffer->d()->data->size < (int)newLen) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); memcpy(newBuffer->d()->data->data(), a->d()->data->data() + (uint)first, newLen); + return Encode::undefined(); } -void ArrayBufferPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayBufferPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - Scoped<ArrayBuffer> a(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + ArrayBuffer *a = callData->thisObject.as<ArrayBuffer>(); if (!a) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(QString::fromUtf8(a->asByteArray())); + return Encode(v4->newString(QString::fromUtf8(a->asByteArray()))); } diff --git a/src/qml/jsruntime/qv4arraybuffer_p.h b/src/qml/jsruntime/qv4arraybuffer_p.h index 4f7926d3dc..e144faa47f 100644 --- a/src/qml/jsruntime/qv4arraybuffer_p.h +++ b/src/qml/jsruntime/qv4arraybuffer_p.h @@ -78,10 +78,10 @@ struct ArrayBufferCtor: FunctionObject { V4_OBJECT2(ArrayBufferCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 void method_isView(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_isView(const BuiltinFunction *, CallData *callData); }; @@ -104,9 +104,9 @@ struct ArrayBufferPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_byteLength(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_slice(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4arraydata.cpp b/src/qml/jsruntime/qv4arraydata.cpp index df9884d84a..b77fab3305 100644 --- a/src/qml/jsruntime/qv4arraydata.cpp +++ b/src/qml/jsruntime/qv4arraydata.cpp @@ -43,6 +43,7 @@ #include "qv4runtime_p.h" #include "qv4argumentsobject_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" using namespace QV4; @@ -672,15 +673,14 @@ bool ArrayElementLessThan::operator()(Value v1, Value v2) const return false; if (v2.isUndefined() || v2.isEmpty()) return true; - ScopedObject o(scope, m_comparefn); + ScopedFunctionObject o(scope, m_comparefn); if (o) { Scope scope(o->engine()); ScopedValue result(scope); - ScopedCallData callData(scope, 2); - callData->thisObject = Primitive::undefinedValue(); - callData->args[0] = v1; - callData->args[1] = v2; - result = QV4::Runtime::method_callValue(scope.engine, m_comparefn, callData); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = v1; + jsCallData->args[1] = v2; + result = o->call(jsCallData); return result->toNumber() < 0; } @@ -754,7 +754,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c if (!arrayData || !arrayData->length()) return; - if (!(comparefn.isUndefined() || comparefn.as<Object>())) { + if (!comparefn.isUndefined() && !comparefn.isFunctionObject()) { engine->throwTypeError(); return; } @@ -833,7 +833,7 @@ void ArrayData::sort(ExecutionEngine *engine, Object *thisObject, const Value &c } - ArrayElementLessThan lessThan(engine, thisObject, comparefn); + ArrayElementLessThan lessThan(engine, thisObject, static_cast<const FunctionObject &>(comparefn)); Value *begin = thisObject->arrayData()->values.values; sortHelper(begin, begin + len, *begin, lessThan); diff --git a/src/qml/jsruntime/qv4arraydata_p.h b/src/qml/jsruntime/qv4arraydata_p.h index e1de2e82e6..6e41c756a8 100644 --- a/src/qml/jsruntime/qv4arraydata_p.h +++ b/src/qml/jsruntime/qv4arraydata_p.h @@ -144,7 +144,7 @@ DECLARE_HEAP_OBJECT(ArrayData, Base) { V4_ASSERT_IS_TRIVIAL(ArrayData) struct SimpleArrayData : public ArrayData { - uint mappedIndex(uint index) const { return (index + offset) % values.alloc; } + uint mappedIndex(uint index) const { index += offset; if (index >= values.alloc) index -= values.alloc; return index; } const Value &data(uint index) const { return values[mappedIndex(index)]; } void setData(EngineBase *e, uint index, Value newVal) { values.set(e, mappedIndex(index), newVal); diff --git a/src/qml/jsruntime/qv4arrayobject.cpp b/src/qml/jsruntime/qv4arrayobject.cpp index a2c19e1f2d..dede423b73 100644 --- a/src/qml/jsruntime/qv4arrayobject.cpp +++ b/src/qml/jsruntime/qv4arrayobject.cpp @@ -40,7 +40,7 @@ #include "qv4arrayobject_p.h" #include "qv4sparsearray_p.h" #include "qv4objectproto_p.h" -#include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include "qv4argumentsobject_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" @@ -55,35 +55,34 @@ void Heap::ArrayCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Array")); } -void ArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue ArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const ArrayCtor *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const ArrayCtor *>(f)->engine(); + Scope scope(v4); ScopedArrayObject a(scope, v4->newArrayObject()); uint len; - if (callData->argc == 1 && callData->args[0].isNumber()) { + if (argc == 1 && argv[0].isNumber()) { bool ok; - len = callData->args[0].asArrayLength(&ok); + len = argv[0].asArrayLength(&ok); - if (!ok) { - scope.result = v4->throwRangeError(callData->args[0]); - return; - } + if (!ok) + return v4->throwRangeError(argv[0]); if (len < 0x1000) a->arrayReserve(len); } else { - len = callData->argc; + len = argc; a->arrayReserve(len); - a->arrayPut(0, callData->args, len); + a->arrayPut(0, argv, len); } a->setArrayLengthUnchecked(len); - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -119,34 +118,36 @@ void ArrayPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("reduceRight"), method_reduceRight, 1); } -void ArrayPrototype::method_isArray(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_isArray(const BuiltinFunction *, CallData *callData) { - bool isArray = callData->argc && callData->args[0].as<ArrayObject>(); - scope.result = Encode(isArray); + bool isArray = callData->argc() && callData->args[0].as<ArrayObject>(); + return Encode(isArray); } -void ArrayPrototype::method_toString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_toString(const BuiltinFunction *builtin, CallData *callData) { - ScopedObject o(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); - ScopedString s(scope, scope.engine->newString(QStringLiteral("join"))); - ScopedFunctionObject f(scope, o->get(s)); - if (!!f) { - ScopedCallData d(scope, 0); - d->thisObject = callData->thisObject; - f->call(scope, d); - return; + ExecutionEngine *v4 = builtin->engine(); + if (!callData->thisObject.isObject()) { + callData->thisObject = callData->thisObject.toObject(v4); + if (v4->hasException) + return QV4::Encode::undefined(); } - ObjectPrototype::method_toString(builtin, scope, callData); + + callData->accumulator = v4->newString(QStringLiteral("join")); + callData->function = static_cast<Object &>(callData->thisObject).get(static_cast<String *>(&callData->accumulator)); + if (callData->function.isFunctionObject()) + return static_cast<FunctionObject &>(callData->function).call(&callData->thisObject, callData->args, callData->argc()); + return ObjectPrototype::method_toString(builtin, callData); } -void ArrayPrototype::method_toLocaleString(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_toLocaleString(const BuiltinFunction *builtin, CallData *callData) { - return method_toString(builtin, scope, callData); + return method_toString(builtin, callData); } -void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_concat(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject thisObject(scope, callData->thisObject.toObject(scope.engine)); if (!thisObject) RETURN_UNDEFINED(); @@ -162,7 +163,7 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa ScopedArrayObject elt(scope); ScopedObject eltAsObj(scope); ScopedValue entry(scope); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = callData->argc(); i < ei; ++i) { eltAsObj = callData->args[i]; elt = callData->args[i]; if (elt) { @@ -173,6 +174,7 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa const uint startIndex = result->getLength(); for (int i = 0, len = eltAsObj->getLength(); i < len; ++i) { entry = eltAsObj->getIndexed(i); + // spec says not to throw if this fails result->putIndexed(startIndex + i, entry); } } else { @@ -180,11 +182,12 @@ void ArrayPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallDa } } - scope.result = result; + return result.asReturnedValue(); } -void ArrayPrototype::method_find(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_find(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -195,30 +198,32 @@ void ArrayPrototype::method_find(const BuiltinFunction *, Scope &scope, CallData if (!callback) THROW_TYPE_ERROR(); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); ScopedValue v(scope); + ScopedValue result(scope); for (uint k = 0; k < len; ++k) { v = instance->getIndexed(k); CHECK_EXCEPTION(); - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + result = callback->call(jsCallData); CHECK_EXCEPTION(); - if (scope.result.toBoolean()) - RETURN_RESULT(v); + if (result->toBoolean()) + return v->asReturnedValue(); } RETURN_UNDEFINED(); } -void ArrayPrototype::method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_findIndex(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -229,37 +234,37 @@ void ArrayPrototype::method_findIndex(const BuiltinFunction *, Scope &scope, Cal if (!callback) THROW_TYPE_ERROR(); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); ScopedValue v(scope); + ScopedValue result(scope); for (uint k = 0; k < len; ++k) { v = instance->getIndexed(k); CHECK_EXCEPTION(); - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + result = callback->call(jsCallData); CHECK_EXCEPTION(); - if (scope.result.toBoolean()) - RETURN_RESULT(Encode(k)); + if (result->toBoolean()) + return Encode(k); } - RETURN_RESULT(Encode(-1)); + return Encode(-1); } -void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_join(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedValue arg(scope, callData->argument(0)); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); - if (!instance) { - scope.result = scope.engine->newString(); - return; - } + if (!instance) + return Encode(scope.engine->newString()); QString r4; if (arg->isUndefined()) @@ -270,10 +275,8 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData ScopedValue length(scope, instance->get(scope.engine->id_length())); const quint32 r2 = length->isUndefined() ? 0 : length->toUInt32(); - if (!r2) { - scope.result = scope.engine->newString(); - return; - } + if (!r2) + return Encode(scope.engine->newString()); QString R; @@ -311,11 +314,12 @@ void ArrayPrototype::method_join(const BuiltinFunction *, Scope &scope, CallData } } - scope.result = scope.engine->newString(R); + return Encode(scope.engine->newString(R)); } -void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_pop(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -331,18 +335,21 @@ void ArrayPrototype::method_pop(const BuiltinFunction *, Scope &scope, CallData ScopedValue result(scope, instance->getIndexed(len - 1)); CHECK_EXCEPTION(); - instance->deleteIndexedProperty(len - 1); - CHECK_EXCEPTION(); + if (!instance->deleteIndexedProperty(len - 1)) + return scope.engine->throwTypeError(); if (instance->isArrayObject()) instance->setArrayLength(len - 1); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); - scope.result = result; + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1)))) + return scope.engine->throwTypeError(); + } + return result->asReturnedValue(); } -void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_push(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -352,46 +359,51 @@ void ArrayPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData uint len = instance->getLength(); - if (len + callData->argc < len) { - // ughh... + if (len + callData->argc() < len) { + // ughh... this goes beyond UINT_MAX double l = len; ScopedString s(scope); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = callData->argc(); i < ei; ++i) { s = Primitive::fromDouble(l + i).toString(scope.engine); - instance->put(s, callData->args[i]); + if (!instance->put(s, callData->args[i])) + return scope.engine->throwTypeError(); } - double newLen = l + callData->argc; - if (!instance->isArrayObject()) - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); - else { + double newLen = l + callData->argc(); + if (!instance->isArrayObject()) { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)))) + return scope.engine->throwTypeError(); + } else { ScopedString str(scope, scope.engine->newString(QStringLiteral("Array.prototype.push: Overflow"))); - scope.result = scope.engine->throwRangeError(str); - return; + return scope.engine->throwRangeError(str); } - scope.result = Encode(newLen); - return; + return Encode(newLen); } - if (!callData->argc) + if (!callData->argc()) ; else if (!instance->protoHasArray() && instance->arrayData()->length() <= len && instance->arrayData()->type == Heap::ArrayData::Simple) { - instance->arrayData()->vtable()->putArray(instance, len, callData->args, callData->argc); + instance->arrayData()->vtable()->putArray(instance, len, callData->args, callData->argc()); len = instance->arrayData()->length(); } else { - for (int i = 0; i < callData->argc; ++i) - instance->putIndexed(len + i, callData->args[i]); - len += callData->argc; + for (int i = 0, ei = callData->argc(); i < ei; ++i) { + if (!instance->putIndexed(len + i, callData->args[i])) + return scope.engine->throwTypeError(); + } + len += callData->argc(); } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len))); + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len)))) + return scope.engine->throwTypeError(); + } - scope.result = Encode(len); + return Encode(len); } -void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reverse(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -407,21 +419,26 @@ void ArrayPrototype::method_reverse(const BuiltinFunction *, Scope &scope, CallD lval = instance->getIndexed(lo, &loExists); hval = instance->getIndexed(hi, &hiExists); CHECK_EXCEPTION(); + bool ok; if (hiExists) - instance->putIndexed(lo, hval); - else - instance->deleteIndexedProperty(lo); - CHECK_EXCEPTION(); - if (loExists) - instance->putIndexed(hi, lval); + ok = instance->putIndexed(lo, hval); else - instance->deleteIndexedProperty(hi); + ok = instance->deleteIndexedProperty(lo); + if (ok) { + if (loExists) + ok = instance->putIndexed(hi, lval); + else + ok = instance->deleteIndexedProperty(hi); + } + if (!ok) + return scope.engine->throwTypeError(); } - scope.result = instance; + return instance->asReturnedValue(); } -void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_shift(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -433,14 +450,16 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat if (!len) { if (!instance->isArrayObject()) - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0))); + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromInt32(0)))) + return scope.engine->throwTypeError(); RETURN_UNDEFINED(); } + ScopedValue result(scope); if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - scope.result = instance->arrayData()->vtable()->pop_front(instance); + result = instance->arrayData()->vtable()->pop_front(instance); } else { - scope.result = instance->getIndexed(0); + result = instance->getIndexed(0); CHECK_EXCEPTION(); ScopedValue v(scope); // do it the slow way @@ -448,24 +467,33 @@ void ArrayPrototype::method_shift(const BuiltinFunction *, Scope &scope, CallDat bool exists; v = instance->getIndexed(k, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k - 1, v); + ok = instance->putIndexed(k - 1, v); else - instance->deleteIndexedProperty(k - 1); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k - 1); + if (!ok) + return scope.engine->throwTypeError(); } - instance->deleteIndexedProperty(len - 1); - CHECK_EXCEPTION(); + bool ok = instance->deleteIndexedProperty(len - 1); + if (!ok) + return scope.engine->throwTypeError(); } if (instance->isArrayObject()) instance->setArrayLengthUnchecked(len - 1); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); + else { + bool ok = instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - 1))); + if (!ok) + return scope.engine->throwTypeError(); + } + + return result->asReturnedValue(); } -void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_slice(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); if (!o) RETURN_UNDEFINED(); @@ -481,7 +509,7 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat else start = (uint) s; uint end = len; - if (callData->argc > 1 && !callData->args[1].isUndefined()) { + if (callData->argc() > 1 && !callData->args[1].isUndefined()) { double e = callData->args[1].toInteger(); if (e < 0) end = (uint)qMax(len + e, 0.); @@ -501,11 +529,12 @@ void ArrayPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDat result->arraySet(n, v); ++n; } - scope.result = result; + return result->asReturnedValue(); } -void ArrayPrototype::method_sort(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_sort(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -514,11 +543,12 @@ void ArrayPrototype::method_sort(const BuiltinFunction *, Scope &scope, CallData ScopedValue comparefn(scope, callData->argument(0)); ArrayData::sort(scope.engine, instance, comparefn, len); - scope.result = callData->thisObject; + return callData->thisObject.asReturnedValue(); } -void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_splice(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -547,22 +577,24 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa } newArray->setArrayLengthUnchecked(deleteCount); - uint itemCount = callData->argc < 2 ? 0 : callData->argc - 2; + uint itemCount = callData->argc() < 2 ? 0 : callData->argc() - 2; if (itemCount < deleteCount) { for (uint k = start; k < len - deleteCount; ++k) { bool exists; v = instance->getIndexed(k + deleteCount, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k + itemCount, v); + ok = instance->putIndexed(k + itemCount, v); else - instance->deleteIndexedProperty(k + itemCount); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k + itemCount); + if (!ok) + return scope.engine->throwTypeError(); } for (uint k = len; k > len - deleteCount + itemCount; --k) { - instance->deleteIndexedProperty(k - 1); - CHECK_EXCEPTION(); + if (!instance->deleteIndexedProperty(k - 1)) + return scope.engine->throwTypeError(); } } else if (itemCount > deleteCount) { uint k = len - deleteCount; @@ -570,30 +602,29 @@ void ArrayPrototype::method_splice(const BuiltinFunction *, Scope &scope, CallDa bool exists; v = instance->getIndexed(k + deleteCount - 1, &exists); CHECK_EXCEPTION(); + bool ok; if (exists) - instance->putIndexed(k + itemCount - 1, v); + ok = instance->putIndexed(k + itemCount - 1, v); else - instance->deleteIndexedProperty(k + itemCount - 1); - CHECK_EXCEPTION(); + ok = instance->deleteIndexedProperty(k + itemCount - 1); + if (!ok) + return scope.engine->throwTypeError(); --k; } } - for (uint i = 0; i < itemCount; ++i) { + for (uint i = 0; i < itemCount; ++i) instance->putIndexed(start + i, callData->args[i + 2]); - CHECK_EXCEPTION(); - } - bool wasStrict = scope.engine->current->strictMode; - scope.engine->current->strictMode = true; - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount))); + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(len - deleteCount + itemCount)))) + return scope.engine->throwTypeError(); - scope.result = newArray; - scope.engine->current->strictMode = wasStrict; + return newArray->asReturnedValue(); } -void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_unshift(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -605,52 +636,57 @@ void ArrayPrototype::method_unshift(const BuiltinFunction *, Scope &scope, CallD if (!instance->protoHasArray() && !instance->arrayData()->attrs && instance->arrayData()->length() <= len && instance->arrayData()->type != Heap::ArrayData::Custom) { - instance->arrayData()->vtable()->push_front(instance, callData->args, callData->argc); + instance->arrayData()->vtable()->push_front(instance, callData->args, callData->argc()); } else { ScopedValue v(scope); for (uint k = len; k > 0; --k) { bool exists; v = instance->getIndexed(k - 1, &exists); + bool ok; if (exists) - instance->putIndexed(k + callData->argc - 1, v); + ok = instance->putIndexed(k + callData->argc() - 1, v); else - instance->deleteIndexedProperty(k + callData->argc - 1); + ok = instance->deleteIndexedProperty(k + callData->argc() - 1); + if (!ok) + return scope.engine->throwTypeError(); + } + for (int i = 0, ei = callData->argc(); i < ei; ++i) { + bool ok = instance->putIndexed(i, callData->args[i]); + if (!ok) + return scope.engine->throwTypeError(); } - for (int i = 0; i < callData->argc; ++i) - instance->putIndexed(i, callData->args[i]); } - uint newLen = len + callData->argc; + uint newLen = len + callData->argc(); if (instance->isArrayObject()) instance->setArrayLengthUnchecked(newLen); - else - instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen))); + else { + if (!instance->put(scope.engine->id_length(), ScopedValue(scope, Primitive::fromDouble(newLen)))) + return scope.engine->throwTypeError(); + } - scope.result = Encode(newLen); + return Encode(newLen); } -void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_indexOf(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - if (!len) { - scope.result = Encode(-1); - return; - } + if (!len) + return Encode(-1); ScopedValue searchValue(scope, callData->argument(0)); uint fromIndex = 0; - if (callData->argc >= 2) { + if (callData->argc() >= 2) { double f = callData->args[1].toInteger(); CHECK_EXCEPTION(); - if (f >= len) { - scope.result = Encode(-1); - return; - } + if (f >= len) + return Encode(-1); if (f < 0) f = qMax(len + f, 0.); fromIndex = (uint) f; @@ -661,13 +697,10 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD for (uint k = fromIndex; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { - scope.result = Encode(k); - return; - } + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) + return Encode(k); } - scope.result = Encode(-1); - return; + return Encode(-1); } ScopedValue value(scope); @@ -679,14 +712,11 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD bool exists; value = instance->getIndexed(i, &exists); CHECK_EXCEPTION(); - if (exists && RuntimeHelpers::strictEqual(value, searchValue)) { - scope.result = Encode(i); - return; - } + if (exists && RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(i); } } else if (!instance->arrayData()) { - scope.result = Encode(-1); - return; + return Encode(-1); } else { Q_ASSERT(instance->arrayType() == Heap::ArrayData::Simple || instance->arrayType() == Heap::ArrayData::Complex); Heap::SimpleArrayData *sa = instance->d()->arrayData.cast<Heap::SimpleArrayData>(); @@ -696,47 +726,42 @@ void ArrayPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallD while (idx < len) { value = sa->data(idx); CHECK_EXCEPTION(); - if (RuntimeHelpers::strictEqual(value, searchValue)) { - scope.result = Encode(idx); - return; - } + if (RuntimeHelpers::strictEqual(value, searchValue)) + return Encode(idx); ++idx; } } - scope.result = Encode(-1); + return Encode(-1); } -void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_lastIndexOf(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); uint len = instance->getLength(); - if (!len) { - scope.result = Encode(-1); - return; - } + if (!len) + return Encode(-1); ScopedValue searchValue(scope); uint fromIndex = len; - if (callData->argc >= 1) + if (callData->argc() >= 1) searchValue = callData->argument(0); else searchValue = Primitive::undefinedValue(); - if (callData->argc >= 2) { + if (callData->argc() >= 2) { double f = callData->args[1].toInteger(); CHECK_EXCEPTION(); if (f > 0) f = qMin(f, (double)(len - 1)); else if (f < 0) { f = len + f; - if (f < 0) { - scope.result = Encode(-1); - return; - } + if (f < 0) + return Encode(-1); } fromIndex = (uint) f + 1; } @@ -747,16 +772,15 @@ void ArrayPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, C bool exists; v = instance->getIndexed(k, &exists); CHECK_EXCEPTION(); - if (exists && RuntimeHelpers::strictEqual(v, searchValue)) { - scope.result = Encode(k); - return; - } + if (exists && RuntimeHelpers::strictEqual(v, searchValue)) + return Encode(k); } - scope.result = Encode(-1); + return Encode(-1); } -void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_every(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -767,10 +791,10 @@ void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallDat if (!callback) THROW_TYPE_ERROR(); - ScopedCallData cData(scope, 3); - cData->args[2] = instance; - cData->thisObject = callData->argument(1); + ScopedValue r(scope); ScopedValue v(scope); + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); bool ok = true; for (uint k = 0; ok && k < len; ++k) { @@ -779,16 +803,18 @@ void ArrayPrototype::method_every(const BuiltinFunction *, Scope &scope, CallDat if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - ok = scope.result.toBoolean(); + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + r = callback->call(jsCallData); + ok = r->toBoolean(); } - scope.result = Encode(ok); + return Encode(ok); } -void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_some(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -799,10 +825,10 @@ void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData if (!callback) THROW_TYPE_ERROR(); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; ScopedValue v(scope); + ScopedValue result(scope); + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); for (uint k = 0; k < len; ++k) { bool exists; @@ -810,19 +836,19 @@ void ArrayPrototype::method_some(const BuiltinFunction *, Scope &scope, CallData if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - if (scope.result.toBoolean()) { - scope.result = Encode(true); - return; - } + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + result = callback->call(jsCallData); + if (result->toBoolean()) + return Encode(true); } - scope.result = Encode(false); + return Encode(false); } -void ArrayPrototype::method_forEach(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_forEach(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -833,26 +859,27 @@ void ArrayPrototype::method_forEach(const BuiltinFunction *, Scope &scope, CallD if (!callback) THROW_TYPE_ERROR(); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - ScopedValue v(scope); + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); + for (uint k = 0; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + callback->call(jsCallData); } RETURN_UNDEFINED(); } -void ArrayPrototype::method_map(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_map(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -867,27 +894,29 @@ void ArrayPrototype::method_map(const BuiltinFunction *, Scope &scope, CallData a->arrayReserve(len); a->setArrayLengthUnchecked(len); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - ScopedValue v(scope); + ScopedValue mapped(scope); + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); + for (uint k = 0; k < len; ++k) { bool exists; v = instance->getIndexed(k, &exists); if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - a->arraySet(k, scope.result); + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + mapped = callback->call(jsCallData); + a->arraySet(k, mapped); } - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_filter(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -901,11 +930,10 @@ void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallDa ScopedArrayObject a(scope, scope.engine->newArrayObject()); a->arrayReserve(len); - ScopedCallData cData(scope, 3); - cData->thisObject = callData->argument(1); - cData->args[2] = instance; - + ScopedValue selected(scope); ScopedValue v(scope); + JSCallData jsCallData(scope, 3); + *jsCallData->thisObject = callData->argument(1); uint to = 0; for (uint k = 0; k < len; ++k) { @@ -914,19 +942,21 @@ void ArrayPrototype::method_filter(const BuiltinFunction *, Scope &scope, CallDa if (!exists) continue; - cData->args[0] = v; - cData->args[1] = Primitive::fromDouble(k); - callback->call(scope, cData); - if (scope.result.toBoolean()) { + jsCallData->args[0] = v; + jsCallData->args[1] = Primitive::fromDouble(k); + jsCallData->args[2] = instance; + selected = callback->call(jsCallData); + if (selected->toBoolean()) { a->arraySet(to, v); ++to; } } - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void ArrayPrototype::method_reduce(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reduce(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -938,42 +968,43 @@ void ArrayPrototype::method_reduce(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); uint k = 0; + ScopedValue acc(scope); ScopedValue v(scope); - if (callData->argc > 1) { - scope.result = callData->argument(1); + if (callData->argc() > 1) { + acc = callData->argument(1); } else { bool kPresent = false; while (k < len && !kPresent) { v = instance->getIndexed(k, &kPresent); if (kPresent) - scope.result = v; + acc = v; ++k; } if (!kPresent) THROW_TYPE_ERROR(); } - ScopedCallData cData(scope, 4); - cData->thisObject = Primitive::undefinedValue(); - cData->args[0] = scope.result; - cData->args[3] = instance; + JSCallData jsCallData(scope, 4); while (k < len) { bool kPresent; v = instance->getIndexed(k, &kPresent); if (kPresent) { - cData->args[0] = scope.result; - cData->args[1] = v; - cData->args[2] = Primitive::fromDouble(k); - callback->call(scope, cData); + jsCallData->args[0] = acc; + jsCallData->args[1] = v; + jsCallData->args[2] = Primitive::fromDouble(k); + jsCallData->args[3] = instance; + acc = callback->call(jsCallData); } ++k; } + return acc->asReturnedValue(); } -void ArrayPrototype::method_reduceRight(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ArrayPrototype::method_reduceRight(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); if (!instance) RETURN_UNDEFINED(); @@ -985,43 +1016,43 @@ void ArrayPrototype::method_reduceRight(const BuiltinFunction *, Scope &scope, C THROW_TYPE_ERROR(); if (len == 0) { - if (callData->argc == 1) + if (callData->argc() == 1) THROW_TYPE_ERROR(); - scope.result = callData->argument(1); - return; + return callData->argument(1); } uint k = len; + ScopedValue acc(scope); ScopedValue v(scope); - if (callData->argc > 1) { - scope.result = callData->argument(1); + if (callData->argc() > 1) { + acc = callData->argument(1); } else { bool kPresent = false; while (k > 0 && !kPresent) { v = instance->getIndexed(k - 1, &kPresent); if (kPresent) - scope.result = v; + acc = v; --k; } if (!kPresent) THROW_TYPE_ERROR(); } - ScopedCallData cData(scope, 4); - cData->thisObject = Primitive::undefinedValue(); - cData->args[3] = instance; + JSCallData jsCallData(scope, 4); + *jsCallData->thisObject = Primitive::undefinedValue(); while (k > 0) { bool kPresent; v = instance->getIndexed(k - 1, &kPresent); if (kPresent) { - cData->args[0] = scope.result; - cData->args[1] = v; - cData->args[2] = Primitive::fromDouble(k - 1); - callback->call(scope, cData); + jsCallData->args[0] = acc; + jsCallData->args[1] = v; + jsCallData->args[2] = Primitive::fromDouble(k - 1); + jsCallData->args[3] = instance; + acc = callback->call(jsCallData); } --k; } - scope.result = scope.result.asReturnedValue(); + return acc->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4arrayobject_p.h b/src/qml/jsruntime/qv4arrayobject_p.h index 689752433b..db12811cd7 100644 --- a/src/qml/jsruntime/qv4arrayobject_p.h +++ b/src/qml/jsruntime/qv4arrayobject_p.h @@ -70,38 +70,38 @@ struct ArrayCtor: FunctionObject { V4_OBJECT2(ArrayCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 ArrayPrototype: ArrayObject { void init(ExecutionEngine *engine, Object *ctor); - static void method_isArray(const BuiltinFunction *, Scope &, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *builtin, Scope &, CallData *callData); - static void method_concat(const BuiltinFunction *, Scope &, CallData *callData); - static void method_find(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_findIndex(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_join(const BuiltinFunction *, Scope &, CallData *callData); - static void method_pop(const BuiltinFunction *, Scope &, CallData *callData); - static void method_push(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reverse(const BuiltinFunction *, Scope &, CallData *callData); - static void method_shift(const BuiltinFunction *, Scope &, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &, CallData *callData); - static void method_sort(const BuiltinFunction *, Scope &, CallData *callData); - static void method_splice(const BuiltinFunction *, Scope &, CallData *callData); - static void method_unshift(const BuiltinFunction *, Scope &, CallData *callData); - static void method_indexOf(const BuiltinFunction *, Scope &, CallData *callData); - static void method_lastIndexOf(const BuiltinFunction *, Scope &, CallData *callData); - static void method_every(const BuiltinFunction *, Scope &, CallData *callData); - static void method_some(const BuiltinFunction *, Scope &, CallData *callData); - static void method_forEach(const BuiltinFunction *, Scope &, CallData *callData); - static void method_map(const BuiltinFunction *, Scope &, CallData *callData); - static void method_filter(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reduce(const BuiltinFunction *, Scope &, CallData *callData); - static void method_reduceRight(const BuiltinFunction *, Scope &, CallData *callData); + static ReturnedValue method_isArray(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_concat(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_find(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_findIndex(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_join(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_pop(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_push(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_reverse(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_shift(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_slice(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_sort(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_splice(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_unshift(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_indexOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_lastIndexOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_every(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_some(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_forEach(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_map(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_filter(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_reduce(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_reduceRight(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4booleanobject.cpp b/src/qml/jsruntime/qv4booleanobject.cpp index c8e9ebb2dd..2d89366e74 100644 --- a/src/qml/jsruntime/qv4booleanobject.cpp +++ b/src/qml/jsruntime/qv4booleanobject.cpp @@ -50,16 +50,16 @@ void Heap::BooleanCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Boolean")); } -void BooleanCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue BooleanCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) { - bool n = callData->argc ? callData->args[0].toBoolean() : false; - scope.result = Encode(scope.engine->newBooleanObject(n)); + bool n = argc ? argv[0].toBoolean() : false; + return Encode(that->engine()->newBooleanObject(n)); } -void BooleanCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue BooleanCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) { - bool value = callData->argc ? callData->args[0].toBoolean() : 0; - scope.result = Encode(value); + bool value = argc ? argv[0].toBoolean() : 0; + return Encode(value); } void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -73,31 +73,30 @@ void BooleanPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(engine->id_valueOf(), method_valueOf); } -void BooleanPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue BooleanPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); bool result; if (callData->thisObject.isBoolean()) { result = callData->thisObject.booleanValue(); } else { const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); if (!thisObject) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); result = thisObject->value(); } - scope.result = result ? scope.engine->id_true() : scope.engine->id_false(); + return Encode(result ? v4->id_true() : v4->id_false()); } -void BooleanPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue BooleanPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - if (callData->thisObject.isBoolean()) { - scope.result = callData->thisObject.asReturnedValue(); - return; - } + if (callData->thisObject.isBoolean()) + return callData->thisObject.asReturnedValue(); const BooleanObject *thisObject = callData->thisObject.as<BooleanObject>(); if (!thisObject) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = Encode(thisObject->value()); + return Encode(thisObject->value()); } diff --git a/src/qml/jsruntime/qv4booleanobject_p.h b/src/qml/jsruntime/qv4booleanobject_p.h index ca2cf7d17a..7a686e0d53 100644 --- a/src/qml/jsruntime/qv4booleanobject_p.h +++ b/src/qml/jsruntime/qv4booleanobject_p.h @@ -70,8 +70,8 @@ struct BooleanCtor: FunctionObject { V4_OBJECT2(BooleanCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct BooleanPrototype: BooleanObject @@ -79,8 +79,8 @@ struct BooleanPrototype: BooleanObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 6807c835b0..9d7cafabc8 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -48,59 +48,50 @@ #include "qv4errorobject_p.h" #include "qv4string_p.h" #include "qv4qmlcontext_p.h" -#include "qv4profiling_p.h" -#include <private/qqmljavascriptexpression_p.h> using namespace QV4; DEFINE_MANAGED_VTABLE(ExecutionContext); -DEFINE_MANAGED_VTABLE(SimpleCallContext); DEFINE_MANAGED_VTABLE(CallContext); -DEFINE_MANAGED_VTABLE(WithContext); DEFINE_MANAGED_VTABLE(CatchContext); -DEFINE_MANAGED_VTABLE(GlobalContext); -Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) +Heap::CallContext *ExecutionContext::newCallContext(Heap::ExecutionContext *outer, Function *function, CallData *callData) { - uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals); + uint nFormals = qMax(static_cast<uint>(callData->argc()), function->nFormals); + uint localsAndFormals = function->compiledFunction->nLocals + nFormals; size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); - ExecutionEngine *v4 = engine(); - Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory); - c->init(Heap::ExecutionContext::Type_CallContext); + ExecutionEngine *v4 = outer->internalClass->engine; + Heap::CallContext *c = v4->memoryManager->allocManaged<CallContext>(requiredMemory, function->internalClass); + c->init(); c->v4Function = function; - c->strictMode = function->isStrict(); - c->outer.set(v4, this->d()); - - c->compilationUnit = function->compilationUnit; - c->lookups = function->compilationUnit->runtimeLookups; - c->constantTable = function->compilationUnit->constants; + c->outer.set(v4, outer); + c->function.set(v4, static_cast<Heap::FunctionObject *>(callData->function.m())); const CompiledData::Function *compiledFunction = function->compiledFunction; uint nLocals = compiledFunction->nLocals; c->locals.size = nLocals; c->locals.alloc = localsAndFormals; -#if QT_POINTER_SIZE == 8 - // memory allocated from the JS heap is 0 initialized, so skip the std::fill() below + // memory allocated from the JS heap is 0 initialized, so check if undefined is 0 Q_ASSERT(Primitive::undefinedValue().asReturnedValue() == 0); -#else - if (nLocals) - std::fill(c->locals.values, c->locals.values + nLocals, Primitive::undefinedValue()); -#endif - c->callData = reinterpret_cast<CallData *>(c->locals.values + nLocals); - ::memcpy(c->callData, callData, sizeof(CallData) - sizeof(Value) + static_cast<uint>(callData->argc) * sizeof(Value)); - if (callData->argc < static_cast<int>(compiledFunction->nFormals)) - std::fill(c->callData->args + c->callData->argc, c->callData->args + compiledFunction->nFormals, Primitive::undefinedValue()); + ::memcpy(c->locals.values + nLocals, &callData->args[0], nFormals * sizeof(Value)); + c->nArgs = callData->argc(); return c; } -Heap::WithContext *ExecutionContext::newWithContext(Heap::Object *with) +Heap::ExecutionContext *ExecutionContext::newWithContext(Heap::Object *with) { - return engine()->memoryManager->alloc<WithContext>(d(), with); + Heap::ExecutionContext *c = engine()->memoryManager->alloc<ExecutionContext>(Heap::ExecutionContext::Type_WithContext); + c->outer.set(engine(), d()); + c->activation.set(engine(), with); + + c->v4Function = d()->v4Function; + + return c; } Heap::CatchContext *ExecutionContext::newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue) @@ -120,25 +111,23 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) while (ctx) { switch (ctx->d()->type) { case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); if (!activation) { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx->d()); if (!c->activation) c->activation.set(scope.engine, scope.engine->newObject()); activation = c->activation; } break; - } case Heap::ExecutionContext::Type_QmlContext: { // this is ugly, as it overrides the inner callcontext, but has to stay as long // as bindings still get their own callcontext - Heap::QmlContext *qml = static_cast<Heap::QmlContext *>(ctx->d()); - activation = qml->qml; + activation = ctx->d()->activation; break; } case Heap::ExecutionContext::Type_GlobalContext: { + Q_ASSERT(scope.engine->globalObject->d() == ctx->d()->activation); if (!activation) - activation = scope.engine->globalObject; + activation = ctx->d()->activation; break; } default: @@ -155,99 +144,47 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) activation->__defineOwnProperty__(scope.engine, name, desc, attrs); } -void Heap::GlobalContext::init(ExecutionEngine *eng) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_GlobalContext); - global.set(eng, eng->globalObject->d()); -} - void Heap::CatchContext::init(ExecutionContext *outerContext, String *exceptionVarName, const Value &exceptionValue) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_CatchContext); outer.set(internalClass->engine, outerContext); - strictMode = outer->strictMode; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; + v4Function = outer->v4Function; this->exceptionVarName.set(internalClass->engine, exceptionVarName); this->exceptionValue.set(internalClass->engine, exceptionValue); } -void Heap::WithContext::init(ExecutionContext *outerContext, Object *with) -{ - Heap::ExecutionContext::init(Heap::ExecutionContext::Type_WithContext); - outer.set(internalClass->engine, outerContext); - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; - - withObject.set(internalClass->engine, with); -} - -Identifier * const *SimpleCallContext::formals() const -{ - return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() : 0; -} - -unsigned int SimpleCallContext::formalCount() const -{ - return d()->v4Function ? d()->v4Function->nFormals : 0; -} - -Identifier * const *SimpleCallContext::variables() const -{ - return d()->v4Function ? d()->v4Function->internalClass->nameMap.constData() + d()->v4Function->nFormals : 0; -} - -unsigned int SimpleCallContext::variableCount() const -{ - return d()->v4Function ? d()->v4Function->compiledFunction->nLocals : 0; -} - - - bool ExecutionContext::deleteProperty(String *name) { name->makeIdentifier(); Identifier *id = name->identifier(); - Scope scope(this); - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + 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->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return false; break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject withObject(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - if (withObject->hasProperty(name)) - return withObject->deleteProperty(name); - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - if (global->hasProperty(name)) - return global->deleteProperty(name); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - uint index = c->v4Function->internalClass->find(id); + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); + uint index = c->internalClass->find(id); if (index < UINT_MAX) // ### throw in strict mode? return false; - ScopedObject qml(scope, c->activation); - if (qml && qml->hasProperty(name)) - return qml->deleteProperty(name); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_WithContext: + case Heap::ExecutionContext::Type_GlobalContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject object(scope, ctx->activation); + if (object && object->hasProperty(name)) + return object->deleteProperty(name); + } break; } case Heap::ExecutionContext::Type_QmlContext: @@ -256,291 +193,164 @@ bool ExecutionContext::deleteProperty(String *name) } } - if (d()->strictMode) - engine()->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(name->toQString())); - return true; + return !d()->v4Function->isStrict(); } -// Do a standard call with this execution context as the outer scope -void ExecutionContext::call(Scope &scope, CallData *callData, Function *function, const FunctionObject *f) -{ - ExecutionContextSaver ctxSaver(scope); - - Scoped<CallContext> ctx(scope, newCallContext(function, callData)); - if (f) - ctx->d()->function.set(scope.engine, f->d()); - scope.engine->pushContext(ctx); - - scope.result = Q_V4_PROFILE(scope.engine, function); - - if (function->hasQmlDependencies) - QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope); -} - -// Do a simple, fast call with this execution context as the outer scope -void QV4::ExecutionContext::simpleCall(Scope &scope, CallData *callData, Function *function) -{ - Q_ASSERT(function->canUseSimpleFunction()); - - ExecutionContextSaver ctxSaver(scope); - - SimpleCallContext::Data *ctx = scope.engine->memoryManager->allocSimpleCallContext(); - - ctx->strictMode = function->isStrict(); - ctx->callData = callData; - ctx->v4Function = function; - ctx->compilationUnit = function->compilationUnit; - ctx->lookups = function->compilationUnit->runtimeLookups; - ctx->constantTable = function->compilationUnit->constants; - ctx->outer.set(scope.engine, this->d()); - for (int i = callData->argc; i < (int)function->nFormals; ++i) - callData->args[i] = Encode::undefined(); - - scope.engine->pushContext(ctx); - Q_ASSERT(scope.engine->current == ctx); - - scope.result = Q_V4_PROFILE(scope.engine, function); - - if (function->hasQmlDependencies) - QQmlPropertyCapture::registerQmlDependencies(function->compiledFunction, scope); - scope.engine->memoryManager->freeSimpleCallContext(); -} - -void ExecutionContext::setProperty(String *name, const Value &value) +ExecutionContext::Error ExecutionContext::setProperty(String *name, const Value &value) { name->makeIdentifier(); Identifier *id = name->identifier(); - Scope scope(this); - ScopedContext ctx(scope, this); - ScopedObject activation(scope); + QV4::ExecutionEngine *v4 = engine(); + Heap::ExecutionContext *ctx = d(); - for (; ctx; ctx = ctx->d()->outer) { - activation = (Object *)0; - switch (ctx->d()->type) { + for (; ctx; ctx = ctx->outer) { + switch (ctx->type) { case Heap::ExecutionContext::Type_CatchContext: { - Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) { - c->exceptionValue.set(scope.engine, value); - return; + c->exceptionValue.set(v4, value); + return NoError; } break; } case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); + Scope scope(v4); + ScopedObject w(scope, ctx->activation); if (w->hasProperty(name)) { - w->put(name, value); - return; + if (!w->put(name, value)) + return TypeError; + return NoError; } break; } - case Heap::ExecutionContext::Type_GlobalContext: { - activation = static_cast<Heap::GlobalContext *>(ctx->d())->global; - break; - } - case Heap::ExecutionContext::Type_CallContext: - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); if (c->v4Function) { - uint index = c->v4Function->internalClass->find(id); + uint index = c->internalClass->find(id); if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) { - c->callData->args[c->v4Function->nFormals - index - 1] = value; - } else { - Q_ASSERT(c->type == Heap::ExecutionContext::Type_CallContext); - index -= c->v4Function->nFormals; - static_cast<Heap::CallContext *>(c)->locals.set(scope.engine, index, value); - } - return; + static_cast<Heap::CallContext *>(c)->locals.set(v4, index, value); + return NoError; } } - activation = c->activation; - break; } + Q_FALLTHROUGH(); + case Heap::ExecutionContext::Type_GlobalContext: + if (ctx->activation) { + uint member = ctx->activation->internalClass->find(id); + if (member < UINT_MAX) { + Scope scope(v4); + ScopedObject a(scope, ctx->activation); + if (!a->putValue(member, value)) + return TypeError; + return NoError; + } + } + break; case Heap::ExecutionContext::Type_QmlContext: { - activation = static_cast<Heap::QmlContext *>(ctx->d())->qml; - activation->put(name, value); - return; + Scope scope(v4); + ScopedObject activation(scope, ctx->activation); + if (!activation->put(name, value)) + return TypeError; + return NoError; } } - if (activation) { - uint member = activation->internalClass()->find(id); - if (member < UINT_MAX) { - activation->putValue(member, value); - return; - } - } } - if (d()->strictMode || name->equals(engine()->id_this())) { - ScopedValue n(scope, name->asReturnedValue()); - engine()->throwReferenceError(n); - return; - } - engine()->globalObject->put(name, value); + return RangeError; } ReturnedValue ExecutionContext::getProperty(String *name) { - Scope scope(this); - ScopedValue v(scope); name->makeIdentifier(); - if (name->equals(engine()->id_this())) - return thisObject().asReturnedValue(); - - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + 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->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - bool hasProperty = false; - v = w->get(name, &hasProperty); - if (hasProperty) { - return v->asReturnedValue(); - } - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - - name->makeIdentifier(); + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - if (c->type == Heap::ExecutionContext::Type_CallContext) - return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - ScopedObject activation(scope, c->activation); - if (activation) { + uint index = c->internalClass->find(id); + if (index < UINT_MAX) + return c->locals[index].asReturnedValue(); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_WithContext: + case Heap::ExecutionContext::Type_GlobalContext: + case Heap::ExecutionContext::Type_QmlContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(name, &hasProperty); if (hasProperty) - return v->asReturnedValue(); - } - - if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { - if (name->equals(ScopedString(scope, c->v4Function->name()))) - if (auto func = static_cast<Heap::CallContext *>(c)->function) - return func->asReturnedValue(); + return v; } break; } - case Heap::ExecutionContext::Type_QmlContext: { - ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); - bool hasProperty = false; - v = qml->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } } } - ScopedValue n(scope, name); - return engine()->throwReferenceError(n); + return engine()->throwReferenceError(*name); } ReturnedValue ExecutionContext::getPropertyAndBase(String *name, Value *base) { - Scope scope(this); - ScopedValue v(scope); base->setM(0); name->makeIdentifier(); - if (name->equals(engine()->id_this())) - return thisObject().asReturnedValue(); - - ScopedContext ctx(scope, this); - for (; ctx; ctx = ctx->d()->outer) { - switch (ctx->d()->type) { + 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->d()); + Heap::CatchContext *c = static_cast<Heap::CatchContext *>(ctx); if (c->exceptionVarName->isEqualTo(name->d())) return c->exceptionValue.asReturnedValue(); break; } - case Heap::ExecutionContext::Type_WithContext: { - ScopedObject w(scope, static_cast<Heap::WithContext *>(ctx->d())->withObject); - bool hasProperty = false; - v = w->get(name, &hasProperty); - if (hasProperty) { - base->setM(w->d()); - return v->asReturnedValue(); - } - break; - } - case Heap::ExecutionContext::Type_GlobalContext: { - ScopedObject global(scope, static_cast<Heap::GlobalContext *>(ctx->d())->global); - bool hasProperty = false; - v = global->get(name, &hasProperty); - if (hasProperty) - return v->asReturnedValue(); - break; - } - case Heap::ExecutionContext::Type_CallContext: - Q_FALLTHROUGH(); - case Heap::ExecutionContext::Type_SimpleCallContext: { - Heap::SimpleCallContext *c = static_cast<Heap::SimpleCallContext *>(ctx->d()); - + case Heap::ExecutionContext::Type_CallContext: { + Heap::CallContext *c = static_cast<Heap::CallContext *>(ctx); name->makeIdentifier(); Identifier *id = name->identifier(); - uint index = c->v4Function->internalClass->find(id); - if (index < UINT_MAX) { - if (index < c->v4Function->nFormals) - return c->callData->args[c->v4Function->nFormals - index - 1].asReturnedValue(); - if (c->type == Heap::ExecutionContext::Type_CallContext) - return static_cast<Heap::CallContext *>(c)->locals[index - c->v4Function->nFormals].asReturnedValue(); - } - ScopedObject activation(scope, c->activation); - if (activation) { + uint index = c->internalClass->find(id); + if (index < UINT_MAX) + return c->locals[index].asReturnedValue(); + Q_FALLTHROUGH(); + } + case Heap::ExecutionContext::Type_GlobalContext: { + if (ctx->activation) { + Scope scope(this); + ScopedObject activation(scope, ctx->activation); bool hasProperty = false; - v = activation->get(name, &hasProperty); + ReturnedValue v = activation->get(name, &hasProperty); if (hasProperty) - return v->asReturnedValue(); - } - - if (c->v4Function->isNamedExpression() && c->type == Heap::ExecutionContext::Type_CallContext) { - if (name->equals(ScopedString(scope, c->v4Function->name()))) - if (auto func = static_cast<Heap::CallContext *>(c)->function) - return func->asReturnedValue(); + return v; } break; } + case Heap::ExecutionContext::Type_WithContext: case Heap::ExecutionContext::Type_QmlContext: { - ScopedObject qml(scope, static_cast<Heap::QmlContext *>(ctx->d())->qml); + Scope scope(this); + ScopedObject o(scope, ctx->activation); bool hasProperty = false; - v = qml->get(name, &hasProperty); + ReturnedValue v = o->get(name, &hasProperty); if (hasProperty) { - base->setM(qml->d()); - return v->asReturnedValue(); + base->setM(o->d()); + return v; } break; } } } - ScopedValue n(scope, name); - return engine()->throwReferenceError(n); + return engine()->throwReferenceError(*name); } Function *ExecutionContext::getFunction() const @@ -548,9 +358,10 @@ Function *ExecutionContext::getFunction() const Scope scope(engine()); ScopedContext it(scope, this->d()); for (; it; it = it->d()->outer) { - if (const SimpleCallContext *callCtx = it->asSimpleCallContext()) + if (const CallContext *callCtx = it->asCallContext()) return callCtx->d()->v4Function; - else if (it->asCatchContext() || it->asWithContext()) + else if (it->d()->type == Heap::ExecutionContext::Type_CatchContext || + it->d()->type == Heap::ExecutionContext::Type_WithContext) continue; // look in the parent context for a FunctionObject else break; @@ -558,3 +369,9 @@ Function *ExecutionContext::getFunction() const return 0; } + + +void Heap::CallContext::setArg(uint index, Value v) +{ + locals.set(internalClass->engine, locals.size + index, v); +} diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index a854c324d0..7ed3320f8a 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -68,48 +68,57 @@ struct Function; struct Function; struct Identifier; struct CallContext; -struct SimpleCallContext; struct CatchContext; -struct WithContext; struct QmlContext; struct QQmlContextWrapper; -// Attention: Make sure that this structure is the same size on 32-bit and 64-bit -// architecture or you'll have to change the JIT code. struct CallData { - // below is to be compatible with Value. Initialize tag to 0 -#if Q_BYTE_ORDER != Q_LITTLE_ENDIAN - uint tag; -#endif - int argc; -#if Q_BYTE_ORDER == Q_LITTLE_ENDIAN - uint tag; -#endif + enum Offsets { + Function = 0, + Context = 1, + Accumulator = 2, + This = 3, + Argc = 4 + }; + + Value function; + Value context; + Value accumulator; + Value thisObject; + Value _argc; + + int argc() const { + Q_ASSERT(_argc.isInteger()); + return _argc.int_32(); + } + + void setArgc(int argc) { + Q_ASSERT(argc >= 0); + _argc.setInt_32(argc); + } + inline ReturnedValue argument(int i) const { - return i < argc ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); + return i < argc() ? args[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); } - Value thisObject; Value args[1]; + + static Q_DECL_CONSTEXPR int HeaderSize() { return offsetof(CallData, args) / sizeof(QV4::Value); } }; Q_STATIC_ASSERT(std::is_standard_layout<CallData>::value); -Q_STATIC_ASSERT(offsetof(CallData, thisObject) == 8); -Q_STATIC_ASSERT(offsetof(CallData, args) == 16); +Q_STATIC_ASSERT(offsetof(CallData, thisObject) == CallData::This*sizeof(Value)); +Q_STATIC_ASSERT(offsetof(CallData, args) == 5*sizeof(Value)); namespace Heap { struct QmlContext; #define ExecutionContextMembers(class, Member) \ - Member(class, NoMark, CallData *, callData) \ Member(class, Pointer, ExecutionContext *, outer) \ - Member(class, NoMark, Lookup *, lookups) \ - Member(class, NoMark, const QV4::Value *, constantTable) \ - Member(class, NoMark, CompiledData::CompilationUnitBase *, compilationUnit) \ - Member(class, NoMark, int, lineNumber) // as member of non-pointer size this has to come last to preserve the ability to - // translate offsetof of it between 64-bit and 32-bit. + Member(class, Pointer, Object *, activation) \ + Member(class, NoMark, QV4::Function *, v4Function) \ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { DECLARE_MARK_TABLE(ExecutionContext); @@ -119,8 +128,7 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { Type_CatchContext = 0x2, Type_WithContext = 0x3, Type_QmlContext = 0x4, - Type_SimpleCallContext = 0x5, - Type_CallContext = 0x6 + Type_CallContext = 0x5 }; void init(ContextType t) @@ -128,83 +136,54 @@ DECLARE_HEAP_OBJECT(ExecutionContext, Base) { Base::init(); type = t; - lineNumber = -1; } - quint8 type; - bool strictMode : 8; + quint32 type : 8; + quint32 nArgs : 24; #if QT_POINTER_SIZE == 8 - quint8 padding_[6]; -#else - quint8 padding_[2]; + quint8 padding_[4]; #endif }; V4_ASSERT_IS_TRIVIAL(ExecutionContext) Q_STATIC_ASSERT(sizeof(ExecutionContext) == sizeof(Base) + sizeof(ExecutionContextData) + QT_POINTER_SIZE); Q_STATIC_ASSERT(std::is_standard_layout<ExecutionContextData>::value); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, callData) == 0); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == offsetof(ExecutionContextData, callData) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lookups) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, constantTable) == offsetof(ExecutionContextData, lookups) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, compilationUnit) == offsetof(ExecutionContextData, constantTable) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(offsetof(ExecutionContextData, lineNumber) == offsetof(ExecutionContextData, compilationUnit) + QT_POINTER_SIZE); - -#define SimpleCallContextMembers(class, Member) \ - Member(class, Pointer, Object *, activation) \ - Member(class, NoMark, QV4::Function *, v4Function) - -DECLARE_HEAP_OBJECT(SimpleCallContext, ExecutionContext) { - DECLARE_MARK_TABLE(SimpleCallContext); - - void init(ContextType t = Type_SimpleCallContext) - { - ExecutionContext::init(t); - } - - inline unsigned int formalParameterCount() const; - -}; -V4_ASSERT_IS_TRIVIAL(SimpleCallContext) -Q_STATIC_ASSERT(std::is_standard_layout<SimpleCallContextData>::value); -Q_STATIC_ASSERT(offsetof(SimpleCallContextData, activation) == 0); -Q_STATIC_ASSERT(offsetof(SimpleCallContextData, v4Function) == offsetof(SimpleCallContextData, activation) + QT_POINTER_SIZE); -Q_STATIC_ASSERT(sizeof(SimpleCallContextData) == 2 * QT_POINTER_SIZE); -Q_STATIC_ASSERT(sizeof(SimpleCallContext) == sizeof(ExecutionContext) + sizeof(SimpleCallContextData)); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, outer) == 0); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, activation) == offsetof(ExecutionContextData, outer) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(ExecutionContextData, v4Function) == offsetof(ExecutionContextData, activation) + QT_POINTER_SIZE); -#if QT_POINTER_SIZE == 8 -#define CallContextMembers(class, Member) \ - Member(class, Pointer, FunctionObject *, function) \ - Member(class, ValueArray, ValueArray, locals) -#else #define CallContextMembers(class, Member) \ Member(class, Pointer, FunctionObject *, function) \ - Member(class, NoMark, void *, padding) \ Member(class, ValueArray, ValueArray, locals) -#endif -DECLARE_HEAP_OBJECT(CallContext, SimpleCallContext) { +DECLARE_HEAP_OBJECT(CallContext, ExecutionContext) { DECLARE_MARK_TABLE(CallContext); - using SimpleCallContext::formalParameterCount; -}; - -Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); -Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); -// IMPORTANT: we cannot do offsetof(CallContextData, locals) in the JIT as the offset does not scale with -// the pointer size. On 32-bit ARM the offset of the ValueArray is aligned to 8 bytes, on 32-bit x86 for -// example it is not. Therefore we have a padding in place and always have a distance of 8 bytes. -Q_STATIC_ASSERT(offsetof(CallContextData, locals) == offsetof(CallContextData, function) + 8); - -#define GlobalContextMembers(class, Member) \ - Member(class, Pointer, Object *, global) + void init() + { + ExecutionContext::init(Type_CallContext); + } -DECLARE_HEAP_OBJECT(GlobalContext, ExecutionContext) { - DECLARE_MARK_TABLE(GlobalContext); + int argc() const { + return static_cast<int>(nArgs); + } + const Value *args() const { + return locals.data() + locals.size; + } + void setArg(uint index, Value v); - void init(ExecutionEngine *engine); + inline unsigned int formalParameterCount() const; }; -V4_ASSERT_IS_TRIVIAL(GlobalContext) +V4_ASSERT_IS_TRIVIAL(CallContext) +Q_STATIC_ASSERT(std::is_standard_layout<CallContextData>::value); +Q_STATIC_ASSERT(offsetof(CallContextData, function) == 0); +//### The following size check fails on Win8. With the ValueArray at the end of the +// CallContextMembers, it doesn't look very useful. +//#if defined(Q_PROCESSOR_ARM_32) && !defined(Q_OS_IOS) +//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData) + QT_POINTER_SIZE); +//#else +//Q_STATIC_ASSERT(sizeof(CallContext) == sizeof(ExecutionContext) + sizeof(CallContextData)); +//#endif #define CatchContextMembers(class, Member) \ Member(class, Pointer, String *, exceptionVarName) \ @@ -217,16 +196,6 @@ DECLARE_HEAP_OBJECT(CatchContext, ExecutionContext) { }; V4_ASSERT_IS_TRIVIAL(CatchContext) -#define WithContextMembers(class, Member) \ - Member(class, Pointer, Object *, withObject) - -DECLARE_HEAP_OBJECT(WithContext, ExecutionContext) { - DECLARE_MARK_TABLE(WithContext); - - void init(ExecutionContext *outerContext, Object *with); -}; -V4_ASSERT_IS_TRIVIAL(WithContext) - } struct Q_QML_EXPORT ExecutionContext : public Managed @@ -239,68 +208,42 @@ struct Q_QML_EXPORT ExecutionContext : public Managed Q_MANAGED_TYPE(ExecutionContext) V4_INTERNALCLASS(ExecutionContext) - Heap::CallContext *newCallContext(Function *f, CallData *callData); - Heap::WithContext *newWithContext(Heap::Object *with); + static Heap::CallContext *newCallContext(Heap::ExecutionContext *outer, Function *function, CallData *callData); + Heap::ExecutionContext *newWithContext(Heap::Object *with); Heap::CatchContext *newCatchContext(Heap::String *exceptionVarName, ReturnedValue exceptionValue); void createMutableBinding(String *name, bool deletable); - void setProperty(String *name, const Value &value); + enum Error { + NoError, + TypeError, + RangeError + }; + + Error setProperty(String *name, const Value &value); + ReturnedValue getProperty(String *name); ReturnedValue getPropertyAndBase(String *name, Value *base); bool deleteProperty(String *name); - inline SimpleCallContext *asSimpleCallContext(); - inline const SimpleCallContext *asSimpleCallContext() const; - inline const CatchContext *asCatchContext() const; - inline const WithContext *asWithContext() const; + inline CallContext *asCallContext(); + inline const CallContext *asCallContext() const; Function *getFunction() const; - Value &thisObject() const { - return d()->callData->thisObject; - } - int argc() const { - return d()->callData->argc; - } - const Value *args() const { - return d()->callData->args; - } - ReturnedValue argument(int i) const { - return d()->callData->argument(i); - } - - void call(Scope &scope, CallData *callData, QV4::Function *function, const QV4::FunctionObject *f = 0); - void simpleCall(Scope &scope, CallData *callData, QV4::Function *function); }; -struct Q_QML_EXPORT SimpleCallContext : public ExecutionContext +struct Q_QML_EXPORT CallContext : public ExecutionContext { - V4_MANAGED(SimpleCallContext, ExecutionContext) - V4_INTERNALCLASS(SimpleCallContext) - - // formals are in reverse order - Identifier * const *formals() const; - unsigned int formalCount() const; - Identifier * const *variables() const; - unsigned int variableCount() const; - - inline ReturnedValue argument(int i) const; -}; - -inline ReturnedValue SimpleCallContext::argument(int i) const { - return i < argc() ? args()[i].asReturnedValue() : Primitive::undefinedValue().asReturnedValue(); -} - -struct Q_QML_EXPORT CallContext : public SimpleCallContext -{ - V4_MANAGED(CallContext, SimpleCallContext) -}; - -struct GlobalContext : public ExecutionContext -{ - V4_MANAGED(GlobalContext, ExecutionContext) + V4_MANAGED(CallContext, ExecutionContext) + V4_INTERNALCLASS(CallContext) + int argc() const { + return d()->argc(); + } + const Value *args() const { + return d()->args(); + } }; struct CatchContext : public ExecutionContext @@ -308,29 +251,14 @@ struct CatchContext : public ExecutionContext V4_MANAGED(CatchContext, ExecutionContext) }; -struct WithContext : public ExecutionContext -{ - V4_MANAGED(WithContext, ExecutionContext) -}; - -inline SimpleCallContext *ExecutionContext::asSimpleCallContext() -{ - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<SimpleCallContext *>(this) : 0; -} - -inline const SimpleCallContext *ExecutionContext::asSimpleCallContext() const -{ - return d()->type >= Heap::ExecutionContext::Type_SimpleCallContext ? static_cast<const SimpleCallContext *>(this) : 0; -} - -inline const CatchContext *ExecutionContext::asCatchContext() const +inline CallContext *ExecutionContext::asCallContext() { - return d()->type == Heap::ExecutionContext::Type_CatchContext ? static_cast<const CatchContext *>(this) : 0; + return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<CallContext *>(this) : 0; } -inline const WithContext *ExecutionContext::asWithContext() const +inline const CallContext *ExecutionContext::asCallContext() const { - return d()->type == Heap::ExecutionContext::Type_WithContext ? static_cast<const WithContext *>(this) : 0; + return d()->type == Heap::ExecutionContext::Type_CallContext ? static_cast<const CallContext *>(this) : 0; } } // namespace QV4 diff --git a/src/qml/jsruntime/qv4dataview.cpp b/src/qml/jsruntime/qv4dataview.cpp index f1405e08ee..815f6ed991 100644 --- a/src/qml/jsruntime/qv4dataview.cpp +++ b/src/qml/jsruntime/qv4dataview.cpp @@ -54,34 +54,31 @@ void Heap::DataViewCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("DataView")); } -void DataViewCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue DataViewCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); - if (!buffer) { - scope.result = scope.engine->throwTypeError(); - return; - } + Scope scope(f->engine()); + Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); + if (!buffer) + return scope.engine->throwTypeError(); - double bo = callData->argc > 1 ? callData->args[1].toNumber() : 0; + double bo = argc > 1 ? argv[1].toNumber() : 0; uint byteOffset = (uint)bo; uint bufferLength = buffer->d()->data->size; - double bl = callData->argc < 3 || callData->args[2].isUndefined() ? (bufferLength - bo) : callData->args[2].toNumber(); + double bl = argc < 3 || argv[2].isUndefined() ? (bufferLength - bo) : argv[2].toNumber(); uint byteLength = (uint)bl; - if (bo != byteOffset || bl != byteLength || byteOffset + byteLength > bufferLength) { - scope.result = scope.engine->throwRangeError(QStringLiteral("DataView: constructor arguments out of range")); - return; - } + 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>()); a->d()->buffer.set(scope.engine, buffer->d()); a->d()->byteLength = byteLength; a->d()->byteOffset = byteOffset; - scope.result = a.asReturnedValue(); + return a.asReturnedValue(); } -void DataViewCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue DataViewCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -122,84 +119,84 @@ void DataViewPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setUInt32"), method_set<unsigned int>, 0); } -void DataViewPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_buffer(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); + DataView *v = callData->thisObject.as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = v->d()->buffer; + return v->d()->buffer->asReturnedValue(); } -void DataViewPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_byteLength(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); + DataView *v = callData->thisObject.as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->byteLength); + return Encode(v->d()->byteLength); } -void DataViewPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get_byteOffset(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); + DataView *v = callData->thisObject.as<DataView>(); if (!v) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); - scope.result = Encode(v->d()->byteOffset); + return Encode(v->d()->byteOffset); } template <typename T> -void DataViewPrototype::method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_getChar(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; T t = T(v->d()->buffer->data->data()[idx]); - scope.result = Encode((int)t); + return Encode((int)t); } template <typename T> -void DataViewPrototype::method_get(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_get(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); + bool littleEndian = callData->argc() < 2 ? false : callData->args[1].toBoolean(); T t = littleEndian ? qFromLittleEndian<T>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<T>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(t); + return Encode(t); } template <typename T> -void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_getFloat(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - bool littleEndian = callData->argc < 2 ? false : callData->args[1].toBoolean(); + bool littleEndian = callData->argc() < 2 ? false : callData->args[1].toBoolean(); if (sizeof(T) == 4) { // float @@ -210,7 +207,7 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C u.i = littleEndian ? qFromLittleEndian<uint>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<uint>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(u.f); + return Encode(u.f); } else { Q_ASSERT(sizeof(T) == 8); union { @@ -220,43 +217,43 @@ void DataViewPrototype::method_getFloat(const BuiltinFunction *, Scope &scope, C u.i = littleEndian ? qFromLittleEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx) : qFromBigEndian<quint64>((uchar *)v->d()->buffer->data->data() + idx); - scope.result = Encode(u.d); + return Encode(u.d); } } template <typename T> -void DataViewPrototype::method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_setChar(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; + int val = callData->argc() >= 2 ? callData->args[1].toInt32() : 0; v->d()->buffer->data->data()[idx] = (char)val; RETURN_UNDEFINED(); } template <typename T> -void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_set(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - int val = callData->argc >= 2 ? callData->args[1].toInt32() : 0; + int val = callData->argc() >= 2 ? callData->args[1].toInt32() : 0; - bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); + bool littleEndian = callData->argc() < 3 ? false : callData->args[2].toBoolean(); if (littleEndian) qToLittleEndian<T>(val, (uchar *)v->d()->buffer->data->data() + idx); @@ -267,19 +264,19 @@ void DataViewPrototype::method_set(const BuiltinFunction *, Scope &scope, CallDa } template <typename T> -void DataViewPrototype::method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DataViewPrototype::method_setFloat(const BuiltinFunction *b, CallData *callData) { - Scoped<DataView> v(scope, callData->thisObject); - if (!v || callData->argc < 1) - THROW_TYPE_ERROR(); + DataView *v = callData->thisObject.as<DataView>(); + if (!v || callData->argc() < 1) + return b->engine()->throwTypeError(); double l = callData->args[0].toNumber(); uint idx = (uint)l; if (l != idx || idx + sizeof(T) > v->d()->byteLength) - THROW_TYPE_ERROR(); + return b->engine()->throwTypeError(); idx += v->d()->byteOffset; - double val = callData->argc >= 2 ? callData->args[1].toNumber() : qt_qnan(); - bool littleEndian = callData->argc < 3 ? false : callData->args[2].toBoolean(); + double val = callData->argc() >= 2 ? callData->args[1].toNumber() : qt_qnan(); + bool littleEndian = callData->argc() < 3 ? false : callData->args[2].toBoolean(); if (sizeof(T) == 4) { // float diff --git a/src/qml/jsruntime/qv4dataview_p.h b/src/qml/jsruntime/qv4dataview_p.h index 5c50df4655..30fc7e993b 100644 --- a/src/qml/jsruntime/qv4dataview_p.h +++ b/src/qml/jsruntime/qv4dataview_p.h @@ -79,8 +79,8 @@ struct DataViewCtor: FunctionObject { V4_OBJECT2(DataViewCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 DataView : Object @@ -93,21 +93,21 @@ struct DataViewPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_buffer(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_byteLength(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_byteOffset(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_getChar(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getChar(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_get(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_getFloat(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getFloat(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_setChar(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_setChar(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_set(const BuiltinFunction *, CallData *callData); template <typename T> - static void method_setFloat(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_setFloat(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index c56d007028..8b6325badd 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -43,6 +43,7 @@ #include "qv4scopedvalue_p.h" #include "qv4runtime_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDebug> #include <QtCore/QDateTime> @@ -677,15 +678,16 @@ void Heap::DateCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Date")); } -void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue DateCtor::callAsConstructor(const FunctionObject *that, const Value *argv, int argc) { double t = 0; - if (callData->argc == 0) + if (argc == 0) t = currentTime(); - else if (callData->argc == 1) { - ScopedValue arg(scope, callData->args[0]); + else if (argc == 1) { + Scope scope(that->engine()); + ScopedValue arg(scope, argv[0]); if (DateObject *d = arg->as<DateObject>()) { t = d->date(); } else { @@ -699,26 +701,26 @@ void DateCtor::construct(const Managed *, Scope &scope, CallData *callData) } else { // d.argc > 1 - double year = callData->args[0].toNumber(); - double month = callData->args[1].toNumber(); - double day = callData->argc >= 3 ? callData->args[2].toNumber() : 1; - double hours = callData->argc >= 4 ? callData->args[3].toNumber() : 0; - double mins = callData->argc >= 5 ? callData->args[4].toNumber() : 0; - double secs = callData->argc >= 6 ? callData->args[5].toNumber() : 0; - double ms = callData->argc >= 7 ? callData->args[6].toNumber() : 0; + double year = argv[0].toNumber(); + double month = argv[1].toNumber(); + double day = argc >= 3 ? argv[2].toNumber() : 1; + double hours = argc >= 4 ? argv[3].toNumber() : 0; + double mins = argc >= 5 ? argv[4].toNumber() : 0; + double secs = argc >= 6 ? argv[5].toNumber() : 0; + double ms = argc >= 7 ? argv[6].toNumber() : 0; if (year >= 0 && year <= 99) year += 1900; t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); t = TimeClip(UTC(t)); } - scope.result = Encode(scope.engine->newDateObject(Primitive::fromDouble(t))); + return Encode(that->engine()->newDateObject(Primitive::fromDouble(t))); } -void DateCtor::call(const Managed *m, Scope &scope, CallData *) +ReturnedValue DateCtor::call(const FunctionObject *m, const Value *, const Value *, int) { double t = currentTime(); - scope.result = static_cast<const DateCtor *>(m)->engine()->newString(ToString(t)); + return m->engine()->newString(ToString(t))->asReturnedValue(); } void DatePrototype::init(ExecutionEngine *engine, Object *ctor) @@ -795,27 +797,27 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } -double DatePrototype::getThisDate(Scope &scope, CallData *callData) +double DatePrototype::getThisDate(ExecutionEngine *v4, CallData *callData) { if (DateObject *thisObject = callData->thisObject.as<DateObject>()) return thisObject->date(); else { - scope.engine->throwTypeError(); + v4->throwTypeError(); return 0; } } -void DatePrototype::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_parse(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) - scope.result = Encode(qt_qnan()); + if (!callData->argc()) + return Encode(qt_qnan()); else - scope.result = Encode(ParseString(callData->args[0].toQString())); + return Encode(ParseString(callData->args[0].toQString())); } -void DatePrototype::method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_UTC(const BuiltinFunction *, CallData *callData) { - const int numArgs = callData->argc; + const int numArgs = callData->argc(); if (numArgs >= 2) { double year = callData->args[0].toNumber(); double month = callData->args[1].toNumber(); @@ -828,426 +830,488 @@ void DatePrototype::method_UTC(const BuiltinFunction *, Scope &scope, CallData * year += 1900; double t = MakeDate(MakeDay(year, month, day), MakeTime(hours, mins, secs, ms)); - scope.result = Encode(TimeClip(t)); - return; + return Encode(TimeClip(t)); } RETURN_UNDEFINED(); } -void DatePrototype::method_now(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_now(const BuiltinFunction *, CallData *callData) { Q_UNUSED(callData); - double t = currentTime(); - scope.result = Encode(t); + return Encode(currentTime()); } -void DatePrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToString(t))); } -void DatePrototype::method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toDateString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToDateString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToDateString(t))); } -void DatePrototype::method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toTimeString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToTimeString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToTimeString(t))); } -void DatePrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToLocaleString(t))); } -void DatePrototype::method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleDateString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleDateString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToLocaleDateString(t))); } -void DatePrototype::method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toLocaleTimeString(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = scope.engine->newString(ToLocaleTimeString(t)); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(v4->newString(ToLocaleTimeString(t))); } -void DatePrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = Encode(t); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(t); } -void DatePrototype::method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getTime(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); - scope.result = Encode(t); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); + return Encode(t); } -void DatePrototype::method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getYear(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = YearFromTime(LocalTime(t)) - 1900; - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getFullYear(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = YearFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCFullYear(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = YearFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMonth(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = MonthFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMonth(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = MonthFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getDate(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = DateFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCDate(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = DateFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getDay(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = WeekDay(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCDay(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = WeekDay(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getHours(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = HourFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCHours(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = HourFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMinutes(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = MinFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMinutes(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = MinFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getSeconds(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = SecFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCSeconds(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = SecFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getMilliseconds(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = msFromTime(LocalTime(t)); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getUTCMilliseconds(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = msFromTime(t); - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_getTimezoneOffset(const BuiltinFunction *b, CallData *callData) { - double t = getThisDate(scope, callData); + ExecutionEngine *v4 = b->engine(); + double t = getThisDate(v4, callData); if (!std::isnan(t)) t = (t - LocalTime(t)) / msPerMinute; - scope.result = Encode(t); + return Encode(t); } -void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setTime(const BuiltinFunction *b, CallData *callData) { - Scoped<DateObject> self(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + double t = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); self->setDate(TimeClip(t)); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMilliseconds(const BuiltinFunction *b, CallData *callData) { - Scoped<DateObject> self(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = callData->argc() ? callData->args[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))))); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - CHECK_EXCEPTION(); - double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setSeconds(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (callData->argc() < 2) ? msFromTime(t) : callData->args[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCSeconds(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); + double sec = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double ms = (callData->argc() < 2) ? msFromTime(t) : callData->args[1].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMinutes(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double min = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = (callData->argc() < 2) ? SecFromTime(t) : callData->args[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (callData->argc() < 3) ? msFromTime(t) : callData->args[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMinutes(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); - double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); + double min = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double sec = (callData->argc() < 2) ? SecFromTime(t) : callData->args[1].toNumber(); + double ms = (callData->argc() < 3) ? msFromTime(t) : callData->args[2].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setHours(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); - double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double hour = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double min = (callData->argc() < 2) ? MinFromTime(t) : callData->args[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double sec = (callData->argc() < 3) ? SecFromTime(t) : callData->args[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double ms = (callData->argc() < 4) ? msFromTime(t) : callData->args[3].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCHours(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); - double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); - double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); + double hour = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double min = (callData->argc() < 2) ? MinFromTime(t) : callData->args[1].toNumber(); + double sec = (callData->argc() < 3) ? SecFromTime(t) : callData->args[2].toNumber(); + double ms = (callData->argc() < 4) ? msFromTime(t) : callData->args[3].toNumber(); t = TimeClip(MakeDate(Day(t), MakeTime(hour, min, sec, ms))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setDate(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCDate(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - CHECK_EXCEPTION(); - double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setMonth(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); - double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); + double month = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = (callData->argc() < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCMonth(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + double month = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double date = (callData->argc() < 2) ? DateFromTime(t) : callData->args[1].toNumber(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setYear(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); if (std::isnan(t)) t = 0; else t = LocalTime(t); - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double year = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); double r; if (std::isnan(year)) { r = qt_qnan(); @@ -1259,53 +1323,60 @@ void DatePrototype::method_setYear(const BuiltinFunction *, Scope &scope, CallDa r = TimeClip(r); } self->setDate(r); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setUTCFullYear(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); - double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + double year = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double month = (callData->argc() < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); + double date = (callData->argc() < 3) ? DateFromTime(t) : callData->args[2].toNumber(); t = TimeClip(MakeDate(MakeDay(year, month, date), TimeWithinDay(t))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_setFullYear(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = LocalTime(self->date()); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); if (std::isnan(t)) t = 0; - double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - CHECK_EXCEPTION(); - double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); - CHECK_EXCEPTION(); - double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); - CHECK_EXCEPTION(); + double year = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + if (v4->hasException) + return QV4::Encode::undefined(); + double month = (callData->argc() < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); + double date = (callData->argc() < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + if (v4->hasException) + return QV4::Encode::undefined(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); - scope.result = Encode(self->date()); + return Encode(self->date()); } -void DatePrototype::method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toUTCString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); - scope.result = scope.engine->newString(ToUTCString(t)); + return Encode(v4->newString(ToUTCString(t))); } static void addZeroPrefixedInt(QString &str, int num, int nDigits) @@ -1321,21 +1392,22 @@ static void addZeroPrefixedInt(QString &str, int num, int nDigits) } } -void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toISOString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); DateObject *self = callData->thisObject.as<DateObject>(); if (!self) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); double t = self->date(); if (!std::isfinite(t)) - RETURN_RESULT(scope.engine->throwRangeError(callData->thisObject)); + RETURN_RESULT(v4->throwRangeError(callData->thisObject)); QString result; int year = (int)YearFromTime(t); if (year < 0 || year > 9999) { if (qAbs(year) >= 1000000) - RETURN_RESULT(scope.engine->newString(QStringLiteral("Invalid Date"))); + RETURN_RESULT(v4->newString(QStringLiteral("Invalid Date"))); result += year < 0 ? QLatin1Char('-') : QLatin1Char('+'); year = qAbs(year); addZeroPrefixedInt(result, year, 6); @@ -1356,29 +1428,32 @@ void DatePrototype::method_toISOString(const BuiltinFunction *, Scope &scope, Ca addZeroPrefixedInt(result, msFromTime(t), 3); result += QLatin1Char('Z'); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void DatePrototype::method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue DatePrototype::method_toJSON(const BuiltinFunction *b, CallData *callData) { - ScopedObject O(scope, callData->thisObject.toObject(scope.engine)); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + Scope scope(v4); + ScopedObject O(scope, callData->thisObject.toObject(v4)); + if (v4->hasException) + return QV4::Encode::undefined(); ScopedValue tv(scope, RuntimeHelpers::toPrimitive(O, NUMBER_HINT)); if (tv->isNumber() && !std::isfinite(tv->toNumber())) RETURN_RESULT(Encode::null()); - ScopedString s(scope, scope.engine->newString(QStringLiteral("toISOString"))); + ScopedString s(scope, v4->newString(QStringLiteral("toISOString"))); ScopedValue v(scope, O->get(s)); FunctionObject *toIso = v->as<FunctionObject>(); if (!toIso) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - ScopedCallData cData(scope); - cData->thisObject = callData->thisObject; - toIso->call(scope, cData); + JSCallData jsCallData(scope); + *jsCallData->thisObject = callData->thisObject; + return toIso->call(jsCallData); } void DatePrototype::timezoneUpdated() diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index b0373884dd..776822d312 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -108,8 +108,8 @@ struct DateCtor: FunctionObject { V4_OBJECT2(DateCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int); }; struct DatePrototype: Object @@ -118,57 +118,57 @@ struct DatePrototype: Object void init(ExecutionEngine *engine, Object *ctor); - static double getThisDate(Scope &scope, CallData *callData); - - static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_UTC(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_now(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toDateString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toTimeString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleDateString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleTimeString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getDay(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCDay(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getTimezoneOffset(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setTime(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMilliseconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCSeconds(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMinutes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCHours(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCDate(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCMonth(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_setUTCFullYear(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toUTCString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toISOString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toJSON(const BuiltinFunction *, Scope &scope, CallData *callData); + static double getThisDate(ExecutionEngine *v4, CallData *callData); + + static ReturnedValue method_parse(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_UTC(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_now(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toDateString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toTimeString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleDateString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleTimeString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getTime(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getFullYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCFullYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getMonth(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCMonth(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getDate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCDate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getDay(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCDay(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getHours(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCHours(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getMinutes(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCMinutes(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getSeconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCSeconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getMilliseconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getUTCMilliseconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getTimezoneOffset(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setTime(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setMilliseconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCMilliseconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setSeconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCSeconds(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setMinutes(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCMinutes(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setHours(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCHours(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setDate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCDate(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setMonth(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCMonth(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setFullYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_setUTCFullYear(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toUTCString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toISOString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toJSON(const BuiltinFunction *, CallData *callData); static void timezoneUpdated(); }; diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 7b298a302c..049e4a117d 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -81,14 +81,6 @@ #include <QtCore/QTextStream> #include <QDateTime> -#ifdef V4_ENABLE_JIT -#include "qv4isel_masm_p.h" -#endif // V4_ENABLE_JIT - -#if QT_CONFIG(qml_interpreter) -#include "qv4isel_moth_p.h" -#endif - #if USE(PTHREADS) # include <pthread.h> #if !defined(Q_OS_INTEGRITY) @@ -109,9 +101,9 @@ using namespace QV4; static QBasicAtomicInt engineSerial = Q_BASIC_ATOMIC_INITIALIZER(1); -void throwTypeError(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue throwTypeError(const BuiltinFunction *b, CallData *) { - scope.result = scope.engine->throwTypeError(); + return b->engine()->throwTypeError(); } @@ -129,7 +121,7 @@ QQmlEngine *ExecutionEngine::qmlEngine() const qint32 ExecutionEngine::maxCallDepth = -1; -ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) +ExecutionEngine::ExecutionEngine() : executableAllocator(new QV4::ExecutableAllocator) , regExpAllocator(new QV4::ExecutableAllocator) , bumperPointerAllocator(new WTF::BumpPointerAllocator) @@ -149,38 +141,15 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) bool ok = false; maxCallDepth = qEnvironmentVariableIntValue("QV4_MAX_CALL_DEPTH", &ok); if (!ok || maxCallDepth <= 0) { +#ifdef QT_NO_DEBUG maxCallDepth = 1234; - } - } - Q_ASSERT(maxCallDepth > 0); - - if (!factory) { -#if QT_CONFIG(qml_interpreter) - bool jitDisabled = true; - -#ifdef V4_ENABLE_JIT - static const bool forceMoth = !qEnvironmentVariableIsEmpty("QV4_FORCE_INTERPRETER") || - !OSAllocator::canAllocateExecutableMemory(); - if (forceMoth) { - factory = new Moth::ISelFactory; - } else { - factory = new JIT::ISelFactory<>; - jitDisabled = false; - } -#else // !V4_ENABLE_JIT - factory = new Moth::ISelFactory; -#endif // V4_ENABLE_JIT - - if (jitDisabled) { - qWarning("JIT is disabled for QML. Property bindings and animations will be " - "very slow. Visit https://wiki.qt.io/V4 to learn about possible " - "solutions for your platform."); - } #else - factory = new JIT::ISelFactory<>; + // no (tail call) optimization is done, so there'll be a lot mare stack frames active + maxCallDepth = 200; #endif + } } - iselFactory.reset(factory); + Q_ASSERT(maxCallDepth > 0); // reserve space for the JS stack // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues @@ -219,7 +188,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) 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_SimpleCallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); + internalClasses[Class_CallContext] = internalClasses[EngineBase::Class_Empty]->changeVTable(QV4::CallContext::staticVTable()); jsStrings[String_Empty] = newIdentifier(QString()); jsStrings[String_undefined] = newIdentifier(QStringLiteral("undefined")); @@ -425,8 +394,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) // // set up the global object // - rootContext()->d()->global.set(scope.engine, globalObject->d()); - rootContext()->d()->callData->thisObject = globalObject; + rootContext()->d()->activation.set(scope.engine, globalObject->d()); Q_ASSERT(globalObject->d()->vtable()); globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); @@ -535,19 +503,11 @@ void ExecutionEngine::setProfiler(Profiling::Profiler *profiler) void ExecutionEngine::initRootContext() { Scope scope(this); - Scoped<GlobalContext> r(scope, memoryManager->allocManaged<GlobalContext>( - sizeof(GlobalContext::Data) + sizeof(CallData))); - r->d_unchecked()->init(this); - r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); - r->d()->callData->tag = quint32(Value::ValueTypeInternal::Integer); - r->d()->callData->argc = 0; - r->d()->callData->thisObject = globalObject; - r->d()->callData->args[0] = Encode::undefined(); + Scoped<ExecutionContext> r(scope, memoryManager->allocManaged<ExecutionContext>(sizeof(ExecutionContext::Data))); + r->d_unchecked()->init(Heap::ExecutionContext::Type_GlobalContext); + r->d()->activation.set(this, globalObject->d()); jsObjects[RootContext] = r; jsObjects[IntegerNull] = Encode((int)0); - - currentContext = static_cast<ExecutionContext *>(jsObjects + RootContext); - current = currentContext->d(); } InternalClass *ExecutionEngine::newClass(const InternalClass &other) @@ -555,14 +515,6 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other) return new (classPool) InternalClass(other); } -ExecutionContext *ExecutionEngine::pushGlobalContext() -{ - pushContext(rootContext()->d()); - - Q_ASSERT(current == rootContext()->d()); - return currentContext; -} - InternalClass *ExecutionEngine::newInternalClass(const VTable *vtable, Object *prototype) { return internalClasses[EngineBase::Class_Empty]->changeVTable(vtable)->changePrototype(prototype ? prototype->d() : 0); @@ -685,9 +637,9 @@ Heap::DateObject *ExecutionEngine::newDateObjectFromTime(const QTime &t) Heap::RegExpObject *ExecutionEngine::newRegExpObject(const QString &pattern, int flags) { - bool global = (flags & IR::RegExp::RegExp_Global); - bool ignoreCase = (flags & IR::RegExp::RegExp_IgnoreCase); - bool multiline = (flags & IR::RegExp::RegExp_Multiline); + bool global = (flags & QV4::CompiledData::RegExp::RegExp_Global); + bool ignoreCase = (flags & QV4::CompiledData::RegExp::RegExp_IgnoreCase); + bool multiline = (flags & QV4::CompiledData::RegExp::RegExp_Multiline); Scope scope(this); Scoped<RegExp> re(scope, RegExp::create(this, pattern, ignoreCase, multiline, global)); @@ -760,11 +712,9 @@ Heap::Object *ExecutionEngine::newForEachIteratorObject(Object *o) Heap::QmlContext *ExecutionEngine::qmlContext() const { - Heap::ExecutionContext *ctx = current; - - // get the correct context when we're within a builtin function - if (ctx->type == Heap::ExecutionContext::Type_SimpleCallContext && !ctx->outer) - ctx = parentContext(currentContext)->d(); + if (!currentStackFrame) + return 0; + Heap::ExecutionContext *ctx = currentContext()->d(); if (ctx->type != Heap::ExecutionContext::Type_QmlContext && !ctx->outer) return 0; @@ -785,7 +735,7 @@ QObject *ExecutionEngine::qmlScopeObject() const if (!ctx) return 0; - return ctx->qml->scopeObject; + return ctx->qml()->scopeObject; } ReturnedValue ExecutionEngine::qmlSingletonWrapper(String *name) @@ -815,57 +765,56 @@ QQmlContextData *ExecutionEngine::callingQmlContext() const if (!ctx) return 0; - return ctx->qml->context->contextData(); + return ctx->qml()->context->contextData(); } -QVector<StackFrame> ExecutionEngine::stackTrace(int frameLimit) const +QString CppStackFrame::source() const { - Scope scope(const_cast<ExecutionEngine *>(this)); - ScopedString name(scope); - QVector<StackFrame> stack; - - ExecutionContext *c = currentContext; - while (c && frameLimit) { - QV4::Function *function = c->getFunction(); - if (function) { - StackFrame frame; - frame.source = function->sourceFile(); - name = function->name(); - frame.function = name->toQString(); - - // line numbers can be negative for places where you can't set a real breakpoint - frame.line = qAbs(c->d()->lineNumber); - frame.column = -1; - - stack.append(frame); - --frameLimit; - } - c = parentContext(c); - } - - if (frameLimit && globalCode) { - StackFrame frame; - frame.source = globalCode->sourceFile(); - frame.function = globalCode->name()->toQString(); - frame.line = rootContext()->d()->lineNumber; - frame.column = -1; + return v4Function->sourceFile(); +} - stack.append(frame); - } - return stack; +QString CppStackFrame::function() const +{ + return v4Function->name()->toQString(); } -StackFrame ExecutionEngine::currentStackFrame() const +int CppStackFrame::lineNumber() const { - StackFrame frame; - frame.line = -1; - frame.column = -1; + auto findLine = [](const CompiledData::CodeOffsetToLine &entry, uint offset) { + return entry.codeOffset < offset; + }; + + const QV4::CompiledData::Function *cf = v4Function->compiledFunction; + uint offset = static_cast<uint>(instructionPointer - v4Function->codeData); + const CompiledData::CodeOffsetToLine *lineNumbers = cf->lineNumberTable(); + uint nLineNumbers = cf->nLineNumbers; + const CompiledData::CodeOffsetToLine *line = std::lower_bound(lineNumbers, lineNumbers + nLineNumbers, offset, findLine) - 1; + return line->line; +} + +ReturnedValue CppStackFrame::thisObject() const { + return jsFrame->thisObject.asReturnedValue(); +} - QVector<StackFrame> trace = stackTrace(/*limit*/ 1); - if (!trace.isEmpty()) - frame = trace.first(); +StackTrace ExecutionEngine::stackTrace(int frameLimit) const +{ + Scope scope(const_cast<ExecutionEngine *>(this)); + ScopedString name(scope); + StackTrace stack; + + CppStackFrame *f = currentStackFrame; + while (f && frameLimit) { + QV4::StackFrame frame; + frame.source = f->source(); + frame.function = f->function(); + frame.line = qAbs(f->lineNumber()); + frame.column = -1; + stack.append(frame); + --frameLimit; + f = f->parent; + } - return frame; + return stack; } /* Helper and "C" linkage exported function to format a GDBMI stacktrace for @@ -908,14 +857,13 @@ QUrl ExecutionEngine::resolvedUrl(const QString &file) return src; QUrl base; - ExecutionContext *c = currentContext; - while (c) { - SimpleCallContext *callCtx = c->asSimpleCallContext(); - if (callCtx && callCtx->d()->v4Function) { - base.setUrl(callCtx->d()->v4Function->sourceFile()); + CppStackFrame *f = currentStackFrame; + while (f) { + if (f->v4Function) { + base.setUrl(f->v4Function->sourceFile()); break; } - c = parentContext(c); + f = f->parent; } if (base.isEmpty() && globalCode) @@ -1589,11 +1537,6 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::failStackLimitCheck(Scope &scope) -{ - scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); -} - // Converts a JS value to a meta-type. // data must point to a place that can store a value of the given type. // Returns true if conversion succeeded, false otherwise. diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 4549cda5b9..db002035da 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -51,7 +51,6 @@ // #include "qv4global_p.h" -#include "private/qv4isel_p.h" #include "qv4managed_p.h" #include "qv4context_p.h" #include <private/qintrusivelist_p.h> @@ -89,6 +88,30 @@ struct CompilationUnit; struct InternalClass; struct InternalClassPool; +struct Q_QML_EXPORT CppStackFrame { + CppStackFrame *parent; + Function *v4Function; + CallData *jsFrame; + const uchar *instructionPointer; + + QString source() const; + QString function() const; + inline QV4::ExecutionContext *context() const { + return static_cast<ExecutionContext *>(&jsFrame->context); + } + int lineNumber() const; + + inline QV4::Heap::CallContext *callContext() const { + Heap::ExecutionContext *ctx = static_cast<ExecutionContext &>(jsFrame->context).d();\ + while (ctx->type != Heap::ExecutionContext::Type_CallContext) + ctx = ctx->outer; + return static_cast<Heap::CallContext *>(ctx); + } + ReturnedValue thisObject() const; +}; + + + struct Q_QML_EXPORT ExecutionEngine : public EngineBase { private: @@ -100,7 +123,6 @@ private: public: ExecutableAllocator *executableAllocator; ExecutableAllocator *regExpAllocator; - QScopedPointer<EvalISelFactory> iselFactory; WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. @@ -183,7 +205,7 @@ public: Value *jsObjects; enum { NTypedArrayTypes = 9 }; // == TypedArray::NValues, avoid header dependency - GlobalContext *rootContext() const { return reinterpret_cast<GlobalContext *>(jsObjects + RootContext); } + ExecutionContext *rootContext() const { return reinterpret_cast<ExecutionContext *>(jsObjects + RootContext); } FunctionObject *objectCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Object_Ctor); } FunctionObject *stringCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + String_Ctor); } FunctionObject *numberCtor() const { return reinterpret_cast<FunctionObject *>(jsObjects + Number_Ctor); } @@ -342,7 +364,7 @@ public: // bookkeeping. MultiplyWrappedQObjectMap *m_multiplyWrappedQObjects; - ExecutionEngine(EvalISelFactory *iselFactory = 0); + ExecutionEngine(); ~ExecutionEngine(); #ifdef QT_NO_QML_DEBUGGER @@ -359,11 +381,10 @@ public: void setProfiler(Profiling::Profiler *profiler); #endif // QT_NO_QML_DEBUGGER - ExecutionContext *pushGlobalContext(); - void pushContext(Heap::ExecutionContext *context); - void pushContext(ExecutionContext *context); - void popContext(); - ExecutionContext *parentContext(ExecutionContext *context) const; + void setCurrentContext(Heap::ExecutionContext *context); + ExecutionContext *currentContext() const { + return static_cast<ExecutionContext *>(¤tStackFrame->jsFrame->context); + } InternalClass *newInternalClass(const VTable *vtable, Object *prototype); @@ -413,7 +434,6 @@ public: StackTrace stackTrace(int frameLimit = -1) const; - StackFrame currentStackFrame() const; QUrl resolvedUrl(const QString &file); void requireArgumentsAccessors(int n); @@ -424,8 +444,6 @@ public: InternalClass *newClass(const InternalClass &other); - // Exception handling - Value *exceptionValue; StackTrace exceptionStackTrace; ReturnedValue throwError(const Value &value); @@ -455,11 +473,9 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - bool checkStackLimits(Scope &scope); + bool checkStackLimits(); private: - void failStackLimitCheck(Scope &scope); - #ifndef QT_NO_QML_DEBUGGER QScopedPointer<QV4::Debugging::Debugger> m_debugger; QScopedPointer<QV4::Profiling::Profiler> m_profiler; @@ -477,39 +493,9 @@ struct NoThrowEngine; #endif -inline void ExecutionEngine::pushContext(Heap::ExecutionContext *context) -{ - Q_ASSERT(currentContext && context); - Value *v = jsAlloca(2); - v[0] = Encode(context); - v[1] = Encode((int)(v - static_cast<Value *>(currentContext))); - currentContext = static_cast<ExecutionContext *>(v); - current = currentContext->d(); -} - -inline void ExecutionEngine::pushContext(ExecutionContext *context) -{ - pushContext(context->d()); -} - - -inline void ExecutionEngine::popContext() -{ - Q_ASSERT(jsStackTop > currentContext); - QV4::Value *offset = (currentContext + 1); - Q_ASSERT(offset->isInteger()); - int o = offset->integerValue(); - Q_ASSERT(o); - currentContext -= o; - current = currentContext->d(); -} - -inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *context) const +inline void ExecutionEngine::setCurrentContext(Heap::ExecutionContext *context) { - Value *offset = static_cast<Value *>(context) + 1; - Q_ASSERT(offset->isInteger()); - int o = offset->integerValue(); - return o ? context - o : 0; + currentStackFrame->jsFrame->context = context; } inline @@ -541,7 +527,7 @@ inline void Managed::mark(MarkStack *markStack) m()->mark(markStack); } -#define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ +#define CHECK_STACK_LIMITS(v4) if ((v4)->checkStackLimits()) return Encode::undefined(); \ ExecutionEngineCallDepthRecorder _executionEngineCallDepthRecorder(v4); struct ExecutionEngineCallDepthRecorder @@ -552,10 +538,10 @@ struct ExecutionEngineCallDepthRecorder ~ExecutionEngineCallDepthRecorder() { --ee->callDepth; } }; -inline bool ExecutionEngine::checkStackLimits(Scope &scope) +inline bool ExecutionEngine::checkStackLimits() { if (Q_UNLIKELY((jsStackTop > jsStackLimit) || (callDepth >= maxCallDepth))) { - failStackLimitCheck(scope); + throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); return true; } diff --git a/src/qml/jsruntime/qv4enginebase_p.h b/src/qml/jsruntime/qv4enginebase_p.h index 88f9dfd85c..e0f5f3ffb1 100644 --- a/src/qml/jsruntime/qv4enginebase_p.h +++ b/src/qml/jsruntime/qv4enginebase_p.h @@ -57,31 +57,36 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct CppStackFrame; + // Base class for the execution engine #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) #pragma pack(push, 1) #endif -struct EngineBase { - Heap::ExecutionContext *current = 0; +struct Q_QML_EXPORT EngineBase { + + CppStackFrame *currentStackFrame = nullptr; - Value *jsStackTop = 0; + Value *jsStackTop = nullptr; quint8 hasException = false; quint8 writeBarrierActive = false; quint16 unused = 0; #if QT_POINTER_SIZE == 8 quint8 padding[4]; #endif - MemoryManager *memoryManager = 0; + MemoryManager *memoryManager = nullptr; Runtime runtime; qint32 callDepth = 0; - Value *jsStackLimit = 0; - Value *jsStackBase = 0; + Value *jsStackLimit = nullptr; + Value *jsStackBase = nullptr; + + IdentifierTable *identifierTable = nullptr; + Object *globalObject = nullptr; - ExecutionContext *currentContext = 0; - IdentifierTable *identifierTable = 0; - Object *globalObject = 0; + // Exception handling + Value *exceptionValue = nullptr; enum { Class_Empty, @@ -90,7 +95,7 @@ struct EngineBase { Class_SimpleArrayData, Class_SparseArrayData, Class_ExecutionContext, - Class_SimpleCallContext, + Class_CallContext, Class_Object, Class_ArrayObject, Class_FunctionObject, @@ -115,8 +120,8 @@ struct EngineBase { #endif Q_STATIC_ASSERT(std::is_standard_layout<EngineBase>::value); -Q_STATIC_ASSERT(offsetof(EngineBase, current) == 0); -Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, current) + QT_POINTER_SIZE); +Q_STATIC_ASSERT(offsetof(EngineBase, currentStackFrame) == 0); +Q_STATIC_ASSERT(offsetof(EngineBase, jsStackTop) == offsetof(EngineBase, currentStackFrame) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, hasException) == offsetof(EngineBase, jsStackTop) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, memoryManager) == offsetof(EngineBase, hasException) + QT_POINTER_SIZE); Q_STATIC_ASSERT(offsetof(EngineBase, runtime) == offsetof(EngineBase, memoryManager) + QT_POINTER_SIZE); diff --git a/src/qml/jsruntime/qv4errorobject.cpp b/src/qml/jsruntime/qv4errorobject.cpp index b3bd28e18b..01c59dbcbe 100644 --- a/src/qml/jsruntime/qv4errorobject.cpp +++ b/src/qml/jsruntime/qv4errorobject.cpp @@ -51,7 +51,6 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> #include <qv4codegen_p.h> #ifndef Q_OS_WIN @@ -107,6 +106,7 @@ void Heap::ErrorObject::init(const Value &message, ErrorType t) void Heap::ErrorObject::init(const Value &message, const QString &fileName, int line, int column, ErrorObject::ErrorType t) { + Q_UNUSED(fileName); // #### Object::init(); errorType = t; @@ -123,10 +123,9 @@ void Heap::ErrorObject::init(const Value &message, const QString &fileName, int frame.column = column; e->d()->stackTrace->prepend(frame); - if (!e->d()->stackTrace->isEmpty()) { - setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); - setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); - } + Q_ASSERT(!e->d()->stackTrace->isEmpty()); + setProperty(scope.engine, QV4::ErrorObject::Index_FileName, scope.engine->newString(e->d()->stackTrace->at(0).source)); + setProperty(scope.engine, QV4::ErrorObject::Index_LineNumber, Primitive::fromInt32(e->d()->stackTrace->at(0).line)); if (!message.isUndefined()) setProperty(scope.engine, QV4::ErrorObject::Index_Message, message); @@ -153,11 +152,12 @@ const char *ErrorObject::className(Heap::ErrorObject::ErrorType t) Q_UNREACHABLE(); } -void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ErrorObject::method_get_stack(const BuiltinFunction *b, CallData *callData) { - Scoped<ErrorObject> This(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + ErrorObject *This = callData->thisObject.as<ErrorObject>(); if (!This) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); if (!This->d()->stack) { QString trace; for (int i = 0; i < This->d()->stackTrace->count(); ++i) { @@ -168,9 +168,9 @@ void ErrorObject::method_get_stack(const BuiltinFunction *, Scope &scope, CallDa if (frame.line >= 0) trace += QLatin1Char(':') + QString::number(frame.line); } - This->d()->stack.set(scope.engine, scope.engine->newString(trace)); + This->d()->stack.set(v4, v4->newString(trace)); } - scope.result = This->d()->stack; + return This->d()->stack->asReturnedValue(); } DEFINE_OBJECT_VTABLE(ErrorObject); @@ -233,15 +233,15 @@ void Heap::ErrorCtor::init(QV4::ExecutionContext *scope, const QString &name) Heap::FunctionObject::init(scope, name); } -void ErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<ErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<ErrorObject>(f->engine(), v)->asReturnedValue(); } -void ErrorCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ErrorCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - static_cast<const Object *>(that)->construct(scope, callData); + return f->callAsConstructor(argv, argc); } void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) @@ -249,10 +249,10 @@ void Heap::EvalErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("EvalError")); } -void EvalErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue EvalErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<EvalErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<EvalErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) @@ -260,10 +260,10 @@ void Heap::RangeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("RangeError")); } -void RangeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue RangeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<RangeErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<RangeErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) @@ -271,10 +271,10 @@ void Heap::ReferenceErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("ReferenceError")); } -void ReferenceErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ReferenceErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<ReferenceErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<ReferenceErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) @@ -282,10 +282,10 @@ void Heap::SyntaxErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("SyntaxError")); } -void SyntaxErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue SyntaxErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<SyntaxErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<SyntaxErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) @@ -293,10 +293,10 @@ void Heap::TypeErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("TypeError")); } -void TypeErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue TypeErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<TypeErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<TypeErrorObject>(f->engine(), v)->asReturnedValue(); } void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) @@ -304,10 +304,10 @@ void Heap::URIErrorCtor::init(QV4::ExecutionContext *scope) Heap::ErrorCtor::init(scope, QStringLiteral("URIError")); } -void URIErrorCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue URIErrorCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ScopedValue v(scope, callData->argument(0)); - scope.result = ErrorObject::create<URIErrorObject>(scope.engine, v); + Value v = argc ? *argv : Primitive::undefinedValue(); + return ErrorObject::create<URIErrorObject>(f->engine(), v)->asReturnedValue(); } void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t) @@ -323,12 +323,14 @@ void ErrorPrototype::init(ExecutionEngine *engine, Object *ctor, Object *obj, He obj->defineDefaultProperty(engine->id_toString(), method_toString, 0); } -void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ErrorPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); Object *o = callData->thisObject.as<Object>(); if (!o) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); + Scope scope(v4); ScopedValue name(scope, o->get(scope.engine->id_name())); QString qname; if (name->isUndefined()) @@ -351,5 +353,5 @@ void ErrorPrototype::method_toString(const BuiltinFunction *, Scope &scope, Call str = qname + QLatin1String(": ") + qmessage; } - scope.result = scope.engine->newString(str)->asReturnedValue(); + return scope.engine->newString(str)->asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4errorobject_p.h b/src/qml/jsruntime/qv4errorobject_p.h index d556617b48..9942ee1ad6 100644 --- a/src/qml/jsruntime/qv4errorobject_p.h +++ b/src/qml/jsruntime/qv4errorobject_p.h @@ -175,7 +175,7 @@ struct ErrorObject: Object { static const char *className(Heap::ErrorObject::ErrorType t); - static void method_get_stack(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_stack(const BuiltinFunction *, CallData *callData); }; template<> @@ -229,50 +229,50 @@ struct ErrorCtor: FunctionObject { V4_OBJECT2(ErrorCtor, FunctionObject) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 EvalErrorCtor: ErrorCtor { V4_OBJECT2(EvalErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct RangeErrorCtor: ErrorCtor { V4_OBJECT2(RangeErrorCtor, ErrorCtor) - static void construct(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct ReferenceErrorCtor: ErrorCtor { V4_OBJECT2(ReferenceErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct SyntaxErrorCtor: ErrorCtor { V4_OBJECT2(SyntaxErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct TypeErrorCtor: ErrorCtor { V4_OBJECT2(TypeErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; struct URIErrorCtor: ErrorCtor { V4_OBJECT2(URIErrorCtor, ErrorCtor) - static void construct(const Managed *m, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); }; @@ -286,7 +286,7 @@ struct ErrorPrototype : ErrorObject void init(ExecutionEngine *engine, Object *ctor) { init(engine, ctor, this, Heap::ErrorObject::Error); } static void init(ExecutionEngine *engine, Object *ctor, Object *obj, Heap::ErrorObject::ErrorType t); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); }; struct EvalErrorPrototype : ErrorObject diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 4b88d85e68..32a72fda3f 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -38,6 +38,7 @@ ****************************************************************************/ #include "qv4function_p.h" +#include "qv4functionobject_p.h" #include "qv4managed_p.h" #include "qv4string_p.h" #include "qv4value_p.h" @@ -50,39 +51,27 @@ QT_BEGIN_NAMESPACE using namespace QV4; -Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, - ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *)) +Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr) : compiledFunction(function) , compilationUnit(unit) , code(codePtr) - , codeData(0) + , codeData(function->code()) , hasQmlDependencies(function->hasQmlDependencies()) { - internalClass = engine->internalClasses[EngineBase::Class_Empty]; - const quint32_le *formalsIndices = compiledFunction->formalsTable(); - // iterate backwards, so we get the right ordering for duplicate names - Scope scope(engine); - ScopedString arg(scope); - for (int i = static_cast<int>(compiledFunction->nFormals - 1); i >= 0; --i) { - arg = compilationUnit->runtimeStrings[formalsIndices[i]]; - while (1) { - InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); - if (newClass != internalClass) { - internalClass = newClass; - break; - } - // duplicate arguments, need some trick to store them - MemoryManager *mm = engine->memoryManager; - arg = mm->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); - } - } - nFormals = compiledFunction->nFormals; + Q_UNUSED(engine); + + internalClass = 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); - canUseSimpleCall = compiledFunction->flags & CompiledData::Function::CanUseSimpleCall; + 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); + + nFormals = compiledFunction->nFormals; } Function::~Function() @@ -91,30 +80,45 @@ Function::~Function() void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) { - internalClass = engine->internalClasses[EngineBase::Class_Empty]; + QStringList parameterNames; - // iterate backwards, so we get the right ordering for duplicate names - Scope scope(engine); - ScopedString arg(scope); - for (int i = parameters.size() - 1; i >= 0; --i) { - arg = engine->newString(QString::fromUtf8(parameters.at(i))); - while (1) { - InternalClass *newClass = internalClass->addMember(arg, Attr_NotConfigurable); - if (newClass != internalClass) { - internalClass = newClass; + // Resolve duplicate parameter names: + for (int i = 0, ei = parameters.count(); i != ei; ++i) { + const QByteArray ¶m = parameters.at(i); + int duplicate = -1; + + for (int j = i - 1; j >= 0; --j) { + const QByteArray &prevParam = parameters.at(j); + if (param == prevParam) { + duplicate = j; break; } - // duplicate arguments, need some trick to store them - arg = engine->memoryManager->alloc<String>(arg->d(), engine->newString(QString(0xfffe))); } + + if (duplicate == -1) { + parameterNames.append(QString::fromUtf8(param)); + } else { + const QString &dup = parameterNames[duplicate]; + parameterNames.append(dup); + parameterNames[duplicate] = + QString(0xfffe) + QString::number(duplicate) + dup; + } + + } + + internalClass = engine->internalClasses[EngineBase::Class_CallContext]; + + Scope scope(engine); + ScopedString arg(scope); + for (const QString ¶meterName : parameterNames) { + arg = engine->newString(parameterName); + internalClass = internalClass->addMember(arg, Attr_NotConfigurable); } nFormals = parameters.size(); 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); - - canUseSimpleCall = false; } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index b11c8af94a..5ecbd0a359 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -63,17 +63,21 @@ struct Q_QML_EXPORT Function { const CompiledData::Function *compiledFunction; CompiledData::CompilationUnit *compilationUnit; - ReturnedValue (*code)(ExecutionEngine *, const uchar *); + ReturnedValue call(CallData *callData) { + return code(callData, this); + } + + + typedef ReturnedValue (*Code)(CallData *, Function *); + Code code; const uchar *codeData; // first nArguments names in internalClass are the actual arguments InternalClass *internalClass; uint nFormals; bool hasQmlDependencies; - bool canUseSimpleCall; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, - ReturnedValue (*codePtr)(ExecutionEngine *, const uchar *)); + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function, Code codePtr); ~Function(); // used when dynamically assigning signal handlers (QQmlConnection) @@ -86,21 +90,18 @@ struct Q_QML_EXPORT Function { inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } - inline bool isNamedExpression() const { return compiledFunction->flags & CompiledData::Function::IsNamedExpression; } - - inline bool canUseSimpleFunction() const { return canUseSimpleCall; } QQmlSourceLocation sourceLocation() const { return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } - }; -inline unsigned int Heap::SimpleCallContext::formalParameterCount() const +inline unsigned int Heap::CallContext::formalParameterCount() const { - return v4Function ? v4Function->nFormals : 0; + Q_ASSERT(v4Function); + return v4Function->nFormals; } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index 45fdde98f7..bd8bb9c8fb 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -38,8 +38,6 @@ ****************************************************************************/ #include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4function_p.h" @@ -55,9 +53,10 @@ #include <private/qqmljsast_p.h> #include <private/qqmljavascriptexpression_p.h> #include <private/qqmlengine_p.h> -#include <qv4codegen_p.h> +#include <qv4runtimecodegen_p.h> #include "private/qlocale_tools_p.h" #include "private/qqmlbuiltinfunctions_p.h" +#include <private/qv4jscall_p.h> #include <QtCore/QDebug> #include <algorithm> @@ -73,6 +72,9 @@ Q_STATIC_ASSERT((Heap::FunctionObject::markTable & Heap::Object::markTable) == H void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, bool createProto) { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); function = nullptr; this->scope.set(scope->engine(), scope->d()); @@ -83,6 +85,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, QV4::String *name, void Heap::FunctionObject::init(QV4::ExecutionContext *scope, Function *function, bool createProto) { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); this->function = function; function->compilationUnit->addref(); @@ -102,6 +107,9 @@ void Heap::FunctionObject::init(QV4::ExecutionContext *scope, const QString &nam void Heap::FunctionObject::init() { + jsCall = reinterpret_cast<const ObjectVTable *>(vtable())->call; + jsConstruct = reinterpret_cast<const ObjectVTable *>(vtable())->callAsConstructor; + Object::init(); function = nullptr; this->scope.set(internalClass->engine, internalClass->engine->rootContext()->d()); @@ -141,14 +149,14 @@ ReturnedValue FunctionObject::name() const return get(scope()->internalClass->engine->id_name()); } -void FunctionObject::construct(const Managed *that, Scope &scope, CallData *) +ReturnedValue FunctionObject::callAsConstructor(const FunctionObject *f, const Value *, int) { - scope.result = static_cast<const FunctionObject *>(that)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } -void FunctionObject::call(const Managed *, Scope &scope, CallData *) +ReturnedValue FunctionObject::call(const FunctionObject *, const Value *, const Value *, int) { - scope.result = Encode::undefined(); + return Encode::undefined(); } Heap::FunctionObject *FunctionObject::createScriptFunction(ExecutionContext *scope, Function *function) @@ -179,24 +187,22 @@ void Heap::FunctionCtor::init(QV4::ExecutionContext *scope) } // 15.3.2 -void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue FunctionCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<FunctionCtor> f(scope, static_cast<const FunctionCtor *>(that)); + Scope scope(f->engine()); QString arguments; QString body; - if (callData->argc > 0) { - for (int i = 0; i < callData->argc - 1; ++i) { + if (argc > 0) { + for (int i = 0, ei = argc - 1; i < ei; ++i) { if (i) arguments += QLatin1String(", "); - arguments += callData->args[i].toQString(); + arguments += argv[i].toQString(); } - body = callData->args[callData->argc - 1].toQString(); - } - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; + body = argv[argc - 1].toQString(); } + if (scope.engine->hasException) + return Encode::undefined(); QString function = QLatin1String("function(") + arguments + QLatin1String("){") + body + QLatin1Char('}'); @@ -207,36 +213,30 @@ void FunctionCtor::construct(const Managed *that, Scope &scope, CallData *callDa const bool parsed = parser.parseExpression(); - if (!parsed) { - scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); - return; - } + if (!parsed) + return scope.engine->throwSyntaxError(QLatin1String("Parse error")); - using namespace QQmlJS::AST; - FunctionExpression *fe = QQmlJS::AST::cast<FunctionExpression *>(parser.rootNode()); - if (!fe) { - scope.result = scope.engine->throwSyntaxError(QLatin1String("Parse error")); - return; - } + QQmlJS::AST::FunctionExpression *fe = QQmlJS::AST::cast<QQmlJS::AST::FunctionExpression *>(parser.rootNode()); + if (!fe) + return scope.engine->throwSyntaxError(QLatin1String("Parse error")); - IR::Module module(scope.engine->debugger() != 0); + Compiler::Module module(scope.engine->debugger() != 0); - QQmlJS::RuntimeCodegen cg(scope.engine, f->strictMode()); + Compiler::JSUnitGenerator jsGenerator(&module); + RuntimeCodegen cg(scope.engine, &jsGenerator, false); cg.generateFromFunctionExpression(QString(), function, fe, &module); - Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(scope.engine->iselFactory->create(QQmlEnginePrivate::get(scope.engine), scope.engine->executableAllocator, &module, &jsGenerator)); - QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = isel->compile(); + QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit = cg.generateCompilationUnit(); Function *vmf = compilationUnit->linkToEngine(scope.engine); ExecutionContext *global = scope.engine->rootContext(); - scope.result = FunctionObject::createScriptFunction(global, vmf); + return Encode(FunctionObject::createScriptFunction(global, vmf)); } // 15.3.1: This is equivalent to new Function(...) -void FunctionCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue FunctionCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } DEFINE_OBJECT_VTABLE(FunctionPrototype); @@ -263,152 +263,139 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) } -void FunctionPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); FunctionObject *fun = callData->thisObject.as<FunctionObject>(); if (!fun) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = scope.engine->newString(QStringLiteral("function() { [code] }")); + return Encode(v4->newString(QStringLiteral("function() { [code] }"))); } -void FunctionPrototype::method_apply(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_apply(const BuiltinFunction *b, CallData *callData) { - FunctionObject *o = callData->thisObject.as<FunctionObject>(); + ExecutionEngine *v4 = b->engine(); + callData->function = callData->thisObject; + FunctionObject *o = callData->function.as<FunctionObject>(); if (!o) - THROW_TYPE_ERROR(); - - ScopedValue arg(scope, callData->argument(1)); - - ScopedObject arr(scope, arg); - - quint32 len; - if (!arr) { - len = 0; - if (!arg->isNullOrUndefined()) - THROW_TYPE_ERROR(); - } else { - len = arr->getLength(); - } - - ScopedCallData cData(scope, len); - - if (len) { - if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { - QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); - int l = qMin(len, (uint)a->d()->context->callData->argc); - memcpy(cData->args, a->d()->context->callData->args, l*sizeof(Value)); - for (quint32 i = l; i < len; ++i) - cData->args[i] = Primitive::undefinedValue(); - } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { - auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); - uint alen = sad ? sad->values.size : 0; - if (alen > len) - alen = len; - for (uint i = 0; i < alen; ++i) - cData->args[i] = sad->data(i); - for (quint32 i = alen; i < len; ++i) - cData->args[i] = Primitive::undefinedValue(); - } else { - for (quint32 i = 0; i < len; ++i) - cData->args[i] = arr->getIndexed(i); + return v4->throwTypeError(); + callData->thisObject = callData->argument(0); + callData->accumulator = callData->argument(1); + + if (callData->accumulator.isObject()) { + Object *arr = static_cast<Object *>(&callData->accumulator); + uint len = arr->getLength(); + callData->setArgc(len); + + v4->jsStackTop = callData->args + len; + if (len) { + if (ArgumentsObject::isNonStrictArgumentsObject(arr) && !arr->cast<ArgumentsObject>()->fullyCreated()) { + QV4::ArgumentsObject *a = arr->cast<ArgumentsObject>(); + int l = qMin(len, (uint)a->d()->context->argc()); + memcpy(callData->args, a->d()->context->args(), l*sizeof(Value)); + for (quint32 i = l; i < len; ++i) + callData->args[i] = Primitive::undefinedValue(); + } else if (arr->arrayType() == Heap::ArrayData::Simple && !arr->protoHasArray()) { + auto sad = static_cast<Heap::SimpleArrayData *>(arr->arrayData()); + uint alen = sad ? sad->values.size : 0; + if (alen > len) + alen = len; + for (uint i = 0; i < alen; ++i) + callData->args[i] = sad->data(i); + for (quint32 i = alen; i < len; ++i) + callData->args[i] = Primitive::undefinedValue(); + } else { + for (quint32 i = 0; i < len; ++i) + callData->args[i] = arr->getIndexed(i); + } } + } else if (!callData->accumulator.isNullOrUndefined()) { + return v4->throwTypeError(); + } else { + callData->setArgc(0); + v4->jsStackTop = callData->args; } - cData->thisObject = callData->argument(0); - o->call(scope, cData); + return o->call(&callData->thisObject, callData->args, callData->argc()); } -void FunctionPrototype::method_call(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_call(const BuiltinFunction *b, CallData *callData) { - FunctionObject *o = callData->thisObject.as<FunctionObject>(); - if (!o) - THROW_TYPE_ERROR(); + ExecutionEngine *engine = b->engine(); + if (!callData->thisObject.isFunctionObject()) + return engine->throwTypeError(); - ScopedCallData cData(scope, callData->argc ? callData->argc - 1 : 0); - if (callData->argc) { - for (int i = 1; i < callData->argc; ++i) - cData->args[i - 1] = callData->args[i]; - } - cData->thisObject = callData->argument(0); + Q_ASSERT(engine->jsStackTop == callData->args + callData->argc()); - o->call(scope, cData); + callData->function = callData->thisObject; + callData->thisObject = callData->argc() ? callData->args[0] : Primitive::undefinedValue(); + if (callData->argc()) { + callData->setArgc(callData->argc() - 1); + for (int i = 0, ei = callData->argc(); i < ei; ++i) + callData->args[i] = callData->args[i + 1]; + --engine->jsStackTop; + } + return static_cast<FunctionObject &>(callData->function).call(&callData->thisObject, callData->args, callData->argc()); } -void FunctionPrototype::method_bind(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue FunctionPrototype::method_bind(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); FunctionObject *target = callData->thisObject.as<FunctionObject>(); if (!target) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); ScopedValue boundThis(scope, callData->argument(0)); Scoped<MemberData> boundArgs(scope, (Heap::MemberData *)0); - if (callData->argc > 1) { - boundArgs = MemberData::allocate(scope.engine, callData->argc - 1); - boundArgs->d()->values.size = callData->argc - 1; - for (uint i = 0; i < static_cast<uint>(callData->argc - 1); ++i) + if (callData->argc() > 1) { + boundArgs = MemberData::allocate(scope.engine, callData->argc() - 1); + boundArgs->d()->values.size = callData->argc() - 1; + for (uint i = 0, ei = static_cast<uint>(callData->argc() - 1); i < ei; ++i) boundArgs->set(scope.engine, i, callData->args[i + 1]); } ExecutionContext *global = scope.engine->rootContext(); - scope.result = BoundFunction::create(global, target, boundThis, boundArgs); + return BoundFunction::create(global, target, boundThis, boundArgs)->asReturnedValue(); } DEFINE_OBJECT_VTABLE(ScriptFunction); -void ScriptFunction::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ScriptFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - ExecutionContextSaver ctxSaver(scope); - - Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); + ExecutionEngine *v4 = fo->engine(); + const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); + Scope scope(v4); + JSCallData callData(scope, argc, argv); + CallData *cData = callData.callData(f); InternalClass *ic = f->classForConstructor(); - ScopedObject proto(scope, ic->prototype); - ScopedObject obj(scope, v4->newObject(ic, proto)); - callData->thisObject = obj.asReturnedValue(); + cData->context = f->scope(); + cData->thisObject = v4->memoryManager->allocObject<Object>(ic); QV4::Function *v4Function = f->function(); Q_ASSERT(v4Function); - ScopedContext c(scope, f->scope()); - if (v4Function->canUseSimpleCall) - c->simpleCall(scope, callData, v4Function); - else - c->call(scope, callData, v4Function, f); + ReturnedValue result = v4Function->call(cData); - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - } else if (!scope.result.isObject()) { - scope.result = obj.asReturnedValue(); - } + if (Q_UNLIKELY(v4->hasException)) + return Encode::undefined(); + else if (!Value::fromReturnedValue(result).isObject()) + return cData->thisObject.asReturnedValue(); + return result; } -void ScriptFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ScriptFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (Q_UNLIKELY(v4->hasException)) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - Scoped<ScriptFunction> f(scope, static_cast<const ScriptFunction *>(that)); + const ScriptFunction *f = static_cast<const ScriptFunction *>(fo); + Scope scope(f->engine()); + JSCallData callData(scope, argc, argv, thisObject); + CallData *cData = callData.callData(f); + cData->context = f->scope(); QV4::Function *v4Function = f->function(); Q_ASSERT(v4Function); - - ScopedContext c(scope, f->scope()); - if (v4Function->canUseSimpleCall) - c->simpleCall(scope, callData, v4Function); - else - c->call(scope, callData, v4Function, f); + return v4Function->call(cData); } void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function) @@ -429,7 +416,7 @@ void Heap::ScriptFunction::init(QV4::ExecutionContext *scope, Function *function Q_ASSERT(internalClass && internalClass->find(s.engine->id_length()) == Index_Length); setProperty(s.engine, Index_Length, Primitive::fromInt32(f->formalParameterCount())); - if (scope->d()->strictMode) { + if (function->isStrict()) { ScopedProperty pd(s); pd->value = s.engine->thrower(); pd->set = s.engine->thrower(); @@ -455,49 +442,23 @@ InternalClass *ScriptFunction::classForConstructor() const DEFINE_OBJECT_VTABLE(BuiltinFunction); -void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)) +void Heap::BuiltinFunction::init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)) { Heap::FunctionObject::init(scope, name); this->code = code; } -void BuiltinFunction::construct(const Managed *f, Scope &scope, CallData *) +ReturnedValue BuiltinFunction::callAsConstructor(const QV4::FunctionObject *f, const Value *, int) { - scope.result = static_cast<const BuiltinFunction *>(f)->internalClass()->engine->throwTypeError(); + return f->engine()->throwTypeError(); } -void BuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue BuiltinFunction::call(const FunctionObject *fo, const Value *thisObject, const Value *argv, int argc) { - const BuiltinFunction *f = static_cast<const BuiltinFunction *>(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - f->d()->code(f, scope, callData); -} - - -void IndexedBuiltinFunction::call(const Managed *that, Scope &scope, CallData *callData) -{ - const IndexedBuiltinFunction *f = static_cast<const IndexedBuiltinFunction *>(that); - ExecutionEngine *v4 = scope.engine; - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } - CHECK_STACK_LIMITS(v4, scope); - - ExecutionContextSaver ctxSaver(scope); - - SimpleCallContext::Data *ctx = v4->memoryManager->allocSimpleCallContext(); - ctx->strictMode = f->scope()->strictMode; // ### needed? scope or parent context? - ctx->callData = callData; - v4->pushContext(ctx); - Q_ASSERT(v4->current == ctx); - - scope.result = f->d()->code(static_cast<QV4::CallContext *>(v4->currentContext), f->d()->index); - v4->memoryManager->freeSimpleCallContext(); + const BuiltinFunction *f = static_cast<const BuiltinFunction *>(fo); + Scope scope(f->engine()); + JSCallData callData(scope, argc, argv, thisObject); + return f->d()->code(f, callData.callData()); } DEFINE_OBJECT_VTABLE(IndexedBuiltinFunction); @@ -530,43 +491,43 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject f->insertMember(s.engine->id_caller(), pd, Attr_Accessor|Attr_NotConfigurable|Attr_NotEnumerable); } -void BoundFunction::call(const Managed *that, Scope &scope, CallData *dd) +ReturnedValue BoundFunction::call(const FunctionObject *fo, const Value *, const Value *argv, int argc) { - const BoundFunction *f = static_cast<const BoundFunction *>(that); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + const BoundFunction *f = static_cast<const BoundFunction *>(fo); + Scope scope(f->engine()); + + if (scope.hasException()) + return Encode::undefined(); Scoped<MemberData> boundArgs(scope, f->boundArgs()); - ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); - callData->thisObject = f->boundThis(); - Value *argp = callData->args; + ScopedFunctionObject target(scope, f->target()); + JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + *jsCallData->thisObject = f->boundThis(); + Value *argp = jsCallData->args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); } - memcpy(argp, dd->args, dd->argc*sizeof(Value)); - ScopedFunctionObject t(scope, f->target()); - t->call(scope, callData); + memcpy(argp, argv, argc*sizeof(Value)); + return target->call(jsCallData); } -void BoundFunction::construct(const Managed *that, Scope &scope, CallData *dd) +ReturnedValue BoundFunction::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - const BoundFunction *f = static_cast<const BoundFunction *>(that); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + const BoundFunction *f = static_cast<const BoundFunction *>(fo); + Scope scope(f->engine()); + + if (scope.hasException()) + return Encode::undefined(); Scoped<MemberData> boundArgs(scope, f->boundArgs()); - ScopedCallData callData(scope, (boundArgs ? boundArgs->size() : 0) + dd->argc); - Value *argp = callData->args; + ScopedFunctionObject target(scope, f->target()); + JSCallData jsCallData(scope, (boundArgs ? boundArgs->size() : 0) + argc); + Value *argp = jsCallData->args; if (boundArgs) { memcpy(argp, boundArgs->data(), boundArgs->size()*sizeof(Value)); argp += boundArgs->size(); } - memcpy(argp, dd->args, dd->argc*sizeof(Value)); - ScopedFunctionObject t(scope, f->target()); - t->construct(scope, callData); + memcpy(argp, argv, argc*sizeof(Value)); + return target->callAsConstructor(jsCallData); } diff --git a/src/qml/jsruntime/qv4functionobject_p.h b/src/qml/jsruntime/qv4functionobject_p.h index 6ce5734b6d..3fd0fbeec8 100644 --- a/src/qml/jsruntime/qv4functionobject_p.h +++ b/src/qml/jsruntime/qv4functionobject_p.h @@ -62,12 +62,20 @@ struct QQmlSourceLocation; namespace QV4 { struct BuiltinFunction; +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 { + #define FunctionObjectMembers(class, Member) \ Member(class, Pointer, ExecutionContext *, scope) \ - Member(class, NoMark, Function *, function) + Member(class, NoMark, Function *, function) \ + Member(class, NoMark, jsCallFunction, jsCall) \ + Member(class, NoMark, jsConstructFunction, jsConstruct) DECLARE_HEAP_OBJECT(FunctionObject, Object) { DECLARE_MARK_TABLE(FunctionObject); @@ -96,19 +104,13 @@ struct FunctionPrototype : FunctionObject { void init(); }; -struct Q_QML_EXPORT OldBuiltinFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(QV4::CallContext *)); - ReturnedValue (*code)(QV4::CallContext *); -}; - struct Q_QML_EXPORT BuiltinFunction : FunctionObject { - void init(QV4::ExecutionContext *scope, QV4::String *name, void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *)); - void (*code)(const QV4::BuiltinFunction *, Scope &, CallData *); + void init(QV4::ExecutionContext *scope, QV4::String *name, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)); + ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *); }; -struct IndexedBuiltinFunction : FunctionObject { - inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(QV4::CallContext *ctx, uint index)); - ReturnedValue (*code)(QV4::CallContext *, uint index); +struct IndexedBuiltinFunction : BuiltinFunction { + inline void init(QV4::ExecutionContext *scope, uint index, ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)); uint index; }; @@ -154,10 +156,16 @@ struct Q_QML_EXPORT FunctionObject: Object { void init(String *name, bool createProto); - using Object::construct; - using Object::call; - static void construct(const Managed *that, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *d); + inline ReturnedValue callAsConstructor(const JSCallData &data) const; + ReturnedValue callAsConstructor(const Value *argv, int argc) const { + return d()->jsConstruct(this, argv, argc); + } + inline ReturnedValue call(const JSCallData &data) const; + ReturnedValue call(const Value *thisObject, const Value *argv, int argc) const { + return d()->jsCall(this, thisObject, argv, argc); + } + 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 Heap::FunctionObject *createScriptFunction(ExecutionContext *scope, Function *function); @@ -178,8 +186,8 @@ struct FunctionCtor: FunctionObject { V4_OBJECT2(FunctionCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 FunctionPrototype: FunctionObject @@ -188,43 +196,36 @@ struct FunctionPrototype: FunctionObject void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_apply(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_call(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_bind(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_apply(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_call(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_bind(const BuiltinFunction *, CallData *callData); }; struct Q_QML_EXPORT BuiltinFunction : FunctionObject { V4_OBJECT2(BuiltinFunction, FunctionObject) V4_INTERNALCLASS(BuiltinFunction) - static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *)) + static Heap::BuiltinFunction *create(ExecutionContext *scope, String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *)) { return scope->engine()->memoryManager->allocObject<BuiltinFunction>(scope, name, code); } - static void construct(const Managed *, Scope &scope, CallData *); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; -struct IndexedBuiltinFunction: FunctionObject +struct IndexedBuiltinFunction: BuiltinFunction { - V4_OBJECT2(IndexedBuiltinFunction, FunctionObject) - - static void construct(const Managed *m, Scope &scope, CallData *) - { - scope.result = static_cast<const IndexedBuiltinFunction *>(m)->engine()->throwTypeError(); - } - - static void call(const Managed *that, Scope &scope, CallData *callData); + V4_OBJECT2(IndexedBuiltinFunction, BuiltinFunction) }; void Heap::IndexedBuiltinFunction::init(QV4::ExecutionContext *scope, uint index, - ReturnedValue (*code)(QV4::CallContext *ctx, uint index)) + ReturnedValue (*code)(const QV4::BuiltinFunction *, CallData *)) { Heap::FunctionObject::init(scope); - this->index = index; this->code = code; + this->index = index; } @@ -232,8 +233,8 @@ struct ScriptFunction : FunctionObject { V4_OBJECT2(ScriptFunction, FunctionObject) V4_INTERNALCLASS(ScriptFunction) - static void construct(const Managed *, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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; }; @@ -251,8 +252,8 @@ struct BoundFunction: FunctionObject { Value boundThis() const { return d()->boundThis; } Heap::MemberData *boundArgs() const { return d()->boundArgs; } - static void construct(const Managed *, Scope &scope, CallData *d); - static void call(const Managed *that, Scope &scope, CallData *dd); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; } diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 8769519a59..6bdeda3313 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -147,6 +147,12 @@ QT_BEGIN_NAMESPACE namespace QV4 { +namespace Compiler { + struct Module; + struct Context; + struct JSUnitGenerator; +} + namespace Heap { struct Base; struct MemberData; @@ -157,7 +163,6 @@ namespace Heap { struct ObjectPrototype; struct ExecutionContext; - struct GlobalContext; struct CallContext; struct ScriptFunction; @@ -182,12 +187,12 @@ namespace Heap { } class MemoryManager; +class ExecutableAllocator; struct String; struct Object; struct ObjectPrototype; struct ObjectIterator; struct ExecutionContext; -struct GlobalContext; struct CallContext; struct ScriptFunction; struct InternalClass; @@ -243,12 +248,6 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; -namespace Global { - enum { - ReservedArgumentCount = 6 - }; -} - enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, @@ -349,11 +348,11 @@ struct PropertyAttributes } }; -struct StackFrame { +struct Q_QML_EXPORT StackFrame { QString source; QString function; - int line; - int column; + int line = -1; + int column = -1; }; typedef QVector<StackFrame> StackTrace; diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 0916e8e110..a015461c47 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -47,12 +47,12 @@ #include "qv4script_p.h" #include "qv4scopedvalue_p.h" #include "qv4string_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 <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include "private/qtools_p.h" @@ -338,72 +338,68 @@ void Heap::EvalFunction::init(QV4::ExecutionContext *scope) f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(1)); } -void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) const +ReturnedValue EvalFunction::evalCall(const Value *, const Value *argv, int argc, bool directCall) const { - if (callData->argc < 1) { - scope.result = Encode::undefined(); - return; - } + if (argc < 1) + return Encode::undefined(); ExecutionEngine *v4 = engine(); - ExecutionContextSaver ctxSaver(scope); + bool isStrict = v4->currentStackFrame->v4Function->isStrict(); - ExecutionContext *currentContext = v4->currentContext; - ExecutionContext *ctx = currentContext; + Scope scope(v4); + ScopedContext ctx(scope, v4->currentContext()); if (!directCall) { - // the context for eval should be the global scope, so we fake a root - // context - ctx = v4->pushGlobalContext(); + // the context for eval should be the global scope + ctx = v4->rootContext(); } - String *scode = callData->args[0].stringValue(); - if (!scode) { - scope.result = callData->args[0].asReturnedValue(); - return; - } + String *scode = argv[0].stringValue(); + if (!scode) + return argv[0].asReturnedValue(); const QString code = scode->toQString(); - bool inheritContext = !ctx->d()->strictMode; + bool inheritContext = !ctx->d()->v4Function->isStrict(); - Script script(ctx, code, QStringLiteral("eval code")); - script.strictMode = (directCall && currentContext->d()->strictMode); + Script script(ctx, QV4::Compiler::EvalCode, code, QStringLiteral("eval code")); + script.strictMode = (directCall && isStrict); script.inheritContext = inheritContext; script.parse(); - if (v4->hasException) { - scope.result = Encode::undefined(); - return; - } + if (v4->hasException) + return Encode::undefined(); Function *function = script.function(); - if (!function) { - scope.result = Encode::undefined(); - return; - } + if (!function) + return Encode::undefined(); - if (function->isStrict() || (ctx->d()->strictMode)) { + if (function->isStrict() || (ctx->d()->v4Function->isStrict())) { ScopedFunctionObject e(scope, FunctionObject::createScriptFunction(ctx, function)); - ScopedCallData callData(scope, 0); - callData->thisObject = ctx->thisObject(); - e->call(scope, callData); - return; + JSCallData jsCallData(scope, 0); + if (directCall) + *jsCallData->thisObject = scope.engine->currentStackFrame->thisObject(); + else + *jsCallData->thisObject = scope.engine->globalObject; + return e->call(jsCallData); } ContextStateSaver stateSaver(scope, ctx); - // set the correct strict mode flag on the context - ctx->d()->strictMode = false; - ctx->d()->compilationUnit = function->compilationUnit; - ctx->d()->constantTable = function->compilationUnit->constants; + // set the correct v4 function for the context + ctx->d()->v4Function = function; + + JSCallData jsCallData(scope); + *jsCallData->thisObject = scope.engine->currentStackFrame->thisObject(); + CallData *cData = jsCallData.callData(); + cData->context = *ctx; - scope.result = Q_V4_PROFILE(ctx->engine(), function); + return function->call(cData); } -void EvalFunction::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue EvalFunction::call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc) { // indirect call - static_cast<const EvalFunction *>(that)->evalCall(scope, callData, false); + return static_cast<const EvalFunction *>(f)->evalCall(thisObject, argv, argc, false); } @@ -424,8 +420,9 @@ static inline int toInt(const QChar &qc, int R) } // parseInt [15.1.2.2] -void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_parseInt(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedValue inputString(scope, callData->argument(0)); ScopedValue radix(scope, callData->argument(1)); int R = radix->isUndefined() ? 0 : radix->toInt32(); @@ -504,8 +501,9 @@ void GlobalFunctions::method_parseInt(const BuiltinFunction *, Scope &scope, Cal } // parseFloat [15.1.2.3] -void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_parseFloat(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); // [15.1.2.3] step by step: ScopedString inputString(scope, callData->argument(0), ScopedString::Convert); CHECK_EXCEPTION(); @@ -530,9 +528,9 @@ void GlobalFunctions::method_parseFloat(const BuiltinFunction *, Scope &scope, C } /// isNaN [15.1.2.4] -void GlobalFunctions::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_isNaN(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) + if (!callData->argc()) // undefined gets converted to NaN RETURN_RESULT(Encode(true)); @@ -544,9 +542,9 @@ void GlobalFunctions::method_isNaN(const BuiltinFunction *, Scope &scope, CallDa } /// isFinite [15.1.2.5] -void GlobalFunctions::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_isFinite(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) + if (!callData->argc()) // undefined gets converted to NaN RETURN_RESULT(Encode(false)); @@ -558,87 +556,97 @@ void GlobalFunctions::method_isFinite(const BuiltinFunction *, Scope &scope, Cal } /// decodeURI [15.1.3.1] -void GlobalFunctions::method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_decodeURI(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + if (callData->argc() == 0) RETURN_UNDEFINED(); + ExecutionEngine *v4 = b->engine(); QString uriString = callData->args[0].toQString(); bool ok; QString out = decode(uriString, DecodeNonReserved, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// decodeURIComponent [15.1.3.2] -void GlobalFunctions::method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_decodeURIComponent(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + if (callData->argc() == 0) RETURN_UNDEFINED(); + ExecutionEngine *v4 = b->engine(); QString uriString = callData->args[0].toQString(); bool ok; QString out = decode(uriString, DecodeAll, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// encodeURI [15.1.3.3] -void GlobalFunctions::method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_encodeURI(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + if (callData->argc() == 0) RETURN_UNDEFINED(); + ExecutionEngine *v4 = b->engine(); QString uriString = callData->args[0].toQString(); bool ok; QString out = encode(uriString, uriUnescapedReserved, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } /// encodeURIComponent [15.1.3.4] -void GlobalFunctions::method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_encodeURIComponent(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + if (callData->argc() == 0) RETURN_UNDEFINED(); + ExecutionEngine *v4 = b->engine(); QString uriString = callData->args[0].toQString(); bool ok; QString out = encode(uriString, uriUnescaped, &ok); if (!ok) { + Scope scope(v4); ScopedString s(scope, scope.engine->newString(QStringLiteral("malformed URI sequence"))); RETURN_RESULT(scope.engine->throwURIError(s)); } - RETURN_RESULT(scope.engine->newString(out)); + RETURN_RESULT(v4->newString(out)); } -void GlobalFunctions::method_escape(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_escape(const BuiltinFunction *b, CallData *callData) { - if (!callData->argc) - RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); + ExecutionEngine *v4 = b->engine(); + if (!callData->argc()) + RETURN_RESULT(v4->newString(QStringLiteral("undefined"))); QString str = callData->args[0].toQString(); - RETURN_RESULT(scope.engine->newString(escape(str))); + RETURN_RESULT(v4->newString(escape(str))); } -void GlobalFunctions::method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue GlobalFunctions::method_unescape(const BuiltinFunction *b, CallData *callData) { - if (!callData->argc) - RETURN_RESULT(scope.engine->newString(QStringLiteral("undefined"))); + ExecutionEngine *v4 = b->engine(); + if (!callData->argc()) + RETURN_RESULT(v4->newString(QStringLiteral("undefined"))); QString str = callData->args[0].toQString(); - RETURN_RESULT(scope.engine->newString(unescape(str))); + RETURN_RESULT(v4->newString(unescape(str))); } diff --git a/src/qml/jsruntime/qv4globalobject_p.h b/src/qml/jsruntime/qv4globalobject_p.h index 273f1ba7ea..af72d3af2f 100644 --- a/src/qml/jsruntime/qv4globalobject_p.h +++ b/src/qml/jsruntime/qv4globalobject_p.h @@ -69,23 +69,23 @@ struct Q_QML_EXPORT EvalFunction : FunctionObject { V4_OBJECT2(EvalFunction, FunctionObject) - void evalCall(Scope &scope, CallData *callData, bool directCall) const; + ReturnedValue evalCall(const Value *thisObject, const Value *argv, int argc, bool directCall) const; - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *f, const Value *thisObject, const Value *argv, int argc); }; struct GlobalFunctions { - static void method_parseInt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_parseFloat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_decodeURI(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_decodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_encodeURI(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_encodeURIComponent(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_escape(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_unescape(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_parseInt(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_parseFloat(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isNaN(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isFinite(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_decodeURI(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_decodeURIComponent(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_encodeURI(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_encodeURIComponent(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_escape(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_unescape(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4include.cpp b/src/qml/jsruntime/qv4include.cpp index 1edb7d3914..3e04ed63df 100644 --- a/src/qml/jsruntime/qv4include.cpp +++ b/src/qml/jsruntime/qv4include.cpp @@ -39,6 +39,7 @@ #include "qv4include_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <QtQml/qjsengine.h> #if QT_CONFIG(qml_network) @@ -118,10 +119,10 @@ void QV4Include::callback(const QV4::Value &callback, const QV4::Value &status) if (!f) return; - QV4::ScopedCallData callData(scope, 1); - callData->thisObject = v4->globalObject->asReturnedValue(); - callData->args[0] = status; - f->call(scope, callData); + QV4::JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = v4->globalObject->asReturnedValue(); + jsCallData->args[0] = status; + f->call(jsCallData); if (scope.hasException()) scope.engine->catchException(); } @@ -195,9 +196,10 @@ void QV4Include::finished() /* Documented in qv8engine.cpp */ -void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData) +QV4::ReturnedValue QV4Include::method_include(const QV4::BuiltinFunction *b, QV4::CallData *callData) { - if (!callData->argc) + QV4::Scope scope(b); + if (!callData->argc()) RETURN_UNDEFINED(); QQmlContextData *context = scope.engine->callingQmlContext(); @@ -206,7 +208,7 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, RETURN_RESULT(scope.engine->throwError(QString::fromUtf8("Qt.include(): Can only be called from JavaScript files"))); QV4::ScopedValue callbackFunction(scope, QV4::Primitive::undefinedValue()); - if (callData->argc >= 2 && callData->args[1].as<QV4::FunctionObject>()) + if (callData->argc() >= 2 && callData->args[1].as<QV4::FunctionObject>()) callbackFunction = callData->args[1]; #if QT_CONFIG(qml_network) @@ -260,13 +262,13 @@ void QV4Include::method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, callback(callbackFunction, result); } - scope.result = result; #else QV4::ScopedValue result(scope); result = resultValue(scope.engine, NetworkError); callback(callbackFunction, result); - scope.result = result; #endif + + return result->asReturnedValue(); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4include_p.h b/src/qml/jsruntime/qv4include_p.h index 5908d6bfde..68537ba2e8 100644 --- a/src/qml/jsruntime/qv4include_p.h +++ b/src/qml/jsruntime/qv4include_p.h @@ -77,7 +77,7 @@ public: Exception = 3 }; - static void method_include(const QV4::BuiltinFunction *, QV4::Scope &scope, QV4::CallData *callData); + static QV4::ReturnedValue method_include(const QV4::BuiltinFunction *, QV4::CallData *callData); private Q_SLOTS: void finished(); diff --git a/src/qml/jsruntime/qv4jscall_p.h b/src/qml/jsruntime/qv4jscall_p.h new file mode 100644 index 0000000000..6d641bf9c5 --- /dev/null +++ b/src/qml/jsruntime/qv4jscall_p.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QV4JSCALL_H +#define QV4JSCALL_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 "qv4function_p.h" +#include "qv4functionobject_p.h" +#include "qv4context_p.h" +#include "qv4scopedvalue_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct JSCallData { + JSCallData(const Scope &scope, int argc = 0, const Value *argv = 0, const Value *thisObject = 0) + : scope(scope), argc(argc) + { + if (thisObject) + this->thisObject = const_cast<Value *>(thisObject); + else + this->thisObject = scope.alloc(1); + if (argv) + this->args = const_cast<Value *>(argv); + else + this->args = scope.alloc(argc); + } + + JSCallData *operator->() { + return this; + } + + 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; + ptr->function = Encode::undefined(); + ptr->context = Encode::undefined(); + ptr->accumulator = Encode::undefined(); + ptr->thisObject = thisObject->asReturnedValue(); + ptr->setArgc(argc); + if (argc) + memcpy(ptr->args, args, argc*sizeof(Value)); + if (f) + ptr->function = f->asReturnedValue(); + return ptr; + } + const Scope &scope; + int argc; + Value *args; + Value *thisObject; +}; + +inline +ReturnedValue FunctionObject::callAsConstructor(const JSCallData &data) const +{ + return d()->jsConstruct(this, data.args, data.argc); +} + +inline +ReturnedValue FunctionObject::call(const JSCallData &data) const +{ + return d()->jsCall(this, data.thisObject, data.args, data.argc); +} + + +struct ScopedStackFrame { + Scope &scope; + CppStackFrame frame; + + ScopedStackFrame(Scope &scope, Heap::ExecutionContext *context) + : scope(scope) + { + frame.parent = scope.engine->currentStackFrame; + if (!context) + return; + frame.jsFrame = reinterpret_cast<CallData *>(scope.alloc(sizeof(CallData)/sizeof(Value))); + frame.jsFrame->context = context; + frame.v4Function = frame.parent ? frame.parent->v4Function : 0; + scope.engine->currentStackFrame = &frame; + } + ~ScopedStackFrame() { + scope.engine->currentStackFrame = frame.parent; + } +}; + +} + +QT_END_NAMESPACE + +#endif // QV4JSCALL_H diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 0f021c8bd0..5e580b8b4d 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -46,6 +46,7 @@ #include <qv4runtime_p.h> #include <qv4variantobject_p.h> #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <qstack.h> #include <qstringlist.h> @@ -689,57 +690,57 @@ static QString quote(const QString &str) QString Stringify::Str(const QString &key, const Value &v) { Scope scope(v4); - scope.result = v; - ScopedObject o(scope, scope.result); + ScopedValue value(scope, v); + ScopedObject o(scope, value); if (o) { ScopedString s(scope, v4->newString(QStringLiteral("toJSON"))); ScopedFunctionObject toJSON(scope, o->get(s)); if (!!toJSON) { - ScopedCallData callData(scope, 1); - callData->thisObject = scope.result; - callData->args[0] = v4->newString(key); - toJSON->call(scope, callData); + JSCallData jsCallData(scope, 1); + *jsCallData->thisObject = value; + jsCallData->args[0] = v4->newString(key); + value = toJSON->call(jsCallData); } } if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine->id_empty(), scope.result); - ScopedCallData callData(scope, 2); - callData->args[0] = v4->newString(key); - callData->args[1] = scope.result; - callData->thisObject = holder; - replacerFunction->call(scope, callData); + holder->put(scope.engine->id_empty(), value); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = v4->newString(key); + jsCallData->args[1] = value; + *jsCallData->thisObject = holder; + value = replacerFunction->call(jsCallData); } - o = scope.result.asReturnedValue(); + o = value->asReturnedValue(); if (o) { if (NumberObject *n = o->as<NumberObject>()) - scope.result = Encode(n->value()); + value = Encode(n->value()); else if (StringObject *so = o->as<StringObject>()) - scope.result = so->d()->string; + value = so->d()->string; else if (BooleanObject *b = o->as<BooleanObject>()) - scope.result = Encode(b->value()); + value = Encode(b->value()); } - if (scope.result.isNull()) + if (value->isNull()) return QStringLiteral("null"); - if (scope.result.isBoolean()) - return scope.result.booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); - if (String *s = scope.result.stringValue()) - return quote(s->toQString()); - - if (scope.result.isNumber()) { - double d = scope.result.toNumber(); - return std::isfinite(d) ? scope.result.toQString() : QStringLiteral("null"); + if (value->isBoolean()) + return value->booleanValue() ? QStringLiteral("true") : QStringLiteral("false"); + if (value->isString()) + return quote(value->stringValue()->toQString()); + + if (value->isNumber()) { + double d = value->toNumber(); + return std::isfinite(d) ? value->toQString() : QStringLiteral("null"); } - if (const QV4::VariantObject *v = scope.result.as<QV4::VariantObject>()) { + if (const QV4::VariantObject *v = value->as<QV4::VariantObject>()) { return v->d()->data().toString(); } - o = scope.result.asReturnedValue(); + o = value->asReturnedValue(); if (o) { if (!o->as<FunctionObject>()) { if (o->as<ArrayObject>()) { @@ -883,25 +884,28 @@ void Heap::JsonObject::init() } -void JsonObject::method_parse(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue JsonObject::method_parse(const BuiltinFunction *b, CallData *callData) { - ScopedValue v(scope, callData->argument(0)); - QString jtext = v->toQString(); + ExecutionEngine *v4 = b->engine(); + QString jtext; + if (callData->argc() > 0) + jtext = callData->args[0].toQString(); DEBUG << "parsing source = " << jtext; - JsonParser parser(scope.engine, jtext.constData(), jtext.length()); + JsonParser parser(v4, jtext.constData(), jtext.length()); QJsonParseError error; - ScopedValue result(scope, parser.parse(&error)); + ReturnedValue result = parser.parse(&error); if (error.error != QJsonParseError::NoError) { DEBUG << "parse error" << error.errorString(); - RETURN_RESULT(scope.engine->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"))); + RETURN_RESULT(v4->throwSyntaxError(QStringLiteral("JSON.parse: Parse error"))); } - scope.result = result; + return result; } -void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue JsonObject::method_stringify(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); Stringify stringify(scope.engine); ScopedObject o(scope, callData->argument(1)); @@ -946,7 +950,7 @@ void JsonObject::method_stringify(const BuiltinFunction *, Scope &scope, CallDat QString result = stringify.Str(QString(), arg0); if (result.isEmpty() || scope.engine->hasException) RETURN_UNDEFINED(); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4jsonobject_p.h b/src/qml/jsruntime/qv4jsonobject_p.h index a73ce1c74e..19dba14aef 100644 --- a/src/qml/jsruntime/qv4jsonobject_p.h +++ b/src/qml/jsruntime/qv4jsonobject_p.h @@ -88,8 +88,8 @@ private: typedef QSet<ObjectItem> V4ObjectSet; public: - static void method_parse(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_stringify(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_parse(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_stringify(const BuiltinFunction *, CallData *callData); static ReturnedValue fromJsonValue(ExecutionEngine *engine, const QJsonValue &value); static ReturnedValue fromJsonObject(ExecutionEngine *engine, const QJsonObject &object); diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index afadf59bd5..ec3482c1e9 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -38,7 +38,7 @@ ****************************************************************************/ #include "qv4lookup_p.h" #include "qv4functionobject_p.h" -#include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include "qv4string_p.h" #include <private/qv4identifiertable_p.h> @@ -50,7 +50,7 @@ using namespace QV4; ReturnedValue Lookup::lookup(const Value &thisObject, Object *o, PropertyAttributes *attrs) { ExecutionEngine *engine = o->engine(); - Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); + Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); int i = 0; Heap::Object *obj = o->d(); while (i < Size && obj) { @@ -86,7 +86,7 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs { Heap::Object *obj = thisObject->d(); ExecutionEngine *engine = thisObject->engine(); - Identifier *name = engine->identifierTable->identifier(engine->current->compilationUnit->runtimeStrings[nameIndex]); + Identifier *name = engine->identifierTable->identifier(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); int i = 0; while (i < Size && obj) { classList[i] = obj->internalClass; @@ -117,140 +117,6 @@ ReturnedValue Lookup::lookup(const Object *thisObject, PropertyAttributes *attrs return Primitive::emptyValue().asReturnedValue(); } -ReturnedValue Lookup::indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - uint idx; - if (object.isObject() && index.asArrayIndex(idx)) { - l->indexedGetter = indexedGetterObjectInt; - return indexedGetterObjectInt(l, engine, object, index); - } - return indexedGetterFallback(l, engine, object, index); -} - -ReturnedValue Lookup::indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - Q_UNUSED(l); - Scope scope(engine); - uint idx = 0; - bool isInt = index.asArrayIndex(idx); - - ScopedObject o(scope, object); - if (!o) { - if (isInt) { - if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { - return Encode::undefined(); - } - const QString s = str->toQString().mid(idx, 1); - return scope.engine->newString(s)->asReturnedValue(); - } - } - - if (object.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot read property '%1' of %2").arg(index.toQStringNoThrow()).arg(object.toQStringNoThrow()); - return engine->throwTypeError(message); - } - - o = RuntimeHelpers::convertToObject(scope.engine, object); - if (!o) // type error - return Encode::undefined(); - } - - if (isInt) { - if (o->d()->arrayData && !o->d()->arrayData->attrs) { - ScopedValue v(scope, Scoped<ArrayData>(scope, o->arrayData())->get(idx)); - if (!v->isEmpty()) - return v->asReturnedValue(); - } - - return o->getIndexed(idx); - } - - ScopedString name(scope, index.toString(scope.engine)); - if (scope.hasException()) - return Encode::undefined(); - return o->get(name); - -} - - -ReturnedValue Lookup::indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index) -{ - uint idx; - if (index.asArrayIndex(idx)) { - if (Heap::Base *b = object.heapObject()) { - if (b->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>(); - if (idx < s->values.size) - if (!s->data(idx).isEmpty()) - return s->data(idx).asReturnedValue(); - } - } - } - } - - return indexedGetterFallback(l, engine, object, index); -} - -void Lookup::indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) -{ - if (Object *o = object.objectValue()) { - uint idx; - if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple && index.asArrayIndex(idx)) { - l->indexedSetter = indexedSetterObjectInt; - indexedSetterObjectInt(l, engine, object, index, v); - return; - } - } - indexedSetterFallback(l, engine, object, index, v); -} - -void Lookup::indexedSetterFallback(Lookup *, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) -{ - Scope scope(engine); - ScopedObject o(scope, object.toObject(scope.engine)); - if (scope.engine->hasException) - return; - - uint idx; - if (index.asArrayIndex(idx)) { - if (o->d()->arrayData && o->d()->arrayData->type == Heap::ArrayData::Simple) { - Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); - if (idx < s->values.size) { - s->setData(engine, idx, value); - return; - } - } - o->putIndexed(idx, value); - return; - } - - ScopedString name(scope, index.toString(scope.engine)); - o->put(name, value); -} - -void Lookup::indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v) -{ - uint idx; - if (index.asArrayIndex(idx)) { - if (Heap::Base *b = object.heapObject()) { - if (b->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>(); - if (idx < s->values.size) { - s->setData(engine, idx, v); - return; - } - } - } - } - } - indexedSetterFallback(l, engine, object, index, v); -} - ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object) { if (const Object *o = object.as<Object>()) @@ -268,7 +134,7 @@ ReturnedValue Lookup::getterGeneric(Lookup *l, ExecutionEngine *engine, const Va Q_ASSERT(object.isString()); proto = engine->stringPrototype(); Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); if (name->equals(engine->id_length())) { // special case, as the property is on the object itself l->getter = stringLengthGetter; @@ -369,7 +235,7 @@ ReturnedValue Lookup::getterFallback(Lookup *l, ExecutionEngine *engine, const V QV4::ScopedObject o(scope, object.toObject(scope.engine)); if (!o) return Encode::undefined(); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); return o->get(name); } @@ -534,10 +400,9 @@ ReturnedValue Lookup::getterAccessor0(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterFallback; @@ -557,10 +422,9 @@ ReturnedValue Lookup::getterAccessor1(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterFallback; @@ -583,10 +447,9 @@ ReturnedValue Lookup::getterAccessor2(Lookup *l, ExecutionEngine *engine, const if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } } @@ -639,10 +502,9 @@ ReturnedValue Lookup::primitiveGetterAccessor0(Lookup *l, ExecutionEngine *engin if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterGeneric; @@ -660,10 +522,9 @@ ReturnedValue Lookup::primitiveGetterAccessor1(Lookup *l, ExecutionEngine *engin if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = object; - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = object; + return getter->call(jsCallData); } } l->getter = getterGeneric; @@ -720,7 +581,7 @@ ReturnedValue Lookup::globalGetterGeneric(Lookup *l, ExecutionEngine *engine) } } Scope scope(engine); - ScopedString n(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString n(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); return engine->throwReferenceError(n); } @@ -780,10 +641,8 @@ ReturnedValue Lookup::globalGetterAccessor0(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -799,10 +658,8 @@ ReturnedValue Lookup::globalGetterAccessor1(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } l->globalGetter = globalGetterGeneric; return globalGetterGeneric(l, engine); @@ -821,10 +678,8 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) if (!getter) return Encode::undefined(); - ScopedCallData callData(scope, 0); - callData->thisObject = Primitive::undefinedValue(); - getter->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + return getter->call(jsCallData); } } } @@ -832,87 +687,88 @@ ReturnedValue Lookup::globalGetterAccessor2(Lookup *l, ExecutionEngine *engine) return globalGetterGeneric(l, engine); } -void Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Scope scope(engine); ScopedObject o(scope, object); if (!o) { o = RuntimeHelpers::convertToObject(scope.engine, object); if (!o) // type error - return; - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - o->put(name, value); - return; + return false; + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); + return o->put(name, value); } - o->setLookup(l, value); + return o->setLookup(l, value); } -void Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Lookup l1 = *l; if (Object *o = object.as<Object>()) { - o->setLookup(l, value); + if (!o->setLookup(l, value)) + return false; if (l->setter == Lookup::setter0 || l->setter == Lookup::setter0Inline) { l->setter = setter0setter0; l->classList[1] = l1.classList[0]; l->index2 = l1.index; - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { QV4::Scope scope(engine); QV4::ScopedObject o(scope, object.toObject(scope.engine)); - if (o) { - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - o->put(name, value); - } + if (!o) + return false; + + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); + return o->put(name, value); } -void Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { o->setProperty(engine, l->index, value); - return; + return true; } - setterTwoClasses(l, engine, object, value); + return setterTwoClasses(l, engine, object, value); } -void Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { o->d()->setInlineProperty(engine, l->index, value); - return; + return true; } - setterTwoClasses(l, engine, object, value); + return setterTwoClasses(l, engine, object, value); } -void Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { Q_ASSERT(!o->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { @@ -922,15 +778,15 @@ void Lookup::setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, co Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o && o->internalClass() == l->classList[0]) { @@ -943,32 +799,31 @@ void Lookup::setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, co Q_ASSERT(!p->prototype()); o->setInternalClass(l->classList[3]); o->setProperty(l->index, value); - return; + return true; } } } l->setter = setterFallback; - setterFallback(l, engine, object, value); + return setterFallback(l, engine, object, value); } -void Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) +bool Lookup::setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value) { Object *o = static_cast<Object *>(object.managed()); if (o) { if (o->internalClass() == l->classList[0]) { o->setProperty(l->index, value); - return; + return true; } if (o->internalClass() == l->classList[1]) { o->setProperty(l->index2, value); - return; + return true; } } l->setter = setterFallback; - setterFallback(l, engine, object, value); - + return setterFallback(l, engine, object, value); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index ce5189a780..826760aa2d 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -67,11 +67,9 @@ namespace QV4 { struct Lookup { enum { Size = 4 }; union { - ReturnedValue (*indexedGetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - void (*indexedSetter)(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); - void (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); + bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { InternalClass *classList[Size]; @@ -90,14 +88,6 @@ struct Lookup { uint index; uint nameIndex; - static ReturnedValue indexedGetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static ReturnedValue indexedGetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - static ReturnedValue indexedGetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index); - - static void indexedSetterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); - static void indexedSetterFallback(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &value); - static void indexedSetterObjectInt(Lookup *l, ExecutionEngine *engine, const Value &object, const Value &index, const Value &v); - static ReturnedValue getterGeneric(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterTwoClasses(Lookup *l, ExecutionEngine *engine, const Value &object); static ReturnedValue getterFallback(Lookup *l, ExecutionEngine *engine, const Value &object); @@ -133,15 +123,15 @@ struct Lookup { static ReturnedValue globalGetterAccessor1(Lookup *l, ExecutionEngine *engine); static ReturnedValue globalGetterAccessor2(Lookup *l, ExecutionEngine *engine); - static void setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); - static void setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterTwoClasses(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterFallback(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0Inline(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert1(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setterInsert2(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); + static bool setter0setter0(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value); ReturnedValue lookup(const Value &thisObject, Object *obj, PropertyAttributes *attrs); ReturnedValue lookup(const Object *obj, PropertyAttributes *attrs); @@ -151,7 +141,6 @@ struct Lookup { Q_STATIC_ASSERT(std::is_standard_layout<Lookup>::value); // Ensure that these offsets are always at this point to keep generated code compatible // across 32-bit and 64-bit (matters when cross-compiling). -Q_STATIC_ASSERT(offsetof(Lookup, indexedGetter) == 0); Q_STATIC_ASSERT(offsetof(Lookup, getter) == 0); } diff --git a/src/qml/jsruntime/qv4mathobject.cpp b/src/qml/jsruntime/qv4mathobject.cpp index 46230b5bc1..0c94c1ac43 100644 --- a/src/qml/jsruntime/qv4mathobject.cpp +++ b/src/qml/jsruntime/qv4mathobject.cpp @@ -94,9 +94,9 @@ static Q_ALWAYS_INLINE double copySign(double x, double y) return ::copysign(x, y); } -void MathObject::method_abs(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_abs(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) + if (!callData->argc()) RETURN_RESULT(Encode(qt_qnan())); if (callData->args[0].isInteger()) { @@ -111,37 +111,37 @@ void MathObject::method_abs(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(v < 0 ? -v : v)); } -void MathObject::method_acos(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_acos(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : 2; + double v = callData->argc() ? callData->args[0].toNumber() : 2; if (v > 1) RETURN_RESULT(Encode(qt_qnan())); RETURN_RESULT(Encode(std::acos(v))); } -void MathObject::method_asin(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_asin(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : 2; + double v = callData->argc() ? callData->args[0].toNumber() : 2; if (v > 1) RETURN_RESULT(Encode(qt_qnan())); else RETURN_RESULT(Encode(std::asin(v))); } -void MathObject::method_atan(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_atan(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (v == 0.0) RETURN_RESULT(Encode(v)); else RETURN_RESULT(Encode(std::atan(v))); } -void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_atan2(const BuiltinFunction *, CallData *callData) { - double v1 = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - double v2 = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); + double v1 = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + double v2 = callData->argc() > 1 ? callData->args[1].toNumber() : qt_qnan(); if ((v1 < 0) && qt_is_finite(v1) && qt_is_inf(v2) && (copySign(1.0, v2) == 1.0)) RETURN_RESULT(Encode(copySign(0, -1.0))); @@ -156,24 +156,24 @@ void MathObject::method_atan2(const BuiltinFunction *, Scope &scope, CallData *c RETURN_RESULT(Encode(std::atan2(v1, v2))); } -void MathObject::method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_ceil(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (v < 0.0 && v > -1.0) RETURN_RESULT(Encode(copySign(0, -1.0))); else RETURN_RESULT(Encode(std::ceil(v))); } -void MathObject::method_cos(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_cos(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::cos(v))); } -void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_exp(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (qt_is_inf(v)) { if (copySign(1.0, v) == -1.0) RETURN_RESULT(Encode(0)); @@ -184,25 +184,27 @@ void MathObject::method_exp(const BuiltinFunction *, Scope &scope, CallData *cal } } -void MathObject::method_floor(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_floor(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); - RETURN_RESULT(Encode(std::floor(v))); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); + Value result = Primitive::fromDouble(std::floor(v)); + result.isInt32(); + RETURN_RESULT(result); } -void MathObject::method_log(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_log(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (v < 0) RETURN_RESULT(Encode(qt_qnan())); else RETURN_RESULT(Encode(std::log(v))); } -void MathObject::method_max(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_max(const BuiltinFunction *, CallData *callData) { double mx = -qt_inf(); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = callData->argc(); i < ei; ++i) { double x = callData->args[i].toNumber(); if (x > mx || std::isnan(x)) mx = x; @@ -210,10 +212,10 @@ void MathObject::method_max(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(mx)); } -void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_min(const BuiltinFunction *, CallData *callData) { double mx = qt_inf(); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = callData->argc(); i < ei; ++i) { double x = callData->args[i].toNumber(); if ((x == 0 && mx == x && copySign(1.0, x) == -1.0) || (x < mx) || std::isnan(x)) { @@ -223,10 +225,10 @@ void MathObject::method_min(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(mx)); } -void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_pow(const BuiltinFunction *, CallData *callData) { - double x = callData->argc > 0 ? callData->args[0].toNumber() : qt_qnan(); - double y = callData->argc > 1 ? callData->args[1].toNumber() : qt_qnan(); + double x = callData->argc() > 0 ? callData->args[0].toNumber() : qt_qnan(); + double y = callData->argc() > 1 ? callData->args[1].toNumber() : qt_qnan(); if (std::isnan(y)) RETURN_RESULT(Encode(qt_qnan())); @@ -273,21 +275,21 @@ void MathObject::method_pow(const BuiltinFunction *, Scope &scope, CallData *cal RETURN_RESULT(Encode(qt_qnan())); } -void MathObject::method_random(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue MathObject::method_random(const BuiltinFunction *, CallData *) { RETURN_RESULT(Encode(QRandomGenerator::getReal())); } -void MathObject::method_round(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_round(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); v = copySign(std::floor(v + 0.5), v); RETURN_RESULT(Encode(v)); } -void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sign(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (std::isnan(v)) RETURN_RESULT(Encode(qt_qnan())); @@ -298,21 +300,21 @@ void MathObject::method_sign(const BuiltinFunction *, Scope &scope, CallData *ca RETURN_RESULT(Encode(std::signbit(v) ? -1 : 1)); } -void MathObject::method_sin(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sin(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::sin(v))); } -void MathObject::method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_sqrt(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); RETURN_RESULT(Encode(std::sqrt(v))); } -void MathObject::method_tan(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue MathObject::method_tan(const BuiltinFunction *, CallData *callData) { - double v = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + double v = callData->argc() ? callData->args[0].toNumber() : qt_qnan(); if (v == 0.0) RETURN_RESULT(Encode(v)); else diff --git a/src/qml/jsruntime/qv4mathobject_p.h b/src/qml/jsruntime/qv4mathobject_p.h index e617712905..016f0c16ca 100644 --- a/src/qml/jsruntime/qv4mathobject_p.h +++ b/src/qml/jsruntime/qv4mathobject_p.h @@ -69,25 +69,25 @@ struct MathObject: Object V4_OBJECT2(MathObject, Object) Q_MANAGED_TYPE(MathObject) - static void method_abs(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_acos(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_asin(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_atan(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_atan2(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_ceil(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_cos(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_exp(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_floor(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_log(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_max(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_min(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_pow(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_random(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_round(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sign(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sin(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_sqrt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_tan(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_abs(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_acos(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_asin(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_atan(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_atan2(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_ceil(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_cos(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_exp(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_floor(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_log(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_max(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_min(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_pow(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_random(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_round(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_sign(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_sin(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_sqrt(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_tan(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index d5c80dbf80..ca5340c37d 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -78,16 +78,16 @@ void Heap::NumberCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Number")); } -void NumberCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue NumberCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - scope.result = Encode(scope.engine->newNumberObject(dbl)); + double dbl = argc ? argv[0].toNumber() : 0.; + return Encode(f->engine()->newNumberObject(dbl)); } -void NumberCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue NumberCtor::call(const FunctionObject *, const Value *, const Value *argv, int argc) { - double dbl = callData->argc ? callData->args[0].toNumber() : 0.; - scope.result = Encode(dbl); + double dbl = argc ? argv[0].toNumber() : 0.; + return Encode(dbl); } void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -124,119 +124,101 @@ QT_WARNING_POP defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1); } -inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) +inline ReturnedValue thisNumberValue(ExecutionEngine *v4, CallData *callData) { if (callData->thisObject.isNumber()) return callData->thisObject.asReturnedValue(); NumberObject *n = callData->thisObject.as<NumberObject>(); if (!n) { - scope.engine->throwTypeError(); + v4->throwTypeError(); return Encode::undefined(); } return Encode(n->value()); } -inline double thisNumber(Scope &scope, CallData *callData) +inline double thisNumber(ExecutionEngine *engine, CallData *callData) { if (callData->thisObject.isNumber()) return callData->thisObject.asDouble(); NumberObject *n = callData->thisObject.as<NumberObject>(); if (!n) { - scope.engine->throwTypeError(); + engine->throwTypeError(); return 0; } return n->value(); } -void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isFinite(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!callData->argc()) + return Encode(false); double v = callData->args[0].toNumber(); - scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); + return Encode(!std::isnan(v) && !qt_is_inf(v)); } -void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isInteger(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!callData->argc()) + return Encode(false); const Value &v = callData->args[0]; - if (!v.isNumber()) { - scope.result = Encode(false); - return; - } + if (!v.isNumber()) + return Encode(false); double dv = v.toNumber(); - if (std::isnan(dv) || qt_is_inf(dv)) { - scope.result = Encode(false); - return; - } + if (std::isnan(dv) || qt_is_inf(dv)) + return Encode(false); double iv = v.toInteger(); - scope.result = Encode(dv == iv); + return Encode(dv == iv); } -void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isSafeInteger(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!callData->argc()) + return Encode(false); const Value &v = callData->args[0]; - if (!v.isNumber()) { - scope.result = Encode(false); - return; - } + if (!v.isNumber()) + return Encode(false); double dv = v.toNumber(); - if (std::isnan(dv) || qt_is_inf(dv)) { - scope.result = Encode(false); - return; - } + if (std::isnan(dv) || qt_is_inf(dv)) + return Encode(false); double iv = v.toInteger(); - scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1); + return Encode(dv == iv && std::fabs(iv) <= (2^53)-1); } -void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_isNaN(const BuiltinFunction *, CallData *callData) { - if (!callData->argc) { - scope.result = Encode(false); - return; - } + if (!callData->argc()) + return Encode(false); double v = callData->args[0].toNumber(); // cast to bool explicitly as std::isnan() may give us ::isnan(), which // sometimes returns an int and we don't want the Encode(int) overload. - scope.result = Encode(bool(std::isnan(v))); + return Encode(bool(std::isnan(v))); } -void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - double num = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double num = thisNumber(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); - if (callData->argc && !callData->args[0].isUndefined()) { + if (callData->argc() && !callData->args[0].isUndefined()) { int radix = callData->args[0].toInt32(); if (radix < 2 || radix > 36) { - scope.result = scope.engine->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix") - .arg(radix)); - return; + return v4->throwError(QStringLiteral("Number.prototype.toString: %0 is not a valid radix").arg(radix)); } if (std::isnan(num)) { - scope.result = scope.engine->newString(QStringLiteral("NaN")); - return; + return Encode(v4->newString(QStringLiteral("NaN"))); } else if (qt_is_inf(num)) { - scope.result = scope.engine->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity")); - return; + return Encode(v4->newString(QLatin1String(num < 0 ? "-Infinity" : "Infinity"))); } if (radix != 10) { @@ -266,43 +248,42 @@ void NumberPrototype::method_toString(const BuiltinFunction *, Scope &scope, Cal } if (negative) str.prepend(QLatin1Char('-')); - scope.result = scope.engine->newString(str); - return; + return Encode(v4->newString(str)); } } - scope.result = Primitive::fromDouble(num).toString(scope.engine); + return Encode(Primitive::fromDouble(num).toString(v4)); } -void NumberPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toLocaleString(const BuiltinFunction *b, CallData *callData) { - ScopedValue v(scope, thisNumberValue(scope, callData)); - scope.result = v->toString(scope.engine); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedValue v(scope, thisNumberValue(b->engine(), callData)); + return Encode(v->toString(scope.engine)); } -void NumberPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - scope.result = thisNumberValue(scope, callData); + return thisNumberValue(b->engine(), callData); } -void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toFixed(const BuiltinFunction *b, CallData *callData) { - double v = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double v = thisNumber(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); double fdigits = 0; - if (callData->argc > 0) + if (callData->argc() > 0) fdigits = callData->args[0].toInteger(); if (std::isnan(fdigits)) fdigits = 0; - if (fdigits < 0 || fdigits > 20) { - scope.result = scope.engine->throwRangeError(callData->thisObject); - return; - } + if (fdigits < 0 || fdigits > 20) + return v4->throwRangeError(callData->thisObject); QString str; if (std::isnan(v)) @@ -312,49 +293,51 @@ void NumberPrototype::method_toFixed(const BuiltinFunction *, Scope &scope, Call else if (v < 1.e21) str = NumberLocale::instance()->toString(v, 'f', int(fdigits)); else { - scope.result = RuntimeHelpers::stringFromNumber(scope.engine, v); - return; + return Encode(RuntimeHelpers::stringFromNumber(v4, v)); } - scope.result = scope.engine->newString(str); + return Encode(v4->newString(str)); } -void NumberPrototype::method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toExponential(const BuiltinFunction *b, CallData *callData) { - double d = thisNumber(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + double d = thisNumber(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); + int fdigits = NumberLocale::instance()->defaultDoublePrecision; - if (callData->argc && !callData->args[0].isUndefined()) { + if (callData->argc() && !callData->args[0].isUndefined()) { fdigits = callData->args[0].toInt32(); if (fdigits < 0 || fdigits > 20) { - ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"))); - scope.result = scope.engine->throwRangeError(error); - return; + Scope scope(v4); + ScopedString error(scope, v4->newString(QStringLiteral("Number.prototype.toExponential: fractionDigits out of range"))); + return v4->throwRangeError(error); } } QString result = NumberLocale::instance()->toString(d, 'e', fdigits); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void NumberPrototype::method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue NumberPrototype::method_toPrecision(const BuiltinFunction *b, CallData *callData) { - ScopedValue v(scope, thisNumberValue(scope, callData)); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedValue v(scope, thisNumberValue(scope.engine, callData)); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - if (!callData->argc || callData->args[0].isUndefined()) { - scope.result = v->toString(scope.engine); - return; - } + + if (!callData->argc() || callData->args[0].isUndefined()) + return Encode(v->toString(scope.engine)); int precision = callData->args[0].toInt32(); if (precision < 1 || precision > 21) { ScopedString error(scope, scope.engine->newString(QStringLiteral("Number.prototype.toPrecision: precision out of range"))); - scope.result = scope.engine->throwRangeError(error); - return; + return scope.engine->throwRangeError(error); } QString result = NumberLocale::instance()->toString(v->asDouble(), 'g', precision); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 0bc2cd8c65..e4ff87c93a 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -79,8 +79,8 @@ struct NumberCtor: FunctionObject { V4_OBJECT2(NumberCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct NumberPrototype: NumberObject @@ -88,16 +88,16 @@ struct NumberPrototype: NumberObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toFixed(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toExponential(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toPrecision(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_isFinite(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isInteger(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isSafeInteger(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isNaN(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toFixed(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toExponential(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toPrecision(const BuiltinFunction *, CallData *callData); }; diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 94d5a74fe5..1859466348 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -38,8 +38,6 @@ ****************************************************************************/ #include "qv4object_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4stringobject_p.h" #include "qv4argumentsobject_p.h" @@ -51,6 +49,7 @@ #include "qv4identifier_p.h" #include "qv4string_p.h" #include "qv4identifiertable_p.h" +#include "qv4jscall_p.h" #include <stdint.h> @@ -108,10 +107,9 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property return Encode::undefined(); Scope scope(f->engine()); - ScopedCallData callData(scope); - callData->thisObject = thisObject; - f->call(scope, callData); - return scope.result.asReturnedValue(); + JSCallData jsCallData(scope); + *jsCallData->thisObject = thisObject; + return f->call(jsCallData); } bool Object::putValue(uint memberIndex, const Value &value) @@ -127,25 +125,20 @@ bool Object::putValue(uint memberIndex, const Value &value) if (set) { Scope scope(ic->engine); ScopedFunctionObject setter(scope, set); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); return !ic->engine->hasException; } - goto reject; + return false; } if (!attrs.isWritable()) - goto reject; + return false; setProperty(memberIndex, value); return true; - - reject: - if (engine()->current->strictMode) - engine()->throwTypeError(); - return false; } void Object::defineDefaultProperty(const QString &name, const Value &value) @@ -156,7 +149,7 @@ void Object::defineDefaultProperty(const QString &name, const Value &value) defineDefaultProperty(s, value); } -void Object::defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) +void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); @@ -167,7 +160,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built defineDefaultProperty(s, function); } -void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount) +void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount) { ExecutionEngine *e = engine(); Scope scope(e); @@ -177,8 +170,8 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct defineDefaultProperty(name, function); } -void Object::defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)) +void Object::defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)) { ExecutionEngine *e = engine(); Scope scope(e); @@ -186,8 +179,8 @@ void Object::defineAccessorProperty(const QString &name, void (*getter)(const Bu defineAccessorProperty(s, getter, setter); } -void Object::defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)) +void Object::defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)) { ExecutionEngine *v4 = engine(); QV4::Scope scope(v4); @@ -402,14 +395,14 @@ bool Object::hasOwnProperty(uint index) const return false; } -void Object::construct(const Managed *m, Scope &scope, CallData *) +ReturnedValue Object::callAsConstructor(const FunctionObject *f, const Value *, int) { - scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } -void Object::call(const Managed *m, Scope &scope, CallData *) +ReturnedValue Object::call(const FunctionObject *f, const Value *, const Value *, int) { - scope.result = static_cast<const Object *>(m)->engine()->throwTypeError(); + return f->engine()->throwTypeError(); } ReturnedValue Object::get(const Managed *m, String *name, bool *hasProperty) @@ -512,11 +505,11 @@ ReturnedValue Object::getLookup(const Managed *m, Lookup *l) return Encode::undefined(); } -void Object::setLookup(Managed *m, Lookup *l, const Value &value) +bool Object::setLookup(Managed *m, Lookup *l, const Value &value) { Scope scope(static_cast<Object *>(m)->engine()); ScopedObject o(scope, static_cast<Object *>(m)); - ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); InternalClass *c = o->internalClass(); uint idx = c->find(name); @@ -526,42 +519,44 @@ void Object::setLookup(Managed *m, Lookup *l, const Value &value) l->index = idx; l->setter = idx < o->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0; o->setProperty(idx, value); - return; + return true; } - if (idx != UINT_MAX) { - o->putValue(idx, value); - return; - } + if (idx != UINT_MAX) + return o->putValue(idx, value); } - o->put(name, value); + if (!o->put(name, value)) { + l->setter = Lookup::setterFallback; + return false; + } if (o->internalClass() == c) - return; + return true; idx = o->internalClass()->find(name); - if (idx == UINT_MAX) - return; + if (idx == UINT_MAX) // ### can this even happen? + return false; l->classList[0] = c; l->classList[3] = o->internalClass(); l->index = idx; if (!o->prototype()) { l->setter = Lookup::setterInsert0; - return; + return true; } o = o->prototype(); l->classList[1] = o->internalClass(); if (!o->prototype()) { l->setter = Lookup::setterInsert1; - return; + return true; } o = o->prototype(); l->classList[2] = o->internalClass(); if (!o->prototype()) { l->setter = Lookup::setterInsert2; - return; + return true; } l->setter = Lookup::setterGeneric; + return true; } void Object::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *pd, PropertyAttributes *attrs) @@ -725,9 +720,9 @@ bool Object::internalPut(String *name, const Value &value) if (attrs.isAccessor()) { if (memberIndex->as<FunctionObject>()) goto cont; - goto reject; + return false; } else if (!attrs.isWritable()) - goto reject; + return false; else if (isArrayObject() && name->equals(engine->id_length())) { bool ok; uint l = value.asArrayLength(&ok); @@ -737,14 +732,14 @@ bool Object::internalPut(String *name, const Value &value) } ok = setArrayLength(l); if (!ok) - goto reject; + return false; } else { memberIndex.set(engine, value); } return true; } else if (!prototype()) { if (!isExtensible()) - goto reject; + return false; } else { // clause 4 Scope scope(engine); @@ -752,12 +747,12 @@ bool Object::internalPut(String *name, const Value &value) if (!memberIndex.isNull()) { if (attrs.isAccessor()) { if (!memberIndex->as<FunctionObject>()) - goto reject; + return false; } else if (!isExtensible() || !attrs.isWritable()) { - goto reject; + return false; } } else if (!isExtensible()) { - goto reject; + return false; } } @@ -769,24 +764,15 @@ bool Object::internalPut(String *name, const Value &value) Scope scope(engine); ScopedFunctionObject setter(scope, *memberIndex); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); - return !internalClass()->engine->hasException; + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); + return !engine->hasException; } insertMember(name, value); return true; - - reject: - // ### this should be removed once everything is ported to use Object::set() - if (engine->current->strictMode) { - QString message = QLatin1String("Cannot assign to read-only property \"") + - name->toQString() + QLatin1Char('\"'); - engine->throwTypeError(message); - } - return false; } bool Object::internalPutIndexed(uint index, const Value &value) @@ -802,7 +788,7 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (arrayIndex.isNull() && isStringObject()) { if (index < static_cast<StringObject *>(this)->length()) // not writable - goto reject; + return false; } // clause 1 @@ -810,15 +796,15 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (attrs.isAccessor()) { if (arrayIndex->as<FunctionObject>()) goto cont; - goto reject; + return false; } else if (!attrs.isWritable()) - goto reject; + return false; arrayIndex.set(engine, value); return true; } else if (!prototype()) { if (!isExtensible()) - goto reject; + return false; } else { // clause 4 Scope scope(engine); @@ -826,12 +812,12 @@ bool Object::internalPutIndexed(uint index, const Value &value) if (!arrayIndex.isNull()) { if (attrs.isAccessor()) { if (!arrayIndex->as<FunctionObject>()) - goto reject; + return false; } else if (!isExtensible() || !attrs.isWritable()) { - goto reject; + return false; } } else if (!isExtensible()) { - goto reject; + return false; } } @@ -843,21 +829,15 @@ bool Object::internalPutIndexed(uint index, const Value &value) Scope scope(engine); ScopedFunctionObject setter(scope, *arrayIndex); - ScopedCallData callData(scope, 1); - callData->args[0] = value; - callData->thisObject = this; - setter->call(scope, callData); - return !internalClass()->engine->hasException; + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = value; + *jsCallData->thisObject = this; + setter->call(jsCallData); + return !engine->hasException; } arraySet(index, value); return true; - - reject: - // ### this should be removed once everything is ported to use Object::setIndexed() - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } // Section 8.12.7 @@ -878,8 +858,6 @@ bool Object::internalDeleteProperty(String *name) InternalClass::removeMember(this, name->identifier()); return true; } - if (engine()->current->strictMode) - engine()->throwTypeError(); return false; } @@ -896,8 +874,6 @@ bool Object::internalDeleteIndexedProperty(uint index) if (!ad || ad->vtable()->del(this, index)) return true; - if (engine()->current->strictMode) - engine()->throwTypeError(); return false; } @@ -921,7 +897,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const if (attrs.isEmpty() || p->isSubset(attrs, lp, cattrs)) return true; if (!cattrs.isWritable() || attrs.type() == PropertyAttributes::Accessor || attrs.isConfigurable() || attrs.isEnumerable()) - goto reject; + return false; bool succeeded = true; if (attrs.type() == PropertyAttributes::Data) { bool ok; @@ -938,7 +914,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const InternalClass::changeMember(this, engine->id_length(), cattrs); } if (!succeeded) - goto reject; + return false; return true; } @@ -948,7 +924,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const if (memberIndex == UINT_MAX) { // clause 3 if (!isExtensible()) - goto reject; + return false; // clause 4 ScopedProperty pd(scope); pd->copy(p, attrs); @@ -958,26 +934,18 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, String *name, const } return __defineOwnProperty__(engine, memberIndex, name, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) { // 15.4.5.1, 4b if (isArrayObject() && index >= getLength() && !internalClass()->propertyData[Heap::ArrayObject::LengthPropertyIndex].isWritable()) - goto reject; + return false; if (ArgumentsObject::isNonStrictArgumentsObject(this)) return static_cast<ArgumentsObject *>(this)->defineOwnProperty(engine, index, p, attrs); return defineOwnProperty2(engine, index, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Property *p, PropertyAttributes attrs) @@ -994,7 +962,7 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope if (!hasProperty) { // clause 3 if (!isExtensible()) - goto reject; + return false; // clause 4 Scope scope(engine); ScopedProperty pp(scope); @@ -1010,10 +978,6 @@ bool Object::defineOwnProperty2(ExecutionEngine *engine, uint index, const Prope } return __defineOwnProperty__(engine, index, 0, p, attrs); -reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String *member, const Property *p, PropertyAttributes attrs) @@ -1040,9 +1004,9 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * // clause 7 if (!cattrs.isConfigurable()) { if (attrs.isConfigurable()) - goto reject; + return false; if (attrs.hasEnumerable() && attrs.isEnumerable() != cattrs.isEnumerable()) - goto reject; + return false; } // clause 8 @@ -1053,7 +1017,7 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * if (cattrs.isData() != attrs.isData()) { // 9a if (!cattrs.isConfigurable()) - goto reject; + return false; if (cattrs.isData()) { // 9b cattrs.setType(PropertyAttributes::Accessor); @@ -1079,15 +1043,15 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * } else if (cattrs.isData() && attrs.isData()) { // clause 10 if (!cattrs.isConfigurable() && !cattrs.isWritable()) { if (attrs.isWritable() || !current->value.sameValue(p->value)) - goto reject; + return false; } } else { // clause 10 Q_ASSERT(cattrs.isAccessor() && attrs.isAccessor()); if (!cattrs.isConfigurable()) { if (!p->value.isEmpty() && current->value.rawValue() != p->value.rawValue()) - goto reject; + return false; if (!p->set.isEmpty() && current->set.rawValue() != p->set.rawValue()) - goto reject; + return false; } } @@ -1102,10 +1066,6 @@ bool Object::__defineOwnProperty__(ExecutionEngine *engine, uint index, String * arrayData()->setProperty(scope.engine, index, current); } return true; - reject: - if (engine->current->strictMode) - engine->throwTypeError(); - return false; } @@ -1260,7 +1220,7 @@ void Heap::ArrayObject::init(const QStringList &list) ReturnedValue ArrayObject::getLookup(const Managed *m, Lookup *l) { Scope scope(static_cast<const Object *>(m)->engine()); - ScopedString name(scope, scope.engine->current->compilationUnit->runtimeStrings[l->nameIndex]); + ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]); if (name->equals(scope.engine->id_length())) { // special case, as the property is on the object itself l->getter = Lookup::arrayLengthGetter; diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 066a93cc61..af68f09700 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -57,6 +57,7 @@ #include "qv4scopedvalue_p.h" #include "qv4value_p.h" #include "qv4internalclass_p.h" +#include "qv4string_p.h" QT_BEGIN_NAMESPACE @@ -174,8 +175,8 @@ Q_STATIC_ASSERT(Object::markTable == ((2 << 2) | (2 << 4))); struct ObjectVTable { VTable vTable; - void (*call)(const Managed *, Scope &scope, CallData *data); - void (*construct)(const Managed *, Scope &scope, CallData *data); + 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 (*getIndexed)(const Managed *, uint index, bool *hasProperty); bool (*put)(Managed *, String *name, const Value &value); @@ -185,7 +186,7 @@ struct ObjectVTable bool (*deleteProperty)(Managed *m, String *name); bool (*deleteIndexedProperty)(Managed *m, uint index); ReturnedValue (*getLookup)(const Managed *m, Lookup *l); - void (*setLookup)(Managed *m, Lookup *l, const Value &v); + bool (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*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); @@ -196,7 +197,7 @@ const QV4::ObjectVTable classname::static_vtbl = \ { \ DEFINE_MANAGED_VTABLE_INT(classname, (std::is_same<classname::SuperClass, Object>::value) ? nullptr : &classname::SuperClass::static_vtbl.vTable), \ call, \ - construct, \ + callAsConstructor, \ get, \ getIndexed, \ put, \ @@ -287,12 +288,12 @@ struct Q_QML_EXPORT Object: Managed { insertMember(name, value, Attr_Data|Attr_NotEnumerable); } void defineDefaultProperty(const QString &name, const Value &value); - void defineDefaultProperty(const QString &name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineDefaultProperty(String *name, void (*code)(const BuiltinFunction *, Scope &, CallData *), int argumentCount = 0); - void defineAccessorProperty(const QString &name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)); - void defineAccessorProperty(String *name, void (*getter)(const BuiltinFunction *, Scope &, CallData *), - void (*setter)(const BuiltinFunction *, Scope &, CallData *)); + void defineDefaultProperty(const QString &name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); + void defineDefaultProperty(String *name, ReturnedValue (*code)(const BuiltinFunction *, CallData *), int argumentCount = 0); + void defineAccessorProperty(const QString &name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)); + void defineAccessorProperty(String *name, ReturnedValue (*getter)(const BuiltinFunction *, CallData *), + ReturnedValue (*setter)(const BuiltinFunction *, CallData *)); /* Fixed: Writable: false, Enumerable: false, Configurable: false */ void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); @@ -408,19 +409,6 @@ public: return ret; } - inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) - { - bool ret = vtable()->putIndexed(this, idx, v); - if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { - ExecutionEngine *e = engine(); - if (!e->hasException) { // allow a custom set impl to throw itself - e->throwTypeError(); - } - } - return ret; - } - - PropertyAttributes query(String *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const @@ -431,21 +419,17 @@ public: { return vtable()->deleteIndexedProperty(this, index); } ReturnedValue getLookup(Lookup *l) const { return vtable()->getLookup(this, l); } - void setLookup(Lookup *l, const Value &v) - { vtable()->setLookup(this, l, v); } + bool setLookup(Lookup *l, const Value &v) + { return vtable()->setLookup(this, l, v); } 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); } ReturnedValue instanceOf(const Value &var) const { return vtable()->instanceOf(this, var); } - inline void construct(Scope &scope, CallData *d) const - { return vtable()->construct(this, scope, d); } - inline void call(Scope &scope, CallData *d) const - { vtable()->call(this, scope, d); } protected: - static void construct(const Managed *m, Scope &scope, CallData *); - static void call(const Managed *m, Scope &scope, CallData *); + 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 getIndexed(const Managed *m, uint index, bool *hasProperty); static bool put(Managed *m, String *name, const Value &value); @@ -455,7 +439,7 @@ protected: static bool deleteProperty(Managed *m, String *name); static bool deleteIndexedProperty(Managed *m, uint index); static ReturnedValue getLookup(const Managed *m, Lookup *l); - static void setLookup(Managed *m, Lookup *l, const Value &v); + static bool setLookup(Managed *m, Lookup *l, const Value &v); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); static ReturnedValue instanceOf(const Object *typeObject, const Value &var); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 2e72c0f13f..07573e61f3 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -46,6 +46,7 @@ #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDateTime> #include <QtCore/QStringList> @@ -60,27 +61,29 @@ void Heap::ObjectCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("Object")); } -void ObjectCtor::construct(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue ObjectCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - const ObjectCtor *ctor = static_cast<const ObjectCtor *>(that); - if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { + ExecutionEngine *v4 = f->engine(); + const ObjectCtor *ctor = static_cast<const ObjectCtor *>(f); + if (!argc || argv[0].isUndefined() || argv[0].isNull()) { + Scope scope(v4); ScopedObject obj(scope, scope.engine->newObject()); ScopedObject proto(scope, ctor->get(scope.engine->id_prototype())); if (!!proto) obj->setPrototype(proto); - scope.result = obj.asReturnedValue(); + return obj.asReturnedValue(); } else { - scope.result = callData->args[0].toObject(scope.engine); + return argv[0].toObject(v4)->asReturnedValue(); } } -void ObjectCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue ObjectCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (!callData->argc || callData->args[0].isUndefined() || callData->args[0].isNull()) { - scope.result = v4->newObject()->asReturnedValue(); + ExecutionEngine *v4 = m->engine(); + if (!argc || argv[0].isUndefined() || argv[0].isNull()) { + return v4->newObject()->asReturnedValue(); } else { - scope.result = callData->args[0].toObject(v4); + return argv[0].toObject(v4)->asReturnedValue(); } } @@ -123,58 +126,78 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) insertMember(v4->id___proto__(), p, Attr_Accessor|Attr_NotEnumerable); } -void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); + if (callData->argc() < 1) + return scope.engine->throwTypeError(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject p(scope, o->prototype()); - scope.result = !!p ? p->asReturnedValue() : Encode::null(); + return (!!p ? p->asReturnedValue() : Encode::null()); } -void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); + if (callData->argc() < 1) + return scope.engine->throwTypeError(); + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); ScopedValue v(scope, callData->argument(1)); ScopedString name(scope, v->toString(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); PropertyAttributes attrs; ScopedProperty desc(scope); O->getOwnProperty(name, &attrs, desc); - scope.result = fromPropertyDescriptor(scope.engine, desc, attrs); + return fromPropertyDescriptor(scope.engine, desc, attrs); } -void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); + if (callData->argc() < 1) + return scope.engine->throwTypeError(); + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); + return Encode(getOwnPropertyNames(scope.engine, callData->args[0])); } // 19.1.2.1 -void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_assign(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); + if (callData->argc() < 1) + return scope.engine->throwTypeError(); + ScopedObject to(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - if (callData->argc == 1) { - scope.result = to; - return; - } + if (callData->argc() == 1) + return to.asReturnedValue(); - for (int i = 1; i < callData->argc; ++i) { + for (int i = 1, ei = callData->argc(); i < ei; ++i) { if (callData->args[i].isUndefined() || callData->args[i].isNull()) continue; ScopedObject from(scope, callData->args[i].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); quint32 length = keys->getLength(); @@ -195,64 +218,66 @@ void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallD propValue = from->get(nextKey); to->set(nextKey, propValue, Object::DoThrowOnRejection); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); } } - scope.result = to; + return to.asReturnedValue(); } -void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_create(const BuiltinFunction *builtin, CallData *callData) { + Scope scope(builtin); ScopedValue O(scope, callData->argument(0)); - if (!O->isObject() && !O->isNull()) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (!O->isObject() && !O->isNull()) + return scope.engine->throwTypeError(); ScopedObject newObject(scope, scope.engine->newObject()); newObject->setPrototype(O->as<Object>()); - if (callData->argc > 1 && !callData->args[1].isUndefined()) { + if (callData->argc() > 1 && !callData->args[1].isUndefined()) { callData->args[0] = newObject; - method_defineProperties(builtin, scope, callData); - return; + return method_defineProperties(builtin, callData); } - scope.result = newObject; + return newObject.asReturnedValue(); } -void ObjectPrototype::method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineProperty(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (!O) + return scope.engine->throwTypeError(); ScopedString name(scope, callData->argument(1), ScopedString::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedValue attributes(scope, callData->argument(2)); ScopedProperty pd(scope); PropertyAttributes attrs; toPropertyDescriptor(scope.engine, attributes, pd, &attrs); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); if (!O->__defineOwnProperty__(scope.engine, name, pd, attrs)) THROW_TYPE_ERROR(); - scope.result = O; + return O.asReturnedValue(); } -void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineProperties(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject O(scope, callData->argument(0)); if (!O) THROW_TYPE_ERROR(); ScopedObject o(scope, callData->argument(1), ScopedObject::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedValue val(scope); @@ -269,7 +294,8 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc PropertyAttributes nattrs; val = o->getValue(pd->value, attrs); toPropertyDescriptor(scope.engine, val, n, &nattrs); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); bool ok; if (name) ok = O->__defineOwnProperty__(scope.engine, name, n, nattrs); @@ -279,17 +305,16 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc THROW_TYPE_ERROR(); } - scope.result = O; + return O.asReturnedValue(); } -void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_seal(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->argument(0)); - if (!o) { + if (!o) // 19.1.2.17, 1 - scope.result = callData->argument(0); - return; - } + return callData->argument(0); o->setInternalClass(o->internalClass()->sealed()); @@ -301,17 +326,16 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat } } - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_freeze(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->argument(0)); - if (!o) { + if (!o) // 19.1.2.5, 1 - scope.result = callData->argument(0); - return; - } + return callData->argument(0); if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -327,116 +351,94 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD o->arrayData()->attrs[i].setWritable(false); } } - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_preventExtensions(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = callData->argument(0); - return; - } + if (!o) + return callData->argument(0); o->setInternalClass(o->internalClass()->nonExtensible()); - scope.result = o; + return o.asReturnedValue(); } -void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isSealed(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(true); - return; - } + if (!o) + return Encode(true); - if (o->isExtensible()) { - scope.result = Encode(false); - return; - } + if (o->isExtensible()) + return Encode(false); - if (o->internalClass() != o->internalClass()->sealed()) { - scope.result = Encode(false); - return; - } + if (o->internalClass() != o->internalClass()->sealed()) + return Encode(false); - if (!o->arrayData() || !o->arrayData()->length()) { - scope.result = Encode(true); - return; - } + if (!o->arrayData() || !o->arrayData()->length()) + return Encode(true); Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) { - scope.result = Encode(false); - return; - } + if (!o->arrayData()->attrs) + return Encode(false); for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable()) { - scope.result = Encode(false); - return; - } + if (o->arrayData()->attributes(i).isConfigurable()) + return Encode(false); } - scope.result = Encode(true); + return Encode(true); } -void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isFrozen(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(true); - return; - } + if (!o) + return Encode(true); - if (o->isExtensible()) { - scope.result = Encode(false); - return; - } + if (o->isExtensible()) + return Encode(false); - if (o->internalClass() != o->internalClass()->frozen()) { - scope.result = Encode(false); - return; - } + if (o->internalClass() != o->internalClass()->frozen()) + return Encode(false); - if (!o->arrayData() || !o->arrayData()->length()) { - scope.result = Encode(true); - return; - } + if (!o->arrayData() || !o->arrayData()->length()) + return Encode(true); Q_ASSERT(o->arrayData() && o->arrayData()->length()); - if (!o->arrayData()->attrs) { - scope.result = Encode(false); - return; - } + if (!o->arrayData()->attrs) + return Encode(false); for (uint i = 0; i < o->arrayData()->values.alloc; ++i) { if (!o->arrayData()->isEmpty(i)) - if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) { - scope.result = Encode(false); - return; - } + if (o->arrayData()->attributes(i).isConfigurable() || o->arrayData()->attributes(i).isWritable()) + return Encode(false); } - scope.result = Encode(true); + return Encode(true); } -void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isExtensible(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - if (!o) { - scope.result = Encode(false); - return; - } + if (!o) + return Encode(false); - scope.result = Encode((bool)o->isExtensible()); + return Encode((bool)o->isExtensible()); } -void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_keys(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->args[0].toObject(scope.engine)); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -449,24 +451,27 @@ void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallDat a->push_back(name); } - scope.result = a; + return a.asReturnedValue(); } -void ObjectPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { + ExecutionEngine *v4 = b->engine(); if (callData->thisObject.isUndefined()) { - scope.result = scope.engine->newString(QStringLiteral("[object Undefined]")); + return Encode(v4->newString(QStringLiteral("[object Undefined]"))); } else if (callData->thisObject.isNull()) { - scope.result = scope.engine->newString(QStringLiteral("[object Null]")); + return Encode(v4->newString(QStringLiteral("[object Null]"))); } else { + Scope scope(v4); ScopedObject obj(scope, callData->thisObject.toObject(scope.engine)); QString className = obj->className(); - scope.result = scope.engine->newString(QStringLiteral("[object %1]").arg(className)); + return Encode(v4->newString(QStringLiteral("[object %1]").arg(className))); } } -void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_toLocaleString(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->thisObject.toObject(scope.engine)); if (!o) RETURN_UNDEFINED(); @@ -474,64 +479,69 @@ void ObjectPrototype::method_toLocaleString(const BuiltinFunction *, Scope &scop ScopedFunctionObject f(scope, o->get(scope.engine->id_toString())); if (!f) THROW_TYPE_ERROR(); - ScopedCallData cData(scope); - cData->thisObject = o; - f->call(scope, callData); + JSCallData jsCallData(scope); + *jsCallData->thisObject = o; + return f->call(jsCallData); } -void ObjectPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - scope.result = callData->thisObject.toObject(scope.engine); + return Encode(callData->thisObject.toObject(b->engine())); } -void ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_hasOwnProperty(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedString P(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); bool r = O->hasOwnProperty(P); if (!r) r = !O->query(P).isEmpty(); - scope.result = Encode(r); + return Encode(r); } -void ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_isPrototypeOf(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject V(scope, callData->argument(0)); - if (!V) { - scope.result = Encode(false); - return; - } + if (!V) + return Encode(false); ScopedObject O(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject proto(scope, V->prototype()); while (proto) { - if (O->d() == proto->d()) { - scope.result = Encode(true); - return; - } + if (O->d() == proto->d()) + return Encode(true); proto = proto->prototype(); } - scope.result = Encode(false); + return Encode(false); } -void ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_propertyIsEnumerable(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedString p(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject o(scope, callData->thisObject, ScopedObject::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); PropertyAttributes attrs; o->getOwnProperty(p, &attrs); - scope.result = Encode(attrs.isEnumerable()); + return Encode(attrs.isEnumerable()); } -void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineGetter(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 2) + Scope scope(b); + if (callData->argc() < 2) THROW_TYPE_ERROR(); ScopedFunctionObject f(scope, callData->argument(1)); @@ -539,7 +549,8 @@ void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, THROW_TYPE_ERROR(); ScopedString prop(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject o(scope, callData->thisObject); if (!o) { @@ -551,13 +562,16 @@ void ObjectPrototype::method_defineGetter(const BuiltinFunction *, Scope &scope, ScopedProperty pd(scope); pd->value = f; pd->set = Primitive::emptyValue(); - o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + if (!ok) + THROW_TYPE_ERROR(); RETURN_UNDEFINED(); } -void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_defineSetter(const BuiltinFunction *b, CallData *callData) { - if (callData->argc < 2) + Scope scope(b); + if (callData->argc() < 2) THROW_TYPE_ERROR(); ScopedFunctionObject f(scope, callData->argument(1)); @@ -565,7 +579,8 @@ void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, THROW_TYPE_ERROR(); ScopedString prop(scope, callData->argument(0), ScopedString::Convert); - CHECK_EXCEPTION(); + if (scope.engine->hasException) + return QV4::Encode::undefined(); ScopedObject o(scope, callData->thisObject); if (!o) { @@ -577,23 +592,27 @@ void ObjectPrototype::method_defineSetter(const BuiltinFunction *, Scope &scope, ScopedProperty pd(scope); pd->value = Primitive::emptyValue(); pd->set = f; - o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + bool ok = o->__defineOwnProperty__(scope.engine, prop, pd, Attr_Accessor); + if (!ok) + THROW_TYPE_ERROR(); RETURN_UNDEFINED(); } -void ObjectPrototype::method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_get_proto(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->thisObject.as<Object>()); if (!o) THROW_TYPE_ERROR(); - scope.result = o->prototype(); + return Encode(o->prototype()); } -void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue ObjectPrototype::method_set_proto(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); ScopedObject o(scope, callData->thisObject); - if (!o || !callData->argc) + if (!o || !callData->argc()) THROW_TYPE_ERROR(); if (callData->args[0].isNull()) { @@ -610,10 +629,8 @@ void ObjectPrototype::method_set_proto(const BuiltinFunction *, Scope &scope, Ca ok = o->setPrototype(p); } } - if (!ok) { - scope.result = scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); - return; - } + if (!ok) + return scope.engine->throwTypeError(QStringLiteral("Cyclic __proto__ value")); RETURN_UNDEFINED(); } diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 44b54267f3..7c97963baa 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -70,41 +70,41 @@ struct ObjectCtor: FunctionObject { V4_OBJECT2(ObjectCtor, FunctionObject) - static void construct(const Managed *that, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc); }; struct ObjectPrototype: Object { void init(ExecutionEngine *engine, Object *ctor); - static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_seal(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_keys(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_hasOwnProperty(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_isPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_propertyIsEnumerable(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_defineGetter(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_defineSetter(const BuiltinFunction *, Scope &scope, CallData *callData); - - static void method_get_proto(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_set_proto(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_getPrototypeOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getOwnPropertyDescriptor(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_getOwnPropertyNames(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_assign(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_create(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_defineProperty(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_defineProperties(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_seal(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_freeze(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_preventExtensions(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isSealed(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isFrozen(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isExtensible(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_keys(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_hasOwnProperty(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_isPrototypeOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_propertyIsEnumerable(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_defineGetter(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_defineSetter(const BuiltinFunction *, CallData *callData); + + static ReturnedValue method_get_proto(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_set_proto(const BuiltinFunction *, CallData *callData); static void toPropertyDescriptor(ExecutionEngine *engine, const Value &v, Property *desc, PropertyAttributes *attrs); static ReturnedValue fromPropertyDescriptor(ExecutionEngine *engine, const Property *desc, PropertyAttributes attrs); diff --git a/src/qml/jsruntime/qv4profiling_p.h b/src/qml/jsruntime/qv4profiling_p.h index 9de597ad0e..9b71342467 100644 --- a/src/qml/jsruntime/qv4profiling_p.h +++ b/src/qml/jsruntime/qv4profiling_p.h @@ -59,9 +59,6 @@ #ifdef QT_NO_QML_DEBUGGER -#define Q_V4_PROFILE_ALLOC(engine, size, type) (!engine) -#define Q_V4_PROFILE_DEALLOC(engine, size, type) (!engine) -#define Q_V4_PROFILE(engine, function) (function->code(engine, function->codeData)) QT_BEGIN_NAMESPACE @@ -75,22 +72,6 @@ QT_END_NAMESPACE #else -#define Q_V4_PROFILE_ALLOC(engine, size, type)\ - (engine->profiler() &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler()->trackAlloc(size, type) : false) - -#define Q_V4_PROFILE_DEALLOC(engine, size, type) \ - (engine->profiler() &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureMemoryAllocation)) ?\ - engine->profiler()->trackDealloc(size, type) : false) - -#define Q_V4_PROFILE(engine, function)\ - (Q_UNLIKELY(engine->profiler()) &&\ - (engine->profiler()->featuresEnabled & (1 << Profiling::FeatureFunctionCall)) ?\ - Profiling::FunctionCallProfiler::profileCall(engine->profiler(), engine, function) :\ - function->code(engine, function->codeData)) - QT_BEGIN_NAMESPACE namespace QV4 { @@ -270,19 +251,21 @@ public: // It's enough to ref() the function in the destructor as it will probably not disappear while // it's executing ... - FunctionCallProfiler(Profiler *profiler, Function *function) : - profiler(profiler), function(function), startTime(profiler->m_timer.nsecsElapsed()) - {} - - ~FunctionCallProfiler() + FunctionCallProfiler(ExecutionEngine *engine, Function *f) + : profiler(0) { - profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed())); + Profiler *p = engine->profiler(); + if (Q_UNLIKELY(p) && (p->featuresEnabled & (1 << Profiling::FeatureFunctionCall))) { + profiler = p; + function = f; + startTime = profiler->m_timer.nsecsElapsed(); + } } - static ReturnedValue profileCall(Profiler *profiler, ExecutionEngine *engine, Function *function) + ~FunctionCallProfiler() { - FunctionCallProfiler callProfiler(profiler, function); - return function->code(engine, function->codeData); + if (profiler) + profiler->m_data.append(FunctionCall(function, startTime, profiler->m_timer.nsecsElapsed())); } Profiler *profiler; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 8b9ef149d6..ce0c89be57 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -296,13 +296,9 @@ void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContex { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); outer.set(internalClass->engine, outerContext->d()); - strictMode = false; - callData = outer->callData; - lookups = outer->lookups; - constantTable = outer->constantTable; - compilationUnit = outer->compilationUnit; + v4Function = outer->v4Function; - this->qml.set(internalClass->engine, qml->d()); + this->activation.set(internalClass->engine, qml->d()); } Heap::QmlContext *QmlContext::createWorkerContext(ExecutionContext *parent, const QUrl &source, Value *sendFunction) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 48c9ee2c36..f0782c7ee1 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -76,12 +76,12 @@ struct QQmlContextWrapper : Object { QQmlQPointer<QObject> scopeObject; }; -#define QmlContextMembers(class, Member) \ - Member(class, Pointer, QQmlContextWrapper *, qml) +#define QmlContextMembers(class, Member) DECLARE_HEAP_OBJECT(QmlContext, ExecutionContext) { DECLARE_MARK_TABLE(QmlContext); + QQmlContextWrapper *qml() { return static_cast<QQmlContextWrapper *>(activation.get()); } void init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml); }; @@ -109,10 +109,10 @@ struct Q_QML_EXPORT QmlContext : public ExecutionContext static Heap::QmlContext *create(QV4::ExecutionContext *parent, QQmlContextData *context, QObject *scopeObject); QObject *qmlScope() const { - return d()->qml->scopeObject; + return d()->qml()->scopeObject; } QQmlContextData *qmlContext() const { - return *d()->qml->context; + return *d()->qml()->context; } }; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index fb3aa47d1b..9746b82817 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -62,6 +62,7 @@ #include <private/qv4regexpobject_p.h> #include <private/qv4dateobject_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include <private/qv4mm_p.h> #include <private/qqmlscriptstring_p.h> #include <private/qv4compileddata_p.h> @@ -468,11 +469,11 @@ void QObjectWrapper::setProperty(ExecutionEngine *engine, QObject *object, QQmlP if (auto binding = QQmlPropertyPrivate::binding(object, QQmlPropertyIndex(property->coreIndex()))) { Q_ASSERT(!binding->isValueTypeProxy()); const auto qmlBinding = static_cast<const QQmlBinding*>(binding); - const auto stackFrame = engine->currentStackFrame(); + const auto stackFrame = engine->currentStackFrame; qCInfo(lcBindingRemoval, "Overwriting binding on %s::%s at %s:%d that was initially bound at %s", object->metaObject()->className(), qPrintable(property->name(object)), - qPrintable(stackFrame.source), stackFrame.line, + qPrintable(stackFrame->source()), stackFrame->lineNumber(), qPrintable(qmlBinding->expressionIdentifier())); } } @@ -815,18 +816,18 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase QV4::Scope scope(v4); QV4::ScopedFunctionObject f(scope, This->function.value()); - QV4::ScopedCallData callData(scope, argCount); - callData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); + QV4::JSCallData jsCallData(scope, argCount); + *jsCallData->thisObject = This->thisObject.isUndefined() ? v4->globalObject->asReturnedValue() : This->thisObject.value(); for (int ii = 0; ii < argCount; ++ii) { int type = argsTypes[ii + 1]; if (type == qMetaTypeId<QVariant>()) { - callData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); + jsCallData->args[ii] = v4->fromVariant(*((QVariant *)metaArgs[ii + 1])); } else { - callData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); + jsCallData->args[ii] = v4->fromVariant(QVariant(type, metaArgs[ii + 1])); } } - f->call(scope, callData); + f->call(jsCallData); if (scope.hasException()) { QQmlError error = v4->catchExceptionAsQmlError(); if (error.description().isEmpty()) { @@ -899,9 +900,11 @@ struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase } // namespace QV4 -void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QObjectWrapper::method_connect(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + + if (callData->argc() == 0) THROW_GENERIC_ERROR("Function.prototype.connect: no arguments given"); QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); @@ -920,9 +923,9 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD QV4::ScopedFunctionObject f(scope); QV4::ScopedValue thisObject (scope, QV4::Encode::undefined()); - if (callData->argc == 1) { + if (callData->argc() == 1) { f = callData->args[0]; - } else if (callData->argc >= 2) { + } else if (callData->argc() >= 2) { thisObject = callData->args[0]; f = callData->args[1]; } @@ -949,9 +952,11 @@ void QObjectWrapper::method_connect(const BuiltinFunction *, Scope &scope, CallD RETURN_UNDEFINED(); } -void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue QObjectWrapper::method_disconnect(const BuiltinFunction *b, CallData *callData) { - if (callData->argc == 0) + QV4::Scope scope(b); + + if (callData->argc() == 0) THROW_GENERIC_ERROR("Function.prototype.disconnect: no arguments given"); QPair<QObject *, int> signalInfo = extractQtSignal(callData->thisObject); @@ -970,9 +975,9 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca QV4::ScopedFunctionObject functionValue(scope); QV4::ScopedValue functionThisValue(scope, QV4::Encode::undefined()); - if (callData->argc == 1) { + if (callData->argc() == 1) { functionValue = callData->args[0]; - } else if (callData->argc >= 2) { + } else if (callData->argc() >= 2) { functionThisValue = callData->args[0]; functionValue = callData->args[1]; } @@ -1412,7 +1417,7 @@ static QV4::ReturnedValue CallPrecise(const QQmlObjectOrGadget &object, const QQ + QLatin1String(unknownTypeError)); } - if (args[0] > callArgs->argc) { + if (args[0] > callArgs->argc()) { QString error = QLatin1String("Insufficient arguments"); return engine->throwError(error); } @@ -1443,7 +1448,7 @@ static QV4::ReturnedValue CallOverloaded(const QQmlObjectOrGadget &object, const QV4::ExecutionEngine *engine, QV4::CallData *callArgs, const QQmlPropertyCache *propertyCache, QMetaObject::Call callType = QMetaObject::InvokeMetaMethod) { - int argumentCount = callArgs->argc; + int argumentCount = callArgs->argc(); QQmlPropertyData best; int bestParameterScore = INT_MAX; @@ -1848,7 +1853,7 @@ const QMetaObject *Heap::QObjectMethod::metaObject() return object()->metaObject(); } -QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) const +QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionEngine *engine) const { QString result; if (const QMetaObject *metaObject = d()->metaObject()) { @@ -1867,15 +1872,15 @@ QV4::ReturnedValue QObjectMethod::method_toString(QV4::ExecutionContext *ctx) co result = QLatin1String("null"); } - return ctx->engine()->newString(result)->asReturnedValue(); + return engine->newString(result)->asReturnedValue(); } -QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const +QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionEngine *engine, const Value *args, int argc) const { if (!d()->object()) return Encode::undefined(); if (QQmlData::keepAliveDuringGarbageCollection(d()->object())) - return ctx->engine()->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); + return engine->throwError(QStringLiteral("Invalid attempt to destroy() an indestructible object")); int delay = 0; if (argc > 0) @@ -1889,32 +1894,24 @@ QV4::ReturnedValue QObjectMethod::method_destroy(QV4::ExecutionContext *ctx, con return Encode::undefined(); } -void QObjectMethod::call(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue QObjectMethod::call(const FunctionObject *m, const Value *thisObject, const Value *argv, int argc) { const QObjectMethod *This = static_cast<const QObjectMethod*>(m); - This->callInternal(callData, scope); + return This->callInternal(thisObject, argv, argc); } -void QObjectMethod::callInternal(CallData *callData, Scope &scope) const +ReturnedValue QObjectMethod::callInternal(const Value *thisObject, const Value *argv, int argc) const { ExecutionEngine *v4 = engine(); - ExecutionContext *context = v4->currentContext; - if (d()->index == DestroyMethod) { - scope.result = method_destroy(context, callData->args, callData->argc); - return; - } - - else if (d()->index == ToStringMethod) { - scope.result = method_toString(context); - return; - } + if (d()->index == DestroyMethod) + return method_destroy(v4, argv, argc); + else if (d()->index == ToStringMethod) + return method_toString(v4); QQmlObjectOrGadget object(d()->object()); if (!d()->object()) { - if (!d()->valueTypeWrapper) { - scope.result = Encode::undefined(); - return; - } + if (!d()->valueTypeWrapper) + return Encode::undefined(); object = QQmlObjectOrGadget(d()->propertyCache(), d()->valueTypeWrapper->gadgetPtr); } @@ -1923,20 +1920,16 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const if (d()->propertyCache()) { QQmlPropertyData *data = d()->propertyCache()->method(d()->index); - if (!data) { - scope.result = QV4::Encode::undefined(); - return; - } + if (!data) + return QV4::Encode::undefined(); method = *data; } else { const QMetaObject *mo = d()->object()->metaObject(); const QMetaMethod moMethod = mo->method(d()->index); method.load(moMethod); - if (method.coreIndex() == -1) { - scope.result = QV4::Encode::undefined(); - return; - } + if (method.coreIndex() == -1) + return QV4::Encode::undefined(); // Look for overloaded methods QByteArray methodName = moMethod.name(); @@ -1951,21 +1944,25 @@ void QObjectMethod::callInternal(CallData *callData, Scope &scope) const } } + Scope scope(v4); + JSCallData cData(scope, argc, argv, thisObject); + CallData *callData = cData.callData(); + if (method.isV4Function()) { - scope.result = QV4::Encode::undefined(); - QQmlV4Function func(callData, &scope.result, v4); + QV4::ScopedValue rv(scope, QV4::Primitive::undefinedValue()); + QQmlV4Function func(callData, rv, v4); QQmlV4Function *funcptr = &func; void *args[] = { 0, &funcptr }; object.metacall(QMetaObject::InvokeMetaMethod, method.coreIndex(), args); - return; + return rv->asReturnedValue(); } if (!method.isOverload()) { - scope.result = CallPrecise(object, method, v4, callData); + return CallPrecise(object, method, v4, callData); } else { - scope.result = CallOverloaded(object, method, v4, callData, d()->propertyCache()); + return CallOverloaded(object, method, v4, callData, d()->propertyCache()); } } @@ -2028,13 +2025,14 @@ void QMetaObjectWrapper::init(ExecutionEngine *) { } } -void QMetaObjectWrapper::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue QMetaObjectWrapper::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(m); - scope.result = This->constructInternal(callData); + const QMetaObjectWrapper *This = static_cast<const QMetaObjectWrapper*>(f); + return This->constructInternal(argv, argc); } -ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { +ReturnedValue QMetaObjectWrapper::constructInternal(const Value *argv, int argc) const +{ d()->ensureConstructorsCache(); @@ -2047,6 +2045,8 @@ ReturnedValue QMetaObjectWrapper::constructInternal(CallData * callData) const { Scope scope(v4); Scoped<QObjectWrapper> object(scope); + JSCallData cData(scope, argc, argv); + CallData *callData = cData.callData(); if (d()->constructorCount == 1) { object = callConstructor(d()->constructors[0], v4, callData); @@ -2071,7 +2071,7 @@ ReturnedValue QMetaObjectWrapper::callConstructor(const QQmlPropertyData &data, ReturnedValue QMetaObjectWrapper::callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const { const int numberOfConstructors = d()->constructorCount; - const int argumentCount = callArgs->argc; + const int argumentCount = callArgs->argc(); const QQmlStaticMetaObject object(d()->metaObject); QQmlPropertyData best; diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 018e444f7c..7b31fcc1e5 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -198,8 +198,8 @@ protected: static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static void markObjects(Heap::Base *that, QV4::MarkStack *markStack); - static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_connect(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_disconnect(const BuiltinFunction *, CallData *callData); private: Q_NEVER_INLINE static ReturnedValue wrap_slowPath(ExecutionEngine *engine, QObject *object); @@ -234,12 +234,12 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } - QV4::ReturnedValue method_toString(QV4::ExecutionContext *ctx) const; - QV4::ReturnedValue method_destroy(QV4::ExecutionContext *ctx, const Value *args, int argc) const; + QV4::ReturnedValue method_toString(QV4::ExecutionEngine *engine) const; + QV4::ReturnedValue method_destroy(QV4::ExecutionEngine *ctx, const Value *args, int argc) const; - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); - void callInternal(CallData *callData, Scope &scope) const; + ReturnedValue callInternal(const Value *thisObject, const Value *argv, int argc) const; static QPair<QObject *, int> extractQtMethod(const QV4::FunctionObject *function); }; @@ -251,14 +251,14 @@ struct Q_QML_EXPORT QMetaObjectWrapper : public QV4::FunctionObject V4_NEEDS_DESTROY static ReturnedValue create(ExecutionEngine *engine, const QMetaObject* metaObject); - static void construct(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *, const Value *argv, int argc); static bool isEqualTo(Managed *a, Managed *b); const QMetaObject *metaObject() const { return d()->metaObject; } private: void init(ExecutionEngine *engine); - ReturnedValue constructInternal(CallData *callData) const; + ReturnedValue constructInternal(const Value *argv, int argc) const; ReturnedValue callConstructor(const QQmlPropertyData &data, QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; ReturnedValue callOverloadedConstructor(QV4::ExecutionEngine *engine, QV4::CallData *callArgs) const; diff --git a/src/qml/jsruntime/qv4regexp.cpp b/src/qml/jsruntime/qv4regexp.cpp index 162f0fba57..fb49def317 100644 --- a/src/qml/jsruntime/qv4regexp.cpp +++ b/src/qml/jsruntime/qv4regexp.cpp @@ -62,7 +62,7 @@ uint RegExp::match(const QString &string, int start, uint *matchOffsets) WTF::String s(string); #if ENABLE(YARR_JIT) - if (!jitCode()->isFallBack() && jitCode()->has16BitCode()) + if (d()->hasValidJITCode()) return uint(jitCode()->execute(s.characters16(), start, s.length(), (int*)matchOffsets).start); #endif @@ -82,7 +82,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); Scope scope(engine); - Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(engine, pattern, ignoreCase, multiline, global)); + Scoped<RegExp> result(scope, engine->memoryManager->alloc<RegExp>(pattern, ignoreCase, multiline, global)); result->d()->cache = cache; cachedValue.set(engine, result); @@ -90,7 +90,7 @@ Heap::RegExp *RegExp::create(ExecutionEngine* engine, const QString& pattern, bo return result->d(); } -void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ignoreCase, bool multiline, bool global) +void Heap::RegExp::init(const QString &pattern, bool ignoreCase, bool multiline, bool global) { Base::init(); this->pattern = new QString(pattern); @@ -98,20 +98,28 @@ void Heap::RegExp::init(ExecutionEngine* engine, const QString &pattern, bool ig this->multiLine = multiline; this->global = global; + valid = false; + const char* error = 0; - JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiline, &error); + JSC::Yarr::YarrPattern yarrPattern(WTF::String(pattern), ignoreCase, multiLine, &error); if (error) return; subPatternCount = yarrPattern.m_numSubpatterns; - OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, engine->bumperPointerAllocator); - byteCode = p.take(); #if ENABLE(YARR_JIT) - jitCode = new JSC::Yarr::YarrCodeBlock; - if (!yarrPattern.m_containsBackreferences && engine->iselFactory->jitCompileRegexps()) { - JSC::JSGlobalData dummy(engine->regExpAllocator); + if (!yarrPattern.m_containsBackreferences) { + jitCode = new JSC::Yarr::YarrCodeBlock; + JSC::JSGlobalData dummy(internalClass->engine->regExpAllocator); JSC::Yarr::jitCompile(yarrPattern, JSC::Yarr::Char16, &dummy, *jitCode); } #endif + if (hasValidJITCode()) { + valid = true; + return; + } + OwnPtr<JSC::Yarr::BytecodePattern> p = JSC::Yarr::byteCompile(yarrPattern, internalClass->engine->bumperPointerAllocator); + byteCode = p.take(); + if (byteCode) + valid = true; } void Heap::RegExp::destroy() diff --git a/src/qml/jsruntime/qv4regexp_p.h b/src/qml/jsruntime/qv4regexp_p.h index 998f6e3da3..498468e165 100644 --- a/src/qml/jsruntime/qv4regexp_p.h +++ b/src/qml/jsruntime/qv4regexp_p.h @@ -76,7 +76,7 @@ struct RegExpCacheKey; namespace Heap { struct RegExp : Base { - void init(ExecutionEngine* engine, const QString& pattern, bool ignoreCase, bool multiline, bool global); + void init(const QString& pattern, bool ignoreCase, bool multiline, bool global); void destroy(); QString *pattern; @@ -84,11 +84,19 @@ struct RegExp : Base { #if ENABLE(YARR_JIT) JSC::Yarr::YarrCodeBlock *jitCode; #endif + bool hasValidJITCode() const { +#if ENABLE(YARR_JIT) + return jitCode && !jitCode->isFallBack() && jitCode->has16BitCode(); +#else + return false; +#endif + } RegExpCache *cache; int subPatternCount; bool ignoreCase; bool multiLine; bool global; + bool valid; int captureCount() const { return subPatternCount + 1; } }; @@ -116,7 +124,7 @@ struct RegExp : public Managed static Heap::RegExp *create(ExecutionEngine* engine, const QString& pattern, bool ignoreCase = false, bool multiline = false, bool global = false); - bool isValid() const { return d()->byteCode; } + bool isValid() const { return d()->valid; } uint match(const QString& string, int start, uint *matchOffsets); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 83bfe21c67..3ff5349e74 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -38,20 +38,17 @@ ****************************************************************************/ #include "qv4regexpobject_p.h" -#include "qv4jsir_p.h" -#include "qv4isel_p.h" #include "qv4objectproto_p.h" #include "qv4regexp_p.h" #include "qv4stringobject_p.h" #include <private/qv4mm_p.h> #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 <qv4jsir_p.h> -#include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" #include <QtCore/QDebug> @@ -217,39 +214,33 @@ void Heap::RegExpCtor::clearLastMatch() lastMatchEnd = 0; } -void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) +ReturnedValue RegExpCtor::callAsConstructor(const FunctionObject *fo, const Value *argv, int argc) { - ScopedValue r(scope, callData->argument(0)); - ScopedValue f(scope, callData->argument(1)); + Scope scope(fo->engine()); + ScopedValue r(scope, argc ? argv[0] : Primitive::undefinedValue()); + ScopedValue f(scope, argc > 1 ? argv[1] : Primitive::undefinedValue()); Scoped<RegExpObject> re(scope, r); if (re) { - if (!f->isUndefined()) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (!f->isUndefined()) + return scope.engine->throwTypeError(); Scoped<RegExp> regexp(scope, re->value()); - scope.result = Encode(scope.engine->newRegExpObject(regexp)); - return; + return Encode(scope.engine->newRegExpObject(regexp)); } QString pattern; if (!r->isUndefined()) pattern = r->toQString(); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + if (scope.hasException()) + return Encode::undefined(); bool global = false; bool ignoreCase = false; bool multiLine = false; if (!f->isUndefined()) { ScopedString s(scope, f->toString(scope.engine)); - if (scope.hasException()) { - scope.result = Encode::undefined(); - return; - } + if (scope.hasException()) + return Encode::undefined(); QString str = s->toQString(); for (int i = 0; i < str.length(); ++i) { if (str.at(i) == QLatin1Char('g') && !global) { @@ -259,31 +250,27 @@ void RegExpCtor::construct(const Managed *, Scope &scope, CallData *callData) } else if (str.at(i) == QLatin1Char('m') && !multiLine) { multiLine = true; } else { - scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); - return; + return scope.engine->throwSyntaxError(QStringLiteral("Invalid flags supplied to RegExp constructor")); } } } Scoped<RegExp> regexp(scope, RegExp::create(scope.engine, pattern, ignoreCase, multiLine, global)); if (!regexp->isValid()) { - scope.result = scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); - return; + return scope.engine->throwSyntaxError(QStringLiteral("Invalid regular expression")); } - scope.result = Encode(scope.engine->newRegExpObject(regexp)); + return Encode(scope.engine->newRegExpObject(regexp)); } -void RegExpCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue RegExpCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - if (callData->argc > 0 && callData->args[0].as<RegExpObject>()) { - if (callData->argc == 1 || callData->args[1].isUndefined()) { - scope.result = callData->args[0]; - return; - } + if (argc > 0 && argv[0].as<RegExpObject>()) { + if (argc == 1 || argv[1].isUndefined()) + return Encode(argv[0]); } - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) @@ -323,11 +310,58 @@ void RegExpPrototype::init(ExecutionEngine *engine, Object *constructor) defineDefaultProperty(QStringLiteral("compile"), method_compile, 2); } -void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallData *callData) +/* used by String.match */ +ReturnedValue RegExpPrototype::execFirstMatch(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); + Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); + Q_ASSERT(r && r->global()); + + ScopedString str(scope, callData->args[0]); + Q_ASSERT(str); + QString s = str->toQString(); + + int offset = r->lastIndex(); + if (offset < 0 || offset > s.length()) { + r->setLastIndex(0); + RETURN_RESULT(Encode::null()); + } + + Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); + const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); + + RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor()); + regExpCtor->d()->clearLastMatch(); + + if (result == -1) { + r->setLastIndex(0); + RETURN_RESULT(Encode::null()); + } + + ReturnedValue retVal = Encode::undefined(); + // return first match + if (r->value()->captureCount()) { + int start = matchOffsets[0]; + int end = matchOffsets[1]; + retVal = (start != -1) ? scope.engine->newString(s.mid(start, end - start))->asReturnedValue() : Encode::undefined(); + } + + RegExpCtor::Data *dd = regExpCtor->d(); + dd->lastInput.set(scope.engine, str->d()); + dd->lastMatchStart = matchOffsets[0]; + dd->lastMatchEnd = matchOffsets[1]; + + r->setLastIndex(matchOffsets[1]); + + return retVal; +} + +ReturnedValue RegExpPrototype::method_exec(const BuiltinFunction *b, CallData *callData) +{ + Scope scope(b); Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); if (!r) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); ScopedValue arg(scope, callData->argument(0)); ScopedString str(scope, arg->toString(scope.engine)); @@ -344,7 +378,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); - Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); + RegExpCtor *regExpCtor = static_cast<RegExpCtor *>(scope.engine->regExpCtor()); regExpCtor->d()->clearLastMatch(); if (result == -1) { @@ -376,73 +410,81 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat if (r->global()) r->setLastIndex(matchOffsets[1]); - scope.result = array; + return array.asReturnedValue(); } -void RegExpPrototype::method_test(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_test(const BuiltinFunction *b, CallData *callData) { - method_exec(b, scope, callData); - scope.result = Encode(!scope.result.isNull()); + Value res = Value::fromReturnedValue(method_exec(b, callData)); + return Encode(!res.isNull()); } -void RegExpPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); + ExecutionEngine *v4 = b->engine(); + RegExpObject *r = callData->thisObject.as<RegExpObject>(); if (!r) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = scope.engine->newString(r->toString()); + return Encode(v4->newString(r->toString())); } -void RegExpPrototype::method_compile(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue RegExpPrototype::method_compile(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); Scoped<RegExpObject> r(scope, callData->thisObject.as<RegExpObject>()); if (!r) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); - ScopedCallData cData(scope, callData->argc); - memcpy(cData->args, callData->args, callData->argc*sizeof(Value)); + JSCallData jsCallData(scope, callData->argc()); + memcpy(jsCallData->args, callData->args, callData->argc()*sizeof(Value)); - scope.engine->regExpCtor()->as<FunctionObject>()->construct(scope, cData); - Scoped<RegExpObject> re(scope, scope.result.asReturnedValue()); + Scoped<RegExpObject> re(scope, scope.engine->regExpCtor()->callAsConstructor(jsCallData)); r->d()->value.set(scope.engine, re->value()); + return Encode::undefined(); } template <int index> -void RegExpPrototype::method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_lastMatch_n(const BuiltinFunction *b, CallData *) { + Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - scope.result = lastMatch ? lastMatch->getIndexed(index) : Encode::undefined(); - if (scope.result.isUndefined()) - scope.result = scope.engine->newString(); + ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(index) : Encode::undefined()); + if (res->isUndefined()) + res = scope.engine->newString(); + return res->asReturnedValue(); } -void RegExpPrototype::method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_lastParen(const BuiltinFunction *b, CallData *) { + Scope scope(b); ScopedArrayObject lastMatch(scope, static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastMatch()); - scope.result = lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined(); - if (scope.result.isUndefined()) - scope.result = scope.engine->newString(); + ScopedValue res(scope, lastMatch ? lastMatch->getIndexed(lastMatch->getLength() - 1) : Encode::undefined()); + if (res->isUndefined()) + res = scope.engine->newString(); + return res->asReturnedValue(); } -void RegExpPrototype::method_get_input(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_input(const BuiltinFunction *b, CallData *) { - scope.result = static_cast<RegExpCtor*>(scope.engine->regExpCtor())->lastInput(); + return static_cast<RegExpCtor*>(b->engine()->regExpCtor())->lastInput()->asReturnedValue(); } -void RegExpPrototype::method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_leftContext(const BuiltinFunction *b, CallData *) { + Scope scope(b); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - scope.result = scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart())); + return Encode(scope.engine->newString(lastInput.left(regExpCtor->lastMatchStart()))); } -void RegExpPrototype::method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *) +ReturnedValue RegExpPrototype::method_get_rightContext(const BuiltinFunction *b, CallData *) { + Scope scope(b); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); QString lastInput = regExpCtor->lastInput()->toQString(); - scope.result = scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd())); + return Encode(scope.engine->newString(lastInput.mid(regExpCtor->lastMatchEnd()))); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4regexpobject_p.h b/src/qml/jsruntime/qv4regexpobject_p.h index 65055ccc81..1f92516928 100644 --- a/src/qml/jsruntime/qv4regexpobject_p.h +++ b/src/qml/jsruntime/qv4regexpobject_p.h @@ -55,8 +55,6 @@ #include "qv4context_p.h" #include "qv4functionobject_p.h" #include "qv4string_p.h" -#include <qv4codegen_p.h> -#include <qv4isel_p.h> #include "qv4managed_p.h" #include "qv4property_p.h" #include "qv4objectiterator_p.h" @@ -106,7 +104,7 @@ struct RegExpObject: Object { V4_INTERNALCLASS(RegExpObject) V4_PROTOTYPE(regExpPrototype) - // needs to be compatible with the flags in qv4jsir_p.h + // needs to be compatible with the flags in qv4compileddata_p.h enum Flags { RegExp_Global = 0x01, RegExp_IgnoreCase = 0x02, @@ -152,25 +150,27 @@ struct RegExpCtor: FunctionObject int lastMatchStart() { return d()->lastMatchStart; } int lastMatchEnd() { return d()->lastMatchEnd; } - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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 RegExpPrototype: RegExpObject { void init(ExecutionEngine *engine, Object *ctor); - static void method_exec(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_test(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_compile(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_exec(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_test(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_compile(const BuiltinFunction *, CallData *callData); template <int index> - static void method_get_lastMatch_n(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_lastParen(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_input(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_leftContext(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_rightContext(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_lastMatch_n(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_lastParen(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_input(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_leftContext(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_rightContext(const BuiltinFunction *, CallData *callData); + + static ReturnedValue execFirstMatch(const BuiltinFunction *b, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 4b952bcbbc..7a5d114271 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -55,6 +55,7 @@ #include "qv4regexpobject_p.h" #include "private/qlocale_tools_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <private/qv4qmlcontext_p.h> #include <private/qqmltypewrapper_p.h> #include <private/qqmlengine_p.h> @@ -310,47 +311,47 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; Q_ASSERT(clos); - return FunctionObject::createScriptFunction(engine->currentContext, clos)->asReturnedValue(); + ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -ReturnedValue Runtime::method_deleteElement(ExecutionEngine *engine, const Value &base, const Value &index) +bool Runtime::method_deleteElement(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 Encode((bool)o->deleteIndexedProperty(n)); - } + if (n < UINT_MAX) + return o->deleteIndexedProperty(n); } ScopedString name(scope, index.toString(engine)); return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) +bool Runtime::method_deleteMember(ExecutionEngine *engine, const Value &base, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return method_deleteMemberString(engine, base, name); } -ReturnedValue Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) +bool Runtime::method_deleteMemberString(ExecutionEngine *engine, const Value &base, String *name) { Scope scope(engine); ScopedObject obj(scope, base.toObject(engine)); if (scope.engine->hasException) return Encode::undefined(); - return Encode(obj->deleteProperty(name)); + return obj->deleteProperty(name); } -ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) +bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - return Encode(engine->currentContext->deleteProperty(name)); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); } QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) @@ -425,14 +426,13 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH qSwap(meth1, meth2); Scope scope(engine); - ScopedCallData callData(scope, 0); - callData->thisObject = *object; - + ScopedValue result(scope); ScopedValue conv(scope, object->get(meth1)); + if (FunctionObject *o = conv->as<FunctionObject>()) { - o->call(scope, callData); - if (scope.result.isPrimitive()) - return scope.result.asReturnedValue(); + result = o->call(object, nullptr, 0); + if (result->isPrimitive()) + return result->asReturnedValue(); } if (engine->hasException) @@ -440,9 +440,9 @@ ReturnedValue RuntimeHelpers::objectDefaultValue(const Object *object, int typeH conv = object->get(meth2); if (FunctionObject *o = conv->as<FunctionObject>()) { - o->call(scope, callData); - if (scope.result.isPrimitive()) - return scope.result.asReturnedValue(); + result = o->call(object, nullptr, 0); + if (result->isPrimitive()) + return result->asReturnedValue(); } return engine->throwTypeError(); @@ -469,8 +469,9 @@ Heap::Object *RuntimeHelpers::convertToObject(ExecutionEngine *engine, const Val } } -Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Value &value) +Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value value, TypeHint hint) { + redo: switch (value.type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); @@ -484,15 +485,15 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val return engine->id_true()->d(); else return engine->id_false()->d(); - case Value::Managed_Type: - if (String *s = value.stringValue()) - return s->d(); - { - Scope scope(engine); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, STRING_HINT)); - Q_ASSERT(!prim->isManaged() || prim->isString()); - return RuntimeHelpers::convertToString(engine, prim); - } + case Value::Managed_Type: { + if (value.isString()) + return static_cast<const String &>(value).d(); + value = Primitive::fromReturnedValue(RuntimeHelpers::toPrimitive(value, hint)); + Q_ASSERT(value.isPrimitive()); + if (value.isString()) + return static_cast<const String &>(value).d(); + goto redo; + } case Value::Integer_Type: return RuntimeHelpers::stringFromNumber(engine, value.int_32()); default: // double @@ -502,34 +503,9 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val // This is slightly different from the method above, as // the + operator requires a slightly different conversion -static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value &value) +static Heap::String *convert_to_string_add(ExecutionEngine *engine, Value value) { - switch (value.type()) { - case Value::Empty_Type: - Q_ASSERT(!"empty Value encountered"); - Q_UNREACHABLE(); - case Value::Undefined_Type: - return engine->id_undefined()->d(); - case Value::Null_Type: - return engine->id_null()->d(); - case Value::Boolean_Type: - if (value.booleanValue()) - return engine->id_true()->d(); - else - return engine->id_false()->d(); - case Value::Managed_Type: - if (String *s = value.stringValue()) - return s->d(); - { - Scope scope(engine); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(value, PREFERREDTYPE_HINT)); - return RuntimeHelpers::convertToString(engine, prim); - } - case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32()); - default: // double - return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); - } // switch + return RuntimeHelpers::convertToString(engine, value, PREFERREDTYPE_HINT); } QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Value &left, const Value &right) @@ -549,7 +525,7 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu pright = convert_to_string_add(engine, pright); sright = static_cast<String *>(pright.ptr); } - if (scope.engine->hasException) + if (engine->hasException) return Encode::undefined(); if (!sleft->d()->length()) return sright->asReturnedValue(); @@ -563,42 +539,14 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -QV4::ReturnedValue Runtime::method_addString(ExecutionEngine *engine, const Value &left, const Value &right) +bool Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { - Q_ASSERT(left.isString() || right.isString()); - Scope scope(engine); - ScopedValue pleft(scope, left); - ScopedValue pright(scope, right); - String *sleft = pleft->stringValue(); - String *sright = pright->stringValue(); - - if (!sleft) { - pleft = convert_to_string_add(engine, pleft); - sleft = static_cast<String *>(pleft.ptr); - } - if (!sright) { - pright = convert_to_string_add(engine, pright); - sright = static_cast<String *>(pright.ptr); - } - if (scope.engine->hasException) - return Encode::undefined(); - if (!sleft->d()->length()) - return pright->asReturnedValue(); - if (!sright->d()->length()) - return pleft->asReturnedValue(); - MemoryManager *mm = engine->memoryManager; - return (mm->alloc<String>(sleft->d(), sright->d()))->asReturnedValue(); -} - -void Runtime::method_setProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object.toObject(engine)); if (!o) - return; - o->put(name, value); + return false; + return o->put(name, value); } static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engine, const Value &object, uint idx) @@ -656,7 +604,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, return o->get(name); } -ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) { uint idx; if (index.asArrayIndex(idx)) { @@ -677,12 +625,12 @@ ReturnedValue Runtime::method_getElement(ExecutionEngine *engine, const Value &o return getElementFallback(engine, object, index); } -static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); ScopedObject o(scope, object.toObject(engine)); if (engine->hasException) - return; + return false; uint idx; if (index.asArrayIndex(idx)) { @@ -690,18 +638,17 @@ static Q_NEVER_INLINE void setElementFallback(ExecutionEngine *engine, const Val Heap::SimpleArrayData *s = o->d()->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return; + return true; } } - o->putIndexed(idx, value); - return; + return o->putIndexed(idx, value); } ScopedString name(scope, index.toString(engine)); - o->put(name, value); + return o->put(name, value); } -void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +bool Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { uint idx; if (index.asArrayIndex(idx)) { @@ -712,7 +659,7 @@ void Runtime::method_setElement(ExecutionEngine *engine, const Value &object, co Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); if (idx < s->values.size) { s->setData(engine, idx, value); - return; + return true; } } } @@ -742,17 +689,31 @@ ReturnedValue Runtime::method_foreachNextPropertyName(const Value &foreach_itera } -void Runtime::method_setActivationProperty(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) +{ + Scope scope(engine); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + + if (e == ExecutionContext::RangeError) + engine->globalObject->put(name, value); +} + +void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - engine->currentContext->setProperty(name, value); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ExecutionContext::Error e = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).setProperty(name, value); + if (e == ExecutionContext::TypeError) + engine->throwTypeError(); + else if (e == ExecutionContext::RangeError) + engine->throwReferenceError(name); } -ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject o(scope, object); if (o) @@ -769,11 +730,11 @@ ReturnedValue Runtime::method_getProperty(ExecutionEngine *engine, const Value & return o->get(name); } -ReturnedValue Runtime::method_getActivationProperty(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - return engine->currentContext->getProperty(name); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); } #endif // V4_BOOTSTRAP @@ -1002,233 +963,142 @@ uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const } -ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) { - Scope scope(engine); - Q_ASSERT(callData->thisObject.isUndefined()); - - Lookup *l = engine->current->lookups + index; - ScopedFunctionObject o(scope, l->globalGetter(l, engine)); - if (!o) + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); + if (!function.isFunctionObject()) return engine->throwTypeError(); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[l->nameIndex]); - if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - static_cast<EvalFunction *>(o.getPointer())->evalCall(scope, callData, true); - } else { - o->call(scope, callData); - } - - return scope.result.asReturnedValue(); + Value thisObject = Primitive::undefinedValue(); + return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); } - -ReturnedValue Runtime::method_callActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) { - Q_ASSERT(callData->thisObject.isUndefined()); Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue thisObject(scope); - ScopedObject base(scope); - ScopedValue func(scope, engine->currentContext->getPropertyAndBase(name, base.getRef())); - if (scope.engine->hasException) + ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); + ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject)); + if (engine->hasException) return Encode::undefined(); - if (base) - callData->thisObject = base; - - FunctionObject *o = func->as<FunctionObject>(); - if (!o) { + if (!function) { QString objectAsString = QStringLiteral("[null]"); - if (base) - objectAsString = ScopedValue(scope, base.asReturnedValue())->toQStringNoThrow(); - QString msg = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString()).arg(objectAsString); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString); return engine->throwTypeError(msg); } - if (o->d() == scope.engine->evalFunction()->d() && name->equals(scope.engine->id_eval())) { - static_cast<EvalFunction *>(o)->evalCall(scope, callData, true); - } else { - o->call(scope, callData); - } + if (function->d() == engine->evalFunction()->d()) + return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true); - return scope.result.asReturnedValue(); + return function->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) +ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) { Scope scope(engine); - ScopedFunctionObject o(scope, method_getQmlScopeObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true)); - if (!o) { - QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } + ScopedValue thisObject(scope); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - o->call(scope, callData); - return scope.result.asReturnedValue(); -} + ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); + ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject)); + if (engine->hasException) + return Encode::undefined(); -ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, int propertyIndex, CallData *callData) -{ - Scope scope(engine); - ScopedFunctionObject o(scope, method_getQmlContextObjectProperty(engine, callData->thisObject, propertyIndex, /*captureRequired*/true)); - if (!o) { - QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); + if (!f) { + QString objectAsString = QStringLiteral("[null]"); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + objectAsString); + return engine->throwTypeError(msg); } - o->call(scope, callData); - return scope.result.asReturnedValue(); + return f->call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) +ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedObject baseObject(scope, callData->thisObject); - if (!baseObject) { - Q_ASSERT(!callData->thisObject.isEmpty()); - if (callData->thisObject.isNullOrUndefined()) { - QString message = QStringLiteral("Cannot call method '%1' of %2").arg(name->toQString()).arg(callData->thisObject.toQStringNoThrow()); + + if (!base->isObject()) { + 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()); return engine->throwTypeError(message); } - baseObject = RuntimeHelpers::convertToObject(scope.engine, callData->thisObject); - if (!baseObject) // type error + ScopedValue thisObject(scope, RuntimeHelpers::convertToObject(engine, *base)); + if (engine->hasException) // type error return Encode::undefined(); - callData->thisObject = baseObject.asReturnedValue(); + base = thisObject; } - ScopedFunctionObject o(scope, baseObject->get(name)); - if (o) { - o->call(scope, callData); - return scope.result.asReturnedValue(); - } else { - QString error = QStringLiteral("Property '%1' of object %2 is not a function").arg(name->toQString(), callData->thisObject.toQStringNoThrow()); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(name)); + + if (!f) { + QString error = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + base->toQStringNoThrow()); return engine->throwTypeError(error); } + return f->call(base, argv, argc); } -ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) -{ - Lookup *l = engine->current->lookups + index; - Value v; - v = l->getter(l, engine, callData->thisObject); - Object *o = v.objectValue(); - if (Q_LIKELY(o)) { - Scope scope(engine); - o->call(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(); -} - -ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, const Value &index, CallData *callData) +ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc) { - Scope scope(engine); - ScopedObject baseObject(scope, callData->thisObject.toObject(engine)); - ScopedString s(scope, index.toString(engine)); - - if (scope.engine->hasException) - return Encode::undefined(); - callData->thisObject = baseObject; + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + // ok to have the value on the stack here + Value f = Value::fromReturnedValue(l->getter(l, engine, *base)); - ScopedObject o(scope, baseObject->get(s)); - if (!o) + if (!f.isFunctionObject()) return engine->throwTypeError(); - o->call(scope, callData); - return scope.result.asReturnedValue(); -} - -ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, CallData *callData) -{ - if (Object *o = func.objectValue()) { - Scope scope(engine); - o->call(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); + return static_cast<FunctionObject &>(f).call(base, argv, argc); } - -ReturnedValue Runtime::method_constructGlobalLookup(ExecutionEngine *engine, uint index, CallData *callData) +ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc) { Scope scope(engine); - Q_ASSERT(callData->thisObject.isUndefined()); - - Lookup *l = engine->current->lookups + index; - ScopedObject f(scope, l->globalGetter(l, engine)); - if (f) { - f->construct(scope, callData); - return scope.result.asReturnedValue(); - } else { - return engine->throwTypeError(); - } -} - + ScopedValue thisObject(scope, base->toObject(engine)); + base = thisObject; -ReturnedValue Runtime::method_constructActivationProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue func(scope, engine->currentContext->getProperty(name)); - if (scope.engine->hasException) + ScopedString str(scope, index.toString(engine)); + if (engine->hasException) return Encode::undefined(); - Object *f = func->as<Object>(); + ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str)); if (!f) return engine->throwTypeError(); - f->construct(scope, callData); - return scope.result.asReturnedValue(); + return f->call(base, argv, argc); } -ReturnedValue Runtime::method_constructValue(ExecutionEngine *engine, const Value &func, CallData *callData) +ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc) { - const Object *f = func.as<Object>(); - if (!f) - return engine->throwTypeError(); - - Scope scope(engine); - f->construct(scope, callData); - return scope.result.asReturnedValue(); + if (!func.isFunctionObject()) + return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); + return static_cast<const FunctionObject &>(func).call(nullptr, argv, argc); } -ReturnedValue Runtime::method_constructProperty(ExecutionEngine *engine, int nameIndex, CallData *callData) -{ - Scope scope(engine); - ScopedObject thisObject(scope, callData->thisObject.toObject(engine)); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedObject f(scope, thisObject->get(name)); - if (f) { - Scope scope(engine); - f->construct(scope, callData); - return scope.result.asReturnedValue(); - } else { +ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, Value *argv, int argc) +{ + if (!function.isFunctionObject()) return engine->throwTypeError(); - } -} -ReturnedValue Runtime::method_constructPropertyLookup(ExecutionEngine *engine, uint index, CallData *callData) -{ - Lookup *l = engine->current->lookups + index; - Value v; - v = l->getter(l, engine, callData->thisObject); - Object *o = v.objectValue(); - if (Q_LIKELY(o)) { - Scope scope(engine); - o->construct(scope, callData); - return scope.result.asReturnedValue(); - } - return engine->throwTypeError(); + return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc); } - void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) @@ -1267,96 +1137,37 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue prop(scope, engine->currentContext->getProperty(name)); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; return method_typeofValue(engine, prop); } -#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::method_typeofScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) -{ - Scope scope(engine); - ScopedValue prop(scope, method_getQmlScopeObjectProperty(engine, context, propertyIndex, /*captureRequired*/true)); - if (scope.engine->hasException) - return Encode::undefined(); - return method_typeofValue(engine, prop); -} - -ReturnedValue Runtime::method_typeofContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex) -{ - Scope scope(engine); - ScopedValue prop(scope, method_getQmlContextObjectProperty(engine, context, propertyIndex, /*captureRequired*/true)); - if (scope.engine->hasException) - return Encode::undefined(); - return method_typeofValue(engine, prop); -} -#endif // V4_BOOTSTRAP - -QV4::ReturnedValue Runtime::method_typeofMember(ExecutionEngine *engine, const Value &base, int nameIndex) -{ - Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - ScopedObject obj(scope, base.toObject(engine)); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedValue prop(scope, obj->get(name)); - return method_typeofValue(engine, prop); -} - -QV4::ReturnedValue Runtime::method_typeofElement(ExecutionEngine *engine, const Value &base, const Value &index) -{ - Scope scope(engine); - ScopedString name(scope, index.toString(engine)); - ScopedObject obj(scope, base.toObject(engine)); - if (scope.engine->hasException) - return Encode::undefined(); - ScopedValue prop(scope, obj->get(name)); - return method_typeofValue(engine, prop); -} - -ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine) -{ - if (!engine->hasException) - return Primitive::emptyValue().asReturnedValue(); - return engine->catchException(0); -} - /* 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. */ -void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine) -{ - QV4::Value *v = engine->jsAlloca(1); - Heap::Object *withObject = o.toObject(engine); - *v = withObject; - engine->pushContext(engine->currentContext->newWithContext(withObject)); - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); -} - -void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) +ReturnedValue Runtime::method_createWithContext(ExecutionContext *parent, const Value &o) { - engine->jsAlloca(1); // keep this symmetric with pushWithScope - ExecutionContext *c = engine->currentContext; - engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); + Q_ASSERT(o.isObject()); + const Object &obj = static_cast<const Object &>(o); + return parent->newWithContext(obj.d())->asReturnedValue(); } -void Runtime::method_popScope(NoThrowEngine *engine) +ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int exceptionVarNameIndex) { - Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); - engine->popContext(); - engine->jsStackTop -= 3; + ExecutionEngine *e = parent->engine(); + return parent->newCatchContext(e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex], + e->catchException(0))->asReturnedValue(); } void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); - engine->currentContext->createMutableBinding(name, deletable); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); } ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) @@ -1367,7 +1178,7 @@ ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *value ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4::Value *args, int classId, int arrayValueCount, int arrayGetterSetterCountAndFlags) { Scope scope(engine); - QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->runtimeClasses[classId]; + QV4::InternalClass *klass = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeClasses[classId]; ScopedObject o(scope, engine->newObject(klass, engine->objectPrototype())); { @@ -1406,134 +1217,49 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, const QV4:: return o.asReturnedValue(); } -QV4::ReturnedValue Runtime::method_setupArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) { - Q_ASSERT(engine->current->type == Heap::ExecutionContext::Type_CallContext); - QV4::CallContext *c = static_cast<QV4::CallContext *>(engine->currentContext); - QV4::InternalClass *ic = engine->internalClasses[c->d()->strictMode ? EngineBase::Class_StrictArgumentsObject : EngineBase::Class_ArgumentsObject]; - return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c)->asReturnedValue(); + Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); + QV4::CallContext *c = static_cast<QV4::CallContext *>(&engine->currentStackFrame->jsFrame->context); + QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_ArgumentsObject]; + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c, false)->asReturnedValue(); } -#endif // V4_BOOTSTRAP - -QV4::ReturnedValue Runtime::method_increment(const Value &value) +QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) { - TRACE1(value); - - if (value.isInteger() && value.integerValue() < INT_MAX) - return Encode(value.integerValue() + 1); - else { - double d = value.toNumber(); - return Encode(d + 1.); - } -} - -QV4::ReturnedValue Runtime::method_decrement(const Value &value) -{ - TRACE1(value); - - if (value.isInteger() && value.integerValue() > INT_MIN) - return Encode(value.integerValue() - 1); - else { - double d = value.toNumber(); - return Encode(d - 1.); - } -} - -ReturnedValue Runtime::method_toDouble(const Value &value) -{ - TRACE1(value); - return Encode(value.toNumber()); -} - -int Runtime::method_toInt(const Value &value) -{ - TRACE1(value); - return value.toInt32(); -} - -int Runtime::method_doubleToInt(const double &d) -{ - TRACE0(); - return Primitive::toInt32(d); -} - -unsigned Runtime::method_toUInt(const Value &value) -{ - TRACE1(value); - return value.toUInt32(); -} - -unsigned Runtime::method_doubleToUInt(const double &d) -{ - TRACE0(); - return Primitive::toUInt32(d); + Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); + QV4::CallContext *c = static_cast<QV4::CallContext *>(&engine->currentStackFrame->jsFrame->context); + QV4::InternalClass *ic = engine->internalClasses[EngineBase::Class_StrictArgumentsObject]; + return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->objectPrototype(), c, true)->asReturnedValue(); } -#ifndef V4_BOOTSTRAP - -ReturnedValue Runtime::method_getQmlContext(NoThrowEngine *engine) +ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) { return engine->qmlContext()->asReturnedValue(); } ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject( - static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit) - ->runtimeRegularExpressions[id].as<RegExp>()); + Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); return ro->asReturnedValue(); } -ReturnedValue Runtime::method_getQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) -{ - Scope scope(engine); - QV4::Scoped<QObjectWrapper> wrapper(scope, object); - if (!wrapper) { - engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); - } - return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->object(), propertyIndex, captureRequired); -} - -QV4::ReturnedValue Runtime::method_getQmlAttachedProperty(ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex) -{ - QObject *scopeObject = engine->qmlScopeObject(); - QObject *attachedObject = qmlAttachedPropertiesObjectById(attachedPropertiesId, scopeObject); - - QJSEngine *jsEngine = engine->jsEngine(); - QQmlData::ensurePropertyCache(jsEngine, attachedObject); - return QV4::QObjectWrapper::getProperty(engine, attachedObject, propertyIndex, /*captureRequired*/true); -} - -ReturnedValue Runtime::method_getQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) +ReturnedValue Runtime::method_loadQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, c.d()->qml->scopeObject, propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired); } -ReturnedValue Runtime::method_getQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) +ReturnedValue Runtime::method_loadQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::method_getQmlSingletonQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired) -{ - Scope scope(engine); - QV4::Scoped<QQmlTypeWrapper> wrapper(scope, object); - if (!wrapper) { - scope.engine->throwTypeError(QStringLiteral("Cannot read property of null")); - return Encode::undefined(); - } - return QV4::QObjectWrapper::getProperty(scope.engine, wrapper->singletonObject(), propertyIndex, captureRequired); + return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired); } -ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) +ReturnedValue Runtime::method_loadQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) { - Scope scope(engine); const QmlContext &qmlContext = static_cast<const QmlContext &>(c); - QQmlContextData *context = *qmlContext.d()->qml->context; + QQmlContextData *context = *qmlContext.d()->qml()->context; if (!context || index >= (uint)context->idValueCount) return Encode::undefined(); @@ -1544,30 +1270,19 @@ ReturnedValue Runtime::method_getQmlIdObject(ExecutionEngine *engine, const Valu return QObjectWrapper::wrap(engine, context->idValues[index].data()); } -void Runtime::method_setQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +void Runtime::method_storeQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, c.d()->qml->scopeObject, propertyIndex, value); + return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value); } -void Runtime::method_setQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) +void Runtime::method_storeQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) { const QmlContext &c = static_cast<const QmlContext &>(context); - return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml->context)->contextObject, propertyIndex, value); + return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value); } -void Runtime::method_setQmlQObjectProperty(ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value) -{ - Scope scope(engine); - QV4::Scoped<QObjectWrapper> wrapper(scope, object); - if (!wrapper) { - engine->throwTypeError(QStringLiteral("Cannot write property of null")); - return; - } - wrapper->setProperty(engine, propertyIndex, value); -} - -ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine) +ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine) { QQmlContextData *context = engine->callingQmlContext(); if (!context) @@ -1575,44 +1290,20 @@ ReturnedValue Runtime::method_getQmlImportedScripts(NoThrowEngine *engine) return context->importedScripts.value(); } -QV4::ReturnedValue Runtime::method_getQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::method_loadQmlSingleton(QV4::NoThrowEngine *engine, int nameIndex) { Scope scope(engine); - ScopedString name(scope, engine->current->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); return engine->qmlSingletonWrapper(name); } -void Runtime::method_convertThisToObject(ExecutionEngine *engine) -{ - Value *t = &engine->current->callData->thisObject; - if (t->isObject()) - return; - if (t->isNullOrUndefined()) { - *t = engine->globalObject->asReturnedValue(); - } else { - *t = t->toObject(engine)->asReturnedValue(); - } -} - -ReturnedValue Runtime::method_uPlus(const Value &value) -{ - TRACE1(value); - - if (value.isNumber()) - return value.asReturnedValue(); - if (value.integerCompatible()) - return Encode(value.int_32()); - - double n = value.toNumberImpl(); - return Encode(n); -} - ReturnedValue Runtime::method_uMinus(const Value &value) { TRACE1(value); // +0 != -0, so we need to convert to double when negating 0 - if (value.isInteger() && value.integerValue()) + if (value.isInteger() && value.integerValue() && + value.integerValue() != std::numeric_limits<int>::min()) return Encode(-value.integerValue()); else { double n = RuntimeHelpers::toNumber(value); @@ -1620,49 +1311,7 @@ ReturnedValue Runtime::method_uMinus(const Value &value) } } -ReturnedValue Runtime::method_complement(const Value &value) -{ - TRACE1(value); - - int n = value.toInt32(); - return Encode((int)~n); -} - -ReturnedValue Runtime::method_uNot(const Value &value) -{ - TRACE1(value); - - bool b = value.toBoolean(); - return Encode(!b); -} - // binary operators -ReturnedValue Runtime::method_bitOr(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval | rval); -} - -ReturnedValue Runtime::method_bitXor(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval ^ rval); -} - -ReturnedValue Runtime::method_bitAnd(const Value &left, const Value &right) -{ - TRACE2(left, right); - - int lval = left.toInt32(); - int rval = right.toInt32(); - return Encode(lval & rval); -} #ifndef V4_BOOTSTRAP ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) @@ -1878,11 +1527,6 @@ Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right return ! RuntimeHelpers::strictEqual(left, right); } -Bool Runtime::method_toBoolean(const Value &value) -{ - return value.toBoolean(); -} - } // namespace QV4 QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index 0d787714cf..3a26c23990 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -83,8 +83,8 @@ private: }; # define TRACE0() RuntimeCounters::instance->count(Q_FUNC_INFO); -# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type()); -# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x->type(), y->type()); +# define TRACE1(x) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type()); +# define TRACE2(x, y) RuntimeCounters::instance->count(Q_FUNC_INFO, x.type(), y.type()); #else # define TRACE0() # define TRACE1(x) @@ -99,14 +99,14 @@ enum TypeHint { struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { static ReturnedValue objectDefaultValue(const Object *object, int typeHint); - static ReturnedValue toPrimitive(const Value &value, int typeHint); + static ReturnedValue toPrimitive(const Value &value, TypeHint typeHint); static double stringToNumber(const QString &s); static Heap::String *stringFromNumber(ExecutionEngine *engine, double number); static double toNumber(const Value &value); static void numberToString(QString *result, double num, int radix = 10); - static Heap::String *convertToString(ExecutionEngine *engine, const Value &value); + static Heap::String *convertToString(ExecutionEngine *engine, Value value, TypeHint = STRING_HINT); static Heap::Object *convertToObject(ExecutionEngine *engine, const Value &value); static Bool equalHelper(const Value &x, const Value &y); @@ -118,12 +118,11 @@ struct Q_QML_PRIVATE_EXPORT RuntimeHelpers { // type conversion and testing #ifndef V4_BOOTSTRAP -inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, int typeHint) +inline ReturnedValue RuntimeHelpers::toPrimitive(const Value &value, TypeHint typeHint) { - const Object *o = value.as<Object>(); - if (!o) + if (!value.isObject()) return value.asReturnedValue(); - return RuntimeHelpers::objectDefaultValue(o, typeHint); + return RuntimeHelpers::objectDefaultValue(&reinterpret_cast<const Object &>(value), typeHint); } #endif diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 302facba06..ea31dfd08b 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -58,7 +58,6 @@ namespace QV4 { typedef uint Bool; struct NoThrowEngine; - namespace { template <typename T> struct ExceptionCheck { @@ -93,84 +92,64 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { #define FOR_EACH_RUNTIME_METHOD(F) \ /* call */ \ - F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, callActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ - F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, int propertyIndex, CallData *callData)) \ - F(ReturnedValue, callProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, callElement, (ExecutionEngine *engine, const Value &index, CallData *callData)) \ - F(ReturnedValue, callValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ + F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \ + F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \ + F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \ + 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)) \ \ /* construct */ \ - F(ReturnedValue, constructGlobalLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, constructActivationProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, constructProperty, (ExecutionEngine *engine, int nameIndex, CallData *callData)) \ - F(ReturnedValue, constructPropertyLookup, (ExecutionEngine *engine, uint index, CallData *callData)) \ - F(ReturnedValue, constructValue, (ExecutionEngine *engine, const Value &func, CallData *callData)) \ + F(ReturnedValue, construct, (ExecutionEngine *engine, const Value &func, Value *argv, int argc)) \ \ - /* set & get */ \ - F(void, setActivationProperty, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ - F(void, setProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ - F(void, setElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ - F(ReturnedValue, getProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ - F(ReturnedValue, getActivationProperty, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, getElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + /* load & store */ \ + F(void, storeNameStrict, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ + F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ + F(bool, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ + F(bool, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ + F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ + F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ \ /* typeof */ \ F(ReturnedValue, typeofValue, (ExecutionEngine *engine, const Value &val)) \ F(ReturnedValue, typeofName, (ExecutionEngine *engine, int nameIndex)) \ - F(ReturnedValue, typeofScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ - F(ReturnedValue, typeofContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex)) \ - F(ReturnedValue, typeofMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(ReturnedValue, typeofElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ \ /* delete */ \ - F(ReturnedValue, deleteElement, (ExecutionEngine *engine, const Value &base, const Value &index)) \ - F(ReturnedValue, deleteMember, (ExecutionEngine *engine, const Value &base, int nameIndex)) \ - F(ReturnedValue, deleteMemberString, (ExecutionEngine *engine, const Value &base, String *name)) \ - F(ReturnedValue, deleteName, (ExecutionEngine *engine, int nameIndex)) \ + 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, deleteName, (ExecutionEngine *engine, int nameIndex)) \ \ /* exceptions & scopes */ \ F(void, throwException, (ExecutionEngine *engine, const Value &value)) \ - F(ReturnedValue, unwindException, (ExecutionEngine *engine)) \ - F(void, pushWithScope, (const Value &o, NoThrowEngine *engine)) \ - F(void, pushCatchScope, (NoThrowEngine *engine, int exceptionVarNameIndex)) \ - F(void, popScope, (NoThrowEngine *engine)) \ + F(ReturnedValue, createWithContext, (ExecutionContext *parent, const Value &o)) \ + F(ReturnedValue, createCatchContext, (ExecutionContext *parent, int exceptionVarNameIndex)) \ \ /* closures */ \ F(ReturnedValue, closure, (ExecutionEngine *engine, int functionId)) \ \ /* function header */ \ F(void, declareVar, (ExecutionEngine *engine, bool deletable, int nameIndex)) \ - F(ReturnedValue, setupArgumentsObject, (ExecutionEngine *engine)) \ - F(void, convertThisToObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createMappedArgumentsObject, (ExecutionEngine *engine)) \ + F(ReturnedValue, createUnmappedArgumentsObject, (ExecutionEngine *engine)) \ \ /* 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, regexpLiteral, (ExecutionEngine *engine, int id)) \ \ /* foreach */ \ F(ReturnedValue, foreachIterator, (ExecutionEngine *engine, const Value &in)) \ F(ReturnedValue, foreachNextPropertyName, (const Value &foreach_iterator)) \ \ /* unary operators */ \ - F(ReturnedValue, uPlus, (const Value &value)) \ F(ReturnedValue, uMinus, (const Value &value)) \ - F(ReturnedValue, uNot, (const Value &value)) \ - F(ReturnedValue, complement, (const Value &value)) \ - F(ReturnedValue, increment, (const Value &value)) \ - F(ReturnedValue, decrement, (const Value &value)) \ \ /* binary operators */ \ F(ReturnedValue, instanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(ReturnedValue, in, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(ReturnedValue, add, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, addString, (ExecutionEngine *engine, const Value &left, const Value &right)) \ - F(ReturnedValue, bitOr, (const Value &left, const Value &right)) \ - F(ReturnedValue, bitXor, (const Value &left, const Value &right)) \ - F(ReturnedValue, bitAnd, (const Value &left, const Value &right)) \ F(ReturnedValue, sub, (const Value &left, const Value &right)) \ F(ReturnedValue, mul, (const Value &left, const Value &right)) \ F(ReturnedValue, div, (const Value &left, const Value &right)) \ @@ -200,28 +179,18 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ \ - /* conversions */ \ - F(Bool, toBoolean, (const Value &value)) \ - F(ReturnedValue, toDouble, (const Value &value)) \ - F(int, toInt, (const Value &value)) \ - F(int, doubleToInt, (const double &d)) \ - F(unsigned, toUInt, (const Value &value)) \ - F(unsigned, doubleToUInt, (const double &d)) \ + F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \ \ /* qml */ \ - F(ReturnedValue, getQmlContext, (NoThrowEngine *engine)) \ - F(ReturnedValue, getQmlImportedScripts, (NoThrowEngine *engine)) \ - F(ReturnedValue, getQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ - F(ReturnedValue, getQmlAttachedProperty, (ExecutionEngine *engine, int attachedPropertiesId, int propertyIndex)) \ - F(ReturnedValue, getQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlSingletonQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, getQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ - \ - F(void, setQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ - F(void, setQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ - F(void, setQmlQObjectProperty, (ExecutionEngine *engine, const Value &object, int propertyIndex, const Value &value)) + F(ReturnedValue, loadQmlContext, (NoThrowEngine *engine)) \ + F(ReturnedValue, loadQmlImportedScripts, (NoThrowEngine *engine)) \ + F(ReturnedValue, loadQmlSingleton, (NoThrowEngine *engine, int nameIndex)) \ + F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ + F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ + \ + F(void, storeQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ + F(void, storeQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ struct Q_QML_PRIVATE_EXPORT Runtime { Runtime(); diff --git a/src/qml/jsruntime/qv4runtimecodegen.cpp b/src/qml/jsruntime/qv4runtimecodegen.cpp new file mode 100644 index 0000000000..9c115099b5 --- /dev/null +++ b/src/qml/jsruntime/qv4runtimecodegen.cpp @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "qv4runtimecodegen_p.h" +#include "qv4compilerscanfunctions_p.h" + +using namespace QV4; + +void RuntimeCodegen::generateFromFunctionExpression(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + Compiler::Module *module) +{ + _module = module; + _module->fileName = fileName; + _context = 0; + + Compiler::ScanFunctions scan(this, sourceCode, Compiler::GlobalCode); + // fake a global environment + scan.enterEnvironment(0, Compiler::FunctionCode); + scan(ast); + scan.leaveEnvironment(); + + int index = defineFunction(ast->name.toString(), ast, ast->formals, ast->body ? ast->body->elements : 0); + _module->rootContext = _module->functions.at(index); +} + +void RuntimeCodegen::throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) +{ + if (hasError) + return; + hasError = true; + engine->throwSyntaxError(detail, _module->fileName, loc.startLine, loc.startColumn); +} + +void RuntimeCodegen::throwReferenceError(const AST::SourceLocation &loc, const QString &detail) +{ + if (hasError) + return; + hasError = true; + engine->throwReferenceError(detail, _module->fileName, loc.startLine, loc.startColumn); +} + diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h new file mode 100644 index 0000000000..be66dc57ca --- /dev/null +++ b/src/qml/jsruntime/qv4runtimecodegen_p.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 QV4RUNTIMECODEGEN_P_H +#define QV4RUNTIMECODEGEN_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 <private/qv4codegen_p.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +class RuntimeCodegen : public Compiler::Codegen +{ +public: + RuntimeCodegen(ExecutionEngine *engine, Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) + : Codegen(jsUnitGenerator, strict) + , engine(engine) + {} + + void generateFromFunctionExpression(const QString &fileName, + const QString &sourceCode, + AST::FunctionExpression *ast, + Compiler::Module *module); + + void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override; + void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override; +private: + ExecutionEngine *engine; +}; + +} + +QT_END_NAMESPACE + +#endif // QV4CODEGEN_P_H diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 04a0c74133..afb5c21d36 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -52,7 +52,6 @@ #include "qv4engine_p.h" #include "qv4value_p.h" -#include "qv4persistent_p.h" #include "qv4property_p.h" #ifdef V4_USE_VALGRIND @@ -71,56 +70,45 @@ struct ScopedValue; #define CHECK_EXCEPTION() \ do { \ if (scope.hasException()) { \ - scope.result = QV4::Encode::undefined(); \ - return; \ + return QV4::Encode::undefined(); \ } \ } while (false) #define RETURN_UNDEFINED() \ - do { \ - scope.result = QV4::Encode::undefined(); \ - return; \ - } while (false) + return QV4::Encode::undefined() #define RETURN_RESULT(r) \ - do { \ - scope.result = r; \ - return; \ - } while (false) + return QV4::Encode(r) #define THROW_TYPE_ERROR() \ - do { \ - scope.result = scope.engine->throwTypeError(); \ - return; \ - } while (false) + return scope.engine->throwTypeError() #define THROW_GENERIC_ERROR(str) \ - do { \ - scope.result = scope.engine->throwError(QString::fromUtf8(str)); \ - return; \ - } while (false) + return scope.engine->throwError(QString::fromUtf8(str)) struct Scope { - inline Scope(ExecutionContext *ctx) + explicit Scope(ExecutionContext *ctx) : engine(ctx->engine()) , mark(engine->jsStackTop) - , result(*engine->jsAlloca(1)) { - result = Encode::undefined(); } explicit Scope(ExecutionEngine *e) : engine(e) , mark(engine->jsStackTop) - , result(*engine->jsAlloca(1)) { - result = Encode::undefined(); + } + + explicit Scope(const Managed *m) + : engine(m->engine()) + , mark(engine->jsStackTop) + { } ~Scope() { #ifndef QT_NO_DEBUG Q_ASSERT(engine->jsStackTop >= mark); - Q_ASSERT(engine->currentContext < mark); +// Q_ASSERT(engine->currentContext < mark); memset(mark, 0, (engine->jsStackTop - mark)*sizeof(Value)); #endif #ifdef V4_USE_VALGRIND @@ -139,7 +127,6 @@ struct Scope { ExecutionEngine *engine; Value *mark; - Value &result; private: Q_DISABLE_COPY(Scope) @@ -363,27 +350,6 @@ struct Scoped Value *ptr; }; -struct ScopedCallData { - ScopedCallData(const Scope &scope, int argc = 0) - { - int size = int(offsetof(QV4::CallData, args)/sizeof(QV4::Value)) + qMax(argc , int(QV4::Global::ReservedArgumentCount)); - ptr = reinterpret_cast<CallData *>(scope.alloc(size)); - ptr->tag = quint32(QV4::Value::ValueTypeInternal::Integer); - ptr->argc = argc; - } - - CallData *operator->() { - return ptr; - } - - operator CallData *() const { - return ptr; - } - - CallData *ptr; -}; - - inline Value &Value::operator =(const ScopedValue &v) { _val = v.ptr->rawValue(); @@ -411,25 +377,6 @@ struct ScopedProperty Property *property; }; -struct ExecutionContextSaver -{ - Scope scope; // this makes sure that a reference to context on the JS stack goes out of scope as soon as the context is not used anymore. - ExecutionContext *savedContext; - - ExecutionContextSaver(const Scope &scope) - : scope(scope.engine) - { - savedContext = scope.engine->currentContext; - } - ~ExecutionContextSaver() - { - Q_ASSERT(scope.engine->jsStackTop > scope.engine->currentContext); - scope.engine->currentContext = savedContext; - scope.engine->current = savedContext->d(); - } -}; - - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 62145f36cc..c3b19ac77c 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -45,6 +45,7 @@ #include "qv4debugging_p.h" #include "qv4profiling_p.h" #include "qv4scopedvalue_p.h" +#include "qv4jscall_p.h" #include <private/qqmljsengine_p.h> #include <private/qqmljslexer_p.h> @@ -52,8 +53,7 @@ #include <private/qqmljsast_p.h> #include <private/qqmlengine_p.h> #include <private/qv4profiling_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> +#include <qv4runtimecodegen_p.h> #include <QtCore/QDebug> #include <QtCore/QString> @@ -61,7 +61,7 @@ using namespace QV4; Script::Script(ExecutionEngine *v4, QmlContext *qml, CompiledData::CompilationUnit *compilationUnit) - : line(0), column(0), scope(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) + : line(1), column(0), context(v4->rootContext()), strictMode(false), inheritContext(true), parsed(false) , compilationUnit(compilationUnit), vmFunction(0), parseAsBinding(true) { if (qml) @@ -81,16 +81,16 @@ void Script::parse() if (parsed) return; - using namespace QQmlJS; + using namespace QV4::Compiler; parsed = true; - ExecutionEngine *v4 = scope->engine(); + ExecutionEngine *v4 = context->engine(); Scope valueScope(v4); - IR::Module module(v4->debugger() != 0); + Module module(v4->debugger() != 0); - QQmlJS::Engine ee, *engine = ⅇ + Engine ee, *engine = ⅇ Lexer lexer(engine); lexer.setCode(sourceCode, line, parseAsBinding); Parser parser(engine); @@ -98,7 +98,7 @@ void Script::parse() const bool parsed = parser.parseProgram(); const auto diagnosticMessages = parser.diagnosticMessages(); - for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { + for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isError()) { valueScope.engine->throwSyntaxError(m.message, sourceFile, m.loc.startLine, m.loc.startColumn); return; @@ -117,25 +117,15 @@ void Script::parse() return; } - QStringList inheritedLocals; - if (inheritContext) { - Scoped<CallContext> ctx(valueScope, scope); - if (ctx) { - for (Identifier * const *i = ctx->variables(), * const *ei = i + ctx->variableCount(); i < ei; ++i) - inheritedLocals.append(*i ? (*i)->string : QString()); - } - } - - RuntimeCodegen cg(v4, strictMode); - cg.generateFromProgram(sourceFile, sourceCode, program, &module, QQmlJS::Codegen::EvalCode, inheritedLocals); + QV4::Compiler::JSUnitGenerator jsGenerator(&module); + RuntimeCodegen cg(v4, &jsGenerator, strictMode); + if (inheritContext) + cg.setUseFastLookups(false); + cg.generateFromProgram(sourceFile, sourceCode, program, &module, compilationMode); if (v4->hasException) return; - QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); - if (inheritContext) - isel->setUseFastLookups(false); - compilationUnit = isel->compile(); + compilationUnit = cg.generateCompilationUnit(); vmFunction = compilationUnit->linkToEngine(v4); } @@ -153,29 +143,26 @@ ReturnedValue Script::run() if (!vmFunction) return Encode::undefined(); - QV4::ExecutionEngine *engine = scope->engine(); + QV4::ExecutionEngine *engine = context->engine(); QV4::Scope valueScope(engine); if (qmlContext.isUndefined()) { TemporaryAssignment<Function*> savedGlobalCode(engine->globalCode, vmFunction); - ExecutionContextSaver ctxSaver(valueScope); - ContextStateSaver stateSaver(valueScope, scope); - scope->d()->strictMode = vmFunction->isStrict(); - scope->d()->lookups = vmFunction->compilationUnit->runtimeLookups; - scope->d()->constantTable = vmFunction->compilationUnit->constants; - scope->d()->compilationUnit = vmFunction->compilationUnit; + ContextStateSaver stateSaver(valueScope, context); + context->d()->v4Function = vmFunction; - return Q_V4_PROFILE(engine, vmFunction); + QV4::JSCallData jsCall(valueScope); + jsCall->thisObject = engine->globalObject; + QV4::CallData *cData = jsCall.callData(); + cData->context = *context; + return vmFunction->call(cData); } else { Scoped<QmlContext> qml(valueScope, qmlContext.value()); - ScopedCallData callData(valueScope); - callData->thisObject = Primitive::undefinedValue(); - if (vmFunction->canUseSimpleFunction()) - qml->simpleCall(valueScope, callData, vmFunction); - else - qml->call(valueScope, callData, vmFunction); - return valueScope.result.asReturnedValue(); + JSCallData jsCall(valueScope); + QV4::CallData *cData = jsCall.callData(); + cData->context = *qml; + return vmFunction->call(cData); } } @@ -186,24 +173,26 @@ Function *Script::function() return vmFunction; } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, QQmlJS::Directives *directivesCollector) +QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, + const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors, + Directives *directivesCollector) { - using namespace QQmlJS; + using namespace QV4::Compiler; using namespace QQmlJS::AST; - QQmlJS::Engine ee; + Engine ee; if (directivesCollector) ee.setDirectives(directivesCollector); - QQmlJS::Lexer lexer(&ee); + Lexer lexer(&ee); lexer.setCode(source, /*line*/1, /*qml mode*/false); - QQmlJS::Parser parser(&ee); + Parser parser(&ee); parser.parseProgram(); QList<QQmlError> errors; const auto diagnosticMessages = parser.diagnosticMessages(); - for (const QQmlJS::DiagnosticMessage &m : diagnosticMessages) { + for (const DiagnosticMessage &m : diagnosticMessages) { if (m.isWarning()) { qWarning("%s:%d : %s", qPrintable(url.toString()), m.loc.startLine, qPrintable(m.message)); continue; @@ -230,8 +219,9 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module return 0; } - QQmlJS::Codegen cg(/*strict mode*/false); - cg.generateFromProgram(url.toString(), source, program, module, QQmlJS::Codegen::EvalCode); + Codegen cg(unitGenerator, /*strict mode*/false); + cg.setUseFastLookups(false); + cg.generateFromProgram(url.toString(), source, program, module, GlobalCode); errors = cg.qmlErrors(); if (!errors.isEmpty()) { if (reportedErrors) @@ -239,9 +229,7 @@ QQmlRefPointer<QV4::CompiledData::CompilationUnit> Script::precompile(IR::Module return 0; } - QScopedPointer<EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, module, unitGenerator)); - isel->setUseFastLookups(false); - return isel->compile(/*generate unit data*/false); + return cg.generateCompilationUnit(/*generate unit data*/false); } QV4::ReturnedValue Script::evaluate(ExecutionEngine *engine, const QString &script, QmlContext *qmlContext) diff --git a/src/qml/jsruntime/qv4script_p.h b/src/qml/jsruntime/qv4script_p.h index 4ebe2dd609..f3a4853fa3 100644 --- a/src/qml/jsruntime/qv4script_p.h +++ b/src/qml/jsruntime/qv4script_p.h @@ -54,6 +54,7 @@ #include "qv4engine_p.h" #include "qv4functionobject_p.h" #include "qv4qmlcontext_p.h" +#include "private/qv4compilercontext_p.h" #include <QQmlError> @@ -61,37 +62,21 @@ QT_BEGIN_NAMESPACE class QQmlContextData; -namespace QQmlJS { -class Directives; -} - namespace QV4 { struct ContextStateSaver { Value *savedContext; - bool strictMode; - Lookup *lookups; - const QV4::Value *constantTable; - CompiledData::CompilationUnitBase *compilationUnit; - int lineNumber; + QV4::Function *v4Function; ContextStateSaver(const Scope &scope, ExecutionContext *context) : savedContext(scope.alloc(1)) - , strictMode(context->d()->strictMode) - , lookups(context->d()->lookups) - , constantTable(context->d()->constantTable) - , compilationUnit(context->d()->compilationUnit) - , lineNumber(context->d()->lineNumber) + , v4Function(context->d()->v4Function) { savedContext->setM(context->d()); } ContextStateSaver(const Scope &scope, Heap::ExecutionContext *context) : savedContext(scope.alloc(1)) - , strictMode(context->strictMode) - , lookups(context->lookups) - , constantTable(context->constantTable) - , compilationUnit(context->compilationUnit) - , lineNumber(context->lineNumber) + , v4Function(context->v4Function) { savedContext->setM(context); } @@ -99,22 +84,18 @@ struct ContextStateSaver { ~ContextStateSaver() { Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(savedContext->m()); - ctx->strictMode = strictMode; - ctx->lookups = lookups; - ctx->constantTable = constantTable; - ctx->compilationUnit = compilationUnit; - ctx->lineNumber = lineNumber; + ctx->v4Function = v4Function; } }; struct Q_QML_EXPORT Script { - Script(ExecutionContext *scope, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) + Script(ExecutionContext *scope, QV4::Compiler::CompilationMode mode, const QString &sourceCode, const QString &source = QString(), int line = 1, int column = 0) : sourceFile(source), line(line), column(column), sourceCode(sourceCode) - , scope(scope), strictMode(false), inheritContext(false), parsed(false) + , context(scope), strictMode(false), inheritContext(false), parsed(false), compilationMode(mode) , vmFunction(0), 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) - , scope(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) + , context(engine->rootContext()), strictMode(false), inheritContext(true), parsed(false) , vmFunction(0), parseAsBinding(true) { if (qml) qmlContext.set(engine, *qml); @@ -125,10 +106,11 @@ struct Q_QML_EXPORT Script { int line; int column; QString sourceCode; - ExecutionContext *scope; + ExecutionContext *context; bool strictMode; bool inheritContext; bool parsed; + QV4::Compiler::CompilationMode compilationMode = QV4::Compiler::EvalCode; QV4::PersistentValue qmlContext; QQmlRefPointer<CompiledData::CompilationUnit> compilationUnit; Function *vmFunction; @@ -139,7 +121,7 @@ struct Q_QML_EXPORT Script { Function *function(); - static QQmlRefPointer<CompiledData::CompilationUnit> precompile(IR::Module *module, Compiler::JSUnitGenerator *unitGenerator, ExecutionEngine *engine, const QUrl &url, const QString &source, + static QQmlRefPointer<CompiledData::CompilationUnit> precompile(QV4::Compiler::Module *module, Compiler::JSUnitGenerator *unitGenerator, const QUrl &url, const QString &source, QList<QQmlError> *reportedErrors = 0, QQmlJS::Directives *directivesCollector = 0); 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 8afc672aa2..78cd7529d8 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -45,6 +45,7 @@ #include <private/qv4arrayobject_p.h> #include <private/qqmlengine_p.h> #include <private/qv4scopedvalue_p.h> +#include <private/qv4jscall_p.h> #include "qv4runtime_p.h" #include "qv4objectiterator_p.h" #include <private/qqmlvaluetypewrapper_p.h> @@ -66,10 +67,10 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description QQmlError retn; retn.setDescription(description); - QV4::StackFrame frame = v4->currentStackFrame(); + QV4::CppStackFrame *stackFrame = v4->currentStackFrame; - retn.setLine(frame.line); - retn.setUrl(QUrl(frame.source)); + retn.setLine(stackFrame->lineNumber()); + retn.setUrl(QUrl(stackFrame->source())); QQmlEnginePrivate::warning(engine, retn); } @@ -416,13 +417,15 @@ public: bool operator()(typename Container::value_type lhs, typename Container::value_type rhs) { QV4::Scope scope(m_v4); - ScopedObject compare(scope, m_compareFn); - ScopedCallData callData(scope, 2); - callData->args[0] = convertElementToValue(m_v4, lhs); - callData->args[1] = convertElementToValue(m_v4, rhs); - callData->thisObject = m_v4->globalObject; - compare->call(scope, callData); - return scope.result.toNumber() < 0; + ScopedFunctionObject compare(scope, m_compareFn); + if (!compare) + return m_v4->throwTypeError(); + JSCallData jsCallData(scope, 2); + jsCallData->args[0] = convertElementToValue(m_v4, lhs); + jsCallData->args[1] = convertElementToValue(m_v4, rhs); + *jsCallData->thisObject = m_v4->globalObject; + QV4::ScopedValue result(scope, compare->call(jsCallData)); + return result->toNumber() < 0; } private: @@ -438,7 +441,7 @@ public: loadReference(); } - if (callData->argc == 1 && callData->args[0].as<FunctionObject>()) { + if (callData->argc() == 1 && callData->args[0].as<FunctionObject>()) { CompareFunctor cf(scope.engine, callData->args[0]); std::sort(d()->container->begin(), d()->container->end(), cf); } else { @@ -450,8 +453,9 @@ public: storeReference(); } - static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) + static QV4::ReturnedValue method_get_length(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) THROW_TYPE_ERROR(); @@ -464,8 +468,9 @@ public: RETURN_RESULT(Encode(qint32(This->d()->container->size()))); } - static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData) + static QV4::ReturnedValue method_set_length(const BuiltinFunction *b, CallData *callData) { + QV4::Scope scope(b); QV4::Scoped<QQmlSequence<Container> > This(scope, callData->thisObject.as<QQmlSequence<Container> >()); if (!This) THROW_TYPE_ERROR(); @@ -647,14 +652,20 @@ void SequencePrototype::init() } #undef REGISTER_QML_SEQUENCE_METATYPE -void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue SequencePrototype::method_valueOf(const BuiltinFunction *f, CallData *callData) { + return Encode(callData->thisObject.toString(f->engine())); +} + +ReturnedValue SequencePrototype::method_sort(const BuiltinFunction *b, CallData *callData) +{ + Scope scope(b); QV4::ScopedObject o(scope, callData->thisObject); if (!o || !o->isListType()) THROW_TYPE_ERROR(); - if (callData->argc >= 2) - RETURN_RESULT(o); + if (callData->argc() >= 2) + return o.asReturnedValue(); #define CALL_SORT(SequenceElementType, SequenceElementTypeName, SequenceType, DefaultValue) \ if (QQml##SequenceElementTypeName##List *s = o->as<QQml##SequenceElementTypeName##List>()) { \ @@ -665,7 +676,7 @@ void SequencePrototype::method_sort(const BuiltinFunction *b, Scope &scope, Call #undef CALL_SORT {} - RETURN_RESULT(o); + return o.asReturnedValue(); } #define IS_SEQUENCE(unused1, unused2, SequenceType, unused3) \ diff --git a/src/qml/jsruntime/qv4sequenceobject_p.h b/src/qml/jsruntime/qv4sequenceobject_p.h index 2b8d1ea716..169a48c2f9 100644 --- a/src/qml/jsruntime/qv4sequenceobject_p.h +++ b/src/qml/jsruntime/qv4sequenceobject_p.h @@ -68,12 +68,8 @@ struct SequencePrototype : public QV4::Object V4_PROTOTYPE(arrayPrototype) void init(); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) - { - scope.result = callData->thisObject.toString(scope.engine); - } - - static void method_sort(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *f, CallData *callData); + static ReturnedValue method_sort(const BuiltinFunction *, CallData *callData); static bool isSequenceType(int sequenceTypeId); static ReturnedValue newSequence(QV4::ExecutionEngine *engine, int sequenceTypeId, QObject *object, int propertyIndex, bool *succeeded); diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 2f34dd6139..d1b936bb4e 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -181,7 +181,8 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { makeIdentifierImpl(); } - void makeIdentifierImpl() const; + // slow path + Q_NEVER_INLINE void makeIdentifierImpl() const; static uint createHashValue(const QChar *ch, int length, uint *subtype) { diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 7c65c97d73..571a638355 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -45,17 +45,11 @@ #include <private/qv4mm_p.h> #include "qv4scopedvalue_p.h" #include "qv4alloca_p.h" +#include "qv4jscall_p.h" #include <QtCore/QDateTime> #include <QtCore/QDebug> #include <QtCore/QStringList> -#include <private/qqmljsengine_p.h> -#include <private/qqmljslexer_p.h> -#include <private/qqmljsparser_p.h> -#include <private/qqmljsast_p.h> -#include <qv4jsir_p.h> -#include <qv4codegen_p.h> - #include <cassert> #ifndef Q_OS_WIN @@ -108,11 +102,8 @@ bool StringObject::deleteIndexedProperty(Managed *m, uint index) Scoped<StringObject> o(scope, m->as<StringObject>()); Q_ASSERT(!!o); - if (index < static_cast<uint>(o->d()->string->toQString().length())) { - if (v4->current->strictMode) - v4->throwTypeError(); + if (index < static_cast<uint>(o->d()->string->toQString().length())) return false; - } return true; } @@ -152,24 +143,25 @@ void Heap::StringCtor::init(QV4::ExecutionContext *scope) Heap::FunctionObject::init(scope, QStringLiteral("String")); } -void StringCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue StringCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - ExecutionEngine *v4 = static_cast<const Object *>(m)->engine(); + ExecutionEngine *v4 = static_cast<const Object *>(f)->engine(); + Scope scope(v4); ScopedString value(scope); - if (callData->argc) - value = callData->args[0].toString(v4); + if (argc) + value = argv[0].toString(v4); else value = v4->newString(); - scope.result = Encode(v4->newStringObject(value)); + return Encode(v4->newStringObject(value)); } -void StringCtor::call(const Managed *, Scope &scope, CallData *callData) +ReturnedValue StringCtor::call(const FunctionObject *m, const Value *, const Value *argv, int argc) { - ExecutionEngine *v4 = scope.engine; - if (callData->argc) - scope.result = callData->args[0].toString(v4); + ExecutionEngine *v4 = m->engine(); + if (argc) + return argv[0].toString(v4)->asReturnedValue(); else - scope.result = v4->newString(); + return v4->newString()->asReturnedValue(); } void StringPrototype::init(ExecutionEngine *engine, Object *ctor) @@ -208,140 +200,155 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("trim"), method_trim); } -static QString getThisString(Scope &scope, CallData *callData) +static QString getThisString(ExecutionEngine *v4, CallData *callData) { - ScopedValue t(scope, callData->thisObject); + Value *t = &callData->thisObject; if (String *s = t->stringValue()) return s->toQString(); if (StringObject *thisString = t->as<StringObject>()) return thisString->d()->string->toQString(); if (t->isUndefined() || t->isNull()) { - scope.engine->throwTypeError(); + v4->throwTypeError(); return QString(); } return t->toQString(); } -void StringPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { if (callData->thisObject.isString()) RETURN_RESULT(callData->thisObject); + ExecutionEngine *v4 = b->engine(); StringObject *o = callData->thisObject.as<StringObject>(); if (!o) - THROW_TYPE_ERROR(); - scope.result = o->d()->string; + return v4->throwTypeError(); + return o->d()->string->asReturnedValue(); } -void StringPrototype::method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_charAt(const BuiltinFunction *b, CallData *callData) { - const QString str = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString str = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); int pos = 0; - if (callData->argc > 0) + if (callData->argc() > 0) pos = (int) callData->args[0].toInteger(); QString result; if (pos >= 0 && pos < str.length()) result += str.at(pos); - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void StringPrototype::method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_charCodeAt(const BuiltinFunction *b, CallData *callData) { - const QString str = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString str = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); int pos = 0; - if (callData->argc > 0) + if (callData->argc() > 0) pos = (int) callData->args[0].toInteger(); if (pos >= 0 && pos < str.length()) RETURN_RESULT(Encode(str.at(pos).unicode())); - scope.result = Encode(qt_qnan()); + return Encode(qt_qnan()); } -void StringPrototype::method_concat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_concat(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); + Scope scope(v4); ScopedString s(scope); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0; i < callData->argc(); ++i) { s = callData->args[i].toString(scope.engine); - CHECK_EXCEPTION(); + if (v4->hasException) + return QV4::Encode::undefined(); Q_ASSERT(s->isString()); value += s->toQString(); } - scope.result = scope.engine->newString(value); + return Encode(v4->newString(value)); } -void StringPrototype::method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_endsWith(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { + if (callData->argc()) { if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); searchString = callData->args[0].toQString(); } int pos = value.length(); - if (callData->argc > 1) + if (callData->argc() > 1) pos = (int) callData->args[1].toInteger(); if (pos == value.length()) RETURN_RESULT(Encode(value.endsWith(searchString))); QStringRef stringToSearch = value.leftRef(pos); - scope.result = Encode(stringToSearch.endsWith(searchString)); + return Encode(stringToSearch.endsWith(searchString)); } -void StringPrototype::method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_indexOf(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) + if (callData->argc()) searchString = callData->args[0].toQString(); int pos = 0; - if (callData->argc > 1) + if (callData->argc() > 1) pos = (int) callData->args[1].toInteger(); int index = -1; if (! value.isEmpty()) index = value.indexOf(searchString, qMin(qMax(pos, 0), value.length())); - scope.result = Encode(index); + return Encode(index); } -void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_includes(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { + if (callData->argc()) { if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); searchString = callData->args[0].toQString(); } int pos = 0; - if (callData->argc > 1) { - ScopedValue posArg(scope, callData->argument(1)); - pos = (int) posArg->toInteger(); - if (!posArg->isInteger() && posArg->isNumber() && qIsInf(posArg->toNumber())) + if (callData->argc() > 1) { + Value &posArg = callData->args[1]; + pos = (int) posArg.toInteger(); + if (!posArg.isInteger() && posArg.isNumber() && qIsInf(posArg.toNumber())) pos = value.length(); } @@ -349,20 +356,21 @@ void StringPrototype::method_includes(const BuiltinFunction *, Scope &scope, Cal RETURN_RESULT(Encode(value.contains(searchString))); QStringRef stringToSearch = value.midRef(pos); - scope.result = Encode(stringToSearch.contains(searchString)); + return Encode(stringToSearch.contains(searchString)); } -void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_lastIndexOf(const BuiltinFunction *b, CallData *callData) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) + if (callData->argc()) searchString = callData->args[0].toQString(); - ScopedValue posArg(scope, callData->argument(1)); - double position = RuntimeHelpers::toNumber(posArg); + double position = callData->argc() > 1 ? RuntimeHelpers::toNumber(callData->args[1]) : +qInf(); if (std::isnan(position)) position = +qInf(); else @@ -374,97 +382,94 @@ void StringPrototype::method_lastIndexOf(const BuiltinFunction *, Scope &scope, if (searchString.isNull() && pos == 0) RETURN_RESULT(Encode(-1)); int index = value.lastIndexOf(searchString, pos); - scope.result = Encode(index); + return Encode(index); } -void StringPrototype::method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_localeCompare(const BuiltinFunction *b, CallData *callData) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); - ScopedValue v(scope, callData->argument(0)); - const QString that = v->toQString(); - scope.result = Encode(QString::localeAwareCompare(value, that)); + if (callData->argc() < 1) + callData->args[0] = Encode::undefined(); + + const QString that = callData->args[0].toQString(); + return Encode(QString::localeAwareCompare(value, that)); } -void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_match(const BuiltinFunction *b, CallData *callData) { - if (callData->thisObject.isUndefined() || callData->thisObject.isNull()) - THROW_TYPE_ERROR(); + ExecutionEngine *v4 = b->engine(); + if (callData->thisObject.isNullOrUndefined()) + return v4->throwTypeError(); - ScopedString s(scope, callData->thisObject.toString(scope.engine)); + callData->thisObject = callData->thisObject.toString(v4); + if (v4->hasException) + return Encode::undefined(); - ScopedValue regexp(scope, callData->argument(0)); - Scoped<RegExpObject> rx(scope, regexp); - if (!rx) { - ScopedCallData callData(scope, 1); - callData->args[0] = regexp; - scope.engine->regExpCtor()->construct(scope, callData); - rx = scope.result.asReturnedValue(); - } + Q_ASSERT(v4->jsStackTop == callData->args + callData->argc()); + if (!callData->argc()) + callData->args[0] = Encode::undefined(); + callData->setArgc(1); + v4->jsStackTop = callData->args + 1; - if (!rx) - // ### CHECK - THROW_TYPE_ERROR(); + Scope scope(v4); - bool global = rx->global(); + if (!callData->args[0].as<RegExpObject>()) { + // convert args[0] to a regexp + callData->args[0] = RegExpCtor::callAsConstructor(b, callData->args, callData->argc()); + if (v4->hasException) + return Encode::undefined(); + } - // ### use the standard builtin function, not the one that might be redefined in the proto - ScopedString execString(scope, scope.engine->newString(QStringLiteral("exec"))); - ScopedFunctionObject exec(scope, scope.engine->regExpPrototype()->get(execString)); + bool global = static_cast<RegExpObject *>(&callData->args[0])->global(); - ScopedCallData cData(scope, 1); - cData->thisObject = rx; - cData->args[0] = s; - if (!global) { - exec->call(scope, cData); - return; - } + qSwap(callData->thisObject, callData->args[0]); + if (!global) + return RegExpPrototype::method_exec(b, callData); - ScopedString lastIndex(scope, scope.engine->newString(QStringLiteral("lastIndex"))); - rx->put(lastIndex, ScopedValue(scope, Primitive::fromInt32(0))); + // rx is now in thisObject + RegExpObject *rx = static_cast<RegExpObject *>(&callData->thisObject); + rx->setLastIndex(0); ScopedArrayObject a(scope, scope.engine->newArrayObject()); - double previousLastIndex = 0; + int previousLastIndex = 0; uint n = 0; - ScopedValue matchStr(scope); - ScopedValue index(scope); while (1) { - exec->call(scope, cData); - if (scope.result.isNull()) + Value result = Primitive::fromReturnedValue(RegExpPrototype::execFirstMatch(b, callData)); + if (result.isNull()) break; - assert(scope.result.isObject()); - index = rx->get(lastIndex, 0); - double thisIndex = index->toInteger(); - if (previousLastIndex == thisIndex) { - previousLastIndex = thisIndex + 1; - rx->put(lastIndex, ScopedValue(scope, Primitive::fromDouble(previousLastIndex))); + int index = rx->lastIndex(); + if (previousLastIndex == index) { + previousLastIndex = index + 1; + rx->setLastIndex(previousLastIndex); } else { - previousLastIndex = thisIndex; + previousLastIndex = index; } - matchStr = scope.result.objectValue()->getIndexed(0); - a->arraySet(n, matchStr); + a->arraySet(n, result); ++n; } if (!n) - scope.result = Encode::null(); + return Encode::null(); else - scope.result = a; + return a.asReturnedValue(); } -void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_repeat(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); double repeats = callData->args[0].toInteger(); - if (repeats < 0 || qIsInf(repeats)) { - scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value")); - return; - } + if (repeats < 0 || qIsInf(repeats)) + return v4->throwRangeError(QLatin1String("Invalid count value")); - scope.result = scope.engine->newString(value.repeated(int(repeats))); + return Encode(v4->newString(value.repeated(int(repeats)))); } static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) @@ -513,7 +518,7 @@ static void appendReplacementString(QString *result, const QString &input, const } } -void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_replace(const BuiltinFunction *b, CallData *callData) { QString string; if (StringObject *thisString = callData->thisObject.as<StringObject>()) @@ -528,6 +533,7 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call uint _matchOffsets[64]; uint *matchOffsets = _matchOffsets; + Scope scope(b); ScopedValue searchValue(scope, callData->argument(0)); Scoped<RegExpObject> regExp(scope, searchValue); if (regExp) { @@ -571,14 +577,15 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call } QString result; + ScopedValue replacement(scope); ScopedValue replaceValue(scope, callData->argument(1)); ScopedFunctionObject searchCallback(scope, replaceValue); if (!!searchCallback) { result.reserve(string.length() + 10*numStringMatches); - ScopedCallData callData(scope, numCaptures + 2); - callData->thisObject = Primitive::undefinedValue(); - int lastEnd = 0; ScopedValue entry(scope); + JSCallData jsCallData(scope, numCaptures + 2); + *jsCallData->thisObject = Primitive::undefinedValue(); + int lastEnd = 0; for (int i = 0; i < numStringMatches; ++i) { for (int k = 0; k < numCaptures; ++k) { int idx = (i * numCaptures + k) * 2; @@ -587,17 +594,17 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call entry = Primitive::undefinedValue(); if (start != JSC::Yarr::offsetNoMatch && end != JSC::Yarr::offsetNoMatch) entry = scope.engine->newString(string.mid(start, end - start)); - callData->args[k] = entry; + jsCallData->args[k] = entry; } uint matchStart = matchOffsets[i * numCaptures * 2]; Q_ASSERT(matchStart >= static_cast<uint>(lastEnd)); uint matchEnd = matchOffsets[i * numCaptures * 2 + 1]; - callData->args[numCaptures] = Primitive::fromUInt32(matchStart); - callData->args[numCaptures + 1] = scope.engine->newString(string); + jsCallData->args[numCaptures] = Primitive::fromUInt32(matchStart); + jsCallData->args[numCaptures + 1] = scope.engine->newString(string); - searchCallback->call(scope, callData); + replacement = searchCallback->call(jsCallData); result += string.midRef(lastEnd, matchStart - lastEnd); - result += scope.result.toQString(); + result += replacement->toQString(); lastEnd = matchEnd; } result += string.midRef(lastEnd); @@ -623,43 +630,49 @@ void StringPrototype::method_replace(const BuiltinFunction *, Scope &scope, Call if (matchOffsets != _matchOffsets) free(matchOffsets); - scope.result = scope.engine->newString(result); + return Encode(scope.engine->newString(result)); } -void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_search(const BuiltinFunction *b, CallData *callData) { - QString string = getThisString(scope, callData); - scope.result = callData->argument(0); - CHECK_EXCEPTION(); + Scope scope(b); + ScopedValue regExpObj(scope); + QString string = getThisString(scope.engine, callData); + regExpObj = callData->argument(0); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - Scoped<RegExpObject> regExp(scope, scope.result.as<RegExpObject>()); + RegExpObject *regExp = regExpObj->as<RegExpObject>(); if (!regExp) { - ScopedCallData callData(scope, 1); - callData->args[0] = scope.result; - scope.engine->regExpCtor()->construct(scope, callData); - CHECK_EXCEPTION(); + JSCallData jsCallData(scope, 1); + jsCallData->args[0] = regExpObj; + regExpObj = scope.engine->regExpCtor()->callAsConstructor(jsCallData); + if (scope.engine->hasException) + return QV4::Encode::undefined(); - regExp = scope.result.as<RegExpObject>(); + regExp = regExpObj->as<RegExpObject>(); Q_ASSERT(regExp); } Scoped<RegExp> re(scope, regExp->value()); Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint)); uint result = re->match(string, /*offset*/0, matchOffsets); if (result == JSC::Yarr::offsetNoMatch) - scope.result = Encode(-1); + return Encode(-1); else - scope.result = Encode(result); + return Encode(result); } -void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_slice(const BuiltinFunction *b, CallData *callData) { - const QString text = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString text = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); const double length = text.length(); - double start = callData->argc ? callData->args[0].toInteger() : 0; - double end = (callData->argc < 2 || callData->args[1].isUndefined()) + double start = callData->argc() ? callData->args[0].toInteger() : 0; + double end = (callData->argc() < 2 || callData->args[1].isUndefined()) ? length : callData->args[1].toInteger(); if (start < 0) @@ -676,14 +689,17 @@ void StringPrototype::method_slice(const BuiltinFunction *, Scope &scope, CallDa const int intEnd = int(end); int count = qMax(0, intEnd - intStart); - scope.result = scope.engine->newString(text.mid(intStart, count)); + return Encode(v4->newString(text.mid(intStart, count))); } -void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_split(const BuiltinFunction *b, CallData *callData) { - QString text = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString text = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); + Scope scope(v4); ScopedValue separatorValue(scope, callData->argument(0)); ScopedValue limitValue(scope, callData->argument(1)); @@ -693,7 +709,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (limitValue->isUndefined()) { ScopedString s(scope, scope.engine->newString(text)); array->push_back(s); - RETURN_RESULT(array); + return array.asReturnedValue(); } RETURN_RESULT(scope.engine->newString(text.left(limitValue->toInteger()))); } @@ -701,7 +717,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa uint limit = limitValue->isUndefined() ? UINT_MAX : limitValue->toUInt32(); if (limit == 0) - RETURN_RESULT(array); + return array.asReturnedValue(); Scoped<RegExpObject> re(scope, separatorValue); if (re) { @@ -742,7 +758,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (separator.isEmpty()) { for (uint i = 0; i < qMin(limit, uint(text.length())); ++i) array->push_back((s = scope.engine->newString(text.mid(i, 1)))); - RETURN_RESULT(array); + return array.asReturnedValue(); } int start = 0; @@ -756,43 +772,47 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa if (array->getLength() < limit && start != -1) array->push_back((s = scope.engine->newString(text.mid(start)))); } - RETURN_RESULT(array); + return array.asReturnedValue(); } -void StringPrototype::method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_startsWith(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); QString searchString; - if (callData->argc) { + if (callData->argc()) { if (callData->args[0].as<RegExpObject>()) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); searchString = callData->args[0].toQString(); } int pos = 0; - if (callData->argc > 1) + if (callData->argc() > 1) pos = (int) callData->args[1].toInteger(); if (pos == 0) - RETURN_RESULT(Encode(value.startsWith(searchString))); + return Encode(value.startsWith(searchString)); QStringRef stringToSearch = value.midRef(pos); RETURN_RESULT(Encode(stringToSearch.startsWith(searchString))); } -void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_substr(const BuiltinFunction *b, CallData *callData) { - const QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); double start = 0; - if (callData->argc > 0) + if (callData->argc() > 0) start = callData->args[0].toInteger(); double length = +qInf(); - if (callData->argc > 1) + if (callData->argc() > 1) length = callData->args[1].toInteger(); double count = value.length(); @@ -803,25 +823,26 @@ void StringPrototype::method_substr(const BuiltinFunction *, Scope &scope, CallD qint32 x = Primitive::toInt32(start); qint32 y = Primitive::toInt32(length); - scope.result = scope.engine->newString(value.mid(x, y)); + return Encode(v4->newString(value.mid(x, y))); } -void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_substring(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); int length = value.length(); double start = 0; double end = length; - if (callData->argc > 0) + if (callData->argc() > 0) start = callData->args[0].toInteger(); - ScopedValue endValue(scope, callData->argument(1)); - if (!endValue->isUndefined()) - end = endValue->toInteger(); + if (callData->argc() > 1 && !callData->args[1].isUndefined()) + end = callData->args[1].toInteger(); if (std::isnan(start) || start < 0) start = 0; @@ -843,50 +864,56 @@ void StringPrototype::method_substring(const BuiltinFunction *, Scope &scope, Ca qint32 x = (int)start; qint32 y = (int)(end - start); - scope.result = scope.engine->newString(value.mid(x, y)); + return Encode(v4->newString(value.mid(x, y))); } -void StringPrototype::method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLowerCase(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); - scope.result = scope.engine->newString(value.toLower()); + return Encode(v4->newString(value.toLower())); } -void StringPrototype::method_toLocaleLowerCase(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLocaleLowerCase(const BuiltinFunction *b, CallData *callData) { - method_toLowerCase(b, scope, callData); + return method_toLowerCase(b, callData); } -void StringPrototype::method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toUpperCase(const BuiltinFunction *b, CallData *callData) { - QString value = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + const QString value = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); - scope.result = scope.engine->newString(value.toUpper()); + return Encode(v4->newString(value.toUpper())); } -void StringPrototype::method_toLocaleUpperCase(const BuiltinFunction *b, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_toLocaleUpperCase(const BuiltinFunction *b, CallData *callData) { - return method_toUpperCase(b, scope, callData); + return method_toUpperCase(b, callData); } -void StringPrototype::method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_fromCharCode(const BuiltinFunction *b, CallData *callData) { - QString str(callData->argc, Qt::Uninitialized); + QString str(callData->argc(), Qt::Uninitialized); QChar *ch = str.data(); - for (int i = 0; i < callData->argc; ++i) { + for (int i = 0, ei = callData->argc(); i < ei; ++i) { *ch = QChar(callData->args[i].toUInt16()); ++ch; } - scope.result = scope.engine->newString(str); + return Encode(b->engine()->newString(str)); } -void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue StringPrototype::method_trim(const BuiltinFunction *b, CallData *callData) { - QString s = getThisString(scope, callData); - CHECK_EXCEPTION(); + ExecutionEngine *v4 = b->engine(); + QString s = getThisString(v4, callData); + if (v4->hasException) + return QV4::Encode::undefined(); const QChar *chars = s.constData(); int start, end; @@ -899,5 +926,5 @@ void StringPrototype::method_trim(const BuiltinFunction *, Scope &scope, CallDat break; } - scope.result = scope.engine->newString(QString(chars + start, end - start + 1)); + return Encode(v4->newString(QString(chars + start, end - start + 1))); } diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index ce046f4844..a76684384d 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -106,8 +106,8 @@ struct StringCtor: FunctionObject { V4_OBJECT2(StringCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *, Scope &scope, CallData *callData); + static ReturnedValue callAsConstructor(const FunctionObject *f, const Value *argv, int argc); + static ReturnedValue call(const FunctionObject *, const Value *thisObject, const Value *argv, int argc); }; struct StringPrototype: StringObject @@ -115,30 +115,30 @@ struct StringPrototype: StringObject V4_PROTOTYPE(objectPrototype) void init(ExecutionEngine *engine, Object *ctor); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_charAt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_charCodeAt(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_concat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_endsWith(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_indexOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_includes(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_split(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_startsWith(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_substr(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_substring(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleLowerCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toLocaleUpperCase(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_fromCharCode(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_trim(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_charAt(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_charCodeAt(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_concat(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_endsWith(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_indexOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_includes(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_lastIndexOf(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_localeCompare(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_match(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_repeat(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_replace(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_search(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_slice(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_split(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_startsWith(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_substr(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_substring(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLowerCase(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleLowerCase(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toUpperCase(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toLocaleUpperCase(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_fromCharCode(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_trim(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index fe27d7c2d2..c1fbec70ea 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -39,6 +39,7 @@ #include "qv4typedarray_p.h" #include "qv4arraybuffer_p.h" #include "qv4string_p.h" +#include "qv4jscall_p.h" #include <cmath> @@ -208,36 +209,32 @@ void Heap::TypedArrayCtor::init(QV4::ExecutionContext *scope, TypedArray::Type t type = t; } -void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callData) +ReturnedValue TypedArrayCtor::callAsConstructor(const FunctionObject *f, const Value *argv, int argc) { - Scoped<TypedArrayCtor> that(scope, static_cast<const TypedArrayCtor *>(m)); + Scope scope(f->engine()); + const TypedArrayCtor *that = static_cast<const TypedArrayCtor *>(f); - if (!callData->argc || !callData->args[0].isObject()) { + if (!argc || !argv[0].isObject()) { // ECMA 6 22.2.1.1 - double l = callData->argc ? callData->args[0].toNumber() : 0; - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + double l = argc ? argv[0].toNumber() : 0; + if (scope.engine->hasException) + return Encode::undefined(); uint len = (uint)l; if (l != len) scope.engine->throwRangeError(QStringLiteral("Non integer length for typed array.")); uint byteLength = len * operations[that->d()->type].bytesPerElement; Scoped<ArrayBuffer> buffer(scope, scope.engine->newArrayBuffer(byteLength)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = 0; - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } - Scoped<TypedArray> typedArray(scope, callData->argument(0)); + Scoped<TypedArray> typedArray(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!!typedArray) { // ECMA 6 22.2.1.2 Scoped<ArrayBuffer> buffer(scope, typedArray->d()->buffer); @@ -247,10 +244,8 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat uint destByteLength = byteLength*destElementSize/srcElementSize; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(destByteLength)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, newBuffer->d()); @@ -275,39 +270,30 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat } } - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } - Scoped<ArrayBuffer> buffer(scope, callData->argument(0)); + Scoped<ArrayBuffer> buffer(scope, argc ? argv[0] : Primitive::undefinedValue()); if (!!buffer) { // ECMA 6 22.2.1.4 - double dbyteOffset = callData->argc > 1 ? callData->args[1].toInteger() : 0; + double dbyteOffset = argc > 1 ? argv[1].toInteger() : 0; uint byteOffset = (uint)dbyteOffset; uint elementSize = operations[that->d()->type].bytesPerElement; - if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); - return; - } + if (dbyteOffset < 0 || (byteOffset % elementSize) || dbyteOffset > buffer->byteLength()) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid byteOffset")); uint byteLength; - if (callData->argc < 3 || callData->args[2].isUndefined()) { + if (argc < 3 || argv[2].isUndefined()) { byteLength = buffer->byteLength() - byteOffset; - if (buffer->byteLength() < byteOffset || byteLength % elementSize) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); - return; - } + if (buffer->byteLength() < byteOffset || byteLength % elementSize) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); } else { - double l = qBound(0., callData->args[2].toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + double l = qBound(0., argv[2].toInteger(), (double)UINT_MAX); + if (scope.engine->hasException) + return Encode::undefined(); l *= elementSize; - if (buffer->byteLength() - byteOffset < l) { - scope.result = scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); - return; - } + if (buffer->byteLength() - byteOffset < l) + return scope.engine->throwRangeError(QStringLiteral("new TypedArray: invalid length")); byteLength = (uint)l; } @@ -315,25 +301,20 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat array->d()->buffer.set(scope.engine, buffer->d()); array->d()->byteLength = byteLength; array->d()->byteOffset = byteOffset; - scope.result = array.asReturnedValue(); - return; + return array.asReturnedValue(); } // ECMA 6 22.2.1.3 - ScopedObject o(scope, callData->argument(0)); + ScopedObject o(scope, argc ? argv[0] : Primitive::undefinedValue()); uint l = (uint) qBound(0., ScopedValue(scope, o->get(scope.engine->id_length()))->toInteger(), (double)UINT_MAX); - if (scope.engine->hasException) { - scope.result = scope.engine->throwTypeError(); - return; - } + if (scope.engine->hasException) + return scope.engine->throwTypeError(); uint elementSize = operations[that->d()->type].bytesPerElement; Scoped<ArrayBuffer> newBuffer(scope, scope.engine->newArrayBuffer(l * elementSize)); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); Scoped<TypedArray> array(scope, TypedArray::create(scope.engine, that->d()->type)); array->d()->buffer.set(scope.engine, newBuffer->d()); @@ -346,21 +327,19 @@ void TypedArrayCtor::construct(const Managed *m, Scope &scope, CallData *callDat while (idx < l) { val = o->getIndexed(idx); array->d()->type->write(scope.engine, b, 0, val); - if (scope.engine->hasException) { - scope.result = Encode::undefined(); - return; - } + if (scope.engine->hasException) + return Encode::undefined(); ++idx; b += elementSize; } - scope.result = array.asReturnedValue(); + return array.asReturnedValue(); } -void TypedArrayCtor::call(const Managed *that, Scope &scope, CallData *callData) +ReturnedValue TypedArrayCtor::call(const FunctionObject *f, const Value *, const Value *argv, int argc) { - construct(that, scope, callData); + return callAsConstructor(f, argv, argc); } void Heap::TypedArray::init(Type t) @@ -406,15 +385,10 @@ bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) uint bytesPerElement = a->d()->type->bytesPerElement; uint byteOffset = a->d()->byteOffset + index * bytesPerElement; if (byteOffset + bytesPerElement > (uint)a->d()->buffer->byteLength()) - goto reject; + return false; a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); return true; - -reject: - if (scope.engine->current->strictMode) - scope.engine->throwTypeError(); - return false; } void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) @@ -435,52 +409,57 @@ void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) defineDefaultProperty(QStringLiteral("subarray"), method_subarray, 0); } -void TypedArrayPrototype::method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_buffer(const BuiltinFunction *b, CallData *callData) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + TypedArray *v = callData->thisObject.as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = v->d()->buffer; + return v->d()->buffer->asReturnedValue(); } -void TypedArrayPrototype::method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_byteLength(const BuiltinFunction *b, CallData *callData) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + TypedArray *v = callData->thisObject.as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteLength); + return Encode(v->d()->byteLength); } -void TypedArrayPrototype::method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_byteOffset(const BuiltinFunction *b, CallData *callData) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + TypedArray *v = callData->thisObject.as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteOffset); + return Encode(v->d()->byteOffset); } -void TypedArrayPrototype::method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_get_length(const BuiltinFunction *b, CallData *callData) { - Scoped<TypedArray> v(scope, callData->thisObject); + ExecutionEngine *v4 = b->engine(); + TypedArray *v = callData->thisObject.as<TypedArray>(); if (!v) - THROW_TYPE_ERROR(); + return v4->throwTypeError(); - scope.result = Encode(v->d()->byteLength/v->d()->type->bytesPerElement); + return Encode(v->d()->byteLength/v->d()->type->bytesPerElement); } -void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_set(const BuiltinFunction *b, CallData *callData) { + Scope scope(b); Scoped<TypedArray> a(scope, callData->thisObject); if (!a) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) scope.engine->throwTypeError(); - double doffset = callData->argc >= 2 ? callData->args[1].toInteger() : 0; + double doffset = callData->argc() >= 2 ? callData->args[1].toInteger() : 0; if (scope.engine->hasException) RETURN_UNDEFINED(); @@ -494,12 +473,12 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call // src is a regular object ScopedObject o(scope, callData->args[0].toObject(scope.engine)); if (scope.engine->hasException || !o) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); double len = ScopedValue(scope, o->get(scope.engine->id_length()))->toNumber(); uint l = (uint)len; if (scope.engine->hasException || l != len) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); if (offset + l > a->length()) RETURN_RESULT(scope.engine->throwRangeError(QStringLiteral("TypedArray.set: out of range"))); @@ -521,7 +500,7 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call // src is a typed array Scoped<ArrayBuffer> srcBuffer(scope, srcTypedArray->d()->buffer); if (!srcBuffer) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); uint l = srcTypedArray->length(); if (offset + l > a->length()) @@ -559,24 +538,25 @@ void TypedArrayPrototype::method_set(const BuiltinFunction *, Scope &scope, Call RETURN_UNDEFINED(); } -void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue TypedArrayPrototype::method_subarray(const BuiltinFunction *builtin, CallData *callData) { + Scope scope(builtin); Scoped<TypedArray> a(scope, callData->thisObject); if (!a) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); Scoped<ArrayBuffer> buffer(scope, a->d()->buffer); if (!buffer) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); int len = a->length(); - double b = callData->argc > 0 ? callData->args[0].toInteger() : 0; + double b = callData->argc() > 0 ? callData->args[0].toInteger() : 0; if (b < 0) b = len + b; uint begin = (uint)qBound(0., b, (double)len); - double e = callData->argc < 2 || callData->args[1].isUndefined() ? len : callData->args[1].toInteger(); + double e = callData->argc() < 2 || callData->args[1].isUndefined() ? len : callData->args[1].toInteger(); if (e < 0) e = len + e; uint end = (uint)qBound(0., e, (double)len); @@ -590,11 +570,11 @@ void TypedArrayPrototype::method_subarray(const BuiltinFunction *, Scope &scope, ScopedFunctionObject constructor(scope, a->get(scope.engine->id_constructor())); if (!constructor) - THROW_TYPE_ERROR(); + return scope.engine->throwTypeError(); - ScopedCallData cData(scope, 3); - cData->args[0] = buffer; - cData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); - cData->args[2] = Encode(newLen); - constructor->construct(scope, cData); + JSCallData jsCallData(scope, 3); + jsCallData->args[0] = buffer; + jsCallData->args[1] = Encode(a->d()->byteOffset + begin*a->d()->type->bytesPerElement); + jsCallData->args[2] = Encode(newLen); + return constructor->callAsConstructor(jsCallData); } diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index a472dfa607..6fe53e68d5 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -141,8 +141,8 @@ struct TypedArrayCtor: FunctionObject { V4_OBJECT2(TypedArrayCtor, FunctionObject) - static void construct(const Managed *m, Scope &scope, CallData *callData); - static void call(const Managed *that, Scope &scope, CallData *callData); + 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); }; @@ -153,13 +153,13 @@ struct TypedArrayPrototype : Object void init(ExecutionEngine *engine, TypedArrayCtor *ctor); - static void method_get_buffer(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteLength(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_byteOffset(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_get_length(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_get_buffer(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_byteLength(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_byteOffset(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_get_length(const BuiltinFunction *, CallData *callData); - static void method_set(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_subarray(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_set(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_subarray(const BuiltinFunction *, CallData *callData); }; inline void diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index f41442df7a..e7aaa97e6b 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -75,39 +75,29 @@ int Value::toUInt16() const return (unsigned short)number; } -bool Value::toBoolean() const +bool Value::toBooleanImpl(Value val) { - if (isInteger() || isBoolean()) - return static_cast<bool>(int_32()); - - if (isUndefined() || isNull()) - return false; - - if (isManaged()) { + if (val.isManagedOrUndefined()) { + Heap::Base *b = val.m(); + if (!b) + return false; #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); #else - if (String *s = stringValue()) - return s->toQString().length() > 0; + if (b->vtable()->isString) + return static_cast<Heap::String *>(b)->length() > 0; #endif return true; } // double - return doubleValue() && !std::isnan(doubleValue()); + double d = val.doubleValue(); + return d && !std::isnan(d); } -double Value::toInteger() const +double Value::toNumberImpl(Value val) { - if (integerCompatible()) - return int_32(); - - return Primitive::toInteger(toNumber()); -} - -double Value::toNumberImpl() const -{ - switch (type()) { + switch (val.type()) { case QV4::Value::Undefined_Type: return std::numeric_limits<double>::quiet_NaN(); case QV4::Value::Managed_Type: @@ -115,12 +105,13 @@ double Value::toNumberImpl() const Q_UNIMPLEMENTED(); Q_FALLTHROUGH(); #else - if (String *s = stringValue()) + if (String *s = val.stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); { - Q_ASSERT(isObject()); - Scope scope(objectValue()->engine()); - ScopedValue prim(scope, RuntimeHelpers::toPrimitive(*this, NUMBER_HINT)); + Q_ASSERT(val.isObject()); + Scope scope(val.objectValue()->engine()); + ScopedValue protectThis(scope, val); + ScopedValue prim(scope, RuntimeHelpers::toPrimitive(val, NUMBER_HINT)); if (scope.engine->hasException) return 0; return prim->toNumber(); @@ -129,7 +120,7 @@ double Value::toNumberImpl() const case QV4::Value::Null_Type: case QV4::Value::Boolean_Type: case QV4::Value::Integer_Type: - return int_32(); + return val.int_32(); default: // double Q_UNREACHABLE(); } @@ -236,83 +227,27 @@ bool Value::sameValue(Value other) const { if (s && os) return s->isEqualTo(os); if (isInteger() && other.isDouble()) - return int_32() ? (double(int_32()) == other.doubleValue()) : (other._val == 0); + return int_32() ? (double(int_32()) == other.doubleValue()) + : (other.doubleValue() == 0 && !std::signbit(other.doubleValue())); if (isDouble() && other.isInteger()) - return other.int_32() ? (doubleValue() == double(other.int_32())) : (_val == 0); + return other.int_32() ? (doubleValue() == double(other.int_32())) + : (doubleValue() == 0 && !std::signbit(doubleValue())); return false; } - -int Primitive::toInt32(double number) -{ - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - - if ((number >= -D31 && number < D31)) - return static_cast<int>(number); - - - if (!std::isfinite(number)) - return 0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < -D31) - number += D32; - else if (number >= D31) - number -= D32; - - return int(number); -} - -unsigned int Primitive::toUInt32(double number) -{ - const double D32 = 4294967296.0; - if ((number >= 0 && number < D32)) - return static_cast<uint>(number); - - if (!std::isfinite(number)) - return +0; - - double d = ::floor(::fabs(number)); - if (std::signbit(number)) - d = -d; - - number = ::fmod(d , D32); - - if (number < 0) - number += D32; - - return unsigned(number); -} - -double Primitive::toInteger(double number) -{ - if (std::isnan(number)) - return +0; - else if (! number || std::isinf(number)) - return number; - const double v = floor(fabs(number)); - return std::signbit(number) ? -v : v; -} - #ifndef V4_BOOTSTRAP -Heap::String *Value::toString(ExecutionEngine *e) const +Heap::String *Value::toString(ExecutionEngine *e, Value val) { - if (String *s = stringValue()) + if (String *s = val.stringValue()) return s->d(); - return RuntimeHelpers::convertToString(e, *this); + return RuntimeHelpers::convertToString(e, val); } -Heap::Object *Value::toObject(ExecutionEngine *e) const +Heap::Object *Value::toObject(ExecutionEngine *e, Value val) { - if (Object *o = objectValue()) + if (Object *o = val.objectValue()) return o->d(); - return RuntimeHelpers::convertToObject(e, *this); + return RuntimeHelpers::convertToObject(e, val); } uint Value::asArrayLength(bool *ok) const diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 17d0c32853..8497d47800 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -51,15 +51,12 @@ // #include <limits.h> +#include <cmath> #include <QtCore/QString> #include "qv4global_p.h" #include <private/qv4heap_p.h> -#if QT_POINTER_SIZE == 8 -#define QV4_USE_64_BIT_VALUE_ENCODING -#endif - QT_BEGIN_NAMESPACE namespace QV4 { @@ -72,9 +69,7 @@ struct Q_QML_PRIVATE_EXPORT Value { private: /* - We use two different ways of encoding JS values. One for 32bit and one for 64bit systems. - - In both cases, we use 8 bytes for a value and a different variant of NaN boxing. A Double + We use 8 bytes for a value and a different variant of NaN boxing. A Double NaN (actually -qNaN) is indicated by a number that has the top 13 bits set, and for a signalling NaN it is the top 14 bits. The other values are usually set to 0 by the processor, and are thus free for us to store other data. We keep pointers in there for @@ -83,18 +78,14 @@ private: only have 48 bits of addressable memory. (Note: we do leave the lower 49 bits available for pointers.) - On 32bit, we store doubles as doubles. All other values, have the high 32bits set to a value - that will make the number a NaN. The Masks below are used for encoding the other types. - - On 64 bit, we xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will + We xor Doubles with (0xffff8000 << 32). That has the effect that no doubles will get encoded with bits 63-49 all set to 0. We then use bit 48 to distinguish between managed/undefined (0), or Null/Int/Bool/Empty (1). So, storing a 49 bit pointer will leave the top 15 bits 0, which is exactly the 'natural' representation of pointers. If bit 49 is set, bit 48 indicates Empty (0) or integer-convertible (1). Then the 3 bit below that are used to encode Null/Int/Bool. - On both 32bit and 64bit, Undefined is encoded as a managed pointer with value 0. This is - the same as a nullptr. + Undefined is encoded as a managed pointer with value 0. This is the same as a nullptr. Specific bit-sequences: 0 = always 0 @@ -102,8 +93,6 @@ private: x = stored value a,b,c,d = specific bit values, see notes - 64bit: - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value ------------------------------------------------------------------------+-------------- @@ -112,9 +101,9 @@ private: a0000000 0000bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf dddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double 00000000 00000010 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 00000000 00000011 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 00000000 00000011 01000000 00000000 00000000 00000000 00000000 0000000x | Bool - 00000000 00000011 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int + 00000000 00000010 10000000 00000000 00000000 00000000 00000000 00000000 | Null + 00000000 00000011 00000000 00000000 00000000 00000000 00000000 0000000x | Bool + 00000000 00000011 10000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int Notes: - a: xor-ed signbit, always 1 for NaN @@ -128,31 +117,8 @@ private: - Null, Bool, and Int have bit 48 set, indicating integer-convertible - xoring _val with NaNEncodeMask will convert to a double in "natural" representation, where any non double results in a NaN - - 32bit: - - 32109876 54321098 76543210 98765432 10987654 32109876 54321098 76543210 | - 66665555 55555544 44444444 33333333 33222222 22221111 11111100 00000000 | JS Value - ------------------------------------------------------------------------+-------------- - 01111111 11111100 00000000 00000000 00000000 00000000 00000000 00000000 | Undefined - 01111111 11111100 00000000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Managed (heap pointer) - a1111111 1111bc00 00000000 00000000 00000000 00000000 00000000 00000000 | NaN/Inf - xddddddd ddddddxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | double - 01111111 11111110 00000000 00000000 00000000 00000000 00000000 00000000 | empty (non-sparse array hole) - 01111111 11111111 10000000 00000000 00000000 00000000 00000000 00000000 | Null - 01111111 11111111 01000000 00000000 00000000 00000000 00000000 0000000x | Bool - 01111111 11111111 00100000 00000000 xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx | Int - - Notes: - - the upper 32 bits are the tag, the lower 32 bits the value - - Undefined has a nullptr in the value, Managed has a non-nullptr stored in the value - - a: sign bit, always 0 for NaN - - b,c: 00=inf, 01 = sNaN, 10 = qNaN, 11 = boxed value - - d: stored double value, as long as not *all* of them are 1, because that's a boxed value - (see above) - - empty, Null, Bool, and Int have bit 63 set to 0, bits 62-50 set to 1 (same as undefined - and managed), and bit 49 set to 1 (where undefined and managed have it set to 0) - - Null, Bool, and Int have bit 48 set, indicating integer-convertible + - on 32bit we can use the fact that addresses are 32bits wide, so the tag part (bits 32 to + 63) are zero. No need to shift. */ quint64 _val; @@ -172,8 +138,9 @@ public: QML_NEARLY_ALWAYS_INLINE void setTagValue(quint32 tag, quint32 value) { _val = quint64(tag) << 32 | value; } QML_NEARLY_ALWAYS_INLINE quint32 value() const { return _val & quint64(~quint32(0)); } QML_NEARLY_ALWAYS_INLINE quint32 tag() const { return _val >> 32; } + QML_NEARLY_ALWAYS_INLINE void setTag(quint32 tag) { setTagValue(tag, value()); } -#if defined(QV4_USE_64_BIT_VALUE_ENCODING) +#if QT_POINTER_SIZE == 8 QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const { Heap::Base *b; @@ -184,7 +151,7 @@ public: { memcpy(&_val, &b, 8); } -#else // !QV4_USE_64_BIT_VALUE_ENCODING +#elif QT_POINTER_SIZE == 4 QML_NEARLY_ALWAYS_INLINE Heap::Base *m() const { Q_STATIC_ASSERT(sizeof(Heap::Base*) == sizeof(quint32)); @@ -199,6 +166,8 @@ public: memcpy(&v, &b, 4); setTagValue(Managed_Type_Internal, v); } +#else +# error "unsupported pointer size" #endif QML_NEARLY_ALWAYS_INLINE int int_32() const @@ -232,24 +201,37 @@ public: return quint32(value()); } + // ### Fix for 32 bit (easiest solution is to set highest bit to 1 for mananged/undefined/integercompatible + // and use negative numbers here + enum QuickType { + QT_ManagedOrUndefined = 0, + QT_ManagedOrUndefined1 = 1, + QT_ManagedOrUndefined2 = 2, + QT_ManagedOrUndefined3 = 3, + QT_Empty = 4, + QT_Null = 5, + QT_Bool = 6, + QT_Int = 7 + // all other values are doubles + }; + enum Type { - Undefined_Type, - Managed_Type, - Empty_Type, - Integer_Type, - Boolean_Type, - Null_Type, - Double_Type + Undefined_Type = 0, + Managed_Type = 1, + Empty_Type = 4, + Null_Type = 5, + Boolean_Type = 6, + Integer_Type = 7, + Double_Type = 8 }; inline Type type() const { - if (isUndefined()) return Undefined_Type; - if (isManaged()) return Managed_Type; - if (isEmpty()) return Empty_Type; - if (isInteger()) return Integer_Type; - if (isBoolean()) return Boolean_Type; - if (isNull()) return Null_Type; - Q_ASSERT(isDouble()); return Double_Type; + int t = quickType(); + if (t < QT_Empty) + return _val ? Managed_Type : Undefined_Type; + if (t > QT_Int) + return Double_Type; + return static_cast<Type>(t); } // Shared between 32-bit and 64-bit encoding @@ -262,19 +244,18 @@ public: enum { IsDouble_Shift = 64-14, IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-16, - IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, - Managed_Type_Internal_64 = 0 + IsIntegerConvertible_Shift = 64-15, + IsIntegerOrBool_Shift = 64-16, + QuickType_Shift = 64 - 17 }; static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 enum class ValueTypeInternal_64 { - Empty = Immediate_Mask_64| 0, - ConvertibleToInt = Immediate_Mask_64| 0x10000u, // bit 48 - Null = ConvertibleToInt | 0x08000u, - Boolean = ConvertibleToInt | 0x04000u, - Integer = ConvertibleToInt | 0x02000u + Empty = Immediate_Mask_64 | 0, + Null = Immediate_Mask_64 | 0x08000u, + Boolean = Immediate_Mask_64 | 0x10000u, + Integer = Immediate_Mask_64 | 0x18000u }; // Used only by 32-bit encoding @@ -285,50 +266,45 @@ public: static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; enum class ValueTypeInternal_32 { - Empty = Immediate_Mask_32| 0, - ConvertibleToInt = Immediate_Mask_32| 0x10000u, // bit 48 - Null = ConvertibleToInt | 0x08000u, - Boolean = ConvertibleToInt | 0x04000u, - Integer = ConvertibleToInt | 0x02000u + Empty = Immediate_Mask_32 | 0, + Null = Immediate_Mask_32 | 0x08000u, + Boolean = Immediate_Mask_32 | 0x10000u, + Integer = Immediate_Mask_32 | 0x18000u }; enum { - Managed_Type_Internal_32 = NotDouble_Mask + Managed_Type_Internal = 0 }; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - enum { - Managed_Type_Internal = Managed_Type_Internal_64 - }; - static const quint64 Immediate_Mask = Immediate_Mask_64; using ValueTypeInternal = ValueTypeInternal_64; -#else - enum { - Managed_Type_Internal = Managed_Type_Internal_32 - }; - static const quint64 Immediate_Mask = Immediate_Mask_32; - using ValueTypeInternal = ValueTypeInternal_32; -#endif + enum { NaN_Mask = 0x7ff80000, }; + inline quint64 quickType() const { return (_val >> QuickType_Shift); } + // used internally in property inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } - inline bool isNumber() const { return isDouble() || isInteger(); } + inline bool isNumber() const { return quickType() >= QT_Int; } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING +#if /*QT_POINTER_SIZE ==*/ 8 // TODO: check if we need specialized versions for 32bit inline bool isUndefined() const { return _val == 0; } inline bool isDouble() const { return (_val >> IsDouble_Shift); } - inline bool isManaged() const { return !isUndefined() && ((_val >> IsManagedOrUndefined_Shift) == 0); } + inline bool isManaged() const { return _val && ((_val >> IsManagedOrUndefined_Shift) == 0); } inline bool isManagedOrUndefined() const { return ((_val >> IsManagedOrUndefined_Shift) == 0); } + inline bool isIntOrBool() const { + return (_val >> IsIntegerOrBool_Shift) == 3; + } + inline bool integerCompatible() const { - return (_val >> IsIntegerConvertible_Shift) == 3; + Q_ASSERT(!isEmpty()); + return (_val >> IsIntegerConvertible_Shift) == 1; } static inline bool integerCompatible(Value a, Value b) { return a.integerCompatible() && b.integerCompatible(); @@ -342,9 +318,16 @@ public: inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); } inline bool isManagedOrUndefined() const { return tag() == Managed_Type_Internal; } - inline bool integerCompatible() const { return (tag() & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } + inline bool integerCompatible() const { + Q_ASSERT(!isEmpty()); + return (tag() >> (IsIntegerConvertible_Shift - 32)) == 1; + } + inline bool isIntOrBool() const { + return (tag() >> (IsIntegerOrBool_Shift - 32)) == 3; + } + static inline bool integerCompatible(Value a, Value b) { - return ((a.tag() & b.tag()) & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); + return a.integerCompatible() && b.integerCompatible(); } static inline bool bothDouble(Value a, Value b) { return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; @@ -354,22 +337,27 @@ public: QML_NEARLY_ALWAYS_INLINE double doubleValue() const { Q_ASSERT(isDouble()); double d; - quint64 v = _val; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - v ^= NaNEncodeMask; + Value v = *this; +#if QT_POINTER_SIZE == 8 + v._val ^= NaNEncodeMask; +#else + v.setTag(v.tag() ^ (NaNEncodeMask >> 32)); #endif - memcpy(&d, &v, 8); + memcpy(&d, &v._val, 8); return d; } QML_NEARLY_ALWAYS_INLINE void setDouble(double d) { memcpy(&_val, &d, 8); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING +#if QT_POINTER_SIZE == 8 _val ^= NaNEncodeMask; +#else + setTag(tag() ^ (NaNEncodeMask >> 32)); #endif Q_ASSERT(isDouble()); } inline bool isString() const; inline bool isObject() const; + inline bool isFunctionObject() const; inline bool isInt32() { if (tag() == quint32(ValueTypeInternal::Integer)) return true; @@ -426,14 +414,27 @@ public: inline int toInt32() const; inline unsigned int toUInt32() const; - bool toBoolean() const; + bool toBoolean() const { + if (integerCompatible()) + return static_cast<bool>(int_32()); + + return toBooleanImpl(*this); + } + static bool toBooleanImpl(Value val); double toInteger() const; inline double toNumber() const; - double toNumberImpl() const; + static double toNumberImpl(Value v); + double toNumberImpl() const { return toNumberImpl(*this); } QString toQStringNoThrow() const; QString toQString() const; - Heap::String *toString(ExecutionEngine *e) const; - Heap::Object *toObject(ExecutionEngine *e) const; + Heap::String *toString(ExecutionEngine *e) const { + return toString(e, *this); + } + static Heap::String *toString(ExecutionEngine *e, Value val); + Heap::Object *toObject(ExecutionEngine *e) const { + return toObject(e, *this); + } + static Heap::Object *toObject(ExecutionEngine *e, Value val); inline bool isPrimitive() const; inline bool tryIntegerConversion() { @@ -520,6 +521,12 @@ inline bool Value::isObject() const return b && b->vtable()->isObject; } +inline bool Value::isFunctionObject() const +{ + Heap::Base *b = heapObject(); + return b && b->vtable()->isFunctionObject; +} + inline bool Value::isPrimitive() const { return !isObject(); @@ -538,7 +545,7 @@ inline double Value::toNumber() const #ifndef V4_BOOTSTRAP inline uint Value::asArrayIndex() const { -#ifdef QV4_USE_64_BIT_VALUE_ENCODING +#if QT_POINTER_SIZE == 8 if (!isNumber()) return UINT_MAX; if (isInteger()) @@ -558,8 +565,8 @@ inline uint Value::asArrayIndex() const inline bool Value::asArrayIndex(uint &idx) const { - if (!isDouble()) { - if (isInteger() && int_32() >= 0) { + if (Q_LIKELY(!isDouble())) { + if (Q_LIKELY(isInteger() && int_32() >= 0)) { idx = (uint)int_32(); return true; } @@ -593,9 +600,9 @@ struct Q_QML_PRIVATE_EXPORT Primitive : public Value using Value::toInt32; using Value::toUInt32; - static double toInteger(double fromNumber); - static int toInt32(double value); - static unsigned int toUInt32(double value); + static double toInteger(double d); + static int toInt32(double d); + static unsigned int toUInt32(double d); }; inline Primitive Primitive::undefinedValue() @@ -658,6 +665,72 @@ inline Primitive Primitive::fromUInt32(uint i) return v; } +struct Double { + quint64 d; + + Double(double dbl) { + memcpy(&d, &dbl, sizeof(double)); + } + + int sign() const { + return (d >> 63) ? -1 : 1; + } + + bool isDenormal() const { + return static_cast<int>((d << 1) >> 53) == 0; + } + + int exponent() const { + return static_cast<int>((d << 1) >> 53) - 1023; + } + + quint64 significant() const { + quint64 m = (d << 12) >> 12; + if (!isDenormal()) + m |= (static_cast<quint64>(1) << 52); + return m; + } + + static int toInt32(double d) { + int i = static_cast<int>(d); + if (i == d) + return i; + return Double(d).toInt32(); + } + + int toInt32() { + int e = exponent() - 52; + if (e < 0) { + if (e <= -53) + return 0; + return sign() * static_cast<int>(significant() >> -e); + } else { + if (e > 31) + return 0; + return sign() * (static_cast<int>(significant()) << e); + } + } +}; + +inline double Primitive::toInteger(double d) +{ + if (std::isnan(d)) + return +0; + else if (!d || std::isinf(d)) + return d; + return d >= 0 ? std::floor(d) : std::ceil(d); +} + +inline int Primitive::toInt32(double value) +{ + return Double::toInt32(value); +} + +inline unsigned int Primitive::toUInt32(double d) +{ + return static_cast<uint>(toInt32(d)); +} + struct Encode { static ReturnedValue undefined() { return Primitive::undefinedValue().rawValue(); @@ -666,33 +739,48 @@ struct Encode { return Primitive::nullValue().rawValue(); } - Encode(bool b) { + explicit Encode(bool b) { val = Primitive::fromBoolean(b).rawValue(); } - Encode(double d) { + explicit Encode(double d) { val = Primitive::fromDouble(d).rawValue(); } - Encode(int i) { + explicit Encode(int i) { val = Primitive::fromInt32(i).rawValue(); } - Encode(uint i) { + explicit Encode(uint i) { val = Primitive::fromUInt32(i).rawValue(); } - Encode(ReturnedValue v) { + explicit Encode(ReturnedValue v) { val = v; } + Encode(Value v) { + val = v.rawValue(); + } - Encode(Heap::Base *o) { + explicit Encode(Heap::Base *o) { Q_ASSERT(o); val = Value::fromHeapObject(o).asReturnedValue(); } + explicit Encode(Value *o) { + Q_ASSERT(o); + val = o->asReturnedValue(); + } + + static ReturnedValue smallestNumber(double d) { + if (static_cast<int>(d) == d && !(d == 0. && std::signbit(d))) + return Encode(static_cast<int>(d)); + else + return Encode(d); + } + operator ReturnedValue() const { return val; } quint64 val; private: - Encode(void *); + explicit Encode(void *); }; template<typename T> @@ -700,24 +788,29 @@ ReturnedValue value_convert(ExecutionEngine *e, const Value &v); inline int Value::toInt32() const { - if (isInteger()) + if (Q_LIKELY(integerCompatible())) return int_32(); - double d = isNumber() ? doubleValue() : toNumberImpl(); - - const double D32 = 4294967296.0; - const double D31 = D32 / 2.0; - if ((d >= -D31 && d < D31)) - return static_cast<int>(d); + if (Q_LIKELY(isDouble())) + return Double::toInt32(doubleValue()); - return Primitive::toInt32(d); + return Double::toInt32(toNumberImpl()); } inline unsigned int Value::toUInt32() const { - return (unsigned int)toInt32(); + return static_cast<unsigned int>(toInt32()); } +inline double Value::toInteger() const +{ + if (integerCompatible()) + return int_32(); + + return Primitive::toInteger(isDouble() ? doubleValue() : toNumberImpl()); +} + + } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index f2ff5d307e..00be90b3c2 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -113,17 +113,17 @@ void VariantPrototype::init() defineDefaultProperty(engine()->id_toString(), method_toString, 0); } -void VariantPrototype::method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_preserve(const BuiltinFunction *, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o && o->d()->isScarce()) o->d()->addVmePropertyReference(); RETURN_UNDEFINED(); } -void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_destroy(const BuiltinFunction *, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o) { if (o->d()->isScarce()) o->d()->addVmePropertyReference(); @@ -132,9 +132,10 @@ void VariantPrototype::method_destroy(const BuiltinFunction *, Scope &scope, Cal RETURN_UNDEFINED(); } -void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_toString(const BuiltinFunction *b, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + ExecutionEngine *v4 = b->engine(); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (!o) RETURN_UNDEFINED(); QString result = o->d()->data().toString(); @@ -143,38 +144,33 @@ void VariantPrototype::method_toString(const BuiltinFunction *, Scope &scope, Ca + QLatin1String(o->d()->data().typeName()) + QLatin1Char(')'); } - scope.result = scope.engine->newString(result); + return Encode(v4->newString(result)); } -void VariantPrototype::method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData) +ReturnedValue VariantPrototype::method_valueOf(const BuiltinFunction *b, CallData *callData) { - Scoped<VariantObject> o(scope, callData->thisObject.as<QV4::VariantObject>()); + VariantObject *o = callData->thisObject.as<QV4::VariantObject>(); if (o) { QVariant v = o->d()->data(); switch (v.type()) { case QVariant::Invalid: - scope.result = Encode::undefined(); - return; + return Encode::undefined(); case QVariant::String: - scope.result = scope.engine->newString(v.toString()); - return; + return Encode(b->engine()->newString(v.toString())); case QVariant::Int: - scope.result = Encode(v.toInt()); - return; + return Encode(v.toInt()); case QVariant::Double: case QVariant::UInt: - scope.result = Encode(v.toDouble()); - return; + return Encode(v.toDouble()); case QVariant::Bool: - scope.result = Encode(v.toBool()); - return; + return Encode(v.toBool()); default: if (QMetaType::typeFlags(v.userType()) & QMetaType::IsEnumeration) RETURN_RESULT(Encode(v.toInt())); break; } } - scope.result = callData->thisObject; + return callData->thisObject.asReturnedValue(); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4variantobject_p.h b/src/qml/jsruntime/qv4variantobject_p.h index a7c6fa320c..07b3310e91 100644 --- a/src/qml/jsruntime/qv4variantobject_p.h +++ b/src/qml/jsruntime/qv4variantobject_p.h @@ -108,10 +108,10 @@ public: V4_PROTOTYPE(objectPrototype) void init(); - static void method_preserve(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_destroy(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); - static void method_valueOf(const BuiltinFunction *, Scope &scope, CallData *callData); + static ReturnedValue method_preserve(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_destroy(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_toString(const BuiltinFunction *, CallData *callData); + static ReturnedValue method_valueOf(const BuiltinFunction *, CallData *callData); }; } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 5749d0aef3..282f71fb2e 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -53,19 +53,14 @@ #include <private/qv4regexp_p.h> #include <private/qv4regexpobject_p.h> #include <private/qv4string_p.h> +#include <private/qv4profiling_p.h> +#include <private/qv4jscall_p.h> +#include <private/qqmljavascriptexpression_p.h> #include <iostream> #include "qv4alloca_p.h" -#undef DO_TRACE_INSTR // define to enable instruction tracing - -#ifdef DO_TRACE_INSTR -# define TRACE_INSTR(I) qDebug("executing a %s\n", #I); -# define TRACE(n, str, ...) { char buf[4096]; snprintf(buf, 4096, str, __VA_ARGS__); qDebug(" %s : %s", #n, buf); } -#else -# define TRACE_INSTR(I) -# define TRACE(n, str, ...) -#endif // DO_TRACE_INSTR +#undef COUNT_INSTRUCTIONS extern "C" { @@ -146,7 +141,7 @@ Q_QML_EXPORT int qt_v4DebuggerHook(const char *json); #ifndef QT_NO_QML_DEBUGGER static int qt_v4BreakpointCount = 0; -static bool qt_v4IsDebugging = true; +static bool qt_v4IsDebugging = false; static bool qt_v4IsStepping = false; class Breakpoint @@ -169,14 +164,6 @@ public: static QVector<Breakpoint> qt_v4Breakpoints; static Breakpoint qt_v4LastStop; -static QV4::Function *qt_v4ExtractFunction(QV4::ExecutionContext *context) -{ - if (QV4::Function *function = context->getFunction()) - return function; - else - return context->engine()->globalCode; -} - static void qt_v4TriggerBreakpoint(const Breakpoint &bp, QV4::Function *function) { qt_v4LastStop = bp; @@ -223,6 +210,7 @@ int qt_v4DebuggerHook(const char *json) bp.fullName = ob.value(QLatin1String("fullName")).toString(); bp.condition = ob.value(QLatin1String("condition")).toString(); qt_v4Breakpoints.append(bp); + qt_v4IsDebugging = true; return bp.bpNumber; } @@ -231,6 +219,7 @@ int qt_v4DebuggerHook(const char *json) QString fullName = ob.value(QLatin1String("fullName")).toString(); if (qt_v4Breakpoints.last().matches(fullName, lineNumber)) { qt_v4Breakpoints.removeLast(); + qt_v4IsDebugging = !qt_v4Breakpoints.isEmpty(); return Success; } for (int i = 0; i + 1 < qt_v4Breakpoints.size(); ++i) { @@ -251,13 +240,13 @@ int qt_v4DebuggerHook(const char *json) return -NoSuchCommand; // Failure. } -static void qt_v4CheckForBreak(QV4::ExecutionContext *context) +Q_NEVER_INLINE static void qt_v4CheckForBreak(QV4::CppStackFrame *frame) { if (!qt_v4IsStepping && !qt_v4Breakpoints.size()) return; - const int lineNumber = context->d()->lineNumber; - QV4::Function *function = qt_v4ExtractFunction(context); + const int lineNumber = frame->lineNumber(); + QV4::Function *function = frame->v4Function; QString engineName = function->sourceFile(); if (engineName.isEmpty()) @@ -287,74 +276,64 @@ static void qt_v4CheckForBreak(QV4::ExecutionContext *context) } } +Q_NEVER_INLINE static void debug_slowPath(QV4::ExecutionEngine *engine) +{ + QV4::Debugging::Debugger *debugger = engine->debugger(); + if (debugger && debugger->pauseAtNextOpportunity()) + debugger->maybeBreakAtInstruction(); + if (qt_v4IsDebugging) + qt_v4CheckForBreak(engine->currentStackFrame); +} + #endif // QT_NO_QML_DEBUGGER // End of debugger interface using namespace QV4; using namespace QV4::Moth; -#define MOTH_BEGIN_INSTR_COMMON(I) { \ - const InstrMeta<(int)Instr::I>::DataType &instr = InstrMeta<(int)Instr::I>::data(*genericInstr); \ - code += InstrMeta<(int)Instr::I>::Size; \ - Q_UNUSED(instr); \ - TRACE_INSTR(I) - -#ifdef MOTH_THREADED_INTERPRETER - -# define MOTH_BEGIN_INSTR(I) op_##I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_END_INSTR(I) } \ - genericInstr = reinterpret_cast<const Instr *>(code); \ - goto *jumpTable[genericInstr->common.instructionType]; \ - -#else - -# define MOTH_BEGIN_INSTR(I) \ - case Instr::I: \ - MOTH_BEGIN_INSTR_COMMON(I) - -# define MOTH_END_INSTR(I) } \ - continue; - -#endif - -#ifdef DO_TRACE_INSTR -Param traceParam(const Param ¶m) -{ - if (param.isConstant()) { - qDebug(" constant\n"); - } else if (param.isArgument()) { - qDebug(" argument %d@%d\n", param.index, param.scope); - } else if (param.isLocal()) { - qDebug(" local %d\n", param.index); - } else if (param.isTemp()) { - qDebug(" temp %d\n", param.index); - } else if (param.isScopedLocal()) { - qDebug(" temp %d@%d\n", param.index, param.scope); - } else { - Q_ASSERT(!"INVALID"); +#ifdef COUNT_INSTRUCTIONS +static struct InstrCount { + InstrCount() { + fprintf(stderr, "Counting instructions...\n"); + for (int i = 0; i < MOTH_NUM_INSTRUCTIONS(); ++i) + hits[i] = 0; + } + ~InstrCount() { + fprintf(stderr, "Instruction count:\n"); +#define BLAH(I) \ + fprintf(stderr, "%llu : %s\n", hits[int(Instr::Type::I)], #I); + FOR_EACH_MOTH_INSTR(BLAH) + #undef BLAH + } + quint64 hits[MOTH_NUM_INSTRUCTIONS()]; + void hit(Instr::Type i) { hits[int(i)]++; } +} instrCount; +#endif // COUNT_INSTRUCTIONS + +#define MOTH_BEGIN_INSTR_COMMON(instr) \ + { \ + INSTR_##instr(MOTH_DECODE) + +#ifdef COUNT_INSTRUCTIONS +# define MOTH_BEGIN_INSTR(instr) \ + MOTH_BEGIN_INSTR_COMMON(instr) \ + instrCount.hit(Instr::Type::instr); +#else // !COUNT_INSTRUCTIONS +# define MOTH_BEGIN_INSTR(instr) \ + MOTH_BEGIN_INSTR_COMMON(instr) +#endif // COUNT_INSTRUCTIONS + +#ifdef MOTH_COMPUTED_GOTO +#define MOTH_END_INSTR(instr) \ + MOTH_DISPATCH() \ + } +#else // !MOTH_COMPUTED_GOTO +#define MOTH_END_INSTR(instr) \ + continue; \ } - return param; -} -# define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[traceParam(param).scope].values + param.index) -#else -# define VALUE(param) (*VALUEPTR(param)) -# define VALUEPTR(param) (scopes[param.scope].values + param.index) #endif -// ### add write barrier here -#define STOREVALUE(param, value) { \ - QV4::ReturnedValue tmp = (value); \ - if (engine->hasException) \ - goto catchException; \ - if (Q_LIKELY(!engine->writeBarrierActive || !scopes[param.scope].base)) { \ - VALUE(param) = tmp; \ - } else { \ - QV4::WriteBarrier::write(engine, scopes[param.scope].base, VALUEPTR(param), QV4::Value::fromReturnedValue(tmp)); \ - } \ -} +#define STACK_VALUE(temp) stack[temp] // qv4scopedvalue_p.h also defines a CHECK_EXCEPTION macro #ifdef CHECK_EXCEPTION @@ -364,617 +343,971 @@ Param traceParam(const Param ¶m) if (engine->hasException) \ goto catchException -QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code) +static inline Heap::CallContext *getScope(Value *stack, int level) { -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR - - qt_v4ResolvePendingBreakpointsHook(); - -#ifdef MOTH_THREADED_INTERPRETER -#define MOTH_INSTR_ADDR(I, FMT) &&op_##I, - static void *jumpTable[] = { - FOR_EACH_MOTH_INSTR(MOTH_INSTR_ADDR) - }; -#undef MOTH_INSTR_ADDR -#endif - - QV4::Value *stack = 0; - unsigned stackSize = 0; - - const uchar *exceptionHandler = 0; + Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); + while (level > 0) { + --level; + scope = scope->outer; + } + Q_ASSERT(scope); + return static_cast<Heap::CallContext *>(scope); +} - QV4::Scope scope(engine); - engine->current->lineNumber = -1; +static inline const QV4::Value &constant(Function *function, int index) +{ + return function->compilationUnit->constants[index]; +} -#ifdef DO_TRACE_INSTR - qDebug("Starting VME with context=%p and code=%p", context, code); -#endif // DO_TRACE_INSTR - // setup lookup scopes - int scopeDepth = 0; - { - QV4::Heap::ExecutionContext *scope = engine->current; - while (scope) { - ++scopeDepth; - scope = scope->outer; - } +static bool compareEqual(Value lhs, 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); } - struct Scopes { - QV4::Value *values; - QV4::Heap::Base *base; // non 0 if a write barrier is required - }; - Q_ALLOCA_VAR(Scopes, scopes, sizeof(Scopes)*(2 + 2*scopeDepth)); - { - scopes[0] = { const_cast<QV4::Value *>(static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit)->constants), 0 }; - // stack gets setup in push instruction - scopes[1] = { 0, 0 }; - QV4::Heap::ExecutionContext *scope = engine->current; - int i = 0; - while (scope) { - if (scope->type == QV4::Heap::ExecutionContext::Type_SimpleCallContext) { - QV4::Heap::SimpleCallContext *cc = static_cast<QV4::Heap::SimpleCallContext *>(scope); - scopes[2*i + 2] = { cc->callData->args, 0 }; - scopes[2*i + 3] = { 0, 0 }; - } else if (scope->type == QV4::Heap::ExecutionContext::Type_CallContext) { - QV4::Heap::CallContext *cc = static_cast<QV4::Heap::CallContext *>(scope); - scopes[2*i + 2] = { cc->callData->args, cc }; - scopes[2*i + 3] = { cc->locals.values, cc }; + switch (lt) { + case Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return rhs.isNullOrUndefined(); + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + // LHS: Managed + switch (rt) { + case Value::QT_ManagedOrUndefined: + if (rhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case 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 { - scopes[2*i + 2] = { 0, 0 }; - scopes[2*i + 3] = { 0, 0 }; + Q_ASSERT(r->vtable()->isString); + lhs = Primitive::fromReturnedValue(RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT)); + break; } - ++i; - scope = scope->outer; + return false; + } + case Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return false; + case Value::QT_Bool: + case 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 Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return rhs.isNull(); + case Value::QT_Bool: + case Value::QT_Int: + switch (rt) { + case Value::QT_ManagedOrUndefined: + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + case Value::QT_Empty: + case Value::QT_Null: + Q_UNREACHABLE(); + case Value::QT_Bool: + case 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(Value &accumulator, Value lhs, int rhs) +{ + redo: + switch (lhs.quickType()) { + case Value::QT_ManagedOrUndefined: + if (lhs.isUndefined()) + return false; + Q_FALLTHROUGH(); + case Value::QT_ManagedOrUndefined1: + case Value::QT_ManagedOrUndefined2: + case Value::QT_ManagedOrUndefined3: + // LHS: Managed + if (lhs.m()->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)); + goto redo; + case Value::QT_Empty: + Q_UNREACHABLE(); + case Value::QT_Null: + return false; + case Value::QT_Bool: + case Value::QT_Int: + return lhs.int_32() == rhs; + default: // double + return lhs.doubleValue() == rhs; + } +} + +#define STORE_IP() frame.instructionPointer = code; +#define STORE_ACC() accumulator = acc; +#define ACC Primitive::fromReturnedValue(acc) +#define VALUE_TO_INT(i, val) \ + int i; \ + do { \ + if (Q_LIKELY(val.integerCompatible())) { \ + i = val.int_32(); \ + } else { \ + double d; \ + if (val.isDouble()) \ + d = val.doubleValue(); \ + else { \ + d = val.toNumberImpl(); \ + CHECK_EXCEPTION; \ + } \ + i = Double::toInt32(d); \ + } \ + } while (false) + +QV4::ReturnedValue VME::exec(CallData *callData, QV4::Function *function) +{ + qt_v4ResolvePendingBreakpointsHook(); + + MOTH_JUMP_TABLE; + + ExecutionEngine *engine = function->internalClass->engine; + CHECK_STACK_LIMITS(engine); + Profiling::FunctionCallProfiler profiler(engine, function); + Q_UNUSED(profiler) + + Value *jsStackTop = engine->jsStackTop; + + Q_ASSERT(engine->jsStackTop == callData->args + callData->argc()); + Value *stack = reinterpret_cast<Value *>(callData); + int stackSpaceToAdd = int(function->compiledFunction->nRegisters) - callData->argc(); + if (stackSpaceToAdd > 0) { + // clear out remaining arguments and local registers + for (int i = 0; i < stackSpaceToAdd; ++i) + engine->jsStackTop[i] = Encode::undefined(); + engine->jsStackTop += stackSpaceToAdd; + } + + CppStackFrame frame; + frame.parent = engine->currentStackFrame; + frame.v4Function = function; + frame.instructionPointer = function->codeData; + frame.jsFrame = callData; + engine->currentStackFrame = &frame; + + const uchar *exceptionHandler = 0; + + QV4::Value &accumulator = frame.jsFrame->accumulator; + QV4::ReturnedValue acc = Encode::undefined(); + + if (QV4::Debugging::Debugger *debugger = engine->debugger()) + debugger->enteringFunction(); + + const uchar *code = function->codeData; for (;;) { - const Instr *genericInstr = reinterpret_cast<const Instr *>(code); -#ifdef MOTH_THREADED_INTERPRETER - goto *jumpTable[genericInstr->common.instructionType]; -#else - switch (genericInstr->common.instructionType) { -#endif + MOTH_DISPATCH() + Q_UNREACHABLE(); // only reached when the dispatch doesn't jump somewhere + + MOTH_BEGIN_INSTR(LoadConst) + acc = constant(function, index).asReturnedValue(); + MOTH_END_INSTR(LoadConst) + + MOTH_BEGIN_INSTR(LoadNull) + acc = Encode::null(); + MOTH_END_INSTR(LoadNull) - MOTH_BEGIN_INSTR(Move) - VALUE(instr.result) = VALUE(instr.source); - MOTH_END_INSTR(Move) + MOTH_BEGIN_INSTR(LoadZero) + acc = Encode(static_cast<int>(0)); + MOTH_END_INSTR(LoadZero) + + MOTH_BEGIN_INSTR(LoadTrue) + acc = Encode(true); + MOTH_END_INSTR(LoadTrue) + + MOTH_BEGIN_INSTR(LoadFalse) + acc = Encode(false); + MOTH_END_INSTR(LoadFalse) + + MOTH_BEGIN_INSTR(LoadUndefined) + acc = Encode::undefined(); + MOTH_END_INSTR(LoadUndefined) + + MOTH_BEGIN_INSTR(LoadInt) + acc = Encode(value); + MOTH_END_INSTR(LoadInt) MOTH_BEGIN_INSTR(MoveConst) - VALUE(instr.result) = instr.source; + STACK_VALUE(destTemp) = constant(function, constIndex); MOTH_END_INSTR(MoveConst) - MOTH_BEGIN_INSTR(SwapTemps) - qSwap(VALUE(instr.left), VALUE(instr.right)); - MOTH_END_INSTR(MoveTemp) + MOTH_BEGIN_INSTR(LoadReg) + acc = STACK_VALUE(reg).asReturnedValue(); + MOTH_END_INSTR(LoadReg) + + MOTH_BEGIN_INSTR(StoreReg) + STACK_VALUE(reg) = acc; + MOTH_END_INSTR(StoreReg) + + MOTH_BEGIN_INSTR(MoveReg) + STACK_VALUE(destReg) = STACK_VALUE(srcReg); + MOTH_END_INSTR(MoveReg) + + MOTH_BEGIN_INSTR(LoadLocal) + auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); + 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()); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC); + MOTH_END_INSTR(StoreLocal) + + MOTH_BEGIN_INSTR(LoadScopedLocal) + auto cc = getScope(stack, scope); + acc = cc->locals[index].asReturnedValue(); + MOTH_END_INSTR(LoadScopedLocal) + + MOTH_BEGIN_INSTR(StoreScopedLocal) + CHECK_EXCEPTION; + auto cc = getScope(stack, scope); + QV4::WriteBarrier::write(engine, cc, cc->locals.values + index, ACC); + MOTH_END_INSTR(StoreScopedLocal) MOTH_BEGIN_INSTR(LoadRuntimeString) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - VALUE(instr.result) = engine->current->compilationUnit->runtimeStrings[instr.stringId]; + acc = function->compilationUnit->runtimeStrings[stringId]->asReturnedValue(); MOTH_END_INSTR(LoadRuntimeString) MOTH_BEGIN_INSTR(LoadRegExp) -// TRACE(value, "%s", instr.value.toString(context)->toQString().toUtf8().constData()); - Heap::RegExpObject *ro = engine->newRegExpObject( - static_cast<CompiledData::CompilationUnit*>(engine->current->compilationUnit) - ->runtimeRegularExpressions[instr.regExpId].as<RegExp>()); - VALUE(instr.result) = ro; + acc = Runtime::method_regexpLiteral(engine, regExpId); MOTH_END_INSTR(LoadRegExp) MOTH_BEGIN_INSTR(LoadClosure) - STOREVALUE(instr.result, Runtime::method_closure(engine, instr.value)); + acc = Runtime::method_closure(engine, value); MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - STOREVALUE(instr.result, Runtime::method_getActivationProperty(engine, instr.name)); + STORE_IP(); + acc = Runtime::method_loadName(engine, name); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadName) - MOTH_BEGIN_INSTR(GetGlobalLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - STOREVALUE(instr.result, l->globalGetter(l, engine)); - MOTH_END_INSTR(GetGlobalLookup) + MOTH_BEGIN_INSTR(LoadGlobalLookup) + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->globalGetter(l, engine); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadGlobalLookup) + + MOTH_BEGIN_INSTR(StoreNameStrict) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeNameStrict(engine, name, accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(StoreNameSloppy) - MOTH_BEGIN_INSTR(StoreName) - TRACE(inline, "property name = %s", runtimeStrings[instr.name]->toQString().toUtf8().constData()); - Runtime::method_setActivationProperty(engine, instr.name, VALUE(instr.source)); + MOTH_BEGIN_INSTR(StoreNameSloppy) + STORE_IP(); + STORE_ACC(); + Runtime::method_storeNameSloppy(engine, name, accumulator); CHECK_EXCEPTION; - MOTH_END_INSTR(StoreName) + MOTH_END_INSTR(StoreNameSloppy) MOTH_BEGIN_INSTR(LoadElement) - STOREVALUE(instr.result, Runtime::method_getElement(engine, VALUE(instr.base), VALUE(instr.index))); + STORE_IP(); + acc = Runtime::method_loadElement(engine, STACK_VALUE(base), STACK_VALUE(index)); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) - MOTH_BEGIN_INSTR(LoadElementLookup) - QV4::Lookup *l = engine->current->lookups + instr.lookup; - STOREVALUE(instr.result, l->indexedGetter(l, engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(LoadElementLookup) + MOTH_BEGIN_INSTR(LoadElementA) + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadElementA) MOTH_BEGIN_INSTR(StoreElement) - Runtime::method_setElement(engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + if (!Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) - MOTH_BEGIN_INSTR(StoreElementLookup) - QV4::Lookup *l = engine->current->lookups + instr.lookup; - l->indexedSetter(l, engine, VALUE(instr.base), VALUE(instr.index), VALUE(instr.source)); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreElementLookup) - MOTH_BEGIN_INSTR(LoadProperty) - STOREVALUE(instr.result, Runtime::method_getProperty(engine, VALUE(instr.base), instr.name)); + 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_BEGIN_INSTR(GetLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - STOREVALUE(instr.result, l->getter(l, engine, VALUE(instr.base))); + STORE_IP(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->getter(l, engine, STACK_VALUE(base)); + CHECK_EXCEPTION; MOTH_END_INSTR(GetLookup) + MOTH_BEGIN_INSTR(GetLookupA) + STORE_IP(); + STORE_ACC(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->getter(l, engine, accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(GetLookupA) + MOTH_BEGIN_INSTR(StoreProperty) - Runtime::method_setProperty(engine, VALUE(instr.base), instr.name, VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + if (!Runtime::method_storeProperty(engine, STACK_VALUE(base), name, accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(StoreProperty) MOTH_BEGIN_INSTR(SetLookup) - QV4::Lookup *l = engine->current->lookups + instr.index; - l->setter(l, engine, VALUE(instr.base), VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + if (!l->setter(l, engine, STACK_VALUE(base), accumulator) && function->isStrict()) + engine->throwTypeError(); CHECK_EXCEPTION; MOTH_END_INSTR(SetLookup) - MOTH_BEGIN_INSTR(StoreQObjectProperty) - Runtime::method_setQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreQObjectProperty) - - MOTH_BEGIN_INSTR(LoadQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); - MOTH_END_INSTR(LoadQObjectProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - Runtime::method_setQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + STORE_ACC(); + Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreScopeObjectProperty) MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlScopeObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STORE_IP(); + acc = Runtime::method_loadQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadScopeObjectProperty) MOTH_BEGIN_INSTR(StoreContextObjectProperty) - Runtime::method_setQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, VALUE(instr.source)); + STORE_IP(); + STORE_ACC(); + Runtime::method_storeQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); CHECK_EXCEPTION; MOTH_END_INSTR(StoreContextObjectProperty) MOTH_BEGIN_INSTR(LoadContextObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlContextObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); + STORE_IP(); + acc = Runtime::method_loadQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadContextObjectProperty) MOTH_BEGIN_INSTR(LoadIdObject) - STOREVALUE(instr.result, Runtime::method_getQmlIdObject(engine, VALUE(instr.base), instr.index)); + STORE_IP(); + acc = Runtime::method_loadQmlIdObject(engine, STACK_VALUE(base), index); + CHECK_EXCEPTION; MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(LoadAttachedQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlAttachedProperty(engine, instr.attachedPropertiesId, instr.propertyIndex)); - MOTH_END_INSTR(LoadAttachedQObjectProperty) - - MOTH_BEGIN_INSTR(LoadSingletonQObjectProperty) - STOREVALUE(instr.result, Runtime::method_getQmlSingletonQObjectProperty(engine, VALUE(instr.base), instr.propertyIndex, instr.captureRequired)); - MOTH_END_INSTR(LoadSingletonQObjectProperty) - - MOTH_BEGIN_INSTR(Push) - TRACE(inline, "stack size: %u", instr.value); - stackSize = instr.value; - stack = scope.alloc(stackSize); - scopes[1].values = stack; - MOTH_END_INSTR(Push) - MOTH_BEGIN_INSTR(CallValue) -#if 0 //def DO_TRACE_INSTR - if (Debugging::Debugger *debugger = context->engine()->debugger) { - if (QV4::FunctionObject *o = (VALUE(instr.dest)).asFunctionObject()) { - if (Debugging::FunctionDebugInfo *info = debugger->debugInfo(o)) { - QString n = debugger->name(o); - std::cerr << "*** Call to \"" << (n.isNull() ? "<no name>" : qPrintable(n)) << "\" defined @" << info->startLine << ":" << info->startColumn << std::endl; - } - } - } -#endif // DO_TRACE_INSTR - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callValue(engine, VALUE(instr.dest), callData)); + STORE_IP(); + STORE_ACC(); + acc = Runtime::method_callValue(engine, accumulator, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callProperty(engine, instr.name, callData)); + STORE_IP(); + acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callPropertyLookup(engine, instr.lookupIndex, callData)); + STORE_IP(); + acc = Runtime::method_callPropertyLookup(engine, stack + base, lookupIndex, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallPropertyLookup) - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callQmlScopeObjectProperty(engine, instr.index, callData)); - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(engine)->toQString().toUtf8().constData()); - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callQmlContextObjectProperty(engine, instr.index, callData)); - MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(CallElement) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_callElement(engine, VALUE(instr.index), callData)); + STORE_IP(); + acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallElement) - MOTH_BEGIN_INSTR(CallActivationProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callActivationProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CallActivationProperty) + MOTH_BEGIN_INSTR(CallName) + STORE_IP(); + acc = Runtime::method_callName(engine, name, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallName) + + MOTH_BEGIN_INSTR(CallPossiblyDirectEval) + STORE_IP(); + acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData)); + STORE_IP(); + acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); + CHECK_EXCEPTION; MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(SetExceptionHandler) - exceptionHandler = instr.offset ? ((const uchar *)&instr.offset) + instr.offset : 0; + exceptionHandler = offset ? code + offset : nullptr; MOTH_END_INSTR(SetExceptionHandler) - MOTH_BEGIN_INSTR(CallBuiltinThrow) - Runtime::method_throwException(engine, VALUE(instr.arg)); + MOTH_BEGIN_INSTR(ThrowException) + STORE_IP(); + STORE_ACC(); + Runtime::method_throwException(engine, accumulator); + goto catchException; + MOTH_END_INSTR(ThrowException) + + MOTH_BEGIN_INSTR(GetException) + acc = engine->hasException ? engine->exceptionValue->asReturnedValue() : Primitive::emptyValue().asReturnedValue(); + engine->hasException = false; + MOTH_END_INSTR(HasException) + + MOTH_BEGIN_INSTR(SetException) + *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); + MOTH_END_INSTR(PushCatchContext) + + MOTH_BEGIN_INSTR(CreateCallContext) + Heap::ExecutionContext *ctx = static_cast<Heap::ExecutionContext *>(stack[CallData::Context].m()); + stack[CallData::Context] = ExecutionContext::newCallContext(ctx, function, reinterpret_cast<CallData *>(stack)); + MOTH_END_INSTR(CreateCallContext) + + MOTH_BEGIN_INSTR(PushWithContext) + STORE_IP(); + STORE_ACC(); + accumulator = accumulator.toObject(engine); CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinThrow) + STACK_VALUE(reg) = STACK_VALUE(CallData::Context); + ExecutionContext *c = static_cast<ExecutionContext *>(stack + CallData::Context); + STACK_VALUE(CallData::Context) = Runtime::method_createWithContext(c, accumulator); + MOTH_END_INSTR(PushWithContext) + + MOTH_BEGIN_INSTR(PopContext) + STACK_VALUE(CallData::Context) = STACK_VALUE(reg); + MOTH_END_INSTR(PopContext) + + MOTH_BEGIN_INSTR(ForeachIteratorObject) + STORE_ACC(); + acc = Runtime::method_foreachIterator(engine, accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(ForeachIteratorObject) - MOTH_BEGIN_INSTR(CallBuiltinUnwindException) - STOREVALUE(instr.result, Runtime::method_unwindException(engine)); - MOTH_END_INSTR(CallBuiltinUnwindException) + MOTH_BEGIN_INSTR(ForeachNextPropertyName) + STORE_ACC(); + acc = Runtime::method_foreachNextPropertyName(accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(ForeachNextPropertyName) + + 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(DeleteSubscript) + if (!Runtime::method_deleteElement(engine, STACK_VALUE(base), STACK_VALUE(index))) { + if (function->isStrict()) { + STORE_IP(); + engine->throwTypeError(); + goto catchException; + } + acc = Encode(false); + } else { + acc = Encode(true); + } + MOTH_END_INSTR(DeleteSubscript) + + MOTH_BEGIN_INSTR(DeleteName) + if (!Runtime::method_deleteName(engine, name)) { + if (function->isStrict()) { + STORE_IP(); + QString n = function->compilationUnit->runtimeStrings[name]->toQString(); + engine->throwSyntaxError(QStringLiteral("Can't delete property %1").arg(n)); + goto catchException; + } + acc = Encode(false); + } else { + acc = Encode(true); + } + MOTH_END_INSTR(DeleteName) + + MOTH_BEGIN_INSTR(TypeofName) + acc = Runtime::method_typeofName(engine, name); + MOTH_END_INSTR(TypeofName) + + MOTH_BEGIN_INSTR(TypeofValue) + STORE_ACC(); + acc = Runtime::method_typeofValue(engine, accumulator); + MOTH_END_INSTR(TypeofValue) + + MOTH_BEGIN_INSTR(DeclareVar) + Runtime::method_declareVar(engine, isDeletable, varName); + MOTH_END_INSTR(DeclareVar) + + MOTH_BEGIN_INSTR(DefineArray) + QV4::Value *arguments = stack + args; + acc = Runtime::method_arrayLiteral(engine, arguments, argc); + MOTH_END_INSTR(DefineArray) + + MOTH_BEGIN_INSTR(DefineObjectLiteral) + QV4::Value *arguments = stack + args; + acc = Runtime::method_objectLiteral(engine, arguments, internalClassId, arrayValueCount, arrayGetterSetterCountAndFlags); + MOTH_END_INSTR(DefineObjectLiteral) + + MOTH_BEGIN_INSTR(CreateMappedArgumentsObject) + acc = Runtime::method_createMappedArgumentsObject(engine); + MOTH_END_INSTR(CreateMappedArgumentsObject) + + MOTH_BEGIN_INSTR(CreateUnmappedArgumentsObject) + acc = Runtime::method_createUnmappedArgumentsObject(engine); + MOTH_END_INSTR(CreateUnmappedArgumentsObject) + + MOTH_BEGIN_INSTR(ConvertThisToObject) + Value *t = &stack[CallData::This]; + if (!t->isObject()) { + if (t->isNullOrUndefined()) { + *t = engine->globalObject->asReturnedValue(); + } else { + *t = t->toObject(engine)->asReturnedValue(); + CHECK_EXCEPTION; + } + } + MOTH_END_INSTR(ConvertThisToObject) - MOTH_BEGIN_INSTR(CallBuiltinPushCatchScope) - Runtime::method_pushCatchScope(static_cast<QV4::NoThrowEngine*>(engine), instr.name); - MOTH_END_INSTR(CallBuiltinPushCatchScope) - - MOTH_BEGIN_INSTR(CallBuiltinPushScope) - Runtime::method_pushWithScope(VALUE(instr.arg), static_cast<QV4::NoThrowEngine*>(engine)); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinPushScope) - - MOTH_BEGIN_INSTR(CallBuiltinPopScope) - Runtime::method_popScope(static_cast<QV4::NoThrowEngine*>(engine)); - MOTH_END_INSTR(CallBuiltinPopScope) - - MOTH_BEGIN_INSTR(CallBuiltinForeachIteratorObject) - STOREVALUE(instr.result, Runtime::method_foreachIterator(engine, VALUE(instr.arg))); - MOTH_END_INSTR(CallBuiltinForeachIteratorObject) - - MOTH_BEGIN_INSTR(CallBuiltinForeachNextPropertyName) - STOREVALUE(instr.result, Runtime::method_foreachNextPropertyName(VALUE(instr.arg))); - MOTH_END_INSTR(CallBuiltinForeachNextPropertyName) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteMember) - STOREVALUE(instr.result, Runtime::method_deleteMember(engine, VALUE(instr.base), instr.member)); - MOTH_END_INSTR(CallBuiltinDeleteMember) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteSubscript) - STOREVALUE(instr.result, Runtime::method_deleteElement(engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(CallBuiltinDeleteSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinDeleteName) - STOREVALUE(instr.result, Runtime::method_deleteName(engine, instr.name)); - MOTH_END_INSTR(CallBuiltinDeleteName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofScopeObjectProperty) - STOREVALUE(instr.result, Runtime::method_typeofScopeObjectProperty(engine, VALUE(instr.base), instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofContextObjectProperty) - STOREVALUE(instr.result, Runtime::method_typeofContextObjectProperty(engine, VALUE(instr.base), instr.index)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofMember) - STOREVALUE(instr.result, Runtime::method_typeofMember(engine, VALUE(instr.base), instr.member)); - MOTH_END_INSTR(CallBuiltinTypeofMember) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofSubscript) - STOREVALUE(instr.result, Runtime::method_typeofElement(engine, VALUE(instr.base), VALUE(instr.index))); - MOTH_END_INSTR(CallBuiltinTypeofSubscript) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofName) - STOREVALUE(instr.result, Runtime::method_typeofName(engine, instr.name)); - MOTH_END_INSTR(CallBuiltinTypeofName) - - MOTH_BEGIN_INSTR(CallBuiltinTypeofValue) - STOREVALUE(instr.result, Runtime::method_typeofValue(engine, VALUE(instr.value))); - MOTH_END_INSTR(CallBuiltinTypeofValue) - - MOTH_BEGIN_INSTR(CallBuiltinDeclareVar) - Runtime::method_declareVar(engine, instr.isDeletable, instr.varName); - MOTH_END_INSTR(CallBuiltinDeclareVar) - - MOTH_BEGIN_INSTR(CallBuiltinDefineArray) - Q_ASSERT(instr.args + instr.argc <= stackSize); - QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::method_arrayLiteral(engine, args, instr.argc)); - MOTH_END_INSTR(CallBuiltinDefineArray) - - MOTH_BEGIN_INSTR(CallBuiltinDefineObjectLiteral) - QV4::Value *args = stack + instr.args; - STOREVALUE(instr.result, Runtime::method_objectLiteral(engine, args, instr.internalClassId, instr.arrayValueCount, instr.arrayGetterSetterCountAndFlags)); - MOTH_END_INSTR(CallBuiltinDefineObjectLiteral) - - MOTH_BEGIN_INSTR(CallBuiltinSetupArgumentsObject) - STOREVALUE(instr.result, Runtime::method_setupArgumentsObject(engine)); - MOTH_END_INSTR(CallBuiltinSetupArgumentsObject) - - MOTH_BEGIN_INSTR(CallBuiltinConvertThisToObject) - Runtime::method_convertThisToObject(engine); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallBuiltinConvertThisToObject) - - MOTH_BEGIN_INSTR(CreateValue) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructValue(engine, VALUE(instr.func), callData)); - MOTH_END_INSTR(CreateValue) - - MOTH_BEGIN_INSTR(CreateProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_constructProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CreateProperty) - - MOTH_BEGIN_INSTR(ConstructPropertyLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = VALUE(instr.base); - STOREVALUE(instr.result, Runtime::method_constructPropertyLookup(engine, instr.index, callData)); - MOTH_END_INSTR(ConstructPropertyLookup) - - MOTH_BEGIN_INSTR(CreateActivationProperty) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructActivationProperty(engine, instr.name, callData)); - MOTH_END_INSTR(CreateActivationProperty) - - MOTH_BEGIN_INSTR(ConstructGlobalLookup) - Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); - QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = quint32(Value::ValueTypeInternal::Integer); - callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, Runtime::method_constructGlobalLookup(engine, instr.index, callData)); - MOTH_END_INSTR(ConstructGlobalLookup) + 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(Jump) - code = ((const uchar *)&instr.offset) + instr.offset; + code += offset; MOTH_END_INSTR(Jump) - MOTH_BEGIN_INSTR(JumpEq) - bool cond = VALUEPTR(instr.condition)->toBoolean(); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (cond) - code = ((const uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(JumpEq) + MOTH_BEGIN_INSTR(JumpTrue) + //### store a type hint, and if the input is a bool, do: + // ((acc & 1) == 1) + // because if(1) will end up here with an integer in the accumulator + if ((ACC.integerCompatible() && ACC.int_32()) || ACC.toBoolean()) + code += offset; + MOTH_END_INSTR(JumpTrue) + + MOTH_BEGIN_INSTR(JumpFalse) + //### see comment for JumpTrue + if ((ACC.integerCompatible() && !ACC.int_32()) || !ACC.toBoolean()) + code += offset; + MOTH_END_INSTR(JumpFalse) + + MOTH_BEGIN_INSTR(CmpEqNull) + acc = Encode(ACC.isNullOrUndefined()); + MOTH_END_INSTR(CmpEqNull) + + MOTH_BEGIN_INSTR(CmpNeNull) + acc = Encode(!ACC.isNullOrUndefined()); + MOTH_END_INSTR(CmpNeNull) + + MOTH_BEGIN_INSTR(CmpEqInt) + if (ACC.isIntOrBool()) { + acc = Encode(ACC.int_32() == lhs); + } else { + STORE_ACC(); + acc = Encode(compareEqualInt(accumulator, ACC, lhs)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpEqInt) + + MOTH_BEGIN_INSTR(CmpNeInt) + if (ACC.isIntOrBool()) { + acc = Encode(bool(ACC.int_32() != lhs)); + } else { + STORE_ACC(); + acc = Encode(!compareEqualInt(accumulator, ACC, lhs)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpNeInt) + + MOTH_BEGIN_INSTR(CmpEq) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.asReturnedValue() == ACC.asReturnedValue())) { + acc = Encode(!ACC.isNaN()); + } else if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() == ACC.int_32()); + } else { + STORE_ACC(); + acc = Encode(compareEqual(left, accumulator)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpEq) + + MOTH_BEGIN_INSTR(CmpNe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(bool(left.int_32() != ACC.int_32())); + } else { + STORE_ACC(); + acc = Encode(!compareEqual(left, accumulator)); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpNe) + + MOTH_BEGIN_INSTR(CmpGt) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() > ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() > ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareGreaterThan(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpGt) + + MOTH_BEGIN_INSTR(CmpGe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() >= ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() >= ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareGreaterEqual(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpGe) + + MOTH_BEGIN_INSTR(CmpLt) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() < ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() < ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareLessThan(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpLt) + + MOTH_BEGIN_INSTR(CmpLe) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(left.isInteger() && ACC.isInteger())) { + acc = Encode(left.int_32() <= ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() <= ACC.asDouble()); + } else { + STORE_ACC(); + acc = Encode(bool(Runtime::method_compareLessEqual(left, accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpLe) + + MOTH_BEGIN_INSTR(CmpStrictEqual) + if (STACK_VALUE(lhs).rawValue() == ACC.rawValue() && !ACC.isNaN()) { + acc = Encode(true); + } else { + STORE_ACC(); + acc = Encode(bool(RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator))); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(CmpStrictEqual) + + MOTH_BEGIN_INSTR(CmpStrictNotEqual) + if (STACK_VALUE(lhs).rawValue() != ACC.rawValue() || ACC.isNaN()) { + STORE_ACC(); + acc = Encode(!RuntimeHelpers::strictEqual(STACK_VALUE(lhs), accumulator)); + CHECK_EXCEPTION; + } else { + acc = Encode(false); + } + MOTH_END_INSTR(CmpStrictNotEqual) - MOTH_BEGIN_INSTR(JumpNe) - bool cond = VALUEPTR(instr.condition)->toBoolean(); - TRACE(condition, "%s", cond ? "TRUE" : "FALSE"); - if (!cond) - code = ((const uchar *)&instr.offset) + instr.offset; - MOTH_END_INSTR(JumpNe) + MOTH_BEGIN_INSTR(CmpIn) + STORE_ACC(); + acc = Runtime::method_in(engine, STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(CmpIn) + + MOTH_BEGIN_INSTR(CmpInstanceOf) + STORE_ACC(); + acc = Runtime::method_instanceof(engine, STACK_VALUE(lhs), accumulator); + 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) - STOREVALUE(instr.result, Runtime::method_uNot(VALUE(instr.source))); + if (ACC.integerCompatible()) { + acc = Encode(!static_cast<bool>(ACC.int_32())); + } else { + acc = Encode(!Value::toBooleanImpl(ACC)); + } MOTH_END_INSTR(UNot) - MOTH_BEGIN_INSTR(UNotBool) - bool b = VALUE(instr.source).booleanValue(); - VALUE(instr.result) = QV4::Encode(!b); - MOTH_END_INSTR(UNotBool) - MOTH_BEGIN_INSTR(UPlus) - STOREVALUE(instr.result, Runtime::method_uPlus(VALUE(instr.source))); + if (Q_UNLIKELY(!ACC.isNumber())) { + acc = Encode(ACC.toNumberImpl()); + CHECK_EXCEPTION; + } MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) - STOREVALUE(instr.result, Runtime::method_uMinus(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + int a = ACC.int_32(); + if (a == 0 || a == std::numeric_limits<int>::min()) { + acc = Encode(-static_cast<double>(a)); + } else { + acc = sub_int32(0, ACC.int_32()); + } + } else if (ACC.isDouble()) { + acc ^= (1ull << 63); // simply flip sign bit + } else { + acc = Encode(-ACC.toNumberImpl()); + CHECK_EXCEPTION; + } MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) - STOREVALUE(instr.result, Runtime::method_complement(VALUE(instr.source))); + VALUE_TO_INT(a, ACC); + acc = Encode(~a); MOTH_END_INSTR(UCompl) - MOTH_BEGIN_INSTR(UComplInt) - VALUE(instr.result) = QV4::Encode((int)~VALUE(instr.source).integerValue()); - MOTH_END_INSTR(UComplInt) - MOTH_BEGIN_INSTR(Increment) - STOREVALUE(instr.result, Runtime::method_increment(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + acc = add_int32(ACC.int_32(), 1); + } else if (ACC.isDouble()) { + acc = QV4::Encode(ACC.doubleValue() + 1.); + } else { + acc = Encode(ACC.toNumberImpl() + 1.); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - STOREVALUE(instr.result, Runtime::method_decrement(VALUE(instr.source))); + if (Q_LIKELY(ACC.integerCompatible())) { + acc = sub_int32(ACC.int_32(), 1); + } else if (ACC.isDouble()) { + acc = QV4::Encode(ACC.doubleValue() - 1.); + } else { + acc = Encode(ACC.toNumberImpl() - 1.); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Decrement) - MOTH_BEGIN_INSTR(Binop) - QV4::Runtime::BinaryOperation op = *reinterpret_cast<QV4::Runtime::BinaryOperation *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); - STOREVALUE(instr.result, op(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Binop) - MOTH_BEGIN_INSTR(Add) - STOREVALUE(instr.result, Runtime::method_add(engine, VALUE(instr.lhs), VALUE(instr.rhs))); + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = add_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() + ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_add(engine, left, accumulator); + CHECK_EXCEPTION; + } MOTH_END_INSTR(Add) + MOTH_BEGIN_INSTR(Sub) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = sub_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() - ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_sub(left, accumulator); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(Sub) + + MOTH_BEGIN_INSTR(Mul) + const Value left = STACK_VALUE(lhs); + if (Q_LIKELY(Value::integerCompatible(left, ACC))) { + acc = mul_int32(left.int_32(), ACC.int_32()); + } else if (left.isNumber() && ACC.isNumber()) { + acc = Encode(left.asDouble() * ACC.asDouble()); + } else { + STORE_ACC(); + acc = Runtime::method_mul(left, accumulator); + CHECK_EXCEPTION; + } + MOTH_END_INSTR(Mul) + + MOTH_BEGIN_INSTR(Div) + STORE_ACC(); + acc = Runtime::method_div(STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(Div) + + MOTH_BEGIN_INSTR(Mod) + STORE_ACC(); + acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); + CHECK_EXCEPTION; + MOTH_END_INSTR(Mod) + MOTH_BEGIN_INSTR(BitAnd) - STOREVALUE(instr.result, Runtime::method_bitAnd(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l & a); MOTH_END_INSTR(BitAnd) MOTH_BEGIN_INSTR(BitOr) - STOREVALUE(instr.result, Runtime::method_bitOr(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l | a); MOTH_END_INSTR(BitOr) MOTH_BEGIN_INSTR(BitXor) - STOREVALUE(instr.result, Runtime::method_bitXor(VALUE(instr.lhs), VALUE(instr.rhs))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l ^ a); MOTH_END_INSTR(BitXor) + MOTH_BEGIN_INSTR(UShr) + uint l = STACK_VALUE(lhs).toUInt32(); + VALUE_TO_INT(a, ACC); + acc = Encode(l >> uint(a & 0x1f)); + MOTH_END_INSTR(UShr) + MOTH_BEGIN_INSTR(Shr) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l >> (a & 0x1f)); MOTH_END_INSTR(Shr) MOTH_BEGIN_INSTR(Shl) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << (VALUEPTR(instr.rhs)->toInt32() & 0x1f)))); + VALUE_TO_INT(l, STACK_VALUE(lhs)); + VALUE_TO_INT(a, ACC); + acc = Encode(l << (a & 0x1f)); MOTH_END_INSTR(Shl) MOTH_BEGIN_INSTR(BitAndConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs & instr.rhs))); - MOTH_END_INSTR(BitAnd) + VALUE_TO_INT(a, ACC); + acc = Encode(a & rhs); + CHECK_EXCEPTION; + MOTH_END_INSTR(BitAndConst) MOTH_BEGIN_INSTR(BitOrConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs | instr.rhs))); - MOTH_END_INSTR(BitOr) + VALUE_TO_INT(a, ACC); + acc = Encode(a | rhs); + MOTH_END_INSTR(BitOrConst) MOTH_BEGIN_INSTR(BitXorConst) - int lhs = VALUEPTR(instr.lhs)->toInt32(); - STOREVALUE(instr.result, QV4::Encode((int)(lhs ^ instr.rhs))); - MOTH_END_INSTR(BitXor) + VALUE_TO_INT(a, ACC); + acc = Encode(a ^ rhs); + MOTH_END_INSTR(BitXorConst) + + MOTH_BEGIN_INSTR(UShrConst) + acc = Encode(ACC.toUInt32() >> uint(rhs)); + MOTH_END_INSTR(UShrConst) MOTH_BEGIN_INSTR(ShrConst) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() >> instr.rhs))); + VALUE_TO_INT(a, ACC); + acc = Encode(a >> rhs); MOTH_END_INSTR(ShrConst) MOTH_BEGIN_INSTR(ShlConst) - STOREVALUE(instr.result, QV4::Encode((int)(VALUEPTR(instr.lhs)->toInt32() << instr.rhs))); + VALUE_TO_INT(a, ACC); + acc = Encode(a << rhs); MOTH_END_INSTR(ShlConst) - MOTH_BEGIN_INSTR(Mul) - STOREVALUE(instr.result, Runtime::method_mul(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Mul) - - MOTH_BEGIN_INSTR(Sub) - STOREVALUE(instr.result, Runtime::method_sub(VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(Sub) - - MOTH_BEGIN_INSTR(BinopContext) - QV4::Runtime::BinaryOperationContext op = *reinterpret_cast<QV4::Runtime::BinaryOperationContext *>(reinterpret_cast<char *>(&engine->runtime.runtimeMethods[instr.alu])); - STOREVALUE(instr.result, op(engine, VALUE(instr.lhs), VALUE(instr.rhs))); - MOTH_END_INSTR(BinopContext) - MOTH_BEGIN_INSTR(Ret) -// TRACE(Ret, "returning value %s", result.toString(context)->toQString().toUtf8().constData()); - return VALUE(instr.result).asReturnedValue(); + goto functionExit; MOTH_END_INSTR(Ret) #ifndef QT_NO_QML_DEBUGGER MOTH_BEGIN_INSTR(Debug) - engine->current->lineNumber = instr.lineNumber; - QV4::Debugging::Debugger *debugger = engine->debugger(); - if (debugger && debugger->pauseAtNextOpportunity()) - debugger->maybeBreakAtInstruction(); - if (qt_v4IsDebugging) - qt_v4CheckForBreak(engine->currentContext); + STORE_IP(); + debug_slowPath(engine); MOTH_END_INSTR(Debug) - - MOTH_BEGIN_INSTR(Line) - engine->current->lineNumber = instr.lineNumber; - if (qt_v4IsDebugging) - qt_v4CheckForBreak(engine->currentContext); - MOTH_END_INSTR(Line) #endif // QT_NO_QML_DEBUGGER - MOTH_BEGIN_INSTR(LoadThis) - VALUE(instr.result) = engine->currentContext->thisObject(); - MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlContext) - VALUE(instr.result) = Runtime::method_getQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); + STACK_VALUE(result) = Runtime::method_loadQmlContext(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlContext) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - VALUE(instr.result) = Runtime::method_getQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); + STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast<QV4::NoThrowEngine*>(engine)); MOTH_END_INSTR(LoadQmlImportedScripts) MOTH_BEGIN_INSTR(LoadQmlSingleton) - VALUE(instr.result) = Runtime::method_getQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), instr.name); + acc = Runtime::method_loadQmlSingleton(static_cast<QV4::NoThrowEngine*>(engine), name); MOTH_END_INSTR(LoadQmlSingleton) -#ifdef MOTH_THREADED_INTERPRETER - // nothing to do -#else - default: - qFatal("QQmlJS::Moth::VME: Internal error - unknown instruction %d", genericInstr->common.instructionType); - break; - } -#endif - - Q_ASSERT(false); catchException: Q_ASSERT(engine->hasException); - if (!exceptionHandler) - return QV4::Encode::undefined(); + if (!exceptionHandler) { + acc = Encode::undefined(); + goto functionExit; + } code = exceptionHandler; } -} -QV4::ReturnedValue VME::exec(ExecutionEngine *engine, const uchar *code) -{ - VME vme; - QV4::Debugging::Debugger *debugger = engine->debugger(); - if (debugger) - debugger->enteringFunction(); - QV4::ReturnedValue retVal = vme.run(engine, code); - if (debugger) - debugger->leavingFunction(retVal); - return retVal; +functionExit: + if (QV4::Debugging::Debugger *debugger = engine->debugger()) + debugger->leavingFunction(ACC.asReturnedValue()); + engine->currentStackFrame = frame.parent; + engine->jsStackTop = jsStackTop; + + return acc; } diff --git a/src/qml/jsruntime/qv4vme_moth_p.h b/src/qml/jsruntime/qv4vme_moth_p.h index 8d46207f2b..3a1e7b6637 100644 --- a/src/qml/jsruntime/qv4vme_moth_p.h +++ b/src/qml/jsruntime/qv4vme_moth_p.h @@ -65,10 +65,7 @@ namespace Moth { class VME { public: - static QV4::ReturnedValue exec(QV4::ExecutionEngine *, const uchar *); - -private: - QV4::ReturnedValue run(QV4::ExecutionEngine *, const uchar *code); + static QV4::ReturnedValue exec(CallData *callData, QV4::Function *); }; } // namespace Moth |