diff options
Diffstat (limited to 'src/qml')
94 files changed, 1894 insertions, 1125 deletions
diff --git a/src/qml/animations/qanimationgroupjob_p.h b/src/qml/animations/qanimationgroupjob_p.h index b01b2f3b36..a27c9195dd 100644 --- a/src/qml/animations/qanimationgroupjob_p.h +++ b/src/qml/animations/qanimationgroupjob_p.h @@ -72,7 +72,7 @@ public: QAbstractAnimationJob *firstChild() const { return m_firstChild; } QAbstractAnimationJob *lastChild() const { return m_lastChild; } - void clear(); + virtual void clear(); //called by QAbstractAnimationJob virtual void uncontrolledAnimationFinished(QAbstractAnimationJob *animation); diff --git a/src/qml/animations/qsequentialanimationgroupjob.cpp b/src/qml/animations/qsequentialanimationgroupjob.cpp index 22e20d9268..d98546122f 100644 --- a/src/qml/animations/qsequentialanimationgroupjob.cpp +++ b/src/qml/animations/qsequentialanimationgroupjob.cpp @@ -204,6 +204,15 @@ int QSequentialAnimationGroupJob::duration() const return ret; } +void QSequentialAnimationGroupJob::clear() +{ + m_previousLoop = 0; + QAnimationGroupJob::clear(); + + // clear() should call removeAnimation(), which will clear m_currentAnimation, eventually. + Q_ASSERT(m_currentAnimation == nullptr); +} + void QSequentialAnimationGroupJob::updateCurrentTime(int currentTime) { if (!m_currentAnimation) diff --git a/src/qml/animations/qsequentialanimationgroupjob_p.h b/src/qml/animations/qsequentialanimationgroupjob_p.h index 13f9806be1..34e8fe1e08 100644 --- a/src/qml/animations/qsequentialanimationgroupjob_p.h +++ b/src/qml/animations/qsequentialanimationgroupjob_p.h @@ -68,6 +68,7 @@ public: int duration() const override; QAbstractAnimationJob *currentAnimation() const { return m_currentAnimation; } + void clear() override; protected: void updateCurrentTime(int) override; diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 868f600a10..6e077ec44c 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1082,6 +1082,9 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST binding->type = QV4::CompiledData::Binding::Type_Number; binding->value.constantValueIndex = jsGenerator->registerConstant(QV4::Encode(-lit->value)); } + } else if (QQmlJS::AST::cast<QQmlJS::AST::NullExpression *>(expr)) { + binding->type = QV4::CompiledData::Binding::Type_Null; + binding->value.nullMarker = 0; } } @@ -1627,7 +1630,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen uint nextOffset = objectOffset + objectOffsetTableSize; for (Object *o : qAsConst(output.objects)) { objectOffsets.insert(o, nextOffset); - nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.count); + nextOffset += QV4::CompiledData::Object::calculateSizeExcludingSignalsAndEnums(o->functionCount(), o->propertyCount(), o->aliasCount(), o->enumCount(), o->signalCount(), o->bindingCount(), o->namedObjectsInComponent.size()); int signalTableSize = 0; for (const Signal *s = o->firstSignal(); s; s = s->next) @@ -1702,7 +1705,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen objectToWrite->offsetToBindings = nextOffset; nextOffset += objectToWrite->nBindings * sizeof(QV4::CompiledData::Binding); - objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.count; + objectToWrite->nNamedObjectsInComponent = o->namedObjectsInComponent.size(); objectToWrite->offsetToNamedObjectsInComponent = nextOffset; nextOffset += objectToWrite->nNamedObjectsInComponent * sizeof(quint32); @@ -1774,7 +1777,7 @@ void QmlUnitGenerator::generate(Document &output, const QV4::CompiledData::Depen } quint32_le *namedObjectInComponentPtr = reinterpret_cast<quint32_le *>(objectPtr + objectToWrite->offsetToNamedObjectsInComponent); - for (int i = 0; i < o->namedObjectsInComponent.count; ++i) { + for (int i = 0; i < o->namedObjectsInComponent.size(); ++i) { *namedObjectInComponentPtr++ = o->namedObjectsInComponent.at(i); } } @@ -2105,8 +2108,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO f->location = compiledFunction->location; f->nameIndex = compiledFunction->nameIndex; - const QString name = unit->stringAtInternal(compiledFunction->nameIndex); - f->formals.allocate(pool, int(compiledFunction->nFormals)); const quint32_le *formalNameIdx = compiledFunction->formalsTable(); for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 56724bcda5..22bc2d2953 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -198,69 +198,6 @@ struct PoolList Iterator end() { return Iterator(nullptr); } }; -template <typename T> -class FixedPoolArray -{ - T *data; -public: - int count = 0; - - FixedPoolArray() - : data(nullptr) - - {} - - void allocate(QQmlJS::MemoryPool *pool, int size) - { - count = size; - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - } - - void allocate(QQmlJS::MemoryPool *pool, const QVector<T> &vector) - { - count = vector.count(); - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - - if (QTypeInfo<T>::isComplex) { - for (int i = 0; i < count; ++i) - new (data + i) T(vector.at(i)); - } else { - memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T)); - } - } - - template <typename Container> - void allocate(QQmlJS::MemoryPool *pool, const Container &container) - { - count = container.count(); - data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); - typename Container::ConstIterator it = container.constBegin(); - for (int i = 0; i < count; ++i) - new (data + i) T(*it++); - } - - const T &at(int index) const { - Q_ASSERT(index >= 0 && index < count); - return data[index]; - } - - T &operator[](int index) { - Q_ASSERT(index >= 0 && index < count); - return data[index]; - } - - - int indexOf(const T &value) const { - for (int i = 0; i < count; ++i) - if (data[i] == value) - return i; - return -1; - } - - const T *begin() const { return data; } - const T *end() const { return data + count; } -}; - struct Object; struct EnumValue : public QV4::CompiledData::EnumValue @@ -410,7 +347,7 @@ public: FixedPoolArray<int> runtimeFunctionIndices; FixedPoolArray<quint32> namedObjectsInComponent; - int namedObjectsInComponentCount() const { return namedObjectsInComponent.count; } + int namedObjectsInComponentCount() const { return namedObjectsInComponent.size(); } const quint32 *namedObjectsInComponentTable() const { return namedObjectsInComponent.begin(); } private: diff --git a/src/qml/compiler/qqmlpropertyvalidator.cpp b/src/qml/compiler/qqmlpropertyvalidator.cpp index 5e821518a4..1beaac8095 100644 --- a/src/qml/compiler/qqmlpropertyvalidator.cpp +++ b/src/qml/compiler/qqmlpropertyvalidator.cpp @@ -352,30 +352,45 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache return noError; } + auto warnOrError = [&](const QString &error) { + if (binding->type == QV4::CompiledData::Binding::Type_Null) { + QQmlError warning; + warning.setUrl(compilationUnit->url()); + warning.setLine(binding->valueLocation.line); + warning.setColumn(binding->valueLocation.column); + warning.setDescription(error + tr(" - Assigning null to incompatible properties in QML " + "is deprecated. This will become a compile error in " + "future versions of Qt.")); + enginePrivate->warning(warning); + return noError; + } + return QQmlCompileError(binding->valueLocation, error); + }; + switch (property->propType()) { case QMetaType::QVariant: break; case QVariant::String: { if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string expected")); + return warnOrError(tr("Invalid property assignment: string expected")); } } break; case QVariant::StringList: { if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or string list expected")); + return warnOrError(tr("Invalid property assignment: string or string list expected")); } } break; case QVariant::ByteArray: { if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: byte array expected")); + return warnOrError(tr("Invalid property assignment: byte array expected")); } } break; case QVariant::Url: { if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url expected")); + return warnOrError(tr("Invalid property assignment: url expected")); } } break; @@ -385,7 +400,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache if (double(uint(d)) == d) return noError; } - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsigned int expected")); + return warnOrError(tr("Invalid property assignment: unsigned int expected")); } break; case QVariant::Int: { @@ -394,18 +409,18 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache if (double(int(d)) == d) return noError; } - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int expected")); + return warnOrError(tr("Invalid property assignment: int expected")); } break; case QMetaType::Float: { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + return warnOrError(tr("Invalid property assignment: number expected")); } } break; case QVariant::Double: { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number expected")); + return warnOrError(tr("Invalid property assignment: number expected")); } } break; @@ -413,7 +428,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: color expected")); + return warnOrError(tr("Invalid property assignment: color expected")); } } break; @@ -422,7 +437,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: date expected")); + return warnOrError(tr("Invalid property assignment: date expected")); } } break; @@ -430,7 +445,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: time expected")); + return warnOrError(tr("Invalid property assignment: time expected")); } } break; @@ -438,7 +453,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::dateTimeFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: datetime expected")); + return warnOrError(tr("Invalid property assignment: datetime expected")); } } break; @@ -447,7 +462,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + return warnOrError(tr("Invalid property assignment: point expected")); } } break; @@ -455,7 +470,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + return warnOrError(tr("Invalid property assignment: point expected")); } } break; @@ -463,7 +478,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + return warnOrError(tr("Invalid property assignment: size expected")); } } break; @@ -471,7 +486,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: size expected")); + return warnOrError(tr("Invalid property assignment: size expected")); } } break; @@ -479,7 +494,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: rect expected")); + return warnOrError(tr("Invalid property assignment: rect expected")); } } break; @@ -487,13 +502,13 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache bool ok = false; QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); if (!ok) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: point expected")); + return warnOrError(tr("Invalid property assignment: point expected")); } } break; case QVariant::Bool: { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: boolean expected")); + return warnOrError(tr("Invalid property assignment: boolean expected")); } } break; @@ -503,7 +518,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float yp; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 2D vector expected")); + return warnOrError(tr("Invalid property assignment: 2D vector expected")); } } break; @@ -514,7 +529,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float zy; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 3D vector expected")); + return warnOrError(tr("Invalid property assignment: 3D vector expected")); } } break; @@ -526,7 +541,7 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float wp; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: 4D vector expected")); + return warnOrError(tr("Invalid property assignment: 4D vector expected")); } } break; @@ -538,17 +553,17 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache float zp; } vec; if (!QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec))) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: quaternion expected")); + return warnOrError(tr("Invalid property assignment: quaternion expected")); } } break; case QVariant::RegExp: - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); + return warnOrError(tr("Invalid property assignment: regular expression expected; use /pattern/ syntax")); default: { // generate single literal value assignment to a list property if required if (property->propType() == qMetaTypeId<QList<qreal> >()) { if (binding->type != QV4::CompiledData::Binding::Type_Number) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: number or array of numbers expected")); + return warnOrError(tr("Invalid property assignment: number or array of numbers expected")); } break; } else if (property->propType() == qMetaTypeId<QList<int> >()) { @@ -559,33 +574,36 @@ QQmlCompileError QQmlPropertyValidator::validateLiteralBinding(QQmlPropertyCache ok = false; } if (!ok) - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: int or array of ints expected")); + return warnOrError(tr("Invalid property assignment: int or array of ints expected")); break; } else if (property->propType() == qMetaTypeId<QList<bool> >()) { if (binding->type != QV4::CompiledData::Binding::Type_Boolean) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: bool or array of bools expected")); + return warnOrError(tr("Invalid property assignment: bool or array of bools expected")); } break; } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { if (binding->type != QV4::CompiledData::Binding::Type_String) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: url or array of urls expected")); + return warnOrError(tr("Invalid property assignment: url or array of urls expected")); } break; } else if (property->propType() == qMetaTypeId<QList<QString> >()) { if (!binding->evaluatesToString()) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: string or array of strings expected")); + return warnOrError(tr("Invalid property assignment: string or array of strings expected")); } break; } else if (property->propType() == qMetaTypeId<QJSValue>()) { break; } else if (property->propType() == qMetaTypeId<QQmlScriptString>()) { break; + } else if (property->isQObject() + && binding->type == QV4::CompiledData::Binding::Type_Null) { + break; } // otherwise, try a custom type assignment QQmlMetaType::StringConverter converter = QQmlMetaType::customStringConverter(property->propType()); if (!converter) { - return QQmlCompileError(binding->valueLocation, tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); + return warnOrError(tr("Invalid property assignment: unsupported type \"%1\"").arg(QString::fromLatin1(QMetaType::typeName(property->propType())))); } } break; @@ -671,8 +689,6 @@ QQmlCompileError QQmlPropertyValidator::validateObjectBinding(QQmlPropertyData * } } return noError; - } else if (compilationUnit->objectAt(binding->value.objectIndex)->flags & QV4::CompiledData::Object::IsComponent) { - return noError; } else if (binding->flags & QV4::CompiledData::Binding::IsSignalHandlerObject && property->isFunction()) { return noError; } else if (QQmlValueTypeFactory::isValueType(property->propType())) { diff --git a/src/qml/compiler/qv4bytecodegenerator.cpp b/src/qml/compiler/qv4bytecodegenerator.cpp index d0bca69b56..ea252a6013 100644 --- a/src/qml/compiler/qv4bytecodegenerator.cpp +++ b/src/qml/compiler/qv4bytecodegenerator.cpp @@ -180,6 +180,10 @@ void BytecodeGenerator::finalize(Compiler::Context *context) context->code = code; context->lineNumberMapping = lineNumbers; + + for (const auto &li : _labelInfos) { + context->labelInfo.push_back(instructions.at(labels.at(li.labelIndex)).position); + } } int BytecodeGenerator::addInstructionHelper(Instr::Type type, const Instr &i, int offsetOfOffset) { diff --git a/src/qml/compiler/qv4bytecodegenerator_p.h b/src/qml/compiler/qv4bytecodegenerator_p.h index 4f3dc27acc..1d0a57c536 100644 --- a/src/qml/compiler/qv4bytecodegenerator_p.h +++ b/src/qml/compiler/qv4bytecodegenerator_p.h @@ -51,6 +51,7 @@ // We mean it. // #include <private/qv4instr_moth_p.h> +#include <private/qv4compileddata_p.h> QT_BEGIN_NAMESPACE @@ -65,6 +66,8 @@ namespace Moth { class BytecodeGenerator { public: + typedef CompiledData::Function::TraceInfoCount TraceInfoCount; + BytecodeGenerator(int line, bool debug) : startLine(line), debugMode(debug) {} @@ -161,6 +164,15 @@ public: addInstructionHelper(Moth::Instr::Type(InstrT), genericInstr); } + // Same as addInstruction, but also add a trace slot. Move only, because the instruction cannot + // be reused afterwards. + template<int InstrT> + void addTracingInstruction(InstrData<InstrT> data) + { + data.traceSlot = nextTraceInfo(); + addInstruction(data); + } + Q_REQUIRED_RESULT Jump jump() { QT_WARNING_PUSH @@ -172,14 +184,12 @@ QT_WARNING_POP Q_REQUIRED_RESULT Jump jumpTrue() { - Instruction::JumpTrue data; - return addJumpInstruction(data); + return addTracingJumpInstruction(Instruction::JumpTrue()); } Q_REQUIRED_RESULT Jump jumpFalse() { - Instruction::JumpFalse data; - return addJumpInstruction(data); + return addTracingJumpInstruction(Instruction::JumpFalse()); } Q_REQUIRED_RESULT Jump jumpNotUndefined() @@ -198,16 +208,16 @@ QT_WARNING_POP { Instruction::CmpStrictEqual cmp; cmp.lhs = lhs; - addInstruction(cmp); - addJumpInstruction(Instruction::JumpTrue()).link(target); + addInstruction(std::move(cmp)); + addTracingJumpInstruction(Instruction::JumpTrue()).link(target); } void jumpStrictNotEqual(const StackSlot &lhs, const Label &target) { Instruction::CmpStrictNotEqual cmp; cmp.lhs = lhs; - addInstruction(cmp); - addJumpInstruction(Instruction::JumpTrue()).link(target); + addInstruction(std::move(cmp)); + addTracingJumpInstruction(Instruction::JumpTrue()).link(target); } void setUnwindHandler(ExceptionHandler *handler) @@ -248,6 +258,13 @@ QT_WARNING_POP void finalize(Compiler::Context *context); template<int InstrT> + Jump addTracingJumpInstruction(InstrData<InstrT> &&data) + { + data.traceSlot = nextTraceInfo(); + return addJumpInstruction(data); + } + + template<int InstrT> Jump addJumpInstruction(const InstrData<InstrT> &data) { Instr genericInstr; @@ -258,9 +275,9 @@ QT_WARNING_POP void addCJumpInstruction(bool jumpOnFalse, const Label *trueLabel, const Label *falseLabel) { if (jumpOnFalse) - addJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); + addTracingJumpInstruction(Instruction::JumpFalse()).link(*falseLabel); else - addJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); + addTracingJumpInstruction(Instruction::JumpTrue()).link(*trueLabel); } void clearLastInstruction() @@ -268,6 +285,32 @@ QT_WARNING_POP lastInstrType = -1; } + TraceInfoCount nextTraceInfo() + { + // If tracing is disabled, use slot 0 to unconditionally store all trace info + if (nTraceInfos == CompiledData::Function::NoTracing()) + return TraceInfoCount(0); + return nTraceInfos++; + } + + void setTracing(bool onoff, int argumentCount) + { + if (onoff) + nTraceInfos = argumentCount; + else + nTraceInfos = CompiledData::Function::NoTracing(); + } + + TraceInfoCount traceInfoCount() const + { + return nTraceInfos; + } + + void addLoopStart(const Label &start) + { + _labelInfos.push_back({ start.index }); + } + private: friend struct Jump; friend struct Label; @@ -302,6 +345,13 @@ private: int lastInstrType = -1; Moth::Instr lastInstr; + + TraceInfoCount nTraceInfos = TraceInfoCount(0); + + struct LabelInfo { + int labelIndex; + }; + std::vector<LabelInfo> _labelInfos; }; } diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index e1fc0c6ee3..92b112c2fa 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -79,458 +79,3 @@ void ByteCodeHandler::decode(const char *code, uint len) #undef DECODE_AND_DISPATCH #undef DISPATCH_INSTRUCTION - -#define MOTH_UNUSED_ARGS0() -#define MOTH_UNUSED_ARGS1(arg) \ - Q_UNUSED(arg); -#define MOTH_UNUSED_ARGS2(arg1, arg2) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); -#define MOTH_UNUSED_ARGS3(arg1, arg2, arg3) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); \ - Q_UNUSED(arg3); -#define MOTH_UNUSED_ARGS4(arg1, arg2, arg3, arg4) \ - Q_UNUSED(arg1); \ - Q_UNUSED(arg2); \ - Q_UNUSED(arg3); \ - Q_UNUSED(arg4); - -#define MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, ...) \ - MOTH_EXPAND_FOR_MSVC(MOTH_UNUSED_ARGS##nargs(__VA_ARGS__)) - -#define MOTH_MARK_ARGS_UNUSED_INSTRUCTION(name, nargs, ...) \ - MOTH_MARK_ARGS_UNUSED_PLEASE(nargs, __VA_ARGS__) - -#define COLLECTOR_BEGIN_INSTR(instr) \ - { \ - INSTR_##instr(MOTH_DECODE_WITH_BASE) \ - INSTR_##instr(MOTH_MARK_ARGS_UNUSED) \ - Q_UNUSED(base_ptr); - -#define COLLECTOR_END_INSTR(instr) \ - continue; \ - } - -std::vector<int> ByteCodeHandler::collectLabelsInBytecode(const char *code, uint len) -{ - MOTH_JUMP_TABLE; - - std::vector<int> labels; - - const auto addLabel = [&labels,len](int offset) { - Q_ASSERT(offset >= 0 && offset < static_cast<int>(len)); - labels.push_back(offset); - }; - - const char *start = code; - const char *end = code + len; - while (code < end) { - MOTH_DISPATCH() - Q_UNREACHABLE(); - - COLLECTOR_BEGIN_INSTR(LoadReg) - COLLECTOR_END_INSTR(LoadReg) - - COLLECTOR_BEGIN_INSTR(StoreReg) - COLLECTOR_END_INSTR(StoreReg) - - COLLECTOR_BEGIN_INSTR(MoveReg) - COLLECTOR_END_INSTR(MoveReg) - - COLLECTOR_BEGIN_INSTR(LoadConst) - COLLECTOR_END_INSTR(LoadConst) - - COLLECTOR_BEGIN_INSTR(LoadNull) - COLLECTOR_END_INSTR(LoadNull) - - COLLECTOR_BEGIN_INSTR(LoadZero) - COLLECTOR_END_INSTR(LoadZero) - - COLLECTOR_BEGIN_INSTR(LoadTrue) - COLLECTOR_END_INSTR(LoadTrue) - - COLLECTOR_BEGIN_INSTR(LoadFalse) - COLLECTOR_END_INSTR(LoadFalse) - - COLLECTOR_BEGIN_INSTR(LoadUndefined) - COLLECTOR_END_INSTR(LoadUndefined) - - COLLECTOR_BEGIN_INSTR(LoadInt) - COLLECTOR_END_INSTR(LoadInt) - - COLLECTOR_BEGIN_INSTR(MoveConst) - COLLECTOR_END_INSTR(MoveConst) - - COLLECTOR_BEGIN_INSTR(LoadImport) - COLLECTOR_END_INSTR(LoadImport) - - COLLECTOR_BEGIN_INSTR(LoadLocal) - COLLECTOR_END_INSTR(LoadLocal) - - COLLECTOR_BEGIN_INSTR(StoreLocal) - COLLECTOR_END_INSTR(StoreLocal) - - COLLECTOR_BEGIN_INSTR(LoadScopedLocal) - COLLECTOR_END_INSTR(LoadScopedLocal) - - COLLECTOR_BEGIN_INSTR(StoreScopedLocal) - COLLECTOR_END_INSTR(StoreScopedLocal) - - COLLECTOR_BEGIN_INSTR(LoadRuntimeString) - COLLECTOR_END_INSTR(LoadRuntimeString) - - COLLECTOR_BEGIN_INSTR(MoveRegExp) - COLLECTOR_END_INSTR(MoveRegExp) - - COLLECTOR_BEGIN_INSTR(LoadClosure) - COLLECTOR_END_INSTR(LoadClosure) - - COLLECTOR_BEGIN_INSTR(LoadName) - COLLECTOR_END_INSTR(LoadName) - - COLLECTOR_BEGIN_INSTR(LoadGlobalLookup) - COLLECTOR_END_INSTR(LoadGlobalLookup) - - COLLECTOR_BEGIN_INSTR(LoadQmlContextPropertyLookup) - COLLECTOR_END_INSTR(LoadQmlContextPropertyLookup) - - COLLECTOR_BEGIN_INSTR(StoreNameSloppy) - COLLECTOR_END_INSTR(StoreNameSloppy) - - COLLECTOR_BEGIN_INSTR(StoreNameStrict) - COLLECTOR_END_INSTR(StoreNameStrict) - - COLLECTOR_BEGIN_INSTR(LoadElement) - COLLECTOR_END_INSTR(LoadElement) - - COLLECTOR_BEGIN_INSTR(StoreElement) - COLLECTOR_END_INSTR(StoreElement) - - COLLECTOR_BEGIN_INSTR(LoadProperty) - COLLECTOR_END_INSTR(LoadProperty) - - COLLECTOR_BEGIN_INSTR(GetLookup) - COLLECTOR_END_INSTR(GetLookup) - - COLLECTOR_BEGIN_INSTR(StoreProperty) - COLLECTOR_END_INSTR(StoreProperty) - - COLLECTOR_BEGIN_INSTR(SetLookup) - COLLECTOR_END_INSTR(SetLookup) - - COLLECTOR_BEGIN_INSTR(LoadSuperProperty) - COLLECTOR_END_INSTR(LoadSuperProperty) - - COLLECTOR_BEGIN_INSTR(StoreSuperProperty) - COLLECTOR_END_INSTR(StoreSuperProperty) - - COLLECTOR_BEGIN_INSTR(Yield) - COLLECTOR_END_INSTR(Yield) - - COLLECTOR_BEGIN_INSTR(YieldStar) - COLLECTOR_END_INSTR(YieldStar) - - COLLECTOR_BEGIN_INSTR(Resume) - COLLECTOR_END_INSTR(Resume) - - COLLECTOR_BEGIN_INSTR(CallValue) - COLLECTOR_END_INSTR(CallValue) - - COLLECTOR_BEGIN_INSTR(CallWithReceiver) - COLLECTOR_END_INSTR(CallWithReceiver) - - COLLECTOR_BEGIN_INSTR(CallProperty) - COLLECTOR_END_INSTR(CallProperty) - - COLLECTOR_BEGIN_INSTR(CallPropertyLookup) - COLLECTOR_END_INSTR(CallPropertyLookup) - - COLLECTOR_BEGIN_INSTR(CallElement) - COLLECTOR_END_INSTR(CallElement) - - COLLECTOR_BEGIN_INSTR(CallName) - COLLECTOR_END_INSTR(CallName) - - COLLECTOR_BEGIN_INSTR(CallPossiblyDirectEval) - COLLECTOR_END_INSTR(CallPossiblyDirectEval) - - COLLECTOR_BEGIN_INSTR(CallGlobalLookup) - COLLECTOR_END_INSTR(CallGlobalLookup) - - COLLECTOR_BEGIN_INSTR(CallQmlContextPropertyLookup) - COLLECTOR_END_INSTR(CallQmlContextPropertyLookup) - - COLLECTOR_BEGIN_INSTR(CallWithSpread) - COLLECTOR_END_INSTR(CallWithSpread) - - COLLECTOR_BEGIN_INSTR(Construct) - COLLECTOR_END_INSTR(Construct) - - COLLECTOR_BEGIN_INSTR(ConstructWithSpread) - COLLECTOR_END_INSTR(ConstructWithSpread) - - COLLECTOR_BEGIN_INSTR(SetUnwindHandler) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(SetUnwindHandler) - - COLLECTOR_BEGIN_INSTR(UnwindDispatch) - COLLECTOR_END_INSTR(UnwindDispatch) - - COLLECTOR_BEGIN_INSTR(UnwindToLabel) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(UnwindToLabel) - - COLLECTOR_BEGIN_INSTR(DeadTemporalZoneCheck) - COLLECTOR_END_INSTR(DeadTemporalZoneCheck) - - COLLECTOR_BEGIN_INSTR(ThrowException) - COLLECTOR_END_INSTR(ThrowException) - - COLLECTOR_BEGIN_INSTR(GetException) - COLLECTOR_END_INSTR(HasException) - - COLLECTOR_BEGIN_INSTR(SetException) - COLLECTOR_END_INSTR(SetExceptionFlag) - - COLLECTOR_BEGIN_INSTR(CreateCallContext) - COLLECTOR_END_INSTR(CreateCallContext) - - COLLECTOR_BEGIN_INSTR(PushCatchContext) - COLLECTOR_END_INSTR(PushCatchContext) - - COLLECTOR_BEGIN_INSTR(PushWithContext) - COLLECTOR_END_INSTR(PushWithContext) - - COLLECTOR_BEGIN_INSTR(PushBlockContext) - COLLECTOR_END_INSTR(PushBlockContext) - - COLLECTOR_BEGIN_INSTR(CloneBlockContext) - COLLECTOR_END_INSTR(CloneBlockContext) - - COLLECTOR_BEGIN_INSTR(PushScriptContext) - COLLECTOR_END_INSTR(PushScriptContext) - - COLLECTOR_BEGIN_INSTR(PopScriptContext) - COLLECTOR_END_INSTR(PopScriptContext) - - COLLECTOR_BEGIN_INSTR(PopContext) - COLLECTOR_END_INSTR(PopContext) - - COLLECTOR_BEGIN_INSTR(GetIterator) - COLLECTOR_END_INSTR(GetIterator) - - COLLECTOR_BEGIN_INSTR(IteratorNext) - COLLECTOR_END_INSTR(IteratorNext) - - COLLECTOR_BEGIN_INSTR(IteratorNextForYieldStar) - COLLECTOR_END_INSTR(IteratorNextForYieldStar) - - COLLECTOR_BEGIN_INSTR(IteratorClose) - COLLECTOR_END_INSTR(IteratorClose) - - COLLECTOR_BEGIN_INSTR(DestructureRestElement) - COLLECTOR_END_INSTR(DestructureRestElement) - - COLLECTOR_BEGIN_INSTR(DeleteProperty) - COLLECTOR_END_INSTR(DeleteProperty) - - COLLECTOR_BEGIN_INSTR(DeleteName) - COLLECTOR_END_INSTR(DeleteName) - - COLLECTOR_BEGIN_INSTR(TypeofName) - COLLECTOR_END_INSTR(TypeofName) - - COLLECTOR_BEGIN_INSTR(TypeofValue) - COLLECTOR_END_INSTR(TypeofValue) - - COLLECTOR_BEGIN_INSTR(DeclareVar) - COLLECTOR_END_INSTR(DeclareVar) - - COLLECTOR_BEGIN_INSTR(DefineArray) - COLLECTOR_END_INSTR(DefineArray) - - COLLECTOR_BEGIN_INSTR(DefineObjectLiteral) - COLLECTOR_END_INSTR(DefineObjectLiteral) - - COLLECTOR_BEGIN_INSTR(CreateClass) - COLLECTOR_END_INSTR(CreateClass) - - COLLECTOR_BEGIN_INSTR(CreateMappedArgumentsObject) - COLLECTOR_END_INSTR(CreateMappedArgumentsObject) - - COLLECTOR_BEGIN_INSTR(CreateUnmappedArgumentsObject) - COLLECTOR_END_INSTR(CreateUnmappedArgumentsObject) - - COLLECTOR_BEGIN_INSTR(CreateRestParameter) - COLLECTOR_END_INSTR(CreateRestParameter) - - COLLECTOR_BEGIN_INSTR(ConvertThisToObject) - COLLECTOR_END_INSTR(ConvertThisToObject) - - COLLECTOR_BEGIN_INSTR(LoadSuperConstructor) - COLLECTOR_END_INSTR(LoadSuperConstructor) - - COLLECTOR_BEGIN_INSTR(ToObject) - COLLECTOR_END_INSTR(ToObject) - - COLLECTOR_BEGIN_INSTR(Jump) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(Jump) - - COLLECTOR_BEGIN_INSTR(JumpTrue) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(JumpTrue) - - COLLECTOR_BEGIN_INSTR(JumpFalse) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(JumpFalse) - - COLLECTOR_BEGIN_INSTR(JumpNoException) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(JumpNoException) - - COLLECTOR_BEGIN_INSTR(JumpNotUndefined) - addLabel(code - start + offset); - COLLECTOR_END_INSTR(JumpNotUndefined) - - COLLECTOR_BEGIN_INSTR(CmpEqNull) - COLLECTOR_END_INSTR(CmpEqNull) - - COLLECTOR_BEGIN_INSTR(CmpNeNull) - COLLECTOR_END_INSTR(CmpNeNull) - - COLLECTOR_BEGIN_INSTR(CmpEqInt) - COLLECTOR_END_INSTR(CmpEq) - - COLLECTOR_BEGIN_INSTR(CmpNeInt) - COLLECTOR_END_INSTR(CmpNeInt) - - COLLECTOR_BEGIN_INSTR(CmpEq) - COLLECTOR_END_INSTR(CmpEq) - - COLLECTOR_BEGIN_INSTR(CmpNe) - COLLECTOR_END_INSTR(CmpNe) - - COLLECTOR_BEGIN_INSTR(CmpGt) - COLLECTOR_END_INSTR(CmpGt) - - COLLECTOR_BEGIN_INSTR(CmpGe) - COLLECTOR_END_INSTR(CmpGe) - - COLLECTOR_BEGIN_INSTR(CmpLt) - COLLECTOR_END_INSTR(CmpLt) - - COLLECTOR_BEGIN_INSTR(CmpLe) - COLLECTOR_END_INSTR(CmpLe) - - COLLECTOR_BEGIN_INSTR(CmpStrictEqual) - COLLECTOR_END_INSTR(CmpStrictEqual) - - COLLECTOR_BEGIN_INSTR(CmpStrictNotEqual) - COLLECTOR_END_INSTR(CmpStrictNotEqual) - - COLLECTOR_BEGIN_INSTR(CmpIn) - COLLECTOR_END_INSTR(CmpIn) - - COLLECTOR_BEGIN_INSTR(CmpInstanceOf) - COLLECTOR_END_INSTR(CmpInstanceOf) - - COLLECTOR_BEGIN_INSTR(UNot) - COLLECTOR_END_INSTR(UNot) - - COLLECTOR_BEGIN_INSTR(UPlus) - COLLECTOR_END_INSTR(UPlus) - - COLLECTOR_BEGIN_INSTR(UMinus) - COLLECTOR_END_INSTR(UMinus) - - COLLECTOR_BEGIN_INSTR(UCompl) - COLLECTOR_END_INSTR(UCompl) - - COLLECTOR_BEGIN_INSTR(Increment) - COLLECTOR_END_INSTR(PreIncrement) - - COLLECTOR_BEGIN_INSTR(Decrement) - COLLECTOR_END_INSTR(PreDecrement) - - COLLECTOR_BEGIN_INSTR(Add) - COLLECTOR_END_INSTR(Add) - - COLLECTOR_BEGIN_INSTR(BitAnd) - COLLECTOR_END_INSTR(BitAnd) - - COLLECTOR_BEGIN_INSTR(BitOr) - COLLECTOR_END_INSTR(BitOr) - - COLLECTOR_BEGIN_INSTR(BitXor) - COLLECTOR_END_INSTR(BitXor) - - COLLECTOR_BEGIN_INSTR(UShr) - COLLECTOR_END_INSTR(UShr) - - COLLECTOR_BEGIN_INSTR(Shr) - COLLECTOR_END_INSTR(Shr) - - COLLECTOR_BEGIN_INSTR(Shl) - COLLECTOR_END_INSTR(Shl) - - COLLECTOR_BEGIN_INSTR(BitAndConst) - COLLECTOR_END_INSTR(BitAndConst) - - COLLECTOR_BEGIN_INSTR(BitOrConst) - COLLECTOR_END_INSTR(BitOr) - - COLLECTOR_BEGIN_INSTR(BitXorConst) - COLLECTOR_END_INSTR(BitXor) - - COLLECTOR_BEGIN_INSTR(UShrConst) - COLLECTOR_END_INSTR(UShrConst) - - COLLECTOR_BEGIN_INSTR(ShrConst) - COLLECTOR_END_INSTR(ShrConst) - - COLLECTOR_BEGIN_INSTR(ShlConst) - COLLECTOR_END_INSTR(ShlConst) - - COLLECTOR_BEGIN_INSTR(Exp) - COLLECTOR_END_INSTR(Exp) - - COLLECTOR_BEGIN_INSTR(Mul) - COLLECTOR_END_INSTR(Mul) - - COLLECTOR_BEGIN_INSTR(Div) - COLLECTOR_END_INSTR(Div) - - COLLECTOR_BEGIN_INSTR(Mod) - COLLECTOR_END_INSTR(Mod) - - COLLECTOR_BEGIN_INSTR(Sub) - COLLECTOR_END_INSTR(Sub) - - COLLECTOR_BEGIN_INSTR(Ret) - COLLECTOR_END_INSTR(Ret) - -#ifndef QT_NO_QML_DEBUGGER - COLLECTOR_BEGIN_INSTR(Debug) - COLLECTOR_END_INSTR(Debug) -#endif // QT_NO_QML_DEBUGGER - - COLLECTOR_BEGIN_INSTR(InitializeBlockDeadTemporalZone) - COLLECTOR_END_INSTR(InitializeBlockDeadTemporalZone) - - COLLECTOR_BEGIN_INSTR(ThrowOnNullOrUndefined) - COLLECTOR_END_INSTR(ThrowOnNullOrUndefined) - - COLLECTOR_BEGIN_INSTR(GetTemplateObject) - COLLECTOR_END_INSTR(GetTemplateObject) - - COLLECTOR_BEGIN_INSTR(TailCall) - COLLECTOR_END_INSTR(TailCall) - } - - return labels; -} - -#undef COLLECTOR_BEGIN_INSTR -#undef COLLECTOR_END_INSTR diff --git a/src/qml/compiler/qv4bytecodehandler_p.h b/src/qml/compiler/qv4bytecodehandler_p.h index ca6abf3dc3..797d25b8d0 100644 --- a/src/qml/compiler/qv4bytecodehandler_p.h +++ b/src/qml/compiler/qv4bytecodehandler_p.h @@ -75,6 +75,12 @@ namespace Moth { int arg2, \ int arg3, \ int arg4 +#define BYTECODE_HANDLER_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \ + int arg1, \ + int arg2, \ + int arg3, \ + int arg4, \ + int arg5 #define BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER_INSTRUCTION(name, nargs, ...) \ virtual void generate_##name( \ @@ -93,8 +99,8 @@ public: int currentInstructionOffset() const { return _currentOffset; } int nextInstructionOffset() const { return _nextOffset; } - - static std::vector<int> collectLabelsInBytecode(const char *code, uint len); + int absoluteOffset(int relativeOffset) const + { return nextInstructionOffset() + relativeOffset; } protected: FOR_EACH_MOTH_INSTR(BYTECODE_HANDLER_DEFINE_VIRTUAL_BYTECODE_HANDLER) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3fdba08f20..e0d259bd0c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -288,8 +288,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) switch (op) { case UMinus: { expr.loadInAccumulator(); - Instruction::UMinus uminus; - bytecodeGenerator->addInstruction(uminus); + Instruction::UMinus uminus = {}; + bytecodeGenerator->addTracingInstruction(uminus); return Reference::fromAccumulator(this); } case UPlus: { @@ -317,8 +317,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Instruction::UPlus uplus; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); e.storeConsumeAccumulator(); return originalValue; } else { @@ -329,8 +329,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case PreIncrement: { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -343,8 +343,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) Instruction::UPlus uplus; bytecodeGenerator->addInstruction(uplus); Reference originalValue = Reference::fromStackSlot(this).storeRetainAccumulator(); - Instruction::Decrement dec; - bytecodeGenerator->addInstruction(dec); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); e.storeConsumeAccumulator(); return originalValue; } else { @@ -355,8 +355,8 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr) case PreDecrement: { Reference e = expr.asLValue(); e.loadInAccumulator(); - Instruction::Decrement dec; - bytecodeGenerator->addInstruction(dec); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); if (exprAccept(nx)) return e.storeConsumeAccumulator(); else @@ -1138,8 +1138,8 @@ bool Codegen::visit(ArrayPattern *ast) slot.storeConsumeAccumulator(); index.loadInAccumulator(); - Instruction::Increment inc; - bytecodeGenerator->addInstruction(inc); + Instruction::Increment inc = {}; + bytecodeGenerator->addTracingInstruction(inc); index.storeConsumeAccumulator(); }; @@ -1195,7 +1195,7 @@ bool Codegen::visit(ArrayPattern *ast) next.value = lhsValue.stackSlot(); next.done = iteratorDone.stackSlot(); bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpTrue()).link(end); + bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); lhsValue.loadInAccumulator(); pushAccumulator(); @@ -1482,24 +1482,24 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re { switch (oper) { case QSOperator::Add: { - //### Todo: when we add type hints, we can generate an Increment when both the lhs is a number and the rhs == 1 left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Add add; add.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(add); + bytecodeGenerator->addTracingInstruction(add); break; } case QSOperator::Sub: { if (right.isConstant() && right.constant == Encode(int(1))) { left.loadInAccumulator(); - bytecodeGenerator->addInstruction(Instruction::Decrement()); + Instruction::Decrement dec = {}; + bytecodeGenerator->addTracingInstruction(dec); } else { left = left.storeOnStack(); right.loadInAccumulator(); Instruction::Sub sub; sub.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(sub); + bytecodeGenerator->addTracingInstruction(sub); } break; } @@ -1516,7 +1516,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mul mul; mul.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(mul); + bytecodeGenerator->addTracingInstruction(mul); break; } case QSOperator::Div: { @@ -1532,7 +1532,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re right.loadInAccumulator(); Instruction::Mod mod; mod.lhs = left.stackSlot(); - bytecodeGenerator->addInstruction(mod); + bytecodeGenerator->addTracingInstruction(mod); break; } case QSOperator::BitAnd: @@ -1901,7 +1901,7 @@ bool Codegen::visit(CallExpression *ast) call.thisObject = baseObject.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::TailCall call; call.func = base.stackSlot(); @@ -1930,14 +1930,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.lookupIndex = registerGetterLookup(base.propertyNameIndex); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::CallProperty call; call.base = base.propertyBase.stackSlot(); call.name = base.propertyNameIndex; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } } else if (base.type == Reference::Subscript) { Instruction::CallElement call; @@ -1945,33 +1945,33 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.index = base.elementSubscript.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (base.type == Reference::Name) { if (base.name == QStringLiteral("eval")) { Instruction::CallPossiblyDirectEval call; call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { if (base.qmlGlobal) { Instruction::CallQmlContextPropertyLookup call; call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Instruction::CallGlobalLookup call; call.index = registerGlobalGetterLookup(base.nameAsIndex()); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } } else { Instruction::CallName call; call.name = base.nameAsIndex(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } } else if (base.type == Reference::SuperProperty) { Reference receiver = base.baseObject(); @@ -1988,14 +1988,14 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.thisObject = receiver.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } else { Q_ASSERT(base.isStackSlot()); Instruction::CallValue call; call.name = base.stackSlot(); call.argc = calldata.argc; call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + bytecodeGenerator->addTracingInstruction(call); } setExprResult(Reference::fromAccumulator(this)); @@ -2490,9 +2490,6 @@ bool Codegen::visit(ObjectPattern *ast) TailCallBlocker blockTailCalls(this); - QVector<QPair<Reference, ObjectPropertyValue>> computedProperties; - QMap<QString, ObjectPropertyValue> valueMap; - RegisterScope scope(this); QStringList members; @@ -2734,14 +2731,14 @@ bool Codegen::visit(TemplateLiteral *ast) Instruction::Add instr; instr.lhs = temp2; - bytecodeGenerator->addInstruction(instr); + bytecodeGenerator->addTracingInstruction(instr); } else { expr.loadInAccumulator(); } Instruction::Add instr; instr.lhs = temp; - bytecodeGenerator->addInstruction(instr); + bytecodeGenerator->addTracingInstruction(instr); } auto r = Reference::fromAccumulator(this); @@ -2999,6 +2996,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bool savedFunctionEndsWithReturn = functionEndsWithReturn; functionEndsWithReturn = endsWithReturn(_module, body); + bytecodeGenerator->setTracing(_functionContext->canUseTracingJit(), _context->arguments.size()); // reserve the js stack frame (Context & js Function & accumulator) bytecodeGenerator->newRegisterArray(sizeof(CallData)/sizeof(Value) - 1 + _context->arguments.size()); @@ -3085,6 +3083,7 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, Q_ASSERT(_context == _functionContext); bytecodeGenerator->finalize(_context); _context->registerCountInFunction = bytecodeGenerator->registerCount(); + _context->nTraceInfos = bytecodeGenerator->traceInfoCount(); static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_BYTECODE"); if (showCode) { qDebug() << "=== Bytecode for" << _context->name << "strict mode" << _context->isStrict @@ -3185,25 +3184,28 @@ bool Codegen::visit(DoWhileStatement *ast) RegisterScope scope(this); - BytecodeGenerator::Label body = bytecodeGenerator->label(); + BytecodeGenerator::Label body = bytecodeGenerator->newLabel(); BytecodeGenerator::Label cond = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); ControlFlowLoop flow(this, &end, &cond); - - statement(ast->statement); - setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); + bytecodeGenerator->jump().link(body); cond.link(); + bytecodeGenerator->addLoopStart(cond); - TailCallBlocker blockTailCalls(this); - if (!AST::cast<FalseLiteral *>(ast->expression)) { - if (AST::cast<TrueLiteral *>(ast->expression)) - bytecodeGenerator->jump().link(body); - else - condition(ast->expression, &body, &end, false); + if (!AST::cast<TrueLiteral *>(ast->expression)) { + TailCallBlocker blockTailCalls(this); + condition(ast->expression, &body, &end, true); } + body.link(); + statement(ast->statement); + setJumpOutLocation(bytecodeGenerator, ast->statement, ast->semicolonToken); + + if (!AST::cast<FalseLiteral *>(ast->expression)) + bytecodeGenerator->jump().link(cond); + end.link(); return false; @@ -3274,9 +3276,14 @@ bool Codegen::visit(ForEachStatement *ast) } }; ControlFlowLoop flow(this, &end, &in, cleanup); - bytecodeGenerator->jump().link(in); - - BytecodeGenerator::Label body = bytecodeGenerator->label(); + bytecodeGenerator->addLoopStart(in); + in.link(); + iterator.loadInAccumulator(); + Instruction::IteratorNext next; + next.value = lhsValue.stackSlot(); + next.done = iteratorDone.stackSlot(); + bytecodeGenerator->addInstruction(next); + bytecodeGenerator->addTracingJumpInstruction(Instruction::JumpTrue()).link(end); // each iteration gets it's own context, as per spec { @@ -3307,20 +3314,16 @@ bool Codegen::visit(ForEachStatement *ast) Q_UNREACHABLE(); } + blockTailCalls.unblock(); statement(ast->statement); setJumpOutLocation(bytecodeGenerator, ast->statement, ast->forToken); - } + bytecodeGenerator->jump().link(in); + error: - in.link(); - iterator.loadInAccumulator(); - Instruction::IteratorNext next; - next.value = lhsValue.stackSlot(); - next.done = iteratorDone.stackSlot(); - bytecodeGenerator->addInstruction(next); - bytecodeGenerator->addJumpInstruction(Instruction::JumpFalse()).link(body); end.link(); + // all execution paths need to end up here (normal loop exit, break, and exceptions) in // order to reset the unwind handler, and to close the iterator in calse of an for-of loop. } @@ -3349,7 +3352,7 @@ bool Codegen::visit(ForStatement *ast) BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); ControlFlowLoop flow(this, &end, &step); - + bytecodeGenerator->addLoopStart(cond); condition(ast->condition, &body, &end, true); body.link(); @@ -3639,16 +3642,17 @@ bool Codegen::visit(WhileStatement *ast) return false; RegisterScope scope(this); - TailCallBlocker blockTailCalls(this); BytecodeGenerator::Label start = bytecodeGenerator->newLabel(); BytecodeGenerator::Label end = bytecodeGenerator->newLabel(); BytecodeGenerator::Label cond = bytecodeGenerator->label(); ControlFlowLoop flow(this, &end, &cond); + bytecodeGenerator->addLoopStart(cond); - if (!AST::cast<TrueLiteral *>(ast->expression)) + if (!AST::cast<TrueLiteral *>(ast->expression)) { + TailCallBlocker blockTailCalls(this); condition(ast->expression, &start, &end, true); - blockTailCalls.unblock(); + } start.link(); statement(ast->statement); @@ -4216,7 +4220,7 @@ void Codegen::Reference::storeAccumulator() const Instruction::StoreElement store; store.base = elementBase; store.index = elementSubscript.stackSlot(); - codegen->bytecodeGenerator->addInstruction(store); + codegen->bytecodeGenerator->addTracingInstruction(store); } return; case Invalid: case Accumulator: @@ -4306,12 +4310,12 @@ QT_WARNING_POP if (!scope) { Instruction::LoadLocal load; load.index = index; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadScopedLocal load; load.index = index; load.scope = scope; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } tdzCheck(requiresTDZCheck); return; @@ -4335,16 +4339,16 @@ QT_WARNING_POP if (qmlGlobal) { Instruction::LoadQmlContextPropertyLookup load; load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadGlobalLookup load; load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } } else { Instruction::LoadName load; load.name = nameAsIndex(); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case Member: @@ -4353,11 +4357,11 @@ QT_WARNING_POP if (!disable_lookups && codegen->useFastLookups) { Instruction::GetLookup load; load.index = codegen->registerGetterLookup(propertyNameIndex); - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } else { Instruction::LoadProperty load; load.name = propertyNameIndex; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case Import: { @@ -4372,7 +4376,7 @@ QT_WARNING_POP tdzCheck(subscriptRequiresTDZCheck); Instruction::LoadElement load; load.base = elementBase; - codegen->bytecodeGenerator->addInstruction(load); + codegen->bytecodeGenerator->addTracingInstruction(load); } return; case Invalid: break; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 8f2f4a11c1..e6d079fe36 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -200,7 +200,7 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeFunctions.resize(data->functionTableSize); for (int i = 0 ;i < runtimeFunctions.size(); ++i) { const QV4::CompiledData::Function *compiledFunction = data->functionAt(i); - runtimeFunctions[i] = new QV4::Function(engine, this, compiledFunction); + runtimeFunctions[i] = QV4::Function::create(engine, this, compiledFunction); } Scope scope(engine); @@ -317,7 +317,8 @@ void CompilationUnit::unlink() runtimeRegularExpressions = nullptr; free(runtimeClasses); runtimeClasses = nullptr; - qDeleteAll(runtimeFunctions); + for (QV4::Function *f : qAsConst(runtimeFunctions)) + f->destroy(); runtimeFunctions.clear(); } @@ -777,6 +778,8 @@ QString Binding::valueAsString(const CompilationUnit *unit) const case Type_Script: case Type_String: return unit->stringAt(stringIndex); + case Type_Null: + return QStringLiteral("null"); case Type_Boolean: return value.b ? QStringLiteral("true") : QStringLiteral("false"); case Type_Number: diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 3fd9fdf74b..23e33247a8 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x20 +#define QV4_DATA_STRUCTURE_VERSION 0x21 class QIODevice; class QQmlPropertyCache; @@ -288,10 +288,16 @@ struct Function quint16_le nRegisters; Location location; + quint32_le nLabelInfos; + size_t labelInfosOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } + + typedef quint16_le TraceInfoCount; + TraceInfoCount nTraceInfos; + static constexpr TraceInfoCount NoTracing() { return TraceInfoCount::max(); } + // Keep all unaligned data at the end quint8 flags; quint8 padding1; - quint16 padding2; // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] @@ -305,10 +311,13 @@ struct Function const quint32_le *formalsEnd() const { return formalsTable() + nFormals; } // --- + const quint32_le *labelInfoTable() const { return reinterpret_cast<const quint32_le *>(reinterpret_cast<const char *>(this) + labelInfosOffset()); } + const char *code() const { return reinterpret_cast<const char *>(this) + codeOffset; } - static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int codeSize) { - int trailingData = (nFormals + nLocals + nInnerfunctions)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); + static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int labelInfoSize, int codeSize) { + int trailingData = (nFormals + nLocals + nInnerfunctions + labelInfoSize)*sizeof (quint32) + + nLines*sizeof(CodeOffsetToLine); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); return int(size); @@ -318,7 +327,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 48, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { @@ -424,6 +433,7 @@ struct Q_QML_PRIVATE_EXPORT Binding Type_Boolean, Type_Number, Type_String, + Type_Null, Type_Translation, Type_TranslationById, Type_Script, @@ -455,6 +465,7 @@ struct Q_QML_PRIVATE_EXPORT Binding quint32_le compiledScriptIndex; // used when Type_Script quint32_le objectIndex; quint32_le translationDataIndex; // used when Type_Translation + quint32 nullMarker; } value; quint32_le stringIndex; // Set for Type_String and Type_Script (the latter because of script strings) diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 4d85f25d4b..01c033cb2a 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -424,9 +424,15 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte Q_ASSERT(function->lineNumberOffset() == currentOffset); currentOffset += function->nLineNumbers * sizeof(CompiledData::CodeOffsetToLine); - + function->nTraceInfos = irFunction->nTraceInfos; function->nRegisters = irFunction->registerCountInFunction; + if (!irFunction->labelInfo.empty()) { + function->nLabelInfos = quint32(irFunction->labelInfo.size()); + Q_ASSERT(function->labelInfosOffset() == currentOffset); + currentOffset += function->nLabelInfos * sizeof(quint32); + } + function->location.line = irFunction->line; function->location.column = irFunction->column; @@ -446,6 +452,11 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte // write line numbers memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); + quint32_le *labels = (quint32_le *)(f + function->labelInfosOffset()); + for (unsigned u : irFunction->labelInfo) { + *labels++ = u; + } + // write byte code memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } @@ -643,7 +654,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp blockAndFunctionOffsets[i] = nextOffset; quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), - f->code.size()); + int(f->labelInfo.size()), f->code.size()); functionSize += size - f->code.size(); nextOffset += size; } diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index d1a5fee92b..b9ab4e5173 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -410,4 +410,28 @@ void Context::setupFunctionIndices(Moth::BytecodeGenerator *bytecodeGenerator) nRegisters = bytecodeGenerator->currentRegister() - registerOffset; } +bool Context::canUseTracingJit() const +{ +#if QT_CONFIG(qml_tracing) + static bool forceTracing = !qEnvironmentVariableIsEmpty("QV4_FORCE_TRACING"); + if (forceTracing) //### we can probably remove this when tracing is turned on by default + return true; // to be used by unittests + + static bool disableTracing = !qEnvironmentVariableIsEmpty("QV4_DISABLE_TRACING"); + if (disableTracing) + return false; + + static QStringList onlyTrace = + qEnvironmentVariable("QV4_ONLY_TRACE").split(QLatin1Char(','), QString::SkipEmptyParts); + if (!onlyTrace.isEmpty()) + return onlyTrace.contains(name); + + //### the next condition should be refined and have the IR distinguish between escaping and + // non-escaping locals + return !hasTry && !requiresExecutionContext && !hasNestedFunctions; +#else + return false; +#endif +} + QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 8f3b60e395..cb20ea9d57 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -162,6 +162,7 @@ struct Context { int line = 0; int column = 0; int registerCountInFunction = 0; + uint nTraceInfos = 0; int functionIndex = -1; int blockIndex = -1; @@ -200,6 +201,7 @@ struct Context { ControlFlow *controlFlow = nullptr; QByteArray code; QVector<CompiledData::CodeOffsetToLine> lineNumberMapping; + std::vector<unsigned> labelInfo; int nRegisters = 0; int registerOffset = -1; @@ -362,6 +364,8 @@ struct Context { return parent->canHaveTailCalls(); return false; } + + bool canUseTracingJit() const; }; diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index c6c8caffba..345f03ae8a 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -156,7 +156,7 @@ QString dumpRegister(int reg, int nFormals) return QStringLiteral("(this)"); else if (reg == CallData::Argc) return QStringLiteral("(argc)"); - reg -= CallData::OffsetCount; + reg -= CallData::HeaderSize(); if (reg < nFormals) return QStringLiteral("a%1").arg(reg); reg -= nFormals; @@ -171,6 +171,7 @@ QString dumpArguments(int argc, int argv, int nFormals) return QStringLiteral("(") + dumpRegister(argv, nFormals) + QStringLiteral(", ") + QString::number(argc) + QStringLiteral(")"); } +#define TRACE_SLOT QStringLiteral(" {%1}").arg(traceSlot) void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*startLine*/, const QVector<CompiledData::CodeOffsetToLine> &lineNumberMapping) { @@ -240,9 +241,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadLocal) if (index < nLocals) - d << "l" << index; + d << "l" << index << TRACE_SLOT; else - d << "a" << (index - nLocals); + d << "a" << (index - nLocals) << TRACE_SLOT; MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -254,9 +255,9 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_BEGIN_INSTR(LoadScopedLocal) if (index < nLocals) - d << "l" << index << "@" << scope; + d << "l" << index << "@" << scope << TRACE_SLOT; else - d << "a" << (index - nLocals) << "@" << scope; + d << "a" << (index - nLocals) << "@" << scope << TRACE_SLOT; MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -279,15 +280,15 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(LoadClosure) MOTH_BEGIN_INSTR(LoadName) - d << name; + d << name << TRACE_SLOT; MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) - d << index; + d << index << TRACE_SLOT; MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) - d << index; + d << index << TRACE_SLOT; MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameSloppy) @@ -299,19 +300,20 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(StoreNameStrict) MOTH_BEGIN_INSTR(LoadElement) - d << dumpRegister(base, nFormals) << "[acc]"; + d << dumpRegister(base, nFormals) << "[acc]" << TRACE_SLOT; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]"; + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" + << TRACE_SLOT; MOTH_END_INSTR(StoreElement) MOTH_BEGIN_INSTR(LoadProperty) - d << "acc[" << name << "]"; + d << "acc[" << name << "]" << TRACE_SLOT; MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) - d << "acc(" << index << ")"; + d << "acc(" << index << ")" << TRACE_SLOT; MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -341,51 +343,57 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Resume) MOTH_BEGIN_INSTR(CallValue) - d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals); + d << dumpRegister(name, nFormals) << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) - d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + d << dumpRegister(name, nFormals) << dumpRegister(thisObject, nFormals) + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals) + << TRACE_SLOT; MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) - d << dumpRegister(base, nFormals) << "." << lookupIndex << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "." << lookupIndex + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) - d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" << dumpArguments(argc, argv, nFormals); + d << dumpRegister(base, nFormals) << "[" << dumpRegister(index, nFormals) << "]" + << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) - d << name << dumpArguments(argc, argv, nFormals); + d << name << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) - d << dumpArguments(argc, argv, nFormals); + d << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) - d << index << dumpArguments(argc, argv, nFormals); + d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) - d << index << dumpArguments(argc, argv, nFormals); + d << index << dumpArguments(argc, argv, nFormals) << TRACE_SLOT; MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) - d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) + << dumpArguments(argc, argv, nFormals) + << TRACE_SLOT; MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(Construct) - d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(Construct) MOTH_BEGIN_INSTR(ConstructWithSpread) - d << "new" << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); + d << "new " << dumpRegister(func, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(ConstructWithSpread) MOTH_BEGIN_INSTR(SetUnwindHandler) @@ -520,11 +528,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - d << ABSOLUTE_OFFSET(); + d << ABSOLUTE_OFFSET() << TRACE_SLOT; MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - d << ABSOLUTE_OFFSET(); + d << ABSOLUTE_OFFSET() << TRACE_SLOT; MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNotUndefined) @@ -588,19 +596,22 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(UPlus) MOTH_BEGIN_INSTR(UMinus) + d << TRACE_SLOT; MOTH_END_INSTR(UMinus) MOTH_BEGIN_INSTR(UCompl) MOTH_END_INSTR(UCompl) MOTH_BEGIN_INSTR(Increment) - MOTH_END_INSTR(PreIncrement) + d << TRACE_SLOT; + MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) - MOTH_END_INSTR(PreDecrement) + d << TRACE_SLOT; + MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(BitAnd) @@ -656,7 +667,7 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Exp) MOTH_BEGIN_INSTR(Mul) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Mul) MOTH_BEGIN_INSTR(Div) @@ -664,11 +675,11 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st MOTH_END_INSTR(Div) MOTH_BEGIN_INSTR(Mod) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(Sub) - d << dumpRegister(lhs, nFormals) << ", acc"; + d << dumpRegister(lhs, nFormals) << ", acc" << TRACE_SLOT; MOTH_END_INSTR(Sub) MOTH_BEGIN_INSTR(CmpIn) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 25fca3c0b0..26901c297c 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -77,20 +77,20 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreReg(op) INSTRUCTION(op, StoreReg, 1, reg) #define INSTR_MoveReg(op) INSTRUCTION(op, MoveReg, 2, srcReg, destReg) #define INSTR_LoadImport(op) INSTRUCTION(op, LoadImport, 1, index) -#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 1, index) +#define INSTR_LoadLocal(op) INSTRUCTION(op, LoadLocal, 2, index, traceSlot) #define INSTR_StoreLocal(op) INSTRUCTION(op, StoreLocal, 1, index) -#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 2, scope, index) +#define INSTR_LoadScopedLocal(op) INSTRUCTION(op, LoadScopedLocal, 3, scope, index, traceSlot) #define INSTR_StoreScopedLocal(op) INSTRUCTION(op, StoreScopedLocal, 2, scope, index) #define INSTR_LoadRuntimeString(op) INSTRUCTION(op, LoadRuntimeString, 1, stringId) #define INSTR_MoveRegExp(op) INSTRUCTION(op, MoveRegExp, 2, regExpId, destReg) #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) -#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) -#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) -#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) +#define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 2, name, traceSlot) +#define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 2, index, traceSlot) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 2, index, traceSlot) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) -#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) -#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) +#define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 2, name, traceSlot) +#define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 2, index, traceSlot) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -100,18 +100,18 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) -#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) -#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) -#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 4, name, thisObject, argc, argv) -#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 4, name, base, argc, argv) -#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 4, lookupIndex, base, argc, argv) -#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 4, base, index, argc, argv) -#define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) -#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) -#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) -#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) -#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) +#define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 2, base, traceSlot) +#define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 3, base, index, traceSlot) +#define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 4, name, argc, argv, traceSlot) +#define INSTR_CallWithReceiver(op) INSTRUCTION(op, CallWithReceiver, 5, name, thisObject, argc, argv, traceSlot) +#define INSTR_CallProperty(op) INSTRUCTION(op, CallProperty, 5, name, base, argc, argv, traceSlot) +#define INSTR_CallPropertyLookup(op) INSTRUCTION(op, CallPropertyLookup, 5, lookupIndex, base, argc, argv, traceSlot) +#define INSTR_CallElement(op) INSTRUCTION(op, CallElement, 5, base, index, argc, argv, traceSlot) +#define INSTR_CallName(op) INSTRUCTION(op, CallName, 4, name, argc, argv, traceSlot) +#define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 3, argc, argv, traceSlot) +#define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 4, index, argc, argv, traceSlot) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 4, index, argc, argv, traceSlot) +#define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 5, func, thisObject, argc, argv, traceSlot) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) #define INSTR_SetUnwindHandler(op) INSTRUCTION(op, SetUnwindHandler, 1, offset) @@ -148,8 +148,8 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadSuperConstructor(op) INSTRUCTION(op, LoadSuperConstructor, 0) #define INSTR_ToObject(op) INSTRUCTION(op, ToObject, 0) #define INSTR_Jump(op) INSTRUCTION(op, Jump, 1, offset) -#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 1, offset) -#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 1, offset) +#define INSTR_JumpTrue(op) INSTRUCTION(op, JumpTrue, 2, traceSlot, offset) +#define INSTR_JumpFalse(op) INSTRUCTION(op, JumpFalse, 2, traceSlot, offset) #define INSTR_JumpNotUndefined(op) INSTRUCTION(op, JumpNotUndefined, 1, offset) #define INSTR_JumpNoException(op) INSTRUCTION(op, JumpNoException, 1, offset) #define INSTR_CmpEqNull(op) INSTRUCTION(op, CmpEqNull, 0) @@ -168,11 +168,11 @@ QT_BEGIN_NAMESPACE #define INSTR_CmpInstanceOf(op) INSTRUCTION(op, CmpInstanceOf, 1, lhs) #define INSTR_UNot(op) INSTRUCTION(op, UNot, 0) #define INSTR_UPlus(op) INSTRUCTION(op, UPlus, 0) -#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 0) +#define INSTR_UMinus(op) INSTRUCTION(op, UMinus, 1, traceSlot) #define INSTR_UCompl(op) INSTRUCTION(op, UCompl, 0) -#define INSTR_Increment(op) INSTRUCTION(op, Increment, 0) -#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 0) -#define INSTR_Add(op) INSTRUCTION(op, Add, 1, lhs) +#define INSTR_Increment(op) INSTRUCTION(op, Increment, 1, traceSlot) +#define INSTR_Decrement(op) INSTRUCTION(op, Decrement, 1, traceSlot) +#define INSTR_Add(op) INSTRUCTION(op, Add, 2, lhs, traceSlot) #define INSTR_BitAnd(op) INSTRUCTION(op, BitAnd, 1, lhs) #define INSTR_BitOr(op) INSTRUCTION(op, BitOr, 1, lhs) #define INSTR_BitXor(op) INSTRUCTION(op, BitXor, 1, lhs) @@ -186,10 +186,10 @@ QT_BEGIN_NAMESPACE #define INSTR_ShrConst(op) INSTRUCTION(op, ShrConst, 1, rhs) #define INSTR_ShlConst(op) INSTRUCTION(op, ShlConst, 1, rhs) #define INSTR_Exp(op) INSTRUCTION(op, Exp, 1, lhs) -#define INSTR_Mul(op) INSTRUCTION(op, Mul, 1, lhs) +#define INSTR_Mul(op) INSTRUCTION(op, Mul, 2, lhs, traceSlot) #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) -#define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) -#define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) +#define INSTR_Mod(op) INSTRUCTION(op, Mod, 2, lhs, traceSlot) +#define INSTR_Sub(op) INSTRUCTION(op, Sub, 2, lhs, traceSlot) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -334,12 +334,18 @@ QT_BEGIN_NAMESPACE #define MOTH_NUM_INSTRUCTIONS() (static_cast<int>(Moth::Instr::Type::Debug_Wide) + 1) -#if defined(Q_CC_GNU) && !defined(Q_CC_INTEL) +#if defined(Q_CC_GNU) +#if defined(Q_CC_INTEL) // icc before version 1200 doesn't support computed goto, and at least up to version 18.0.0 the // current use results in an internal compiler error. We could enable this if/when it gets fixed // in a later version. +# elif defined(Q_OS_WASM) && !defined(__asmjs) +// Upstream llvm does not support computed goto for the wasm target, unlike the 'fastcomp' llvm fork +// shipped with the emscripten SDK. Disable computed goto usage for non-fastcomp llvm on Wasm. +#else # define MOTH_COMPUTED_GOTO #endif +#endif #define MOTH_INSTR_ALIGN_MASK (Q_ALIGNOF(QV4::Moth::Instr) - 1) @@ -365,6 +371,12 @@ QT_BEGIN_NAMESPACE int arg2; \ int arg3; \ int arg4; +#define MOTH_DEFINE_ARGS5(arg1, arg2, arg3, arg4, arg5) \ + int arg1; \ + int arg2; \ + int arg3; \ + int arg4; \ + int arg5; #define MOTH_COLLECT_ENUMS(instr) \ INSTR_##instr(MOTH_GET_ENUM) @@ -435,6 +447,9 @@ QT_BEGIN_NAMESPACE #define MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4) \ MOTH_DECODE_ARGS3(name, type, nargs, arg1, arg2, arg3); \ MOTH_DECODE_ARG(arg4, type, nargs, 3); +#define MOTH_DECODE_ARGS5(name, type, nargs, arg1, arg2, arg3, arg4, arg5) \ + MOTH_DECODE_ARGS4(name, type, nargs, arg1, arg2, arg3, arg4); \ + MOTH_DECODE_ARG(arg5, type, nargs, 4); #ifdef MOTH_COMPUTED_GOTO /* collect jump labels */ diff --git a/src/qml/configure.json b/src/qml/configure.json index 17d1f800ac..c35f5be06b 100644 --- a/src/qml/configure.json +++ b/src/qml/configure.json @@ -8,6 +8,7 @@ "commandline": { "options": { "qml-network": "boolean", + "qml-tracing": "boolean", "qml-debug": "boolean" } }, @@ -39,6 +40,13 @@ "condition": "features.network", "output": [ "publicFeature" ] }, + "qml-tracing": { + "label": "QML tracing JIT support", + "purpose": "Provides a JIT that uses trace information generated by the interpreter.", + "section": "QML", + "output": [ "privateFeature" ], + "autoDetect": false + }, "qml-debug": { "label": "QML debugging and profiling support", "purpose": "Provides infrastructure and plugins for debugging and profiling.", @@ -132,6 +140,7 @@ "entries": [ "qml-network", "qml-debug", + "qml-tracing", "qml-sequence-object", "qml-list-model", "qml-xml-http-request", diff --git a/src/qml/debugger/qqmldebug.cpp b/src/qml/debugger/qqmldebug.cpp index 6246fb4207..6532576e03 100644 --- a/src/qml/debugger/qqmldebug.cpp +++ b/src/qml/debugger/qqmldebug.cpp @@ -44,16 +44,16 @@ #include <private/qqmlengine_p.h> #include <private/qv4compileddata_p.h> +#include <cstdio> + QT_REQUIRE_CONFIG(qml_debug); QT_BEGIN_NAMESPACE QQmlDebuggingEnabler::QQmlDebuggingEnabler(bool printWarning) { - if (!QQmlEnginePrivate::qml_debugging_enabled - && printWarning) { - qDebug("QML debugging is enabled. Only use this in a safe environment."); - } + if (!QQmlEnginePrivate::qml_debugging_enabled && printWarning) + fprintf(stderr, "QML debugging is enabled. Only use this in a safe environment.\n"); QQmlEnginePrivate::qml_debugging_enabled = true; } diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml b/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml index 5339fcddce..e2104f8740 100644 --- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml +++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/app.qml @@ -49,7 +49,7 @@ ****************************************************************************/ //![0] import QtQuick 2.0 -import "script.js" as MyScript +import "script.mjs" as MyScript Item { width: 100; height: 100 diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/factorial.js b/src/qml/doc/snippets/qml/integrating-javascript/includejs/factorial.mjs index f2e31265e3..d0a09b68ad 100644 --- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/factorial.js +++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/factorial.mjs @@ -48,8 +48,8 @@ ** ****************************************************************************/ //![0] -// factorial.js -function factorial(a) { +// factorial.mjs +export function factorial(a) { a = parseInt(a); if (a <= 0) return 1; diff --git a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.js b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs index c44b39abda..ef7688693d 100644 --- a/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.js +++ b/src/qml/doc/snippets/qml/integrating-javascript/includejs/script.mjs @@ -49,7 +49,7 @@ ****************************************************************************/ //![0] // script.js -Qt.include("factorial.js") +import { factorial } from "factorial.mjs" function showCalculations(value) { console.log( diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 8ebbd28737..171b2b6a11 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -284,6 +284,9 @@ In particular, QML currently supports: \li \c {std::vector<bool>} \endlist +and all registered QList, QVector, QQueue, QStack, QSet, QLinkedList, std::list, +std::vector that contain a type marked with \l Q_DECLARE_METATYPE. + These sequence types are implemented directly in terms of the underlying C++ sequence. There are two ways in which such sequences can be exposed to QML: as a Q_PROPERTY of the given sequence type; or as the return type of a diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc index 7da2cd22fe..974f2e154f 100644 --- a/src/qml/doc/src/javascript/imports.qdoc +++ b/src/qml/doc/src/javascript/imports.qdoc @@ -99,9 +99,44 @@ A JavaScript resource may import another in the following fashion: \endcode For example: \code -.import "factorial.js" as MathFunctions +import * as MathFunctions from "factorial.mjs"; \endcode +The latter is standard ECMAScript syntax for importing ECMAScript modules, and +only works from within ECMAScript modules as denoted by the \c mjs file +extension. The former is an extension to JavaScript provided by the \c QML +engine and will work also with non-modules. + +When a JavaScript file is imported this way, it is imported with a qualifier. +The functions in that file are then accessible from the importing script via the +qualifier (that is, as \tt{Qualifier.functionName(params)}). + +Sometimes it is desirable to have the functions made available in the importing +context without needing to qualify them. In this case ECMAScript modules and the +JavaScript \c import statement should be used without the \c as qualifier. + +For example, the QML code below left calls \c showCalculations() in \c script.mjs, +which in turn can call \c factorial() in \c factorial.mjs, as it has included +\c factorial.mjs using \c import. + +\table +\row +\li {1,2} \snippet qml/integrating-javascript/includejs/app.qml 0 +\li \snippet qml/integrating-javascript/includejs/script.mjs 0 +\row +\li \snippet qml/integrating-javascript/includejs/factorial.mjs 0 +\endtable + +The \l{QtQml::Qt::include()} {Qt.include()} function includes one JavaScript +file from another without using ECMAScript modules and without qualifying the +import. It makes all functions and variables from the other file available in +the current file's namespace, but ignores all pragmas and imports defined in +that file. This is not a good idea as a function call should never modify the +caller's context. + +\l{QtQml::Qt::include()} {Qt.include()} is deprecated and should be avoided. It +will be removed in a future version of Qt. + \section2 Importing a QML Module from a JavaScript Resource A JavaScript resource may import a QML module in the following fashion: @@ -119,32 +154,4 @@ via a singleton type; see qmlRegisterSingletonType() for more information. \note The .import syntax doesn't work for scripts used in the \l {WorkerScript} -\section1 Including a JavaScript Resource from Another JavaScript Resource - -When a JavaScript file is imported, it must be imported with a qualifier. The -functions in that file are then accessible from the importing script via the -qualifier (that is, as \tt{Qualifier.functionName(params)}). Sometimes it is -desirable to have the functions made available in the importing context without -needing to qualify them, and in this circumstance the \l{QtQml::Qt::include()} -{Qt.include()} function may be used to include one JavaScript file from another. -This copies all functions from the other file into the current file's -namespace, but ignores all pragmas and imports defined in that file. - -For example, the QML code below left calls \c showCalculations() in \c script.js, -which in turn can call \c factorial() in \c factorial.js, as it has included -\c factorial.js using \l {QtQml::Qt::include()}{Qt.include()}. - -\table -\row -\li {1,2} \snippet qml/integrating-javascript/includejs/app.qml 0 -\li \snippet qml/integrating-javascript/includejs/script.js 0 -\row -\li \snippet qml/integrating-javascript/includejs/factorial.js 0 -\endtable - -Notice that calling \l {QtQml::Qt::include()}{Qt.include()} copies all functions -from \c factorial.js into the \c MyScript namespace, which means the QML -component can also access \c factorial() directly as \c MyScript.factorial(). - - */ diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 2bb1fcac61..15e8e4c52c 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -540,6 +540,58 @@ Internally, however, the rectangle can correctly set its \c color property and refer to the actual defined property rather than the alias. +\section4 Property Aliases and Types + +Property aliases cannot have explicit type specifications. The type of a +property alias is the \e declared type of the property or object it refers to. +Therefore, if you create an alias to an object referenced via id with extra +properties declared inline, the extra properties won't be accessible through +the alias: + +\code +// MyItem.qml +Item { + property alias inner: innerItem + + Item { + id: innerItem + property int extraProperty + } +} +\code + +You cannot initialize \a inner.extraProperty from outside of this component, as +inner is only an \a Item: + +\code +// main.qml +MyItem { + inner.extraProperty: 5 // fails +} +\code + +However, if you extract the inner object into a separate component with a +dedicated .qml file, you can instantiate that component instead and have all +its properties available through the alias: + +\code +// MainItem.qml +Item { + // Now you can access inner.extraProperty, as inner is now an ExtraItem + property alias inner: innerItem + + ExtraItem { + id: innerItem + } +} + +// ExtraItem.qml +Item { + property int extraProperty +} +\code + + \section3 Default Properties An object definition can have a single \e default property. A default property diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc index 6986f1baa0..231b85af76 100644 --- a/src/qml/doc/src/statemachine.qdoc +++ b/src/qml/doc/src/statemachine.qdoc @@ -27,7 +27,7 @@ /*! \qmlmodule QtQml.StateMachine 1.\QtMinorVersion - \title Declarative State Machine QML Types + \title Qt QML State Machine QML Types \brief Provides QML types to create and execute state graphs. The following is a list of QML types provided by the module: @@ -322,7 +322,7 @@ \section1 Related Information \list - \li \l{Declarative State Machine QML Types} + \li \l{Qt QML State Machine QML Types} \li \l{The State Machine Framework} \endlist */ diff --git a/src/qml/jit/qv4assemblercommon.cpp b/src/qml/jit/qv4assemblercommon.cpp index 831496a628..dd810d9d70 100644 --- a/src/qml/jit/qv4assemblercommon.cpp +++ b/src/qml/jit/qv4assemblercommon.cpp @@ -39,10 +39,12 @@ #include <QBuffer> #include <QFile> +#include <QLoggingCategory> #include "qv4engine_p.h" #include "qv4assemblercommon_p.h" #include <private/qv4function_p.h> +#include <private/qv4functiontable_p.h> #include <private/qv4runtime_p.h> #include <assembler/MacroAssemblerCodeRef.h> @@ -57,6 +59,8 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +Q_LOGGING_CATEGORY(lcAsm, "qt.v4.asm") + namespace { class QIODevicePrintStream: public FilePrintStream { @@ -109,18 +113,9 @@ static void printDisassembledOutputWithCalls(QByteArray processedOutput, } } - qDebug("%s", processedOutput.constData()); -} - -static QByteArray functionName(Function *function) -{ - QByteArray name = function->name()->toQString().toUtf8(); - if (name.isEmpty()) { - name = QByteArray::number(reinterpret_cast<quintptr>(function), 16); - name.prepend("QV4::Function(0x"); - name.append(')'); - } - return name; + auto lines = processedOutput.split('\n'); + for (const auto &line : lines) + qCDebug(lcAsm, "%s", line.constData()); } JIT::PlatformAssemblerCommon::~PlatformAssemblerCommon() @@ -141,13 +136,15 @@ void PlatformAssemblerCommon::link(Function *function, const char *jitKind) JSC::MacroAssemblerCodeRef codeRef; - static const bool showCode = qEnvironmentVariableIsSet("QV4_SHOW_ASM"); + static const bool showCode = lcAsm().isDebugEnabled(); if (showCode) { QBuffer buf; buf.open(QIODevice::WriteOnly); WTF::setDataFile(new QIODevicePrintStream(&buf)); - QByteArray name = functionName(function); + // We use debugAddress here because it's actually for debugging and hidden behind an + // environment variable. + const QByteArray name = Function::prettyName(function, linkBuffer.debugAddress()).toUtf8(); codeRef = linkBuffer.finalizeCodeWithDisassembly(jitKind, "function %s", name.constData()); WTF::setDataFile(stderr); @@ -159,31 +156,9 @@ void PlatformAssemblerCommon::link(Function *function, const char *jitKind) function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef); function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress()); - // This implements writing of JIT'd addresses so that perf can find the - // symbol names. - // - // Perf expects the mapping to be in a certain place and have certain - // content, for more information, see: - // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt - static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); - if (Q_UNLIKELY(doProfile)) { - static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map") - .arg(QCoreApplication::applicationPid())); - static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly); - if (!isOpen) { - qWarning("QV4::JIT::Assembler: Cannot write perf map file."); - doProfile = false; - } else { - perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>( - codeRef.code().executableAddress()), 16)); - perfMapFile.putChar(' '); - perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef.size()), 16)); - perfMapFile.putChar(' '); - perfMapFile.write(functionName(function)); - perfMapFile.putChar('\n'); - perfMapFile.flush(); - } - } + generateFunctionTable(function, &codeRef); + + linkBuffer.makeExecutable(); } void PlatformAssemblerCommon::prepareCallWithArgCount(int argc) @@ -316,6 +291,19 @@ void PlatformAssemblerCommon::passInt32AsArg(int value, int arg) store32(TrustedImm32(value), argStackAddress(arg)); } +void JIT::PlatformAssemblerCommon::passPointerAsArg(void *ptr, int arg) +{ +#ifndef QT_NO_DEBUG + Q_ASSERT(arg < remainingArgcForCall); + --remainingArgcForCall; +#endif + + if (arg < ArgInRegCount) + move(TrustedImmPtr(ptr), registerForArg(arg)); + else + storePtr(TrustedImmPtr(ptr), argStackAddress(arg)); +} + void PlatformAssemblerCommon::callRuntime(const char *functionName, const void *funcPtr) { #ifndef QT_NO_DEBUG diff --git a/src/qml/jit/qv4assemblercommon_p.h b/src/qml/jit/qv4assemblercommon_p.h index 729d0fc53d..d3d7eedae2 100644 --- a/src/qml/jit/qv4assemblercommon_p.h +++ b/src/qml/jit/qv4assemblercommon_p.h @@ -587,7 +587,7 @@ public: Address loadCompilationUnitPtr(RegisterID target) { Address addr = loadFunctionPtr(target); - addr.offset = offsetof(QV4::Function, compilationUnit); + addr.offset = offsetof(QV4::FunctionData, compilationUnit); loadPtr(addr, target); return Address(target); } @@ -670,7 +670,8 @@ public: void addLabelForOffset(int offset) { - labelForOffset.insert(offset, label()); + if (!labelForOffset.contains(offset)) + labelForOffset.insert(offset, label()); } void addJumpToOffset(const Jump &jump, int offset) @@ -699,6 +700,7 @@ public: void passAddressAsArg(Address addr, int arg); void passCppFrameAsArg(int arg); void passInt32AsArg(int value, int arg); + void passPointerAsArg(void *ptr, int arg); void callRuntime(const char *functionName, const void *funcPtr); void callRuntimeUnchecked(const char *functionName, const void *funcPtr); void tailCallRuntime(const char *functionName, const void *funcPtr); diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index b13f646360..25c74e74e8 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -1382,28 +1382,31 @@ void BaselineAssembler::cmpStrictNotEqual(int lhs) pasm()->setAccumulatorTag(QV4::Value::ValueTypeInternal::Boolean); } -void BaselineAssembler::jump(int offset) +int BaselineAssembler::jump(int offset) { pasm()->addJumpToOffset(pasm()->jump(), offset); + return offset; } -void BaselineAssembler::jumpTrue(int offset) +int BaselineAssembler::jumpTrue(int offset) { pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) { auto jump = pasm()->branch32(PlatformAssembler::NotEqual, TrustedImm32(0), resultReg); pasm()->addJumpToOffset(jump, offset); }); + return offset; } -void BaselineAssembler::jumpFalse(int offset) +int BaselineAssembler::jumpFalse(int offset) { pasm()->toBoolean([this, offset](PlatformAssembler::RegisterID resultReg) { auto jump = pasm()->branch32(PlatformAssembler::Equal, TrustedImm32(0), resultReg); pasm()->addJumpToOffset(jump, offset); }); + return offset; } -void BaselineAssembler::jumpNoException(int offset) +int BaselineAssembler::jumpNoException(int offset) { auto jump = pasm()->branch32( PlatformAssembler::Equal, @@ -1411,11 +1414,13 @@ void BaselineAssembler::jumpNoException(int offset) offsetof(EngineBase, hasException)), TrustedImm32(0)); pasm()->addJumpToOffset(jump, offset); + return offset; } -void BaselineAssembler::jumpNotUndefined(int offset) +int BaselineAssembler::jumpNotUndefined(int offset) { pasm()->jumpNotUndefined(offset); + return offset; } void BaselineAssembler::prepareCallWithArgCount(int argc) @@ -1458,6 +1463,11 @@ void BaselineAssembler::passInt32AsArg(int value, int arg) pasm()->passInt32AsArg(value, arg); } +void BaselineAssembler::passPointerAsArg(void *ptr, int arg) +{ + pasm()->passPointerAsArg(ptr, arg); +} + void BaselineAssembler::callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest) { pasm()->callRuntime(functionName, funcPtr, dest); @@ -1534,10 +1544,11 @@ void BaselineAssembler::setException() noException.link(pasm()); } -void BaselineAssembler::setUnwindHandler(int offset) +int BaselineAssembler::setUnwindHandler(int offset) { auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), pasm()->exceptionHandlerAddress()); pasm()->addEHTarget(l, offset); + return offset; } @@ -1563,12 +1574,13 @@ void JIT::BaselineAssembler::unwindDispatch() noUnwind.link(pasm()); } -void JIT::BaselineAssembler::unwindToLabel(int level, int offset) +int JIT::BaselineAssembler::unwindToLabel(int level, int offset) { auto l = pasm()->storePtrWithPatch(TrustedImmPtr(nullptr), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLabel))); pasm()->addEHTarget(l, offset); pasm()->store32(TrustedImm32(level), Address(PlatformAssembler::CppStackFrameRegister, offsetof(CppStackFrame, unwindLevel))); gotoCatchException(); + return offset; } void BaselineAssembler::pushCatchContext(int index, int name) diff --git a/src/qml/jit/qv4baselineassembler_p.h b/src/qml/jit/qv4baselineassembler_p.h index 0aa508ae71..c39d002bf9 100644 --- a/src/qml/jit/qv4baselineassembler_p.h +++ b/src/qml/jit/qv4baselineassembler_p.h @@ -135,11 +135,11 @@ public: void cmpStrictNotEqual(int lhs); // jumps - void jump(int offset); - void jumpTrue(int offset); - void jumpFalse(int offset); - void jumpNoException(int offset); - void jumpNotUndefined(int offset); + Q_REQUIRED_RESULT int jump(int offset); + Q_REQUIRED_RESULT int jumpTrue(int offset); + Q_REQUIRED_RESULT int jumpFalse(int offset); + Q_REQUIRED_RESULT int jumpNoException(int offset); + Q_REQUIRED_RESULT int jumpNotUndefined(int offset); // stuff for runtime calls void prepareCallWithArgCount(int argc); @@ -150,6 +150,7 @@ public: void passJSSlotAsArg(int reg, int arg); void passCppFrameAsArg(int arg); void passInt32AsArg(int value, int arg); + void passPointerAsArg(void *ptr, int arg); void callRuntime(const char *functionName, const void *funcPtr, CallResultDestination dest); void saveAccumulatorInFrame(); void jsTailCall(int func, int thisObject, int argc, int argv); @@ -159,10 +160,10 @@ public: void gotoCatchException(); void getException(); void setException(); - void setUnwindHandler(int offset); + Q_REQUIRED_RESULT int setUnwindHandler(int offset); void clearUnwindHandler(); void unwindDispatch(); - void unwindToLabel(int level, int offset); + Q_REQUIRED_RESULT int unwindToLabel(int level, int offset); void pushCatchContext(int index, int name); void popContext(); void deadTemporalZoneCheck(int offsetForSavedIP, int variableName); diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 1e4288e3c9..e518fc5a0e 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -63,7 +63,9 @@ void BaselineJIT::generate() // qDebug()<<"jitting" << function->name()->toQString(); const char *code = function->codeData; uint len = function->compiledFunction->codeSize; - labels = collectLabelsInBytecode(code, len); + + for (unsigned i = 0, ei = function->compiledFunction->nLabelInfos; i != ei; ++i) + labels.insert(int(function->compiledFunction->labelInfoTable()[i])); as->generatePrologue(); decode(code, len); @@ -149,7 +151,7 @@ void BaselineJIT::generate_LoadImport(int index) as->loadImport(index); } -void BaselineJIT::generate_LoadLocal(int index) +void BaselineJIT::generate_LoadLocal(int index, int /*traceSlot*/) { as->loadLocal(index); } @@ -160,7 +162,7 @@ void BaselineJIT::generate_StoreLocal(int index) as->storeLocal(index); } -void BaselineJIT::generate_LoadScopedLocal(int scope, int index) +void BaselineJIT::generate_LoadScopedLocal(int scope, int index, int /*traceSlot*/) { as->loadLocal(index, scope); } @@ -193,7 +195,7 @@ void BaselineJIT::generate_LoadClosure(int value) BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_closure, CallResultDestination::InAccumulator); } -void BaselineJIT::generate_LoadName(int name) +void BaselineJIT::generate_LoadName(int name, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(2); @@ -203,7 +205,7 @@ void BaselineJIT::generate_LoadName(int name) as->checkException(); } -void BaselineJIT::generate_LoadGlobalLookup(int index) +void BaselineJIT::generate_LoadGlobalLookup(int index, int /*traceSlot*/) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); @@ -213,7 +215,7 @@ void BaselineJIT::generate_LoadGlobalLookup(int index) as->checkException(); } -void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index) +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index, int /*traceSlot*/) { as->prepareCallWithArgCount(3); as->passInt32AsArg(index, 2); @@ -247,7 +249,7 @@ void BaselineJIT::generate_StoreNameStrict(int name) as->checkException(); } -void BaselineJIT::generate_LoadElement(int base) +void BaselineJIT::generate_LoadElement(int base, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -259,7 +261,7 @@ void BaselineJIT::generate_LoadElement(int base) as->checkException(); } -void BaselineJIT::generate_StoreElement(int base, int index) +void BaselineJIT::generate_StoreElement(int base, int index, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -272,7 +274,7 @@ void BaselineJIT::generate_StoreElement(int base, int index) as->checkException(); } -void BaselineJIT::generate_LoadProperty(int name) +void BaselineJIT::generate_LoadProperty(int name, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -284,7 +286,7 @@ void BaselineJIT::generate_LoadProperty(int name) as->checkException(); } -void BaselineJIT::generate_GetLookup(int index) +void BaselineJIT::generate_GetLookup(int index, int /*traceSlot*/) { STORE_IP(); STORE_ACC(); @@ -365,7 +367,7 @@ void BaselineJIT::generate_Resume(int) Q_UNREACHABLE(); } -void BaselineJIT::generate_CallValue(int name, int argc, int argv) +void BaselineJIT::generate_CallValue(int name, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -377,7 +379,7 @@ void BaselineJIT::generate_CallValue(int name, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv) +void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -390,7 +392,7 @@ void BaselineJIT::generate_CallWithReceiver(int name, int thisObject, int argc, as->checkException(); } -void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) +void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -403,7 +405,7 @@ void BaselineJIT::generate_CallProperty(int name, int base, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) +void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -416,7 +418,7 @@ void BaselineJIT::generate_CallPropertyLookup(int lookupIndex, int base, int arg as->checkException(); } -void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) +void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -429,7 +431,7 @@ void BaselineJIT::generate_CallElement(int base, int index, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallName(int name, int argc, int argv) +void BaselineJIT::generate_CallName(int name, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -441,7 +443,7 @@ void BaselineJIT::generate_CallName(int name, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) +void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(3); @@ -452,7 +454,7 @@ void BaselineJIT::generate_CallPossiblyDirectEval(int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) +void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -464,7 +466,8 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) as->checkException(); } -void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv, + int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(4); @@ -476,7 +479,7 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int as->checkException(); } -void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) +void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv, int /*traceSlot*/) { STORE_IP(); as->prepareCallWithArgCount(5); @@ -526,7 +529,7 @@ void BaselineJIT::generate_ConstructWithSpread(int func, int argc, int argv) void BaselineJIT::generate_SetUnwindHandler(int offset) { if (offset) - as->setUnwindHandler(absoluteOffsetForJump(offset)); + labels.insert(as->setUnwindHandler(absoluteOffset(offset))); else as->clearUnwindHandler(); } @@ -538,7 +541,7 @@ void BaselineJIT::generate_UnwindDispatch() void BaselineJIT::generate_UnwindToLabel(int level, int offset) { - as->unwindToLabel(level, absoluteOffsetForJump(offset)); + labels.insert(as->unwindToLabel(level, absoluteOffset(offset))); } void BaselineJIT::generate_DeadTemporalZoneCheck(int name) @@ -805,11 +808,30 @@ void BaselineJIT::generate_ToObject() } -void BaselineJIT::generate_Jump(int offset) { as->jump(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpTrue(int offset) { as->jumpTrue(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpFalse(int offset) { as->jumpFalse(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpNoException(int offset) { as->jumpNoException(absoluteOffsetForJump(offset)); } -void BaselineJIT::generate_JumpNotUndefined(int offset) { as->jumpNotUndefined(absoluteOffsetForJump(offset)); } +void BaselineJIT::generate_Jump(int offset) +{ + labels.insert(as->jump(absoluteOffset(offset))); +} + +void BaselineJIT::generate_JumpTrue(int /*traceSlot*/, int offset) +{ + labels.insert(as->jumpTrue(absoluteOffset(offset))); +} + +void BaselineJIT::generate_JumpFalse(int /*traceSlot*/, int offset) +{ + labels.insert(as->jumpFalse(absoluteOffset(offset))); +} + +void BaselineJIT::generate_JumpNoException(int offset) +{ + labels.insert(as->jumpNoException(absoluteOffset(offset))); +} + +void BaselineJIT::generate_JumpNotUndefined(int offset) +{ + labels.insert(as->jumpNotUndefined(absoluteOffset(offset))); +} void BaselineJIT::generate_CmpEqNull() { as->cmpeqNull(); } void BaselineJIT::generate_CmpNeNull() { as->cmpneNull(); } @@ -848,11 +870,11 @@ void BaselineJIT::generate_CmpInstanceOf(int lhs) void BaselineJIT::generate_UNot() { as->unot(); } void BaselineJIT::generate_UPlus() { as->toNumber(); } -void BaselineJIT::generate_UMinus() { as->uminus(); } +void BaselineJIT::generate_UMinus(int /*traceSlot*/) { as->uminus(); } void BaselineJIT::generate_UCompl() { as->ucompl(); } -void BaselineJIT::generate_Increment() { as->inc(); } -void BaselineJIT::generate_Decrement() { as->dec(); } -void BaselineJIT::generate_Add(int lhs) { as->add(lhs); } +void BaselineJIT::generate_Increment(int /*traceSlot*/) { as->inc(); } +void BaselineJIT::generate_Decrement(int /*traceSlot*/) { as->dec(); } +void BaselineJIT::generate_Add(int lhs, int /*traceSlot*/) { as->add(lhs); } void BaselineJIT::generate_BitAnd(int lhs) { as->bitAnd(lhs); } void BaselineJIT::generate_BitOr(int lhs) { as->bitOr(lhs); } @@ -877,10 +899,10 @@ void BaselineJIT::generate_Exp(int lhs) { BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::exp, CallResultDestination::InAccumulator); as->checkException(); } -void BaselineJIT::generate_Mul(int lhs) { as->mul(lhs); } +void BaselineJIT::generate_Mul(int lhs, int /*traceSlot*/) { as->mul(lhs); } void BaselineJIT::generate_Div(int lhs) { as->div(lhs); } -void BaselineJIT::generate_Mod(int lhs) { as->mod(lhs); } -void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } +void BaselineJIT::generate_Mod(int lhs, int /*traceSlot*/) { as->mod(lhs); } +void BaselineJIT::generate_Sub(int lhs, int /*traceSlot*/) { as->sub(lhs); } //void BaselineJIT::generate_BinopContext(int alu, int lhs) //{ @@ -923,7 +945,7 @@ void BaselineJIT::generate_GetTemplateObject(int index) void BaselineJIT::startInstruction(Instr::Type /*instr*/) { - if (hasLabel()) + if (labels.contains(currentInstructionOffset())) as->addLabel(currentInstructionOffset()); } diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 6646eb713e..10c89bc74b 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -88,22 +88,22 @@ public: void generate_StoreReg(int reg) override; void generate_MoveReg(int srcReg, int destReg) override; void generate_LoadImport(int index) override; - void generate_LoadLocal(int index) override; + void generate_LoadLocal(int index, int traceSlot) override; void generate_StoreLocal(int index) override; - void generate_LoadScopedLocal(int scope, int index) override; + void generate_LoadScopedLocal(int scope, int index, int traceSlot) override; void generate_StoreScopedLocal(int scope, int index) override; void generate_LoadRuntimeString(int stringId) override; void generate_MoveRegExp(int regExpId, int destReg) override; void generate_LoadClosure(int value) override; - void generate_LoadName(int name) override; - void generate_LoadGlobalLookup(int index) override; - void generate_LoadQmlContextPropertyLookup(int index) override; + void generate_LoadName(int name, int traceSlot) override; + void generate_LoadGlobalLookup(int index, int traceSlot) override; + void generate_LoadQmlContextPropertyLookup(int index, int traceSlot) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; - void generate_LoadElement(int base) override; - void generate_StoreElement(int base, int index) override; - void generate_LoadProperty(int name) override; - void generate_GetLookup(int index) override; + void generate_LoadElement(int base, int traceSlot) override; + void generate_StoreElement(int base, int index, int traceSlot) override; + void generate_LoadProperty(int name, int traceSlot) override; + void generate_GetLookup(int index, int traceSlot) override; void generate_StoreProperty(int name, int base) override; void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; @@ -112,16 +112,16 @@ public: void generate_YieldStar() override; void generate_Resume(int) override; - void generate_CallValue(int name, int argc, int argv) override; - void generate_CallWithReceiver(int name, int thisObject, int argc, int argv) override; - void generate_CallProperty(int name, int base, int argc, int argv) override; - void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv) override; - void generate_CallElement(int base, int index, int argc, int argv) override; - void generate_CallName(int name, int argc, int argv) override; - void generate_CallPossiblyDirectEval(int argc, int argv) override; - void generate_CallGlobalLookup(int index, int argc, int argv) override; - void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; - void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; + void generate_CallValue(int name, int argc, int argv, int traceSlot) override; + void generate_CallWithReceiver(int name, int thisObject, int argc, int argv, int traceSlot) override; + void generate_CallProperty(int name, int base, int argc, int argv, int traceSlot) override; + void generate_CallPropertyLookup(int lookupIndex, int base, int argc, int argv, int traceSlot) override; + void generate_CallElement(int base, int index, int argc, int argv, int traceSlot) override; + void generate_CallName(int name, int argc, int argv, int traceSlot) override; + void generate_CallPossiblyDirectEval(int argc, int argv, int traceSlot) override; + void generate_CallGlobalLookup(int index, int argc, int argv, int traceSlot) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv, int traceSlot) override; + void generate_CallWithSpread(int func, int thisObject, int argc, int argv, int traceSlot) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; void generate_ConstructWithSpread(int func, int argc, int argv) override; @@ -160,8 +160,8 @@ public: void generate_LoadSuperConstructor() override; void generate_ToObject() override; void generate_Jump(int offset) override; - void generate_JumpTrue(int offset) override; - void generate_JumpFalse(int offset) override; + void generate_JumpTrue(int traceSlot, int offset) override; + void generate_JumpFalse(int traceSlot, int offset) override; void generate_JumpNoException(int offset) override; void generate_JumpNotUndefined(int offset) override; void generate_CmpEqNull() override; @@ -180,11 +180,11 @@ public: void generate_CmpInstanceOf(int lhs) override; void generate_UNot() override; void generate_UPlus() override; - void generate_UMinus() override; + void generate_UMinus(int traceSlot) override; void generate_UCompl() override; - void generate_Increment() override; - void generate_Decrement() override; - void generate_Add(int lhs) override; + void generate_Increment(int traceSlot) override; + void generate_Decrement(int traceSlot) override; + void generate_Add(int lhs, int traceSlot) override; void generate_BitAnd(int lhs) override; void generate_BitOr(int lhs) override; void generate_BitXor(int lhs) override; @@ -198,10 +198,10 @@ public: void generate_ShrConst(int rhs) override; void generate_ShlConst(int rhs) override; void generate_Exp(int lhs) override; - void generate_Mul(int lhs) override; + void generate_Mul(int lhs, int traceSlot) override; void generate_Div(int lhs) override; - void generate_Mod(int lhs) override; - void generate_Sub(int lhs) override; + void generate_Mod(int lhs, int traceSlot) override; + void generate_Sub(int lhs, int traceSlot) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; @@ -209,17 +209,10 @@ public: void startInstruction(Moth::Instr::Type instr) override; void endInstruction(Moth::Instr::Type instr) override; -protected: - bool hasLabel() const - { return std::find(labels.cbegin(), labels.cend(), currentInstructionOffset()) != labels.cend(); } - - int absoluteOffsetForJump(int relativeOffset) const - { return nextInstructionOffset() + relativeOffset; } - private: QV4::Function *function; QScopedPointer<BaselineAssembler> as; - std::vector<int> labels; + QSet<int> labels; }; #endif // V4_ENABLE_JIT diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 5ec55b960b..a24ee0a188 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -147,7 +147,8 @@ HEADERS += \ $$PWD/qv4value_p.h \ $$PWD/qv4string_p.h \ $$PWD/qv4util_p.h \ - $$PWD/qv4value_p.h + $$PWD/qv4value_p.h \ + $$PWD/qv4functiontable_p.h SOURCES += \ $$PWD/qv4engine.cpp \ @@ -156,6 +157,23 @@ SOURCES += \ $$PWD/qv4value.cpp \ $$PWD/qv4executableallocator.cpp +qmldevtools_build { + SOURCES += \ + $$PWD/qv4functiontable_noop.cpp +} else:win32 { + !winrt:equals(QT_ARCH, x86_64) { + SOURCES += \ + $$PWD/qv4functiontable_win64.cpp + } else { + SOURCES += \ + $$PWD/qv4functiontable_noop.cpp + } +} else { + SOURCES += \ + $$PWD/qv4functiontable_unix.cpp +} + + valgrind { DEFINES += V4_USE_VALGRIND } diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index a13fb37a52..21c6a5d06b 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -670,17 +670,17 @@ static inline QString ToTimeString(double t) static inline QString ToLocaleString(double t) { - return ToDateTime(t, Qt::LocalTime).toString(Qt::LocaleDate); + return ToDateTime(t, Qt::LocalTime).toString(Qt::DefaultLocaleShortDate); } static inline QString ToLocaleDateString(double t) { - return ToDateTime(t, Qt::LocalTime).date().toString(Qt::LocaleDate); + return ToDateTime(t, Qt::LocalTime).date().toString(Qt::DefaultLocaleShortDate); } static inline QString ToLocaleTimeString(double t) { - return ToDateTime(t, Qt::LocalTime).time().toString(Qt::LocaleDate); + return ToDateTime(t, Qt::LocalTime).time().toString(Qt::DefaultLocaleShortDate); } static double getLocalTZA() diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index bd1124beb6..4c5e124cb0 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -50,6 +50,7 @@ #include <QDateTime> #include <QDir> #include <QFileInfo> +#include <QLoggingCategory> #ifndef V4_BOOTSTRAP @@ -134,6 +135,8 @@ QT_BEGIN_NAMESPACE +Q_LOGGING_CATEGORY(lcTracingAll, "qt.v4.tracing.all") + using namespace QV4; #ifndef V4_BOOTSTRAP @@ -652,6 +655,13 @@ ExecutionEngine::ExecutionEngine(QJSEngine *jsEngine) ExecutionEngine::~ExecutionEngine() { + if (Q_UNLIKELY(lcTracingAll().isDebugEnabled())) { + for (auto cu : compilationUnits) { + for (auto f : qAsConst(cu->runtimeFunctions)) + qCDebug(lcTracingAll).noquote().nospace() << f->traceInfoToString(); + } + } + modules.clear(); delete m_multiplyWrappedQObjects; m_multiplyWrappedQObjects = nullptr; @@ -1620,6 +1630,22 @@ static QV4::ReturnedValue variantListToJS(QV4::ExecutionEngine *v4, const QVaria return a.asReturnedValue(); } +// Converts a QSequentialIterable to JS. +// The result is a new Array object with length equal to the length +// of the QSequentialIterable, and the elements being the QSequentialIterable's +// elements converted to JS, recursively. +static QV4::ReturnedValue sequentialIterableToJS(QV4::ExecutionEngine *v4, const QSequentialIterable &lst) +{ + QV4::Scope scope(v4); + QV4::ScopedArrayObject a(scope, v4->newArrayObject()); + a->arrayReserve(lst.size()); + QV4::ScopedValue v(scope); + for (int i = 0; i < lst.size(); i++) + a->arrayPut(i, (v = variantToJS(v4, lst.at(i)))); + a->setArrayLengthUnchecked(lst.size()); + return a.asReturnedValue(); +} + // Converts a QVariantMap to JS. // The result is a new Object object with property names being // the keys of the QVariantMap, and values being the values of @@ -1724,9 +1750,18 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return QV4::Encode::null(); } QMetaType mt(type); - if (mt.flags() & QMetaType::IsGadget) { - Q_ASSERT(mt.metaObject()); - return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), mt.metaObject(), type); + if (auto metaObject = mt.metaObject()) { + auto flags = mt.flags(); + if (flags & QMetaType::IsGadget) { + return QV4::QQmlValueTypeWrapper::create(this, QVariant(type, data), metaObject, type); + } else if (flags & QMetaType::PointerToQObject) { + return QV4::QObjectWrapper::wrap(this, *reinterpret_cast<QObject* const *>(data)); + } + } + if (QMetaType::hasRegisteredConverterFunction(type, qMetaTypeId<QtMetaTypePrivate::QSequentialIterableImpl>())) { + auto v = QVariant(type, data); + QSequentialIterable lst = v.value<QSequentialIterable>(); + return sequentialIterableToJS(this, lst); } // Fall back to wrapping in a QVariant. return QV4::Encode(newVariantObject(QVariant(type, data))); diff --git a/src/qml/jsruntime/qv4executableallocator.cpp b/src/qml/jsruntime/qv4executableallocator.cpp index 6f04a712e6..c836d121e3 100644 --- a/src/qml/jsruntime/qv4executableallocator.cpp +++ b/src/qml/jsruntime/qv4executableallocator.cpp @@ -38,17 +38,23 @@ ****************************************************************************/ #include "qv4executableallocator_p.h" +#include "qv4functiontable_p.h" #include <wtf/StdLibExtras.h> #include <wtf/PageAllocation.h> using namespace QV4; -void *ExecutableAllocator::Allocation::start() const +void *ExecutableAllocator::Allocation::exceptionHandler() const { return reinterpret_cast<void*>(addr); } +void *ExecutableAllocator::Allocation::start() const +{ + return reinterpret_cast<void*>(addr + exceptionHandlerSize()); +} + void ExecutableAllocator::Allocation::deallocate(ExecutableAllocator *allocator) { if (isValid()) @@ -162,7 +168,7 @@ ExecutableAllocator::Allocation *ExecutableAllocator::allocate(size_t size) Allocation *allocation = nullptr; // Code is best aligned to 16-byte boundaries. - size = WTF::roundUpToMultipleOf(16, size); + size = WTF::roundUpToMultipleOf(16, size + exceptionHandlerSize()); QMultiMap<size_t, Allocation*>::Iterator it = freeAllocations.lowerBound(size); if (it != freeAllocations.end()) { diff --git a/src/qml/jsruntime/qv4executableallocator_p.h b/src/qml/jsruntime/qv4executableallocator_p.h index 375c9a365f..013c6d7120 100644 --- a/src/qml/jsruntime/qv4executableallocator_p.h +++ b/src/qml/jsruntime/qv4executableallocator_p.h @@ -86,6 +86,7 @@ public: , free(true) {} + void *exceptionHandler() const; void *start() const; void invalidate() { addr = 0; } bool isValid() const { return addr != 0; } diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 51a9b92967..1bd4329fe8 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -46,6 +46,7 @@ #include "qv4lookup_p.h" #include <private/qv4mm_p.h> #include <private/qv4identifiertable_p.h> +#include <private/qv4functiontable_p.h> #include <assembler/MacroAssemblerCodeRef.h> #include <private/qv4vme_moth_p.h> #include <private/qqmlglobal_p.h> @@ -72,12 +73,29 @@ ReturnedValue Function::call(const Value *thisObject, const Value *argv, int arg return result; } +Function *Function::create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) +{ + quint16 traceSlotCount = 0; +#if QT_CONFIG(qml_tracing) + traceSlotCount = function->nTraceInfos == CompiledData::Function::NoTracing() + ? 1 + : function->nTraceInfos; +#endif + quint8 *storage = new quint8[sizeof(Function) + traceSlotCount]; + return new(storage) Function(engine, unit, function); +} + +void Function::destroy() +{ + delete[] reinterpret_cast<quint8 *>(this); +} + Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function) - : compiledFunction(function) - , compilationUnit(unit) + : FunctionData(unit) + , compiledFunction(function) , codeData(function->code()) - , jittedCode(nullptr) - , codeRef(nullptr) + , jittedCode(nullptr) + , codeRef(nullptr) { Scope scope(engine); Scoped<InternalClass> ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); @@ -93,11 +111,21 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, internalClass = ic->d(); nFormals = compiledFunction->nFormals; + +#if QT_CONFIG(qml_tracing) + if (tracingEnabled()) { + for (uint i = 0; i < function->nTraceInfos; ++i) + *traceInfo(i) = 0; + } +#endif } Function::~Function() { - delete codeRef; + if (codeRef) { + destroyFunctionTable(this, codeRef); + delete codeRef; + } } void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters) @@ -144,9 +172,38 @@ void Function::updateInternalClass(ExecutionEngine *engine, const QList<QByteArr nFormals = parameters.size(); } +QString Function::prettyName(const Function *function, const void *code) +{ + QString prettyName = function ? function->name()->toQString() : QString(); + if (prettyName.isEmpty()) { + prettyName = QString::number(reinterpret_cast<quintptr>(code), 16); + prettyName.prepend(QLatin1String("QV4::Function(0x")); + prettyName.append(QLatin1Char(')')); + } + return prettyName; +} + QQmlSourceLocation Function::sourceLocation() const { return QQmlSourceLocation(sourceFile(), compiledFunction->location.line, compiledFunction->location.column); } +QString Function::traceInfoToString() +{ + QString info = QLatin1String("=== Trace information for ") + name()->toQString() + QLatin1Char(':'); + if (!tracingEnabled()) + return info + QStringLiteral(" disabled. Interpreter call count: %1\n").arg(interpreterCallCount); + if (compiledFunction->nTraceInfos == 0) + return info + QLatin1String(" none.\n"); + + info += QLatin1Char('\n'); + for (uint i = 0, ei = compiledFunction->nTraceInfos; i < ei; ++i) { + auto bits = QString::number(*traceInfo(i), 2); + if (bits.size() < 8) + bits.prepend(QString(8 - bits.size(), '0')); + info += QStringLiteral(" %1: %2\n").arg(QString::number(i), bits); + } + return info; +} + QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 62c5d24fc4..f8125a58f8 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -64,10 +64,24 @@ struct QQmlSourceLocation; namespace QV4 { -struct Q_QML_EXPORT Function { - const CompiledData::Function *compiledFunction; +struct Q_QML_EXPORT FunctionData { CompiledData::CompilationUnit *compilationUnit; + FunctionData(CompiledData::CompilationUnit *compilationUnit) + : compilationUnit(compilationUnit) + {} +}; +// Make sure this class can be accessed through offsetof (done by the assemblers): +Q_STATIC_ASSERT(std::is_standard_layout< FunctionData >::value); + +struct Q_QML_EXPORT Function : public FunctionData { +private: + Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + ~Function(); + +public: + const CompiledData::Function *compiledFunction; + ReturnedValue call(const Value *thisObject, const Value *argv, int argc, const ExecutionContext *context); const char *codeData; @@ -82,15 +96,18 @@ struct Q_QML_EXPORT Function { int interpreterCallCount = 0; bool isEval = false; - Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); - ~Function(); + static Function *create(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); + void destroy(); // used when dynamically assigning signal handlers (QQmlConnection) void updateInternalClass(ExecutionEngine *engine, const QList<QByteArray> ¶meters); - inline Heap::String *name() { + inline Heap::String *name() const { return compilationUnit->runtimeStrings[compiledFunction->nameIndex]; } + + static QString prettyName(const Function *function, const void *address); + inline QString sourceFile() const { return compilationUnit->fileName(); } inline QUrl finalUrl() const { return compilationUnit->finalUrl(); } @@ -106,6 +123,31 @@ struct Q_QML_EXPORT Function { return nullptr; return compilationUnit->runtimeFunctions[compiledFunction->nestedFunctionIndex]; } + + Q_NEVER_INLINE QString traceInfoToString(); + + quint8 *traceInfo(uint i) + { +#if QT_CONFIG(qml_tracing) + Q_ASSERT((tracingEnabled() && i < traceInfoCount()) || (i == 0)); + return reinterpret_cast<quint8 *>(this) + sizeof(Function) + i; +#else + Q_UNUSED(i); + return nullptr; +#endif + } + + quint32 traceInfoCount() const + { return compiledFunction->nTraceInfos; } + + bool tracingEnabled() const + { +#if QT_CONFIG(qml_tracing) + return traceInfoCount() != CompiledData::Function::NoTracing(); +#else + return false; +#endif + } }; } diff --git a/src/qml/jsruntime/qv4functiontable_noop.cpp b/src/qml/jsruntime/qv4functiontable_noop.cpp new file mode 100644 index 0000000000..31c198eb00 --- /dev/null +++ b/src/qml/jsruntime/qv4functiontable_noop.cpp @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4functiontable_p.h" + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef) +{ + Q_UNUSED(function); + Q_UNUSED(codeRef); +} + +void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef) +{ + Q_UNUSED(function); + Q_UNUSED(codeRef); +} + +size_t exceptionHandlerSize() +{ + return 0; +} + +} // QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4functiontable_p.h b/src/qml/jsruntime/qv4functiontable_p.h new file mode 100644 index 0000000000..69e3d2bdd5 --- /dev/null +++ b/src/qml/jsruntime/qv4functiontable_p.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QV4FUNCTIONTABLE_P_H +#define QV4FUNCTIONTABLE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qv4global_p.h" + +namespace JSC { +class MacroAssemblerCodeRef; +} + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +struct Function; + +void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef); +void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef); + +size_t exceptionHandlerSize(); + +} + +QT_END_NAMESPACE + +#endif // QV4FUNCTIONTABLE_P_H diff --git a/src/qml/jsruntime/qv4functiontable_unix.cpp b/src/qml/jsruntime/qv4functiontable_unix.cpp new file mode 100644 index 0000000000..25b5c27161 --- /dev/null +++ b/src/qml/jsruntime/qv4functiontable_unix.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4functiontable_p.h" +#include "qv4function_p.h" + +#include <assembler/MacroAssemblerCodeRef.h> + +#include <QtCore/qfile.h> +#include <QtCore/qcoreapplication.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +void generateFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef) +{ + // This implements writing of JIT'd addresses so that perf can find the + // symbol names. + // + // Perf expects the mapping to be in a certain place and have certain + // content, for more information, see: + // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt + static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP"); + if (Q_UNLIKELY(doProfile)) { + static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map") + .arg(QCoreApplication::applicationPid())); + static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly); + if (!isOpen) { + qWarning("QV4::JIT::Assembler: Cannot write perf map file."); + doProfile = false; + } else { + const void *address = codeRef->code().executableAddress(); + perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>(address), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef->size()), 16)); + perfMapFile.putChar(' '); + perfMapFile.write(Function::prettyName(function, address).toUtf8()); + perfMapFile.putChar('\n'); + perfMapFile.flush(); + } + } +} + +void destroyFunctionTable(Function *function, JSC::MacroAssemblerCodeRef *codeRef) +{ + Q_UNUSED(function); + Q_UNUSED(codeRef); + + // It's not advisable to remove things from the perf map file, as it's primarily used to analyze + // a trace after the application has terminated. We want to know about all functions that were + // ever jitted then. If the memory ranges overlap, we will have a problem when analyzing the + // trace. The JIT should try to avoid this. +} + +size_t exceptionHandlerSize() +{ + return 0; +} + +} // QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4functiontable_win64.cpp b/src/qml/jsruntime/qv4functiontable_win64.cpp new file mode 100644 index 0000000000..fc13dc2602 --- /dev/null +++ b/src/qml/jsruntime/qv4functiontable_win64.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qv4functiontable_p.h" + +#include <assembler/MacroAssemblerCodeRef.h> + +#include <QtCore/qdebug.h> + +#include <windows.h> + +QT_BEGIN_NAMESPACE + +namespace QV4 { + +enum UnwindOpcode: UINT8 +{ + UWOP_PUSH_NONVOL = 0, /* info == register number */ + UWOP_ALLOC_LARGE, /* no info, alloc size in next 2 slots */ + UWOP_ALLOC_SMALL, /* info == size of allocation / 8 - 1 */ + UWOP_SET_FPREG, /* no info, FP = RSP + UNWIND_INFO.FPRegOffset*16 */ + UWOP_SAVE_NONVOL, /* info == register number, offset in next slot */ + UWOP_SAVE_NONVOL_FAR, /* info == register number, offset in next 2 slots */ + UWOP_SAVE_XMM128 = 8, /* info == XMM reg number, offset in next slot */ + UWOP_SAVE_XMM128_FAR, /* info == XMM reg number, offset in next 2 slots */ + UWOP_PUSH_MACHFRAME /* info == 0: no error-code, 1: error-code */ +}; + +enum Register : UINT8 +{ + RAX = 0, + RCX, + RDX, + RBX, + RSP, + RBP, + RSI, + RDI, + NONE = 15 +}; + +struct UnwindCode +{ + UnwindCode(UINT8 offset, UnwindOpcode operation, Register info) + : offset(offset), operation(operation), info(info) + {} + + UINT8 offset; + UINT8 operation: 4; + UINT8 info: 4; +}; + +struct UnwindInfo +{ + UINT8 Version : 3; + UINT8 Flags : 5; + UINT8 SizeOfProlog; + UINT8 CountOfUnwindCodes; + UINT8 FrameRegister : 4; + UINT8 FrameRegisterOffset : 4; + UnwindCode UnwindCodes[2]; +}; + +struct ExceptionHandlerRecord +{ + RUNTIME_FUNCTION handler; + UnwindInfo info; +}; + +void generateFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef) +{ + ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>( + codeRef->executableMemory()->exceptionHandler()); + + record->info.Version = 1; + record->info.Flags = 0; + record->info.SizeOfProlog = 4; + record->info.CountOfUnwindCodes = 2; + record->info.FrameRegister = RBP; + record->info.FrameRegisterOffset = 0; + + // Push frame pointer + record->info.UnwindCodes[1] = UnwindCode(1, UWOP_PUSH_NONVOL, RBP); + // Set frame pointer from stack pointer + record->info.UnwindCodes[0] = UnwindCode(4, UWOP_SET_FPREG, NONE); + + const quintptr codeStart = quintptr(codeRef->code().executableAddress()); + const quintptr codeSize = codeRef->size(); + + record->handler.BeginAddress = DWORD(codeStart - quintptr(record)); + record->handler.EndAddress = DWORD(codeStart + codeSize - quintptr(record)); + record->handler.UnwindData = offsetof(ExceptionHandlerRecord, info); + + if (!RtlAddFunctionTable(&record->handler, 1, DWORD64(record))) { + const unsigned int errorCode = GetLastError(); + qWarning() << "Failed to install win64 unwind hook. Error code:" << errorCode; + } +} + +void destroyFunctionTable(Function *, JSC::MacroAssemblerCodeRef *codeRef) +{ + ExceptionHandlerRecord *record = reinterpret_cast<ExceptionHandlerRecord *>( + codeRef->executableMemory()->exceptionHandler()); + if (!RtlDeleteFunctionTable(&record->handler)) { + const unsigned int errorCode = GetLastError(); + qWarning() << "Failed to remove win64 unwind hook. Error code:" << errorCode; + } +} + +size_t exceptionHandlerSize() +{ + return sizeof(ExceptionHandlerRecord); +} + +} // QV4 + +QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 162fb66cba..44adac26cd 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -279,6 +279,20 @@ struct IdentifierTable; class RegExpCache; class MultiplyWrappedQObjectMap; +enum class ObservedTraceValues : quint8 { + Integer = 1 << 0, + Boolean = 1 << 1, + Double = 1 << 2, + Other = 1 << 3, + TypeMask = Integer | Boolean | Double | Other, + + TruePathTaken = 1 << 0, + FalsePathTaken = 1 << 1, + + ArrayWasAccessed = 1 << 7, + ArrayAccessNeededFallback = 1 << 6, +}; + enum PropertyFlag { Attr_Data = 0, Attr_Accessor = 0x1, diff --git a/src/qml/jsruntime/qv4math_p.h b/src/qml/jsruntime/qv4math_p.h index 90246c4229..a60a49a811 100644 --- a/src/qml/jsruntime/qv4math_p.h +++ b/src/qml/jsruntime/qv4math_p.h @@ -66,27 +66,42 @@ QT_BEGIN_NAMESPACE namespace QV4 { -static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b) +static inline QMLJS_READONLY ReturnedValue add_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(add_overflow(a, b, &result))) + if (Q_UNLIKELY(add_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); return Value::fromDouble(static_cast<double>(a) + b).asReturnedValue(); + } + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b) +static inline QMLJS_READONLY ReturnedValue sub_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(sub_overflow(a, b, &result))) + if (Q_UNLIKELY(sub_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); return Value::fromDouble(static_cast<double>(a) - b).asReturnedValue(); + } + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } -static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b) +static inline QMLJS_READONLY ReturnedValue mul_int32(int a, int b, quint8 *traceInfo = nullptr) { int result; - if (Q_UNLIKELY(mul_overflow(a, b, &result))) + if (Q_UNLIKELY(mul_overflow(a, b, &result))) { + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Double); return Value::fromDouble(static_cast<double>(a) * b).asReturnedValue(); + } + if (traceInfo) + *traceInfo |= quint8(QV4::ObservedTraceValues::Integer); return Value::fromInt32(result).asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 6a242ba5f6..f7c339dc26 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -704,6 +704,30 @@ ReturnedValue Runtime::method_loadElement(ExecutionEngine *engine, const Value & return getElementFallback(engine, object, index); } +ReturnedValue Runtime::method_loadElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot) +{ + *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); + if (Heap::Base *b = object.heapObject()) { + if (b->internalClass->vtable->isObject) { + Heap::Object *o = static_cast<Heap::Object *>(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); + if (idx < s->values.size) + if (!s->data(idx).isEmpty()) + return s->data(idx).asReturnedValue(); + } + } + } + *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); + return getElementIntFallback(engine, object, idx); + } + + *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); + return getElementFallback(engine, object, index); +} + static Q_NEVER_INLINE bool setElementFallback(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value) { Scope scope(engine); @@ -759,6 +783,30 @@ void Runtime::method_storeElement(ExecutionEngine *engine, const Value &object, engine->throwTypeError(); } +void Runtime::method_storeElement_traced(ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot) +{ + *traceSlot |= quint8(ObservedTraceValues::ArrayWasAccessed); + if (index.isPositiveInt()) { + uint idx = static_cast<uint>(index.int_32()); + if (Heap::Base *b = object.heapObject()) { + if (b->internalClass->vtable->isObject) { + Heap::Object *o = static_cast<Heap::Object *>(b); + if (o->arrayData && o->arrayData->type == Heap::ArrayData::Simple) { + Heap::SimpleArrayData *s = o->arrayData.cast<Heap::SimpleArrayData>(); + if (idx < s->values.size) { + s->setData(engine, idx, value); + return; + } + } + } + } + } + + *traceSlot |= quint8(ObservedTraceValues::ArrayAccessNeededFallback); + if (!setElementFallback(engine, object, index, value) && engine->currentStackFrame->v4Function->isStrict()) + engine->throwTypeError(); +} + ReturnedValue Runtime::method_getIterator(ExecutionEngine *engine, const Value &in, int iterator) { Scope scope(engine); @@ -1405,7 +1453,7 @@ ReturnedValue Runtime::method_callProperty(ExecutionEngine *engine, Value *base, if (!f) { QString error = QStringLiteral("Property '%1' of object %2 is not a function") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), + .arg(name->toQString(), base->toQStringNoThrow()); return engine->throwTypeError(error); } diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 4b3905c56f..ceec13a3bb 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -113,9 +113,11 @@ struct ExceptionCheck<void (*)(QV4::NoThrowEngine *, A, B, C)> { F(void, storeNameSloppy, (ExecutionEngine *engine, int nameIndex, const Value &value)) \ F(void, storeProperty, (ExecutionEngine *engine, const Value &object, int nameIndex, const Value &value)) \ F(void, storeElement, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value)) \ + F(void, storeElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, const Value &value, quint8 *traceSlot)) \ F(ReturnedValue, loadProperty, (ExecutionEngine *engine, const Value &object, int nameIndex)) \ F(ReturnedValue, loadName, (ExecutionEngine *engine, int nameIndex)) \ F(ReturnedValue, loadElement, (ExecutionEngine *engine, const Value &object, const Value &index)) \ + F(ReturnedValue, loadElement_traced, (ExecutionEngine *engine, const Value &object, const Value &index, quint8 *traceSlot)) \ F(ReturnedValue, loadSuperProperty, (ExecutionEngine *engine, const Value &property)) \ F(void, storeSuperProperty, (ExecutionEngine *engine, const Value &property, const Value &value)) \ F(ReturnedValue, loadSuperConstructor, (ExecutionEngine *engine, const Value &t)) \ diff --git a/src/qml/jsruntime/qv4variantobject.cpp b/src/qml/jsruntime/qv4variantobject.cpp index ef0877dbd0..e4d8bcaafc 100644 --- a/src/qml/jsruntime/qv4variantobject.cpp +++ b/src/qml/jsruntime/qv4variantobject.cpp @@ -138,11 +138,14 @@ ReturnedValue VariantPrototype::method_toString(const FunctionObject *b, const V const VariantObject *o = thisObject->as<QV4::VariantObject>(); if (!o) RETURN_UNDEFINED(); - QString result = o->d()->data().toString(); - if (result.isEmpty() && !o->d()->data().canConvert(QVariant::String)) { - result = QLatin1String("QVariant(") - + QLatin1String(o->d()->data().typeName()) - + QLatin1Char(')'); + const QVariant variant = o->d()->data(); + QString result = variant.toString(); + if (result.isEmpty() && !variant.canConvert(QVariant::String)) { + QDebug dbg(&result); + dbg << variant; + // QDebug appends a space, we're not interested in continuing the stream so we chop it off. + // Can't use nospace() because it would affect the debug-stream operator of the variant. + result.chop(1); } return Encode(v4->newString(result)); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index ea2217499f..dd712a15d6 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -346,6 +346,70 @@ static struct InstrCount { if (engine->hasException) \ goto handleUnwind +static inline void traceJumpTakesTruePath(bool truePathTaken, Function *f, int slot) +{ +#if QT_CONFIG(qml_tracing) + quint8 *traceInfo = f->traceInfo(slot); + Q_ASSERT(traceInfo); + *traceInfo |= truePathTaken ? quint8(ObservedTraceValues::TruePathTaken) + : quint8(ObservedTraceValues::FalsePathTaken); +#else + Q_UNUSED(truePathTaken); + Q_UNUSED(f); + Q_UNUSED(slot); +#endif +} + +static inline void traceValue(ReturnedValue acc, Function *f, int slot) +{ +#if QT_CONFIG(qml_tracing) + quint8 *traceInfo = f->traceInfo(slot); + Q_ASSERT(traceInfo); + switch (Primitive::fromReturnedValue(acc).type()) { + case QV4::Value::Integer_Type: + *traceInfo |= quint8(ObservedTraceValues::Integer); + break; + case QV4::Value::Boolean_Type: + *traceInfo |= quint8(ObservedTraceValues::Boolean); + break; + case QV4::Value::Double_Type: + *traceInfo |= quint8(ObservedTraceValues::Double); + break; + default: + *traceInfo |= quint8(ObservedTraceValues::Other); + break; + } +#else + Q_UNUSED(acc); + Q_UNUSED(f); + Q_UNUSED(slot); +#endif +} + +static inline void traceDoubleValue(Function *f, int slot) +{ +#if QT_CONFIG(qml_tracing) + quint8 *traceInfo = f->traceInfo(slot); + Q_ASSERT(traceInfo); + *traceInfo |= quint8(ObservedTraceValues::Double); +#else + Q_UNUSED(f); + Q_UNUSED(slot); +#endif +} + +static inline void traceOtherValue(Function *f, int slot) +{ +#if QT_CONFIG(qml_tracing) + quint8 *traceInfo = f->traceInfo(slot); + Q_ASSERT(traceInfo); + *traceInfo |= quint8(ObservedTraceValues::Other); +#else + Q_UNUSED(f); + Q_UNUSED(slot); +#endif +} + static inline Heap::CallContext *getScope(QV4::Value *stack, int level) { Heap::ExecutionContext *scope = static_cast<ExecutionContext &>(stack[CallData::Context]).d(); @@ -453,6 +517,11 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::ReturnedValue acc = accumulator.asReturnedValue(); Value *stack = reinterpret_cast<Value *>(frame->jsFrame); + if (function->tracingEnabled()) { + for (int i = 0; i < int(function->nFormals); ++i) + traceValue(frame->jsFrame->argument(i), function, i); + } + MOTH_JUMP_TABLE; for (;;) { @@ -511,6 +580,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, auto cc = static_cast<Heap::CallContext *>(stack[CallData::Context].m()); Q_ASSERT(cc->type != QV4::Heap::CallContext::Type_GlobalContext); acc = cc->locals[index].asReturnedValue(); + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadLocal) MOTH_BEGIN_INSTR(StoreLocal) @@ -523,6 +593,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadScopedLocal) auto cc = getScope(stack, scope); acc = cc->locals[index].asReturnedValue(); + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadScopedLocal) MOTH_BEGIN_INSTR(StoreScopedLocal) @@ -547,6 +618,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_IP(); acc = Runtime::method_loadName(engine, name); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadName) MOTH_BEGIN_INSTR(LoadGlobalLookup) @@ -554,6 +626,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->globalGetter(l, engine); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadGlobalLookup) MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) @@ -561,6 +634,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; acc = l->qmlContextPropertyGetter(l, engine, nullptr); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadQmlContextPropertyLookup) MOTH_BEGIN_INSTR(StoreNameStrict) @@ -580,14 +654,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(LoadElement) STORE_IP(); STORE_ACC(); +#if QT_CONFIG(qml_tracing) + acc = Runtime::method_loadElement_traced(engine, STACK_VALUE(base), accumulator, function->traceInfo(traceSlot)); + traceValue(acc, function, traceSlot); +#else + Q_UNUSED(traceSlot); acc = Runtime::method_loadElement(engine, STACK_VALUE(base), accumulator); +#endif CHECK_EXCEPTION; MOTH_END_INSTR(LoadElement) MOTH_BEGIN_INSTR(StoreElement) STORE_IP(); STORE_ACC(); +#if QT_CONFIG(qml_tracing) + Runtime::method_storeElement_traced(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator, function->traceInfo(traceSlot)); +#else + Q_UNUSED(traceSlot); Runtime::method_storeElement(engine, STACK_VALUE(base), STACK_VALUE(index), accumulator); +#endif CHECK_EXCEPTION; MOTH_END_INSTR(StoreElement) @@ -596,6 +681,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::method_loadProperty(engine, accumulator, name); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(LoadProperty) MOTH_BEGIN_INSTR(GetLookup) @@ -614,6 +700,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = l->getter(l, engine, accumulator); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(GetLookup) MOTH_BEGIN_INSTR(StoreProperty) @@ -687,6 +774,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, Value undef = Value::undefinedValue(); acc = static_cast<const FunctionObject &>(func).call(&undef, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallValue) MOTH_BEGIN_INSTR(CallWithReceiver) @@ -698,12 +786,14 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, } acc = static_cast<const FunctionObject &>(func).call(stack + thisObject, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithReceiver) MOTH_BEGIN_INSTR(CallProperty) STORE_IP(); acc = Runtime::method_callProperty(engine, stack + base, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallProperty) MOTH_BEGIN_INSTR(CallPropertyLookup) @@ -731,42 +821,49 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, acc = static_cast<FunctionObject &>(f).call(stack + base, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPropertyLookup) MOTH_BEGIN_INSTR(CallElement) STORE_IP(); acc = Runtime::method_callElement(engine, stack + base, STACK_VALUE(index), stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallElement) MOTH_BEGIN_INSTR(CallName) STORE_IP(); acc = Runtime::method_callName(engine, name, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallName) MOTH_BEGIN_INSTR(CallPossiblyDirectEval) STORE_IP(); acc = Runtime::method_callPossiblyDirectEval(engine, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallPossiblyDirectEval) MOTH_BEGIN_INSTR(CallGlobalLookup) STORE_IP(); acc = Runtime::method_callGlobalLookup(engine, index, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallGlobalLookup) MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) STORE_IP(); acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallQmlContextPropertyLookup) MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(CallWithSpread) MOTH_BEGIN_INSTR(TailCall) @@ -1004,23 +1101,25 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_END_INSTR(Jump) MOTH_BEGIN_INSTR(JumpTrue) - if (Q_LIKELY(ACC.integerCompatible())) { - if (ACC.int_32()) - code += offset; - } else { - if (ACC.toBoolean()) - code += offset; - } + bool takeJump; + if (Q_LIKELY(ACC.integerCompatible())) + takeJump = ACC.int_32(); + else + takeJump = ACC.toBoolean(); + traceJumpTakesTruePath(takeJump, function, traceSlot); + if (takeJump) + code += offset; MOTH_END_INSTR(JumpTrue) MOTH_BEGIN_INSTR(JumpFalse) - if (Q_LIKELY(ACC.integerCompatible())) { - if (!ACC.int_32()) - code += offset; - } else { - if (!ACC.toBoolean()) - code += offset; - } + bool takeJump; + if (Q_LIKELY(ACC.integerCompatible())) + takeJump = !ACC.int_32(); + else + takeJump = !ACC.toBoolean(); + traceJumpTakesTruePath(!takeJump, function, traceSlot); + if (takeJump) + code += offset; MOTH_END_INSTR(JumpFalse) MOTH_BEGIN_INSTR(JumpNoException) @@ -1189,14 +1288,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, int a = ACC.int_32(); if (a == 0 || a == std::numeric_limits<int>::min()) { acc = Encode(-static_cast<double>(a)); + traceDoubleValue(function, traceSlot); } else { - acc = sub_int32(0, ACC.int_32()); + acc = sub_int32(0, ACC.int_32(), function->traceInfo(traceSlot)); } } else if (ACC.isDouble()) { acc ^= (1ull << 63); // simply flip sign bit + traceDoubleValue(function, traceSlot); } else { acc = Encode(-ACC.toNumberImpl()); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(UMinus) @@ -1207,49 +1309,57 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Increment) if (Q_LIKELY(ACC.integerCompatible())) { - acc = add_int32(ACC.int_32(), 1); + acc = add_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() + 1.); + traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() + 1.); CHECK_EXCEPTION; + traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Increment) MOTH_BEGIN_INSTR(Decrement) if (Q_LIKELY(ACC.integerCompatible())) { - acc = sub_int32(ACC.int_32(), 1); + acc = sub_int32(ACC.int_32(), 1, function->traceInfo(traceSlot)); } else if (ACC.isDouble()) { acc = QV4::Encode(ACC.doubleValue() - 1.); + traceDoubleValue(function, traceSlot); } else { acc = Encode(ACC.toNumberImpl() - 1.); CHECK_EXCEPTION; + traceDoubleValue(function, traceSlot); } MOTH_END_INSTR(Decrement) MOTH_BEGIN_INSTR(Add) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = add_int32(left.int_32(), ACC.int_32()); + acc = add_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() + ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_add(engine, left, accumulator); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Add) MOTH_BEGIN_INSTR(Sub) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = sub_int32(left.int_32(), ACC.int_32()); + acc = sub_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() - ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_sub(left, accumulator); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Sub) @@ -1266,13 +1376,15 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, MOTH_BEGIN_INSTR(Mul) const Value left = STACK_VALUE(lhs); if (Q_LIKELY(Value::integerCompatible(left, ACC))) { - acc = mul_int32(left.int_32(), ACC.int_32()); + acc = mul_int32(left.int_32(), ACC.int_32(), function->traceInfo(traceSlot)); } else if (left.isNumber() && ACC.isNumber()) { acc = Encode(left.asDouble() * ACC.asDouble()); + traceDoubleValue(function, traceSlot); } else { STORE_ACC(); acc = Runtime::method_mul(left, accumulator); CHECK_EXCEPTION; + traceOtherValue(function, traceSlot); } MOTH_END_INSTR(Mul) @@ -1286,6 +1398,7 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, STORE_ACC(); acc = Runtime::method_mod(STACK_VALUE(lhs), accumulator); CHECK_EXCEPTION; + traceValue(acc, function, traceSlot); MOTH_END_INSTR(Mod) MOTH_BEGIN_INSTR(BitAnd) diff --git a/src/qml/parser/parser.pri b/src/qml/parser/parser.pri index adab4ef9a2..2c0175c94b 100644 --- a/src/qml/parser/parser.pri +++ b/src/qml/parser/parser.pri @@ -8,7 +8,8 @@ HEADERS += \ $$PWD/qqmljsglobal_p.h \ $$PWD/qqmljskeywords_p.h \ $$PWD/qqmljsengine_p.h \ - $$PWD/qqmljsglobal_p.h + $$PWD/qqmljsglobal_p.h \ + $$PWD/qqmljssourcelocation_p.h SOURCES += \ $$PWD/qqmljsast.cpp \ diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 8ae51a795f..0c947b541b 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -249,7 +249,7 @@ #include <QtCore/qlist.h> #include <QtCore/qstring.h> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -486,7 +486,7 @@ protected: using namespace QQmlJS; -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE void Parser::reallocateStack() { @@ -4476,12 +4476,12 @@ ExportSpecifier: IdentifierName T_AS IdentifierName; return false; } -QT_QML_END_NAMESPACE +QT_END_NAMESPACE ./ /: -QT_QML_END_NAMESPACE +QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp index 54a1200493..e5817ab763 100644 --- a/src/qml/parser/qqmljsast.cpp +++ b/src/qml/parser/qqmljsast.cpp @@ -41,7 +41,7 @@ #include "qqmljsastvisitor_p.h" -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { @@ -1474,6 +1474,6 @@ LeftHandSideExpression *LeftHandSideExpression::leftHandSideExpressionCast() } } // namespace QQmlJS::AST -QT_QML_END_NAMESPACE +QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index e84c62af2f..b81553776d 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -57,7 +57,7 @@ #include <QtCore/qstring.h> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE #define QQMLJS_DECLARE_AST_NODE(name) \ enum { K = Kind_##name }; @@ -3395,6 +3395,6 @@ public: -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif diff --git a/src/qml/parser/qqmljsastfwd_p.h b/src/qml/parser/qqmljsastfwd_p.h index 996264db59..e9caa918d5 100644 --- a/src/qml/parser/qqmljsastfwd_p.h +++ b/src/qml/parser/qqmljsastfwd_p.h @@ -41,6 +41,7 @@ #define QQMLJSAST_FWD_P_H #include "qqmljsglobal_p.h" +#include "qqmljssourcelocation_p.h" #include <QtCore/qglobal.h> @@ -55,31 +56,10 @@ // We mean it. // -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { -class SourceLocation -{ -public: - explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) - : offset(offset), length(length), - startLine(line), startColumn(column) - { } - - bool isValid() const { return length != 0; } - - quint32 begin() const { return offset; } - quint32 end() const { return offset + length; } - -// attributes - // ### encode - quint32 offset; - quint32 length; - quint32 startLine; - quint32 startColumn; -}; - class Visitor; class Node; class ExpressionNode; @@ -201,6 +181,6 @@ class UiEnumMemberList; } } // namespace AST -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp index 666623eecc..5ecac36423 100644 --- a/src/qml/parser/qqmljsastvisitor.cpp +++ b/src/qml/parser/qqmljsastvisitor.cpp @@ -39,7 +39,7 @@ #include "qqmljsastvisitor_p.h" -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { @@ -53,4 +53,4 @@ Visitor::~Visitor() } } // namespace QQmlJS::AST -QT_QML_END_NAMESPACE +QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h index 9c69f88e0c..9115449a46 100644 --- a/src/qml/parser/qqmljsastvisitor_p.h +++ b/src/qml/parser/qqmljsastvisitor_p.h @@ -54,7 +54,7 @@ #include "qqmljsastfwd_p.h" #include "qqmljsglobal_p.h" -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { namespace AST { @@ -412,6 +412,6 @@ protected: } } // namespace AST -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif // QQMLJSASTVISITOR_P_H diff --git a/src/qml/parser/qqmljsengine_p.cpp b/src/qml/parser/qqmljsengine_p.cpp index 97ce6ebea3..bb27f3992e 100644 --- a/src/qml/parser/qqmljsengine_p.cpp +++ b/src/qml/parser/qqmljsengine_p.cpp @@ -44,7 +44,7 @@ #include <QtCore/qhash.h> #include <QtCore/qdebug.h> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -155,4 +155,4 @@ QStringRef Engine::newStringRef(const QChar *chars, int size) } // end of namespace QQmlJS -QT_QML_END_NAMESPACE +QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsengine_p.h b/src/qml/parser/qqmljsengine_p.h index 1de907d296..b7f7da9478 100644 --- a/src/qml/parser/qqmljsengine_p.h +++ b/src/qml/parser/qqmljsengine_p.h @@ -52,13 +52,13 @@ // #include "qqmljsglobal_p.h" -#include "qqmljsastfwd_p.h" #include "qqmljsmemorypool_p.h" +#include "qqmljssourcelocation_p.h" #include <QtCore/qstring.h> #include <QtCore/qset.h> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -95,7 +95,7 @@ public: class QML_PARSER_EXPORT DiagnosticMessage { public: - enum Kind { Warning, Error }; + enum Kind { Hint, Warning, Error }; DiagnosticMessage() {} @@ -150,6 +150,6 @@ double integerFromString(const char *buf, int size, int radix); } // end of namespace QQmlJS -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif // QQMLJSENGINE_P_H diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h index 0e195994b4..bf8155c6ec 100644 --- a/src/qml/parser/qqmljsglobal_p.h +++ b/src/qml/parser/qqmljsglobal_p.h @@ -53,8 +53,6 @@ #include <QtCore/qglobal.h> #ifdef QT_CREATOR -# define QT_QML_BEGIN_NAMESPACE -# define QT_QML_END_NAMESPACE # ifdef QDECLARATIVEJS_BUILD_DIR # define QML_PARSER_EXPORT Q_DECL_EXPORT @@ -65,8 +63,6 @@ # endif // QQMLJS_BUILD_DIR #else // !QT_CREATOR -# define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE -# define QT_QML_END_NAMESPACE QT_END_NAMESPACE # ifndef QT_STATIC # if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) // QmlDevTools is a static library diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index b0a4951ece..96b3709162 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -53,7 +53,7 @@ #include "qqmljslexer_p.h" -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -918,6 +918,6 @@ int Lexer::classify(const QChar *s, int n, int parseModeFlags) { } // namespace QQmlJS -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif // QQMLJSKEYWORDS_P_H diff --git a/src/qml/parser/qqmljslexer_p.h b/src/qml/parser/qqmljslexer_p.h index 03f33f6e06..51152bfd6e 100644 --- a/src/qml/parser/qqmljslexer_p.h +++ b/src/qml/parser/qqmljslexer_p.h @@ -57,7 +57,7 @@ #include <QtCore/qstring.h> #include <QtCore/qstack.h> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -257,6 +257,6 @@ private: } // end of namespace QQmlJS -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif // LEXER_H diff --git a/src/qml/parser/qqmljsmemorypool_p.h b/src/qml/parser/qqmljsmemorypool_p.h index afd0f809da..e7b1f46414 100644 --- a/src/qml/parser/qqmljsmemorypool_p.h +++ b/src/qml/parser/qqmljsmemorypool_p.h @@ -59,7 +59,7 @@ #include <cstring> -QT_QML_BEGIN_NAMESPACE +QT_BEGIN_NAMESPACE namespace QQmlJS { @@ -104,6 +104,8 @@ public: } template <typename Tp> Tp *New() { return new (this->allocate(sizeof(Tp))) Tp(); } + template <typename Tp, typename... Ta> Tp *New(Ta... args) + { return new (this->allocate(sizeof(Tp))) Tp(args...); } QStringRef newString(const QString &string) { strings.append(new QString(string)); @@ -172,8 +174,83 @@ public: void operator delete(void *, MemoryPool *) {} }; +template <typename T> +class FixedPoolArray +{ + T *data; + int count = 0; + +public: + FixedPoolArray() + : data(nullptr) + {} + + FixedPoolArray(MemoryPool *pool, int size) + { allocate(pool, size); } + + void allocate(MemoryPool *pool, int size) + { + count = size; + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + } + + void allocate(MemoryPool *pool, const QVector<T> &vector) + { + count = vector.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + + if (QTypeInfo<T>::isComplex) { + for (int i = 0; i < count; ++i) + new (data + i) T(vector.at(i)); + } else { + memcpy(data, static_cast<const void*>(vector.constData()), count * sizeof(T)); + } + } + + template <typename Container> + void allocate(MemoryPool *pool, const Container &container) + { + count = container.count(); + data = reinterpret_cast<T*>(pool->allocate(count * sizeof(T))); + typename Container::ConstIterator it = container.constBegin(); + for (int i = 0; i < count; ++i) + new (data + i) T(*it++); + } + + int size() const + { return count; } + + const T &at(int index) const { + Q_ASSERT(index >= 0 && index < count); + return data[index]; + } + + T &at(int index) { + Q_ASSERT(index >= 0 && index < count); + return data[index]; + } + + T &operator[](int index) { + return at(index); + } + + + int indexOf(const T &value) const { + for (int i = 0; i < count; ++i) + if (data[i] == value) + return i; + return -1; + } + + const T *begin() const { return data; } + const T *end() const { return data + count; } + + T *begin() { return data; } + T *end() { return data + count; } +}; + } // namespace QQmlJS -QT_QML_END_NAMESPACE +QT_END_NAMESPACE #endif diff --git a/src/qml/parser/qqmljssourcelocation_p.h b/src/qml/parser/qqmljssourcelocation_p.h new file mode 100644 index 0000000000..d76e701d49 --- /dev/null +++ b/src/qml/parser/qqmljssourcelocation_p.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLJSSOURCELOCATION_P_H +#define QQMLJSSOURCELOCATION_P_H + +#include "qqmljsglobal_p.h" + +#include <QtCore/qglobal.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +namespace QQmlJS { namespace AST { + +class SourceLocation +{ +public: + explicit SourceLocation(quint32 offset = 0, quint32 length = 0, quint32 line = 0, quint32 column = 0) + : offset(offset), length(length), + startLine(line), startColumn(column) + { } + + bool isValid() const { return length != 0; } + + quint32 begin() const { return offset; } + quint32 end() const { return offset + length; } + +// attributes + // ### encode + quint32 offset; + quint32 length; + quint32 startLine; + quint32 startColumn; +}; + +} } // namespace AST + +QT_END_NAMESPACE + +#endif diff --git a/src/qml/qml.pro b/src/qml/qml.pro index db59140f06..94717a8f43 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -73,6 +73,7 @@ include(jsruntime/jsruntime.pri) include(jit/jit.pri) include(qml/qml.pri) include(debugger/debugger.pri) +include(qmldirparser/qmldirparser.pri) qtConfig(qml-animation) { include(animations/animations.pri) } diff --git a/src/qml/qml/qml.pri b/src/qml/qml/qml.pri index 6d69294c17..ca13ce9211 100644 --- a/src/qml/qml/qml.pri +++ b/src/qml/qml/qml.pri @@ -18,7 +18,6 @@ SOURCES += \ $$PWD/qqmlparserstatus.cpp \ $$PWD/qqmltypeloader.cpp \ $$PWD/qqmlinfo.cpp \ - $$PWD/qqmlerror.cpp \ $$PWD/qqmlvaluetype.cpp \ $$PWD/qqmlcleanup.cpp \ $$PWD/qqmlpropertycache.cpp \ @@ -44,7 +43,6 @@ SOURCES += \ $$PWD/qqmltypewrapper.cpp \ $$PWD/qqmlfileselector.cpp \ $$PWD/qqmlobjectcreator.cpp \ - $$PWD/qqmldirparser.cpp \ $$PWD/qqmldelayedcallqueue.cpp \ $$PWD/qqmlloggingcategory.cpp @@ -81,7 +79,6 @@ HEADERS += \ $$PWD/qqmllist.h \ $$PWD/qqmllist_p.h \ $$PWD/qqmldata_p.h \ - $$PWD/qqmlerror.h \ $$PWD/qqmlvaluetype_p.h \ $$PWD/qqmlcleanup_p.h \ $$PWD/qqmlpropertycache_p.h \ @@ -112,7 +109,6 @@ HEADERS += \ $$PWD/qqmlfileselector_p.h \ $$PWD/qqmlfileselector.h \ $$PWD/qqmlobjectcreator_p.h \ - $$PWD/qqmldirparser_p.h \ $$PWD/qqmldelayedcallqueue_p.h \ $$PWD/qqmlloggingcategory_p.h diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 05a9f70247..bf9330856d 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -584,7 +584,6 @@ namespace QtQml { const QMetaObject *, bool create); #ifndef Q_QDOC } -#endif QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wheader-hygiene") @@ -594,6 +593,8 @@ using namespace QtQml; QT_WARNING_POP +#endif // Q_QDOC + //The C++ version of protected namespaces in qmldir Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion); Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor); diff --git a/src/qml/qml/qqmlabstracturlinterceptor.h b/src/qml/qml/qqmlabstracturlinterceptor.h index 665b37fb3a..af231f51b2 100644 --- a/src/qml/qml/qqmlabstracturlinterceptor.h +++ b/src/qml/qml/qqmlabstracturlinterceptor.h @@ -55,8 +55,8 @@ public: UrlString = 0x1000 }; - QQmlAbstractUrlInterceptor() {} - virtual ~QQmlAbstractUrlInterceptor() {} + QQmlAbstractUrlInterceptor() = default; + virtual ~QQmlAbstractUrlInterceptor() = default; virtual QUrl intercept(const QUrl &path, DataType type) = 0; }; diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 4795170bed..6cf6828832 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -74,7 +74,6 @@ public: void loadTranslations(const QUrl &rootFile); void finishLoad(QQmlComponent *component); QList<QObject *> objects; - QObject *appObj; #if QT_CONFIG(translation) QList<QTranslator *> translators; diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index 7f0442d034..b164517011 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -314,7 +314,7 @@ protected: break; default: if (const QV4::QQmlValueTypeWrapper *vtw = result.as<const QV4::QQmlValueTypeWrapper>()) { - if (vtw->d()->valueType->typeId == pd->propType()) { + if (vtw->d()->valueType->metaType.id() == pd->propType()) { return vtw->write(m_target.data(), pd->coreIndex()); } } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index 5841a480fc..0a26ed89cc 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -661,6 +661,10 @@ The following functions are also on the Qt object. /*! \qmlmethod object Qt::include(string url, jsobject callback) +\deprecated + +This method should not be used. Use ECMAScript modules instead and the native +JavaScript \c import and \c export statements instead. Includes another JavaScript file. This method can only be used from within JavaScript files, and not regular QML files. diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 8a9581fb35..d05c945ae4 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -64,7 +64,6 @@ #include "qqmlproperty_p.h" #include "qqmlpropertycache_p.h" #include "qqmlmetatype_p.h" -#include "qqmldirparser_p.h" #include <private/qintrusivelist_p.h> #include <private/qrecyclepool_p.h> #include <private/qfieldlist_p.h> @@ -80,6 +79,7 @@ #include <private/qv8engine_p.h> #include <private/qjsengine_p.h> +#include <private/qqmldirparser_p.h> QT_BEGIN_NAMESPACE diff --git a/src/qml/qml/qqmlexpression.cpp b/src/qml/qml/qqmlexpression.cpp index 0c1ffbf3a0..ac2629979f 100644 --- a/src/qml/qml/qqmlexpression.cpp +++ b/src/qml/qml/qqmlexpression.cpp @@ -46,6 +46,7 @@ #include "qqmlscriptstring_p.h" #include "qqmlbinding_p.h" #include <private/qv8engine_p.h> +#include <private/qqmlsourcecoordinate_p.h> #include <QtCore/qdebug.h> diff --git a/src/qml/qml/qqmlextensioninterface.h b/src/qml/qml/qqmlextensioninterface.h index c2d20ef0a3..d2eb79c5c9 100644 --- a/src/qml/qml/qqmlextensioninterface.h +++ b/src/qml/qml/qqmlextensioninterface.h @@ -51,14 +51,14 @@ class QQmlEngine; class Q_QML_EXPORT QQmlTypesExtensionInterface { public: - virtual ~QQmlTypesExtensionInterface() {} + virtual ~QQmlTypesExtensionInterface() = default; virtual void registerTypes(const char *uri) = 0; }; class Q_QML_EXPORT QQmlExtensionInterface : public QQmlTypesExtensionInterface { public: - ~QQmlExtensionInterface() override {} + ~QQmlExtensionInterface() override = default; virtual void initializeEngine(QQmlEngine *engine, const char *uri) = 0; }; diff --git a/src/qml/qml/qqmlfile.cpp b/src/qml/qml/qqmlfile.cpp index 99031e1e74..465a342129 100644 --- a/src/qml/qml/qqmlfile.cpp +++ b/src/qml/qml/qqmlfile.cpp @@ -64,6 +64,7 @@ static char file_string[] = "file"; #if defined(Q_OS_ANDROID) static char assets_string[] = "assets"; +static char content_string[] = "content"; #endif class QQmlFilePrivate; @@ -452,6 +453,8 @@ bool QQmlFile::isSynchronous(const QUrl &url) #if defined(Q_OS_ANDROID) } else if (scheme.length() == 6 && 0 == scheme.compare(QLatin1String(assets_string), Qt::CaseInsensitive)) { return true; + } else if (scheme.length() == 7 && 0 == scheme.compare(QLatin1String(content_string), Qt::CaseInsensitive)) { + return true; #endif } else { @@ -492,7 +495,10 @@ bool QQmlFile::isSynchronous(const QString &url) return url.length() >= 8 /* assets:/ */ && url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) && url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/'); - + } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) { + return url.length() >= 9 /* content:/ */ && + url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) && + url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); } #endif @@ -556,7 +562,10 @@ bool QQmlFile::isLocalFile(const QString &url) return url.length() >= 8 /* assets:/ */ && url.startsWith(QLatin1String(assets_string), Qt::CaseInsensitive) && url[6] == QLatin1Char(':') && url[7] == QLatin1Char('/'); - + } else if (f == QLatin1Char('c') || f == QLatin1Char('C')) { + return url.length() >= 9 /* content:/ */ && + url.startsWith(QLatin1String(content_string), Qt::CaseInsensitive) && + url[7] == QLatin1Char(':') && url[8] == QLatin1Char('/'); } #endif @@ -580,6 +589,8 @@ QString QQmlFile::urlToLocalFileOrQrc(const QUrl& url) if (url.authority().isEmpty()) return url.toString(); return QString(); + } else if (url.scheme().compare(QLatin1String("content"), Qt::CaseInsensitive) == 0) { + return url.toString(); } #endif @@ -618,6 +629,8 @@ QString QQmlFile::urlToLocalFileOrQrc(const QString& url) #if defined(Q_OS_ANDROID) else if (url.startsWith(QLatin1String("assets:"), Qt::CaseInsensitive)) { return url; + } else if (url.startsWith(QLatin1String("content:"), Qt::CaseInsensitive)) { + return url; } #endif diff --git a/src/qml/qml/qqmlglobal_p.h b/src/qml/qml/qqmlglobal_p.h index 302fdd56c4..818537560c 100644 --- a/src/qml/qml/qqmlglobal_p.h +++ b/src/qml/qml/qqmlglobal_p.h @@ -174,16 +174,6 @@ T qmlobject_cast(QObject *object) return 0; } -inline quint16 qmlSourceCoordinate(int n) -{ - return (n > 0 && n <= static_cast<int>(USHRT_MAX)) ? static_cast<quint16>(n) : 0; -} - -inline int qmlSourceCoordinate(quint16 n) -{ - return (n == 0) ? -1 : static_cast<int>(n); -} - #define IS_SIGNAL_CONNECTED(Sender, SenderType, Name, Arguments) \ do { \ QObject *sender = (Sender); \ diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index f801e9aeba..a8ec5d18f8 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -1102,13 +1102,6 @@ QString QQmlType::noCreationReason() const return d->extraData.cd->noCreationReason; } -int QQmlType::createSize() const -{ - if (!d || d->regType != CppType) - return 0; - return d->extraData.cd->allocationSize; -} - bool QQmlType::isCreatable() const { return d && d->regType == CppType && d->extraData.cd->newFunc; @@ -2172,11 +2165,14 @@ int QQmlMetaType::attachedPropertiesFuncId(QQmlEnginePrivate *engine, const QMet QMutexLocker lock(metaTypeDataLock()); QQmlMetaTypeData *data = metaTypeData(); - QQmlType type(data->metaObjectToType.value(mo)); - if (type.attachedPropertiesFunction(engine)) - return type.attachedPropertiesId(engine); - else - return -1; + for (auto it = data->metaObjectToType.constFind(mo), end = data->metaObjectToType.constEnd(); + it != end && it.key() == mo; ++it) { + const QQmlType type(it.value()); + if (type.attachedPropertiesFunction(engine)) + return type.attachedPropertiesId(engine); + } + + return -1; } QQmlAttachedPropertiesFunc QQmlMetaType::attachedPropertiesFuncById(QQmlEnginePrivate *engine, int id) diff --git a/src/qml/qml/qqmlmetatype_p.h b/src/qml/qml/qqmlmetatype_p.h index 8256212207..3ad2de7bb3 100644 --- a/src/qml/qml/qqmlmetatype_p.h +++ b/src/qml/qml/qqmlmetatype_p.h @@ -196,8 +196,6 @@ public: typedef void (*CreateFunc)(void *); CreateFunc createFunction() const; - int createSize() const; - QQmlCustomParser *customParser() const; bool isCreatable() const; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 5af658194f..c36b3ed386 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -379,6 +379,28 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } } + auto assertOrNull = [&](bool ok) + { + Q_ASSERT(ok || binding->type == QV4::CompiledData::Binding::Type_Null); + Q_UNUSED(ok); + }; + + auto assertType = [&](QV4::CompiledData::Binding::ValueType type) + { + Q_ASSERT(binding->type == type || binding->type == QV4::CompiledData::Binding::Type_Null); + Q_UNUSED(type); + }; + + if (property->isQObject()) { + if (binding->type == QV4::CompiledData::Binding::Type_Null) { + QObject *value = nullptr; + const bool ok = property->writeProperty(_qobject, &value, propertyWriteFlags); + Q_ASSERT(ok); + Q_UNUSED(ok); + return; + } + } + switch (propertyType) { case QMetaType::QVariant: { if (binding->type == QV4::CompiledData::Binding::Type_Number) { @@ -406,6 +428,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const QVariant value(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); } + } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + if (property->isVarProperty()) { + _vmeMetaObject->setVMEProperty(property->coreIndex(), QV4::Value::nullValue()); + } else { + QVariant nullValue = QVariant::fromValue(nullptr); + property->writeProperty(_qobject, &nullValue, propertyWriteFlags); + } } else { QString stringValue = binding->valueAsString(compilationUnit.data()); if (property->isVarProperty()) { @@ -419,25 +448,25 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } break; case QVariant::String: { - Q_ASSERT(binding->evaluatesToString()); + assertOrNull(binding->evaluatesToString()); QString value = binding->valueAsString(compilationUnit.data()); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::StringList: { - Q_ASSERT(binding->evaluatesToString()); + assertOrNull(binding->evaluatesToString()); QStringList value(binding->valueAsString(compilationUnit.data())); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::ByteArray: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + assertType(QV4::CompiledData::Binding::Type_String); QByteArray value(binding->valueAsString(compilationUnit.data()).toUtf8()); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Url: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + assertType(QV4::CompiledData::Binding::Type_String); QString string = binding->valueAsString(compilationUnit.data()); // Encoded dir-separators defeat QUrl processing - decode them first string.replace(QLatin1String("%2f"), QLatin1String("/"), Qt::CaseInsensitive); @@ -449,7 +478,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } break; case QVariant::UInt: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(compilationUnit->constants); uint value = uint(d); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -457,7 +486,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } break; case QVariant::Int: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); double d = binding->valueAsNumber(compilationUnit->constants); int value = int(d); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -465,13 +494,13 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const } break; case QMetaType::Float: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); float value = float(binding->valueAsNumber(compilationUnit->constants)); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Double: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); double value = binding->valueAsNumber(compilationUnit->constants); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -479,7 +508,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::Color: { bool ok = false; uint colorValue = QQmlStringConverters::rgbaFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); struct { void *data[4]; } buffer; if (QQml_valueTypeProvider()->storeValueType(property->propType(), &colorValue, &buffer, sizeof(buffer))) { property->writeProperty(_qobject, &buffer, propertyWriteFlags); @@ -490,14 +519,14 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::Date: { bool ok = false; QDate value = QQmlStringConverters::dateFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Time: { bool ok = false; QTime value = QQmlStringConverters::timeFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; @@ -510,7 +539,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const const int msecsSinceStartOfDay = value.time().msecsSinceStartOfDay(); value = QDateTime(QDate::fromJulianDay(date), QTime::fromMSecsSinceStartOfDay(msecsSinceStartOfDay)); } - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; @@ -518,47 +547,47 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const case QVariant::Point: { bool ok = false; QPoint value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok).toPoint(); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::PointF: { bool ok = false; QPointF value = QQmlStringConverters::pointFFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Size: { bool ok = false; QSize value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok).toSize(); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::SizeF: { bool ok = false; QSizeF value = QQmlStringConverters::sizeFFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Rect: { bool ok = false; QRect value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok).toRect(); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::RectF: { bool ok = false; QRectF value = QQmlStringConverters::rectFFromString(binding->valueAsString(compilationUnit.data()), &ok); - Q_ASSERT(ok); + assertOrNull(ok); property->writeProperty(_qobject, &value, propertyWriteFlags); } break; case QVariant::Bool: { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); + assertType(QV4::CompiledData::Binding::Type_Boolean); bool value = binding->valueAsBoolean(); property->writeProperty(_qobject, &value, propertyWriteFlags); } @@ -569,7 +598,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float yp; } vec; bool ok = QQmlStringConverters::createFromString(QMetaType::QVector2D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); - Q_ASSERT(ok); + assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); } @@ -581,7 +610,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float zy; } vec; bool ok = QQmlStringConverters::createFromString(QMetaType::QVector3D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); - Q_ASSERT(ok); + assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); } @@ -594,7 +623,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float wp; } vec; bool ok = QQmlStringConverters::createFromString(QMetaType::QVector4D, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); - Q_ASSERT(ok); + assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); } @@ -607,37 +636,37 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const float zp; } vec; bool ok = QQmlStringConverters::createFromString(QMetaType::QQuaternion, binding->valueAsString(compilationUnit.data()), &vec, sizeof(vec)); - Q_ASSERT(ok); + assertOrNull(ok); Q_UNUSED(ok); property->writeProperty(_qobject, &vec, propertyWriteFlags); } break; case QVariant::RegExp: - Q_ASSERT(!"not possible"); + assertOrNull(!"not possible"); break; default: { // generate single literal value assignment to a list property if required if (property->propType() == qMetaTypeId<QList<qreal> >()) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); QList<qreal> value; value.append(binding->valueAsNumber(compilationUnit->constants)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<int> >()) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Number); + assertType(QV4::CompiledData::Binding::Type_Number); double n = binding->valueAsNumber(compilationUnit->constants); QList<int> value; value.append(int(n)); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<bool> >()) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_Boolean); + assertType(QV4::CompiledData::Binding::Type_Boolean); QList<bool> value; value.append(binding->valueAsBoolean()); property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<QUrl> >()) { - Q_ASSERT(binding->type == QV4::CompiledData::Binding::Type_String); + assertType(QV4::CompiledData::Binding::Type_String); QString urlString = binding->valueAsString(compilationUnit.data()); QUrl u = urlString.isEmpty() ? QUrl() : compilationUnit->finalUrl().resolved(QUrl(urlString)); @@ -646,7 +675,7 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const property->writeProperty(_qobject, &value, propertyWriteFlags); break; } else if (property->propType() == qMetaTypeId<QList<QString> >()) { - Q_ASSERT(binding->evaluatesToString()); + assertOrNull(binding->evaluatesToString()); QList<QString> value; value.append(binding->valueAsString(compilationUnit.data())); property->writeProperty(_qobject, &value, propertyWriteFlags); @@ -661,6 +690,8 @@ void QQmlObjectCreator::setPropertyValue(const QQmlPropertyData *property, const value = QJSValue(int(n)); } else value = QJSValue(n); + } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + value = QJSValue::NullValue; } else { value = QJSValue(binding->valueAsString(compilationUnit.data())); } diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 20d5c76029..73bfd7bbaa 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -898,6 +898,17 @@ struct StaticQtMetaObject : public QObject { return &staticQtMetaObject; } }; +static bool isNamedEnumeratorInScope(const QMetaObject *resolvedMetaObject, const QByteArray &scope, + const QByteArray &name) +{ + for (int i = resolvedMetaObject->enumeratorCount() - 1; i >= 0; --i) { + QMetaEnum m = resolvedMetaObject->enumerator(i); + if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) + return true; + } + return false; +} + static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scopedName) { QByteArray scope; @@ -909,52 +920,21 @@ static bool isNamedEnumerator(const QMetaObject *metaObj, const QByteArray &scop } else { name = scopedName; } - const QMetaObject *meta; - if (scope == "Qt") - meta = StaticQtMetaObject::get(); - else - meta = metaObj; - for (int i = meta->enumeratorCount() - 1; i >= 0; --i) { - QMetaEnum m = meta->enumerator(i); - if ((m.name() == name) && (scope.isEmpty() || (m.scope() == scope))) - return true; - } - return false; -} - -static bool passTypeAsInt(int type) -{ - // We should not encounter the unknown type here. - // In order to check that we need extra information. - Q_ASSERT(type != QMetaType::UnknownType); - const QMetaType::TypeFlags flags = QMetaType::typeFlags(type); + if (scope == "Qt") + return isNamedEnumeratorInScope(StaticQtMetaObject::get(), scope, name); - // Cast enumerations to int. - if (flags & QMetaType::IsEnumeration) + if (isNamedEnumeratorInScope(metaObj, scope, name)) return true; - // Qt builtins can be handled as they are. - if (type < int(QMetaType::User)) - return false; - - // Pointers to QObjects and QGadgets, and QGadgets themselves can be handled as they are. - if (flags & (QMetaType::PointerToQObject | QMetaType::PointerToGadget | QMetaType::IsGadget)) - return false; - - // If it wasn't declared as metatype, better don't touch it. - if (!(flags & QMetaType::WasDeclaredAsMetaType)) - return false; - - // If it needs construction or destruction (that is, it is a structured type), - // pass as original type. - if (flags & (QMetaType::NeedsConstruction | QMetaType::NeedsDestruction)) - return false; + if (metaObj->d.relatedMetaObjects && !scope.isEmpty()) { + for (auto related = metaObj->d.relatedMetaObjects; *related; ++related) { + if (isNamedEnumeratorInScope(*related, scope, name)) + return true; + } + } - // A single value that's not a pointer to a QObject or QGadget, not a builtin type, was declared - // as meta type, but we don't know it as an enumeration (although it probably is one). - // Pass as int if it fits into an int. - return QMetaType::sizeOf(type) <= int(sizeof(int)); + return false; } QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) @@ -1658,14 +1638,18 @@ int QQmlMetaObject::methodReturnType(const QQmlPropertyData &data, QByteArray *u propTypeName = m.typeName(); } - if (type == QMetaType::UnknownType) { + if (QMetaType::sizeOf(type) <= int(sizeof(int))) { + if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) + return QMetaType::Int; + if (isNamedEnumerator(metaObject(), propTypeName)) - type = QVariant::Int; - else if (unknownTypeError) - *unknownTypeError = propTypeName; - } else if (passTypeAsInt(type)) { - type = QVariant::Int; - } + return QMetaType::Int; + + if (type == QMetaType::UnknownType) { + if (unknownTypeError) + *unknownTypeError = propTypeName; + } + } // else we know that it's a known type, as sizeOf(UnknownType) == 0 return type; } @@ -1704,18 +1688,23 @@ int *QQmlMetaObject::methodParameterTypes(int index, ArgTypeStorage *argStorage, for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - if (type == QMetaType::UnknownType) { + + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); if (isNamedEnumerator(metaObject, argTypeNames.at(ii))) { - type = QVariant::Int; - } else { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType){ if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); return nullptr; } - } else if (passTypeAsInt(type)) { - type = QVariant::Int; + } args->arguments[ii + 1] = type; } @@ -1741,18 +1730,21 @@ int *QQmlMetaObject::methodParameterTypes(const QMetaMethod &m, ArgTypeStorage * for (int ii = 0; ii < argc; ++ii) { int type = m.parameterType(ii); - if (type == QMetaType::UnknownType) { + if (QMetaType::sizeOf(type) > int(sizeof(int))) { + // Cannot be passed as int + // We know that it's a known type, as sizeOf(UnknownType) == 0 + } else if (QMetaType::typeFlags(type) & QMetaType::IsEnumeration) { + type = QMetaType::Int; + } else { if (argTypeNames.isEmpty()) argTypeNames = m.parameterTypes(); if (isNamedEnumerator(_m.asT2(), argTypeNames.at(ii))) { - type = QVariant::Int; - } else { + type = QMetaType::Int; + } else if (type == QMetaType::UnknownType) { if (unknownTypeError) *unknownTypeError = argTypeNames.at(ii); return nullptr; } - } else if (passTypeAsInt(type)) { - type = QVariant::Int; } argStorage->operator[](ii + 1) = type; } diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index df6a8f1500..457558fb56 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1821,6 +1821,11 @@ QString QQmlTypeLoader::absoluteFilePath(const QString &path) // assets resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); + } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && + path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { + // content url + QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path)); + return fileInfo.isFile() ? fileInfo.absoluteFilePath() : QString(); } #endif @@ -1878,6 +1883,11 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) // assets resource url QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)); return fileInfo.isFile(); + } else if (path.count() > 8 && path.at(7) == QLatin1Char(':') && path.at(8) == QLatin1Char('/') && + path.startsWith(QLatin1String("content"), Qt::CaseInsensitive)) { + // content url + QFileInfo fileInfo(QQmlFile::urlToLocalFileOrQrc(path + file)); + return fileInfo.isFile(); } #endif @@ -1891,8 +1901,6 @@ bool QQmlTypeLoader::fileExists(const QString &path, const QString &file) if (!fileSet) return false; - QString absoluteFilePath; - bool *value = fileSet->object(file); if (value) { return *value; @@ -1915,7 +1923,7 @@ bool QQmlTypeLoader::directoryExists(const QString &path) bool isResource = path.at(0) == QLatin1Char(':'); #if defined(Q_OS_ANDROID) - isResource = isResource || path.startsWith(QLatin1String("assets:/")); + isResource = isResource || path.startsWith(QLatin1String("assets:/")) || path.startsWith(QLatin1String("content:/")); #endif if (isResource) { diff --git a/src/qml/qml/qqmlvaluetype.cpp b/src/qml/qml/qqmlvaluetype.cpp index cb6a467c6c..6fd0f0d37c 100644 --- a/src/qml/qml/qqmlvaluetype.cpp +++ b/src/qml/qml/qqmlvaluetype.cpp @@ -198,7 +198,6 @@ void QQmlValueTypeFactory::registerValueTypes(const char *uri, int versionMajor, QQmlValueType::QQmlValueType(int typeId, const QMetaObject *gadgetMetaObject) : gadgetPtr(QMetaType::create(typeId)) - , typeId(typeId) , metaType(typeId) { QObjectPrivate *op = QObjectPrivate::get(this); @@ -237,12 +236,12 @@ void QQmlValueType::write(QObject *obj, int idx, QQmlPropertyData::WriteFlags fl QVariant QQmlValueType::value() { Q_ASSERT(gadgetPtr); - return QVariant(typeId, gadgetPtr); + return QVariant(metaType.id(), gadgetPtr); } void QQmlValueType::setValue(const QVariant &value) { - Q_ASSERT(typeId == value.userType()); + Q_ASSERT(metaType.id() == value.userType()); metaType.destruct(gadgetPtr); metaType.construct(gadgetPtr, value.constData()); } diff --git a/src/qml/qml/qqmlvaluetype_p.h b/src/qml/qml/qqmlvaluetype_p.h index 4ea71e8955..89f1b71d61 100644 --- a/src/qml/qml/qqmlvaluetype_p.h +++ b/src/qml/qml/qqmlvaluetype_p.h @@ -84,7 +84,6 @@ private: void *gadgetPtr; public: - int typeId; QMetaType metaType; }; diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index b22d1530e2..7df5757b95 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -107,7 +107,7 @@ void Heap::QQmlValueTypeWrapper::destroy() void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const { - Q_ASSERT(valueType->typeId == value.userType()); + Q_ASSERT(valueType->metaType.id() == value.userType()); if (gadgetPtr) valueType->metaType.destruct(gadgetPtr); if (!gadgetPtr) @@ -118,7 +118,7 @@ void Heap::QQmlValueTypeWrapper::setValue(const QVariant &value) const QVariant Heap::QQmlValueTypeWrapper::toVariant() const { Q_ASSERT(gadgetPtr); - return QVariant(valueType->typeId, gadgetPtr); + return QVariant(valueType->metaType.id(), gadgetPtr); } @@ -223,7 +223,7 @@ bool QQmlValueTypeWrapper::toGadget(void *data) const if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) if (!ref->readReferenceValue()) return false; - const int typeId = d()->valueType->typeId; + const int typeId = d()->valueType->metaType.id(); QMetaType::destruct(typeId, data); QMetaType::construct(typeId, data, d()->gadgetPtr); return true; @@ -307,7 +307,7 @@ bool QQmlValueTypeWrapper::isEqual(const QVariant& value) const int QQmlValueTypeWrapper::typeId() const { - return d()->valueType->typeId; + return d()->valueType->metaType.id(); } bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const @@ -354,10 +354,10 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con // Prepare a buffer to pass to QMetaType::convert() QString convertResult; convertResult.~QString(); - if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->typeId, &convertResult, QMetaType::QString)) { + if (QMetaType::convert(w->d()->gadgetPtr, w->d()->valueType->metaType.id(), &convertResult, QMetaType::QString)) { result = convertResult; } else { - result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->typeId)) + result += QString::fromUtf8(QMetaType::typeName(w->d()->valueType->metaType.id())) + QLatin1Char('('); const QMetaObject *mo = w->d()->propertyCache()->metaObject(); const int propCount = mo->propertyCount(); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index f713efb289..64dc581a56 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -1527,6 +1527,27 @@ static QString jsStack(QV4::ExecutionEngine *engine) { return stack; } +static QString serializeArray(Object *array, ExecutionEngine *v4) { + Scope scope(v4); + ScopedValue val(scope); + QString result; + + result += QLatin1Char('['); + const uint length = array->getLength(); + for (uint i = 0; i < length; ++i) { + if (i != 0) + result += QLatin1Char(','); + val = array->get(i); + if (val->isManaged() && val->managed()->isArrayLike()) + result += serializeArray(val->objectValue(), v4); + else + result += val->toQStringNoThrow(); + } + result += QLatin1Char(']'); + + return result; +}; + static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, const Value *argv, int argc, ConsoleLogTypes logType, bool printStack = false) { @@ -1554,7 +1575,7 @@ static ReturnedValue writeToConsole(const FunctionObject *b, const Value *, cons result.append(QLatin1Char(' ')); if (argv[i].isManaged() && argv[i].managed()->isArrayLike()) - result += QLatin1Char('[') + argv[i].toQStringNoThrow() + QLatin1Char(']'); + result.append(serializeArray(argv[i].objectValue(), v4)); else result.append(argv[i].toQStringNoThrow()); } diff --git a/src/qml/qmldirparser/qmldirparser.pri b/src/qml/qmldirparser/qmldirparser.pri new file mode 100644 index 0000000000..660e7b395a --- /dev/null +++ b/src/qml/qmldirparser/qmldirparser.pri @@ -0,0 +1,11 @@ +INCLUDEPATH += $$PWD +INCLUDEPATH += $$OUT_PWD + +HEADERS += \ + $$PWD/qqmldirparser_p.h \ + $$PWD/qqmlerror.h \ + $$PWD/qqmlsourcecoordinate_p.h + +SOURCES += \ + $$PWD/qqmldirparser.cpp \ + $$PWD/qqmlerror.cpp diff --git a/src/qml/qml/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp index d87bf433b8..e944b52e47 100644 --- a/src/qml/qml/qqmldirparser.cpp +++ b/src/qml/qmldirparser/qqmldirparser.cpp @@ -367,12 +367,10 @@ QList<QQmlDirParser::Script> QQmlDirParser::scripts() const return _scripts; } -#ifdef QT_CREATOR QList<QQmlDirParser::TypeInfo> QQmlDirParser::typeInfos() const { return _typeInfos; } -#endif bool QQmlDirParser::designerSupported() const { diff --git a/src/qml/qml/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h index d7e29813d1..cff9cb11a4 100644 --- a/src/qml/qml/qqmldirparser_p.h +++ b/src/qml/qmldirparser/qqmldirparser_p.h @@ -122,7 +122,6 @@ public: QList<Plugin> plugins() const; bool designerSupported() const; -#ifdef QT_CREATOR struct TypeInfo { TypeInfo() {} @@ -133,7 +132,6 @@ public: }; QList<TypeInfo> typeInfos() const; -#endif QString className() const; @@ -149,9 +147,7 @@ private: QList<Script> _scripts; QList<Plugin> _plugins; bool _designerSupported; -#ifdef QT_CREATOR QList<TypeInfo> _typeInfos; -#endif QString _className; }; diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qmldirparser/qqmlerror.cpp index 61e9a3f37e..5e181f7e27 100644 --- a/src/qml/qml/qqmlerror.cpp +++ b/src/qml/qmldirparser/qqmlerror.cpp @@ -38,15 +38,17 @@ ****************************************************************************/ #include "qqmlerror.h" -#include "qqmlglobal_p.h" +#include "qqmlsourcecoordinate_p.h" #include <QtCore/qdebug.h> #include <QtCore/qfile.h> #include <QtCore/qstringlist.h> #include <QtCore/qvector.h> -#include <QtCore/qpointer.h> -#include <private/qv4errorobject_p.h> +#ifndef QT_NO_QOBJECT +#include <QtCore/qobject.h> +#include <QtCore/qpointer.h> +#endif QT_BEGIN_NAMESPACE @@ -85,11 +87,13 @@ public: quint16 line; quint16 column; QtMsgType messageType; +#ifndef QT_NO_QOBJECT QPointer<QObject> object; +#endif }; QQmlErrorPrivate::QQmlErrorPrivate() -: line(0), column(0), messageType(QtMsgType::QtWarningMsg), object() +: line(0), column(0), messageType(QtMsgType::QtWarningMsg) { } @@ -125,7 +129,9 @@ QQmlError &QQmlError::operator=(const QQmlError &other) d->description = other.d->description; d->line = other.d->line; d->column = other.d->column; +#ifndef QT_NO_QOBJECT d->object = other.d->object; +#endif d->messageType = other.d->messageType; } return *this; @@ -227,6 +233,7 @@ void QQmlError::setColumn(int column) d->column = qmlSourceCoordinate(column); } +#ifndef QT_NO_QOBJECT /*! Returns the nearest object where this error occurred. Exceptions in bound property expressions set this to the object @@ -249,6 +256,7 @@ void QQmlError::setObject(QObject *object) d = new QQmlErrorPrivate; d->object = object; } +#endif // QT_NO_QOBJECT /*! \since 5.9 diff --git a/src/qml/qml/qqmlerror.h b/src/qml/qmldirparser/qqmlerror.h index ef529e3828..f4221358e9 100644 --- a/src/qml/qml/qqmlerror.h +++ b/src/qml/qmldirparser/qqmlerror.h @@ -68,8 +68,12 @@ public: void setLine(int); int column() const; void setColumn(int); + +#ifndef QT_NO_QOBJECT QObject *object() const; void setObject(QObject *); +#endif + QtMsgType messageType() const; void setMessageType(QtMsgType messageType); diff --git a/src/qml/qmldirparser/qqmlsourcecoordinate_p.h b/src/qml/qmldirparser/qqmlsourcecoordinate_p.h new file mode 100644 index 0000000000..76ac741ae8 --- /dev/null +++ b/src/qml/qmldirparser/qqmlsourcecoordinate_p.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QQMLSOURCECOORDINATE_P_H +#define QQMLSOURCECOORDINATE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qglobal.h> + +#include <climits> + +QT_BEGIN_NAMESPACE + +inline quint16 qmlSourceCoordinate(int n) +{ + return (n > 0 && n <= static_cast<int>(USHRT_MAX)) ? static_cast<quint16>(n) : 0; +} + +inline int qmlSourceCoordinate(quint16 n) +{ + return (n == 0) ? -1 : static_cast<int>(n); +} + +QT_END_NAMESPACE + +#endif // QQMLSOURCECOORDINATE_P_H diff --git a/src/qml/qtqmlglobal.h b/src/qml/qtqmlglobal.h index e02dfa5ed4..090b830b3c 100644 --- a/src/qml/qtqmlglobal.h +++ b/src/qml/qtqmlglobal.h @@ -53,6 +53,7 @@ #else # define QT_FEATURE_qml_debug -1 # define QT_FEATURE_qml_sequence_object 1 +# define QT_FEATURE_qml_tracing -1 #endif QT_BEGIN_NAMESPACE diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 69b7876cf6..565e60b3c1 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -2764,6 +2764,8 @@ bool QQmlListModelParser::applyProperty(const QQmlRefPointer<QV4::CompiledData:: value = binding->valueAsNumber(compilationUnit->constants); } else if (binding->type == QV4::CompiledData::Binding::Type_Boolean) { value = binding->valueAsBoolean(); + } else if (binding->type == QV4::CompiledData::Binding::Type_Null) { + value = QVariant::fromValue(nullptr); } else if (binding->type == QV4::CompiledData::Binding::Type_Script) { QString scriptStr = binding->valueAsScriptString(compilationUnit.data()); if (definesEmptyList(scriptStr)) { diff --git a/src/qml/types/qquickworkerscript.cpp b/src/qml/types/qquickworkerscript.cpp index fe51f8ec47..edb112276c 100644 --- a/src/qml/types/qquickworkerscript.cpp +++ b/src/qml/types/qquickworkerscript.cpp @@ -481,7 +481,7 @@ void QQuickWorkerScriptEngine::run() \qmltype WorkerScript \instantiates QQuickWorkerScript \ingroup qtquick-threading - \inqmlmodule QtQuick + \inqmlmodule QtQml \brief Enables the use of threads in a Qt Quick application. Use WorkerScript to run operations in a new thread. |