diff options
-rw-r--r-- | src/qml/qml/v8/qjsengine.cpp | 29 | ||||
-rw-r--r-- | src/qml/qml/v8/qjsvalue.cpp | 26 | ||||
-rw-r--r-- | src/qml/qml/v8/qjsvalue_impl_p.h | 5 | ||||
-rw-r--r-- | src/qml/qml/v8/qjsvalue_p.h | 1 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/qjsengine.pro | 2 | ||||
-rw-r--r-- | tests/auto/qml/qjsengine/tst_qjsengine.cpp | 104 | ||||
-rw-r--r-- | tests/benchmarks/script/qjsvalue/qjsvalue.pro | 11 | ||||
-rw-r--r-- | tests/benchmarks/script/qjsvalue/tst_qjsvalue.cpp | 189 | ||||
-rw-r--r-- | tests/benchmarks/script/script.pro | 4 |
9 files changed, 370 insertions, 1 deletions
diff --git a/src/qml/qml/v8/qjsengine.cpp b/src/qml/qml/v8/qjsengine.cpp index dcdaf2c359..03cd51eab9 100644 --- a/src/qml/qml/v8/qjsengine.cpp +++ b/src/qml/qml/v8/qjsengine.cpp @@ -414,6 +414,35 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) \sa toScriptValue() */ +/*! + \internal + + Returns this engine's internal V8 context. + + The context enables direct use of the V8 API. + The caller is responsible for ensuring that a handle scope is in place, + and for entering/exiting the context. + Example: + + \code + QJSEngine eng; + ... + v8::HandleScope handleScope; + v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + // Do stuff (e.g., call v8::Script::Compile()) ... + \endcode + + \sa qt_QJSValueV8Value() +*/ +Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *engine) +{ + Q_ASSERT(engine != 0); + QV8Engine *d = engine->handle(); + return v8::Local<v8::Context>::New(d->context()); +} + QT_END_NAMESPACE #include "moc_qjsengine.cpp" diff --git a/src/qml/qml/v8/qjsvalue.cpp b/src/qml/qml/v8/qjsvalue.cpp index 19aee1e459..58732f2df8 100644 --- a/src/qml/qml/v8/qjsvalue.cpp +++ b/src/qml/qml/v8/qjsvalue.cpp @@ -869,4 +869,30 @@ bool QJSValue::isQObject() const return d->isQObject(); } +/*! + \internal + + Returns this value's internal V8 value, or an empty handle if + the QJSValue isn't bound to a QJSEngine. + + The V8 value enables direct use of the V8 API. + The caller is responsible for ensuring that a handle scope is in place. + Example: + + \code + QJSValue value = ...; + v8::HandleScope handleScope; + v8::Local<v8::Value> v8value = qt_QJSValueV8Value(value); + + // Do something with the V8 value (e.g., call v8::Value::IsInt32()) ... + \endcode + + \sa qt_QJSEngineV8Context() +*/ +Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &value) +{ + QJSValuePrivate *d = QJSValuePrivate::get(value); + return v8::Local<v8::Value>::New(d->handle()); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/v8/qjsvalue_impl_p.h b/src/qml/qml/v8/qjsvalue_impl_p.h index e6bbd438f9..d9f78c2560 100644 --- a/src/qml/qml/v8/qjsvalue_impl_p.h +++ b/src/qml/qml/v8/qjsvalue_impl_p.h @@ -880,6 +880,11 @@ inline QJSValuePrivate::operator v8::Handle<v8::Object>() const return v8::Handle<v8::Object>::Cast(m_value); } +inline v8::Handle<v8::Value> QJSValuePrivate::handle() const +{ + return m_value; +} + /*! * Return a v8::Handle, assign to the engine if needed. */ diff --git a/src/qml/qml/v8/qjsvalue_p.h b/src/qml/qml/v8/qjsvalue_p.h index 31b30aad0d..1564b8c5f0 100644 --- a/src/qml/qml/v8/qjsvalue_p.h +++ b/src/qml/qml/v8/qjsvalue_p.h @@ -164,6 +164,7 @@ public: inline operator v8::Handle<v8::Value>() const; inline operator v8::Handle<v8::Object>() const; + inline v8::Handle<v8::Value> handle() const; inline v8::Handle<v8::Value> asV8Value(QV8Engine *engine); private: QIntrusiveListNode m_node; diff --git a/tests/auto/qml/qjsengine/qjsengine.pro b/tests/auto/qml/qjsengine/qjsengine.pro index 49338e7d2c..f3d37bc183 100644 --- a/tests/auto/qml/qjsengine/qjsengine.pro +++ b/tests/auto/qml/qjsengine/qjsengine.pro @@ -1,7 +1,7 @@ CONFIG += testcase CONFIG += parallel_test TARGET = tst_qjsengine -QT += qml widgets testlib +QT += v8-private qml widgets testlib macx:CONFIG -= app_bundle SOURCES += tst_qjsengine.cpp diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp index bf76adbf6d..b0be3be376 100644 --- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp +++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp @@ -49,6 +49,8 @@ #include <QtCore/qnumeric.h> #include <stdlib.h> +#include <private/v8.h> + Q_DECLARE_METATYPE(QList<int>) Q_DECLARE_METATYPE(QObjectList) @@ -67,6 +69,11 @@ static void collectGarbage_helper(QJSEngine &eng) eng.collectGarbage(); } +QT_BEGIN_NAMESPACE +extern Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *); +extern Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &); +QT_END_NAMESPACE + class tst_QJSEngine : public QObject { Q_OBJECT @@ -316,6 +323,10 @@ private slots: void dateConversionQtJS(); void functionPrototypeExtensions(); void threadedEngine(); + + void v8Context_simple(); + void v8Context_exception(); + void v8Context_mixAPIs(); }; tst_QJSEngine::tst_QJSEngine() @@ -6351,6 +6362,99 @@ void tst_QJSEngine::threadedEngine() QCOMPARE(thread2.result, 2); } +void tst_QJSEngine::v8Context_simple() +{ + QJSEngine eng; + + v8::HandleScope handleScope; + v8::Local<v8::Context> context = QT_PREPEND_NAMESPACE(qt_QJSEngineV8Context(&eng)); + v8::Context::Scope contextScope(context); + + v8::Local<v8::Script> script = v8::Script::Compile( + v8::String::New("({ foo: 123, bar: 'ciao', baz: true })")); + + v8::TryCatch tc; + v8::Local<v8::Value> result = script->Run(); + + QVERIFY(!tc.HasCaught()); + QVERIFY(result->IsObject()); + + v8::Local<v8::Object> object = result.As<v8::Object>(); + QVERIFY(object->Get(v8::String::New("foo"))->Equals(v8::Number::New(123))); + QVERIFY(object->Get(v8::String::New("bar"))->Equals(v8::String::New("ciao"))); + QVERIFY(object->Get(v8::String::New("baz"))->IsTrue()); +} + +void tst_QJSEngine::v8Context_exception() +{ + QJSEngine eng; + + v8::HandleScope handleScope; + v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + int startLineNumber = 42; + v8::ScriptOrigin origin(v8::String::New("test.js"), v8::Integer::New(startLineNumber)); + v8::Local<v8::Script> script = v8::Script::Compile( + v8::String::New( + "function foo(i) {\n" + " if (i > 5)\n" + " throw Error('Catch me if you can');\n" + " foo(i + 1);\n" + "}\n" + "foo(0);"), + &origin); + +// QJS does this for us: +// v8::V8::SetCaptureStackTraceForUncaughtExceptions(true); + + v8::TryCatch tc; + v8::Local<v8::Value> result = script->Run(); + + QVERIFY(tc.HasCaught()); + QVERIFY(result.IsEmpty()); + + v8::Local<v8::Message> message = tc.Message(); + QVERIFY(!message.IsEmpty()); + QCOMPARE(*v8::String::AsciiValue(message->Get()), "Uncaught Error: Catch me if you can"); + QCOMPARE(*v8::String::AsciiValue(message->GetScriptResourceName()), "test.js"); + QCOMPARE(message->GetLineNumber(), startLineNumber + 3); +} + +void tst_QJSEngine::v8Context_mixAPIs() +{ + QJSEngine eng; + + v8::HandleScope handleScope; + v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + QJSValue globalQJS = eng.globalObject(); + v8::Local<v8::Value> globalV8Value = qt_QJSValueV8Value(globalQJS); + QVERIFY(!globalV8Value.IsEmpty()); + QVERIFY(globalV8Value->IsObject()); + v8::Local<v8::Object> globalV8 = globalV8Value.As<v8::Object>(); + + QVERIFY(globalQJS.property("foo").isUndefined()); + QVERIFY(globalV8->Get(v8::String::New("foo"))->IsUndefined()); + + globalQJS.setProperty("foo", 123); + QVERIFY(globalV8->Get(v8::String::New("foo"))->Equals(v8::Number::New(123))); + + globalV8->Set(v8::String::New("bar"), v8::String::New("ciao")); + QVERIFY(globalQJS.property("bar").equals("ciao")); + + QJSValue arrayQJS = eng.newArray(10); + v8::Local<v8::Value> arrayV8Value = qt_QJSValueV8Value(arrayQJS); + QVERIFY(!arrayV8Value.IsEmpty()); + QVERIFY(arrayV8Value->IsArray()); + v8::Local<v8::Array> arrayV8 = arrayV8Value.As<v8::Array>(); + + QCOMPARE(int(arrayV8->Length()), 10); + arrayV8->Set(5, v8::Null()); + QVERIFY(arrayQJS.property(5).isNull()); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" diff --git a/tests/benchmarks/script/qjsvalue/qjsvalue.pro b/tests/benchmarks/script/qjsvalue/qjsvalue.pro new file mode 100644 index 0000000000..eb78cc179e --- /dev/null +++ b/tests/benchmarks/script/qjsvalue/qjsvalue.pro @@ -0,0 +1,11 @@ +CONFIG += testcase +TEMPLATE = app +TARGET = tst_bench_qjsvalue +DEPENDPATH += . +INCLUDEPATH += . +macx:CONFIG -= app_bundle +CONFIG += release + +SOURCES += tst_qjsvalue.cpp + +QT += core-private v8-private qml-private testlib diff --git a/tests/benchmarks/script/qjsvalue/tst_qjsvalue.cpp b/tests/benchmarks/script/qjsvalue/tst_qjsvalue.cpp new file mode 100644 index 0000000000..aea74bda3a --- /dev/null +++ b/tests/benchmarks/script/qjsvalue/tst_qjsvalue.cpp @@ -0,0 +1,189 @@ +/**************************************************************************** +** +** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/ +** +** This file is part of the test suite of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** GNU Lesser General Public License Usage +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this +** file. Please review the following information to ensure the GNU Lesser +** General Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU General +** Public License version 3.0 as published by the Free Software Foundation +** and appearing in the file LICENSE.GPL included in the packaging of this +** file. Please review the following information to ensure the GNU General +** Public License version 3.0 requirements will be met: +** http://www.gnu.org/copyleft/gpl.html. +** +** Other Usage +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qtest.h> +#include <QJSEngine> +#include <QJSValue> +#include <private/v8.h> + +QT_BEGIN_NAMESPACE +extern Q_QML_EXPORT v8::Local<v8::Context> qt_QJSEngineV8Context(QJSEngine *); +extern Q_QML_EXPORT v8::Local<v8::Value> qt_QJSValueV8Value(const QJSValue &); +QT_END_NAMESPACE + +class tst_QJSValue : public QObject +{ + Q_OBJECT +public: + tst_QJSValue() {} + +private slots: + void fillArray(); + void fillArray_V8(); + + void property(); + void property_V8(); + + void setProperty(); + void setProperty_V8(); + + void call(); + void call_V8(); +}; + +void tst_QJSValue::fillArray() +{ + QJSEngine eng; + static const int ArrayLength = 10000; + QJSValue array = eng.newArray(ArrayLength); + QBENCHMARK { + for (int i = 0; i < ArrayLength; ++i) + array.setProperty(i, i); + } +} + +void tst_QJSValue::fillArray_V8() +{ + QJSEngine eng; + static const int ArrayLength = 10000; + QJSValue array = eng.newArray(ArrayLength); + + v8::HandleScope handleScope; + v8::Local<v8::Array> v8array = qt_QJSValueV8Value(array).As<v8::Array>(); + QBENCHMARK { + for (int i = 0; i < ArrayLength; ++i) + v8array->Set(i, v8::Number::New(i)); + } +} + +void tst_QJSValue::property() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + QString propertyName = QString::fromLatin1("foo"); + object.setProperty(propertyName, 123); + QVERIFY(object.property(propertyName).isNumber()); + QBENCHMARK { + object.property(propertyName); + } +} + +void tst_QJSValue::property_V8() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + QString propertyName = QString::fromLatin1("foo"); + object.setProperty(propertyName, 123); + QVERIFY(object.property(propertyName).isNumber()); + + v8::HandleScope handleScope; + v8::Local<v8::Object> v8object = qt_QJSValueV8Value(object).As<v8::Object>(); + v8::Local<v8::String> v8propertyName = v8::String::New("foo"); + QVERIFY(v8object->Get(v8propertyName)->IsNumber()); + QBENCHMARK { + v8object->Get(v8propertyName); + } +} + +void tst_QJSValue::setProperty() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + QString propertyName = QString::fromLatin1("foo"); + QJSValue value(123); + QBENCHMARK { + object.setProperty(propertyName, value); + } +} + +void tst_QJSValue::setProperty_V8() +{ + QJSEngine eng; + QJSValue object = eng.newObject(); + + v8::HandleScope handleScope; + // A context scope is needed for v8::Object::Set(), otherwise we crash. + v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + v8::Local<v8::Object> v8object = qt_QJSValueV8Value(object).As<v8::Object>(); + v8::Local<v8::String> v8propertyName = v8::String::New("foo"); + v8::Local<v8::Value> v8value = v8::Number::New(123); + QBENCHMARK { + v8object->Set(v8propertyName, v8value); + } +} + +#define TEST_FUNCTION_SOURCE "(function() { return 123; })" + +void tst_QJSValue::call() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate(TEST_FUNCTION_SOURCE); + QVERIFY(fun.isCallable()); + QJSValueList args; + QVERIFY(fun.call(args).isNumber()); + QBENCHMARK { + fun.call(args); + } +} + +void tst_QJSValue::call_V8() +{ + QJSEngine eng; + QJSValue fun = eng.evaluate(TEST_FUNCTION_SOURCE); + QVERIFY(fun.isCallable()); + + v8::HandleScope handleScope; + v8::Local<v8::Context> context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + v8::Local<v8::Function> v8fun = qt_QJSValueV8Value(fun).As<v8::Function>(); + v8::Local<v8::Object> v8thisObject = v8::Object::New(); + QVERIFY(v8fun->Call(v8thisObject, /*argc=*/0, /*argv=*/0)->IsNumber()); + QBENCHMARK { + v8fun->Call(v8thisObject, /*argc=*/0, /*argv=*/0); + } +} + +QTEST_MAIN(tst_QJSValue) + +#include "tst_qjsvalue.moc" diff --git a/tests/benchmarks/script/script.pro b/tests/benchmarks/script/script.pro new file mode 100644 index 0000000000..37dc03801d --- /dev/null +++ b/tests/benchmarks/script/script.pro @@ -0,0 +1,4 @@ +TEMPLATE = subdirs + +SUBDIRS += \ + qjsvalue |