From 6318f91dc0bf1ac428037c963b80b7a5d4e1ad30 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Thu, 22 Mar 2012 22:30:14 +0100 Subject: Add internal API for accessing V8 handles of QJS types Make it possible to use the V8 API directly. This might be necessary in cases where the QJS API is missing some functionality (e.g., controlling the V8 profiler), or for performance reasons (e.g., avoiding overhead of QJSValue indirection). The V8 API is clearly more extensive than the QJS API, and QJS will likely never reach feature parity with it (since that's outside the scope of QJS). By providing access to the underlying V8 types, users can still choose to use V8 directly in the (hopefully rare) cases where the public QJS API isn't sufficient. Two new functions are introduced: - qt_QJSEngineV8Context(QJSEngine *) returns a local handle to the engine's internal V8 context. - qt_QJSValueV8Value(const QJSValue &) returns a local handle to the QJSValue's internal V8 value. The caller is responsible for - ensuring that a V8 handle scope is in place; - entering/exiting the QJSEngine's V8 context. The documentation and tests show how that can be done. Also added a benchmark for QJSValue that can be used to measure the effect of using raw V8 API for some common operations. Change-Id: I680aeb2f67ffe5eeadd432a05c8084e43921a118 Reviewed-by: Jamey Hicks Reviewed-by: Lars Knoll --- tests/auto/qml/qjsengine/tst_qjsengine.cpp | 104 +++++++++++++++++++++++++++++ 1 file changed, 104 insertions(+) (limited to 'tests/auto/qml/qjsengine/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 #include +#include + Q_DECLARE_METATYPE(QList) 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 qt_QJSEngineV8Context(QJSEngine *); +extern Q_QML_EXPORT v8::Local 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 context = QT_PREPEND_NAMESPACE(qt_QJSEngineV8Context(&eng)); + v8::Context::Scope contextScope(context); + + v8::Local script = v8::Script::Compile( + v8::String::New("({ foo: 123, bar: 'ciao', baz: true })")); + + v8::TryCatch tc; + v8::Local result = script->Run(); + + QVERIFY(!tc.HasCaught()); + QVERIFY(result->IsObject()); + + v8::Local object = result.As(); + 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 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 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 result = script->Run(); + + QVERIFY(tc.HasCaught()); + QVERIFY(result.IsEmpty()); + + v8::Local 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 context = qt_QJSEngineV8Context(&eng); + v8::Context::Scope contextScope(context); + + QJSValue globalQJS = eng.globalObject(); + v8::Local globalV8Value = qt_QJSValueV8Value(globalQJS); + QVERIFY(!globalV8Value.IsEmpty()); + QVERIFY(globalV8Value->IsObject()); + v8::Local globalV8 = globalV8Value.As(); + + 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 arrayV8Value = qt_QJSValueV8Value(arrayQJS); + QVERIFY(!arrayV8Value.IsEmpty()); + QVERIFY(arrayV8Value->IsArray()); + v8::Local arrayV8 = arrayV8Value.As(); + + QCOMPARE(int(arrayV8->Length()), 10); + arrayV8->Set(5, v8::Null()); + QVERIFY(arrayQJS.property(5).isNull()); +} + QTEST_MAIN(tst_QJSEngine) #include "tst_qjsengine.moc" -- cgit v1.2.3