Skip to content

Commit

Permalink
Add QJSValue::callAsConstructor() function
Browse files Browse the repository at this point in the history
The old name, construct(), was bad. This name is more descriptive
and consistent with the other callXXX() functions.

Task-number: QTBUG-23604
Change-Id: Ie205b0c52721782101e665f7dfedcac9051a00d0
Reviewed-by: Simon Hausmann <simon.hausmann@nokia.com>
Reviewed-by: Jędrzej Nowacki <jedrzej.nowacki@nokia.com>
  • Loading branch information
Kent Hansen authored and Qt by Nokia committed Jan 20, 2012
1 parent 174ee89 commit 1d577f6
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 70 deletions.
50 changes: 30 additions & 20 deletions src/declarative/qml/v8/qjsvalue.cpp
Expand Up @@ -73,7 +73,7 @@
Function objects (objects for which isCallable()) returns true) can
be invoked by calling call(). Constructor functions can be used to
construct new objects by calling construct().
construct new objects by calling callAsConstructor().
Use equals() or strictlyEquals() to compare a QJSValue to another.
Expand Down Expand Up @@ -710,7 +710,7 @@ QVariant QJSValue::toVariant() const
QJSEngine::hasUncaughtException() to determine if an exception
occurred.
\sa isCallable(), callWithInstance()
\sa isCallable(), callWithInstance(), callAsConstructor()
*/
QJSValue QJSValue::call(const QJSValueList &args)
{
Expand Down Expand Up @@ -738,8 +738,6 @@ QJSValue QJSValue::call(const QJSValueList &args)
QJSEngine::hasUncaughtException() to determine if an exception
occurred.
\snippet doc/src/snippets/code/src_script_qjsvalue.cpp 1
\sa call()
*/
QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList &args)
Expand All @@ -749,6 +747,31 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList
return d->call(QJSValuePrivate::get(instance), args);
}

/*!
Creates a new \c{Object} and calls this QJSValue as a
constructor, using the created object as the `this' object and
passing \a args as arguments. If the return value from the
constructor call is an object, then that object is returned;
otherwise the default constructed object is returned.
If this QJSValue is not a function, callAsConstructor() does
nothing and returns an undefined QJSValue.
Calling this function can cause an exception to occur in the
script engine; in that case, the value that was thrown
(typically an \c{Error} object) is returned. You can call
QJSEngine::hasUncaughtException() to determine if an exception
occurred.
\sa call(), QJSEngine::newObject()
*/
QJSValue QJSValue::callAsConstructor(const QJSValueList &args)
{
Q_D(QJSValue);
QScriptIsolate api(d->engine());
return QJSValuePrivate::get(d->callAsConstructor(args));
}

/*!
\obsolete
Expand All @@ -762,28 +785,15 @@ QJSValue QJSValue::call(const QJSValue& thisObject, const QJSValueList& args)
}

/*!
Creates a new \c{Object} and calls this QJSValue as a
constructor, using the created object as the `this' object and
passing \a args as arguments. If the return value from the
constructor call is an object, then that object is returned;
otherwise the default constructed object is returned.
If this QJSValue is not a function, construct() does nothing
and returns an invalid QJSValue.
Calling construct() can cause an exception to occur in the script
engine; in that case, construct() returns the value that was thrown
(typically an \c{Error} object). You can call
QJSEngine::hasUncaughtException() to determine if an exception
occurred.
\obsolete
\sa call(), QJSEngine::newObject()
Use callAsConstructor() instead.
*/
QJSValue QJSValue::construct(const QJSValueList &args)
{
Q_D(QJSValue);
QScriptIsolate api(d->engine());
return QJSValuePrivate::get(d->construct(args));
return QJSValuePrivate::get(d->callAsConstructor(args));
}

