aboutsummaryrefslogtreecommitdiffstats
path: root/src/qml/jsruntime/qv4runtime.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qml/jsruntime/qv4runtime.cpp')
-rw-r--r--src/qml/jsruntime/qv4runtime.cpp170
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);