From c54f04e9fc9d8468a04eb483451e292bdf547a98 Mon Sep 17 00:00:00 2001 From: Lars Knoll Date: Thu, 18 Apr 2013 12:41:59 +0200 Subject: [PATCH] Implement most of QJSEngine against V4 Also do various fixes in other places, so more of the QJS* autotests pass Change-Id: I39662a2ca1441f778595d260860375c5c628bf8e Reviewed-by: Simon Hausmann --- src/qml/qml/v4vm/qv4runtime.cpp | 4 +- src/qml/qml/v4vm/qv4runtime_p.h | 1 + src/qml/qml/v4vm/qv4v8.cpp | 7 +- src/qml/qml/v8/qjsengine.cpp | 45 ++++++------ src/qml/qml/v8/qjsvalue.cpp | 120 ++++++++++++++++++++++++++----- src/qml/qml/v8/qjsvalue_p.h | 8 +++ src/qml/qml/v8/qjsvalueiterator.cpp | 13 +++- src/qml/qml/v8/script.pri | 4 +- tests/auto/qml/qjsvalue/tst_qjsvalue.cpp | 2 +- 9 files changed, 154 insertions(+), 50 deletions(-) diff --git a/src/qml/qml/v4vm/qv4runtime.cpp b/src/qml/qml/v4vm/qv4runtime.cpp index fd22db4..fd1db0e 100644 --- a/src/qml/qml/v4vm/qv4runtime.cpp +++ b/src/qml/qml/v4vm/qv4runtime.cpp @@ -68,7 +68,7 @@ namespace QQmlJS { namespace VM { -QString numberToString(double num, int radix = 10) +QString __qmljs_numberToString(double num, int radix) { if (std::isnan(num)) { return QStringLiteral("NaN"); @@ -457,7 +457,7 @@ double __qmljs_string_to_number(const QString &string) Value __qmljs_string_from_number(ExecutionContext *ctx, double number) { - String *string = ctx->engine->newString(numberToString(number, 10)); + String *string = ctx->engine->newString(__qmljs_numberToString(number, 10)); return Value::fromString(string); } diff --git a/src/qml/qml/v4vm/qv4runtime_p.h b/src/qml/qml/v4vm/qv4runtime_p.h index 1672573..b4cd826 100644 --- a/src/qml/qml/v4vm/qv4runtime_p.h +++ b/src/qml/qml/v4vm/qv4runtime_p.h @@ -184,6 +184,7 @@ Bool __qmljs_to_boolean(const Value &value); double __qmljs_to_number(const Value &value); Value __qmljs_to_string(const Value &value, ExecutionContext *ctx); Q_QML_EXPORT String *__qmljs_convert_to_string(ExecutionContext *ctx, const Value &value); +QString __qmljs_numberToString(double num, int radix = 10); Value __qmljs_to_object(ExecutionContext *ctx, const Value &value); Object *__qmljs_convert_to_object(ExecutionContext *ctx, const Value &value); diff --git a/src/qml/qml/v4vm/qv4v8.cpp b/src/qml/qml/v4vm/qv4v8.cpp index 2b63458..6e55e77 100644 --- a/src/qml/qml/v4vm/qv4v8.cpp +++ b/src/qml/qml/v4vm/qv4v8.cpp @@ -969,7 +969,8 @@ Local Object::GetPrototype() { Local result; QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); - assert(o); + if (!o) + return Local(); return Local::New(Value::fromVmValue(QQmlJS::VM::Value::fromObject(o->prototype))); } @@ -979,7 +980,9 @@ bool Object::SetPrototype(Handle prototype) if (!p) return false; QQmlJS::VM::Object *o = ConstValuePtr(this)->asObject(); - assert(o); + if (!o) + return false; + o->prototype = p; return true; } diff --git a/src/qml/qml/v8/qjsengine.cpp b/src/qml/qml/v8/qjsengine.cpp index 76bb6e8..99279fc 100644 --- a/src/qml/qml/v8/qjsengine.cpp +++ b/src/qml/qml/v8/qjsengine.cpp @@ -46,6 +46,10 @@ #include "qscriptisolate_p.h" #include "qv8engine_p.h" +#include "private/qv4engine_p.h" +#include "private/qv4mm_p.h" +#include "private/qv4globalobject_p.h" + #include #include #include @@ -220,9 +224,7 @@ QJSEngine::~QJSEngine() */ void QJSEngine::collectGarbage() { - Q_D(QJSEngine); - QScriptIsolate api(d); - d->collectGarbage(); + d->m_v4Engine->memoryManager->runGC(); } /*! @@ -255,10 +257,18 @@ void QJSEngine::collectGarbage() */ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, int lineNumber) { - Q_D(QJSEngine); - QScriptIsolate api(d, QScriptIsolate::NotNullEngine); - v8::HandleScope handleScope; - return d->evaluate(program, fileName, qmlSourceCoordinate(lineNumber)); + try { + QQmlJS::VM::Function *f = QQmlJS::VM::EvalFunction::parseSource(d->m_v4Engine->current, fileName, program, QQmlJS::Codegen::EvalCode, + d->m_v4Engine->current->strictMode, true); + if (!f) + return new QJSValuePrivate(d->m_v4Engine, d->m_v4Engine->newSyntaxErrorObject(d->m_v4Engine->current, 0)); + + QQmlJS::VM::Value result = d->m_v4Engine->run(f); + return new QJSValuePrivate(d->m_v4Engine, result); + } catch (QQmlJS::VM::Exception& ex) { + ex.accept(d->m_v4Engine->current); + return new QJSValuePrivate(d->m_v4Engine, ex.value()); + } } /*! @@ -271,11 +281,7 @@ QJSValue QJSEngine::evaluate(const QString& program, const QString& fileName, in */ QJSValue QJSEngine::newObject() { - Q_D(QJSEngine); - QScriptIsolate api(d, QScriptIsolate::NotNullEngine); - v8::HandleScope handleScope; - QQmlJS::VM::ExecutionEngine *engine = d->m_v4Engine; - return new QJSValuePrivate(engine, QQmlJS::VM::Value::fromObject(engine->newObject())); + return new QJSValuePrivate(d->m_v4Engine, d->m_v4Engine->newObject()); } /*! @@ -285,10 +291,9 @@ QJSValue QJSEngine::newObject() */ QJSValue QJSEngine::newArray(uint length) { - Q_D(QJSEngine); - QScriptIsolate api(d, QScriptIsolate::NotNullEngine); - v8::HandleScope handleScope; - return QJSValuePrivate::get(d->newArray(length)); + QQmlJS::VM::ArrayObject *array = d->m_v4Engine->newArrayObject(d->m_v4Engine->current); + array->setArrayLength(length); + return new QJSValuePrivate(d->m_v4Engine, array); } /*! @@ -313,6 +318,7 @@ QJSValue QJSEngine::newArray(uint length) */ QJSValue QJSEngine::newQObject(QObject *object) { + // ### Q_D(QJSEngine); QScriptIsolate api(d, QScriptIsolate::NotNullEngine); v8::HandleScope handleScope; @@ -331,10 +337,7 @@ QJSValue QJSEngine::newQObject(QObject *object) */ QJSValue QJSEngine::globalObject() const { - Q_D(const QJSEngine); - QScriptIsolate api(d, QScriptIsolate::NotNullEngine); - v8::HandleScope handleScope; - return d->scriptValueFromInternal(d->global()); + return new QJSValuePrivate(d->m_v4Engine, d->m_v4Engine->globalObject); } /*! @@ -383,7 +386,7 @@ bool QJSEngine::convertV2(const QJSValue &value, int type, void *ptr) *reinterpret_cast(ptr) = vp->value.toNumber(); return true; case QMetaType::QString: - *reinterpret_cast(ptr) = vp->value.toString(engine->m_v4Engine->current)->toQString(); + *reinterpret_cast(ptr) = value.toString(); return true; case QMetaType::Float: *reinterpret_cast(ptr) = vp->value.toNumber(); diff --git a/src/qml/qml/v8/qjsvalue.cpp b/src/qml/qml/v8/qjsvalue.cpp index d74dfa8..4fb1a2a 100644 --- a/src/qml/qml/v8/qjsvalue.cpp +++ b/src/qml/qml/v8/qjsvalue.cpp @@ -335,10 +335,28 @@ bool QJSValue::isVariant() const */ QString QJSValue::toString() const { - if (!d->engine) - // ### - return QString(); - return d->value.toString(d->engine->current)->toQString(); + // have to check these here as converting those to a VM::String requires a context + // (which we don't always have for those types) + if (d->value.isUndefined()) + return QStringLiteral("undefined"); + else if (d->value.isNull()) + return QStringLiteral("null"); + else if (d->value.isBoolean()) { + if (d->value.booleanValue()) + return QStringLiteral("true"); + else + return QStringLiteral("false"); + } + else if (d->value.isNumber()) + return __qmljs_numberToString(d->value.asDouble()); + + QQmlJS::VM::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + try { + return d->value.toString(ctx)->toQString(); + } catch (Exception &e) { + e.accept(ctx); + return e.value().toString(ctx)->toQString(); + } } /*! @@ -355,7 +373,13 @@ QString QJSValue::toString() const */ double QJSValue::toNumber() const { - return d->value.toNumber(); + QQmlJS::VM::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + try { + return d->value.toNumber(); + } catch (Exception &e) { + e.accept(ctx); + return 0; + } } /*! @@ -372,7 +396,13 @@ double QJSValue::toNumber() const */ bool QJSValue::toBool() const { - return d->value.toBoolean(); + QQmlJS::VM::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + try { + return d->value.toBoolean(); + } catch (Exception &e) { + e.accept(ctx); + return false; + } } /*! @@ -389,7 +419,13 @@ bool QJSValue::toBool() const */ qint32 QJSValue::toInt() const { - return d->value.toInt32(); + QQmlJS::VM::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + try { + return d->value.toInt32(); + } catch (Exception &e) { + e.accept(ctx); + return 0; + } } /*! @@ -406,7 +442,13 @@ qint32 QJSValue::toInt() const */ quint32 QJSValue::toUInt() const { - return d->value.toUInt32(); + QQmlJS::VM::ExecutionContext *ctx = d->engine ? d->engine->current : 0; + try { + return d->value.toUInt32(); + } catch (Exception &e) { + e.accept(ctx); + return 0; + } } /*! @@ -466,9 +508,11 @@ QJSValue QJSValue::call(const QJSValueList &args) arguments[i] = args.at(i).d->getValue(engine); Value result; + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; try { - result = f->call(d->engine->current, Value::fromObject(d->engine->globalObject), arguments.data(), arguments.size()); + result = f->call(ctx, Value::fromObject(d->engine->globalObject), arguments.data(), arguments.size()); } catch (Exception &e) { + e.accept(ctx); result = e.value(); } @@ -509,9 +553,11 @@ QJSValue QJSValue::callWithInstance(const QJSValue &instance, const QJSValueList arguments[i] = args.at(i).d->getValue(engine); Value result; + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; try { - result = f->call(d->engine->current, instance.d->getValue(engine), arguments.data(), arguments.size()); + result = f->call(ctx, instance.d->getValue(engine), arguments.data(), arguments.size()); } catch (Exception &e) { + e.accept(ctx); result = e.value(); } @@ -550,9 +596,11 @@ QJSValue QJSValue::callAsConstructor(const QJSValueList &args) arguments[i] = args.at(i).d->getValue(engine); Value result; + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; try { - result = f->construct(d->engine->current, arguments.data(), arguments.size()); + result = f->construct(ctx, arguments.data(), arguments.size()); } catch (Exception &e) { + e.accept(ctx); result = e.value(); } @@ -704,9 +752,20 @@ QJSValue QJSValue::property(const QString& name) const if (!o) return QJSValue(); - String *s = d->engine->newIdentifier(name); - QQmlJS::VM::Value v = o->get(d->engine->current, s); - return new QJSValuePrivate(d->engine, v); + String *s = d->engine->newString(name); + uint idx = s->asArrayIndex(); + if (idx < UINT_MAX) + return property(idx); + + s->makeIdentifier(d->engine->current); + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; + try { + QQmlJS::VM::Value v = o->get(ctx, s); + return new QJSValuePrivate(d->engine, v); + } catch (QQmlJS::VM::Exception &e) { + e.accept(ctx); + return QJSValue(); + } } /*! @@ -727,8 +786,14 @@ QJSValue QJSValue::property(quint32 arrayIndex) const if (!o) return QJSValue(); - QQmlJS::VM::Value v = o->getIndexed(d->engine->current, arrayIndex); - return new QJSValuePrivate(d->engine, v); + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; + try { + QQmlJS::VM::Value v = o->getIndexed(ctx, arrayIndex); + return new QJSValuePrivate(d->engine, v); + } catch (QQmlJS::VM::Exception &e) { + e.accept(ctx); + return QJSValue(); + } } /*! @@ -748,8 +813,20 @@ void QJSValue::setProperty(const QString& name, const QJSValue& value) if (!o) return; - String *s = d->engine->newIdentifier(name); - o->put(d->engine->current, s, value.d->value); + String *s = d->engine->newString(name); + uint idx = s->asArrayIndex(); + if (idx < UINT_MAX) { + setProperty(idx, value); + return; + } + + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; + s->makeIdentifier(ctx); + try { + o->put(ctx, s, value.d->value); + } catch (QQmlJS::VM::Exception &e) { + e.accept(ctx); + } } /*! @@ -770,7 +847,12 @@ void QJSValue::setProperty(quint32 arrayIndex, const QJSValue& value) if (!o) return; - o->putIndexed(d->engine->current, arrayIndex, value.d->value); + QQmlJS::VM::ExecutionContext *ctx = d->engine->current; + try { + o->putIndexed(ctx, arrayIndex, value.d->value); + } catch (QQmlJS::VM::Exception &e) { + e.accept(ctx); + } } /*! diff --git a/src/qml/qml/v8/qjsvalue_p.h b/src/qml/qml/v8/qjsvalue_p.h index 2bc9ec7..d28d22c 100644 --- a/src/qml/qml/v8/qjsvalue_p.h +++ b/src/qml/qml/v8/qjsvalue_p.h @@ -71,6 +71,14 @@ public: : PersistentValuePrivate(e, v) , string(QString()) {} + QJSValuePrivate(QQmlJS::VM::ExecutionEngine *e, QQmlJS::VM::Object *o) + : PersistentValuePrivate(e, QQmlJS::VM::Value::fromObject(o)) + , string(QString()) + {} + QJSValuePrivate(QQmlJS::VM::ExecutionEngine *e, QQmlJS::VM::String *s) + : PersistentValuePrivate(e, QQmlJS::VM::Value::fromString(s)) + , string(QString()) + {} QJSValuePrivate(const QQmlJS::VM::Value &v) : PersistentValuePrivate(v) , string(QString()) diff --git a/src/qml/qml/v8/qjsvalueiterator.cpp b/src/qml/qml/v8/qjsvalueiterator.cpp index 78f1256..4004269 100644 --- a/src/qml/qml/v8/qjsvalueiterator.cpp +++ b/src/qml/qml/v8/qjsvalueiterator.cpp @@ -48,7 +48,7 @@ QT_BEGIN_NAMESPACE QJSValueIteratorPrivate::QJSValueIteratorPrivate(const QJSValue &v) : value(v) - , iterator(QJSValuePrivate::get(v)->value.asObject(), QQmlJS::VM::ObjectIterator::EnumberableOnly) + , iterator(QJSValuePrivate::get(v)->value.asObject(), QQmlJS::VM::ObjectIterator::NoFlags) , currentValue(0) , currentName(0) , currentIndex(UINT_MAX) @@ -115,6 +115,8 @@ QJSValueIterator::~QJSValueIterator() */ bool QJSValueIterator::hasNext() const { + if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + return false; return d_ptr->nextValue != 0; } @@ -131,12 +133,15 @@ bool QJSValueIterator::hasNext() const */ bool QJSValueIterator::next() { + if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + return false; d_ptr->currentValue = d_ptr->nextValue; d_ptr->currentName = d_ptr->nextName; d_ptr->currentIndex = d_ptr->nextIndex; d_ptr->currentAttributes = d_ptr->nextAttributes; d_ptr->nextValue = d_ptr->iterator.next(&d_ptr->nextName, &d_ptr->nextIndex, &d_ptr->nextAttributes); + return d_ptr->nextValue != 0; } /*! @@ -147,6 +152,8 @@ bool QJSValueIterator::next() */ QString QJSValueIterator::name() const { + if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + return false; if (d_ptr->currentName) return d_ptr->currentName->toQString(); if (d_ptr->currentIndex < UINT_MAX) @@ -163,6 +170,8 @@ QString QJSValueIterator::name() const */ QJSValue QJSValueIterator::value() const { + if (!QJSValuePrivate::get(d_ptr->value)->value.isObject()) + return QJSValue(); if (!d_ptr->currentValue) return QJSValue(); @@ -183,7 +192,7 @@ QJSValue QJSValueIterator::value() const */ QJSValueIterator& QJSValueIterator::operator=(QJSValue& object) { - d_ptr->iterator = QQmlJS::VM::ObjectIterator(QJSValuePrivate::get(object)->value.asObject(), QQmlJS::VM::ObjectIterator::EnumberableOnly); + d_ptr->iterator = QQmlJS::VM::ObjectIterator(QJSValuePrivate::get(object)->value.asObject(), QQmlJS::VM::ObjectIterator::NoFlags); d_ptr->nextValue = d_ptr->iterator.next(&d_ptr->nextName, &d_ptr->nextIndex, &d_ptr->nextAttributes); } diff --git a/src/qml/qml/v8/script.pri b/src/qml/qml/v8/script.pri index 09fa426..f7d9e92 100644 --- a/src/qml/qml/v8/script.pri +++ b/src/qml/qml/v8/script.pri @@ -9,9 +9,7 @@ HEADERS += \ $$PWD/qjsvalue.h \ $$PWD/qjsvalue_p.h \ $$PWD/qjsvalueiterator.h \ - $$PWD/qjsvalue_impl_p.h \ $$PWD/qjsconverter_p.h \ $$PWD/qjsconverter_impl_p.h \ $$PWD/qscriptisolate_p.h \ - $$PWD/qjsvalueiterator_p.h \ - $$PWD/qjsvalueiterator_impl_p.h + $$PWD/qjsvalueiterator_p.h diff --git a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp index 49e66c0..effa54e 100644 --- a/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp +++ b/tests/auto/qml/qjsvalue/tst_qjsvalue.cpp @@ -378,7 +378,7 @@ void tst_QJSValue::toString() "})()"); QVERIFY(!objectObject.isError()); QVERIFY(objectObject.isObject()); - QCOMPARE(objectObject.toString(), QString::fromLatin1("TypeError: Function.prototype.toString is not generic")); + QCOMPARE(objectObject.toString(), QString::fromLatin1("TypeError: Type error")); } QJSValue inv = QJSValue(); -- 2.7.4