/*!
Expand Down
1 change: 1 addition & 0 deletions src/declarative/qml/v8/qjsvalue.h
Expand Up @@ -143,6 +143,7 @@ class Q_DECLARATIVE_EXPORT QJSValue
bool isCallable() const;
QJSValue call(const QJSValueList &args);
QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList());
QJSValue callAsConstructor(const QJSValueList &args = QJSValueList());
QJSValue call(const QJSValue &thisObject = QJSValue(),
const QJSValueList &args = QJSValueList());
QJSValue construct(const QJSValueList &args = QJSValueList());
Expand Down
8 changes: 4 additions & 4 deletions src/declarative/qml/v8/qjsvalue_impl_p.h
Expand Up @@ -974,7 +974,7 @@ QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::call(QJSValuePrivate* thisO
return new QJSValuePrivate(e, result);
}

inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(int argc, v8::Handle<v8::Value> *argv)
inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(int argc, v8::Handle<v8::Value> *argv)
{
QV8Engine *e = engine();

Expand All @@ -995,7 +995,7 @@ inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(int argc,
return new QJSValuePrivate(e, result);
}

inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(const QJSValueList& args)
inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::callAsConstructor(const QJSValueList& args)
{
if (!isCallable())
return InvalidValue();
Expand All @@ -1006,11 +1006,11 @@ inline QScriptPassPointer<QJSValuePrivate> QJSValuePrivate::construct(const QJSV
int argc = args.size();
QVarLengthArray<v8::Handle<v8::Value>, 8> argv(argc);
if (!prepareArgumentsForCall(argv.data(), args)) {
qWarning("QJSValue::construct() failed: cannot construct function with argument created in a different engine");
qWarning("QJSValue::callAsConstructor() failed: cannot construct function with argument created in a different engine");
return InvalidValue();
}

return construct(argc, argv.data());
return callAsConstructor(argc, argv.data());
}

/*! \internal
Expand Down
6 changes: 3 additions & 3 deletions src/declarative/qml/v8/qjsvalue_p.h
Expand Up @@ -138,9 +138,9 @@ class QJSValuePrivate
inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValueList& args);
inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, const QJSValue& arguments);
inline QScriptPassPointer<QJSValuePrivate> call(QJSValuePrivate* thisObject, int argc, v8::Handle< v8::Value >* argv);
inline QScriptPassPointer<QJSValuePrivate> construct(int argc, v8::Handle<v8::Value> *argv);
inline QScriptPassPointer<QJSValuePrivate> construct(const QJSValueList& args);
inline QScriptPassPointer<QJSValuePrivate> construct(const QJSValue& arguments);
inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(int argc, v8::Handle<v8::Value> *argv);
inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValueList& args);
inline QScriptPassPointer<QJSValuePrivate> callAsConstructor(const QJSValue& arguments);

inline bool assignEngine(QV8Engine *engine);
inline QV8Engine *engine() const;
Expand Down
44 changes: 22 additions & 22 deletions tests/auto/declarative/qjsengine/tst_qjsengine.cpp
Expand Up @@ -453,7 +453,7 @@ void tst_QJSEngine::newFunction()
QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);

QCOMPARE(fun.call().isNull(), true);
QCOMPARE(fun.construct().isObject(), true);
QCOMPARE(fun.callAsConstructor().isObject(), true);
}
}

Expand All @@ -478,7 +478,7 @@ void tst_QJSEngine::newFunctionWithArg()
QCOMPARE(fun.prototype().strictlyEquals(eng.evaluate("Function.prototype")), true);

QCOMPARE(fun.call().isNull(), true);
QCOMPARE(fun.construct().isObject(), true);
QCOMPARE(fun.callAsConstructor().isObject(), true);
}
}

Expand All @@ -502,7 +502,7 @@ void tst_QJSEngine::newFunctionWithProto()
QCOMPARE(proto.propertyFlags("constructor"), QScriptValue::SkipInEnumeration);

QCOMPARE(fun.call().isNull(), true);
QCOMPARE(fun.construct().isObject(), true);
QCOMPARE(fun.callAsConstructor().isObject(), true);
}
// whether the return value is correct
{
Expand Down Expand Up @@ -841,15 +841,15 @@ void tst_QJSEngine::jsRegExp()
QJSValue r4 = rxCtor.call(QJSValueList() << "foo" << "gim");
QVERIFY(r4.isRegExp());

QJSValue r5 = rxCtor.construct(QJSValueList() << r);
QJSValue r5 = rxCtor.callAsConstructor(QJSValueList() << r);
QVERIFY(r5.isRegExp());
QCOMPARE(r5.toString(), QString::fromLatin1("/foo/gim"));
// In JSC, constructing a RegExp from another produces the same identical object.
// This is different from SpiderMonkey and old back-end.
QVERIFY(!r5.strictlyEquals(r));

QEXPECT_FAIL("", "V8 and jsc ignores invalid flags", Continue); //https://bugs.webkit.org/show_bug.cgi?id=41614
QJSValue r6 = rxCtor.construct(QJSValueList() << "foo" << "bar");
QJSValue r6 = rxCtor.callAsConstructor(QJSValueList() << "foo" << "bar");
QVERIFY(r6.isError());
// QVERIFY(r6.toString().contains(QString::fromLatin1("SyntaxError"))); // Invalid regular expression flag

Expand All @@ -865,15 +865,15 @@ void tst_QJSEngine::jsRegExp()
QVERIFY(r8.isRegExp());
QCOMPARE(r8.toString(), QString::fromLatin1("/foo/gim"));

QJSValue r9 = rxCtor.construct();
QJSValue r9 = rxCtor.callAsConstructor();
QVERIFY(r9.isRegExp());
QCOMPARE(r9.toString(), QString::fromLatin1("/(?:)/"));

QJSValue r10 = rxCtor.construct(QJSValueList() << "" << "gim");
QJSValue r10 = rxCtor.callAsConstructor(QJSValueList() << "" << "gim");
QVERIFY(r10.isRegExp());
QCOMPARE(r10.toString(), QString::fromLatin1("/(?:)/gim"));

QJSValue r11 = rxCtor.construct(QJSValueList() << "{1.*}" << "g");
QJSValue r11 = rxCtor.callAsConstructor(QJSValueList() << "{1.*}" << "g");
QVERIFY(r11.isRegExp());
QCOMPARE(r11.toString(), QString::fromLatin1("/{1.*}/g"));
}
Expand Down Expand Up @@ -1208,14 +1208,14 @@ void tst_QJSEngine::newQMetaObject()
QCOMPARE(qclass.prototype().isObject(), true);
QCOMPARE(qclass2.prototype().isObject(), true);

QScriptValue instance = qclass.construct();
QScriptValue instance = qclass.callAsConstructor();
QCOMPARE(instance.isQObject(), true);
QCOMPARE(instance.toQObject()->metaObject(), qclass.toQMetaObject());
QEXPECT_FAIL("", "FIXME: newQMetaObject not implemented properly yet", Abort);
QVERIFY(instance.instanceOf(qclass));
QVERIFY(instanceofJS(instance, qclass).strictlyEquals(true));

QScriptValue instance2 = qclass2.construct();
QScriptValue instance2 = qclass2.callAsConstructor();
QCOMPARE(instance2.isQObject(), true);
QCOMPARE(instance2.toQObject()->metaObject(), qclass2.toQMetaObject());
QVERIFY(instance2.instanceOf(qclass2));
Expand All @@ -1225,7 +1225,7 @@ void tst_QJSEngine::newQMetaObject()

QScriptValueList args;
args << instance;
QScriptValue instance3 = qclass.construct(args);
QScriptValue instance3 = qclass.callAsConstructor(args);
QCOMPARE(instance3.isQObject(), true);
QCOMPARE(instance3.toQObject()->parent(), instance.toQObject());
QVERIFY(instance3.instanceOf(qclass));
Expand Down Expand Up @@ -1272,7 +1272,7 @@ void tst_QJSEngine::newQMetaObject()
QVERIFY(instanceofJS(ret, qclass).strictlyEquals(false));
}
{
QScriptValue ret = qclass3.construct();
QScriptValue ret = qclass3.callAsConstructor();
QVERIFY(ret.isObject());
QVERIFY(ret.property("isCalledAsConstructor").isBool());
QVERIFY(ret.property("isCalledAsConstructor").toBool());
Expand All @@ -1283,14 +1283,14 @@ void tst_QJSEngine::newQMetaObject()
}

// subclassing
qclass2.setProperty("prototype", qclass.construct());
QVERIFY(qclass2.construct().instanceOf(qclass));
QVERIFY(instanceofJS(qclass2.construct(), qclass).strictlyEquals(true));
qclass2.setProperty("prototype", qclass.callAsConstructor());
QVERIFY(qclass2.callAsConstructor().instanceOf(qclass));
QVERIFY(instanceofJS(qclass2.callAsConstructor(), qclass).strictlyEquals(true));

// with meta-constructor
QScriptValue qclass4 = eng.newQMetaObject(&QObject::staticMetaObject);
{
QScriptValue inst = qclass4.construct();
QScriptValue inst = qclass4.callAsConstructor();
QVERIFY(inst.isQObject());
QVERIFY(inst.toQObject() != 0);
QCOMPARE(inst.toQObject()->parent(), (QObject*)0);
Expand All @@ -1300,7 +1300,7 @@ void tst_QJSEngine::newQMetaObject()
QVERIFY(instanceofJS(inst, qclass3).strictlyEquals(false));
}
{
QScriptValue inst = qclass4.construct(QScriptValueList() << eng.newQObject(this));
QScriptValue inst = qclass4.callAsConstructor(QScriptValueList() << eng.newQObject(this));
QVERIFY(inst.isQObject());
QVERIFY(inst.toQObject() != 0);
QCOMPARE(inst.toQObject()->parent(), (QObject*)this);
Expand Down Expand Up @@ -2896,7 +2896,7 @@ static QScriptValue recurse(QScriptContext *ctx, QScriptEngine *eng)
static QScriptValue recurse2(QScriptContext *ctx, QScriptEngine *eng)
{
Q_UNUSED(eng);
return ctx->callee().construct();
return ctx->callee().callAsConstructor();
}

void tst_QJSEngine::infiniteRecursion()
Expand All @@ -2920,7 +2920,7 @@ void tst_QJSEngine::infiniteRecursion()
}
{
QScriptValue fun = eng.newFunction(recurse2);
QScriptValue ret = fun.construct();
QScriptValue ret = fun.callAsConstructor();
QCOMPARE(ret.isError(), true);
QCOMPARE(ret.toString(), stackOverflowError);
}
Expand Down Expand Up @@ -3236,7 +3236,7 @@ void tst_QJSEngine::processEventsWhileRunning_function()
QCOMPARE(eng.processEventsInterval(), 100);

if (x) script.call();
else script.construct();
else script.callAsConstructor();

QVERIFY(!eng.hasUncaughtException());
QVERIFY(receiver.received);
Expand Down Expand Up @@ -6456,13 +6456,13 @@ void tst_QJSEngine::scriptValueFromQMetaObject()
QCOMPARE(meta.toQMetaObject(), &QScriptEngine::staticMetaObject);
// Because of missing Q_SCRIPT_DECLARE_QMETAOBJECT() for QScriptEngine.
QEXPECT_FAIL("", "FIXME: because construct never returns invalid values", Continue);
QVERIFY(!meta.construct().isValid());
QVERIFY(!meta.callAsConstructor().isValid());
}
{
QScriptValue meta = eng.scriptValueFromQMetaObject<QStandardItemModel>();
QVERIFY(meta.isQMetaObject());
QCOMPARE(meta.toQMetaObject(), &QStandardItemModel::staticMetaObject);
QScriptValue obj = meta.construct(QScriptValueList() << eng.newQObject(&eng));
QScriptValue obj = meta.callAsConstructor(QScriptValueList() << eng.newQObject(&eng));
QVERIFY(obj.isQObject());
QStandardItemModel *model = qobject_cast<QStandardItemModel*>(obj.toQObject());
QVERIFY(model != 0);
Expand Down

0 comments on commit 1d577f6

Please sign in to comment.