QJSManagedValue: Throw an exception when trying to call a non-callable

The JavaScript engine does this, so we should do the same here.

Change-Id: I011a60bc2c013f19306c843da7a9c22dc80a39cb
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
Ulf Hermann 2020-12-18 15:47:46 +01:00
parent 0d835e42c1
commit bf89ec1734
2 changed files with 47 additions and 15 deletions

View File

@ -916,6 +916,21 @@ bool QJSManagedValue::isCallable() const
return d && d->isFunctionObject();
}
static const QV4::FunctionObject *functionObjectForCall(QV4::Value *d)
{
if (Q_UNLIKELY(!d)) {
qWarning("QJSManagedValue: Calling a default-constructed or moved-from managed value"
"should throw an exception, but there is no engine to receive it.");
return nullptr;
}
if (const QV4::FunctionObject *f = d->as<QV4::FunctionObject>())
return f;
v4Engine(d)->throwTypeError(QStringLiteral("Value is not a function"));
return nullptr;
}
/*!
* If this QJSManagedValue represents a JavaScript FunctionObject, calls it with
* the given \a arguments, and returns the result. Otherwise returns a
@ -927,7 +942,7 @@ bool QJSManagedValue::isCallable() const
*/
QJSValue QJSManagedValue::call(const QJSValueList &arguments) const
{
const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
const QV4::FunctionObject *f = functionObjectForCall(d);
if (!f)
return QJSValue();
@ -960,7 +975,7 @@ QJSValue QJSManagedValue::call(const QJSValueList &arguments) const
QJSValue QJSManagedValue::callWithInstance(const QJSValue &instance,
const QJSValueList &arguments) const
{
const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
const QV4::FunctionObject *f = functionObjectForCall(d);
if (!f)
return QJSValue();
@ -999,7 +1014,7 @@ QJSValue QJSManagedValue::callWithInstance(const QJSValue &instance,
*/
QJSValue QJSManagedValue::callAsConstructor(const QJSValueList &arguments) const
{
const QV4::FunctionObject *f = d ? d->as<QV4::FunctionObject>() : nullptr;
const QV4::FunctionObject *f = functionObjectForCall(d);
if (!f)
return QJSValue();

View File

@ -1018,6 +1018,7 @@ void tst_QJSManagedValue::call_function()
QJSManagedValue result(fun.call(), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toInteger(), 1);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_object()
@ -1028,6 +1029,7 @@ void tst_QJSManagedValue::call_object()
QJSManagedValue result(object.callWithInstance(object.toJSValue()), &eng);
QCOMPARE(result.type(), QJSManagedValue::Object);
QVERIFY(!result.isNull());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_newObjects()
@ -1041,6 +1043,7 @@ void tst_QJSManagedValue::call_newObjects()
args << eng.toScriptValue(123);
QJSManagedValue result(number.callWithInstance(object.toJSValue(), args), &eng);
QVERIFY(result.strictlyEquals(QJSManagedValue(args.at(0), &eng)));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_this()
@ -1054,6 +1057,7 @@ void tst_QJSManagedValue::call_this()
QJSManagedValue result(fun.callWithInstance(QJSValue(std::move(numberObject))), &eng);
QCOMPARE(result.type(), QJSManagedValue::Object);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::call_arguments()
@ -1066,6 +1070,7 @@ void tst_QJSManagedValue::call_arguments()
{
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant())), &eng);
QCOMPARE(result.type(), QJSManagedValue::Undefined);
QVERIFY(!eng.hasError());
}
{
QJSValueList args;
@ -1073,6 +1078,7 @@ void tst_QJSManagedValue::call_arguments()
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
// V2 constructors
{
@ -1081,6 +1087,7 @@ void tst_QJSManagedValue::call_arguments()
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 123.0);
QVERIFY(!eng.hasError());
}
}
@ -1091,24 +1098,23 @@ void tst_QJSManagedValue::call()
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { return arguments[1]; })")), &eng);
QCOMPARE(fun.isCallable(), true);
{
QJSValueList args;
args << eng.toScriptValue(123.0) << eng.toScriptValue(456.0);
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 456.0);
}
QJSValueList args;
args << eng.toScriptValue(123.0) << eng.toScriptValue(456.0);
QJSManagedValue result(fun.callWithInstance(eng.toScriptValue(QVariant()), args), &eng);
QCOMPARE(result.type(), QJSManagedValue::Number);
QCOMPARE(result.toNumber(), 456.0);
QVERIFY(!eng.hasError());
}
{
QJSManagedValue fun(eng.evaluate(QStringLiteral("(function() { throw new Error('foo'); })")), &eng);
QCOMPARE(fun.isCallable(), true);
QVERIFY(!eng.hasError());
{
QJSManagedValue result(fun.call(), &eng);
QCOMPARE(result.type(), QJSManagedValue::Undefined);
QJSManagedValue error(eng.catchError(), &eng);
}
QJSManagedValue result(fun.call(), &eng);
QCOMPARE(result.type(), QJSManagedValue::Undefined);
QVERIFY(eng.hasError());
QJSManagedValue error(eng.catchError(), &eng);
QVERIFY(error.toString().contains("foo"));
}
}
@ -1164,6 +1170,9 @@ void tst_QJSManagedValue::call_nonFunction()
// calling things that are not functions
QFETCH(QJSValue, value);
QVERIFY(QJSManagedValue(std::move(value), engine.data()).call().isUndefined());
QVERIFY(engine->hasError());
QJSManagedValue error(engine->catchError(), engine.data());
QVERIFY(error.toString().contains("TypeError"));
}
void tst_QJSManagedValue::construct_nonFunction_data()
@ -1175,6 +1184,9 @@ void tst_QJSManagedValue::construct_nonFunction()
{
QFETCH(QJSValue, value);
QVERIFY(QJSManagedValue(std::move(value), engine.data()).callAsConstructor().isUndefined());
QVERIFY(engine->hasError());
QJSManagedValue error(engine->catchError(), engine.data());
QVERIFY(error.toString().contains("TypeError"));
}
void tst_QJSManagedValue::construct_simple()
@ -1188,6 +1200,7 @@ void tst_QJSManagedValue::construct_simple()
QVERIFY(ret.prototype().strictlyEquals(
QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng)));
QCOMPARE(ret.property(QStringLiteral("foo")).toInt(), 123);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_newObjectJS()
@ -1202,6 +1215,7 @@ void tst_QJSManagedValue::construct_newObjectJS()
QVERIFY(!ret.prototype().strictlyEquals(
QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng)));
QCOMPARE(ret.property(QStringLiteral("bar")).toInt(), 456);
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_arg()
@ -1215,6 +1229,7 @@ void tst_QJSManagedValue::construct_arg()
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QCOMPARE(ret.toNumber(), args.at(0).toNumber());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_proto()
@ -1226,6 +1241,7 @@ void tst_QJSManagedValue::construct_proto()
QCOMPARE(fun.property(QStringLiteral("prototype")).isObject(), true);
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QVERIFY(QJSManagedValue(fun.property(QStringLiteral("prototype")), &eng).strictlyEquals(ret));
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_returnInt()
@ -1237,6 +1253,7 @@ void tst_QJSManagedValue::construct_returnInt()
QJSManagedValue ret(fun.callAsConstructor(), &eng);
QCOMPARE(ret.type(), QJSManagedValue::Object);
QVERIFY(!ret.isNull());
QVERIFY(!eng.hasError());
}
void tst_QJSManagedValue::construct_throw()