diff options
Diffstat (limited to 'src/qml/jsruntime/qv4runtime.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 1126 |
1 files changed, 732 insertions, 394 deletions
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 6a242ba5f6..5977360080 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1,85 +1,48 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "qv4global_p.h" -#include "qv4engine_p.h" +// Copyright (C) 2016 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + #include "qv4runtime_p.h" -#ifndef V4_BOOTSTRAP -#include "qv4object_p.h" -#include "qv4objectproto_p.h" -#include "qv4globalobject_p.h" -#include "qv4stringobject_p.h" -#include "qv4argumentsobject_p.h" -#include "qv4objectiterator_p.h" -#include "qv4dateobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" -#include "qv4numberobject_p.h" -#include "qv4regexp_p.h" -#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> #include <private/qqmljavascriptexpression_p.h> -#include "qv4qobjectwrapper_p.h" -#include "qv4symbol_p.h" -#include "qv4generatorobject_p.h" -#include <private/qv8engine_p.h> -#endif - -#include <QtCore/QDebug> -#include <cassert> -#include <cstdio> -#include <stdlib.h> +#include <private/qqmljsast_p.h> +#include <private/qqmltypewrapper_p.h> +#include <private/qqmlvaluetypewrapper_p.h> +#include <private/qv4argumentsobject_p.h> +#include <private/qv4engine_p.h> +#include <private/qv4function_p.h> +#include <private/qv4generatorobject_p.h> +#include <private/qv4global_p.h> +#include <private/qv4globalobject_p.h> +#include <private/qv4jscall_p.h> +#include <private/qv4lookup_p.h> +#include <private/qv4math_p.h> +#include <private/qv4object_p.h> +#include <private/qv4qmlcontext_p.h> +#include <private/qv4qobjectwrapper_p.h> +#include <private/qv4regexp_p.h> +#include <private/qv4regexpobject_p.h> +#include <private/qv4scopedvalue_p.h> +#include <private/qv4stackframe_p.h> +#include <private/qv4symbol_p.h> #include <wtf/MathExtras.h> +#include <QtCore/private/qlocale_tools_p.h> +#include <QtCore/qdebug.h> + #ifdef QV4_COUNT_RUNTIME_FUNCTIONS -# include <QtCore/QBuffer> -# include <QtCore/QDebug> +# include <QtCore/qbuffer.h> #endif // QV4_COUNT_RUNTIME_FUNCTIONS +#include <cassert> +#include <cstdio> +#include <stdlib.h> + QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcCoercingTypeAssertion, "qt.qml.coercingTypeAssertion"); + namespace QV4 { #ifdef QV4_COUNT_RUNTIME_FUNCTIONS @@ -181,7 +144,7 @@ struct RuntimeCounters::Data { } std::sort(lines.begin(), lines.end(), Line::less); outs << lines.size() << " counters:" << endl; - for (const Line &line : qAsConst(lines)) + for (const Line &line : std::as_const(lines)) outs << qSetFieldWidth(10) << line.count << qSetFieldWidth(0) << " | " << line.func << " | " << pretty(line.tag1) @@ -223,13 +186,9 @@ void RuntimeCounters::count(const char *func, uint tag1, uint tag2) #endif // QV4_COUNT_RUNTIME_FUNCTIONS -#ifndef V4_BOOTSTRAP - -Runtime::Runtime() +static QV4::Lookup *runtimeLookup(Function *f, uint i) { -#define INIT_METHOD(returnvalue, name, args) runtimeMethods[name] = reinterpret_cast<void*>(&method_##name); -FOR_EACH_RUNTIME_METHOD(INIT_METHOD) -#undef INIT_METHOD + return f->executableCompilationUnit()->runtimeLookups + i; } void RuntimeHelpers::numberToString(QString *result, double num, int radix) @@ -258,7 +217,7 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) *result = qdtoa(num, &decpt, &sign); if (decpt <= ecma_shortest_low || decpt > ecma_shortest_high) { - if (result->length() > 1) + if (result->size() > 1) result->insert(1, dot); result->append(QLatin1Char('e')); if (decpt > 0) @@ -266,10 +225,10 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->append(QString::number(decpt - 1)); } else if (decpt <= 0) { result->prepend(QLatin1String("0.") + QString(-decpt, zero)); - } else if (decpt < result->length()) { + } else if (decpt < result->size()) { result->insert(decpt, dot); } else { - result->append(QString(decpt - result->length(), zero)); + result->append(QString(decpt - result->size(), zero)); } if (sign && num) @@ -320,21 +279,22 @@ void RuntimeHelpers::numberToString(QString *result, double num, int radix) result->prepend(QLatin1Char('-')); } -ReturnedValue Runtime::method_closure(ExecutionEngine *engine, int functionId) +ReturnedValue Runtime::Closure::call(ExecutionEngine *engine, int functionId) { - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); if (clos->isGenerator()) return GeneratorFunction::create(current, clos)->asReturnedValue(); return FunctionObject::createScriptFunction(current, clos)->asReturnedValue(); } -bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, const Value &index) +Bool Runtime::DeleteProperty_NoThrow::call(ExecutionEngine *engine, const Value &base, const Value &index) { Scope scope(engine); ScopedObject o(scope, base.toObject(engine)); - if (scope.engine->hasException) + if (scope.hasException()) return Encode::undefined(); Q_ASSERT(o); @@ -344,14 +304,36 @@ bool Runtime::method_deleteProperty(ExecutionEngine *engine, const Value &base, return o->deleteProperty(key); } -bool Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::DeleteProperty::call(ExecutionEngine *engine, QV4::Function *function, const QV4::Value &base, const QV4::Value &index) +{ + if (!Runtime::DeleteProperty_NoThrow::call(engine, base, index)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +Bool Runtime::DeleteName_NoThrow::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).deleteProperty(name); + return engine->currentContext()->deleteProperty(name); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) +ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *function, int name) +{ + if (!Runtime::DeleteName_NoThrow::call(engine, name)) { + if (function->isStrict()) + engine->throwTypeError(); + return Encode(false); + } else { + return Encode(true); + } +} + +static QV4::ReturnedValue doInstanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) { // 11.8.6, 5: rval must be an Object const Object *rhs = rval.as<Object>(); @@ -367,16 +349,78 @@ QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Val Scope scope(engine); ScopedValue hasInstance(scope, rhs->get(engine->symbol_hasInstance())); if (hasInstance->isUndefined()) - return rhs->instanceOf(lval); + return Encode(rhs->instanceOf(lval)); + FunctionObject *fHasInstance = hasInstance->as<FunctionObject>(); if (!fHasInstance) return engine->throwTypeError(); - ScopedValue result(scope, fHasInstance->call(&rval, &lval, 1)); - return Encode(result->toBoolean()); + return Encode(fHasInstance->call(&rval, &lval, 1)); +} + +QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +{ + Scope scope(engine); + ScopedValue result(scope, doInstanceof(engine, lval, rval)); + return scope.hasException() ? Encode::undefined() : Encode(result->toBoolean()); } -QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::As::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +{ + Scope scope(engine); + ScopedValue result(scope, doInstanceof(engine, lval, rval)); + + if (scope.hasException()) { + // "foo instanceof valueType" must not throw an exception. + // So this can only be an object type. + engine->catchException(); + return Encode::null(); + } + + if (result->toBoolean()) + return lval.asReturnedValue(); + else if (result->isBoolean()) + return Encode::null(); + + if (engine->callingQmlContext()->valueTypesAreAssertable()) + return Encode::undefined(); + + // Try to convert the value type + Scoped<QQmlTypeWrapper> typeWrapper(scope, rval); + if (!typeWrapper) + return Encode::undefined(); + + const auto *stackFrame = engine->currentStackFrame; + if (lval.as<QQmlValueTypeWrapper>()) { + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing between incompatible value types mistakenly yields null rather than" + << " undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent this."; + return Encode::null(); + } + + if (lval.as<QV4::QObjectWrapper>()) { + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing from instances of object types to value types mistakenly yields null" + << " rather than undefined. Add 'pragma ValueTypeBehavior: Assertable' to prevent" + << " this."; + return Encode::null(); + } + + result = coerce(engine, lval, typeWrapper->d()->type(), false); + if (result->isUndefined()) + return Encode::undefined(); + + qCWarning(lcCoercingTypeAssertion).nospace().noquote() + << stackFrame->source() << ':' << stackFrame->lineNumber() << ':' + << " Coercing a value to " << typeWrapper->toQStringNoThrow() + << " using a type assertion. This behavior is deprecated." + << " Add 'pragma ValueTypeBehavior: Assertable' to prevent it."; + return result->asReturnedValue(); +} + +QV4::ReturnedValue Runtime::In::call(ExecutionEngine *engine, const Value &left, const Value &right) { Object *ro = right.objectValue(); if (!ro) @@ -391,7 +435,16 @@ QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left double RuntimeHelpers::stringToNumber(const QString &string) { - const QStringRef s = QStringRef(&string).trimmed(); + // The actual maximum valid length is certainly shorter, but due to the sheer number of + // different number formatting variants, we rather err on the side of caution here. + // For example, you can have up to 772 valid decimal digits left of the dot, as stated in the + // libdoubleconversion sources. The same maximum value would be represented by roughly 3.5 times + // as many binary digits. + const int excessiveLength = 16 * 1024; + if (string.size() > excessiveLength) + return qQNaN(); + + const QStringView s = QStringView(string).trimmed(); if (s.startsWith(QLatin1Char('0'))) { int base = -1; if (s.startsWith(QLatin1String("0x")) || s.startsWith(QLatin1String("0X"))) @@ -489,6 +542,8 @@ ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const ScopedValue conv(scope, object->get(meth1)); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); + if (engine->hasException) + return Encode::undefined(); if (result->isPrimitive()) return result->asReturnedValue(); } @@ -499,6 +554,8 @@ ReturnedValue RuntimeHelpers::ordinaryToPrimitive(ExecutionEngine *engine, const conv = object->get(meth2); if (FunctionObject *o = conv->as<FunctionObject>()) { result = o->call(object, nullptr, 0); + if (engine->hasException) + return Encode::undefined(); if (result->isPrimitive()) return result->asReturnedValue(); } @@ -560,7 +617,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, Value val goto redo; } case Value::Integer_Type: - return RuntimeHelpers::stringFromNumber(engine, value.int_32()); + return engine->newString(QString::number(value.int_32())); default: // double return RuntimeHelpers::stringFromNumber(engine, value.doubleValue()); } // switch @@ -604,13 +661,12 @@ QV4::ReturnedValue RuntimeHelpers::addHelper(ExecutionEngine *engine, const Valu return Encode(x + y); } -ReturnedValue RuntimeHelpers::getTemplateObject(Function *function, int index) +ReturnedValue Runtime::GetTemplateObject::call(Function *function, int index) { - return function->compilationUnit->templateObjectAt(index)->asReturnedValue(); + return function->executableCompilationUnit()->templateObjectAt(index)->asReturnedValue(); } - -void Runtime::method_storeProperty(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) +void Runtime::StoreProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value) { Scope scope(engine); QV4::Function *v4Function = engine->currentStackFrame->v4Function; @@ -635,7 +691,7 @@ static Q_NEVER_INLINE ReturnedValue getElementIntFallback(ExecutionEngine *engin ScopedObject o(scope, object); if (!o) { if (const String *str = object.as<String>()) { - if (idx >= (uint)str->toQString().length()) { + if (idx >= (uint)str->toQString().size()) { return Encode::undefined(); } const QString s = str->toQString().mid(idx, 1); @@ -683,7 +739,7 @@ static Q_NEVER_INLINE ReturnedValue getElementFallback(ExecutionEngine *engine, return o->get(name); } -ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value &object, const Value &index) +ReturnedValue Runtime::LoadElement::call(ExecutionEngine *engine, const Value &object, const Value &index) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -737,7 +793,7 @@ static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Val return o->put(name, value); } -void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) +void Runtime::StoreElement::call(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { if (index.isPositiveInt()) { uint idx = static_cast<uint>(index.int_32()); @@ -759,7 +815,7 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, engine->throwTypeError(); } -ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) +ReturnedValue Runtime::GetIterator::call(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); ScopedObject o(scope, (Object *)nullptr); @@ -773,8 +829,10 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value & ScopedFunctionObject f(scope, o->get(engine->symbol_iterator())); if (!f) return engine->throwTypeError(); - JSCallData cData(scope, 0, nullptr, o); + JSCallData cData(o, nullptr, 0); ScopedObject it(scope, f->call(cData)); + if (engine->hasException) + return Encode::undefined(); if (!it) return engine->throwTypeError(); return it->asReturnedValue(); @@ -782,7 +840,7 @@ ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value & return engine->newForInIteratorObject(o)->asReturnedValue(); } -ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value &iterator, Value *value) +ReturnedValue Runtime::IteratorNext::call(ExecutionEngine *engine, const Value &iterator, Value *value) { // if we throw an exception from here, return true, not undefined. This is to ensure iteratorDone is set to true // and the stack unwinding won't close the iterator @@ -794,7 +852,7 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value engine->throwTypeError(); return Encode(true); } - JSCallData cData(scope, 0, nullptr, &iterator); + JSCallData cData(&iterator, nullptr, 0); ScopedObject o(scope, f->call(cData)); if (scope.hasException()) return Encode(true); @@ -818,7 +876,7 @@ ReturnedValue Runtime::method_iteratorNext(ExecutionEngine *engine, const Value return Encode(false); } -ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) +ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, const Value &received, const Value &iterator, Value *object) { // the return value encodes how to continue the yield* iteration. // true implies iteration is done, false for iteration to continue @@ -837,6 +895,8 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, engine->hasException = false; ScopedValue ret(scope, static_cast<const Object &>(iterator).get(engine->id_return())); + if (engine->hasException) + return Encode(true); if (ret->isUndefined()) { // propagate return() return Encode::undefined(); @@ -851,14 +911,13 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, ScopedValue t(scope, static_cast<const Object &>(iterator).get(engine->id_throw())); if (engine->hasException) - return Encode::undefined(); + return Encode(true); if (t->isUndefined()) { // no throw method on the iterator - ScopedValue done(scope, Encode(false)); - method_iteratorClose(engine, iterator, done); - if (engine->hasException) - return Encode::undefined(); - return engine->throwTypeError(); + IteratorClose::call(engine, iterator); + if (!engine->hasException) + engine->throwTypeError(); + return Encode(true); } f = t->as<FunctionObject>(); arg = exceptionValue; @@ -869,14 +928,18 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, f = next->as<FunctionObject>(); } - if (!f) - return engine->throwTypeError(); + if (!f) { + engine->throwTypeError(); + return Encode(true); + } ScopedObject o(scope, f->call(&iterator, arg, 1)); if (scope.hasException()) return Encode(true); - if (!o) - return engine->throwTypeError(); + if (!o) { + engine->throwTypeError(); + return Encode(true); + } ScopedValue d(scope, o->get(engine->id_done())); if (scope.hasException()) @@ -884,18 +947,15 @@ ReturnedValue Runtime::method_iteratorNextForYieldStar(ExecutionEngine *engine, bool done = d->toBoolean(); if (done) { *object = o->get(engine->id_value()); - return returnCalled ? Encode::undefined() : Encode(true); + return (returnCalled && !engine->hasException) ? Encode::undefined() : Encode(true); } *object = o; return Encode(false); } -ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value &iterator, const Value &done) +ReturnedValue Runtime::IteratorClose::call(ExecutionEngine *engine, const Value &iterator) { Q_ASSERT(iterator.isObject()); - Q_ASSERT(done.isBoolean()); - if (done.booleanValue()) - return Encode::undefined(); Scope scope(engine); ScopedValue e(scope); @@ -930,7 +990,7 @@ ReturnedValue Runtime::method_iteratorClose(ExecutionEngine *engine, const Value return originalCompletion(); } -ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, const Value &iterator) +ReturnedValue Runtime::DestructureRestElement::call(ExecutionEngine *engine, const Value &iterator) { Q_ASSERT(iterator.isObject()); @@ -940,7 +1000,7 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co uint index = 0; while (1) { ScopedValue n(scope); - ScopedValue done(scope, method_iteratorNext(engine, iterator, n)); + ScopedValue done(scope, IteratorNext::call(engine, iterator, n)); if (engine->hasException) return Encode::undefined(); Q_ASSERT(done->isBoolean()); @@ -952,28 +1012,28 @@ ReturnedValue Runtime::method_destructureRestElement(ExecutionEngine *engine, co return array->asReturnedValue(); } -void Runtime::method_storeNameSloppy(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameSloppy::call(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); + ExecutionContext::Error e = engine->currentContext()->setProperty(name, value); if (e == ExecutionContext::RangeError) engine->globalObject->put(name, value); } -void Runtime::method_storeNameStrict(ExecutionEngine *engine, int nameIndex, const Value &value) +void Runtime::StoreNameStrict::call(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); + ExecutionContext::Error e = engine->currentContext()->setProperty(name, value); if (e == ExecutionContext::TypeError) engine->throwTypeError(); else if (e == ExecutionContext::RangeError) engine->throwReferenceError(name); } -ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value &object, int nameIndex) +ReturnedValue Runtime::LoadProperty::call(ExecutionEngine *engine, const Value &object, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); @@ -993,35 +1053,54 @@ ReturnedValue Runtime::method_loadProperty(ExecutionEngine *engine, const Value return o->get(name); } -ReturnedValue Runtime::method_loadName(ExecutionEngine *engine, int nameIndex) +ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - return static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name); + return engine->currentContext()->getProperty(name); } static Object *getSuperBase(Scope &scope) { - if (scope.engine->currentStackFrame->jsFrame->thisObject.isEmpty()) { - scope.engine->throwReferenceError(QStringLiteral("Missing call to super()."), QString(), 0, 0); - return nullptr; + Scoped<JavaScriptFunctionObject> f(scope); + ScopedObject homeObject(scope); + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + + if (frame->jsFrame->thisObject.isEmpty()) { + scope.engine->throwReferenceError( + QStringLiteral("Missing call to super()."), QString(), 0, 0); + return nullptr; + } + + f = Value::fromStaticValue(frame->jsFrame->function); + homeObject = f->getHomeObject(); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + if (frame->thisObject() == nullptr) { + scope.engine->throwReferenceError( + QStringLiteral("Missing call to super()."), QString(), 0, 0); + return nullptr; + } } - ScopedFunctionObject f(scope, scope.engine->currentStackFrame->jsFrame->function); - ScopedObject homeObject(scope, f->getHomeObject()); if (!homeObject) { - ScopedContext ctx(scope, static_cast<ExecutionContext *>(&scope.engine->currentStackFrame->jsFrame->context)); + ScopedContext ctx(scope, scope.engine->currentContext()); Q_ASSERT(ctx); while (ctx) { if (CallContext *c = ctx->asCallContext()) { f = c->d()->function; QV4::Function *fn = f->function(); - if (fn && !fn->isArrowFunction() && !fn->isEval) + if (fn && !fn->isArrowFunction() && fn->kind != Function::Eval) break; } ctx = ctx->d()->outer; } - homeObject = f->getHomeObject(); + if (f) + homeObject = f->getHomeObject(); } if (!homeObject) { scope.engine->throwTypeError(); @@ -1036,7 +1115,7 @@ static Object *getSuperBase(Scope &scope) return proto; } -ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const Value &property) +ReturnedValue Runtime::LoadSuperProperty::call(ExecutionEngine *engine, const Value &property) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1045,10 +1124,21 @@ ReturnedValue Runtime::method_loadSuperProperty(ExecutionEngine *engine, const V ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return Encode::undefined(); - return base->get(key, &engine->currentStackFrame->jsFrame->thisObject); + + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + return base->get(key, &(frame->jsFrame->thisObject.asValue<Value>())); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject())); + return base->get(key, wrapper); + } } -void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &property, const Value &value) +void Runtime::StoreSuperProperty::call(ExecutionEngine *engine, const Value &property, const Value &value) { Scope scope(engine); Object *base = getSuperBase(scope); @@ -1057,27 +1147,85 @@ void Runtime::method_storeSuperProperty(ExecutionEngine *engine, const Value &pr ScopedPropertyKey key(scope, property.toPropertyKey(engine)); if (engine->hasException) return; - bool result = base->put(key, value, &engine->currentStackFrame->jsFrame->thisObject); + + bool result; + if (scope.engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( + scope.engine->currentStackFrame); + result = base->put(key, value, &(frame->jsFrame->thisObject.asValue<Value>())); + } else { + Q_ASSERT(scope.engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>( + scope.engine->currentStackFrame); + Scoped<QObjectWrapper> wrapper(scope, QObjectWrapper::wrap(engine, frame->thisObject())); + result = base->put(key, value, wrapper); + } + if (!result && engine->currentStackFrame->v4Function->isStrict()) engine->throwTypeError(); } -ReturnedValue Runtime::method_loadSuperConstructor(ExecutionEngine *engine, const Value &t) +ReturnedValue Runtime::LoadGlobalLookup::call(ExecutionEngine *engine, Function *f, int index) +{ + Lookup *l = runtimeLookup(f, index); + return l->globalGetter(l, engine); +} + +ReturnedValue Runtime::LoadQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index) +{ + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); + return l->qmlContextPropertyGetter(l, engine, nullptr); +} + +ReturnedValue Runtime::GetLookup::call(ExecutionEngine *engine, Function *f, const Value &base, int index) { - if (engine->currentStackFrame->thisObject() != Value::emptyValue().asReturnedValue()) { - return engine->throwReferenceError(QStringLiteral("super() already called."), QString(), 0, 0); // ### fix line number + Lookup *l = runtimeLookup(f, index); + return l->getter(l, engine, base); +} + +void Runtime::SetLookupSloppy::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + l->setter(l, engine, const_cast<Value &>(base), value); +} + +void Runtime::SetLookupStrict::call(Function *f, const Value &base, int index, const Value &value) +{ + ExecutionEngine *engine = f->internalClass->engine; + QV4::Lookup *l = runtimeLookup(f, index); + if (!l->setter(l, engine, const_cast<Value &>(base), value)) + engine->throwTypeError(); +} + +ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const Value &t) +{ + if (engine->currentStackFrame->isJSTypesFrame()) { + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + if (frame->thisObject() != Value::emptyValue().asReturnedValue()) { + // ### TODO: fix line number + return engine->throwReferenceError( + QStringLiteral("super() already called."), QString(), 0, 0); + } + } else { + Q_ASSERT(engine->currentStackFrame->isMetaTypesFrame()); + MetaTypesStackFrame *frame = static_cast<MetaTypesStackFrame *>(engine->currentStackFrame); + if (frame->thisObject() != nullptr) { + // ### TODO: fix line number + return engine->throwReferenceError( + QStringLiteral("super() already called."), QString(), 0, 0); + } } + const FunctionObject *f = t.as<FunctionObject>(); if (!f) return engine->throwTypeError(); Heap::Object *c = static_cast<const Object &>(t).getPrototypeOf(); - if (!c->vtable()->isFunctionObject || !static_cast<Heap::FunctionObject *>(c)->isConstructor()) + if (!c->vtable()->callAsConstructor) return engine->throwTypeError(); return c->asReturnedValue(); } -#endif // V4_BOOTSTRAP - uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) { Q_ASSERT(x.type() != y.type() || (x.isManaged() && (x.isString() != y.isString()))); @@ -1095,25 +1243,21 @@ uint RuntimeHelpers::equalHelper(const Value &x, const Value &y) double dx = RuntimeHelpers::toNumber(x); return dx == y.asDouble(); } else if (x.isBoolean()) { - return Runtime::method_compareEqual(Value::fromDouble((double) x.booleanValue()), y); + return Runtime::CompareEqual::call(Value::fromDouble((double) x.booleanValue()), y); } else if (y.isBoolean()) { - return Runtime::method_compareEqual(x, Value::fromDouble((double) y.booleanValue())); + return Runtime::CompareEqual::call(x, Value::fromDouble((double) y.booleanValue())); } else { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else Object *xo = x.objectValue(); Object *yo = y.objectValue(); if (yo && (x.isNumber() || x.isString())) { Scope scope(yo->engine()); ScopedValue py(scope, RuntimeHelpers::objectDefaultValue(yo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(x, py); + return Runtime::CompareEqual::call(x, py); } else if (xo && (y.isNumber() || y.isString())) { Scope scope(xo->engine()); ScopedValue px(scope, RuntimeHelpers::objectDefaultValue(xo, PREFERREDTYPE_HINT)); - return Runtime::method_compareEqual(px, y); + return Runtime::CompareEqual::call(px, y); } -#endif } return false; @@ -1129,12 +1273,13 @@ Bool RuntimeHelpers::strictEqual(const Value &x, const Value &y) if (x.isNumber()) return y.isNumber() && x.asDouble() == y.asDouble(); - if (x.isManaged()) + if (x.isManaged()) { return y.isManaged() && x.cast<Managed>()->isEqualTo(y.cast<Managed>()); + } return false; } -QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1144,26 +1289,17 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterThan(pl, pr); -#endif + return Runtime::CompareGreaterThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1171,7 +1307,7 @@ QV4::Bool Runtime::method_compareGreaterThan(const Value &l, const Value &r) return dl > dr; } -QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessThan::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1181,26 +1317,17 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessThan(pl, pr); -#endif + return Runtime::CompareLessThan::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1208,7 +1335,7 @@ QV4::Bool Runtime::method_compareLessThan(const Value &l, const Value &r) return dl < dr; } -QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareGreaterEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1218,26 +1345,17 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sl->lessThan(sr); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareGreaterEqual(pl, pr); -#endif + return Runtime::CompareGreaterEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1245,7 +1363,7 @@ QV4::Bool Runtime::method_compareGreaterEqual(const Value &l, const Value &r) return dl >= dr; } -QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) +QV4::Bool Runtime::CompareLessEqual::call(const Value &l, const Value &r) { TRACE2(l, r); if (l.isInteger() && r.isInteger()) @@ -1255,26 +1373,17 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) String *sl = l.stringValue(); String *sr = r.stringValue(); if (sl && sr) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); - return false; -#else return !sr->lessThan(sl); -#endif } Object *ro = r.objectValue(); Object *lo = l.objectValue(); if (ro || lo) { -#ifdef V4_BOOTSTRAP - Q_UNIMPLEMENTED(); -#else QV4::ExecutionEngine *e = (lo ? lo : ro)->engine(); QV4::Scope scope(e); QV4::ScopedValue pl(scope, lo ? RuntimeHelpers::objectDefaultValue(lo, QV4::NUMBER_HINT) : l.asReturnedValue()); QV4::ScopedValue pr(scope, ro ? RuntimeHelpers::objectDefaultValue(ro, QV4::NUMBER_HINT) : r.asReturnedValue()); - return Runtime::method_compareLessEqual(pl, pr); -#endif + return Runtime::CompareLessEqual::call(pl, pr); } double dl = RuntimeHelpers::toNumber(l); @@ -1282,22 +1391,21 @@ QV4::Bool Runtime::method_compareLessEqual(const Value &l, const Value &r) return dl <= dr; } -#ifndef V4_BOOTSTRAP -Bool Runtime::method_compareInstanceof(ExecutionEngine *engine, const Value &left, const Value &right) +Bool Runtime::CompareInstanceof::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_instanceof(engine, left, right)); + ScopedValue v(scope, Instanceof::call(engine, left, right)); return v->booleanValue(); } -uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const Value &right) +uint Runtime::CompareIn::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); Scope scope(engine); - ScopedValue v(scope, method_in(engine, left, right)); + ScopedValue v(scope, In::call(engine, left, right)); return v->booleanValue(); } @@ -1311,39 +1419,44 @@ static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engin return engine->throwTypeError(msg); } -ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallGlobalLookup::call(ExecutionEngine *engine, uint index, Value argv[], int argc) { Scope scope(engine); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); Value thisObject = Value::undefinedValue(); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, &thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } - return static_cast<FunctionObject &>(function).call(&thisObject, argv, argc); + return checkedResult(engine, static_cast<FunctionObject &>(function).call( + &thisObject, argv, argc)); } -ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallQmlContextPropertyLookup::call(ExecutionEngine *engine, uint index, + Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject)); - if (!function.isFunctionObject()) + if (!function.isFunctionObject()) { return throwPropertyIsNotAFunctionTypeError(engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + } - return static_cast<FunctionObject &>(function).call(thisObject, argv, argc); + return checkedResult(engine, static_cast<FunctionObject &>(function).call( + thisObject, argv, argc)); } -ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) +ReturnedValue Runtime::CallPossiblyDirectEval::call(ExecutionEngine *engine, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); - ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); - ScopedFunctionObject function(scope, ctx.getPropertyAndBase(engine->id_eval(), thisObject)); + ScopedFunctionObject function( + scope, engine->currentContext()->getPropertyAndBase(engine->id_eval(), thisObject)); if (engine->hasException) return Encode::undefined(); @@ -1353,31 +1466,35 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va if (function->d() == engine->evalFunction()->d()) return static_cast<EvalFunction *>(function.getPointer())->evalCall(thisObject, argv, argc, true); - return function->call(thisObject, argv, argc); + return checkedResult(engine, function->call(thisObject, argv, argc)); } -ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallName::call(ExecutionEngine *engine, int nameIndex, Value *argv, int argc) { Scope scope(engine); ScopedValue thisObject(scope); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ExecutionContext &ctx = static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context); - ScopedFunctionObject f(scope, ctx.getPropertyAndBase(name, thisObject)); + ScopedFunctionObject f(scope, engine->currentContext()->getPropertyAndBase(name, thisObject)); if (engine->hasException) return Encode::undefined(); - if (!f) - return throwPropertyIsNotAFunctionTypeError(engine, thisObject, - engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString()); + if (!f) { + return throwPropertyIsNotAFunctionTypeError( + engine, thisObject, engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[nameIndex]->toQString()); + } - return f->call(thisObject, argv, argc); + return checkedResult(engine, f->call(thisObject, argv, argc)); } -ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc) +ReturnedValue Runtime::CallProperty::call(ExecutionEngine *engine, const Value &baseRef, int nameIndex, Value *argv, int argc) { + const Value *base = &baseRef; Scope scope(engine); - ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); + ScopedString name( + scope, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); ScopedObject lookupObject(scope, base); if (!lookupObject) { @@ -1389,7 +1506,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, } if (base->isManaged()) { - Managed *m = static_cast<Managed *>(base); + const Managed *m = static_cast<const Managed *>(base); lookupObject = m->internalClass()->prototype; Q_ASSERT(m->internalClass()->prototype); } else { @@ -1405,56 +1522,49 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, if (!f) { QString error = QStringLiteral("Property '%1' of object %2 is not a function") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + .arg(name->toQString(), base->toQStringNoThrow()); return engine->throwTypeError(error); } - return f->call(base, argv, argc); + return checkedResult(engine, f->call(base, argv, argc)); } -ReturnedValue Runtime::method_callPropertyLookup(ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc) +ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const Value &base, uint index, Value *argv, int argc) { - Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Lookup *l = runtimeLookup(engine->currentStackFrame->v4Function, index); // ok to have the value on the stack here - Value f = Value::fromReturnedValue(l->getter(l, engine, *base)); + Value f = Value::fromReturnedValue(l->getter(l, engine, base)); - if (!f.isFunctionObject()) - return engine->throwTypeError(); - - return static_cast<FunctionObject &>(f).call(base, argv, argc); -} - -ReturnedValue Runtime::method_callElement(ExecutionEngine *engine, Value *base, const Value &index, Value *argv, int argc) -{ - Scope scope(engine); - ScopedValue thisObject(scope, base->toObject(engine)); - base = thisObject; + if (Q_LIKELY(f.isFunctionObject())) + return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc)); - ScopedPropertyKey str(scope, index.toPropertyKey(engine)); - if (engine->hasException) - return Encode::undefined(); + if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) + return checkedResult(engine, handler->call(&base, argv, argc)); - ScopedFunctionObject f(scope, static_cast<Object *>(base)->get(str)); - if (!f) - return engine->throwTypeError(); - - return f->call(base, argv, argc); + const QString message = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(engine->currentStackFrame->v4Function->compilationUnit + ->runtimeStrings[l->nameIndex]->toQString()) + .arg(base.toQStringNoThrow()); + return engine->throwTypeError(message); } -ReturnedValue Runtime::method_callValue(ExecutionEngine *engine, const Value &func, Value *argv, int argc) +ReturnedValue Runtime::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); Value undef = Value::undefinedValue(); - return static_cast<const FunctionObject &>(func).call(&undef, argv, argc); + return checkedResult(engine, static_cast<const FunctionObject &>(func).call( + &undef, argv, argc)); } -ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Value &func, const Value *thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithReceiver::call(ExecutionEngine *engine, const Value &func, + const Value &thisObject, Value argv[], int argc) { if (!func.isFunctionObject()) return engine->throwTypeError(QStringLiteral("%1 is not a function").arg(func.toQStringNoThrow())); - return static_cast<const FunctionObject &>(func).call(thisObject, argv, argc); + return checkedResult(engine, static_cast<const FunctionObject &>(func).call( + &thisObject, argv, argc)); } struct CallArgs { @@ -1480,24 +1590,29 @@ static CallArgs createSpreadArguments(Scope &scope, Value *argv, int argc) } // spread element ++i; - it = Runtime::method_getIterator(scope.engine, argv[i], /* ForInIterator */ 1); - if (scope.engine->hasException) + it = Runtime::GetIterator::call(scope.engine, argv[i], /* ForInIterator */ 1); + if (scope.hasException()) return { nullptr, 0 }; while (1) { - done = Runtime::method_iteratorNext(scope.engine, it, v); - if (scope.engine->hasException) + done = Runtime::IteratorNext::call(scope.engine, it, v); + if (scope.hasException()) return { nullptr, 0 }; Q_ASSERT(done->isBoolean()); if (done->booleanValue()) break; ++argCount; + constexpr auto safetyMargin = 100; // leave some space on the stack for actual work with the elements + if (qint64(scope.engine->jsStackLimit - scope.engine->jsStackTop) < safetyMargin) { + scope.engine->throwRangeError(QLatin1String("Too many elements in array to use it with the spread operator")); + return { nullptr, 0 }; + } v = scope.alloc<Scope::Uninitialized>(); } } return { arguments, argCount }; } -ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) +ReturnedValue Runtime::CallWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &thisObject, Value *argv, int argc) { Q_ASSERT(argc >= 1); if (!function.isFunctionObject()) @@ -1508,10 +1623,11 @@ ReturnedValue Runtime::method_callWithSpread(ExecutionEngine *engine, const Valu if (engine->hasException) return Encode::undefined(); - return static_cast<const FunctionObject &>(function).call(&thisObject, arguments.argv, arguments.argc); + return checkedResult(engine, static_cast<const FunctionObject &>(function).call( + &thisObject, arguments.argv, arguments.argc)); } -ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::Construct::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1519,7 +1635,7 @@ ReturnedValue Runtime::method_construct(ExecutionEngine *engine, const Value &fu return static_cast<const FunctionObject &>(function).callAsConstructor(argv, argc, &newTarget); } -ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) +ReturnedValue Runtime::ConstructWithSpread::call(ExecutionEngine *engine, const Value &function, const Value &newTarget, Value *argv, int argc) { if (!function.isFunctionObject()) return engine->throwTypeError(); @@ -1532,7 +1648,7 @@ ReturnedValue Runtime::method_constructWithSpread(ExecutionEngine *engine, const return static_cast<const FunctionObject &>(function).callAsConstructor(arguments.argv, arguments.argc, &newTarget); } -ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *engine) +ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine *engine) { // IMPORTANT! The JIT assumes that this method has the same amount (or less) arguments than // the jitted function, so it can safely do a tail call. @@ -1544,31 +1660,36 @@ ReturnedValue Runtime::method_tailCall(CppStackFrame *frame, ExecutionEngine *en int argc = tos[StackOffsets::tailCall_argc].int_32(); Q_ASSERT(argc >= 0); - if (!function.isFunctionObject()) + const JavaScriptFunctionObject *jsfo = function.as<JavaScriptFunctionObject>(); + if (!jsfo) { + if (const FunctionObject *fo = function.as<FunctionObject>()) + return checkedResult(engine, fo->call(&thisObject, argv, argc)); return engine->throwTypeError(); + } - const FunctionObject &fo = static_cast<const FunctionObject &>(function); - if (!frame->callerCanHandleTailCall || !fo.canBeTailCalled() || engine->debugger() - || unsigned(argc) > fo.formalParameterCount()) { + if (!frame->callerCanHandleTailCall() || !jsfo->canBeTailCalled() || engine->debugger() + || unsigned(argc) > jsfo->formalParameterCount()) { // Cannot tailcall, do a normal call: - return fo.call(&thisObject, argv, argc); + return checkedResult(engine, jsfo->call(&thisObject, argv, argc)); } - memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(engine, fo.function(), frame->jsFrame->args, argc, frame->callerCanHandleTailCall); - frame->setupJSFrame(frame->savedStackTop, fo, fo.scope(), thisObject, Primitive::undefinedValue()); - engine->jsStackTop = frame->savedStackTop + frame->requiredJSStackFrameSize(); - frame->pendingTailCall = true; + memmove(frame->jsFrame->args, argv, argc * sizeof(Value)); + frame->init(jsfo->function(), frame->jsFrame->argValues<Value>(), argc, + frame->callerCanHandleTailCall()); + frame->setupJSFrame(frame->framePointer(), *jsfo, jsfo->scope(), thisObject, + Primitive::undefinedValue()); + engine->jsStackTop = frame->framePointer() + frame->requiredJSStackFrameSize(); + frame->setPendingTailCall(true); return Encode::undefined(); } -void Runtime::method_throwException(ExecutionEngine *engine, const Value &value) +void Runtime::ThrowException::call(ExecutionEngine *engine, const Value &value) { if (!value.isEmpty()) engine->throwError(value); } -ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value &value) +ReturnedValue Runtime::TypeofValue::call(ExecutionEngine *engine, const Value &value) { Scope scope(engine); ScopedString res(scope); @@ -1599,83 +1720,118 @@ ReturnedValue Runtime::method_typeofValue(ExecutionEngine *engine, const Value & return res.asReturnedValue(); } -QV4::ReturnedValue Runtime::method_typeofName(ExecutionEngine *engine, int nameIndex) +QV4::ReturnedValue Runtime::TypeofName::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - ScopedValue prop(scope, static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).getProperty(name)); + ScopedValue prop(scope, engine->currentContext()->getProperty(name)); // typeof doesn't throw. clear any possible exception scope.engine->hasException = false; - return method_typeofValue(engine, prop); + return TypeofValue::call(engine, prop); } -ReturnedValue Runtime::method_createWithContext(ExecutionEngine *engine, Value *jsStackFrame) +void Runtime::PushCallContext::call(JSTypesStackFrame *frame) { - QV4::Value &accumulator = jsStackFrame[CallData::Accumulator]; - accumulator = accumulator.toObject(engine); - if (engine->hasException) - return Encode::undefined(); - Q_ASSERT(accumulator.isObject()); - const Object &obj = static_cast<const Object &>(accumulator); - ExecutionContext *context = static_cast<ExecutionContext *>(jsStackFrame + CallData::Context); - return context->newWithContext(obj.d())->asReturnedValue(); + frame->jsFrame->context = ExecutionContext::newCallContext(frame)->asReturnedValue(); } -ReturnedValue Runtime::method_createCatchContext(ExecutionContext *parent, int blockIndex, int exceptionVarNameIndex) +ReturnedValue Runtime::PushWithContext::call(ExecutionEngine *engine, const Value &acc) { - ExecutionEngine *e = parent->engine(); - return parent->newCatchContext(e->currentStackFrame, blockIndex, - e->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex])->asReturnedValue(); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + CallData *jsFrame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame; + Value &newAcc = jsFrame->accumulator.asValue<Value>(); + newAcc = Value::fromHeapObject(acc.toObject(engine)); + if (!engine->hasException) { + Q_ASSERT(newAcc.isObject()); + const Object &obj = static_cast<const Object &>(newAcc); + Value &context = jsFrame->context.asValue<Value>(); + auto ec = static_cast<const ExecutionContext *>(&context); + context = ec->newWithContext(obj.d())->asReturnedValue(); + } + return newAcc.asReturnedValue(); } -ReturnedValue Runtime::method_createBlockContext(ExecutionContext *parent, int index) +void Runtime::PushCatchContext::call(ExecutionEngine *engine, int blockIndex, int exceptionVarNameIndex) { - ExecutionEngine *e = parent->engine(); - return parent->newBlockContext(e->currentStackFrame, index)->asReturnedValue(); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + auto name = engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[exceptionVarNameIndex]; + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context + = ExecutionContext::newCatchContext(engine->currentStackFrame, blockIndex, name)->asReturnedValue(); } -ReturnedValue Runtime::method_cloneBlockContext(ExecutionContext *previous) +void Runtime::PushBlockContext::call(ExecutionEngine *engine, int index) { - return ExecutionContext::cloneBlockContext(static_cast<Heap::CallContext *>(previous->d()))->asReturnedValue(); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context + = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); } +void Runtime::CloneBlockContext::call(ExecutionEngine *engine) +{ + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + auto frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + auto context = static_cast<Heap::CallContext *>( + Value::fromStaticValue(frame->jsFrame->context).m()); + frame->jsFrame->context = + ExecutionContext::cloneBlockContext(engine, context)->asReturnedValue(); +} -ReturnedValue Runtime::method_createScriptContext(ExecutionEngine *engine, int index) +void Runtime::PushScriptContext::call(ExecutionEngine *engine, int index) { - Q_ASSERT(engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_GlobalContext || - engine->currentStackFrame->context()->d()->type == Heap::ExecutionContext::Type_QmlContext); + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_GlobalContext || + engine->currentContext()->d()->type == Heap::ExecutionContext::Type_QmlContext); ReturnedValue c = ExecutionContext::newBlockContext(engine->currentStackFrame, index)->asReturnedValue(); engine->setScriptContext(c); - return c; + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = c; } -ReturnedValue Runtime::method_popScriptContext(ExecutionEngine *engine) +void Runtime::PopScriptContext::call(ExecutionEngine *engine) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); ReturnedValue root = engine->rootContext()->asReturnedValue(); engine->setScriptContext(root); - return root; + static_cast<JSTypesStackFrame *>(engine->currentStackFrame)->jsFrame->context = root; } -void Runtime::method_throwReferenceError(ExecutionEngine *engine, int nameIndex) +void Runtime::ThrowReferenceError::call(ExecutionEngine *engine, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); engine->throwReferenceError(name); } -void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) +void Runtime::ThrowOnNullOrUndefined::call(ExecutionEngine *engine, const Value &v) +{ + if (v.isNullOrUndefined()) + engine->throwTypeError(); +} + +ReturnedValue Runtime::ConvertThisToObject::call(ExecutionEngine *engine, const Value &t) +{ + if (!t.isObject()) { + if (t.isNullOrUndefined()) { + return engine->globalObject->asReturnedValue(); + } else { + return t.toObject(engine)->asReturnedValue(); + } + } + return t.asReturnedValue(); +} + +void Runtime::DeclareVar::call(ExecutionEngine *engine, Bool deletable, int nameIndex) { Scope scope(engine); ScopedString name(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]); - static_cast<ExecutionContext &>(engine->currentStackFrame->jsFrame->context).createMutableBinding(name, deletable); + engine->currentContext()->createMutableBinding(name, deletable); } -ReturnedValue Runtime::method_arrayLiteral(ExecutionEngine *engine, Value *values, uint length) +ReturnedValue Runtime::ArrayLiteral::call(ExecutionEngine *engine, Value *values, uint length) { return engine->newArrayObject(values, length)->asReturnedValue(); } -ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId, const QV4::Value *args, int argc) +ReturnedValue Runtime::ObjectLiteral::call(ExecutionEngine *engine, int classId, QV4::Value args[], int argc) { Scope scope(engine); Scoped<InternalClass> klass(scope, engine->currentStackFrame->v4Function->compilationUnit->runtimeClasses[classId]); @@ -1707,7 +1863,8 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId if (arg != ObjectLiteralArgument::Value) { Q_ASSERT(args[2].isInteger()); int functionId = args[2].integerValue(); - QV4::Function *clos = static_cast<CompiledData::CompilationUnit*>(engine->currentStackFrame->v4Function->compilationUnit)->runtimeFunctions[functionId]; + QV4::Function *clos = engine->currentStackFrame->v4Function->executableCompilationUnit() + ->runtimeFunctions[functionId]; Q_ASSERT(clos); PropertyKey::FunctionNamePrefix prefix = PropertyKey::None; @@ -1719,7 +1876,7 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId arg = ObjectLiteralArgument::Value; fnName = name->asFunctionName(engine, prefix); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); if (clos->isGenerator()) value = MemberGeneratorFunction::create(current, clos, o, fnName)->asReturnedValue(); else @@ -1748,9 +1905,11 @@ ReturnedValue Runtime::method_objectLiteral(ExecutionEngine *engine, int classId return o.asReturnedValue(); } -ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classIndex, const Value &superClass, const Value *computedNames) +ReturnedValue Runtime::CreateClass::call(ExecutionEngine *engine, int classIndex, + const Value &superClass, Value computedNames[]) { - const CompiledData::CompilationUnit *unit = engine->currentStackFrame->v4Function->compilationUnit; + const QV4::ExecutableCompilationUnit *unit + = engine->currentStackFrame->v4Function->executableCompilationUnit(); const QV4::CompiledData::Class *cls = unit->unitData()->classAt(classIndex); Scope scope(engine); @@ -1775,7 +1934,7 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde ScopedObject proto(scope, engine->newObject()); proto->setPrototypeUnchecked(protoParent); - ExecutionContext *current = static_cast<ExecutionContext *>(&engine->currentStackFrame->jsFrame->context); + ExecutionContext *current = engine->currentContext(); ScopedFunctionObject constructor(scope); QV4::Function *f = cls->constructorFunction != UINT_MAX ? unit->runtimeFunctions[cls->constructorFunction] : nullptr; @@ -1850,36 +2009,59 @@ ReturnedValue Runtime::method_createClass(ExecutionEngine *engine, int classInde return constructor->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createMappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateMappedArgumentsObject::call(ExecutionEngine *engine) { Q_ASSERT(engine->currentContext()->d()->type == Heap::ExecutionContext::Type_CallContext); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_ArgumentsObject); return engine->memoryManager->allocObject<ArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createUnmappedArgumentsObject(ExecutionEngine *engine) +QV4::ReturnedValue Runtime::CreateUnmappedArgumentsObject::call(ExecutionEngine *engine) { + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); Heap::InternalClass *ic = engine->internalClasses(EngineBase::Class_StrictArgumentsObject); - return engine->memoryManager->allocObject<StrictArgumentsObject>(ic, engine->currentStackFrame)->asReturnedValue(); + return engine->memoryManager->allocObject<StrictArgumentsObject>( + ic, static_cast<JSTypesStackFrame *>(engine->currentStackFrame))->asReturnedValue(); } -QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, int argIndex) +QV4::ReturnedValue Runtime::CreateRestParameter::call(ExecutionEngine *engine, int argIndex) { - const Value *values = engine->currentStackFrame->originalArguments + argIndex; - int nValues = engine->currentStackFrame->originalArgumentsCount - argIndex; + Q_ASSERT(engine->currentStackFrame->isJSTypesFrame()); + JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>(engine->currentStackFrame); + const Value *values = frame->argv() + argIndex; + int nValues = frame->argc() - argIndex; if (nValues <= 0) return engine->newArrayObject(0)->asReturnedValue(); return engine->newArrayObject(values, nValues)->asReturnedValue(); } -ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) +ReturnedValue Runtime::RegexpLiteral::call(ExecutionEngine *engine, int id) { - Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as<RegExp>()); + const auto val + = engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id]; + Heap::RegExpObject *ro = engine->newRegExpObject(Value::fromStaticValue(val).as<RegExp>()); return ro->asReturnedValue(); } -#endif // V4_BOOTSTRAP -ReturnedValue Runtime::method_uMinus(const Value &value) +ReturnedValue Runtime::ToObject::call(ExecutionEngine *engine, const Value &obj) +{ + if (obj.isObject()) + return obj.asReturnedValue(); + + return obj.toObject(engine)->asReturnedValue(); +} + +Bool Runtime::ToBoolean::call(const Value &obj) +{ + return obj.toBoolean(); +} + +ReturnedValue Runtime::ToNumber::call(ExecutionEngine *, const Value &v) +{ + return Encode(v.toNumber()); +} + +ReturnedValue Runtime::UMinus::call(const Value &value) { TRACE1(value); @@ -1895,8 +2077,7 @@ ReturnedValue Runtime::method_uMinus(const Value &value) // binary operators -#ifndef V4_BOOTSTRAP -ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, const Value &right) +ReturnedValue Runtime::Add::call(ExecutionEngine *engine, const Value &left, const Value &right) { TRACE2(left, right); @@ -1908,7 +2089,7 @@ ReturnedValue Runtime::method_add(ExecutionEngine *engine, const Value &left, co return RuntimeHelpers::addHelper(engine, left, right); } -ReturnedValue Runtime::method_sub(const Value &left, const Value &right) +ReturnedValue Runtime::Sub::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1921,7 +2102,7 @@ ReturnedValue Runtime::method_sub(const Value &left, const Value &right) return Value::fromDouble(lval - rval).asReturnedValue(); } -ReturnedValue Runtime::method_mul(const Value &left, const Value &right) +ReturnedValue Runtime::Mul::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1934,7 +2115,7 @@ ReturnedValue Runtime::method_mul(const Value &left, const Value &right) return Value::fromDouble(lval * rval).asReturnedValue(); } -ReturnedValue Runtime::method_div(const Value &left, const Value &right) +ReturnedValue Runtime::Div::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1955,7 +2136,7 @@ ReturnedValue Runtime::method_div(const Value &left, const Value &right) return Value::fromDouble(lval / rval).asReturnedValue(); } -ReturnedValue Runtime::method_mod(const Value &left, const Value &right) +ReturnedValue Runtime::Mod::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1976,7 +2157,41 @@ ReturnedValue Runtime::method_mod(const Value &left, const Value &right) return Value::fromDouble(std::fmod(lval, rval)).asReturnedValue(); } -ReturnedValue Runtime::method_shl(const Value &left, const Value &right) +ReturnedValue Runtime::Exp::call(const Value &base, const Value &exp) +{ + double b = base.toNumber(); + double e = exp.toNumber(); + return Encode(QQmlPrivate::jsExponentiate(b, e)); +} + +ReturnedValue Runtime::BitAnd::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval & rval)); +} + +ReturnedValue Runtime::BitOr::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval | rval)); +} + +ReturnedValue Runtime::BitXor::call(const Value &left, const Value &right) +{ + TRACE2(left, right); + + int lval = left.toInt32(); + int rval = right.toInt32(); + return Encode((int)(lval ^ rval)); +} + +ReturnedValue Runtime::Shl::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1985,7 +2200,7 @@ ReturnedValue Runtime::method_shl(const Value &left, const Value &right) return Encode((int)(lval << rval)); } -ReturnedValue Runtime::method_shr(const Value &left, const Value &right) +ReturnedValue Runtime::Shr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -1994,7 +2209,7 @@ ReturnedValue Runtime::method_shr(const Value &left, const Value &right) return Encode((int)(lval >> rval)); } -ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) +ReturnedValue Runtime::UShr::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2005,37 +2220,35 @@ ReturnedValue Runtime::method_ushr(const Value &left, const Value &right) return Encode(res); } -#endif // V4_BOOTSTRAP - -ReturnedValue Runtime::method_greaterThan(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterThan(left, right); + bool r = CompareGreaterThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessThan(const Value &left, const Value &right) +ReturnedValue Runtime::LessThan::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessThan(left, right); + bool r = CompareLessThan::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_greaterEqual(const Value &left, const Value &right) +ReturnedValue Runtime::GreaterEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareGreaterEqual(left, right); + bool r = CompareGreaterEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_lessEqual(const Value &left, const Value &right) +ReturnedValue Runtime::LessEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareLessEqual(left, right); + bool r = CompareLessEqual::call(left, right); return Encode(r); } @@ -2059,49 +2272,34 @@ struct LazyScope } }; -Bool Runtime::method_compareEqual(const Value &left, const Value &right) +Bool Runtime::CompareEqual::call(const Value &left, const Value &right) { TRACE2(left, right); Value lhs = left; Value rhs = right; -#ifndef V4_BOOTSTRAP LazyScope scope; Value *lhsGuard = nullptr; Value *rhsGuard = nullptr; -#endif - redo: + 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); - } + quint32 lt = lhs.quickType(); + quint32 rt = rhs.quickType(); - switch (lt) { - case QV4::Value::QT_ManagedOrUndefined: + // LHS: Check if managed + if ((lt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { if (lhs.isUndefined()) return rhs.isNullOrUndefined(); - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: - // LHS: Managed - switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: + + // RHS: Check if managed + if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { if (rhs.isUndefined()) return false; - Q_FALLTHROUGH(); - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: { -#ifndef V4_BOOTSTRAP - // RHS: Managed + Heap::Base *l = lhs.m(); Heap::Base *r = rhs.m(); Q_ASSERT(l); @@ -2111,16 +2309,18 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) if (l->internalClass->vtable->isStringOrSymbol) { scope.set(&rhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(rhs), PREFERREDTYPE_HINT), r->internalClass->engine); rhs = rhsGuard->asReturnedValue(); - break; + goto redo; } else { Q_ASSERT(r->internalClass->vtable->isStringOrSymbol); scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), l->internalClass->engine); lhs = lhsGuard->asReturnedValue(); - break; + goto redo; } -#endif return false; } + +lhs_managed_and_rhs_not: + switch (rt) { case QV4::Value::QT_Empty: Q_UNREACHABLE(); case QV4::Value::QT_Null: @@ -2128,20 +2328,25 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) case QV4::Value::QT_Bool: case QV4::Value::QT_Int: rhs = Value::fromDouble(rhs.int_32()); - // fall through + Q_FALLTHROUGH(); default: // double -#ifndef V4_BOOTSTRAP if (lhs.m()->internalClass->vtable->isStringOrSymbol) { return lhs.m()->internalClass->vtable->isString ? (RuntimeHelpers::toNumber(lhs) == rhs.doubleValue()) : false; } else { scope.set(&lhsGuard, RuntimeHelpers::objectDefaultValue(&static_cast<QV4::Object &>(lhs), PREFERREDTYPE_HINT), lhs.m()->internalClass->engine); lhs = lhsGuard->asReturnedValue(); } -#else - Q_UNIMPLEMENTED(); -#endif } goto redo; + } else if ((rt & (Value::ManagedMask >> Value::Tag_Shift)) == 0) { + if (rhs.isUndefined()) + return lhs.isNull(); // Can't be undefined + qSwap(lhs, rhs); + qSwap(lt, rt); + goto lhs_managed_and_rhs_not; + } + + switch (lt) { case QV4::Value::QT_Empty: Q_UNREACHABLE(); case QV4::Value::QT_Null: @@ -2149,13 +2354,10 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) case QV4::Value::QT_Bool: case QV4::Value::QT_Int: switch (rt) { - case QV4::Value::QT_ManagedOrUndefined: - case QV4::Value::QT_ManagedOrUndefined1: - case QV4::Value::QT_ManagedOrUndefined2: - case QV4::Value::QT_ManagedOrUndefined3: case QV4::Value::QT_Empty: - case QV4::Value::QT_Null: Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; case QV4::Value::QT_Bool: case QV4::Value::QT_Int: return lhs.int_32() == rhs.int_32(); @@ -2163,28 +2365,37 @@ Bool Runtime::method_compareEqual(const Value &left, const Value &right) return lhs.int_32() == rhs.doubleValue(); } default: // double - Q_ASSERT(rhs.isDouble()); - return lhs.doubleValue() == rhs.doubleValue(); + switch (rt) { + case QV4::Value::QT_Empty: + Q_UNREACHABLE(); + case QV4::Value::QT_Null: + return false; + case QV4::Value::QT_Bool: + case QV4::Value::QT_Int: + return lhs.doubleValue() == rhs.int_32(); + default: // double + return lhs.doubleValue() == rhs.doubleValue(); + } } } -ReturnedValue Runtime::method_equal(const Value &left, const Value &right) +ReturnedValue Runtime::Equal::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = method_compareEqual(left, right); + bool r = CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_notEqual(const Value &left, const Value &right) +ReturnedValue Runtime::NotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - bool r = !method_compareEqual(left, right); + bool r = !CompareEqual::call(left, right); return Encode(r); } -ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2192,7 +2403,7 @@ ReturnedValue Runtime::method_strictEqual(const Value &left, const Value &right) return Encode(r); } -ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &right) +ReturnedValue Runtime::StrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); @@ -2200,27 +2411,154 @@ ReturnedValue Runtime::method_strictNotEqual(const Value &left, const Value &rig return Encode(r); } -Bool Runtime::method_compareNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); - return !Runtime::method_compareEqual(left, right); + return !Runtime::CompareEqual::call(left, right); } -Bool Runtime::method_compareStrictEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictEqual::call(const Value &left, const Value &right) { TRACE2(left, right); return RuntimeHelpers::strictEqual(left, right); } -Bool Runtime::method_compareStrictNotEqual(const Value &left, const Value &right) +Bool Runtime::CompareStrictNotEqual::call(const Value &left, const Value &right) { TRACE2(left, right); return ! RuntimeHelpers::strictEqual(left, right); } +template<typename Operation> +static inline const void *symbol() +{ + return reinterpret_cast<void *>(&Operation::call); +} + +QHash<const void *, const char *> Runtime::symbolTable() +{ + static const QHash<const void *, const char *> symbols({ +#ifndef V4_BOOTSTRAP + {symbol<CallGlobalLookup>(), "CallGlobalLookup" }, + {symbol<CallQmlContextPropertyLookup>(), "CallQmlContextPropertyLookup" }, + {symbol<CallName>(), "CallName" }, + {symbol<CallProperty>(), "CallProperty" }, + {symbol<CallPropertyLookup>(), "CallPropertyLookup" }, + {symbol<CallValue>(), "CallValue" }, + {symbol<CallWithReceiver>(), "CallWithReceiver" }, + {symbol<CallPossiblyDirectEval>(), "CallPossiblyDirectEval" }, + {symbol<CallWithSpread>(), "CallWithSpread" }, + {symbol<TailCall>(), "TailCall" }, + + {symbol<Construct>(), "Construct" }, + {symbol<ConstructWithSpread>(), "ConstructWithSpread" }, + + {symbol<StoreNameStrict>(), "StoreNameStrict" }, + {symbol<StoreNameSloppy>(), "StoreNameSloppy" }, + {symbol<StoreProperty>(), "StoreProperty" }, + {symbol<StoreElement>(), "StoreElement" }, + {symbol<LoadProperty>(), "LoadProperty" }, + {symbol<LoadName>(), "LoadName" }, + {symbol<LoadElement>(), "LoadElement" }, + {symbol<LoadSuperProperty>(), "LoadSuperProperty" }, + {symbol<StoreSuperProperty>(), "StoreSuperProperty" }, + {symbol<LoadSuperConstructor>(), "LoadSuperConstructor" }, + {symbol<LoadGlobalLookup>(), "LoadGlobalLookup" }, + {symbol<LoadQmlContextPropertyLookup>(), "LoadQmlContextPropertyLookup" }, + {symbol<GetLookup>(), "GetLookup" }, + {symbol<SetLookupStrict>(), "SetLookupStrict" }, + {symbol<SetLookupSloppy>(), "SetLookupSloppy" }, + + {symbol<TypeofValue>(), "TypeofValue" }, + {symbol<TypeofName>(), "TypeofName" }, + + {symbol<DeleteProperty_NoThrow>(), "DeleteProperty_NoThrow" }, + {symbol<DeleteProperty>(), "DeleteProperty" }, + {symbol<DeleteName_NoThrow>(), "DeleteName_NoThrow" }, + {symbol<DeleteName>(), "DeleteName" }, + + {symbol<ThrowException>(), "ThrowException" }, + {symbol<PushCallContext>(), "PushCallContext" }, + {symbol<PushWithContext>(), "PushWithContext" }, + {symbol<PushCatchContext>(), "PushCatchContext" }, + {symbol<PushBlockContext>(), "PushBlockContext" }, + {symbol<CloneBlockContext>(), "CloneBlockContext" }, + {symbol<PushScriptContext>(), "PushScriptContext" }, + {symbol<PopScriptContext>(), "PopScriptContext" }, + {symbol<ThrowReferenceError>(), "ThrowReferenceError" }, + {symbol<ThrowOnNullOrUndefined>(), "ThrowOnNullOrUndefined" }, + + {symbol<Closure>(), "Closure" }, + + {symbol<ConvertThisToObject>(), "ConvertThisToObject" }, + {symbol<DeclareVar>(), "DeclareVar" }, + {symbol<CreateMappedArgumentsObject>(), "CreateMappedArgumentsObject" }, + {symbol<CreateUnmappedArgumentsObject>(), "CreateUnmappedArgumentsObject" }, + {symbol<CreateRestParameter>(), "CreateRestParameter" }, + + {symbol<ArrayLiteral>(), "ArrayLiteral" }, + {symbol<ObjectLiteral>(), "ObjectLiteral" }, + {symbol<CreateClass>(), "CreateClass" }, + + {symbol<GetIterator>(), "GetIterator" }, + {symbol<IteratorNext>(), "IteratorNext" }, + {symbol<IteratorNextForYieldStar>(), "IteratorNextForYieldStar" }, + {symbol<IteratorClose>(), "IteratorClose" }, + {symbol<DestructureRestElement>(), "DestructureRestElement" }, + + {symbol<ToObject>(), "ToObject" }, + {symbol<ToBoolean>(), "ToBoolean" }, + {symbol<ToNumber>(), "ToNumber" }, + + {symbol<UMinus>(), "UMinus" }, + + {symbol<Instanceof>(), "Instanceof" }, + {symbol<As>(), "As" }, + {symbol<In>(), "In" }, + {symbol<Add>(), "Add" }, + {symbol<Sub>(), "Sub" }, + {symbol<Mul>(), "Mul" }, + {symbol<Div>(), "Div" }, + {symbol<Mod>(), "Mod" }, + {symbol<Exp>(), "Exp" }, + {symbol<BitAnd>(), "BitAnd" }, + {symbol<BitOr>(), "BitOr" }, + {symbol<BitXor>(), "BitXor" }, + {symbol<Shl>(), "Shl" }, + {symbol<Shr>(), "Shr" }, + {symbol<UShr>(), "UShr" }, + {symbol<GreaterThan>(), "GreaterThan" }, + {symbol<LessThan>(), "LessThan" }, + {symbol<GreaterEqual>(), "GreaterEqual" }, + {symbol<LessEqual>(), "LessEqual" }, + {symbol<Equal>(), "Equal" }, + {symbol<NotEqual>(), "NotEqual" }, + {symbol<StrictEqual>(), "StrictEqual" }, + {symbol<StrictNotEqual>(), "StrictNotEqual" }, + + {symbol<CompareGreaterThan>(), "CompareGreaterThan" }, + {symbol<CompareLessThan>(), "CompareLessThan" }, + {symbol<CompareGreaterEqual>(), "CompareGreaterEqual" }, + {symbol<CompareLessEqual>(), "CompareLessEqual" }, + {symbol<CompareEqual>(), "CompareEqual" }, + {symbol<CompareNotEqual>(), "CompareNotEqual" }, + {symbol<CompareStrictEqual>(), "CompareStrictEqual" }, + {symbol<CompareStrictNotEqual>(), "CompareStrictNotEqual" }, + + {symbol<CompareInstanceof>(), "CompareInstanceOf" }, + {symbol<CompareIn>(), "CompareIn" }, + + {symbol<RegexpLiteral>(), "RegexpLiteral" }, + {symbol<GetTemplateObject>(), "GetTemplateObject" } +#endif + }); + + return symbols; +} + } // namespace QV4 QT_END_NAMESPACE |