diff options
Diffstat (limited to 'src/qml/jsruntime/qv4runtime.cpp')
-rw-r--r-- | src/qml/jsruntime/qv4runtime.cpp | 170 |
1 files changed, 109 insertions, 61 deletions
diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index aa4d5c875a..184e7c7819 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1,44 +1,48 @@ // 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 "qv4global_p.h" #include "qv4runtime_p.h" -#include "qv4engine_p.h" -#include "qv4object_p.h" -#include "qv4globalobject_p.h" -#include "qv4argumentsobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_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 <private/qqmljsast_p.h> +#include <private/qqmltypewrapper_p.h> #include <private/qqmlvaluetypewrapper_p.h> -#include "qv4qobjectwrapper_p.h" -#include "qv4symbol_p.h" -#include "qv4generatorobject_p.h" -#include <QtQml/private/qv4math_p.h> - -#include <QtCore/QDebug> -#include <cassert> -#include <cstdio> -#include <stdlib.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 @@ -329,7 +333,7 @@ ReturnedValue Runtime::DeleteName::call(ExecutionEngine *engine, Function *funct } } -QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Value &lval, const Value &rval) +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>(); @@ -345,26 +349,58 @@ QV4::ReturnedValue Runtime::Instanceof::call(ExecutionEngine *engine, const Valu 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(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::As::call(ExecutionEngine *engine, const Value &lval, const Value &rval) { Scope scope(engine); - ScopedValue result(scope, Runtime::Instanceof::call(engine, lval, rval)); + ScopedValue result(scope, doInstanceof(engine, lval, rval)); - if (scope.hasException()) + if (scope.hasException()) { + // "foo instanceof valueType" must not throw an exception. + // So this can only be an object type. engine->catchException(); - else if (result->toBoolean()) + return Encode::null(); + } + + if (result->toBoolean()) return lval.asReturnedValue(); + else if (result->isBoolean()) + return Encode::null(); - 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(); + + result = coerce(engine, lval, typeWrapper->d()->type(), false); + if (result->isUndefined()) + return Encode::undefined(); + + const auto *stackFrame = engine->currentStackFrame; + 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) @@ -564,7 +600,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 @@ -842,6 +878,8 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c 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(); @@ -856,14 +894,13 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c 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)); - IteratorClose::call(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; @@ -874,14 +911,18 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c 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()) @@ -889,18 +930,15 @@ ReturnedValue Runtime::IteratorNextForYieldStar::call(ExecutionEngine *engine, c 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::IteratorClose::call(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); @@ -1007,7 +1045,7 @@ ReturnedValue Runtime::LoadName::call(ExecutionEngine *engine, int nameIndex) static Object *getSuperBase(Scope &scope) { - ScopedFunctionObject f(scope); + Scoped<JavaScriptFunctionObject> f(scope); ScopedObject homeObject(scope); if (scope.engine->currentStackFrame->isJSTypesFrame()) { JSTypesStackFrame *frame = static_cast<JSTypesStackFrame *>( @@ -1166,7 +1204,7 @@ ReturnedValue Runtime::LoadSuperConstructor::call(ExecutionEngine *engine, const 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(); } @@ -1481,10 +1519,17 @@ ReturnedValue Runtime::CallPropertyLookup::call(ExecutionEngine *engine, const V // ok to have the value on the stack here Value f = Value::fromReturnedValue(l->getter(l, engine, base)); - if (!f.isFunctionObject()) - return engine->throwTypeError(); + if (Q_LIKELY(f.isFunctionObject())) + return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc)); - return checkedResult(engine, static_cast<FunctionObject &>(f).call(&base, argv, argc)); + if (QmlSignalHandler *handler = f.as<QmlSignalHandler>()) + return checkedResult(engine, handler->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::CallValue::call(ExecutionEngine *engine, const Value &func, Value *argv, int argc) @@ -1598,20 +1643,23 @@ ReturnedValue Runtime::TailCall::call(JSTypesStackFrame *frame, ExecutionEngine 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 checkedResult(engine, fo.call(&thisObject, argv, argc)); + return checkedResult(engine, jsfo->call(&thisObject, argv, argc)); } - memcpy(frame->jsFrame->args, argv, argc * sizeof(Value)); - frame->init(fo.function(), frame->jsFrame->argValues<Value>(), argc, + memmove(frame->jsFrame->args, argv, argc * sizeof(Value)); + frame->init(jsfo->function(), frame->jsFrame->argValues<Value>(), argc, frame->callerCanHandleTailCall()); - frame->setupJSFrame(frame->framePointer(), fo, fo.scope(), thisObject, + frame->setupJSFrame(frame->framePointer(), *jsfo, jsfo->scope(), thisObject, Primitive::undefinedValue()); engine->jsStackTop = frame->framePointer() + frame->requiredJSStackFrameSize(); frame->setPendingTailCall(true); |