diff options
Diffstat (limited to 'src/qml')
64 files changed, 1222 insertions, 970 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index fa66d3a6e3..871f28f2d0 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -10,7 +10,8 @@ HEADERS += \ $$PWD/qv4isel_util_p.h \ $$PWD/qv4ssa_p.h \ $$PWD/qqmlirbuilder_p.h \ - $$PWD/qqmltypecompiler_p.h + $$PWD/qqmltypecompiler_p.h \ + $$PWD/qv4jssimplifier_p.h SOURCES += \ $$PWD/qv4compileddata.cpp \ @@ -19,7 +20,8 @@ SOURCES += \ $$PWD/qv4isel_p.cpp \ $$PWD/qv4jsir.cpp \ $$PWD/qv4ssa.cpp \ - $$PWD/qqmlirbuilder.cpp + $$PWD/qqmlirbuilder.cpp \ + $$PWD/qv4jssimplifier.cpp !qmldevtools_build { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index a3b8784fc8..d1d22be0ac 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -47,6 +47,7 @@ #include <private/qv4ssa_p.h> #include "qqmlpropertycachecreator_p.h" +#include "qv4jssimplifier_p.h" #define COMPILE_EXCEPTION(token, desc) \ { \ @@ -144,7 +145,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() if (!jsCodeGen.generateCodeForComponents()) return nullptr; - QQmlJavaScriptBindingExpressionSimplificationPass pass(this); + QQmlJavaScriptBindingExpressionSimplificationPass pass(document->objects, &document->jsModule, &document->jsGenerator); pass.reduceTranslationBindings(); QV4::ExecutionEngine *v4 = engine->v4engine(); @@ -1429,344 +1430,4 @@ void QQmlDefaultPropertyMerger::mergeDefaultProperties(int objectIndex) } } -QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler) - : QQmlCompilePass(typeCompiler) - , qmlObjects(*typeCompiler->qmlObjects()) - , jsModule(typeCompiler->jsIRModule()) -{ - -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() -{ - for (int i = 0; i < qmlObjects.count(); ++i) - reduceTranslationBindings(i); - if (!irFunctionsToRemove.isEmpty()) { - QQmlIRFunctionCleanser cleanser(compiler, irFunctionsToRemove); - cleanser.clean(); - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) -{ - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - - for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { - if (binding->type != QV4::CompiledData::Binding::Type_Script) - continue; - - const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); - QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); - if (simplifyBinding(irFunction, binding)) { - irFunctionsToRemove.append(irFunctionIndex); - jsModule->functions[irFunctionIndex] = 0; - delete irFunction; - } - } -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) -{ - QV4::IR::Temp *target = move->target->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - - if (QV4::IR::Call *call = move->source->asCall()) { - if (QV4::IR::Name *n = call->base->asName()) { - if (n->builtin == QV4::IR::Name::builtin_invalid) { - visitFunctionCall(n->id, call->args, target); - return; - } - } - discard(); - return; - } - - if (QV4::IR::Name *n = move->source->asName()) { - if (n->builtin == QV4::IR::Name::builtin_qml_context - || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { - // these are free of side-effects - return; - } - discard(); - return; - } - - if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { - discard(); - return; - } - - _temps[target->index] = move->source; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) -{ - // more than one function call? - if (_nameOfFunctionCalled) { - discard(); - return; - } - - _nameOfFunctionCalled = name; - - _functionParameters.clear(); - while (args) { - int slot; - if (QV4::IR::Temp *param = args->expr->asTemp()) { - if (param->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - slot = param->index; - _functionParameters.append(slot); - } else if (QV4::IR::Const *param = args->expr->asConst()) { - slot = --_synthesizedConsts; - Q_ASSERT(!_temps.contains(slot)); - _temps[slot] = param; - _functionParameters.append(slot); - } - args = args->next; - } - - _functionCallReturnValue = target->index; -} - -void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) -{ - // nothing initialized earlier? - if (_returnValueOfBindingExpression != -1) { - discard(); - return; - } - QV4::IR::Temp *target = ret->expr->asTemp(); - if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { - discard(); - return; - } - _returnValueOfBindingExpression = target->index; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) -{ - _canSimplify = true; - _nameOfFunctionCalled = 0; - _functionParameters.clear(); - _functionCallReturnValue = -1; - _temps.clear(); - _returnValueOfBindingExpression = -1; - _synthesizedConsts = 0; - - // It would seem unlikely that function with some many basic blocks (after optimization) - // consists merely of a qsTr call or a constant value return ;-) - if (function->basicBlockCount() > 10) - return false; - - for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { - for (QV4::IR::Stmt *s : bb->statements()) { - visit(s); - if (!_canSimplify) - return false; - } - } - - if (_returnValueOfBindingExpression == -1) - return false; - - if (_nameOfFunctionCalled) { - if (_functionCallReturnValue != _returnValueOfBindingExpression) - return false; - return detectTranslationCallAndConvertBinding(binding); - } - - return false; -} - -bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) -{ - if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { - QString translation; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - translation = *stringParam->value; - - ++param; - if (param != end) { - stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - translationData.commentIndex = compiler->registerString(*stringParam->value); - ++param; - - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_Translation; - binding->stringIndex = compiler->registerString(translation); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { - QString id; - QV4::CompiledData::TranslationData translationData; - translationData.number = -1; - translationData.commentIndex = 0; // empty string, but unused - - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - id = *stringParam->value; - - ++param; - if (param != end) { - QV4::IR::Const *constParam = _temps[*param]->asConst(); - if (!constParam || constParam->type != QV4::IR::SInt32Type) - return false; - - translationData.number = int(constParam->value); - ++param; - } - - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_TranslationById; - binding->stringIndex = compiler->registerString(id); - binding->value.translationData = translationData; - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { - QVector<int>::ConstIterator param = _functionParameters.constBegin(); - QVector<int>::ConstIterator end = _functionParameters.constEnd(); - if (param == end) - return false; - - ++param; - if (param == end) - return false; - - QV4::IR::String *stringParam = _temps[*param]->asString(); - if (!stringParam) - return false; - - ++param; - if (param != end) - return false; - - binding->type = QV4::CompiledData::Binding::Type_String; - binding->stringIndex = compiler->registerString(*stringParam->value); - return true; - } - return false; -} - -QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove) - : QQmlCompilePass(typeCompiler) - , module(typeCompiler->jsIRModule()) - , functionsToRemove(functionsToRemove) -{ -} - -void QQmlIRFunctionCleanser::clean() -{ - QVector<QV4::IR::Function*> newFunctions; - newFunctions.reserve(module->functions.count() - functionsToRemove.count()); - - newFunctionIndices.resize(module->functions.count()); - - for (int i = 0; i < module->functions.count(); ++i) { - QV4::IR::Function *f = module->functions.at(i); - Q_ASSERT(f || functionsToRemove.contains(i)); - if (f) { - newFunctionIndices[i] = newFunctions.count(); - newFunctions << f; - } - } - - module->functions = newFunctions; - - for (QV4::IR::Function *function : qAsConst(module->functions)) { - for (QV4::IR::BasicBlock *block : function->basicBlocks()) { - for (QV4::IR::Stmt *s : block->statements()) { - visit(s); - } - } - } - - for (QmlIR::Object *obj : qAsConst(*compiler->qmlObjects())) { - for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) - obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) -{ - - switch (s->stmtKind) { - case QV4::IR::Stmt::PhiStmt: - // nothing to do - break; - default: - STMT_VISIT_ALL_KINDS(s); - break; - } -} - -void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) -{ - switch (e->exprKind) { - case QV4::IR::Expr::ClosureExpr: { - auto closure = e->asClosure(); - closure->value = newFunctionIndices.at(closure->value); - } break; - default: - EXPR_VISIT_ALL_KINDS(e); - break; - } -} - QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index 11261e3099..79fc073d8b 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -345,86 +345,6 @@ private: const QQmlPropertyCacheVector * const propertyCaches; }; -class QQmlJavaScriptBindingExpressionSimplificationPass : public QQmlCompilePass -{ -public: - QQmlJavaScriptBindingExpressionSimplificationPass(QQmlTypeCompiler *typeCompiler); - - void reduceTranslationBindings(); - -private: - void reduceTranslationBindings(int objectIndex); - - void visit(QV4::IR::Stmt *s) - { - switch (s->stmtKind) { - case QV4::IR::Stmt::MoveStmt: - visitMove(s->asMove()); - break; - case QV4::IR::Stmt::RetStmt: - visitRet(s->asRet()); - break; - case QV4::IR::Stmt::CJumpStmt: - discard(); - break; - case QV4::IR::Stmt::ExpStmt: - discard(); - break; - case QV4::IR::Stmt::JumpStmt: - break; - case QV4::IR::Stmt::PhiStmt: - break; - } - } - - void visitMove(QV4::IR::Move *move); - void visitRet(QV4::IR::Ret *ret); - - void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); - - void discard() { _canSimplify = false; } - - bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); - bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); - - const QVector<QmlIR::Object*> &qmlObjects; - QV4::IR::Module *jsModule; - - bool _canSimplify; - const QString *_nameOfFunctionCalled; - QVector<int> _functionParameters; - int _functionCallReturnValue; - - QHash<int, QV4::IR::Expr*> _temps; - int _returnValueOfBindingExpression; - int _synthesizedConsts; - - QVector<int> irFunctionsToRemove; -}; - -class QQmlIRFunctionCleanser : public QQmlCompilePass -{ -public: - QQmlIRFunctionCleanser(QQmlTypeCompiler *typeCompiler, const QVector<int> &functionsToRemove); - - void clean(); - -private: - virtual void visitMove(QV4::IR::Move *s) { - visit(s->source); - visit(s->target); - } - - void visit(QV4::IR::Stmt *s); - void visit(QV4::IR::Expr *e); - -private: - QV4::IR::Module *module; - const QVector<int> &functionsToRemove; - - QVector<int> newFunctionIndices; -}; - QT_END_NAMESPACE #endif // QQMLTYPECOMPILER_P_H diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 3234e7ee63..693a4230ba 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -92,6 +92,27 @@ static bool cjumpCanHandle(IR::AluOp op) } } +static inline void setJumpOutLocation(IR::Stmt *s, const Statement *body, + const SourceLocation &fallback) +{ + switch (body->kind) { + // Statements where we might never execute the last line. + // Use the fallback. + case Statement::Kind_ConditionalExpression: + case Statement::Kind_ForEachStatement: + case Statement::Kind_ForStatement: + case Statement::Kind_IfStatement: + case Statement::Kind_LocalForEachStatement: + case Statement::Kind_LocalForStatement: + case Statement::Kind_WhileStatement: + setLocation(s, fallback); + break; + default: + setLocation(s, body->lastSourceLocation()); + break; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) @@ -2269,7 +2290,7 @@ bool Codegen::visit(DoWhileStatement *ast) _block = loopbody; statement(ast->statement); - setLocation(_block->JUMP(loopcond), ast->statement->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(loopcond), ast->statement, ast->semicolonToken); _block = loopcond; condition(ast->expression, loopbody, loopend); @@ -2334,7 +2355,7 @@ bool Codegen::visit(ForEachStatement *ast) return false; move(*init, _block->TEMP(temp)); statement(ast->statement); - setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2373,7 +2394,7 @@ bool Codegen::visit(ForStatement *ast) _block = forbody; statement(ast->statement); - setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2399,12 +2420,12 @@ bool Codegen::visit(IfStatement *ast) _block = iftrue; statement(ast->ok); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ok, ast->ifToken); if (ast->ko) { _block = iffalse; statement(ast->ko); - _block->JUMP(endif); + setJumpOutLocation(_block->JUMP(endif), ast->ko, ast->elseToken); } _block = endif; @@ -2473,7 +2494,7 @@ bool Codegen::visit(LocalForEachStatement *ast) int temp = _block->newTemp(); move(identifier(ast->declaration->name.toString()), _block->TEMP(temp)); statement(ast->statement); - setLocation(_block->JUMP(foreachin), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(foreachin), ast->statement, ast->forToken); _block = foreachin; @@ -2512,7 +2533,7 @@ bool Codegen::visit(LocalForStatement *ast) _block = forbody; statement(ast->statement); - setLocation(_block->JUMP(forstep), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(forstep), ast->statement, ast->forToken); _block = forstep; statement(ast->expression); @@ -2813,7 +2834,7 @@ bool Codegen::visit(WhileStatement *ast) _block = whilebody; statement(ast->statement); - setLocation(_block->JUMP(whilecond), ast->lastSourceLocation()); + setJumpOutLocation(_block->JUMP(whilecond), ast->statement, ast->whileToken); _block = whileend; leaveLoop(); diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index c56f08c2f0..9832e1c49b 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -125,10 +125,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) runtimeStrings = (QV4::Heap::String **)malloc(data->stringTableSize * sizeof(QV4::Heap::String*)); // memset the strings to 0 in case a GC run happens while we're within the loop below memset(runtimeStrings, 0, data->stringTableSize * sizeof(QV4::Heap::String*)); - for (uint i = 0; i < data->stringTableSize; ++i) { + for (uint i = 0; i < data->stringTableSize; ++i) runtimeStrings[i] = engine->newIdentifier(data->stringAt(i)); - runtimeStrings[i]->setMarkBit(); - } runtimeRegularExpressions = new QV4::Value[data->regexpTableSize]; // memset the regexps to 0 in case a GC run happens while we're within the loop below @@ -144,12 +142,6 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) flags |= IR::RegExp::RegExp_Multiline; QV4::Heap::RegExpObject *ro = engine->newRegExpObject(data->stringAt(re->stringIndex), flags); runtimeRegularExpressions[i] = ro; -#if WRITEBARRIER(steele) - if (engine->memoryManager->nextGCIsIncremental) { - ro->setMarkBit(); - ro->setGrayBit(); - } -#endif } if (data->lookupTableSize) { @@ -249,14 +241,14 @@ void CompilationUnit::unlink() #endif } -void CompilationUnit::markObjects(QV4::ExecutionEngine *e) +void CompilationUnit::markObjects(QV4::MarkStack *markStack) { for (uint i = 0; i < data->stringTableSize; ++i) if (runtimeStrings[i]) - runtimeStrings[i]->mark(e); + runtimeStrings[i]->mark(markStack); if (runtimeRegularExpressions) { for (uint i = 0; i < data->regexpTableSize; ++i) - runtimeRegularExpressions[i].mark(e); + runtimeRegularExpressions[i].mark(markStack); } } @@ -461,6 +453,7 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; #else + Q_UNUSED(outputFileName) *errorString = QStringLiteral("features.temporaryfile is disabled."); return false; #endif // QT_CONFIG(temporaryfile) @@ -745,7 +738,7 @@ static QByteArray ownLibraryChecksum() if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); if (library.open(QIODevice::ReadOnly)) { - QCryptographicHash hash(QCryptographicHash::Sha1); + QCryptographicHash hash(QCryptographicHash::Md5); hash.addData(&library); libraryChecksum = hash.result(); } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 6e9121b5e3..f4ba257cf5 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -897,7 +897,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public CompilationUnitBase, public QV4::Function *linkToEngine(QV4::ExecutionEngine *engine); void unlink(); - void markObjects(QV4::ExecutionEngine *e); + void markObjects(MarkStack *markStack); void destroy() Q_DECL_OVERRIDE; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index b81d724fe7..f7e63437e1 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -376,7 +376,7 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp unit.version = QV4_DATA_STRUCTURE_VERSION; unit.qtVersion = QT_VERSION; memset(unit.md5Checksum, 0, sizeof(unit.md5Checksum)); - unit.architectureIndex = registerString(QSysInfo::buildAbi()); + unit.architectureIndex = registerString(irModule->targetABI.isEmpty() ? QSysInfo::buildAbi() : irModule->targetABI); unit.codeGeneratorIndex = registerString(codeGeneratorName); memset(unit.dependencyMD5Checksum, 0, sizeof(unit.dependencyMD5Checksum)); diff --git a/src/qml/compiler/qv4isel_util_p.h b/src/qml/compiler/qv4isel_util_p.h index 1755193d32..e949e6f0ad 100644 --- a/src/qml/compiler/qv4isel_util_p.h +++ b/src/qml/compiler/qv4isel_util_p.h @@ -58,6 +58,59 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct TargetPrimitive32 { + static TargetPrimitive32 emptyValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Empty) << 32; return p; } + static TargetPrimitive32 nullValue() { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Null) << 32; return p; } + static TargetPrimitive32 undefinedValue() { TargetPrimitive32 p; p._val = quint64(Value::Managed_Type_Internal_32) << 32; return p; } + static TargetPrimitive32 fromBoolean(bool b) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Boolean) << 32 | quint64(b); return p; } + static TargetPrimitive32 fromInt32(int v) { TargetPrimitive32 p; p._val = quint64(Value::ValueTypeInternal_32::Integer) << 32 | quint32(v); return p; } + static TargetPrimitive32 fromDouble(double v) { + TargetPrimitive32 p; + memcpy(&p._val, &v, 8); + return p; + } + static TargetPrimitive32 fromUInt32(uint v) { + if (v < INT_MAX) + return fromInt32(qint32(v)); + return fromDouble(double(v)); + } + + quint32 value() const { return _val & quint64(~quint32(0)); } + quint32 tag() const { return _val >> 32; } + + quint64 rawValue() const { return _val; } + +private: + quint64 _val; +}; + +struct TargetPrimitive64 { + static TargetPrimitive64 emptyValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Empty) << 32; return p; } + static TargetPrimitive64 nullValue() { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Null) << 32; return p; } + static TargetPrimitive64 undefinedValue() { TargetPrimitive64 p; p._val = 0; return p; } + static TargetPrimitive64 fromBoolean(bool b) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Boolean) << 32 | quint64(b); return p; } + static TargetPrimitive64 fromInt32(int v) { TargetPrimitive64 p; p._val = quint64(Value::ValueTypeInternal_64::Integer) << 32 | quint32(v); return p; } + static TargetPrimitive64 fromDouble(double v) { + TargetPrimitive64 p; + memcpy(&p._val, &v, 8); + p._val ^= Value::NaNEncodeMask; + return p; + } + static TargetPrimitive64 fromUInt32(uint v) { + if (v < INT_MAX) + return fromInt32(qint32(v)); + return fromDouble(double(v)); + } + + quint32 value() const { return _val & quint64(~quint32(0)); } + quint32 tag() const { return _val >> 32; } + + quint64 rawValue() const { return _val; } + +private: + quint64 _val; +}; + inline bool canConvertToSignedInteger(double value) { int ival = (int) value; @@ -72,36 +125,37 @@ inline bool canConvertToUnsignedInteger(double value) return uval == value && !(value == 0 && isNegative(value)); } -inline Primitive convertToValue(IR::Const *c) +template <typename PrimitiveType = Primitive> +inline PrimitiveType convertToValue(IR::Const *c) { switch (c->type) { case IR::MissingType: - return Primitive::emptyValue(); + return PrimitiveType::emptyValue(); case IR::NullType: - return Primitive::nullValue(); + return PrimitiveType::nullValue(); case IR::UndefinedType: - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); case IR::BoolType: - return Primitive::fromBoolean(c->value != 0); + return PrimitiveType::fromBoolean(c->value != 0); case IR::SInt32Type: - return Primitive::fromInt32(int(c->value)); + return PrimitiveType::fromInt32(int(c->value)); case IR::UInt32Type: - return Primitive::fromUInt32(unsigned(c->value)); + return PrimitiveType::fromUInt32(unsigned(c->value)); case IR::DoubleType: - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); case IR::NumberType: { int ival = (int)c->value; if (canConvertToSignedInteger(c->value)) { - return Primitive::fromInt32(ival); + return PrimitiveType::fromInt32(ival); } else { - return Primitive::fromDouble(c->value); + return PrimitiveType::fromDouble(c->value); } } default: Q_UNREACHABLE(); } // unreachable, but the function must return something - return Primitive::undefinedValue(); + return PrimitiveType::undefinedValue(); } class ConvertTemps diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index f7c7b76ea8..35cf0fc174 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -946,6 +946,7 @@ struct Q_QML_PRIVATE_EXPORT Module { QDateTime sourceTimeStamp; bool isQmlModule; // implies rootFunction is always 0 uint unitFlags; // flags merged into CompiledData::Unit::flags + QString targetABI; // fallback to QSysInfo::buildAbi() if empty #ifdef QT_NO_QML_DEBUGGER static const bool debugMode = false; #else diff --git a/src/qml/compiler/qv4jssimplifier.cpp b/src/qml/compiler/qv4jssimplifier.cpp new file mode 100644 index 0000000000..7d09218fe6 --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier.cpp @@ -0,0 +1,384 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 "qv4jssimplifier_p.h" + +QT_BEGIN_NAMESPACE + +QQmlJavaScriptBindingExpressionSimplificationPass::QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator) + : qmlObjects(qmlObjects) + , jsModule(jsModule) + , unitGenerator(unitGenerator) +{ + +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings() +{ + for (int i = 0; i < qmlObjects.count(); ++i) + reduceTranslationBindings(i); + if (!irFunctionsToRemove.isEmpty()) { + QQmlIRFunctionCleanser cleanser(jsModule, qmlObjects, irFunctionsToRemove); + cleanser.clean(); + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::reduceTranslationBindings(int objectIndex) +{ + const QmlIR::Object *obj = qmlObjects.at(objectIndex); + + for (QmlIR::Binding *binding = obj->firstBinding(); binding; binding = binding->next) { + if (binding->type != QV4::CompiledData::Binding::Type_Script) + continue; + + const int irFunctionIndex = obj->runtimeFunctionIndices.at(binding->value.compiledScriptIndex); + QV4::IR::Function *irFunction = jsModule->functions.at(irFunctionIndex); + if (simplifyBinding(irFunction, binding)) { + irFunctionsToRemove.append(irFunctionIndex); + jsModule->functions[irFunctionIndex] = 0; + delete irFunction; + } + } +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitMove(QV4::IR::Move *move) +{ + QV4::IR::Temp *target = move->target->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + + if (QV4::IR::Call *call = move->source->asCall()) { + if (QV4::IR::Name *n = call->base->asName()) { + if (n->builtin == QV4::IR::Name::builtin_invalid) { + visitFunctionCall(n->id, call->args, target); + return; + } + } + discard(); + return; + } + + if (QV4::IR::Name *n = move->source->asName()) { + if (n->builtin == QV4::IR::Name::builtin_qml_context + || n->builtin == QV4::IR::Name::builtin_qml_imported_scripts_object) { + // these are free of side-effects + return; + } + discard(); + return; + } + + if (!move->source->asTemp() && !move->source->asString() && !move->source->asConst()) { + discard(); + return; + } + + _temps[target->index] = move->source; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target) +{ + // more than one function call? + if (_nameOfFunctionCalled) { + discard(); + return; + } + + _nameOfFunctionCalled = name; + + _functionParameters.clear(); + while (args) { + int slot; + if (QV4::IR::Temp *param = args->expr->asTemp()) { + if (param->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + slot = param->index; + _functionParameters.append(slot); + } else if (QV4::IR::Const *param = args->expr->asConst()) { + slot = --_synthesizedConsts; + Q_ASSERT(!_temps.contains(slot)); + _temps[slot] = param; + _functionParameters.append(slot); + } + args = args->next; + } + + _functionCallReturnValue = target->index; +} + +void QQmlJavaScriptBindingExpressionSimplificationPass::visitRet(QV4::IR::Ret *ret) +{ + // nothing initialized earlier? + if (_returnValueOfBindingExpression != -1) { + discard(); + return; + } + QV4::IR::Temp *target = ret->expr->asTemp(); + if (!target || target->kind != QV4::IR::Temp::VirtualRegister) { + discard(); + return; + } + _returnValueOfBindingExpression = target->index; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding) +{ + _canSimplify = true; + _nameOfFunctionCalled = 0; + _functionParameters.clear(); + _functionCallReturnValue = -1; + _temps.clear(); + _returnValueOfBindingExpression = -1; + _synthesizedConsts = 0; + + // It would seem unlikely that function with some many basic blocks (after optimization) + // consists merely of a qsTr call or a constant value return ;-) + if (function->basicBlockCount() > 10) + return false; + + for (QV4::IR::BasicBlock *bb : function->basicBlocks()) { + for (QV4::IR::Stmt *s : bb->statements()) { + visit(s); + if (!_canSimplify) + return false; + } + } + + if (_returnValueOfBindingExpression == -1) + return false; + + if (_nameOfFunctionCalled) { + if (_functionCallReturnValue != _returnValueOfBindingExpression) + return false; + return detectTranslationCallAndConvertBinding(binding); + } + + return false; +} + +bool QQmlJavaScriptBindingExpressionSimplificationPass::detectTranslationCallAndConvertBinding(QmlIR::Binding *binding) +{ + if (*_nameOfFunctionCalled == QLatin1String("qsTr")) { + QString translation; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + translation = *stringParam->value; + + ++param; + if (param != end) { + stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + translationData.commentIndex = unitGenerator->registerString(*stringParam->value); + ++param; + + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_Translation; + binding->stringIndex = unitGenerator->registerString(translation); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("qsTrId")) { + QString id; + QV4::CompiledData::TranslationData translationData; + translationData.number = -1; + translationData.commentIndex = 0; // empty string, but unused + + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + id = *stringParam->value; + + ++param; + if (param != end) { + QV4::IR::Const *constParam = _temps[*param]->asConst(); + if (!constParam || constParam->type != QV4::IR::SInt32Type) + return false; + + translationData.number = int(constParam->value); + ++param; + } + + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_TranslationById; + binding->stringIndex = unitGenerator->registerString(id); + binding->value.translationData = translationData; + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TR_NOOP") || *_nameOfFunctionCalled == QLatin1String("QT_TRID_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } else if (*_nameOfFunctionCalled == QLatin1String("QT_TRANSLATE_NOOP")) { + QVector<int>::ConstIterator param = _functionParameters.constBegin(); + QVector<int>::ConstIterator end = _functionParameters.constEnd(); + if (param == end) + return false; + + ++param; + if (param == end) + return false; + + QV4::IR::String *stringParam = _temps[*param]->asString(); + if (!stringParam) + return false; + + ++param; + if (param != end) + return false; + + binding->type = QV4::CompiledData::Binding::Type_String; + binding->stringIndex = unitGenerator->registerString(*stringParam->value); + return true; + } + return false; +} + +QQmlIRFunctionCleanser::QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object *> &qmlObjects, const QVector<int> &functionsToRemove) + : module(module) + , qmlObjects(qmlObjects) + , functionsToRemove(functionsToRemove) +{ +} + +void QQmlIRFunctionCleanser::clean() +{ + QVector<QV4::IR::Function*> newFunctions; + newFunctions.reserve(module->functions.count() - functionsToRemove.count()); + + newFunctionIndices.resize(module->functions.count()); + + for (int i = 0; i < module->functions.count(); ++i) { + QV4::IR::Function *f = module->functions.at(i); + Q_ASSERT(f || functionsToRemove.contains(i)); + if (f) { + newFunctionIndices[i] = newFunctions.count(); + newFunctions << f; + } + } + + module->functions = newFunctions; + + for (QV4::IR::Function *function : qAsConst(module->functions)) { + for (QV4::IR::BasicBlock *block : function->basicBlocks()) { + for (QV4::IR::Stmt *s : block->statements()) { + visit(s); + } + } + } + + for (QmlIR::Object *obj : qmlObjects) { + for (int i = 0; i < obj->runtimeFunctionIndices.count; ++i) + obj->runtimeFunctionIndices[i] = newFunctionIndices[obj->runtimeFunctionIndices.at(i)]; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Stmt *s) +{ + + switch (s->stmtKind) { + case QV4::IR::Stmt::PhiStmt: + // nothing to do + break; + default: + STMT_VISIT_ALL_KINDS(s); + break; + } +} + +void QQmlIRFunctionCleanser::visit(QV4::IR::Expr *e) +{ + switch (e->exprKind) { + case QV4::IR::Expr::ClosureExpr: { + auto closure = e->asClosure(); + closure->value = newFunctionIndices.at(closure->value); + } break; + default: + EXPR_VISIT_ALL_KINDS(e); + break; + } +} + +QT_END_NAMESPACE diff --git a/src/qml/compiler/qv4jssimplifier_p.h b/src/qml/compiler/qv4jssimplifier_p.h new file mode 100644 index 0000000000..ae8d74135c --- /dev/null +++ b/src/qml/compiler/qv4jssimplifier_p.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the tools applications 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 QV4JSSIMPLIFIER +#define QV4JSSIMPLIFIER + +// +// 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 <private/qv4global_p.h> + +#include "qqmlirbuilder_p.h" + +QT_BEGIN_NAMESPACE + +namespace QmlIR { +struct Document; +} + +namespace QV4 { +namespace CompiledData { +struct QmlUnit; +struct Location; +} +} + +class QQmlJavaScriptBindingExpressionSimplificationPass +{ +public: + QQmlJavaScriptBindingExpressionSimplificationPass(const QVector<QmlIR::Object*> &qmlObjects, QV4::IR::Module *jsModule, QV4::Compiler::JSUnitGenerator *unitGenerator); + + void reduceTranslationBindings(); + +private: + void reduceTranslationBindings(int objectIndex); + + void visit(QV4::IR::Stmt *s) + { + switch (s->stmtKind) { + case QV4::IR::Stmt::MoveStmt: + visitMove(s->asMove()); + break; + case QV4::IR::Stmt::RetStmt: + visitRet(s->asRet()); + break; + case QV4::IR::Stmt::CJumpStmt: + discard(); + break; + case QV4::IR::Stmt::ExpStmt: + discard(); + break; + case QV4::IR::Stmt::JumpStmt: + break; + case QV4::IR::Stmt::PhiStmt: + break; + } + } + + void visitMove(QV4::IR::Move *move); + void visitRet(QV4::IR::Ret *ret); + + void visitFunctionCall(const QString *name, QV4::IR::ExprList *args, QV4::IR::Temp *target); + + void discard() { _canSimplify = false; } + + bool simplifyBinding(QV4::IR::Function *function, QmlIR::Binding *binding); + bool detectTranslationCallAndConvertBinding(QmlIR::Binding *binding); + + const QVector<QmlIR::Object*> &qmlObjects; + QV4::IR::Module *jsModule; + QV4::Compiler::JSUnitGenerator *unitGenerator; + + bool _canSimplify; + const QString *_nameOfFunctionCalled; + QVector<int> _functionParameters; + int _functionCallReturnValue; + + QHash<int, QV4::IR::Expr*> _temps; + int _returnValueOfBindingExpression; + int _synthesizedConsts; + + QVector<int> irFunctionsToRemove; +}; + +class QQmlIRFunctionCleanser +{ +public: + QQmlIRFunctionCleanser(QV4::IR::Module *module, const QVector<QmlIR::Object*> &qmlObjects, const QVector<int> &functionsToRemove); + + void clean(); + +private: + virtual void visitMove(QV4::IR::Move *s) { + visit(s->source); + visit(s->target); + } + + void visit(QV4::IR::Stmt *s); + void visit(QV4::IR::Expr *e); + +private: + QV4::IR::Module *module; + const QVector<QmlIR::Object*> &qmlObjects; + const QVector<int> &functionsToRemove; + + QVector<int> newFunctionIndices; +}; + +QT_END_NAMESPACE + +#endif // QV4JSSIMPLIFIER diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index cc542e94e7..62e2833089 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -2345,6 +2345,7 @@ private: case OpIncrement: case OpDecrement: Q_ASSERT(!"Inplace operators should have been removed!"); + Q_UNREACHABLE(); default: Q_UNIMPLEMENTED(); Q_UNREACHABLE(); @@ -2645,6 +2646,7 @@ private: case OpMul: if (!targetTemp || !knownOk.contains(*targetTemp)) return false; + Q_FALLTHROUGH(); case OpBitAnd: case OpBitOr: case OpBitXor: diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index 4523ee39d8..9cc7291583 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -273,10 +273,17 @@ In particular, QML currently supports: \li \c {QList<qreal>} \li \c {QList<bool>} \li \c {QList<QString>} and \c{QStringList} + \li \c {QVector<QString>} + \li \c {std::vector<QString>} \li \c {QList<QUrl>} + \li \c {QVector<QUrl>} + \li \c {std::vector<QUrl>} \li \c {QVector<int>} \li \c {QVector<qreal>} \li \c {QVector<bool>} + \li \c {std::vector<int>} + \li \c {std::vector<qreal>} + \li \c {std::vector<bool>} \endlist These sequence types are implemented directly in terms of the underlying C++ @@ -296,6 +303,10 @@ If the sequence is returned from a Q_INVOKABLE function, access and mutation is much cheaper, as no QObject property read or write occurs; instead, the C++ sequence data is accessed and modified directly. +In both the Q_PROPERTY and return from Q_INVOKABLE cases, the elements +of a std::vector are copied. This copying may be an expensive operation, +so std::vector should be used judiciously. + Other sequence types are not supported transparently, and instead an instance of any other sequence type will be passed between QML and C++ as an opaque QVariantList. @@ -318,10 +329,17 @@ The default-constructed values for each sequence type are as follows: \row \li QList<qreal> \li real value 0.0 \row \li QList<bool> \li boolean value \c {false} \row \li QList<QString> and QStringList \li empty QString +\row \li QVector<QString> \li empty QString +\row \li std::vector<QString> \li empty QString \row \li QList<QUrl> \li empty QUrl +\row \li QVector<QUrl> \li empty QUrl +\row \li std::vector<QUrl> \li empty QUrl \row \li QVector<int> \li integer value 0 \row \li QVector<qreal> \li real value 0.0 \row \li QVector<bool> \li boolean value \c {false} +\row \li std::vector<int> \li integer value 0 +\row \li std::vector<qreal> \li real value 0.0 +\row \li std::vector<bool> \li boolean value \c {false} \endtable If you wish to remove elements from a sequence rather than simply replace diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index 7d4a543089..32084bd308 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -687,7 +687,7 @@ class MessageBoard : public QObject Q_PROPERTY(QQmlListProperty<Message> messages READ messages) Q_CLASSINFO("DefaultProperty", "messages") public: - QQmlListProperty<Message> messages() const; + QQmlListProperty<Message> messages(); private: QList<Message *> messages; diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index 3bffd2eb6f..c4c58c2821 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -267,7 +267,7 @@ class MessageBoard : public QObject Q_OBJECT Q_PROPERTY(QQmlListProperty<Message> messages READ messages) public: - QQmlListProperty<Message> messages() const; + QQmlListProperty<Message> messages(); private: static void append_message(QQmlListProperty<Message> *list, Message *msg); diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 583719a3c7..d062f3bbb2 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -156,9 +156,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit } template <typename TargetConfiguration> -const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - -template <typename TargetConfiguration> Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) : _function(function) , _nextBlock(0) @@ -324,21 +321,21 @@ typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>: loadPtr(Address(Assembler::ScratchRegister, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, compilationUnit))), Assembler::ScratchRegister); loadPtr(Address(Assembler::ScratchRegister, offsetof(CompiledData::CompilationUnitBase, runtimeStrings)), reg); const int id = _jsGenerator->registerString(string); - return Pointer(reg, id * sizeof(QV4::String*)); + return Pointer(reg, id * RegisterSize); } template <typename TargetConfiguration> typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) { - return loadConstant(convertToValue(c), baseReg); + return loadConstant(convertToValue<TargetPrimitive>(c), baseReg); } template <typename TargetConfiguration> -typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const Primitive &v, RegisterID baseReg) +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const TargetPrimitive &v, RegisterID baseReg) { loadPtr(Address(Assembler::EngineRegister, targetStructureOffset(offsetof(QV4::EngineBase, current))), baseReg); loadPtr(Address(baseReg, targetStructureOffset(Heap::ExecutionContextData::baseOffset + offsetof(Heap::ExecutionContextData, constantTable))), baseReg); - const int index = _jsGenerator->registerConstant(v.asReturnedValue()); + const int index = _jsGenerator->registerConstant(v.rawValue()); return Address(baseReg, index * sizeof(QV4::Value)); } @@ -350,7 +347,7 @@ void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString } template <typename TargetConfiguration> -void Assembler<TargetConfiguration>::storeValue(QV4::Primitive value, IR::Expr *destination) +void Assembler<TargetConfiguration>::storeValue(TargetPrimitive value, IR::Expr *destination) { WriteBarrier::Type barrier; Address addr = loadAddressForWriting(ScratchRegister, destination, &barrier); @@ -449,19 +446,13 @@ typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::ge // check if it's an int32: Assembler::Jump isNoInt = branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); + Assembler::TrustedImm32(quint32(ValueTypeInternal::Integer))); convertInt32ToDouble(toInt32Register(src, Assembler::ScratchRegister), dest); Assembler::Jump intDone = jump(); // not an int, check if it's a double: isNoInt.link(this); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); - Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(0)); -#else - and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(Value::NotDouble_Mask)); -#endif + Assembler::Jump isNoDbl = RegisterSizeDependentOps::checkIfTagRegisterIsDouble(this, ScratchRegister); toDoubleRegister(src, dest); intDone.link(this); @@ -530,7 +521,7 @@ void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInfo } else if (IR::Temp *t = s->expr->asTemp()) { RegisterSizeDependentOps::setFunctionReturnValueFromTemp(this, t); } else if (IR::Const *c = s->expr->asConst()) { - QV4::Primitive retVal = convertToValue(c); + auto retVal = convertToValue<TargetPrimitive>(c); RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); } else { Q_UNREACHABLE(); @@ -547,7 +538,7 @@ void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInfo ret(); exceptionReturnLabel = label(); - QV4::Primitive retVal = Primitive::undefinedValue(); + auto retVal = TargetPrimitive::undefinedValue(); RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); jump(leaveStackFrame); } diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index d4a18ae886..9e38696d7a 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -153,6 +153,8 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo using TrustedImm64 = typename JITAssembler::TrustedImm64; using Jump = typename JITAssembler::Jump; using Label = typename JITAssembler::Label; + using ValueTypeInternal = Value::ValueTypeInternal_32; + using TargetPrimitive = TargetPrimitive32; static void emitSetGrayBit(JITAssembler *as, RegisterID base) { @@ -189,18 +191,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->pop(TargetPlatform::EngineRegister); } -#if WRITEBARRIER(steele) - static void emitWriteBarrier(JITAssembler *as, Address addr) - { -// RegisterID test = addr.base == TargetPlatform::ReturnValueRegister ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - // if (engine->writeBarrier) -// as->load8(Address(TargetPlatform::EngineRegister, offsetof(EngineBase, writeBarrierActive)), test); -// typename JITAssembler::Jump jump = as->branch32(JITAssembler::Equal, test, TrustedImm32(0)); - // ### emit fence - emitSetGrayBit(as, addr.base); -// jump.link(as); - } -#elif WRITEBARRIER(none) +#if WRITEBARRIER(none) static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} #endif @@ -223,9 +214,9 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->storeDouble(source, ptr, barrier); } - static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination, WriteBarrier::Type barrier) + static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { - as->store32(TrustedImm32(value.int_32()), destination); + as->store32(TrustedImm32(value.value()), destination); destination.offset += 4; as->store32(TrustedImm32(value.tag()), destination); if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) @@ -282,16 +273,16 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump done = as->jump(); intRange.link(as); as->move(srcReg, lowReg); - as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); done.link(as); } break; case IR::SInt32Type: as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Integer)), highReg); break; case IR::BoolType: as->move((RegisterID) t->index, lowReg); - as->move(TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); + as->move(TrustedImm32(quint32(QV4::Value::ValueTypeInternal_32::Boolean)), highReg); break; default: Q_UNREACHABLE(); @@ -304,9 +295,9 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo } } - static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) { - as->move(TrustedImm32(retVal.int_32()), TargetPlatform::LowReturnValueRegister); + as->move(TrustedImm32(retVal.value()), TargetPlatform::LowReturnValueRegister); as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); } @@ -382,7 +373,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo // check if it's an int32: Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer))); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { as->load32(addr, TargetPlatform::ReturnValueRegister); @@ -390,7 +381,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); as->store32(TargetPlatform::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_32::Integer)), targetAddr); if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) emitWriteBarrier(as, targetAddr); } else { @@ -435,6 +426,13 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); jump.linkTo(loop, as); } + + static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) + { + as->and32(TrustedImm32(Value::NotDouble_Mask), tagRegister); + Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(Value::NotDouble_Mask)); + return isNoDbl; + } }; template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> @@ -451,6 +449,8 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo using BranchTruncateType = typename JITAssembler::BranchTruncateType; using Jump = typename JITAssembler::Jump; using Label = typename JITAssembler::Label; + using ValueTypeInternal = Value::ValueTypeInternal_64; + using TargetPrimitive = TargetPrimitive64; static void emitSetGrayBit(JITAssembler *as, RegisterID base) { @@ -487,18 +487,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo as->pop(TargetPlatform::EngineRegister); } -#if WRITEBARRIER(steele) - static void emitWriteBarrier(JITAssembler *as, Address addr) - { -// RegisterID test = addr.base == TargetPlatform::ReturnValueRegister ? TargetPlatform::ScratchRegister : TargetPlatform::ReturnValueRegister; - // if (engine->writeBarrier) -// as->load8(Address(TargetPlatform::EngineRegister, offsetof(EngineBase, writeBarrierActive)), test); -// typename JITAssembler::Jump jump = as->branch32(JITAssembler::Equal, test, TrustedImm32(0)); - // ### emit fence - emitSetGrayBit(as, addr.base); -// jump.link(as); - } -#elif WRITEBARRIER(none) +#if WRITEBARRIER(none) static Q_ALWAYS_INLINE void emitWriteBarrier(JITAssembler *, Address) {} #endif @@ -558,7 +547,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump done = as->jump(); intRange.link(as); as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); - quint64 tag = QV4::Value::Integer_Type_Internal; + quint64 tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); as->or64(TrustedImm64(tag << 32), TargetPlatform::ReturnValueRegister); done.link(as); @@ -567,10 +556,10 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo quint64 tag; switch (t->type) { case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; + tag = quint64(QV4::Value::ValueTypeInternal_64::Integer); break; case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; + tag = quint64(QV4::Value::ValueTypeInternal_64::Boolean); break; default: tag = 31337; // bogus value @@ -584,12 +573,12 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo } } - static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + static void setFunctionReturnValueFromConst(JITAssembler *as, TargetPrimitive retVal) { as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); } - static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination, WriteBarrier::Type barrier) + static void storeValue(JITAssembler *as, TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { as->store64(TrustedImm64(value.rawValue()), destination); if (WriteBarrier::isRequired<WriteBarrier::Unknown>() && barrier == WriteBarrier::Barrier) @@ -628,7 +617,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Pointer addr = as->loadTempAddress(temp); as->load64(addr, dest); } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } } @@ -641,7 +630,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Pointer addr = as->loadArgLocalAddressForReading(dest, al); as->load64(addr, dest); } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } } @@ -650,7 +639,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo { Q_UNUSED(argumentNumber); - QV4::Value v = convertToValue(c); + auto v = convertToValue<TargetPrimitive64>(c); as->move(TrustedImm64(v.rawValue()), dest); } @@ -659,7 +648,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Q_UNUSED(argumentNumber); if (!expr) { - QV4::Value undefined = QV4::Primitive::undefinedValue(); + auto undefined = TargetPrimitive::undefinedValue(); as->move(TrustedImm64(undefined.rawValue()), dest); } else if (IR::Temp *t = expr->asTemp()){ loadArgumentInRegister(as, t, dest, argumentNumber); @@ -751,7 +740,7 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Pointer targetAddr = as->loadAddressForWriting(TargetPlatform::ScratchRegister, target, &barrier); as->store32(TargetPlatform::ReturnValueRegister, targetAddr); targetAddr.offset += 4; - as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + as->store32(TrustedImm32(quint32(Value::ValueTypeInternal_64::Integer)), targetAddr); if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) emitWriteBarrier(as, targetAddr); } else { @@ -783,6 +772,13 @@ struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatfo Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); jump.linkTo(loop, as); } + + static Jump checkIfTagRegisterIsDouble(JITAssembler *as, RegisterID tagRegister) + { + as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagRegister); + Jump isNoDbl = as->branch32(RelationalCondition::Equal, tagRegister, TrustedImm32(0)); + return isNoDbl; + } }; template <typename TargetConfiguration> @@ -851,8 +847,6 @@ public: return (hostOffset * RegisterSize) / QT_POINTER_SIZE; } - using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; - struct LookupCall { Address addr; uint getterSetterOffset; @@ -883,6 +877,10 @@ public: {} }; + using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + using ValueTypeInternal = typename RegisterSizeDependentOps::ValueTypeInternal; + using TargetPrimitive = typename RegisterSizeDependentOps::TargetPrimitive; + // V4 uses two stacks: one stack with QV4::Value items, which is checked by the garbage // collector, and one stack used by the native C/C++/ABI code. This C++ stack is not scanned // by the garbage collector, so if any JS object needs to be retained, it should be put on the @@ -1112,7 +1110,7 @@ public: } Pointer loadStringAddress(RegisterID reg, const QString &string); Address loadConstant(IR::Const *c, RegisterID baseReg); - Address loadConstant(const Primitive &v, RegisterID baseReg); + Address loadConstant(const TargetPrimitive &v, RegisterID baseReg); void loadStringRef(RegisterID reg, const QString &string); Pointer stackSlotPointer(IR::Temp *t) const { @@ -1387,12 +1385,12 @@ public: RegisterSizeDependentOps::emitWriteBarrier(this, dest); } - void storeValue(QV4::Primitive value, Address destination, WriteBarrier::Type barrier) + void storeValue(TargetPrimitive value, Address destination, WriteBarrier::Type barrier) { RegisterSizeDependentOps::storeValue(this, value, destination, barrier); } - void storeValue(QV4::Primitive value, IR::Expr* temp); + void storeValue(TargetPrimitive value, IR::Expr* temp); void emitWriteBarrier(Address addr, WriteBarrier::Type barrier) { if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) @@ -1426,13 +1424,7 @@ public: if (argumentNumber < RegisterArgumentCount) loadArgumentInRegister(value, registerForArgument(argumentNumber), argumentNumber); else -#if OS(WINDOWS) && CPU(X86_64) - loadArgumentOnStack<argumentNumber>(value, argumentNumber); -#elif CPU(MIPS) // Stack space for 4 arguments needs to be allocated for MIPS platforms. - loadArgumentOnStack<argumentNumber>(value, argumentNumber + 4); -#else // Sanity: - loadArgumentOnStack<argumentNumber - RegisterArgumentCount>(value, argumentNumber); -#endif + loadArgumentOnStack<argumentNumber - RegisterArgumentCount + (StackShadowSpace / RegisterSize)>(value, argumentNumber); } template <int argumentNumber> @@ -1576,8 +1568,8 @@ public: Address tagAddr = addr; tagAddr.offset += 4; - QV4::Primitive v = convertToValue(c); - store32(TrustedImm32(v.int_32()), addr); + auto v = convertToValue<TargetPrimitive>(c); + store32(TrustedImm32(v.value()), addr); store32(TrustedImm32(v.tag()), tagAddr); return Pointer(addr); } @@ -1593,7 +1585,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromBoolean(0).tag()), addr); + store32(TrustedImm32(TargetPrimitive::fromBoolean(0).tag()), addr); if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) RegisterSizeDependentOps::emitWriteBarrier(this, addr); } @@ -1640,7 +1632,7 @@ public: { store32(reg, addr); addr.offset += 4; - store32(TrustedImm32(QV4::Primitive::fromInt32(0).tag()), addr); + store32(TrustedImm32(TargetPrimitive::fromInt32(0).tag()), addr); if (WriteBarrier::isRequired<WriteBarrier::Primitive>() && barrier == WriteBarrier::Barrier) RegisterSizeDependentOps::emitWriteBarrier(this, addr); } @@ -1709,7 +1701,7 @@ public: RegisterID toInt32Register(IR::Expr *e, RegisterID scratchReg) { if (IR::Const *c = e->asConst()) { - move(TrustedImm32(convertToValue(c).int_32()), scratchReg); + move(TrustedImm32(convertToValue<Primitive>(c).int_32()), scratchReg); return scratchReg; } @@ -1748,11 +1740,11 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); + Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(quint32(ValueTypeInternal::Integer))); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); - Address inversionAddress = loadConstant(QV4::Primitive::fromDouble(double(INT_MAX) + 1), scratchReg); + Address inversionAddress = loadConstant(TargetPrimitive::fromDouble(double(INT_MAX) + 1), scratchReg); subDouble(inversionAddress, FPGpr0); Jump canNeverHappen = branchTruncateDoubleToUint32(FPGpr0, scratchReg); canNeverHappen.link(this); @@ -1808,6 +1800,9 @@ private: }; template <typename TargetConfiguration> +const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; + +template <typename TargetConfiguration> template <typename Result, typename Source> void Assembler<TargetConfiguration>::copyValue(Result result, Source source, WriteBarrier::Type barrier) { @@ -1832,7 +1827,7 @@ void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source, } else if (source->asTemp() || source->asArgLocal()) { RegisterSizeDependentOps::copyValueViaRegisters(this, source, result, barrier); } else if (IR::Const *c = source->asConst()) { - QV4::Primitive v = convertToValue(c); + auto v = convertToValue<TargetPrimitive>(c); storeValue(v, result, barrier); } else { Q_UNREACHABLE(); diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 9841620481..c2853a39d2 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -256,7 +256,7 @@ void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &na template <typename JITAssembler> void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) { - _as->storeValue(Primitive::fromBoolean(false), result); + _as->storeValue(JITAssembler::TargetPrimitive::fromBoolean(false), result); } template <typename JITAssembler> @@ -376,7 +376,7 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr ++arrayValueCount; // Index - _as->storeValue(QV4::Primitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); + _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); // Value _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); @@ -400,7 +400,7 @@ void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr ++arrayGetterSetterCount; // Index - _as->storeValue(QV4::Primitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); + _as->storeValue(JITAssembler::TargetPrimitive::fromUInt32(index), _as->stackLayout().argumentAddressForCall(argc++), WriteBarrier::NoBarrier); // Getter _as->copyValue(_as->stackLayout().argumentAddressForCall(argc++), it->expr, WriteBarrier::NoBarrier); @@ -488,7 +488,7 @@ void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::E _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(TrustedImm32(convertToValue(sourceConst).int_32()), + _as->move(TrustedImm32(convertToValue<Primitive>(sourceConst).int_32()), (RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); @@ -497,7 +497,7 @@ void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::E } } - _as->storeValue(convertToValue(sourceConst), target); + _as->storeValue(convertToValue<typename JITAssembler::TargetPrimitive>(sourceConst), target); } template <typename JITAssembler> @@ -781,10 +781,10 @@ void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr * quint32 tag; switch (regTemp->type) { case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; + tag = quint32(JITAssembler::ValueTypeInternal::Boolean); break; case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; + tag = quint32(JITAssembler::ValueTypeInternal::Integer); break; default: tag = 31337; // bogus value @@ -933,7 +933,7 @@ void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, I // check if it's an int32: Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); convertIntToDouble(source, target); Jump intDone = _as->jump(); @@ -1002,6 +1002,7 @@ void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR: generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, PointerToValue(source)); _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); + Q_FALLTHROUGH(); case IR::VarType: default: Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); @@ -1011,13 +1012,13 @@ void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR: // checkif it's a bool: Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Boolean_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); _as->load32(addr, JITTargetPlatform::ReturnValueRegister); Jump boolDone = _as->jump(); // check if it's an int32: notBool.link(_as); Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); _as->load32(addr, JITTargetPlatform::ReturnValueRegister); Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, TrustedImm32(0)); @@ -1087,7 +1088,7 @@ void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, I // check if it's an int32: Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(Value::Integer_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer))); Pointer addr = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, source); _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); Jump intDone = _as->jump(); @@ -1203,7 +1204,8 @@ void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) Address temp = _as->loadAddressForReading(JITTargetPlatform::ScratchRegister, s->cond); Address tag = temp; tag.offset += QV4::Value::tagOffset(); - Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, TrustedImm32(QV4::Value::Boolean_Type_Internal)); + Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); Address data = temp; data.offset += QV4::Value::valueOffset(); @@ -1322,12 +1324,12 @@ int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR:: } Pointer p = _as->stackLayout().callDataAddress(offsetof(CallData, tag)); - _as->store32(TrustedImm32(QV4::Value::Integer_Type_Internal), p); + _as->store32(TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Integer)), p); p = _as->stackLayout().callDataAddress(offsetof(CallData, argc)); _as->store32(TrustedImm32(argc), p); p = _as->stackLayout().callDataAddress(offsetof(CallData, thisObject)); if (!thisObject) - _as->storeValue(QV4::Primitive::undefinedValue(), p, WriteBarrier::NoBarrier); + _as->storeValue(JITAssembler::TargetPrimitive::undefinedValue(), p, WriteBarrier::NoBarrier); else _as->copyValue(p, thisObject, WriteBarrier::NoBarrier); @@ -1464,7 +1466,7 @@ bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal : RelationalCondition::NotEqual; - const TrustedImm32 tag(QV4::Value::Null_Type_Internal); + const TrustedImm32 tag{quint32(JITAssembler::ValueTypeInternal::Null)}; _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } @@ -1546,7 +1548,7 @@ bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, // check if the tag of the var operand is indicates 'boolean' _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, - TrustedImm32(QV4::Value::Boolean_Type_Internal)); + TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Boolean))); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1596,7 +1598,7 @@ bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOr if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(int(QV4::Value::Null_Type_Internal))); + Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(quint32(JITAssembler::ValueTypeInternal::Null))); Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); tagAddr.offset -= 4; _as->load32(tagAddr, tagReg); @@ -1648,18 +1650,18 @@ Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &arch using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; - if (architecture == QLatin1String("armv7")) + if (architecture == QLatin1String("arm")) return new ISelFactory<ARMv7CrossAssembler>; - else if (architecture == QLatin1String("armv8")) + else if (architecture == QLatin1String("arm64")) return new ISelFactory<ARM64CrossAssembler>; QString hostArch; #if CPU(ARM_THUMB2) - hostArch = QStringLiteral("armv7"); + hostArch = QStringLiteral("arm"); #elif CPU(MIPS) hostArch = QStringLiteral("mips"); #elif CPU(X86) - hostArch = QStringLiteral("x86"); + hostArch = QStringLiteral("i386"); #elif CPU(X86_64) hostArch = QStringLiteral("x86_64"); #endif diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 0d02284539..7019a117a2 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -160,7 +160,7 @@ protected: // FramePointerRegister points to its old value on the stack, and above // it we have the return address, hence the need to step over two // values before reaching the first argument. - return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*)); + return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * JITTargetPlatform::RegisterSize); } Pointer baseAddressForCallArguments() diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 8eafaaaa8a..d418b050c4 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -125,6 +125,7 @@ protected: *out << ri->prettyName(); break; } + Q_FALLTHROUGH(); } default: IRPrinterWithPositions::visitTemp(e); @@ -662,6 +663,7 @@ protected: // IRDecoder addUses(rightSource->asTemp(), Use::MustHaveRegister); break; } + Q_FALLTHROUGH(); #endif case OpBitAnd: case OpBitOr: diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index ce6156802d..6d788f4a93 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -405,7 +405,7 @@ public: << RI(JSC::ARMRegisters::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) #endif << RI(JSC::ARMRegisters::r10, QStringLiteral("r10"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) -#if CPU(ARM_THUMB2) && !defined(V4_BOOTSTRAP) +#if CPU(ARM_THUMB2) || defined(V4_BOOTSTRAP) << RI(JSC::ARMRegisters::r11, QStringLiteral("r11"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) #endif << RI(JSC::ARMRegisters::d2, QStringLiteral("d2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) diff --git a/src/qml/jsapi/qjsengine.cpp b/src/qml/jsapi/qjsengine.cpp index b52c859ecb..e4c150057a 100644 --- a/src/qml/jsapi/qjsengine.cpp +++ b/src/qml/jsapi/qjsengine.cpp @@ -333,7 +333,7 @@ QJSEngine::~QJSEngine() */ void QJSEngine::collectGarbage() { - d->m_v4Engine->memoryManager->runGC(/* forceFullCollection = */ true); + d->m_v4Engine->memoryManager->runGC(); } #if QT_DEPRECATED_SINCE(5, 6) diff --git a/src/qml/jsapi/qjsvalue.h b/src/qml/jsapi/qjsvalue.h index ab20a2607d..56bd64eec1 100644 --- a/src/qml/jsapi/qjsvalue.h +++ b/src/qml/jsapi/qjsvalue.h @@ -133,9 +133,9 @@ public: bool deleteProperty(const QString &name); bool isCallable() const; - QJSValue call(const QJSValueList &args = QJSValueList()); - QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); - QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); + QJSValue call(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const + QJSValue callWithInstance(const QJSValue &instance, const QJSValueList &args = QJSValueList()); // ### Qt6: Make const + QJSValue callAsConstructor(const QJSValueList &args = QJSValueList()); // ### Qt6: Make const #ifdef QT_DEPRECATED QT_DEPRECATED QJSEngine *engine() const; diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index b71e71b92f..02d3af619e 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -62,9 +62,8 @@ DEFINE_MANAGED_VTABLE(GlobalContext); Heap::CallContext *ExecutionContext::newCallContext(Function *function, CallData *callData) { - uint localsAndFormals = function->compiledFunction->nLocals + qMax(static_cast<uint>(callData->argc), function->nFormals); - size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + \ - sizeof(Value) * (localsAndFormals) + sizeof(CallData) - sizeof(Value); + uint localsAndFormals = function->compiledFunction->nLocals + sizeof(CallData)/sizeof(Value) - 1 + qMax(static_cast<uint>(callData->argc), function->nFormals); + size_t requiredMemory = sizeof(CallContext::Data) - sizeof(Value) + sizeof(Value) * (localsAndFormals); Heap::CallContext *c = d()->engine->memoryManager->allocManaged<CallContext>(requiredMemory); c->init(d()->engine, Heap::ExecutionContext::Type_CallContext); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 679cd41ce0..2735883603 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -136,6 +136,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , currentContext(0) , bumperPointerAllocator(new WTF::BumpPointerAllocator) , jsStack(new WTF::PageAllocation) + , gcStack(new WTF::PageAllocation) , globalCode(0) , v8Engine(0) , argumentsAccessors(0) @@ -148,8 +149,6 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) , m_profiler(0) #endif { - writeBarrierActive = true; - memoryManager = new QV4::MemoryManager(this); if (maxCallDepth == -1) { @@ -184,24 +183,28 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) "solutions for your platform."); } #else - factory = new JIT::ISelFactory; + factory = new JIT::ISelFactory<>; #endif } iselFactory.reset(factory); // reserve space for the JS stack - // we allow it to grow to 2 times JSStackLimit, as we can overshoot due to garbage collection - // and ScopedValues allocated outside of JIT'ed methods. - *jsStack = WTF::PageAllocation::allocate(2 * JSStackLimit, WTF::OSAllocator::JSVMStackPages, + // we allow it to grow to a bit more than JSStackLimit, as we can overshoot due to ScopedValues + // allocated outside of JIT'ed methods. + *jsStack = WTF::PageAllocation::allocate(JSStackLimit + 256*1024, WTF::OSAllocator::JSVMStackPages, /* writable */ true, /* executable */ false, /* includesGuardPages */ true); jsStackBase = (Value *)jsStack->base(); #ifdef V4_USE_VALGRIND - VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, 2*JSStackLimit); + VALGRIND_MAKE_MEM_UNDEFINED(jsStackBase, JSStackLimit + 256*1024); #endif jsStackTop = jsStackBase; + *gcStack = WTF::PageAllocation::allocate(GCStackLimit, WTF::OSAllocator::JSVMStackPages, + /* writable */ true, /* executable */ false, + /* includesGuardPages */ true); + exceptionValue = jsAlloca(1); globalObject = static_cast<Object *>(jsAlloca(1)); jsObjects = jsAlloca(NJSObjects); @@ -495,6 +498,8 @@ ExecutionEngine::~ExecutionEngine() delete executableAllocator; jsStack->deallocate(); delete jsStack; + gcStack->deallocate(); + delete gcStack; delete [] argumentsAccessors; } @@ -519,7 +524,7 @@ void ExecutionEngine::initRootContext() sizeof(GlobalContext::Data) + sizeof(CallData))); r->d_unchecked()->init(this); r->d()->callData = reinterpret_cast<CallData *>(r->d() + 1); - r->d()->callData->tag = QV4::Value::Integer_Type_Internal; + r->d()->callData->tag = quint32(Value::ValueTypeInternal::Integer); r->d()->callData->argc = 0; r->d()->callData->thisObject = globalObject; r->d()->callData->args[0] = Encode::undefined(); @@ -932,25 +937,23 @@ void ExecutionEngine::requireArgumentsAccessors(int n) } } -void ExecutionEngine::markObjects(bool incremental) +void ExecutionEngine::markObjects(MarkStack *markStack) { - if (!incremental) { - identifierTable->mark(this); + identifierTable->mark(markStack); - for (int i = 0; i < nArgumentsAccessors; ++i) { - const Property &pd = argumentsAccessors[i]; - if (Heap::FunctionObject *getter = pd.getter()) - getter->mark(this); - if (Heap::FunctionObject *setter = pd.setter()) - setter->mark(this); - } + for (int i = 0; i < nArgumentsAccessors; ++i) { + const Property &pd = argumentsAccessors[i]; + if (Heap::FunctionObject *getter = pd.getter()) + getter->mark(markStack); + if (Heap::FunctionObject *setter = pd.setter()) + setter->mark(markStack); + } - classPool->markObjects(this); + classPool->markObjects(markStack); - for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); - it != end; ++it) - (*it)->markObjects(this); - } + for (QSet<CompiledData::CompilationUnit*>::ConstIterator it = compilationUnits.constBegin(), end = compilationUnits.constEnd(); + it != end; ++it) + (*it)->markObjects(markStack); } ReturnedValue ExecutionEngine::throwError(const Value &value) @@ -1568,12 +1571,6 @@ QV4::ReturnedValue ExecutionEngine::metaTypeToJS(int type, const void *data) return 0; } -void ExecutionEngine::assertObjectBelongsToEngine(const Heap::Base &baseObject) -{ - Q_ASSERT(!baseObject.vtable()->isObject || static_cast<const Heap::Object&>(baseObject).internalClass->engine == this); - Q_UNUSED(baseObject); -} - void ExecutionEngine::failStackLimitCheck(Scope &scope) { scope.result = throwRangeError(QStringLiteral("Maximum call stack size exceeded.")); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 1160d69c6c..a2c774c295 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -108,18 +108,14 @@ public: WTF::BumpPointerAllocator *bumperPointerAllocator; // Used by Yarr Regex engine. - enum { JSStackLimit = 4*1024*1024 }; + enum { + JSStackLimit = 4*1024*1024, + GCStackLimit = 2*1024*1024 + }; WTF::PageAllocation *jsStack; Value *jsStackBase; - void pushForGC(Heap::Base *m) { - *jsStackTop = m; - ++jsStackTop; - } - Heap::Base *popForGC() { - --jsStackTop; - return jsStackTop->m(); - } + WTF::PageAllocation *gcStack; QML_NEARLY_ALWAYS_INLINE Value *jsAlloca(int nValues) { Value *ptr = jsStackTop; @@ -446,7 +442,7 @@ public: void requireArgumentsAccessors(int n); - void markObjects(bool incremental); + void markObjects(MarkStack *markStack); void initRootContext(); @@ -483,8 +479,6 @@ public: bool metaTypeFromJS(const Value *value, int type, void *data); QV4::ReturnedValue metaTypeToJS(int type, const void *data); - void assertObjectBelongsToEngine(const Heap::Base &baseObject); - bool checkStackLimits(Scope &scope); private: @@ -543,7 +537,7 @@ inline ExecutionContext *ExecutionEngine::parentContext(ExecutionContext *contex } inline -void Heap::Base::mark(QV4::ExecutionEngine *engine) +void Heap::Base::mark(QV4::MarkStack *markStack) { Q_ASSERT(inUse()); const HeapItem *h = reinterpret_cast<const HeapItem *>(this); @@ -553,19 +547,22 @@ void Heap::Base::mark(QV4::ExecutionEngine *engine) quintptr *bitmap = c->blackBitmap + Chunk::bitmapIndex(index); quintptr bit = Chunk::bitForIndex(index); if (!(*bitmap & bit)) { -#ifndef QT_NO_DEBUG - engine->assertObjectBelongsToEngine(*this); -#endif *bitmap |= bit; - engine->pushForGC(this); + markStack->push(this); } } -inline void Value::mark(ExecutionEngine *e) +inline void Value::mark(MarkStack *markStack) { Heap::Base *o = heapObject(); if (o) - o->mark(e); + o->mark(markStack); +} + +inline void Managed::mark(MarkStack *markStack) +{ + Q_ASSERT(m()); + m()->mark(markStack); } #define CHECK_STACK_LIMITS(v4, scope) if ((v4)->checkStackLimits(scope)) return; \ diff --git a/src/qml/jsruntime/qv4globalobject.cpp b/src/qml/jsruntime/qv4globalobject.cpp index 1bc91f832b..f0630660d4 100644 --- a/src/qml/jsruntime/qv4globalobject.cpp +++ b/src/qml/jsruntime/qv4globalobject.cpp @@ -394,6 +394,7 @@ void EvalFunction::evalCall(Scope &scope, CallData *callData, bool directCall) c // set the correct strict mode flag on the context ctx->d()->strictMode = false; ctx->d()->compilationUnit = function->compilationUnit; + ctx->d()->constantTable = function->compilationUnit->constants; scope.result = Q_V4_PROFILE(ctx->engine(), function); } diff --git a/src/qml/jsruntime/qv4identifiertable.cpp b/src/qml/jsruntime/qv4identifiertable.cpp index d3ef238716..3def6defbf 100644 --- a/src/qml/jsruntime/qv4identifiertable.cpp +++ b/src/qml/jsruntime/qv4identifiertable.cpp @@ -81,7 +81,6 @@ void IdentifierTable::addEntry(Heap::String *str) str->identifier = new Identifier; str->identifier->string = str->toQString(); str->identifier->hashValue = hash; - str->setMarkBit(); bool grow = (alloc <= size*2); diff --git a/src/qml/jsruntime/qv4identifiertable_p.h b/src/qml/jsruntime/qv4identifiertable_p.h index 89af5db731..b0b08f1e54 100644 --- a/src/qml/jsruntime/qv4identifiertable_p.h +++ b/src/qml/jsruntime/qv4identifiertable_p.h @@ -93,14 +93,14 @@ public: Heap::String *stringFromIdentifier(Identifier *i); - void mark(ExecutionEngine *e) { + void mark(MarkStack *markStack) { for (int i = 0; i < alloc; ++i) { Heap::String *entry = entries[i]; if (!entry || entry->isMarked()) continue; entry->setMarkBit(); Q_ASSERT(entry->vtable()->markObjects); - entry->vtable()->markObjects(entry, e); + entry->vtable()->markObjects(entry, markStack); } } }; diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index 9b18a5566e..3d9a672f2f 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -388,9 +388,9 @@ void InternalClass::destroy() } } -void InternalClassPool::markObjects(ExecutionEngine *engine) +void InternalClassPool::markObjects(MarkStack *markStack) { - Q_UNUSED(engine); + Q_UNUSED(markStack); } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index 1d8ef4b0fb..a29ce5b5ff 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -291,7 +291,7 @@ inline uint InternalClass::find(const String *string) struct InternalClassPool : public QQmlJS::MemoryPool { - void markObjects(ExecutionEngine *engine); + void markObjects(MarkStack *markStack); }; } diff --git a/src/qml/jsruntime/qv4managed_p.h b/src/qml/jsruntime/qv4managed_p.h index 3dc54b13da..f97771831c 100644 --- a/src/qml/jsruntime/qv4managed_p.h +++ b/src/qml/jsruntime/qv4managed_p.h @@ -207,9 +207,10 @@ public: bool inUse() const { return d()->inUse(); } bool markBit() const { return d()->isMarked(); } + inline void mark(MarkStack *markStack); static void destroy(Heap::Base *) {} - static void markObjects(Heap::Base *, ExecutionEngine *) {} + static void markObjects(Heap::Base *, MarkStack *) {} Q_ALWAYS_INLINE Heap::Base *heapObject() const { return m(); diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 59115dfe21..3427ee89fe 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -177,10 +177,10 @@ ReturnedValue ObjectIterator::nextPropertyNameAsString() DEFINE_OBJECT_VTABLE(ForEachIteratorObject); -void ForEachIteratorObject::markObjects(Heap::Base *that, ExecutionEngine *e) +void ForEachIteratorObject::markObjects(Heap::Base *that, MarkStack *markStack) { ForEachIteratorObject::Data *o = static_cast<ForEachIteratorObject::Data *>(that); - o->workArea[0].mark(e); - o->workArea[1].mark(e); - Object::markObjects(that, e); + o->workArea[0].mark(markStack); + o->workArea[1].mark(markStack); + Object::markObjects(that, markStack); } diff --git a/src/qml/jsruntime/qv4objectiterator_p.h b/src/qml/jsruntime/qv4objectiterator_p.h index 98e94a95ea..6168d59914 100644 --- a/src/qml/jsruntime/qv4objectiterator_p.h +++ b/src/qml/jsruntime/qv4objectiterator_p.h @@ -129,7 +129,7 @@ struct ForEachIteratorObject: Object { ReturnedValue nextPropertyName() { return d()->it().nextPropertyNameAsString(); } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); }; inline diff --git a/src/qml/jsruntime/qv4persistent.cpp b/src/qml/jsruntime/qv4persistent.cpp index de82bf835f..0b31c971f9 100644 --- a/src/qml/jsruntime/qv4persistent.cpp +++ b/src/qml/jsruntime/qv4persistent.cpp @@ -215,17 +215,15 @@ void PersistentValueStorage::free(Value *v) freePage(p); } -void PersistentValueStorage::mark(ExecutionEngine *e) +void PersistentValueStorage::mark(MarkStack *markStack) { - Value *markBase = e->jsStackTop; - Page *p = static_cast<Page *>(firstPage); while (p) { for (int i = 0; i < kEntriesPerPage; ++i) { if (Managed *m = p->values[i].as<Managed>()) - m->mark(e); + m->mark(markStack); } - e->memoryManager->drainMarkStack(markBase); + markStack->drain(); p = p->header.next; } @@ -384,11 +382,11 @@ void WeakValue::allocVal(ExecutionEngine *engine) val = engine->memoryManager->m_weakValues->allocate(); } -void WeakValue::markOnce(ExecutionEngine *e) +void WeakValue::markOnce(MarkStack *markStack) { if (!val) return; - val->mark(e); + val->mark(markStack); } void WeakValue::free() diff --git a/src/qml/jsruntime/qv4persistent_p.h b/src/qml/jsruntime/qv4persistent_p.h index c1cd1f34df..1f838f5531 100644 --- a/src/qml/jsruntime/qv4persistent_p.h +++ b/src/qml/jsruntime/qv4persistent_p.h @@ -65,7 +65,7 @@ struct Q_QML_EXPORT PersistentValueStorage Value *allocate(); static void free(Value *e); - void mark(ExecutionEngine *e); + void mark(MarkStack *markStack); struct Iterator { Iterator(void *p, int idx); @@ -203,7 +203,7 @@ public: bool isNullOrUndefined() const { return !val || val->isNullOrUndefined(); } void clear() { free(); } - void markOnce(ExecutionEngine *e); + void markOnce(MarkStack *markStack); private: Value *val; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 4f6c179026..1dd90995d3 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -539,7 +539,7 @@ ReturnedValue QObjectWrapper::wrap_slowPath(ExecutionEngine *engine, QObject *ob } } -void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) +void QObjectWrapper::markWrapper(QObject *object, MarkStack *markStack) { if (QQmlData::wasDeleted(object)) return; @@ -548,10 +548,10 @@ void QObjectWrapper::markWrapper(QObject *object, ExecutionEngine *engine) if (!ddata) return; - if (ddata->jsEngineId == engine->m_engineId) - ddata->jsWrapper.markOnce(engine); - else if (engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) - engine->m_multiplyWrappedQObjects->mark(object, engine); + if (ddata->jsEngineId == markStack->engine->m_engineId) + ddata->jsWrapper.markOnce(markStack); + else if (markStack->engine->m_multiplyWrappedQObjects && ddata->hasTaintedV4Object) + markStack->engine->m_multiplyWrappedQObjects->mark(object, markStack); } ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) @@ -938,36 +938,36 @@ void QObjectWrapper::method_disconnect(const BuiltinFunction *, Scope &scope, Ca RETURN_UNDEFINED(); } -static void markChildQObjectsRecursively(QObject *parent, QV4::ExecutionEngine *e) +static void markChildQObjectsRecursively(QObject *parent, QV4::MarkStack *markStack) { const QObjectList &children = parent->children(); for (int i = 0; i < children.count(); ++i) { QObject *child = children.at(i); if (!child) continue; - QObjectWrapper::markWrapper(child, e); - markChildQObjectsRecursively(child, e); + QObjectWrapper::markWrapper(child, markStack); + markChildQObjectsRecursively(child, markStack); } } -void QObjectWrapper::markObjects(Heap::Base *that, QV4::ExecutionEngine *e) +void QObjectWrapper::markObjects(Heap::Base *that, QV4::MarkStack *markStack) { QObjectWrapper::Data *This = static_cast<QObjectWrapper::Data *>(that); if (QObject *o = This->object()) { QQmlVMEMetaObject *vme = QQmlVMEMetaObject::get(o); if (vme) - vme->mark(e); + vme->mark(markStack); // Children usually don't need to be marked, the gc keeps them alive. // But in the rare case of a "floating" QObject without a parent that // _gets_ marked (we've been called here!) then we also need to // propagate the marking down to the children recursively. if (!o->parent()) - markChildQObjectsRecursively(o, e); + markChildQObjectsRecursively(o, markStack); } - QV4::Object::markObjects(that, e); + QV4::Object::markObjects(that, markStack); } void QObjectWrapper::destroyObject(bool lastCall) @@ -992,6 +992,10 @@ void QObjectWrapper::destroyObject(bool lastCall) // If the object is C++-owned, we still have to release the weak reference we have // to it. ddata->jsWrapper.clear(); + if (lastCall && ddata->propertyCache) { + ddata->propertyCache->release(); + ddata->propertyCache = nullptr; + } } } } @@ -2066,12 +2070,12 @@ void MultiplyWrappedQObjectMap::remove(QObject *key) erase(it); } -void MultiplyWrappedQObjectMap::mark(QObject *key, ExecutionEngine *engine) +void MultiplyWrappedQObjectMap::mark(QObject *key, MarkStack *markStack) { Iterator it = find(key); if (it == end()) return; - it->markOnce(engine); + it->markOnce(markStack); } void MultiplyWrappedQObjectMap::removeDestroyedObject(QObject *object) diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index c031a40211..55700d17c1 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -171,7 +171,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); static ReturnedValue wrap(ExecutionEngine *engine, QObject *object); - static void markWrapper(QObject *object, ExecutionEngine *engine); + static void markWrapper(QObject *object, MarkStack *markStack); using Object::get; @@ -195,7 +195,7 @@ protected: static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); - static void markObjects(Heap::Base *that, QV4::ExecutionEngine *e); + static void markObjects(Heap::Base *that, QV4::MarkStack *markStack); static void method_connect(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_disconnect(const BuiltinFunction *, Scope &scope, CallData *callData); @@ -209,13 +209,10 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje if (Q_UNLIKELY(QQmlData::wasDeleted(object))) return QV4::Encode::null(); - QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - if (Q_LIKELY(priv->declarativeData)) { - auto ddata = static_cast<QQmlData *>(priv->declarativeData); - if (Q_LIKELY(ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isUndefined())) { - // We own the JS object - return ddata->jsWrapper.value(); - } + auto ddata = QQmlData::get(object); + if (Q_LIKELY(ddata && ddata->jsEngineId == engine->m_engineId && !ddata->jsWrapper.isUndefined())) { + // We own the JS object + return ddata->jsWrapper.value(); } return wrap_slowPath(engine, object); @@ -295,7 +292,7 @@ public: ReturnedValue value(QObject *key) const { return QHash<QObject*, QV4::WeakValue>::value(key).value(); } Iterator erase(Iterator it); void remove(QObject *key); - void mark(QObject *key, ExecutionEngine *engine); + void mark(QObject *key, MarkStack *markStack); private Q_SLOTS: void removeDestroyedObject(QObject*); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index b28a5f9000..9da0df326f 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -472,6 +472,7 @@ Heap::String *RuntimeHelpers::convertToString(ExecutionEngine *engine, const Val switch (value.type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); + Q_UNREACHABLE(); case Value::Undefined_Type: return engine->id_undefined()->d(); case Value::Null_Type: @@ -504,6 +505,7 @@ static Heap::String *convert_to_string_add(ExecutionEngine *engine, const Value switch (value.type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); + Q_UNREACHABLE(); case Value::Undefined_Type: return engine->id_undefined()->d(); case Value::Null_Type: diff --git a/src/qml/jsruntime/qv4scopedvalue_p.h b/src/qml/jsruntime/qv4scopedvalue_p.h index 894434be16..e9dcc9172f 100644 --- a/src/qml/jsruntime/qv4scopedvalue_p.h +++ b/src/qml/jsruntime/qv4scopedvalue_p.h @@ -368,7 +368,7 @@ struct ScopedCallData { { int size = qMax(argc, QV4::Global::ReservedArgumentCount + int(offsetof(QV4::CallData, args)/sizeof(QV4::Value))); ptr = reinterpret_cast<CallData *>(scope.alloc(size)); - ptr->tag = QV4::Value::Integer_Type_Internal; + ptr->tag = quint32(QV4::Value::ValueTypeInternal::Integer); ptr->argc = argc; } diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 6d3110771e..2281fa22b6 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -78,13 +78,22 @@ static void generateWarning(QV4::ExecutionEngine *v4, const QString& description F(int, IntVector, QVector<int>, 0) \ F(qreal, RealVector, QVector<qreal>, 0.0) \ F(bool, BoolVector, QVector<bool>, false) \ + F(int, IntStdVector, std::vector<int>, 0) \ + F(qreal, RealStdVector, std::vector<qreal>, 0.0) \ + F(bool, BoolStdVector, std::vector<bool>, false) \ F(int, Int, QList<int>, 0) \ F(qreal, Real, QList<qreal>, 0.0) \ F(bool, Bool, QList<bool>, false) \ F(QString, String, QList<QString>, QString()) \ F(QString, QString, QStringList, QString()) \ + F(QString, StringVector, QVector<QString>, QString()) \ + F(QString, StringStdVector, std::vector<QString>, QString()) \ F(QUrl, Url, QList<QUrl>, QUrl()) \ + F(QUrl, UrlVector, QVector<QUrl>, QUrl()) \ + F(QUrl, UrlStdVector, std::vector<QUrl>, QUrl()) \ F(QModelIndex, QModelIndex, QModelIndexList, QModelIndex()) \ + F(QModelIndex, QModelIndexVector, QVector<QModelIndex>, QModelIndex()) \ + F(QModelIndex, QModelIndexStdVector, std::vector<QModelIndex>, QModelIndex()) \ F(QItemSelectionRange, QItemSelectionRange, QItemSelection, QItemSelectionRange()) static QV4::ReturnedValue convertElementToValue(QV4::ExecutionEngine *engine, const QString &element) @@ -263,11 +272,10 @@ public: } loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - if (signedIdx < d()->container->count()) { + if (index < size_t(d()->container->size())) { if (hasProperty) *hasProperty = true; - return convertElementToValue(engine(), d()->container->at(signedIdx)); + return convertElementToValue(engine(), qAsConst(*(d()->container))[index]); } if (hasProperty) *hasProperty = false; @@ -291,24 +299,22 @@ public: loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - - int count = d()->container->count(); + size_t count = size_t(d()->container->size()); typename Container::value_type element = convertValueToElement<typename Container::value_type>(value); - if (signedIdx == count) { - d()->container->append(element); - } else if (signedIdx < count) { - (*d()->container)[signedIdx] = element; + if (index == count) { + d()->container->push_back(element); + } else if (index < count) { + (*d()->container)[index] = element; } else { /* according to ECMA262r3 we need to insert */ /* the value at the given index, increasing length to index+1. */ - d()->container->reserve(signedIdx + 1); - while (signedIdx > count++) { - d()->container->append(typename Container::value_type()); + d()->container->reserve(index + 1); + while (index > count++) { + d()->container->push_back(typename Container::value_type()); } - d()->container->append(element); + d()->container->push_back(element); } if (d()->isReference) @@ -328,8 +334,7 @@ public: return QV4::Attr_Invalid; loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - return (signedIdx < d()->container->count()) ? QV4::Attr_Data : QV4::Attr_Invalid; + return (index < size_t(d()->container->size())) ? QV4::Attr_Data : QV4::Attr_Invalid; } void containerAdvanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) @@ -345,7 +350,7 @@ public: loadReference(); } - if (it->arrayIndex < static_cast<uint>(d()->container->count())) { + if (it->arrayIndex < static_cast<uint>(d()->container->size())) { *index = it->arrayIndex; ++it->arrayIndex; *attrs = QV4::Attr_Data; @@ -365,14 +370,13 @@ public: return false; loadReference(); } - qint32 signedIdx = static_cast<qint32>(index); - if (signedIdx >= d()->container->count()) + if (index >= size_t(d()->container->size())) return false; /* according to ECMA262r3 it should be Undefined, */ /* but we cannot, so we insert a default-value instead. */ - d()->container->replace(signedIdx, typename Container::value_type()); + (*d()->container)[index] = typename Container::value_type(); if (d()->isReference) storeReference(); @@ -457,7 +461,7 @@ public: RETURN_RESULT(Encode(0)); This->loadReference(); } - RETURN_RESULT(Encode(This->d()->container->count())); + RETURN_RESULT(Encode(qint32(This->d()->container->size()))); } static void method_set_length(const BuiltinFunction *, Scope &scope, CallData *callData) @@ -479,8 +483,8 @@ public: This->loadReference(); } /* Determine whether we need to modify the sequence */ - qint32 newCount = static_cast<qint32>(newLength); - qint32 count = This->d()->container->count(); + quint32 newCount = static_cast<quint32>(newLength); + quint32 count = static_cast<quint32>(This->d()->container->size()); if (newCount == count) { RETURN_UNDEFINED(); } else if (newCount > count) { @@ -489,14 +493,13 @@ public: /* We cannot, so we insert default-values instead. */ This->d()->container->reserve(newCount); while (newCount > count++) { - This->d()->container->append(typename Container::value_type()); + This->d()->container->push_back(typename Container::value_type()); } } else { /* according to ECMA262r3 we need to remove */ /* elements until the sequence is the required length. */ - while (newCount < count) { - count--; - This->d()->container->removeAt(count); + if (newCount < count) { + This->d()->container->erase(This->d()->container->begin() + newCount, This->d()->container->end()); } } /* write back if required. */ @@ -517,7 +520,7 @@ public: quint32 length = array->getLength(); QV4::ScopedValue v(scope); for (quint32 i = 0; i < length; ++i) - result << convertValueToElement<typename Container::value_type>((v = array->getIndexed(i))); + result.push_back(convertValueToElement<typename Container::value_type>((v = array->getIndexed(i)))); return QVariant::fromValue(result); } @@ -595,16 +598,34 @@ typedef QQmlSequence<QVector<qreal> > QQmlRealVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealVectorList); typedef QQmlSequence<QVector<bool> > QQmlBoolVectorList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolVectorList); +typedef QQmlSequence<std::vector<int> > QQmlIntStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntStdVectorList); +typedef QQmlSequence<std::vector<qreal> > QQmlRealStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlRealStdVectorList); +typedef QQmlSequence<std::vector<bool> > QQmlBoolStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlBoolStdVectorList); typedef QQmlSequence<QStringList> QQmlQStringList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQStringList); typedef QQmlSequence<QList<QString> > QQmlStringList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringList); +typedef QQmlSequence<QVector<QString> > QQmlStringVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringVectorList); +typedef QQmlSequence<std::vector<QString> > QQmlStringStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlStringStdVectorList); typedef QQmlSequence<QList<int> > QQmlIntList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlIntList); typedef QQmlSequence<QList<QUrl> > QQmlUrlList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlList); +typedef QQmlSequence<QVector<QUrl> > QQmlUrlVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlVectorList); +typedef QQmlSequence<std::vector<QUrl> > QQmlUrlStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlUrlStdVectorList); typedef QQmlSequence<QModelIndexList> QQmlQModelIndexList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexList); +typedef QQmlSequence<QVector<QModelIndex> > QQmlQModelIndexVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexVectorList); +typedef QQmlSequence<std::vector<QModelIndex> > QQmlQModelIndexStdVectorList; +DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQModelIndexStdVectorList); typedef QQmlSequence<QItemSelection> QQmlQItemSelectionRangeList; DEFINE_OBJECT_TEMPLATE_VTABLE(QQmlQItemSelectionRangeList); typedef QQmlSequence<QList<bool> > QQmlBoolList; diff --git a/src/qml/jsruntime/qv4string.cpp b/src/qml/jsruntime/qv4string.cpp index cde2131aab..71f85c2d71 100644 --- a/src/qml/jsruntime/qv4string.cpp +++ b/src/qml/jsruntime/qv4string.cpp @@ -54,12 +54,12 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(String); -void String::markObjects(Heap::Base *that, ExecutionEngine *e) +void String::markObjects(Heap::Base *that, MarkStack *markStack) { String::Data *s = static_cast<String::Data *>(that); if (s->largestSubLength) { - s->left->mark(e); - s->right->mark(e); + s->left->mark(markStack); + s->right->mark(markStack); } } diff --git a/src/qml/jsruntime/qv4string_p.h b/src/qml/jsruntime/qv4string_p.h index 5b0fd292d6..71e55cbcd4 100644 --- a/src/qml/jsruntime/qv4string_p.h +++ b/src/qml/jsruntime/qv4string_p.h @@ -204,7 +204,7 @@ struct Q_QML_PRIVATE_EXPORT String : public Managed { Identifier *identifier() const { return d()->identifier; } protected: - static void markObjects(Heap::Base *that, ExecutionEngine *e); + static void markObjects(Heap::Base *that, MarkStack *markStack); static bool isEqualTo(Managed *that, Managed *o); static uint getLength(const Managed *m); #endif diff --git a/src/qml/jsruntime/qv4value.cpp b/src/qml/jsruntime/qv4value.cpp index e34ac9c764..f41442df7a 100644 --- a/src/qml/jsruntime/qv4value.cpp +++ b/src/qml/jsruntime/qv4value.cpp @@ -113,6 +113,7 @@ double Value::toNumberImpl() const case QV4::Value::Managed_Type: #ifdef V4_BOOTSTRAP Q_UNIMPLEMENTED(); + Q_FALLTHROUGH(); #else if (String *s = stringValue()) return RuntimeHelpers::stringToNumber(s->toQString()); @@ -140,6 +141,7 @@ QString Value::toQStringNoThrow() const switch (type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); + Q_UNREACHABLE(); case Value::Undefined_Type: return QStringLiteral("undefined"); case Value::Null_Type: @@ -193,6 +195,7 @@ QString Value::toQString() const switch (type()) { case Value::Empty_Type: Q_ASSERT(!"empty Value encountered"); + Q_UNREACHABLE(); case Value::Undefined_Type: return QStringLiteral("undefined"); case Value::Null_Type: diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 11d75dde99..50cecb6598 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -207,23 +207,23 @@ public: } QML_NEARLY_ALWAYS_INLINE void setInt_32(int i) { - setTagValue(Integer_Type_Internal, quint32(i)); + setTagValue(quint32(ValueTypeInternal::Integer), quint32(i)); } QML_NEARLY_ALWAYS_INLINE uint uint_32() const { return value(); } QML_NEARLY_ALWAYS_INLINE void setEmpty() { - setTagValue(Empty_Type_Internal, value()); + setTagValue(quint32(ValueTypeInternal::Empty), value()); } QML_NEARLY_ALWAYS_INLINE void setEmpty(int i) { - setTagValue(Empty_Type_Internal, quint32(i)); + setTagValue(quint32(ValueTypeInternal::Empty), quint32(i)); } QML_NEARLY_ALWAYS_INLINE void setEmpty(quint32 i) { - setTagValue(Empty_Type_Internal, i); + setTagValue(quint32(ValueTypeInternal::Empty), i); } QML_NEARLY_ALWAYS_INLINE quint32 emptyValue() @@ -266,8 +266,17 @@ public: IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, Managed_Type_Internal_64 = 0 }; + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + enum class ValueTypeInternal_64 { + Empty = Immediate_Mask_64| 0, + ConvertibleToInt = Immediate_Mask_64| 0x10000u, // bit 48 + Null = ConvertibleToInt | 0x08000u, + Boolean = ConvertibleToInt | 0x04000u, + Integer = ConvertibleToInt | 0x02000u + }; + // Used only by 32-bit encoding enum Masks { SilentNaNBit = 0x00040000, @@ -275,6 +284,14 @@ public: }; static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; + enum class ValueTypeInternal_32 { + Empty = Immediate_Mask_32| 0, + ConvertibleToInt = Immediate_Mask_32| 0x10000u, // bit 48 + Null = ConvertibleToInt | 0x08000u, + Boolean = ConvertibleToInt | 0x04000u, + Integer = ConvertibleToInt | 0x02000u + }; + enum { Managed_Type_Internal_32 = NotDouble_Mask }; @@ -284,28 +301,23 @@ public: Managed_Type_Internal = Managed_Type_Internal_64 }; static const quint64 Immediate_Mask = Immediate_Mask_64; + using ValueTypeInternal = ValueTypeInternal_64; #else enum { Managed_Type_Internal = Managed_Type_Internal_32 }; static const quint64 Immediate_Mask = Immediate_Mask_32; + using ValueTypeInternal = ValueTypeInternal_32; #endif enum { NaN_Mask = 0x7ff80000, }; - enum ValueTypeInternal { - Empty_Type_Internal = Immediate_Mask | 0, - ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 - Null_Type_Internal = ConvertibleToInt | 0x08000u, - Boolean_Type_Internal = ConvertibleToInt | 0x04000u, - Integer_Type_Internal = ConvertibleToInt | 0x02000u - }; // used internally in property - inline bool isEmpty() const { return tag() == Empty_Type_Internal; } - inline bool isNull() const { return tag() == Null_Type_Internal; } - inline bool isBoolean() const { return tag() == Boolean_Type_Internal; } - inline bool isInteger() const { return tag() == Integer_Type_Internal; } + inline bool isEmpty() const { return tag() == quint32(ValueTypeInternal::Empty); } + inline bool isNull() const { return tag() == quint32(ValueTypeInternal::Null); } + inline bool isBoolean() const { return tag() == quint32(ValueTypeInternal::Boolean); } + inline bool isInteger() const { return tag() == quint32(ValueTypeInternal::Integer); } inline bool isNullOrUndefined() const { return isNull() || isUndefined(); } inline bool isNumber() const { return isDouble() || isInteger(); } @@ -330,9 +342,9 @@ public: inline bool isDouble() const { return (tag() & NotDouble_Mask) != NotDouble_Mask; } inline bool isManaged() const { return tag() == Managed_Type_Internal && !isUndefined(); } inline bool isManagedOrUndefined() const { return tag() == Managed_Type_Internal; } - inline bool integerCompatible() const { return (tag() & ConvertibleToInt) == ConvertibleToInt; } + inline bool integerCompatible() const { return (tag() & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } static inline bool integerCompatible(Value a, Value b) { - return ((a.tag() & b.tag()) & ConvertibleToInt) == ConvertibleToInt; + return ((a.tag() & b.tag()) & quint32(ValueTypeInternal::ConvertibleToInt)) == quint32(ValueTypeInternal::ConvertibleToInt); } static inline bool bothDouble(Value a, Value b) { return ((a.tag() | b.tag()) & NotDouble_Mask) != NotDouble_Mask; @@ -359,7 +371,7 @@ public: inline bool isString() const; inline bool isObject() const; inline bool isInt32() { - if (tag() == Integer_Type_Internal) + if (tag() == quint32(ValueTypeInternal::Integer)) return true; if (isDouble()) { double d = doubleValue(); @@ -372,7 +384,7 @@ public: return false; } double asDouble() const { - if (tag() == Integer_Type_Internal) + if (tag() == quint32(ValueTypeInternal::Integer)) return int_32(); return doubleValue(); } @@ -427,7 +439,7 @@ public: inline bool tryIntegerConversion() { bool b = integerCompatible(); if (b) - setTagValue(Integer_Type_Internal, value()); + setTagValue(quint32(ValueTypeInternal::Integer), value()); return b; } @@ -475,7 +487,7 @@ public: // Section 9.12 bool sameValue(Value other) const; - inline void mark(ExecutionEngine *e); + inline void mark(MarkStack *markStack); Value &operator =(const ScopedValue &v); Value &operator=(ReturnedValue v) { _val = v; return *this; } @@ -610,14 +622,14 @@ inline Primitive Primitive::emptyValue(uint e) inline Primitive Primitive::nullValue() { Primitive v; - v.setTagValue(Null_Type_Internal, 0); + v.setTagValue(quint32(ValueTypeInternal::Null), 0); return v; } inline Primitive Primitive::fromBoolean(bool b) { Primitive v; - v.setTagValue(Boolean_Type_Internal, b); + v.setTagValue(quint32(ValueTypeInternal::Boolean), b); return v; } @@ -639,7 +651,7 @@ inline Primitive Primitive::fromUInt32(uint i) { Primitive v; if (i < INT_MAX) { - v.setTagValue(Integer_Type_Internal, i); + v.setTagValue(quint32(ValueTypeInternal::Integer), i); } else { v.setDouble(i); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 8d523f17e9..e16df8dc60 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -251,6 +251,9 @@ int qt_v4DebuggerHook(const char *json) static void qt_v4CheckForBreak(QV4::ExecutionContext *context) { + if (!qt_v4IsStepping && !qt_v4Breakpoints.size()) + return; + const int lineNumber = context->d()->lineNumber; QV4::Function *function = qt_v4ExtractFunction(context); QString engineName = function->sourceFile(); @@ -585,7 +588,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code #endif // DO_TRACE_INSTR Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_callValue(engine, VALUE(instr.dest), callData)); @@ -595,7 +598,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_callProperty(engine, instr.name, callData)); @@ -604,7 +607,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallPropertyLookup) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_callPropertyLookup(engine, instr.lookupIndex, callData)); @@ -614,7 +617,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_callQmlScopeObjectProperty(engine, instr.index, callData)); @@ -624,7 +627,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code TRACE(property name, "%s, args=%u, argc=%u, this=%s", qPrintable(runtimeStrings[instr.name]->toQString()), instr.callData, instr.argc, (VALUE(instr.base)).toString(context)->toQString().toUtf8().constData()); Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_callQmlContextObjectProperty(engine, instr.index, callData)); @@ -633,7 +636,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallElement) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_callElement(engine, VALUE(instr.index), callData)); @@ -642,7 +645,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallActivationProperty) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_callActivationProperty(engine, instr.name, callData)); @@ -651,7 +654,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CallGlobalLookup) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_callGlobalLookup(engine, instr.index, callData)); @@ -757,7 +760,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateValue) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_constructValue(engine, VALUE(instr.func), callData)); @@ -766,7 +769,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateProperty) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_constructProperty(engine, instr.name, callData)); @@ -775,7 +778,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(ConstructPropertyLookup) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = VALUE(instr.base); STOREVALUE(instr.result, Runtime::method_constructPropertyLookup(engine, instr.index, callData)); @@ -784,7 +787,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(CreateActivationProperty) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_constructActivationProperty(engine, instr.name, callData)); @@ -793,7 +796,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code MOTH_BEGIN_INSTR(ConstructGlobalLookup) Q_ASSERT(instr.callData + instr.argc + offsetof(QV4::CallData, args)/sizeof(QV4::Value) <= stackSize); QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); - callData->tag = QV4::Value::Integer_Type_Internal; + callData->tag = quint32(Value::ValueTypeInternal::Integer); callData->argc = instr.argc; callData->thisObject = QV4::Primitive::undefinedValue(); STOREVALUE(instr.result, Runtime::method_constructGlobalLookup(engine, instr.index, callData)); diff --git a/src/qml/memory/qv4heap_p.h b/src/qml/memory/qv4heap_p.h index 1347a9bd6e..a38a938588 100644 --- a/src/qml/memory/qv4heap_p.h +++ b/src/qml/memory/qv4heap_p.h @@ -83,7 +83,7 @@ struct VTable uint type : 8; const char *className; void (*destroy)(Heap::Base *); - void (*markObjects)(Heap::Base *, ExecutionEngine *e); + void (*markObjects)(Heap::Base *, MarkStack *markStack); bool (*isEqualTo)(Managed *m, Managed *other); }; @@ -97,7 +97,7 @@ struct Q_QML_EXPORT Base { const VTable *vt; inline ReturnedValue asReturnedValue() const; - inline void mark(QV4::ExecutionEngine *engine); + inline void mark(QV4::MarkStack *markStack); void setVtable(const VTable *v) { vt = v; } const VTable *vtable() const { return vt; } @@ -127,6 +127,8 @@ struct Q_QML_EXPORT Base { return Chunk::testBit(c->objectBitmap, h - c->realBase()); } + inline void markChildren(MarkStack *markStack); + void *operator new(size_t, Managed *m) { return m; } void *operator new(size_t, Heap::Base *m) { return m; } void operator delete(void *, Heap::Base *) {} diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index c025dd09a4..c4bd1a733f 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -165,6 +165,13 @@ struct MemorySegment { size_t pageSize = WTF::pageSize(); size = (size + pageSize - 1) & ~(pageSize - 1); +#if !defined(Q_OS_LINUX) && !defined(Q_OS_WIN) + // Linux and Windows zero out pages that have been decommitted and get committed again. + // unfortunately that's not true on other OSes (e.g. BSD based ones), so zero out the + // memory before decommit, so that we can be sure that all chunks we allocate will be + // zero initialized. + memset(chunk, 0, size); +#endif pageReservation.decommit(chunk, size); } @@ -260,7 +267,76 @@ void ChunkAllocator::free(Chunk *chunk, size_t size) } -void Chunk::sweep() +void Heap::Base::markChildren(MarkStack *markStack) +{ + if (vtable()->markObjects) + vtable()->markObjects(this, markStack); + if (quint64 m = vtable()->markTable) { +// qDebug() << "using mark table:" << hex << m << "for" << h; + void **mem = reinterpret_cast<void **>(this); + while (m) { + MarkFlags mark = static_cast<MarkFlags>(m & 3); + switch (mark) { + case Mark_NoMark: + break; + case Mark_Value: +// qDebug() << "marking value at " << mem; + reinterpret_cast<Value *>(mem)->mark(markStack); + break; + case Mark_Pointer: { +// qDebug() << "marking pointer at " << mem; + Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem); + if (p) + p->mark(markStack); + break; + } + case Mark_ValueArray: { + Q_ASSERT(m == Mark_ValueArray); +// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h)); + ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem); + Value *v = a->values; + const Value *end = v + a->alloc; + if (a->alloc > 32*1024) { + // drain from time to time to avoid overflows in the js stack + Heap::Base **currentBase = markStack->top; + while (v < end) { + v->mark(markStack); + ++v; + if (markStack->top >= currentBase + 32*1024) { + Heap::Base **oldBase = markStack->base; + markStack->base = currentBase; + markStack->drain(); + markStack->base = oldBase; + } + } + } else { + while (v < end) { + v->mark(markStack); + ++v; + } + } + break; + } + } + + m >>= 2; + ++mem; + } + } +} + +// Stores a classname -> freed count mapping. +typedef QHash<const char*, int> MMStatsHash; +Q_GLOBAL_STATIC(MMStatsHash, freedObjectStatsGlobal) + +// This indirection avoids sticking QHash code in each of the call sites, which +// shaves off some instructions in the case that it's unused. +static void increaseFreedCountForClass(const char *className) +{ + (*freedObjectStatsGlobal())[className]++; +} + +void Chunk::sweep(ClassDestroyStatsCallback classCountPtr) { // DEBUG << "sweeping chunk" << this << (*freeList); HeapItem *o = realBase(); @@ -290,8 +366,11 @@ void Chunk::sweep() HeapItem *itemToFree = o + index; Heap::Base *b = *itemToFree; - if (b->vtable()->destroy) { - b->vtable()->destroy(b); + const VTable *v = b->vtable(); + if (Q_UNLIKELY(classCountPtr)) + classCountPtr(v->className); + if (v->destroy) { + v->destroy(b); b->_checkIsDestroyed(); } } @@ -351,7 +430,7 @@ void Chunk::resetBlackBits() static uint nGrayItems = 0; #endif -void Chunk::collectGrayItems(ExecutionEngine *engine) +void Chunk::collectGrayItems(MarkStack *markStack) { // DEBUG << "sweeping chunk" << this << (*freeList); HeapItem *o = realBase(); @@ -372,7 +451,7 @@ void Chunk::collectGrayItems(ExecutionEngine *engine) HeapItem *itemToFree = o + index; Heap::Base *b = *itemToFree; Q_ASSERT(b->inUse()); - engine->pushForGC(b); + markStack->push(b); #ifdef MM_STATS ++nGrayItems; // qDebug() << "adding gray item" << b << "to mark stack"; @@ -590,7 +669,7 @@ done: return m; } -void BlockAllocator::sweep() +void BlockAllocator::sweep(ClassDestroyStatsCallback classCountPtr) { nextFree = 0; nFree = 0; @@ -599,7 +678,7 @@ void BlockAllocator::sweep() // qDebug() << "BlockAlloc: sweep"; usedSlotsAfterLastSweep = 0; for (auto c : chunks) { - c->sweep(); + c->sweep(classCountPtr); c->sortIntoBins(freeBins, NumBins); // qDebug() << "used slots in chunk" << c << ":" << c->nUsedSlots(); usedSlotsAfterLastSweep += c->nUsedSlots(); @@ -620,10 +699,10 @@ void BlockAllocator::resetBlackBits() c->resetBlackBits(); } -void BlockAllocator::collectGrayItems(ExecutionEngine *engine) +void BlockAllocator::collectGrayItems(MarkStack *markStack) { for (auto c : chunks) - c->collectGrayItems(engine); + c->collectGrayItems(markStack); } @@ -665,22 +744,27 @@ HeapItem *HugeItemAllocator::allocate(size_t size) { return c->first(); } -static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocator::HugeChunk &c) +static void freeHugeChunk(ChunkAllocator *chunkAllocator, const HugeItemAllocator::HugeChunk &c, ClassDestroyStatsCallback classCountPtr) { HeapItem *itemToFree = c.chunk->first(); Heap::Base *b = *itemToFree; - if (b->vtable()->destroy) { - b->vtable()->destroy(b); + const VTable *v = b->vtable(); + if (Q_UNLIKELY(classCountPtr)) + classCountPtr(v->className); + + if (v->destroy) { + v->destroy(b); b->_checkIsDestroyed(); } chunkAllocator->free(c.chunk, c.size); } -void HugeItemAllocator::sweep() { - auto isBlack = [this] (const HugeChunk &c) { +void HugeItemAllocator::sweep(ClassDestroyStatsCallback classCountPtr) +{ + auto isBlack = [this, classCountPtr] (const HugeChunk &c) { bool b = c.chunk->first()->isBlack(); if (!b) - freeHugeChunk(chunkAllocator, c); + freeHugeChunk(chunkAllocator, c, classCountPtr); return !b; }; @@ -694,7 +778,7 @@ void HugeItemAllocator::resetBlackBits() Chunk::clearBit(c.chunk->blackBitmap, c.chunk->first() - c.chunk->realBase()); } -void HugeItemAllocator::collectGrayItems(ExecutionEngine *engine) +void HugeItemAllocator::collectGrayItems(MarkStack *markStack) { for (auto c : chunks) // Correct for a Steele type barrier @@ -702,14 +786,14 @@ void HugeItemAllocator::collectGrayItems(ExecutionEngine *engine) Chunk::testBit(c.chunk->grayBitmap, c.chunk->first() - c.chunk->realBase())) { HeapItem *i = c.chunk->first(); Heap::Base *b = *i; - b->mark(engine); + b->mark(markStack); } } void HugeItemAllocator::freeAll() { for (auto &c : chunks) { - freeHugeChunk(chunkAllocator, c); + freeHugeChunk(chunkAllocator, c, nullptr); } } @@ -852,77 +936,34 @@ Heap::Object *MemoryManager::allocObjectWithMemberData(std::size_t size, uint nM static uint markStackSize = 0; -void MemoryManager::drainMarkStack(Value *markBase) +MarkStack::MarkStack(ExecutionEngine *engine) + : engine(engine) +{ + base = (Heap::Base **)engine->gcStack->base(); + top = base; + limit = base + ExecutionEngine::GCStackLimit/sizeof(Heap::Base)*3/4; +} + +void MarkStack::drain() { - while (engine->jsStackTop > markBase) { - Heap::Base *h = engine->popForGC(); + while (top > base) { + Heap::Base *h = pop(); ++markStackSize; Q_ASSERT(h); // at this point we should only have Heap::Base objects in this area on the stack. If not, weird things might happen. - if (h->vtable()->markObjects) - h->vtable()->markObjects(h, engine); - if (quint64 m = h->vtable()->markTable) { -// qDebug() << "using mark table:" << hex << m << "for" << h; - void **mem = reinterpret_cast<void **>(h); - while (m) { - MarkFlags mark = static_cast<MarkFlags>(m & 3); - switch (mark) { - case Mark_NoMark: - break; - case Mark_Value: -// qDebug() << "marking value at " << mem; - reinterpret_cast<Value *>(mem)->mark(engine); - break; - case Mark_Pointer: { -// qDebug() << "marking pointer at " << mem; - Heap::Base *p = *reinterpret_cast<Heap::Base **>(mem); - if (p) - p->mark(engine); - break; - } - case Mark_ValueArray: { - Q_ASSERT(m == Mark_ValueArray); -// qDebug() << "marking Value Array at offset" << hex << (mem - reinterpret_cast<void **>(h)); - ValueArray<0> *a = reinterpret_cast<ValueArray<0> *>(mem); - Value *v = a->values; - const Value *end = v + a->alloc; - while (v < end) { - v->mark(engine); - ++v; - } - break; - } - } - - m >>= 2; - ++mem; - } - } + h->markChildren(this); } } -void MemoryManager::mark() +void MemoryManager::collectRoots(MarkStack *markStack) { - Value *markBase = engine->jsStackTop; - - markStackSize = 0; - - if (nextGCIsIncremental) { - // need to collect all gray items and push them onto the mark stack - blockAllocator.collectGrayItems(engine); - hugeItemAllocator.collectGrayItems(engine); - } - -// qDebug() << ">>>> Mark phase:"; -// qDebug() << " mark stack after gray items" << (engine->jsStackTop - markBase); - - engine->markObjects(nextGCIsIncremental); + engine->markObjects(markStack); // qDebug() << " mark stack after engine->mark" << (engine->jsStackTop - markBase); - collectFromJSStack(); + collectFromJSStack(markStack); // qDebug() << " mark stack after js stack collect" << (engine->jsStackTop - markBase); - m_persistentValues->mark(engine); + m_persistentValues->mark(markStack); // qDebug() << " mark stack after persistants" << (engine->jsStackTop - markBase); @@ -951,23 +992,25 @@ void MemoryManager::mark() } if (keepAlive) - qobjectWrapper->mark(engine); + qobjectWrapper->mark(markStack); - if (engine->jsStackTop >= engine->jsStackLimit) - drainMarkStack(markBase); + if (markStack->top >= markStack->limit) + markStack->drain(); } - - drainMarkStack(markBase); } -void MemoryManager::sweep(bool lastSweep) +void MemoryManager::mark() { - if (lastSweep && nextGCIsIncremental) { - // ensure we properly clean up on destruction even if the GC is in incremental mode - blockAllocator.resetBlackBits(); - hugeItemAllocator.resetBlackBits(); - } + markStackSize = 0; + + MarkStack markStack(engine); + collectRoots(&markStack); + + markStack.drain(); +} +void MemoryManager::sweep(bool lastSweep, ClassDestroyStatsCallback classCountPtr) +{ for (PersistentValueStorage::Iterator it = m_weakValues->begin(); it != m_weakValues->end(); ++it) { Managed *m = (*it).managed(); if (!m || m->markBit()) @@ -1013,8 +1056,8 @@ void MemoryManager::sweep(bool lastSweep) } } - blockAllocator.sweep(); - hugeItemAllocator.sweep(); + blockAllocator.sweep(classCountPtr); + hugeItemAllocator.sweep(classCountPtr); } bool MemoryManager::shouldRunGC() const @@ -1046,20 +1089,13 @@ size_t dumpBins(BlockAllocator *b, bool printOutput = true) return totalSlotMem*Chunk::SlotSize; } -void MemoryManager::runGC(bool forceFullCollection) +void MemoryManager::runGC() { if (gcBlocked) { // qDebug() << "Not running GC."; return; } - if (forceFullCollection) { - // do a full GC - blockAllocator.resetBlackBits(); - hugeItemAllocator.resetBlackBits(); - nextGCIsIncremental = false; - } - QScopedValueRollback<bool> gcBlocker(gcBlocked, true); // qDebug() << "runGC"; @@ -1081,7 +1117,6 @@ void MemoryManager::runGC(bool forceFullCollection) qDebug() << " Allocations since last GC" << allocationCount; allocationCount = 0; #endif - qDebug() << "Incremental:" << nextGCIsIncremental; qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); dumpBins(&blockAllocator); @@ -1095,7 +1130,7 @@ void MemoryManager::runGC(bool forceFullCollection) mark(); qint64 markTime = t.nsecsElapsed()/1000; t.restart(); - sweep(); + sweep(false, increaseFreedCountForClass); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); qint64 sweepTime = t.nsecsElapsed()/1000; @@ -1107,13 +1142,23 @@ void MemoryManager::runGC(bool forceFullCollection) qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; } size_t memInBins = dumpBins(&blockAllocator); -#ifdef MM_STATS - if (nextGCIsIncremental) - qDebug() << " number of gray items:" << nGrayItems; -#endif qDebug() << "Marked object in" << markTime << "us."; qDebug() << " " << markStackSize << "objects marked"; qDebug() << "Sweeped object in" << sweepTime << "us."; + + // sort our object types by number of freed instances + MMStatsHash freedObjectStats; + std::swap(freedObjectStats, *freedObjectStatsGlobal()); + typedef std::pair<const char*, int> ObjectStatInfo; + std::vector<ObjectStatInfo> freedObjectsSorted; + freedObjectsSorted.reserve(freedObjectStats.count()); + for (auto it = freedObjectStats.constBegin(); it != freedObjectStats.constEnd(); ++it) { + freedObjectsSorted.push_back(std::make_pair(it.key(), it.value())); + } + std::sort(freedObjectsSorted.begin(), freedObjectsSorted.end(), [](const ObjectStatInfo &a, const ObjectStatInfo &b) { + return a.second > b.second && strcmp(a.first, b.first) < 0; + }); + qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC :" << usedAfter; qDebug() << "Freed up bytes :" << (usedBefore - usedAfter); @@ -1125,6 +1170,11 @@ void MemoryManager::runGC(bool forceFullCollection) qDebug() << "Large item memory after GC:" << largeItemsAfter; qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); } + + for (auto it = freedObjectsSorted.cbegin(); it != freedObjectsSorted.cend(); ++it) { + qDebug().noquote() << QString::fromLatin1("Freed JS type: %1 (%2 instances)").arg(QString::fromLatin1(it->first), QString::number(it->second)); + } + qDebug() << "======== End GC ========"; } @@ -1133,36 +1183,11 @@ void MemoryManager::runGC(bool forceFullCollection) Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); } - if (!nextGCIsIncremental) - usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep; + usedSlotsAfterLastFullSweep = blockAllocator.usedSlotsAfterLastSweep; -#if WRITEBARRIER(steele) - static int count = 0; - ++count; - if (aggressiveGC) { - nextGCIsIncremental = (count % 256); - } else { - size_t total = blockAllocator.totalSlots(); - size_t usedSlots = blockAllocator.usedSlotsAfterLastSweep; - if (!nextGCIsIncremental) { - // always try an incremental GC after a full one, unless there is anyway lots of memory pressure - nextGCIsIncremental = usedSlots * 4 < total * 3; - count = 0; - } else { - if (count > 16) - nextGCIsIncremental = false; - else - nextGCIsIncremental = usedSlots * 4 < total * 3; // less than 75% full - } - } -#else - nextGCIsIncremental = false; -#endif - if (!nextGCIsIncremental) { - // do a full GC - blockAllocator.resetBlackBits(); - hugeItemAllocator.resetBlackBits(); - } + // reset all black bits + blockAllocator.resetBlackBits(); + hugeItemAllocator.resetBlackBits(); } size_t MemoryManager::getUsedMem() const @@ -1223,7 +1248,7 @@ void MemoryManager::willAllocate(std::size_t size) #endif // DETAILED_MM_STATS -void MemoryManager::collectFromJSStack() const +void MemoryManager::collectFromJSStack(MarkStack *markStack) const { Value *v = engine->jsStackBase; Value *top = engine->jsStackTop; @@ -1231,7 +1256,7 @@ void MemoryManager::collectFromJSStack() const Managed *m = v->managed(); if (m && m->inUse()) // Skip pointers to already freed objects, they are bogus as well - m->mark(engine); + m->mark(markStack); ++v; } } diff --git a/src/qml/memory/qv4mm_p.h b/src/qml/memory/qv4mm_p.h index 7f02a4f929..07e428b9bc 100644 --- a/src/qml/memory/qv4mm_p.h +++ b/src/qml/memory/qv4mm_p.h @@ -153,10 +153,10 @@ struct BlockAllocator { return used; } - void sweep(); + void sweep(ClassDestroyStatsCallback classCountPtr); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(MarkStack *markStack); // bump allocations HeapItem *nextFree = 0; @@ -176,10 +176,10 @@ struct HugeItemAllocator { {} HeapItem *allocate(size_t size); - void sweep(); + void sweep(ClassDestroyStatsCallback classCountPtr); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(MarkStack *markStack); size_t usedMem() const { size_t used = 0; @@ -422,7 +422,7 @@ public: return t->d(); } - void runGC(bool forceFullCollection = false); + void runGC(); void dumpStats() const; @@ -432,8 +432,6 @@ public: // called when a JS object grows itself. Specifically: Heap::String::append void changeUnmanagedHeapSizeUsage(qptrdiff delta) { unmanagedHeapSize += delta; } - void drainMarkStack(Value *markBase); - protected: /// expects size to be aligned @@ -446,10 +444,11 @@ protected: #endif // DETAILED_MM_STATS private: - void collectFromJSStack() const; + void collectFromJSStack(MarkStack *markStack) const; void mark(); - void sweep(bool lastSweep = false); + void sweep(bool lastSweep = false, ClassDestroyStatsCallback classCountPtr = nullptr); bool shouldRunGC() const; + void collectRoots(MarkStack *markStack); public: QV4::ExecutionEngine *engine; @@ -468,7 +467,6 @@ public: bool gcBlocked = false; bool aggressiveGC = false; bool gcStats = false; - bool nextGCIsIncremental = false; }; } diff --git a/src/qml/memory/qv4mmdefs_p.h b/src/qml/memory/qv4mmdefs_p.h index 1fc7b6a527..bf29b44a2c 100644 --- a/src/qml/memory/qv4mmdefs_p.h +++ b/src/qml/memory/qv4mmdefs_p.h @@ -59,6 +59,10 @@ QT_BEGIN_NAMESPACE namespace QV4 { +struct MarkStack; + +typedef void(*ClassDestroyStatsCallback)(const char *); + /* * Chunks are the basic structure containing GC managed objects. * @@ -182,10 +186,10 @@ struct Chunk { return usedSlots; } - void sweep(); + void sweep(ClassDestroyStatsCallback classCountPtr); void freeAll(); void resetBlackBits(); - void collectGrayItems(ExecutionEngine *engine); + void collectGrayItems(QV4::MarkStack *markStack); void sortIntoBins(HeapItem **bins, uint nBins); }; @@ -265,6 +269,24 @@ Q_STATIC_ASSERT(sizeof(HeapItem) == Chunk::SlotSize); Q_STATIC_ASSERT(QT_POINTER_SIZE*8 == Chunk::Bits); Q_STATIC_ASSERT((1 << Chunk::BitShift) == Chunk::Bits); +struct MarkStack {\ + MarkStack(ExecutionEngine *engine); + Heap::Base **top = 0; + Heap::Base **base = 0; + Heap::Base **limit = 0; + ExecutionEngine *engine; + void push(Heap::Base *m) { + *top = m; + ++top; + } + Heap::Base *pop() { + --top; + return *top; + } + void drain(); + +}; + // Base class for the execution engine #if defined(Q_CC_MSVC) || defined(Q_CC_GNU) diff --git a/src/qml/memory/qv4writebarrier_p.h b/src/qml/memory/qv4writebarrier_p.h index a2f85822ca..e36ea0749a 100644 --- a/src/qml/memory/qv4writebarrier_p.h +++ b/src/qml/memory/qv4writebarrier_p.h @@ -55,7 +55,6 @@ QT_BEGIN_NAMESPACE -#define WRITEBARRIER_steele -1 #define WRITEBARRIER_none 1 #define WRITEBARRIER(x) (1/WRITEBARRIER_##x == 1) @@ -78,42 +77,7 @@ enum NewValueType { // ### this needs to be filled with a real memory fence once marking is concurrent Q_ALWAYS_INLINE void fence() {} -#if WRITEBARRIER(steele) - -template <NewValueType type> -static Q_CONSTEXPR inline bool isRequired() { - return type != Primitive; -} - -inline void write(EngineBase *engine, Heap::Base *base, Value *slot, Value value) -{ - Q_UNUSED(engine); - *slot = value; - if (isRequired<Unknown>()) { - fence(); - base->setGrayBit(); - } -} - -inline void write(EngineBase *engine, Heap::Base *base, Value *slot, Heap::Base *value) -{ - Q_UNUSED(engine); - *slot = value; - if (isRequired<Object>()) { - fence(); - base->setGrayBit(); - } -} - -inline void write(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value) -{ - Q_UNUSED(engine); - *slot = value; - fence(); - base->setGrayBit(); -} - -#elif WRITEBARRIER(none) +#if WRITEBARRIER(none) template <NewValueType type> static Q_CONSTEXPR inline bool isRequired() { diff --git a/src/qml/parser/qqmljsglobal_p.h b/src/qml/parser/qqmljsglobal_p.h index 933c8f5202..0e195994b4 100644 --- a/src/qml/parser/qqmljsglobal_p.h +++ b/src/qml/parser/qqmljsglobal_p.h @@ -67,13 +67,17 @@ #else // !QT_CREATOR # define QT_QML_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE # define QT_QML_END_NAMESPACE QT_END_NAMESPACE -# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) - // QmlDevTools is a static library -# define QML_PARSER_EXPORT -# elif defined(QT_BUILD_QML_LIB) -# define QML_PARSER_EXPORT Q_DECL_EXPORT +# ifndef QT_STATIC +# if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) + // QmlDevTools is a static library +# define QML_PARSER_EXPORT +# elif defined(QT_BUILD_QML_LIB) +# define QML_PARSER_EXPORT Q_DECL_EXPORT +# else +# define QML_PARSER_EXPORT Q_DECL_IMPORT +# endif # else -# define QML_PARSER_EXPORT Q_DECL_IMPORT +# define QML_PARSER_EXPORT # endif #endif // QT_CREATOR diff --git a/src/qml/qml.pro b/src/qml/qml.pro index 8f9e4b7f83..be3956bb61 100644 --- a/src/qml/qml.pro +++ b/src/qml/qml.pro @@ -48,7 +48,9 @@ include(jit/jit.pri) include(jsruntime/jsruntime.pri) include(qml/qml.pri) include(debugger/debugger.pri) -include(animations/animations.pri) +qtConfig(animation) { + include(animations/animations.pri) +} include(types/types.pri) MODULE_PLUGIN_TYPES = \ diff --git a/src/qml/qml/qqmldata_p.h b/src/qml/qml/qqmldata_p.h index e271598c2d..2083326cd5 100644 --- a/src/qml/qml/qqmldata_p.h +++ b/src/qml/qml/qqmldata_p.h @@ -201,7 +201,9 @@ public: static QQmlData *get(const QObject *object, bool create = false) { QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - if (priv->wasDeleted) { + // If QObjectData::isDeletingChildren is set then access to QObjectPrivate::declarativeData has + // to be avoided because QObjectPrivate::currentChildBeingDeleted is in use. + if (priv->isDeletingChildren || priv->wasDeleted) { Q_ASSERT(!create); return 0; } else if (priv->declarativeData) { @@ -269,8 +271,8 @@ bool QQmlData::wasDeleted(QObject *object) if (!priv || priv->wasDeleted) return true; - return priv->declarativeData && - static_cast<QQmlData *>(priv->declarativeData)->isQueuedForDeletion; + QQmlData *ddata = QQmlData::get(object); + return ddata && ddata->isQueuedForDeletion; } QQmlNotifierEndpoint *QQmlData::notify(int index) diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index d4d21583ba..9ff76d7b24 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -82,7 +82,9 @@ #include <private/qqmllocale_p.h> #include <private/qqmlbind_p.h> #include <private/qqmlconnections_p.h> +#if QT_CONFIG(animation) #include <private/qqmltimer_p.h> +#endif #include <private/qqmllistmodel_p.h> #include <private/qqmlplatform_p.h> #include <private/qquickpackage_p.h> @@ -218,7 +220,9 @@ void QQmlEnginePrivate::registerBaseTypes(const char *uri, int versionMajor, int qmlRegisterType<QQmlBind,8>(uri, versionMajor, (versionMinor < 8 ? 8 : versionMinor), "Binding"); //Only available in >=2.8 qmlRegisterType<QQmlConnections,1>(uri, versionMajor, (versionMinor < 3 ? 3 : versionMinor), "Connections"); //Only available in >=2.3 qmlRegisterType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections"); +#if QT_CONFIG(animation) qmlRegisterType<QQmlTimer>(uri, versionMajor, versionMinor,"Timer"); +#endif qmlRegisterType<QQmlInstantiator>(uri, versionMajor, (versionMinor < 1 ? 1 : versionMinor), "Instantiator"); //Only available in >=2.1 qmlRegisterCustomType<QQmlConnections>(uri, versionMajor, versionMinor,"Connections", new QQmlConnectionsParser); qmlRegisterType<QQmlInstanceModel>(); @@ -709,9 +713,7 @@ QQmlEnginePrivate::~QQmlEnginePrivate() void QQmlPrivate::qdeclarativeelement_destructor(QObject *o) { - QObjectPrivate *p = QObjectPrivate::get(o); - if (p->declarativeData) { - QQmlData *d = static_cast<QQmlData*>(p->declarativeData); + if (QQmlData *d = QQmlData::get(o)) { if (d->ownContext && d->context) { d->context->destroy(); d->context = 0; @@ -881,13 +883,10 @@ void QQmlData::markAsDeleted(QObject *o) void QQmlData::setQueuedForDeletion(QObject *object) { if (object) { - if (QObjectPrivate *priv = QObjectPrivate::get(object)) { - if (!priv->wasDeleted && priv->declarativeData) { - QQmlData *ddata = QQmlData::get(object, false); - if (ddata->ownContext && ddata->context) - ddata->context->emitDestruction(); - ddata->isQueuedForDeletion = true; - } + if (QQmlData *ddata = QQmlData::get(object)) { + if (ddata->ownContext && ddata->context) + ddata->context->emitDestruction(); + ddata->isQueuedForDeletion = true; } } } @@ -1336,17 +1335,11 @@ QQmlContext *QQmlEngine::contextForObject(const QObject *object) if(!object) return 0; - QObjectPrivate *priv = QObjectPrivate::get(const_cast<QObject *>(object)); - - QQmlData *data = - static_cast<QQmlData *>(priv->declarativeData); - - if (!data) - return 0; - else if (data->outerContext) + QQmlData *data = QQmlData::get(object); + if (data && data->outerContext) return data->outerContext->asQQmlContext(); - else - return 0; + + return 0; } /*! @@ -1881,6 +1874,7 @@ void QQmlData::setPendingBindingBit(QObject *obj, int coreIndex) QQmlData *QQmlData::createQQmlData(QObjectPrivate *priv) { Q_ASSERT(priv); + Q_ASSERT(!priv->isDeletingChildren); priv->declarativeData = new QQmlData; return static_cast<QQmlData *>(priv->declarativeData); } diff --git a/src/qml/qml/qqmlerror.cpp b/src/qml/qml/qqmlerror.cpp index 7a1e02eec6..64f008cd32 100644 --- a/src/qml/qml/qqmlerror.cpp +++ b/src/qml/qml/qqmlerror.cpp @@ -44,6 +44,7 @@ #include <QtCore/qfile.h> #include <QtCore/qstringlist.h> #include <QtCore/qvector.h> +#include <QtCore/qpointer.h> #include <private/qv4errorobject_p.h> @@ -86,7 +87,7 @@ public: quint16 line; quint16 column; QtMsgType messageType; - QObject *object; + QPointer<QObject> object; }; QQmlErrorPrivate::QQmlErrorPrivate() diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 85fbd86dc4..2cbcfbbfb6 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -1075,7 +1075,9 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo { QQmlData *ddata = new (ddataMemory) QQmlData; ddata->ownMemory = false; - QObjectPrivate::get(instance)->declarativeData = ddata; + QObjectPrivate* p = QObjectPrivate::get(instance); + Q_ASSERT(!p->isDeletingChildren); + p->declarativeData = ddata; } const int parserStatusCast = type->parserStatusCast(); diff --git a/src/qml/qml/qqmlproperty.cpp b/src/qml/qml/qqmlproperty.cpp index ca522c29af..9b5f7b0a06 100644 --- a/src/qml/qml/qqmlproperty.cpp +++ b/src/qml/qml/qqmlproperty.cpp @@ -1627,7 +1627,7 @@ QMetaMethod QQmlPropertyPrivate::findSignalByName(const QMetaObject *mo, const Q */ static inline void flush_vme_signal(const QObject *object, int index, bool indexInSignalRange) { - QQmlData *data = static_cast<QQmlData *>(QObjectPrivate::get(const_cast<QObject *>(object))->declarativeData); + QQmlData *data = QQmlData::get(object); if (data && data->propertyCache) { QQmlPropertyData *property = indexInSignalRange ? data->propertyCache->signal(index) : data->propertyCache->method(index); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index f464a099e0..9f86d1cae9 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -1170,16 +1170,16 @@ void QQmlVMEMetaObject::ensureQObjectWrapper() QV4::QObjectWrapper::wrap(v4, object); } -void QQmlVMEMetaObject::mark(QV4::ExecutionEngine *e) +void QQmlVMEMetaObject::mark(QV4::MarkStack *markStack) { QV4::ExecutionEngine *v4 = cache ? cache->engine : 0; - if (v4 != e) + if (v4 != markStack->engine) return; - propertyAndMethodStorage.markOnce(e); + propertyAndMethodStorage.markOnce(markStack); if (QQmlVMEMetaObject *parent = parentVMEMetaObject()) - parent->mark(e); + parent->mark(markStack); } bool QQmlVMEMetaObject::aliasTarget(int index, QObject **target, int *coreIndex, int *valueTypeIndex) const diff --git a/src/qml/qml/qqmlvmemetaobject_p.h b/src/qml/qml/qqmlvmemetaobject_p.h index bb6fede7c8..031a9a9ddd 100644 --- a/src/qml/qml/qqmlvmemetaobject_p.h +++ b/src/qml/qml/qqmlvmemetaobject_p.h @@ -203,7 +203,7 @@ public: void ensureQObjectWrapper(); - void mark(QV4::ExecutionEngine *e); + void mark(QV4::MarkStack *markStack); void connectAlias(int aliasId); diff --git a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp index 68a64a28f0..8cc0b32168 100644 --- a/src/qml/qml/v8/qqmlbuiltinfunctions.cpp +++ b/src/qml/qml/v8/qqmlbuiltinfunctions.cpp @@ -2019,7 +2019,7 @@ void GlobalExtensions::method_qsTrIdNoOp(const BuiltinFunction *, Scope &scope, void GlobalExtensions::method_gc(const BuiltinFunction *, Scope &scope, CallData *) { - scope.engine->memoryManager->runGC(/* forceFullCollection = */ true); + scope.engine->memoryManager->runGC(); scope.result = QV4::Encode::undefined(); } diff --git a/src/qml/types/qqmlconnections.cpp b/src/qml/types/qqmlconnections.cpp index 870aeaa6e2..66bc772279 100644 --- a/src/qml/types/qqmlconnections.cpp +++ b/src/qml/types/qqmlconnections.cpp @@ -235,14 +235,13 @@ void QQmlConnectionsParser::verifyBindings(const QV4::CompiledData::Unit *qmlUni { for (int ii = 0; ii < props.count(); ++ii) { const QV4::CompiledData::Binding *binding = props.at(ii); - QString propName = qmlUnit->stringAt(binding->propertyNameIndex); + const QString &propName = qmlUnit->stringAt(binding->propertyNameIndex); - if (!propName.startsWith(QLatin1String("on")) || !propName.at(2).isUpper()) { + if (!propName.startsWith(QLatin1String("on")) || (propName.length() < 3 || !propName.at(2).isUpper())) { error(props.at(ii), QQmlConnections::tr("Cannot assign to non-existent property \"%1\"").arg(propName)); return; } - if (binding->type >= QV4::CompiledData::Binding::Type_Object) { const QV4::CompiledData::Object *target = qmlUnit->objectAt(binding->value.objectIndex); if (!qmlUnit->stringAt(target->inheritedTypeNameIndex).isEmpty()) diff --git a/src/qml/types/qqmldelegatemodel.cpp b/src/qml/types/qqmldelegatemodel.cpp index 34bc266cb5..f26e5f6cdb 100644 --- a/src/qml/types/qqmldelegatemodel.cpp +++ b/src/qml/types/qqmldelegatemodel.cpp @@ -1964,9 +1964,8 @@ void QQmlDelegateModelItem::destroyObject() Q_ASSERT(object); Q_ASSERT(contextData); - QObjectPrivate *p = QObjectPrivate::get(object); - Q_ASSERT(p->declarativeData); - QQmlData *data = static_cast<QQmlData*>(p->declarativeData); + QQmlData *data = QQmlData::get(object); + Q_ASSERT(data); if (data->ownContext && data->context) data->context->clearContext(); object->deleteLater(); @@ -1983,10 +1982,8 @@ void QQmlDelegateModelItem::destroyObject() QQmlDelegateModelItem *QQmlDelegateModelItem::dataForObject(QObject *object) { - QObjectPrivate *p = QObjectPrivate::get(object); - QQmlContextData *context = p->declarativeData - ? static_cast<QQmlData *>(p->declarativeData)->context - : 0; + QQmlData *d = QQmlData::get(object); + QQmlContextData *context = d ? d->context : 0; for (context = context ? context->parent : 0; context; context = context->parent) { if (QQmlDelegateModelItem *cacheItem = qobject_cast<QQmlDelegateModelItem *>( context->contextObject)) { diff --git a/src/qml/types/types.pri b/src/qml/types/types.pri index d2e5020738..e85ab5982b 100644 --- a/src/qml/types/types.pri +++ b/src/qml/types/types.pri @@ -7,7 +7,6 @@ SOURCES += \ $$PWD/qqmlmodelsmodule.cpp \ $$PWD/qqmlmodelindexvaluetype.cpp \ $$PWD/qqmlobjectmodel.cpp \ - $$PWD/qqmltimer.cpp \ $$PWD/qquickpackage.cpp \ $$PWD/qquickworkerscript.cpp \ $$PWD/qqmlinstantiator.cpp @@ -23,8 +22,15 @@ HEADERS += \ $$PWD/qqmlmodelsmodule_p.h \ $$PWD/qqmlmodelindexvaluetype_p.h \ $$PWD/qqmlobjectmodel_p.h \ - $$PWD/qqmltimer_p.h \ $$PWD/qquickpackage_p.h \ $$PWD/qquickworkerscript_p.h \ $$PWD/qqmlinstantiator_p.h \ $$PWD/qqmlinstantiator_p_p.h + +qtConfig(animation) { + SOURCES += \ + $$PWD/qqmltimer.cpp + + HEADERS += \ + $$PWD/qqmltimer_p.h +} |