diff options
author | Laszlo Agocs <laszlo.agocs@qt.io> | 2017-03-01 11:54:30 +0100 |
---|---|---|
committer | Laszlo Agocs <laszlo.agocs@qt.io> | 2017-03-01 11:54:35 +0100 |
commit | 87aebe39ae80c0752695b21b5b7508d220035b85 (patch) | |
tree | a60bd61dd81b5e79532762ae014cb7612d619da2 /src/qml | |
parent | a48244d5aa1d14c286b7cd39afebcfff9c9dcb60 (diff) | |
parent | afec9016d0fd51345ea93a1bbadb99b5c3fdf629 (diff) |
Merge remote-tracking branch 'origin/dev' into wip/scenegraphng
Change-Id: Iba540adaeffb0098fc4e1923050eb611bf47287b
Diffstat (limited to 'src/qml')
105 files changed, 5004 insertions, 3223 deletions
diff --git a/src/qml/compiler/compiler.pri b/src/qml/compiler/compiler.pri index 1de5dfa6fa..fa66d3a6e3 100644 --- a/src/qml/compiler/compiler.pri +++ b/src/qml/compiler/compiler.pri @@ -39,7 +39,10 @@ SOURCES += \ unix: SOURCES += $$PWD/qv4compilationunitmapper_unix.cpp else: SOURCES += $$PWD/qv4compilationunitmapper_win.cpp -qtConfig(qml-interpreter) { +qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD +} + +qmldevtools_build|qtConfig(qml-interpreter) { HEADERS += \ $$PWD/qv4instr_moth_p.h \ $$PWD/qv4isel_moth_p.h @@ -48,6 +51,3 @@ qtConfig(qml-interpreter) { $$PWD/qv4isel_moth.cpp } - -qtConfig(private_tests): LIBS_PRIVATE += $$QMAKE_LIBS_DYNLOAD -} diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 54d0cb4f46..030f485504 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -1605,7 +1605,7 @@ QVector<int> JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList<Compil scan.leaveEnvironment(); scan.leaveEnvironment(); - _env = 0; + _variableEnvironment = 0; _function = _module->functions.at(defineFunction(QStringLiteral("context scope"), qmlRoot, 0, 0)); for (int i = 0; i < functions.count(); ++i) { @@ -2061,4 +2061,156 @@ QQmlPropertyData *PropertyResolver::signal(const QString &name, bool *notInRevis return 0; } +IRLoader::IRLoader(const QV4::CompiledData::Unit *qmlData, QmlIR::Document *output) + : unit(qmlData) + , output(output) +{ + pool = output->jsParserEngine.pool(); +} + +void IRLoader::load() +{ + output->jsGenerator.stringTable.clear(); + for (uint i = 0; i < unit->stringTableSize; ++i) + output->jsGenerator.stringTable.registerString(unit->stringAt(i)); + + for (quint32 i = 0; i < unit->nImports; ++i) + output->imports << unit->importAt(i); + + if (unit->flags & QV4::CompiledData::Unit::IsSingleton) { + QmlIR::Pragma *p = New<QmlIR::Pragma>(); + p->location = QV4::CompiledData::Location(); + p->type = QmlIR::Pragma::PragmaSingleton; + output->pragmas << p; + } + + output->indexOfRootObject = unit->indexOfRootObject; + + for (uint i = 0; i < unit->nObjects; ++i) { + const QV4::CompiledData::Object *serializedObject = unit->objectAt(i); + QmlIR::Object *object = loadObject(serializedObject); + output->objects.append(object); + } +} + +struct FakeExpression : public QQmlJS::AST::NullExpression +{ + FakeExpression(int start, int length) + : location(start, length) + {} + + virtual QQmlJS::AST::SourceLocation firstSourceLocation() const + { return location; } + + virtual QQmlJS::AST::SourceLocation lastSourceLocation() const + { return location; } + +private: + QQmlJS::AST::SourceLocation location; +}; + +QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedObject) +{ + QmlIR::Object *object = pool->New<QmlIR::Object>(); + object->init(pool, serializedObject->inheritedTypeNameIndex, serializedObject->idNameIndex); + + object->indexOfDefaultPropertyOrAlias = serializedObject->indexOfDefaultPropertyOrAlias; + object->defaultPropertyIsAlias = serializedObject->defaultPropertyIsAlias; + + object->location = serializedObject->location; + object->locationOfIdProperty = serializedObject->locationOfIdProperty; + + QVector<int> functionIndices; + functionIndices.reserve(serializedObject->nFunctions + serializedObject->nBindings / 2); + + for (uint i = 0; i < serializedObject->nBindings; ++i) { + QmlIR::Binding *b = pool->New<QmlIR::Binding>(); + *static_cast<QV4::CompiledData::Binding*>(b) = serializedObject->bindingTable()[i]; + object->bindings->append(b); + if (b->type == QV4::CompiledData::Binding::Type_Script) { + functionIndices.append(b->value.compiledScriptIndex); + b->value.compiledScriptIndex = functionIndices.count() - 1; + + QmlIR::CompiledFunctionOrExpression *foe = pool->New<QmlIR::CompiledFunctionOrExpression>(); + foe->disableAcceleratedLookups = true; + foe->nameIndex = 0; + + QQmlJS::AST::ExpressionNode *expr; + + if (b->stringIndex != quint32(0)) { + const int start = output->code.length(); + const QString script = output->stringAt(b->stringIndex); + const int length = script.length(); + output->code.append(script); + expr = new (pool) FakeExpression(start, length); + } else + expr = new (pool) QQmlJS::AST::NullExpression(); + foe->node = new (pool) QQmlJS::AST::ExpressionStatement(expr); // dummy + object->functionsAndExpressions->append(foe); + } + } + + Q_ASSERT(object->functionsAndExpressions->count == functionIndices.count()); + + for (uint i = 0; i < serializedObject->nSignals; ++i) { + const QV4::CompiledData::Signal *serializedSignal = serializedObject->signalAt(i); + QmlIR::Signal *s = pool->New<QmlIR::Signal>(); + s->nameIndex = serializedSignal->nameIndex; + s->location = serializedSignal->location; + s->parameters = pool->New<QmlIR::PoolList<QmlIR::SignalParameter> >(); + + for (uint i = 0; i < serializedSignal->nParameters; ++i) { + QmlIR::SignalParameter *p = pool->New<QmlIR::SignalParameter>(); + *static_cast<QV4::CompiledData::Parameter*>(p) = *serializedSignal->parameterAt(i); + s->parameters->append(p); + } + + object->qmlSignals->append(s); + } + + const QV4::CompiledData::Property *serializedProperty = serializedObject->propertyTable(); + for (uint i = 0; i < serializedObject->nProperties; ++i, ++serializedProperty) { + QmlIR::Property *p = pool->New<QmlIR::Property>(); + *static_cast<QV4::CompiledData::Property*>(p) = *serializedProperty; + object->properties->append(p); + } + + QQmlJS::Engine *jsParserEngine = &output->jsParserEngine; + + const QV4::CompiledData::LEUInt32 *functionIdx = serializedObject->functionOffsetTable(); + for (uint i = 0; i < serializedObject->nFunctions; ++i, ++functionIdx) { + QmlIR::Function *f = pool->New<QmlIR::Function>(); + const QV4::CompiledData::Function *compiledFunction = unit->functionAt(*functionIdx); + + functionIndices.append(*functionIdx); + f->index = functionIndices.count() - 1; + f->location = compiledFunction->location; + f->nameIndex = compiledFunction->nameIndex; + + QQmlJS::AST::FormalParameterList *paramList = 0; + const QV4::CompiledData::LEUInt32 *formalNameIdx = compiledFunction->formalsTable(); + for (uint i = 0; i < compiledFunction->nFormals; ++i, ++formalNameIdx) { + const QString formal = unit->stringAt(*formalNameIdx); + QStringRef paramNameRef = jsParserEngine->newStringRef(formal); + + if (paramList) + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramList, paramNameRef); + else + paramList = new (pool) QQmlJS::AST::FormalParameterList(paramNameRef); + } + + if (paramList) + paramList = paramList->finish(); + + const QString name = unit->stringAt(compiledFunction->nameIndex); + f->functionDeclaration = new(pool) QQmlJS::AST::FunctionDeclaration(jsParserEngine->newStringRef(name), paramList, /*body*/0); + + object->functions->append(f); + } + + object->runtimeFunctionIndices.allocate(pool, functionIndices); + + return object; +} + #endif // V4_BOOTSTRAP diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 95756845c3..2022112e07 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -622,6 +622,21 @@ private: int _importedScriptsTemp; }; +struct IRLoader { + IRLoader(const QV4::CompiledData::Unit *unit, QmlIR::Document *output); + + void load(); + +private: + QmlIR::Object *loadObject(const QV4::CompiledData::Object *serializedObject); + + template <typename _Tp> _Tp *New() { return pool->New<_Tp>(); } + + const QV4::CompiledData::Unit *unit; + QmlIR::Document *output; + QQmlJS::MemoryPool *pool; +}; + } // namespace QmlIR QT_END_NAMESPACE diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index ab2b0553a9..85267225be 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -57,12 +57,12 @@ QT_BEGIN_NAMESPACE QQmlTypeCompiler::QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, - QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &importCache, + QmlIR::Document *parsedQML, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) : resolvedTypes(resolvedTypeCache) , engine(engine) , typeData(typeData) - , importCache(importCache) + , typeNameCache(typeNameCache) , document(parsedQML) { } @@ -138,7 +138,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() sss.scan(); } - QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, importCache, &document->jsGenerator.stringTable); + QmlIR::JSCodeGen v4CodeGenerator(typeData->finalUrlString(), document->code, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache, &document->jsGenerator.stringTable); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -164,7 +164,7 @@ QV4::CompiledData::CompilationUnit *QQmlTypeCompiler::compile() QV4::CompiledData::CompilationUnit *compilationUnit = document->javaScriptCompilationUnit; compilationUnit = document->javaScriptCompilationUnit; - compilationUnit->importCache = importCache; + compilationUnit->typeNameCache = typeNameCache; compilationUnit->resolvedTypes = resolvedTypes; compilationUnit->propertyCaches = std::move(m_propertyCaches); Q_ASSERT(compilationUnit->propertyCaches.count() == static_cast<int>(compilationUnit->data->nObjects)); diff --git a/src/qml/compiler/qqmltypecompiler_p.h b/src/qml/compiler/qqmltypecompiler_p.h index de6abb4ced..2b59e7e42f 100644 --- a/src/qml/compiler/qqmltypecompiler_p.h +++ b/src/qml/compiler/qqmltypecompiler_p.h @@ -89,7 +89,7 @@ struct QQmlTypeCompiler { Q_DECLARE_TR_FUNCTIONS(QQmlTypeCompiler) public: - QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); + QQmlTypeCompiler(QQmlEnginePrivate *engine, QQmlTypeData *typeData, QmlIR::Document *document, const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); // --- interface used by QQmlPropertyCacheCreator typedef QmlIR::Object CompiledObject; @@ -139,7 +139,7 @@ private: QList<QQmlError> errors; QQmlEnginePrivate *engine; QQmlTypeData *typeData; - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QmlIR::Document *document; // index is string index of type name (use obj->inheritedTypeNameIndex) QHash<int, QQmlCustomParser*> customParsers; diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 46c27fc735..0afc97e4bf 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -73,10 +73,29 @@ static inline void setLocation(IR::Stmt *s, const SourceLocation &loc) s->location = loc; } +static bool cjumpCanHandle(IR::AluOp op) +{ + switch (op) { + case IR::OpIn: + case IR::OpInstanceof: + case IR::OpEqual: + case IR::OpNotEqual: + case IR::OpGe: + case IR::OpGt: + case IR::OpLe: + case IR::OpLt: + case IR::OpStrictEqual: + case IR::OpStrictNotEqual: + return true; + default: + return false; + } +} + Codegen::ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, CompilationMode defaultProgramMode) : _cg(cg) , _sourceCode(sourceCode) - , _env(0) + , _variableEnvironment(0) , _allowFuncDecls(true) , defaultProgramMode(defaultProgramMode) { @@ -90,17 +109,17 @@ void Codegen::ScanFunctions::operator()(Node *node) void Codegen::ScanFunctions::enterEnvironment(Node *node, CompilationMode compilationMode) { - Environment *e = _cg->newEnvironment(node, _env, compilationMode); + Environment *e = _cg->newEnvironment(node, _variableEnvironment, compilationMode); if (!e->isStrict) e->isStrict = _cg->_strictMode; _envStack.append(e); - _env = e; + _variableEnvironment = e; } void Codegen::ScanFunctions::leaveEnvironment() { _envStack.pop(); - _env = _envStack.isEmpty() ? 0 : _envStack.top(); + _variableEnvironment = _envStack.isEmpty() ? 0 : _envStack.top(); } void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) @@ -116,7 +135,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) continue; QStringRef str = _sourceCode.midRef(strLit->literalToken.offset + 1, strLit->literalToken.length - 2); if (str == QLatin1String("use strict")) { - _env->isStrict = true; + _variableEnvironment->isStrict = true; } else { // TODO: give a warning. } @@ -131,7 +150,7 @@ void Codegen::ScanFunctions::checkDirectivePrologue(SourceElements *ast) void Codegen::ScanFunctions::checkName(const QStringRef &name, const SourceLocation &loc) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { if (name == QLatin1String("implements") || name == QLatin1String("interface") || name == QLatin1String("let") @@ -149,7 +168,7 @@ void Codegen::ScanFunctions::checkForArguments(AST::FormalParameterList *paramet { while (parameters) { if (parameters->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; parameters = parameters->next; } } @@ -168,19 +187,19 @@ void Codegen::ScanFunctions::endVisit(Program *) bool Codegen::ScanFunctions::visit(CallExpression *ast) { - if (! _env->hasDirectEval) { + if (! _variableEnvironment->hasDirectEval) { if (IdentifierExpression *id = cast<IdentifierExpression *>(ast->base)) { if (id->name == QLatin1String("eval")) { - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; - _env->hasDirectEval = true; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; + _variableEnvironment->hasDirectEval = true; } } } int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -189,7 +208,7 @@ bool Codegen::ScanFunctions::visit(NewMemberExpression *ast) int argc = 0; for (ArgumentList *it = ast->arguments; it; it = it->next) ++argc; - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); return true; } @@ -205,26 +224,38 @@ bool Codegen::ScanFunctions::visit(ArrayLiteral *ast) for (Elision *elision = ast->elision->next; elision; elision = elision->next) ++index; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, index); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, index); return true; } bool Codegen::ScanFunctions::visit(VariableDeclaration *ast) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Variable name may not be eval or arguments in strict mode")); checkName(ast->name, ast->identifierToken); if (ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - _env->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration); + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + if (ast->scope == AST::VariableDeclaration::VariableScope::ReadOnlyBlockScope && !ast->expression) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Missing initializer in const declaration")); + return false; + } + QString name = ast->name.toString(); + const Environment::Member *m = 0; + if (_variableEnvironment->memberInfo(name, &m)) { + if (m->isLexicallyScoped() || ast->isLexicallyScoped()) { + _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Identifier %1 has already been declared").arg(name)); + return false; + } + } + _variableEnvironment->enter(ast->name.toString(), ast->expression ? Environment::VariableDefinition : Environment::VariableDeclaration, ast->scope); return true; } bool Codegen::ScanFunctions::visit(IdentifierExpression *ast) { checkName(ast->name, ast->identifierToken); - if (_env->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectUsed; + if (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUnknown && ast->name == QLatin1String("arguments")) + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectUsed; return true; } @@ -256,7 +287,7 @@ bool Codegen::ScanFunctions::visit(FunctionExpression *ast) void Codegen::ScanFunctions::enterFunction(FunctionExpression *ast, bool enterName, bool isExpression) { - if (_env->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) + if (_variableEnvironment->isStrict && (ast->name == QLatin1String("eval") || ast->name == QLatin1String("arguments"))) _cg->throwSyntaxError(ast->identifierToken, QStringLiteral("Function name may not be eval or arguments in strict mode")); enterFunction(ast, ast->name.toString(), ast->formals, ast->body, enterName ? ast : 0, isExpression); } @@ -277,7 +308,7 @@ bool Codegen::ScanFunctions::visit(ObjectLiteral *ast) if (AST::cast<AST::PropertyGetterSetter *>(it->assignment)) ++argc; } - _env->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, argc); + _variableEnvironment->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, argc); TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, true); Node::accept(ast->properties, this); @@ -309,7 +340,7 @@ void Codegen::ScanFunctions::endVisit(FunctionDeclaration *) bool Codegen::ScanFunctions::visit(WithStatement *ast) { - if (_env->isStrict) { + if (_variableEnvironment->isStrict) { _cg->throwSyntaxError(ast->withToken, QStringLiteral("'with' statement is not allowed in strict mode")); return false; } @@ -319,7 +350,7 @@ bool Codegen::ScanFunctions::visit(WithStatement *ast) bool Codegen::ScanFunctions::visit(DoWhileStatement *ast) { { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); } Node::accept(ast->expression, this); @@ -331,7 +362,7 @@ bool Codegen::ScanFunctions::visit(ForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -342,7 +373,7 @@ bool Codegen::ScanFunctions::visit(LocalForStatement *ast) { Node::accept(ast->condition, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -352,7 +383,7 @@ bool Codegen::ScanFunctions::visit(ForEachStatement *ast) { Node::accept(ast->initialiser, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -362,7 +393,7 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { Node::accept(ast->declaration, this); Node::accept(ast->expression, this); - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_env->isStrict); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, !_variableEnvironment->isStrict); Node::accept(ast->statement, this); return false; @@ -370,12 +401,12 @@ bool Codegen::ScanFunctions::visit(LocalForEachStatement *ast) { bool Codegen::ScanFunctions::visit(ThisExpression *) { - _env->usesThis = true; + _variableEnvironment->usesThis = true; return false; } bool Codegen::ScanFunctions::visit(Block *ast) { - TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _env->isStrict ? false : _allowFuncDecls); + TemporaryBoolAssignment allowFuncDecls(_allowFuncDecls, _variableEnvironment->isStrict ? false : _allowFuncDecls); Node::accept(ast->statements, this); return false; } @@ -383,26 +414,26 @@ bool Codegen::ScanFunctions::visit(Block *ast) { void Codegen::ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParameterList *formals, FunctionBody *body, FunctionExpression *expr, bool isExpression) { bool wasStrict = false; - if (_env) { - _env->hasNestedFunctions = true; + if (_variableEnvironment) { + _variableEnvironment->hasNestedFunctions = true; // The identifier of a function expression cannot be referenced from the enclosing environment. if (expr) - _env->enter(name, Environment::FunctionDefinition, expr); + _variableEnvironment->enter(name, Environment::FunctionDefinition, AST::VariableDeclaration::FunctionScope, expr); if (name == QLatin1String("arguments")) - _env->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; - wasStrict = _env->isStrict; + _variableEnvironment->usesArgumentsObject = Environment::ArgumentsObjectNotUsed; + wasStrict = _variableEnvironment->isStrict; } enterEnvironment(ast, FunctionCode); checkForArguments(formals); - _env->isNamedFunctionExpression = isExpression && !name.isEmpty(); - _env->formals = formals; + _variableEnvironment->isNamedFunctionExpression = isExpression && !name.isEmpty(); + _variableEnvironment->formals = formals; if (body) checkDirectivePrologue(body->elements); - if (wasStrict || _env->isStrict) { + if (wasStrict || _variableEnvironment->isStrict) { QStringList args; for (FormalParameterList *it = formals; it; it = it->next) { QString arg = it->name.toString(); @@ -426,7 +457,7 @@ Codegen::Codegen(bool strict) , _block(0) , _exitBlock(0) , _returnAddress(0) - , _env(0) + , _variableEnvironment(0) , _loop(0) , _labelledStatement(0) , _scopeAndFinally(0) @@ -446,7 +477,7 @@ void Codegen::generateFromProgram(const QString &fileName, Q_ASSERT(node); _module = module; - _env = 0; + _variableEnvironment = 0; _module->setFileName(fileName); @@ -465,7 +496,7 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, { _module = module; _module->setFileName(fileName); - _env = 0; + _variableEnvironment = 0; ScanFunctions scan(this, sourceCode, GlobalCode); // fake a global environment @@ -482,14 +513,14 @@ void Codegen::generateFromFunctionExpression(const QString &fileName, void Codegen::enterEnvironment(Node *node) { - _env = _envMap.value(node); - Q_ASSERT(_env); + _variableEnvironment = _envMap.value(node); + Q_ASSERT(_variableEnvironment); } void Codegen::leaveEnvironment() { - Q_ASSERT(_env); - _env = _env->parent; + Q_ASSERT(_variableEnvironment); + _variableEnvironment = _variableEnvironment->parent; } void Codegen::enterLoop(Statement *node, IR::BasicBlock *breakBlock, IR::BasicBlock *continueBlock) @@ -611,7 +642,7 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS if (IR::Const *c1 = left->asConst()) { if (IR::Const *c2 = right->asConst()) { - if (c1->type == IR::NumberType && c2->type == IR::NumberType) { + if ((c1->type & IR::NumberType) && (c2->type & IR::NumberType)) { switch (op) { case IR::OpAdd: return _block->CONST(IR::NumberType, c1->value + c2->value); case IR::OpAnd: return _block->CONST(IR::BoolType, c1->value ? c2->value : 0); @@ -659,20 +690,20 @@ IR::Expr *Codegen::binop(IR::AluOp op, IR::Expr *left, IR::Expr *right, const AS } } - if (!left->asTemp() && !left->asArgLocal()) { + if (!left->asTemp() && !left->asArgLocal() && !left->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), left), loc); left = _block->TEMP(t); } - if (!right->asTemp() && !right->asArgLocal()) { + if (!right->asTemp() && !right->asArgLocal() && !right->asConst()) { const unsigned t = _block->newTemp(); setLocation(move(_block->TEMP(t), right), loc); right = _block->TEMP(t); } - Q_ASSERT(left->asTemp() || left->asArgLocal()); - Q_ASSERT(right->asTemp() || right->asArgLocal()); + Q_ASSERT(left->asTemp() || left->asArgLocal() || left->asConst()); + Q_ASSERT(right->asTemp() || right->asArgLocal() || right->asConst()); return _block->BINOP(op, left, right); } @@ -715,7 +746,7 @@ IR::Stmt *Codegen::cjump(IR::Expr *cond, IR::BasicBlock *iftrue, IR::BasicBlock if (hasError) return 0; - if (! (cond->asTemp() || cond->asBinop())) { + if (! (cond->asTemp() || (cond->asBinop() && cjumpCanHandle(cond->asBinop()->op)) )) { const unsigned t = _block->newTemp(); move(_block->TEMP(t), cond); cond = _block->TEMP(t); @@ -842,9 +873,16 @@ void Codegen::variableDeclaration(VariableDeclaration *ast) Q_ASSERT(expr.code); initializer = *expr; - int initialized = _block->newTemp(); - move(_block->TEMP(initialized), initializer); - move(identifier(ast->name.toString(), ast->identifierToken.startLine, ast->identifierToken.startColumn), _block->TEMP(initialized)); + IR::Expr *lhs = identifier(ast->name.toString(), ast->identifierToken.startLine, + ast->identifierToken.startColumn); + + if (lhs->asArgLocal()) { + move(lhs, initializer); + } else { + int initialized = _block->newTemp(); + move(_block->TEMP(initialized), initializer); + move(lhs, _block->TEMP(initialized)); + } } void Codegen::variableDeclarationList(VariableDeclarationList *ast) @@ -1274,14 +1312,7 @@ bool Codegen::visit(BinaryExpression *ast) if (_expr.accept(cx)) { setLocation(cjump(binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken), _expr.iftrue, _expr.iffalse), ast->operatorToken); } else { - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), e), ast->operatorToken); - _expr.code = _block->TEMP(t); - } + _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); } break; } @@ -1307,14 +1338,7 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; - IR::Expr *e = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); - if (e->asConst() || e->asString()) - _expr.code = e; - else { - const unsigned t = _block->newTemp(); - setLocation(move(_block->TEMP(t), e), ast->operatorToken); - _expr.code = _block->TEMP(t); - } + _expr.code = binop(IR::binaryOperator(ast->op), left, *right, ast->operatorToken); break; } @@ -1389,7 +1413,7 @@ bool Codegen::visit(DeleteExpression *ast) return false; // Temporaries cannot be deleted IR::ArgLocal *al = expr->asArgLocal(); - if (al && al->index < static_cast<unsigned>(_env->members.size())) { + if (al && al->index < static_cast<unsigned>(_variableEnvironment->members.size())) { // Trying to delete a function argument might throw. if (_function->isStrict) { throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode.")); @@ -1418,7 +1442,7 @@ bool Codegen::visit(DeleteExpression *ast) } if (expr->asTemp() || (expr->asArgLocal() && - expr->asArgLocal()->index >= static_cast<unsigned>(_env->members.size()))) { + expr->asArgLocal()->index >= static_cast<unsigned>(_variableEnvironment->members.size()))) { _expr.code = _block->CONST(IR::BoolType, 1); return false; } @@ -1469,7 +1493,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) return 0; uint scope = 0; - Environment *e = _env; + Environment *e = _variableEnvironment; IR::Function *f = _function; while (f && e->parent) { @@ -1500,7 +1524,7 @@ IR::Expr *Codegen::identifier(const QString &name, int line, int col) if (IR::Expr *fallback = fallbackNameLookup(name, line, col)) return fallback; - if (!e->parent && (!f || !f->insideWithOrCatch) && _env->compilationMode != EvalCode && e->compilationMode != QmlBinding) + if (!e->parent && (!f || !f->insideWithOrCatch) && _variableEnvironment->compilationMode != EvalCode && e->compilationMode != QmlBinding) return _block->GLOBALNAME(name, line, col); // global context or with. Lookup by name @@ -1995,7 +2019,7 @@ bool Codegen::visit(FunctionDeclaration * ast) if (hasError) return false; - if (_env->compilationMode == QmlBinding) + if (_variableEnvironment->compilationMode == QmlBinding) move(_block->TEMP(_returnAddress), _block->NAME(ast->name.toString(), 0, 0)); _expr.accept(nx); return false; @@ -2019,26 +2043,26 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, IR::BasicBlock *entryBlock = function->newBasicBlock(0); IR::BasicBlock *exitBlock = function->newBasicBlock(0, IR::Function::DontInsertBlock); - function->hasDirectEval = _env->hasDirectEval || _env->compilationMode == EvalCode + function->hasDirectEval = _variableEnvironment->hasDirectEval || _variableEnvironment->compilationMode == EvalCode || _module->debugMode; // Conditional breakpoints are like eval in the function - function->usesArgumentsObject = _env->parent && (_env->usesArgumentsObject == Environment::ArgumentsObjectUsed); - function->usesThis = _env->usesThis; - function->maxNumberOfArguments = qMax(_env->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); - function->isStrict = _env->isStrict; - function->isNamedExpression = _env->isNamedFunctionExpression; - function->isQmlBinding = _env->compilationMode == QmlBinding; + function->usesArgumentsObject = _variableEnvironment->parent && (_variableEnvironment->usesArgumentsObject == Environment::ArgumentsObjectUsed); + function->usesThis = _variableEnvironment->usesThis; + function->maxNumberOfArguments = qMax(_variableEnvironment->maxNumberOfArguments, (int)QV4::Global::ReservedArgumentCount); + function->isStrict = _variableEnvironment->isStrict; + function->isNamedExpression = _variableEnvironment->isNamedFunctionExpression; + function->isQmlBinding = _variableEnvironment->compilationMode == QmlBinding; AST::SourceLocation loc = ast->firstSourceLocation(); function->line = loc.startLine; function->column = loc.startColumn; if (function->usesArgumentsObject) - _env->enter(QStringLiteral("arguments"), Environment::VariableDeclaration); + _variableEnvironment->enter(QStringLiteral("arguments"), Environment::VariableDeclaration, AST::VariableDeclaration::FunctionScope); // variables in global code are properties of the global context object, not locals as with other functions. - if (_env->compilationMode == FunctionCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == FunctionCode || _variableEnvironment->compilationMode == QmlBinding) { unsigned t = 0; - for (Environment::MemberMap::iterator it = _env->members.begin(), end = _env->members.end(); it != end; ++it) { + for (Environment::MemberMap::iterator it = _variableEnvironment->members.begin(), end = _variableEnvironment->members.end(); it != end; ++it) { const QString &local = it.key(); function->LOCAL(local); (*it).index = t; @@ -2046,18 +2070,19 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, ++t; } } else { - if (!_env->isStrict) { + if (!_variableEnvironment->isStrict) { for (const QString &inheritedLocal : qAsConst(inheritedLocals)) { function->LOCAL(inheritedLocal); unsigned tempIndex = entryBlock->newTemp(); Environment::Member member = { Environment::UndefinedMember, - static_cast<int>(tempIndex), 0 }; - _env->members.insert(inheritedLocal, member); + static_cast<int>(tempIndex), 0, + AST::VariableDeclaration::VariableScope::FunctionScope }; + _variableEnvironment->members.insert(inheritedLocal, member); } } IR::ExprList *args = 0; - for (Environment::MemberMap::const_iterator it = _env->members.constBegin(), cend = _env->members.constEnd(); it != cend; ++it) { + for (Environment::MemberMap::const_iterator it = _variableEnvironment->members.constBegin(), cend = _variableEnvironment->members.constEnd(); it != cend; ++it) { const QString &local = it.key(); IR::ExprList *next = function->New<IR::ExprList>(); next->expr = entryBlock->NAME(local, 0, 0); @@ -2089,11 +2114,11 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, _function->RECEIVE(it->name.toString()); } - for (const Environment::Member &member : qAsConst(_env->members)) { + for (const Environment::Member &member : qAsConst(_variableEnvironment->members)) { if (member.function) { const int function = defineFunction(member.function->name.toString(), member.function, member.function->formals, member.function->body ? member.function->body->elements : 0); - if (! _env->parent) { + if (! _variableEnvironment->parent) { move(_block->NAME(member.function->name.toString(), member.function->identifierToken.startLine, member.function->identifierToken.startColumn), _block->CLOSURE(function)); } else { @@ -2269,7 +2294,7 @@ bool Codegen::visit(ExpressionStatement *ast) if (hasError) return true; - if (_env->compilationMode == EvalCode || _env->compilationMode == QmlBinding) { + if (_variableEnvironment->compilationMode == EvalCode || _variableEnvironment->compilationMode == QmlBinding) { Result e = expression(ast->expression); if (*e) move(_block->TEMP(_returnAddress), *e); @@ -2505,7 +2530,7 @@ bool Codegen::visit(ReturnStatement *ast) if (hasError) return true; - if (_env->compilationMode != FunctionCode && _env->compilationMode != QmlBinding) { + if (_variableEnvironment->compilationMode != FunctionCode && _variableEnvironment->compilationMode != QmlBinding) { throwSyntaxError(ast->returnToken, QStringLiteral("Return statement outside of function")); return false; } @@ -2883,7 +2908,7 @@ bool Codegen::visit(UiSourceElement *) bool Codegen::throwSyntaxErrorOnEvalOrArgumentsInStrictMode(IR::Expr *expr, const SourceLocation& loc) { - if (!_env->isStrict) + if (!_variableEnvironment->isStrict) return false; if (IR::Name *n = expr->asName()) { if (*n->id != QLatin1String("eval") && *n->id != QLatin1String("arguments")) diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 742ee79648..239ed5c4b9 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -141,10 +141,14 @@ protected: VariableDeclaration, FunctionDefinition }; + struct Member { MemberType type; int index; AST::FunctionExpression *function; + AST::VariableDeclaration::VariableScope scope; + + bool isLexicallyScoped() const { return this->scope != AST::VariableDeclaration::FunctionScope; } }; typedef QMap<QString, Member> MemberMap; @@ -191,6 +195,18 @@ protected: return (*it).index; } + bool memberInfo(const QString &name, const Member **m) const + { + Q_ASSERT(m); + MemberMap::const_iterator it = members.find(name); + if (it == members.end()) { + *m = 0; + return false; + } + *m = &(*it); + return true; + } + bool lookupMember(const QString &name, Environment **scope, int *index, int *distance) { Environment *it = this; @@ -206,7 +222,7 @@ protected: return false; } - void enter(const QString &name, MemberType type, AST::FunctionExpression *function = 0) + void enter(const QString &name, MemberType type, AST::VariableDeclaration::VariableScope scope, AST::FunctionExpression *function = 0) { if (! name.isEmpty()) { if (type != FunctionDefinition) { @@ -220,8 +236,10 @@ protected: m.index = -1; m.type = type; m.function = function; + m.scope = scope; members.insert(name, m); } else { + Q_ASSERT(scope == (*it).scope); if ((*it).type <= type) { (*it).type = type; (*it).function = function; @@ -448,7 +466,7 @@ protected: QV4::IR::BasicBlock *_block; QV4::IR::BasicBlock *_exitBlock; unsigned _returnAddress; - Environment *_env; + Environment *_variableEnvironment; Loop *_loop; AST::LabelledStatement *_labelledStatement; ScopeAndFinally *_scopeAndFinally; @@ -526,7 +544,7 @@ protected: // fields: Codegen *_cg; const QString _sourceCode; - Environment *_env; + Environment *_variableEnvironment; QStack<Environment *> _envStack; bool _allowFuncDecls; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6aac111897..8f8d374e24 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -52,7 +52,6 @@ #include "qv4compilationunitmapper_p.h" #include <QQmlPropertyMap> #include <QDateTime> -#include <QSaveFile> #include <QFile> #include <QFileInfo> #include <QScopedValueRollback> @@ -62,6 +61,7 @@ #include <private/qqmlirbuilder_p.h> #include <QCoreApplication> #include <QCryptographicHash> +#include <QSaveFile> #include <algorithm> @@ -77,6 +77,27 @@ namespace QV4 { namespace CompiledData { +#ifdef V4_BOOTSTRAP +static QString cacheFilePath(const QString &localSourcePath) +{ + const QString localCachePath = localSourcePath + QLatin1Char('c'); + return localCachePath; +} +#else +static QString cacheFilePath(const QUrl &url) +{ + const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); + const QString localCachePath = localSourcePath + QLatin1Char('c'); + if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) + return localCachePath; + QCryptographicHash fileNameHash(QCryptographicHash::Sha1); + fileNameHash.addData(localSourcePath.toUtf8()); + QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); + QDir::root().mkpath(directory); + return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); +} +#endif + #ifndef V4_BOOTSTRAP CompilationUnit::CompilationUnit() : data(0) @@ -207,7 +228,7 @@ void CompilationUnit::unlink() dependentScripts.at(ii)->release(); dependentScripts.clear(); - importCache = nullptr; + typeNameCache = nullptr; qDeleteAll(resolvedTypes); resolvedTypes.clear(); @@ -329,20 +350,68 @@ bool CompilationUnit::verifyChecksum(QQmlEngine *engine, sizeof(data->dependencyMD5Checksum)) == 0; } -static QString cacheFilePath(const QUrl &url) +bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) { - const QString localSourcePath = QQmlFile::urlToLocalFileOrQrc(url); - const QString localCachePath = localSourcePath + QLatin1Char('c'); - if (QFileInfo(QFileInfo(localSourcePath).dir().absolutePath()).isWritable()) - return localCachePath; - QCryptographicHash fileNameHash(QCryptographicHash::Sha1); - fileNameHash.addData(localSourcePath.toUtf8()); - QString directory = QStandardPaths::writableLocation(QStandardPaths::CacheLocation) + QLatin1String("/qmlcache/"); - QDir::root().mkpath(directory); - return directory + QString::fromUtf8(fileNameHash.result().toHex()) + QLatin1Char('.') + QFileInfo(localCachePath).completeSuffix(); + if (!QQmlFile::isLocalFile(url)) { + *errorString = QStringLiteral("File has to be a local file."); + return false; + } + + const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); + QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); + + CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); + if (!mappedUnit) + return false; + + const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; + QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + + if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { + *errorString = QStringLiteral("QML source file has moved to a different location."); + return false; + } + + { + const QString foundArchitecture = stringAt(data->architectureIndex); + const QString expectedArchitecture = QSysInfo::buildAbi(); + if (foundArchitecture != expectedArchitecture) { + *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); + return false; + } + } + + { + const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); + const QString expectedCodeGenerator = iselFactory->codeGeneratorName; + if (foundCodeGenerator != expectedCodeGenerator) { + *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); + return false; + } + } + + if (!memoryMapCode(errorString)) + return false; + + dataPtrChange.commit(); + free(const_cast<Unit*>(oldDataPtr)); + backingFile.reset(cacheFile.take()); + return true; +} + +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + *errorString = QStringLiteral("Missing code mapping backend"); + return false; } +#endif // V4_BOOTSTRAP + +#if defined(V4_BOOTSTRAP) +bool CompilationUnit::saveToDisk(const QString &unitUrl, QString *errorString) +#else bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) +#endif { errorString->clear(); @@ -351,10 +420,12 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return false; } +#if !defined(V4_BOOTSTRAP) if (!QQmlFile::isLocalFile(unitUrl)) { *errorString = QStringLiteral("File has to be a local file."); return false; } +#endif // Foo.qml -> Foo.qmlc QSaveFile cacheFile(cacheFilePath(unitUrl)); @@ -390,78 +461,105 @@ bool CompilationUnit::saveToDisk(const QUrl &unitUrl, QString *errorString) return true; } -bool CompilationUnit::loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString) +void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) { - if (!QQmlFile::isLocalFile(url)) { - *errorString = QStringLiteral("File has to be a local file."); - return false; - } + Q_UNUSED(unit); +} - const QString sourcePath = QQmlFile::urlToLocalFileOrQrc(url); - QScopedPointer<CompilationUnitMapper> cacheFile(new CompilationUnitMapper()); +bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) +{ + Q_UNUSED(device); + Q_UNUSED(unit); + *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); + return false; +} - CompiledData::Unit *mappedUnit = cacheFile->open(cacheFilePath(url), sourcePath, errorString); - if (!mappedUnit) - return false; +Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) +{ + if (!irDocument->javaScriptCompilationUnit->data) + return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); - const Unit * const oldDataPtr = (data && !(data->flags & QV4::CompiledData::Unit::StaticData)) ? data : nullptr; - QScopedValueRollback<const Unit *> dataPtrChange(data, mappedUnit); + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit = irDocument->javaScriptCompilationUnit; + QV4::CompiledData::Unit *jsUnit = const_cast<QV4::CompiledData::Unit*>(irDocument->javaScriptCompilationUnit->data); - if (sourcePath != QQmlFile::urlToLocalFileOrQrc(stringAt(data->sourceFileIndex))) { - *errorString = QStringLiteral("QML source file has moved to a different location."); - return false; - } + QV4::Compiler::StringTableGenerator &stringTable = irDocument->jsGenerator.stringTable; - { - const QString foundArchitecture = stringAt(data->architectureIndex); - const QString expectedArchitecture = QSysInfo::buildAbi(); - if (foundArchitecture != expectedArchitecture) { - *errorString = QString::fromUtf8("Architecture mismatch. Found %1 expected %2").arg(foundArchitecture).arg(expectedArchitecture); - return false; + // Collect signals that have had a change in signature (from onClicked to onClicked(mouse) for example) + // and now need fixing in the QV4::CompiledData. Also register strings at the same time, to finalize + // the string table. + QVector<quint32> changedSignals; + QVector<QQmlJS::AST::FormalParameterList*> changedSignalParameters; + for (QmlIR::Object *o: qAsConst(irDocument->objects)) { + for (QmlIR::Binding *binding = o->firstBinding(); binding; binding = binding->next) { + if (!(binding->flags & QV4::CompiledData::Binding::IsSignalHandlerExpression)) + continue; + + quint32 functionIndex = binding->value.compiledScriptIndex; + QmlIR::CompiledFunctionOrExpression *foe = o->functionsAndExpressions->slowAt(functionIndex); + if (!foe) + continue; + + // save absolute index + changedSignals << o->runtimeFunctionIndices.at(functionIndex); + + Q_ASSERT(foe->node); + Q_ASSERT(QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)); + + QQmlJS::AST::FormalParameterList *parameters = QQmlJS::AST::cast<QQmlJS::AST::FunctionDeclaration*>(foe->node)->formals; + changedSignalParameters << parameters; + + for (; parameters; parameters = parameters->next) + stringTable.registerString(parameters->name.toString()); } } - { - const QString foundCodeGenerator = stringAt(data->codeGeneratorIndex); - const QString expectedCodeGenerator = iselFactory->codeGeneratorName; - if (foundCodeGenerator != expectedCodeGenerator) { - *errorString = QString::fromUtf8("Code generator mismatch. Found code generated by %1 but expected %2").arg(foundCodeGenerator).arg(expectedCodeGenerator); - return false; + QVector<quint32> signalParameterNameTable; + quint32 signalParameterNameTableOffset = jsUnit->unitSize; + + // Update signal signatures + if (!changedSignals.isEmpty()) { + if (jsUnit == compilationUnit->data) { + char *unitCopy = (char*)malloc(jsUnit->unitSize); + memcpy(unitCopy, jsUnit, jsUnit->unitSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitCopy); } - } - if (!memoryMapCode(errorString)) - return false; + for (int i = 0; i < changedSignals.count(); ++i) { + const uint functionIndex = changedSignals.at(i); + // The data is now read-write due to the copy above, so the const_cast is ok. + QV4::CompiledData::Function *function = const_cast<QV4::CompiledData::Function *>(jsUnit->functionAt(functionIndex)); + Q_ASSERT(function->nFormals == quint32(0)); - dataPtrChange.commit(); - free(const_cast<Unit*>(oldDataPtr)); - backingFile.reset(cacheFile.take()); - return true; -} + function->formalsOffset = signalParameterNameTableOffset - jsUnit->functionOffsetTable()[functionIndex]; -void CompilationUnit::prepareCodeOffsetsForDiskStorage(Unit *unit) -{ - Q_UNUSED(unit); -} + for (QQmlJS::AST::FormalParameterList *parameters = changedSignalParameters.at(i); + parameters; parameters = parameters->next) { + signalParameterNameTable.append(stringTable.getStringId(parameters->name.toString())); + function->nFormals = function->nFormals + 1; + } -bool CompilationUnit::saveCodeToDisk(QIODevice *device, const Unit *unit, QString *errorString) -{ - Q_UNUSED(device); - Q_UNUSED(unit); - *errorString = QStringLiteral("Saving code to disk is not supported in this configuration"); - return false; -} + // Hack to ensure an activation is created. + function->flags |= QV4::CompiledData::Function::HasCatchOrWith | QV4::CompiledData::Function::HasDirectEval; -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - *errorString = QStringLiteral("Missing code mapping backend"); - return false; -} -#endif // V4_BOOTSTRAP + signalParameterNameTableOffset += function->nFormals * sizeof(quint32); + } + } -Unit *CompilationUnit::createUnitData(QmlIR::Document *irDocument) -{ - return irDocument->jsGenerator.generateUnit(QV4::Compiler::JSUnitGenerator::GenerateWithoutStringTable); + if (!signalParameterNameTable.isEmpty()) { + Q_ASSERT(jsUnit != compilationUnit->data); + const uint signalParameterTableSize = signalParameterNameTable.count() * sizeof(quint32); + uint newSize = jsUnit->unitSize + signalParameterTableSize; + const uint oldSize = jsUnit->unitSize; + char *unitWithSignalParameters = (char*)realloc(jsUnit, newSize); + memcpy(unitWithSignalParameters + oldSize, signalParameterNameTable.constData(), signalParameterTableSize); + jsUnit = reinterpret_cast<QV4::CompiledData::Unit*>(unitWithSignalParameters); + jsUnit->unitSize = newSize; + } + + if (jsUnit != compilationUnit->data) + jsUnit->flags &= ~QV4::CompiledData::Unit::StaticData; + + return jsUnit; } QString Binding::valueAsString(const Unit *unit) const @@ -624,7 +722,7 @@ static QByteArray ownLibraryChecksum() if (checksumInitialized) return libraryChecksum; checksumInitialized = true; -#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) +#if defined(Q_OS_UNIX) && !defined(QT_NO_DYNAMIC_CAST) && !defined(Q_OS_INTEGRITY) Dl_info libInfo; if (dladdr(reinterpret_cast<const void *>(&ownLibraryChecksum), &libInfo) != 0) { QFile library(QFile::decodeName(libInfo.dli_fname)); diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 2682365182..13a0c4b075 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -71,7 +71,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x08 +#define QV4_DATA_STRUCTURE_VERSION 0x09 class QIODevice; class QQmlPropertyCache; @@ -133,7 +133,7 @@ struct Location QJsonPrivate::qle_bitfield<20, 12> column; }; - Location() { line = 0; column = 0; } + Location() { line.val = 0; column.val = 0; } inline bool operator<(const Location &other) const { return line < other.line || @@ -153,7 +153,7 @@ struct RegExp QJsonPrivate::qle_bitfield<4, 28> stringIndex; }; - RegExp() { flags = 0; stringIndex = 0; } + RegExp() { flags.val = 0; stringIndex.val = 0; } }; struct Lookup @@ -171,7 +171,7 @@ struct Lookup QJsonPrivate::qle_bitfield<4, 28> nameIndex; }; - Lookup() { type_and_flags = 0; nameIndex = 0; } + Lookup() { type_and_flags.val = 0; nameIndex.val = 0; } }; struct JSClassMember @@ -625,7 +625,8 @@ struct Unit StaticData = 0x4, // Unit data persistent in memory? IsSingleton = 0x8, IsSharedLibrary = 0x10, // .pragma shared? - ContainsMachineCode = 0x20 // used to determine if we need to mmap with execute permissions + ContainsMachineCode = 0x20, // used to determine if we need to mmap with execute permissions + PendingTypeCompilation = 0x40 // the QML data structures present are incomplete and require type compilation }; LEUInt32 flags; LEUInt32 stringTableSize; @@ -777,31 +778,7 @@ struct TypeReferenceMap : QHash<int, TypeReference> }; #ifndef V4_BOOTSTRAP -struct ResolvedTypeReference -{ - ResolvedTypeReference() - : type(0) - , majorVersion(0) - , minorVersion(0) - , isFullyDynamicType(false) - {} - - QQmlType *type; - QQmlRefPointer<QQmlPropertyCache> typePropertyCache; - QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; - - int majorVersion; - int minorVersion; - // Types such as QQmlPropertyMap can add properties dynamically at run-time and - // therefore cannot have a property cache installed when instantiated. - bool isFullyDynamicType; - - QQmlPropertyCache *propertyCache() const; - QQmlPropertyCache *createPropertyCache(QQmlEngine *); - bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); - - void doDynamicTypeCheck(); -}; +struct ResolvedTypeReference; // map from name index // While this could be a hash, a map is chosen here to provide a stable // order, which is used to calculating a check-sum on dependent meta-objects. @@ -841,10 +818,14 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount #ifndef V4_BOOTSTRAP ExecutionEngine *engine; +#endif + + QV4::Heap::String **runtimeStrings; // Array + +#ifndef V4_BOOTSTRAP QString fileName() const { return data->stringAt(data->sourceFileIndex); } QUrl url() const { if (m_url.isNull) m_url = QUrl(fileName()); return m_url; } - QV4::Heap::String **runtimeStrings; // Array QV4::Lookup *runtimeLookups; QV4::Value *runtimeRegularExpressions; QV4::InternalClass **runtimeClasses; @@ -855,7 +836,7 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount QQmlPropertyCacheVector propertyCaches; QQmlPropertyCache *rootPropertyCache() const { return propertyCaches.at(data->indexOfRootObject); } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; // index is object index. This allows fast access to the // property data when initializing bindings, avoiding expensive @@ -918,17 +899,53 @@ struct Q_QML_PRIVATE_EXPORT CompilationUnit : public QQmlRefCount void destroy() Q_DECL_OVERRIDE; - bool saveToDisk(const QUrl &unitUrl, QString *errorString); bool loadFromDisk(const QUrl &url, EvalISelFactory *iselFactory, QString *errorString); protected: virtual void linkBackendToEngine(QV4::ExecutionEngine *engine) = 0; - virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); - virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); virtual bool memoryMapCode(QString *errorString); #endif // V4_BOOTSTRAP + +public: +#if defined(V4_BOOTSTRAP) + bool saveToDisk(const QString &unitUrl, QString *errorString); +#else + bool saveToDisk(const QUrl &unitUrl, QString *errorString); +#endif + +protected: + virtual void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit); + virtual bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString); }; +#ifndef V4_BOOTSTRAP +struct ResolvedTypeReference +{ + ResolvedTypeReference() + : type(0) + , majorVersion(0) + , minorVersion(0) + , isFullyDynamicType(false) + {} + + QQmlType *type; + QQmlRefPointer<QQmlPropertyCache> typePropertyCache; + QQmlRefPointer<QV4::CompiledData::CompilationUnit> compilationUnit; + + int majorVersion; + int minorVersion; + // Types such as QQmlPropertyMap can add properties dynamically at run-time and + // therefore cannot have a property cache installed when instantiated. + bool isFullyDynamicType; + + QQmlPropertyCache *propertyCache() const; + QQmlPropertyCache *createPropertyCache(QQmlEngine *); + bool addToHash(QCryptographicHash *hash, QQmlEngine *engine); + + void doDynamicTypeCheck(); +}; +#endif + } } diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index cd822a2614..9cfac4a676 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -220,7 +220,7 @@ QV4::CompiledData::Unit *QV4::Compiler::JSUnitGenerator::generateUnit(GeneratorO registerString(*f->locals.at(i)); } - CompiledData::LEUInt32 *functionOffsets = reinterpret_cast<CompiledData::LEUInt32*>(alloca(irModule->functions.size() * sizeof(CompiledData::LEUInt32))); + Q_ALLOCA_VAR(CompiledData::LEUInt32, functionOffsets, irModule->functions.size() * sizeof(CompiledData::LEUInt32)); uint jsClassDataOffset = 0; char *dataPtr; diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index ca4e0b73d4..53d9956315 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -52,10 +52,11 @@ // #include <private/qv4global_p.h> #include <private/qv4value_p.h> -#include <private/qv4function_p.h> #include <private/qv4runtime_p.h> +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 9dbebd1128..04844302d9 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -39,15 +39,15 @@ #include "qv4isel_util_p.h" #include "qv4isel_moth_p.h" -#include "qv4vme_moth_p.h" #include "qv4ssa_p.h" -#include <private/qv4debugging_p.h> -#include <private/qv4function_p.h> -#include <private/qv4regexpobject_p.h> #include <private/qv4compileddata_p.h> -#include <private/qqmlengine_p.h> #include <wtf/MathExtras.h> +#if !defined(V4_BOOTSTRAP) +#include "qv4vme_moth_p.h" +#include <private/qv4function_p.h> +#endif + #undef USE_TYPE_INFO using namespace QV4; @@ -1185,8 +1185,11 @@ void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) void InstructionSelection::callBuiltinPopScope() { + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wuninitialized") Instruction::CallBuiltinPopScope call; addInstruction(call); + QT_WARNING_POP } void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) @@ -1335,8 +1338,11 @@ void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) void QV4::Moth::InstructionSelection::callBuiltinConvertThisToObject() { + QT_WARNING_PUSH + QT_WARNING_DISABLE_GCC("-Wuninitialized") Instruction::CallBuiltinConvertThisToObject call; addInstruction(call); + QT_WARNING_POP } ptrdiff_t InstructionSelection::addInstructionHelper(Instr::Type type, Instr &instr) @@ -1425,6 +1431,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) { #ifdef MOTH_THREADED_INTERPRETER @@ -1461,6 +1469,31 @@ void CompilationUnit::linkBackendToEngine(QV4::ExecutionEngine *engine) } } +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + codeRefs.resize(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); +#ifdef MOTH_THREADED_INTERPRETER + // for the threaded interpreter we need to make a copy of the data because it needs to be + // modified for the instruction handler addresses. + QByteArray code(codePtr, compiledFunction->codeSize); +#else + QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); +#endif + codeRefs[i] = code; + } + + return true; +} + +#endif // V4_BOOTSTRAP + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -1482,7 +1515,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray padding; -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) // Map from instruction label back to instruction type. Only needed when persisting // already linked compilation units; QHash<void*, int> reverseInstructionMapping; @@ -1511,7 +1544,7 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit QByteArray code = codeRefs.at(i); -#ifdef MOTH_THREADED_INTERPRETER +#if defined(MOTH_THREADED_INTERPRETER) && !defined(V4_BOOTSTRAP) if (!reverseInstructionMapping.isEmpty()) { char *codePtr = code.data(); // detaches int index = 0; @@ -1541,29 +1574,6 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); - - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - const char *codePtr = const_cast<const char *>(reinterpret_cast<const char *>(basePtr + compiledFunction->codeOffset)); -#ifdef MOTH_THREADED_INTERPRETER - // for the threaded interpreter we need to make a copy of the data because it needs to be - // modified for the instruction handler addresses. - QByteArray code(codePtr, compiledFunction->codeSize); -#else - QByteArray code = QByteArray::fromRawData(codePtr, compiledFunction->codeSize); -#endif - codeRefs[i] = code; - } - - return true; -} - QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index afe5fe342e..41469f1985 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -59,7 +59,9 @@ #include <private/qv4value_p.h> #include "qv4instr_moth_p.h" +#if !defined(V4_BOOTSTRAP) QT_REQUIRE_CONFIG(qml_interpreter); +#endif QT_BEGIN_NAMESPACE @@ -69,10 +71,12 @@ namespace Moth { struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; QVector<QByteArray> codeRefs; diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 73aa6c4975..04bc3d86e5 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -315,20 +315,20 @@ struct Q_AUTOTEST_EXPORT Expr { Expr(ExprKind exprKind): type(UnknownType), exprKind(exprKind) {} bool isLValue() const; - Const *asConst() { return as<Const>(); } - String *asString() { return as<String>(); } - RegExp *asRegExp() { return as<RegExp>(); } - Name *asName() { return as<Name>(); } - Temp *asTemp() { return as<Temp>(); } - ArgLocal *asArgLocal() { return as<ArgLocal>(); } - Closure *asClosure() { return as<Closure>(); } - Convert *asConvert() { return as<Convert>(); } - Unop *asUnop() { return as<Unop>(); } - Binop *asBinop() { return as<Binop>(); } - Call *asCall() { return as<Call>(); } - New *asNew() { return as<New>(); } - Subscript *asSubscript() { return as<Subscript>(); } - Member *asMember() { return as<Member>(); } + Const *asConst(); + String *asString(); + RegExp *asRegExp(); + Name *asName(); + Temp *asTemp(); + ArgLocal *asArgLocal(); + Closure *asClosure(); + Convert *asConvert(); + Unop *asUnop(); + Binop *asBinop(); + Call *asCall(); + New *asNew(); + Subscript *asSubscript(); + Member *asMember(); }; #define EXPR_VISIT_ALL_KINDS(e) \ @@ -773,12 +773,12 @@ struct Stmt { Stmt *asTerminator(); - Exp *asExp() { return as<Exp>(); } - Move *asMove() { return as<Move>(); } - Jump *asJump() { return as<Jump>(); } - CJump *asCJump() { return as<CJump>(); } - Ret *asRet() { return as<Ret>(); } - Phi *asPhi() { return as<Phi>(); } + Exp *asExp(); + Move *asMove(); + Jump *asJump(); + CJump *asCJump(); + Ret *asRet(); + Phi *asPhi(); int id() const { return _id; } @@ -1720,6 +1720,28 @@ inline Stmt *BasicBlock::RET(Expr *expr) return s; } +inline Const *Expr::asConst() { return as<Const>(); } +inline String *Expr::asString() { return as<String>(); } +inline RegExp *Expr::asRegExp() { return as<RegExp>(); } +inline Name *Expr::asName() { return as<Name>(); } +inline Temp *Expr::asTemp() { return as<Temp>(); } +inline ArgLocal *Expr::asArgLocal() { return as<ArgLocal>(); } +inline Closure *Expr::asClosure() { return as<Closure>(); } +inline Convert *Expr::asConvert() { return as<Convert>(); } +inline Unop *Expr::asUnop() { return as<Unop>(); } +inline Binop *Expr::asBinop() { return as<Binop>(); } +inline Call *Expr::asCall() { return as<Call>(); } +inline New *Expr::asNew() { return as<New>(); } +inline Subscript *Expr::asSubscript() { return as<Subscript>(); } +inline Member *Expr::asMember() { return as<Member>(); } + +inline Exp *Stmt::asExp() { return as<Exp>(); } +inline Move *Stmt::asMove() { return as<Move>(); } +inline Jump *Stmt::asJump() { return as<Jump>(); } +inline CJump *Stmt::asCJump() { return as<CJump>(); } +inline Ret *Stmt::asRet() { return as<Ret>(); } +inline Phi *Stmt::asPhi() { return as<Phi>(); } + } // end of namespace IR } // end of namespace QV4 diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 1d512711b8..10f0bbcf8f 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -5123,7 +5123,7 @@ void LifeTimeInterval::setFrom(int from) { Q_ASSERT(from > 0); if (_ranges.isEmpty()) { // this is the case where there is no use, only a define - _ranges.prepend(Range(from, from)); + _ranges.prepend(LifeTimeIntervalRange(from, from)); if (_end == InvalidPosition) _end = from; } else { @@ -5137,17 +5137,17 @@ void LifeTimeInterval::addRange(int from, int to) { Q_ASSERT(to >= from); if (_ranges.isEmpty()) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); _end = to; return; } - Range *p = &_ranges.first(); + LifeTimeIntervalRange *p = &_ranges.first(); if (to + 1 >= p->start && p->end + 1 >= from) { p->start = qMin(p->start, from); p->end = qMax(p->end, to); while (_ranges.count() > 1) { - Range *p1 = p + 1; + LifeTimeIntervalRange *p1 = p + 1; if (p->end + 1 < p1->start || p1->end + 1 < p->start) break; p1->start = qMin(p->start, p1->start); @@ -5157,10 +5157,10 @@ void LifeTimeInterval::addRange(int from, int to) { } } else { if (to < p->start) { - _ranges.prepend(Range(from, to)); + _ranges.prepend(LifeTimeIntervalRange(from, to)); } else { Q_ASSERT(from > _ranges.last().end); - _ranges.push_back(Range(from, to)); + _ranges.push_back(LifeTimeIntervalRange(from, to)); } } @@ -5206,7 +5206,7 @@ LifeTimeInterval LifeTimeInterval::split(int atPosition, int newStart) } else { // find the first range where the temp will get active again: while (!newInterval._ranges.isEmpty()) { - const Range &range = newInterval._ranges.first(); + const LifeTimeIntervalRange &range = newInterval._ranges.first(); if (range.start > newStart) { // The split position is before the start of the range. Either we managed to skip // over the correct range, or we got an invalid split request. Either way, this diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index db8b6edd1a..c07abd04c4 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -63,20 +63,28 @@ class QQmlEnginePrivate; namespace QV4 { namespace IR { -class Q_AUTOTEST_EXPORT LifeTimeInterval { -public: - struct Range { - int start; - int end; +struct LifeTimeIntervalRange { + int start; + int end; - Range(int start = InvalidPosition, int end = InvalidPosition) - : start(start) - , end(end) - {} + LifeTimeIntervalRange(int start = -1, int end = -1) + : start(start) + , end(end) + {} - bool covers(int position) const { return start <= position && position <= end; } - }; - typedef QVarLengthArray<Range, 4> Ranges; + bool covers(int position) const { return start <= position && position <= end; } +}; +} // IR namespace +} // QV4 namespace + +Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeIntervalRange, Q_PRIMITIVE_TYPE); + +namespace QV4 { +namespace IR { + +class Q_AUTOTEST_EXPORT LifeTimeInterval { +public: + typedef QVarLengthArray<LifeTimeIntervalRange, 4> Ranges; private: Temp _temp; @@ -137,7 +145,7 @@ public: // Validate the new range if (_end != InvalidPosition) { Q_ASSERT(!_ranges.isEmpty()); - for (const Range &range : qAsConst(_ranges)) { + for (const LifeTimeIntervalRange &range : qAsConst(_ranges)) { Q_ASSERT(range.start >= 0); Q_ASSERT(range.end >= 0); Q_ASSERT(range.start <= range.end); @@ -457,7 +465,6 @@ protected: Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval, Q_MOVABLE_TYPE); -Q_DECLARE_TYPEINFO(QV4::IR::LifeTimeInterval::Range, Q_PRIMITIVE_TYPE); QT_END_NAMESPACE diff --git a/src/qml/debugger/qqmlprofiler_p.h b/src/qml/debugger/qqmlprofiler_p.h index 3a507bef74..41fb2c5b7b 100644 --- a/src/qml/debugger/qqmlprofiler_p.h +++ b/src/qml/debugger/qqmlprofiler_p.h @@ -73,7 +73,7 @@ struct QQmlProfiler {}; struct QQmlBindingProfiler { - QQmlBindingProfiler(quintptr, QQmlBinding *, QV4::FunctionObject *) {} + QQmlBindingProfiler(quintptr, QV4::Function *) {} }; struct QQmlHandlingSignalProfiler diff --git a/src/qml/doc/src/cppintegration/data.qdoc b/src/qml/doc/src/cppintegration/data.qdoc index ac6600f38c..4523ee39d8 100644 --- a/src/qml/doc/src/cppintegration/data.qdoc +++ b/src/qml/doc/src/cppintegration/data.qdoc @@ -167,9 +167,9 @@ additional features. See the \l {qtqml-javascript-hostenvironment.html} The QML engine provides automatic type conversion between QVariantList and JavaScript arrays, and between QVariantMap and JavaScript objects. -For example, the function defined in QML below left expects two arguments, an +For example, the function defined in QML below expects two arguments, an array and an object, and prints their contents using the standard JavaScript -syntax for array and object item access. The C++ code below right calls this +syntax for array and object item access. The C++ code below calls this function, passing a QVariantList and a QVariantMap, which are automatically converted to JavaScript array and object values, repectively: @@ -204,9 +204,9 @@ when it is passed to C++. The QML engine provides automatic type conversion between QDateTime values and JavaScript \c Date objects. -For example, the function defined in QML below left expects a JavaScript +For example, the function defined in QML below expects a JavaScript \c Date object, and also returns a new \c Date object with the current date and -time. The C++ code below right calls this function, passing a QDateTime value +time. The C++ code below calls this function, passing a QDateTime value that is automatically converted by the engine into a \c Date object when it is passed to the \c readDate() function. In turn, the readDate() function returns a \c Date object that is automatically converted into a QDateTime value when it @@ -215,7 +215,7 @@ is received in C++: \table \header \row - +\li QML \li \qml // MyItem.qml @@ -227,6 +227,7 @@ Item { } \endqml \row +\li C++ \li \code // C++ diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index e06451b2bc..7d4a543089 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -282,6 +282,9 @@ int qmlRegisterRevision(const char *uri, int versionMajor, int versionMinor) template<typename T, int metaObjectRevision> int qmlRegisterUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) + +template<typename T, typename E, int metaObjectRevision> +int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) \endcode For example, if \c BaseType is changed and now has a revision 1, you can diff --git a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc index ed0d049564..3bffd2eb6f 100644 --- a/src/qml/doc/src/cppintegration/exposecppattributes.qdoc +++ b/src/qml/doc/src/cppintegration/exposecppattributes.qdoc @@ -390,8 +390,8 @@ that is a public slot: \endcode If an instance of \c MessageBoard was set as the context data for a file \c -MyItem.qml, as shown below left, then \c MyItem.qml could invoke the two -methods, as shown below right: +MyItem.qml, then \c MyItem.qml could invoke the two methods as shown in the +examples below: \table \row diff --git a/src/qml/doc/src/cppintegration/topic.qdoc b/src/qml/doc/src/cppintegration/topic.qdoc index a349fd0713..1aa3bb6ab5 100644 --- a/src/qml/doc/src/cppintegration/topic.qdoc +++ b/src/qml/doc/src/cppintegration/topic.qdoc @@ -30,7 +30,7 @@ \brief Description of how to integrate QML and C++ code QML is designed to be easily extensible through C++ code. The classes in the \l {Qt QML} module -enables QML objects to be loaded and manipulated from C++, and the nature of QML engine's +enable QML objects to be loaded and manipulated from C++, and the nature of QML engine's integration with Qt's \l{Meta Object System}{meta object system} enables C++ functionality to be invoked directly from QML. This allows the development of hybrid applications which are implemented with a mixture of QML, JavaScript and C++ code. diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc index f5b1ed3cf1..fd916e1e24 100644 --- a/src/qml/doc/src/javascript/functionlist.qdoc +++ b/src/qml/doc/src/javascript/functionlist.qdoc @@ -182,6 +182,7 @@ \li lastIndexOf(searchString, position) \li localeCompare(that) \li match(regexp) + \li repeat(count) // ECMAScript 6: Added in Qt 5.9 \li replace(searchValue, replaceValue) \li search(regexp) \li slice(start, end) diff --git a/src/qml/doc/src/javascript/imports.qdoc b/src/qml/doc/src/javascript/imports.qdoc index d4ce25c92b..489da08ada 100644 --- a/src/qml/doc/src/javascript/imports.qdoc +++ b/src/qml/doc/src/javascript/imports.qdoc @@ -56,10 +56,10 @@ import "jsfile.js" as Logic \endcode Imported JavaScript resources are always qualified using the "as" keyword. The -qualifier for JavaScript resources must be unique, so there is always a -one-to-one mapping between qualifiers and JavaScript files. (This also means -qualifiers cannot be named the same as built-in JavaScript objects such as -\c Date and \c Math). +qualifier for JavaScript resources must start with an uppercase letter, and must +be unique, so there is always a one-to-one mapping between qualifiers and JavaScript +files. (This also means qualifiers cannot be named the same as built-in JavaScript +objects such as \c Date and \c Math). The functions defined in an imported JavaScript file are available to objects defined in the importing QML document, via the diff --git a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc index 030eb72b5f..33f58dc1b9 100644 --- a/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc +++ b/src/qml/doc/src/qmllanguageref/syntax/objectattributes.qdoc @@ -630,7 +630,7 @@ a \l {MouseArea::}{clicked} signal that is emitted when the user clicks within the mouse area. An object can be notified through a \l{Signal handler attributes} -{signal handler} whenever it a particular signal is emitted. A signal handler +{signal handler} whenever a particular signal is emitted. A signal handler is declared with the syntax \e on<Signal> where \e <Signal> is the name of the signal, with the first letter capitalized. The signal handler must be declared within the definition of the object that emits the signal, and the handler diff --git a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc index ffbf2282a6..a486b47f03 100644 --- a/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc +++ b/src/qml/doc/src/qmllanguageref/typesystem/basictypes.qdoc @@ -324,6 +324,9 @@ property is only invoked when the property is reassigned to a different object v \li Values in the list are accessed using the \c [index] syntax \endlist + Values can be dynamically added to the list by using the \c push method, + as if it were a JavaScript Array + A \c list can only store QML objects, and cannot contain any \l {QML Basic Types}{basic type} values. (To store basic types within a list, use the \l var type instead.) diff --git a/src/qml/doc/src/qmltypereference.qdoc b/src/qml/doc/src/qmltypereference.qdoc index f32574fcc1..b2bb9ebd18 100644 --- a/src/qml/doc/src/qmltypereference.qdoc +++ b/src/qml/doc/src/qmltypereference.qdoc @@ -89,7 +89,7 @@ provided: \ingroup qtquickbasictypes \brief a date value. -The \c date type refers to a date value. +The \c date type refers to a date value, including the time of the day. To create a \c date value, specify it as a "YYYY-MM-DD" string: @@ -100,7 +100,7 @@ MyDatePicker { minDate: "2000-01-01"; maxDate: "2020-12-31" } To read a date value returned from a C++ extension class, use \l{QtQml::Qt::formatDate()}{Qt.formatDate()} and \l{QtQml::Qt::formatDateTime()}{Qt.formatDateTime()}. -When integrating with C++, note that any QDate value +When integrating with C++, note that any QDate or QDateTime value \l{qtqml-cppintegration-data.html}{passed into QML from C++} is automatically converted into a \c date value, and vice-versa. diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp index 018396318e..ca270a0648 100644 --- a/src/qml/jit/qv4assembler.cpp +++ b/src/qml/jit/qv4assembler.cpp @@ -39,11 +39,6 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" -#include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -51,6 +46,10 @@ #include <assembler/LinkBuffer.h> #include <WTFStubs.h> +#if !defined(V4_BOOTSTRAP) +#include "qv4function_p.h" +#endif + #include <iostream> #include <QBuffer> #include <QCoreApplication> @@ -68,6 +67,8 @@ CompilationUnit::~CompilationUnit() { } +#if !defined(V4_BOOTSTRAP) + void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) { runtimeFunctions.resize(data->functionTableSize); @@ -81,6 +82,26 @@ void CompilationUnit::linkBackendToEngine(ExecutionEngine *engine) } } +bool CompilationUnit::memoryMapCode(QString *errorString) +{ + Q_UNUSED(errorString); + codeRefs.resize(data->functionTableSize); + + const char *basePtr = reinterpret_cast<const char *>(data); + + for (uint i = 0; i < data->functionTableSize; ++i) { + const CompiledData::Function *compiledFunction = data->functionAt(i); + void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); + JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); + JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); + codeRefs[i] = codeRef; + } + + return true; +} + +#endif // !defined(V4_BOOTSTRAP) + void CompilationUnit::prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) { const int codeAlignment = 16; @@ -128,27 +149,11 @@ bool CompilationUnit::saveCodeToDisk(QIODevice *device, const CompiledData::Unit return true; } -bool CompilationUnit::memoryMapCode(QString *errorString) -{ - Q_UNUSED(errorString); - codeRefs.resize(data->functionTableSize); - - const char *basePtr = reinterpret_cast<const char *>(data); +template <typename TargetConfiguration> +const typename Assembler<TargetConfiguration>::VoidType Assembler<TargetConfiguration>::Void; - for (uint i = 0; i < data->functionTableSize; ++i) { - const CompiledData::Function *compiledFunction = data->functionAt(i); - void *codePtr = const_cast<void *>(reinterpret_cast<const void *>(basePtr + compiledFunction->codeOffset)); - JSC::MacroAssemblerCodeRef codeRef = JSC::MacroAssemblerCodeRef::createSelfManagedCodeRef(JSC::MacroAssemblerCodePtr(codePtr)); - JSC::ExecutableAllocator::makeExecutable(codePtr, compiledFunction->codeSize); - codeRefs[i] = codeRef; - } - - return true; -} - -const Assembler::VoidType Assembler::Void; - -Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) +template <typename TargetConfiguration> +Assembler<TargetConfiguration>::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator) : _function(function) , _nextBlock(0) , _executableAllocator(executableAllocator) @@ -159,14 +164,16 @@ Assembler::Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* _labelPatches.resize(_function->basicBlockCount()); } -void Assembler::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::registerBlock(IR::BasicBlock* block, IR::BasicBlock *nextBlock) { _addrs[block->index()] = label(); catchBlock = block->catchBlock; _nextBlock = nextBlock; } -void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) { Q_UNUSED(current); @@ -174,12 +181,14 @@ void Assembler::jumpToBlock(IR::BasicBlock* current, IR::BasicBlock *target) _patches[target->index()].push_back(jump()); } -void Assembler::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(IR::BasicBlock* targetBlock, Jump targetJump) { _patches[targetBlock->index()].push_back(targetJump); } -void Assembler::addPatch(DataLabelPtr patch, Label target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, Label target) { DataLabelPatch p; p.dataLabel = patch; @@ -187,37 +196,21 @@ void Assembler::addPatch(DataLabelPtr patch, Label target) _dataLabelPatches.push_back(p); } -void Assembler::addPatch(DataLabelPtr patch, IR::BasicBlock *target) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::addPatch(DataLabelPtr patch, IR::BasicBlock *target) { _labelPatches[target->index()].push_back(patch); } -void Assembler::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { - generateCJumpOnCompare(NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); -} - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING -void Assembler::generateCJumpOnCompare(RelationalCondition cond, - RegisterID left, - TrustedImm64 right, - IR::BasicBlock *currentBlock, - IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock) -{ - if (trueBlock == _nextBlock) { - Jump target = branch64(invert(cond), left, right); - addPatch(falseBlock, target); - } else { - Jump target = branch64(cond, left, right); - addPatch(trueBlock, target); - jumpToBlock(currentBlock, falseBlock); - } + generateCJumpOnCompare(RelationalCondition::NotEqual, reg, TrustedImm32(0), currentBlock, trueBlock, falseBlock); } -#endif -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, @@ -234,7 +227,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -void Assembler::generateCJumpOnCompare(RelationalCondition cond, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, IR::BasicBlock *currentBlock, @@ -251,7 +245,8 @@ void Assembler::generateCJumpOnCompare(RelationalCondition cond, } } -Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadAddress(RegisterID tmp, IR::Expr *e) { IR::Temp *t = e->asTemp(); if (t) @@ -260,7 +255,8 @@ Assembler::Pointer Assembler::loadAddress(RegisterID tmp, IR::Expr *e) return loadArgLocalAddress(tmp, e->asArgLocal()); } -Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadTempAddress(IR::Temp *t) { if (t->kind == IR::Temp::StackSlot) return stackSlotPointer(t); @@ -268,7 +264,8 @@ Assembler::Pointer Assembler::loadTempAddress(IR::Temp *t) Q_UNREACHABLE(); } -Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadArgLocalAddress(RegisterID baseReg, IR::ArgLocal *al) { int32_t offset = 0; int scope = al->scope; @@ -298,7 +295,8 @@ Assembler::Pointer Assembler::loadArgLocalAddress(RegisterID baseReg, IR::ArgLoc return Pointer(baseReg, offset); } -Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &string) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Pointer Assembler<TargetConfiguration>::loadStringAddress(RegisterID reg, const QString &string) { loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, compilationUnit)), Assembler::ScratchRegister); @@ -307,12 +305,14 @@ Assembler::Pointer Assembler::loadStringAddress(RegisterID reg, const QString &s return Pointer(reg, id * sizeof(QV4::String*)); } -Assembler::Address Assembler::loadConstant(IR::Const *c, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(IR::Const *c, RegisterID baseReg) { return loadConstant(convertToValue(c), baseReg); } -Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseReg) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Address Assembler<TargetConfiguration>::loadConstant(const Primitive &v, RegisterID baseReg) { loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), baseReg); loadPtr(Address(baseReg, qOffsetOf(QV4::Heap::ExecutionContext, constantTable)), baseReg); @@ -320,33 +320,36 @@ Assembler::Address Assembler::loadConstant(const Primitive &v, RegisterID baseRe return Address(baseReg, index * sizeof(QV4::Value)); } -void Assembler::loadStringRef(RegisterID reg, const QString &string) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::loadStringRef(RegisterID reg, const QString &string) { const int id = _jsGenerator->registerString(string); move(TrustedImm32(id), reg); } -void Assembler::storeValue(QV4::Primitive value, IR::Expr *destination) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::storeValue(QV4::Primitive value, IR::Expr *destination) { Address addr = loadAddress(ScratchRegister, destination); storeValue(value, addr); } -void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::enterStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { platformEnterStandardStackFrame(this); - move(StackPointerRegister, FramePointerRegister); + move(StackPointerRegister, JITTargetPlatform::FramePointerRegister); const int frameSize = _stackLayout->calculateStackFrameSize(); subPtr(TrustedImm32(frameSize), StackPointerRegister); - Address slotAddr(FramePointerRegister, 0); + Address slotAddr(JITTargetPlatform::FramePointerRegister, 0); for (int i = 0, ei = fpRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); slotAddr.offset -= sizeof(double); - JSC::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); + TargetConfiguration::MacroAssembler::storeDouble(fpRegistersToSave.at(i).reg<FPRegisterID>(), slotAddr); } for (int i = 0, ei = regularRegistersToSave.size(); i < ei; ++i) { Q_ASSERT(regularRegistersToSave.at(i).isRegularRegister()); @@ -355,10 +358,11 @@ void Assembler::enterStandardStackFrame(const RegisterInformation ®ularRegist } } -void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::leaveStandardStackFrame(const RegisterInformation ®ularRegistersToSave, const RegisterInformation &fpRegistersToSave) { - Address slotAddr(FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); + Address slotAddr(JITTargetPlatform::FramePointerRegister, -regularRegistersToSave.size() * RegisterSize - fpRegistersToSave.size() * sizeof(double)); // restore the callee saved registers for (int i = regularRegistersToSave.size() - 1; i >= 0; --i) { @@ -368,23 +372,14 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist } for (int i = fpRegistersToSave.size() - 1; i >= 0; --i) { Q_ASSERT(fpRegistersToSave.at(i).isFloatingPoint()); - JSC::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); + TargetConfiguration::MacroAssembler::loadDouble(slotAddr, fpRegistersToSave.at(i).reg<FPRegisterID>()); slotAddr.offset += sizeof(double); } Q_ASSERT(slotAddr.offset == 0); const int frameSize = _stackLayout->calculateStackFrameSize(); - // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't - // work well for large immediates. -#if CPU(ARM_THUMB2) - move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); - add32(JSC::ARMRegisters::r3, StackPointerRegister); -#else - addPtr(TrustedImm32(frameSize), StackPointerRegister); -#endif - - platformLeaveStandardStackFrame(this); + platformLeaveStandardStackFrame(this, frameSize); } @@ -393,7 +388,8 @@ void Assembler::leaveStandardStackFrame(const RegisterInformation ®ularRegist // Try to load the source expression into the destination FP register. This assumes that two // general purpose (integer) registers are available: the ScratchRegister and the // ReturnValueRegister. It returns a Jump if no conversion can be performed. -Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::genTryDoubleConversion(IR::Expr *src, FPRegisterID dest) { switch (src->type) { case IR::DoubleType: @@ -436,11 +432,10 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe isNoInt.link(this); #ifdef QV4_USE_64_BIT_VALUE_ENCODING rshift32(TrustedImm32(Value::IsDoubleTag_Shift), ScratchRegister); - Assembler::Jump isNoDbl = branch32(Equal, ScratchRegister, TrustedImm32(0)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(0)); #else and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isNoDbl = branch32(Assembler::Equal, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); + Assembler::Jump isNoDbl = branch32(RelationalCondition::Equal, JITTargetPlatform::ScratchRegister, TrustedImm32(Value::NotDouble_Mask)); #endif toDoubleRegister(src, dest); intDone.link(this); @@ -448,10 +443,11 @@ Assembler::Jump Assembler::genTryDoubleConversion(IR::Expr *src, Assembler::FPRe return isNoDbl; } -Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) { - Assembler::DoubleCondition cond; + DoubleCondition cond; switch (op) { case IR::OpGt: cond = Assembler::DoubleGreaterThan; break; case IR::OpLt: cond = Assembler::DoubleLessThan; break; @@ -465,12 +461,13 @@ Assembler::Jump Assembler::branchDouble(bool invertCondition, IR::AluOp op, Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, FPGpr1)); + return TargetConfiguration::MacroAssembler::branchDouble(cond, toDoubleRegister(left, FPGpr0), toDoubleRegister(right, JITTargetPlatform::FPGpr1)); } -Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) +template <typename TargetConfiguration> +typename Assembler<TargetConfiguration>::Jump Assembler<TargetConfiguration>::branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right) { Assembler::RelationalCondition cond; switch (op) { @@ -486,18 +483,51 @@ Assembler::Jump Assembler::branchInt32(bool invertCondition, IR::AluOp op, IR::E Q_UNREACHABLE(); } if (invertCondition) - cond = JSC::MacroAssembler::invert(cond); + cond = TargetConfiguration::MacroAssembler::invert(cond); - return JSC::MacroAssembler::branch32(cond, - toInt32Register(left, Assembler::ScratchRegister), - toInt32Register(right, Assembler::ReturnValueRegister)); + return TargetConfiguration::MacroAssembler::branch32(cond, + toInt32Register(left, Assembler::ScratchRegister), + toInt32Register(right, Assembler::ReturnValueRegister)); } -void Assembler::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave) { _stackLayout.reset(new StackLayout(_function, maxArgCountForBuiltins, regularRegistersToSave, fpRegistersToSave)); } +template <typename TargetConfiguration> +void Assembler<TargetConfiguration>::returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave) +{ + if (!s) { + // this only happens if the method doesn't have a return statement and can + // only exit through an exception + } 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); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + } else { + Q_UNREACHABLE(); + Q_UNUSED(s); + } + + Label leaveStackFrame = label(); + + const int locals = stackLayout().calculateJSStackFrameSize(); + subPtr(TrustedImm32(sizeof(QV4::Value)*locals), JITTargetPlatform::LocalsRegister); + loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), JITTargetPlatform::ScratchRegister); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + + leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); + ret(); + + exceptionReturnLabel = label(); + QV4::Primitive retVal = Primitive::undefinedValue(); + RegisterSizeDependentOps::setFunctionReturnValueFromConst(this, retVal); + jump(leaveStackFrame); +} namespace { class QIODevicePrintStream: public FilePrintStream @@ -516,7 +546,7 @@ public: ~QIODevicePrintStream() {} - void vprintf(const char* format, va_list argList) WTF_ATTRIBUTE_PRINTF(2, 0) + void vprintf(const char* format, va_list argList) override WTF_ATTRIBUTE_PRINTF(2, 0) { const int written = qvsnprintf(buf.data(), buf.size(), format, argList); if (written > 0) @@ -524,7 +554,7 @@ public: memset(buf.data(), 0, qMin(written, buf.size())); } - void flush() + void flush() override {} private: @@ -563,7 +593,8 @@ static void qt_closePmap() #endif -JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) +template <typename TargetConfiguration> +JSC::MacroAssemblerCodeRef Assembler<TargetConfiguration>::link(int *codeSize) { Label endOfCode = label(); @@ -577,7 +608,7 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) } JSC::JSGlobalData dummy(_executableAllocator); - JSC::LinkBuffer linkBuffer(dummy, this, 0); + JSC::LinkBuffer<typename TargetConfiguration::MacroAssembler> linkBuffer(dummy, this, 0); for (const DataLabelPatch &p : qAsConst(_dataLabelPatches)) linkBuffer.patch(p.dataLabel, linkBuffer.locationOf(p.target)); @@ -668,4 +699,14 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } +template class QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; +#endif +#if !CPU(ARM64) +template class QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; +#endif +#endif + #endif diff --git a/src/qml/jit/qv4assembler_p.h b/src/qml/jit/qv4assembler_p.h index de9c246ed6..fd65c9b3d2 100644 --- a/src/qml/jit/qv4assembler_p.h +++ b/src/qml/jit/qv4assembler_p.h @@ -55,7 +55,8 @@ #include "private/qv4isel_p.h" #include "private/qv4isel_util_p.h" #include "private/qv4value_p.h" -#include "private/qv4lookup_p.h" +#include "private/qv4context_p.h" +#include "private/qv4engine_p.h" #include "qv4targetplatform_p.h" #include <config.h> @@ -73,46 +74,671 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class InstructionSelection; - struct CompilationUnit : public QV4::CompiledData::CompilationUnit { virtual ~CompilationUnit(); +#if !defined(V4_BOOTSTRAP) void linkBackendToEngine(QV4::ExecutionEngine *engine) Q_DECL_OVERRIDE; + bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; +#endif void prepareCodeOffsetsForDiskStorage(CompiledData::Unit *unit) Q_DECL_OVERRIDE; bool saveCodeToDisk(QIODevice *device, const CompiledData::Unit *unit, QString *errorString) Q_DECL_OVERRIDE; - bool memoryMapCode(QString *errorString) Q_DECL_OVERRIDE; // Coderef + execution engine QVector<JSC::MacroAssemblerCodeRef> codeRefs; }; -struct LookupCall { - JSC::MacroAssembler::Address addr; - uint getterSetterOffset; +template <typename PlatformAssembler, TargetOperatingSystemSpecialization Specialization> +struct AssemblerTargetConfiguration +{ + typedef JSC::MacroAssembler<PlatformAssembler> MacroAssembler; + typedef TargetPlatform<PlatformAssembler, Specialization> Platform; + // More things coming here in the future, such as Target OS +}; + +#if CPU(ARM_THUMB2) +typedef JSC::MacroAssemblerARMv7 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(ARM64) +typedef JSC::MacroAssemblerARM64 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(ARM_TRADITIONAL) +typedef JSC::MacroAssemblerARM DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(MIPS) +typedef JSC::MacroAssemblerMIPS DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(X86) +typedef JSC::MacroAssemblerX86 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#elif CPU(X86_64) +typedef JSC::MacroAssemblerX86_64 DefaultPlatformMacroAssembler; + +#if OS(WINDOWS) +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, WindowsSpecialization> DefaultAssemblerTargetConfiguration; +#else +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#endif - LookupCall(const JSC::MacroAssembler::Address &addr, uint getterSetterOffset) - : addr(addr) - , getterSetterOffset(getterSetterOffset) - {} +#elif CPU(SH4) +typedef JSC::MacroAssemblerSH4 DefaultPlatformMacroAssembler; +typedef AssemblerTargetConfiguration<DefaultPlatformMacroAssembler, NoOperatingSystemSpecialization> DefaultAssemblerTargetConfiguration; +#endif + +#define isel_stringIfyx(s) #s +#define isel_stringIfy(s) isel_stringIfyx(s) + +#define generateRuntimeCall(as, t, function, ...) \ + as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) + + +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform, int RegisterSize> +struct RegisterSizeDependentAssembler +{ }; -struct RuntimeCall { - JSC::MacroAssembler::Address addr; +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> +struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 4> +{ + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using ResultCondition = typename JITAssembler::ResultCondition; + using Address = typename JITAssembler::Address; + using Pointer = typename JITAssembler::Pointer; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Jump = typename JITAssembler::Jump; + using Label = typename JITAssembler::Label; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->MacroAssembler::loadDouble(addr, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->MacroAssembler::storeDouble(source, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->storeDouble(source, ptr); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store32(TrustedImm32(value.int_32()), destination); + destination.offset += 4; + as->store32(TrustedImm32(value.tag()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + as->loadDouble(source, TargetPlatform::FPGpr0); + as->storeDouble(TargetPlatform::FPGpr0, destination); + } + + static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) + { + as->MacroAssembler::loadDouble(as->loadConstant(c, TargetPlatform::ScratchRegister), target); + } + + static void storeReturnValue(JITAssembler *as, FPRegisterID dest) + { + as->moveIntsToDouble(TargetPlatform::LowReturnValueRegister, TargetPlatform::HighReturnValueRegister, dest, TargetPlatform::FPGpr0); + } + + static void storeReturnValue(JITAssembler *as, const Pointer &dest) + { + Address destination = dest; + as->store32(TargetPlatform::LowReturnValueRegister, destination); + destination.offset += 4; + as->store32(TargetPlatform::HighReturnValueRegister, destination); + } + + static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) + { + const auto lowReg = TargetPlatform::LowReturnValueRegister; + const auto highReg = TargetPlatform::HighReturnValueRegister; + + if (t->kind == IR::Temp::PhysicalRegister) { + switch (t->type) { + case IR::DoubleType: + as->moveDoubleToInts((FPRegisterID) t->index, lowReg, highReg); + break; + case IR::UInt32Type: { + RegisterID srcReg = (RegisterID) t->index; + Jump intRange = as->branch32(JITAssembler::GreaterThanOrEqual, srcReg, TrustedImm32(0)); + as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->moveDoubleToInts(TargetPlatform::FPGpr0, lowReg, highReg); + Jump done = as->jump(); + intRange.link(as); + as->move(srcReg, lowReg); + as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + done.link(as); + } break; + case IR::SInt32Type: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); + break; + case IR::BoolType: + as->move((RegisterID) t->index, lowReg); + as->move(TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); + break; + default: + Q_UNREACHABLE(); + } + } else { + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, t); + as->load32(addr, lowReg); + addr.offset += 4; + as->load32(addr, highReg); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm32(retVal.int_32()), TargetPlatform::LowReturnValueRegister); + as->move(TrustedImm32(retVal.tag()), TargetPlatform::HighReturnValueRegister); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(temp); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(al); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(c); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(as); + Q_UNUSED(expr); + Q_UNUSED(dest); + Q_UNUSED(argumentNumber); + } + + static void zeroRegister(JITAssembler *as, RegisterID reg) + { + as->move(TrustedImm32(0), reg); + } + + static void zeroStackSlot(JITAssembler *as, int slot) + { + as->poke(TrustedImm32(0), slot); + } + + static void generateCJumpOnUndefined(JITAssembler *as, + RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) + { + Pointer tagAddr = as->loadAddress(scratchRegister, right); + as->load32(tagAddr, tagRegister); + Jump j = as->branch32(JITAssembler::invert(cond), tagRegister, TrustedImm32(0)); + as->addPatch(falseBlock, j); + + tagAddr.offset += 4; + as->load32(tagAddr, tagRegister); + const TrustedImm32 tag(QV4::Value::Managed_Type_Internal); + Q_ASSERT(nextBlock == as->nextBlock()); + Q_UNUSED(nextBlock); + as->generateCJumpOnCompare(cond, tagRegister, tag, currentBlock, trueBlock, falseBlock); + } + + static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) + { + Q_ASSERT(source->type == IR::VarType); + // load the tag: + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, source); + Pointer tagAddr = addr; + tagAddr.offset += 4; + as->load32(tagAddr, TargetPlatform::ReturnValueRegister); + + // check if it's an int32: + Jump fallback = as->branch32(RelationalCondition::NotEqual, TargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Integer_Type_Internal)); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + as->load32(addr, TargetPlatform::ReturnValueRegister); + Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->load32(addr, (RegisterID) targetTemp->index); + } + Jump intDone = as->jump(); + + // not an int: + fallback.link(as); + generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, + as->loadAddress(TargetPlatform::ScratchRegister, source)); + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + + intDone.link(as); + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store32(registerWithPtr, destAddr); + destAddr.offset += 4; + as->store32(TrustedImm32(QV4::Value::Managed_Type_Internal_32), destAddr); + } + + static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) + { + as->and32(TrustedImm32(Value::NotDouble_Mask), tagOrValueRegister); + return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, + TrustedImm32(Value::NotDouble_Mask)); + } - inline RuntimeCall(uint offset = uint(INT_MIN)); - bool isValid() const { return addr.offset >= 0; } + static void initializeLocalVariables(JITAssembler *as, int localsCount) + { + as->move(TrustedImm32(0), TargetPlatform::ReturnValueRegister); + as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); + Label loop = as->label(); + as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); + as->store32(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add32(TrustedImm32(4), TargetPlatform::LocalsRegister); + Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); + jump.linkTo(loop, as); + } }; -class Assembler : public JSC::MacroAssembler, public TargetPlatform +template <typename JITAssembler, typename MacroAssembler, typename TargetPlatform> +struct RegisterSizeDependentAssembler<JITAssembler, MacroAssembler, TargetPlatform, 8> +{ + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using Address = typename JITAssembler::Address; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Pointer = typename JITAssembler::Pointer; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using ResultCondition = typename JITAssembler::ResultCondition; + using BranchTruncateType = typename JITAssembler::BranchTruncateType; + using Jump = typename JITAssembler::Jump; + using Label = typename JITAssembler::Label; + + static void loadDouble(JITAssembler *as, Address addr, FPRegisterID dest) + { + as->load64(addr, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, Address addr) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->store64(TargetPlatform::ReturnValueRegister, addr); + } + + static void storeDouble(JITAssembler *as, FPRegisterID source, IR::Expr* target) + { + as->moveDoubleTo64(source, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Pointer ptr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store64(TargetPlatform::ReturnValueRegister, ptr); + } + + static void storeReturnValue(JITAssembler *as, FPRegisterID dest) + { + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, dest); + } + + static void storeReturnValue(JITAssembler *as, const Pointer &dest) + { + as->store64(TargetPlatform::ReturnValueRegister, dest); + } + + static void setFunctionReturnValueFromTemp(JITAssembler *as, IR::Temp *t) + { + if (t->kind == IR::Temp::PhysicalRegister) { + if (t->type == IR::DoubleType) { + as->moveDoubleTo64((FPRegisterID) t->index, + TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), + TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + } else if (t->type == IR::UInt32Type) { + RegisterID srcReg = (RegisterID) t->index; + Jump intRange = as->branch32(RelationalCondition::GreaterThanOrEqual, srcReg, TrustedImm32(0)); + as->convertUInt32ToDouble(srcReg, TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->moveDoubleTo64(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister); + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + Jump done = as->jump(); + intRange.link(as); + as->zeroExtend32ToPtr(srcReg, TargetPlatform::ReturnValueRegister); + quint64 tag = QV4::Value::Integer_Type_Internal; + as->or64(TrustedImm64(tag << 32), + TargetPlatform::ReturnValueRegister); + done.link(as); + } else { + as->zeroExtend32ToPtr((RegisterID) t->index, TargetPlatform::ReturnValueRegister); + quint64 tag; + switch (t->type) { + case IR::SInt32Type: + tag = QV4::Value::Integer_Type_Internal; + break; + case IR::BoolType: + tag = QV4::Value::Boolean_Type_Internal; + break; + default: + tag = 31337; // bogus value + Q_UNREACHABLE(); + } + as->or64(TrustedImm64(tag << 32), + TargetPlatform::ReturnValueRegister); + } + } else { + as->copyValue(TargetPlatform::ReturnValueRegister, t); + } + } + + static void setFunctionReturnValueFromConst(JITAssembler *as, QV4::Primitive retVal) + { + as->move(TrustedImm64(retVal.rawValue()), TargetPlatform::ReturnValueRegister); + } + + static void storeValue(JITAssembler *as, QV4::Primitive value, Address destination) + { + as->store64(TrustedImm64(value.rawValue()), destination); + } + + template <typename Source, typename Destination> + static void copyValueViaRegisters(JITAssembler *as, Source source, Destination destination) + { + // Use ReturnValueRegister as "scratch" register because loadArgument + // and storeArgument are functions that may need a scratch register themselves. + loadArgumentInRegister(as, source, TargetPlatform::ReturnValueRegister, 0); + as->storeReturnValue(destination); + } + + static void loadDoubleConstant(JITAssembler *as, IR::Const *c, FPRegisterID target) + { + Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); + int64_t i; + memcpy(&i, &c->value, sizeof(double)); + as->move(TrustedImm64(i), TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, target); + } + + static void loadArgumentInRegister(JITAssembler *as, Address addressOfValue, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + as->load64(addressOfValue, dest); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Temp* temp, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (temp) { + Pointer addr = as->loadTempAddress(temp); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } + } + + static void loadArgumentInRegister(JITAssembler *as, IR::ArgLocal* al, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (al) { + Pointer addr = as->loadArgLocalAddress(dest, al); + as->load64(addr, dest); + } else { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Const* c, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + QV4::Value v = convertToValue(c); + as->move(TrustedImm64(v.rawValue()), dest); + } + + static void loadArgumentInRegister(JITAssembler *as, IR::Expr* expr, RegisterID dest, int argumentNumber) + { + Q_UNUSED(argumentNumber); + + if (!expr) { + QV4::Value undefined = QV4::Primitive::undefinedValue(); + as->move(TrustedImm64(undefined.rawValue()), dest); + } else if (IR::Temp *t = expr->asTemp()){ + loadArgumentInRegister(as, t, dest, argumentNumber); + } else if (IR::ArgLocal *al = expr->asArgLocal()) { + loadArgumentInRegister(as, al, dest, argumentNumber); + } else if (IR::Const *c = expr->asConst()) { + loadArgumentInRegister(as, c, dest, argumentNumber); + } else { + Q_ASSERT(!"unimplemented expression type in loadArgument"); + } + } + + static void zeroRegister(JITAssembler *as, RegisterID reg) + { + as->move(TrustedImm64(0), reg); + } + + static void zeroStackSlot(JITAssembler *as, int slot) + { + as->store64(TrustedImm64(0), as->addressForPoke(slot)); + } + + static void generateCJumpOnCompare(JITAssembler *as, + RelationalCondition cond, + RegisterID left, + TrustedImm64 right, + IR::BasicBlock *nextBlock, + IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) + { + if (trueBlock == nextBlock) { + Jump target = as->branch64(as->invert(cond), left, right); + as->addPatch(falseBlock, target); + } else { + Jump target = as->branch64(cond, left, right); + as->addPatch(trueBlock, target); + as->jumpToBlock(currentBlock, falseBlock); + } + } + + static void generateCJumpOnUndefined(JITAssembler *as, + RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *nextBlock, IR::BasicBlock *currentBlock, + IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) + { + Pointer addr = as->loadAddress(scratchRegister, right); + as->load64(addr, tagRegister); + const TrustedImm64 tag(0); + generateCJumpOnCompare(as, cond, tagRegister, tag, nextBlock, currentBlock, trueBlock, falseBlock); + } + + static void convertVarToSInt32(JITAssembler *as, IR::Expr *source, IR::Expr *target) + { + Q_ASSERT(source->type == IR::VarType); + Pointer addr = as->loadAddress(TargetPlatform::ScratchRegister, source); + as->load64(addr, TargetPlatform::ScratchRegister); + as->move(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + + // check if it's integer convertible + as->urshift64(TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), TargetPlatform::ScratchRegister); + Jump isIntConvertible = as->branch32(RelationalCondition::Equal, TargetPlatform::ScratchRegister, TrustedImm32(3)); + + // nope, not integer convertible, so check for a double: + as->urshift64(TrustedImm32( + QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), + TargetPlatform::ScratchRegister); + Jump fallback = as->branch32(RelationalCondition::GreaterThan, TargetPlatform::ScratchRegister, TrustedImm32(0)); + + // it's a double + as->move(TrustedImm64(QV4::Value::NaNEncodeMask), TargetPlatform::ScratchRegister); + as->xor64(TargetPlatform::ScratchRegister, TargetPlatform::ReturnValueRegister); + as->move64ToDouble(TargetPlatform::ReturnValueRegister, TargetPlatform::FPGpr0); + Jump success = + as->branchTruncateDoubleToInt32(TargetPlatform::FPGpr0, TargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + + // not an int: + fallback.link(as); + generateRuntimeCall(as, TargetPlatform::ReturnValueRegister, toInt, + as->loadAddress(TargetPlatform::ScratchRegister, source)); + + + isIntConvertible.link(as); + success.link(as); + IR::Temp *targetTemp = target->asTemp(); + if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { + Pointer targetAddr = as->loadAddress(TargetPlatform::ScratchRegister, target); + as->store32(TargetPlatform::ReturnValueRegister, targetAddr); + targetAddr.offset += 4; + as->store32(TrustedImm32(Value::Integer_Type_Internal), targetAddr); + } else { + as->storeInt32(TargetPlatform::ReturnValueRegister, target); + } + } + + static void loadManagedPointer(JITAssembler *as, RegisterID registerWithPtr, Pointer destAddr) + { + as->store64(registerWithPtr, destAddr); + } + + static Jump generateIsDoubleCheck(JITAssembler *as, RegisterID tagOrValueRegister) + { + as->rshift32(TrustedImm32(Value::IsDoubleTag_Shift), tagOrValueRegister); + return as->branch32(RelationalCondition::NotEqual, tagOrValueRegister, + TrustedImm32(0)); + } + + static void initializeLocalVariables(JITAssembler *as, int localsCount) + { + as->move(TrustedImm64(0), TargetPlatform::ReturnValueRegister); + as->move(TrustedImm32(localsCount), TargetPlatform::ScratchRegister); + Label loop = as->label(); + as->store64(TargetPlatform::ReturnValueRegister, Address(TargetPlatform::LocalsRegister)); + as->add64(TrustedImm32(8), TargetPlatform::LocalsRegister); + Jump jump = as->branchSub32(ResultCondition::NonZero, TrustedImm32(1), TargetPlatform::ScratchRegister); + jump.linkTo(loop, as); + } +}; + +template <typename TargetConfiguration> +class Assembler : public TargetConfiguration::MacroAssembler, public TargetConfiguration::Platform { Q_DISABLE_COPY(Assembler) public: Assembler(QV4::Compiler::JSUnitGenerator *jsGenerator, IR::Function* function, QV4::ExecutableAllocator *executableAllocator); + using MacroAssembler = typename TargetConfiguration::MacroAssembler; + using RegisterID = typename MacroAssembler::RegisterID; + using FPRegisterID = typename MacroAssembler::FPRegisterID; + using Address = typename MacroAssembler::Address; + using Label = typename MacroAssembler::Label; + using Jump = typename MacroAssembler::Jump; + using DataLabelPtr = typename MacroAssembler::DataLabelPtr; + using TrustedImm32 = typename MacroAssembler::TrustedImm32; + using TrustedImm64 = typename MacroAssembler::TrustedImm64; + using TrustedImmPtr = typename MacroAssembler::TrustedImmPtr; + using RelationalCondition = typename MacroAssembler::RelationalCondition; + using typename MacroAssembler::DoubleCondition; + using MacroAssembler::label; + using MacroAssembler::move; + using MacroAssembler::jump; + using MacroAssembler::add32; + using MacroAssembler::and32; + using MacroAssembler::store32; + using MacroAssembler::loadPtr; + using MacroAssembler::load32; + using MacroAssembler::branch32; + using MacroAssembler::subDouble; + using MacroAssembler::subPtr; + using MacroAssembler::addPtr; + using MacroAssembler::call; + using MacroAssembler::poke; + using MacroAssembler::branchTruncateDoubleToUint32; + using MacroAssembler::or32; + using MacroAssembler::moveDouble; + using MacroAssembler::convertUInt32ToDouble; + using MacroAssembler::invert; + using MacroAssembler::convertInt32ToDouble; + using MacroAssembler::rshift32; + using MacroAssembler::storePtr; + using MacroAssembler::ret; + + using JITTargetPlatform = typename TargetConfiguration::Platform; + using JITTargetPlatform::RegisterArgumentCount; + using JITTargetPlatform::StackSpaceAllocatedUponFunctionEntry; + using JITTargetPlatform::RegisterSize; + using JITTargetPlatform::StackAlignment; + using JITTargetPlatform::ReturnValueRegister; + using JITTargetPlatform::StackPointerRegister; + using JITTargetPlatform::ScratchRegister; + using JITTargetPlatform::EngineRegister; + using JITTargetPlatform::StackShadowSpace; + using JITTargetPlatform::registerForArgument; + using JITTargetPlatform::FPGpr0; + using JITTargetPlatform::platformEnterStandardStackFrame; + using JITTargetPlatform::platformLeaveStandardStackFrame; + + using RegisterSizeDependentOps = RegisterSizeDependentAssembler<Assembler<TargetConfiguration>, MacroAssembler, JITTargetPlatform, RegisterSize>; + + struct LookupCall { + Address addr; + uint getterSetterOffset; + + LookupCall(const Address &addr, uint getterSetterOffset) + : addr(addr) + , getterSetterOffset(getterSetterOffset) + {} + }; + + struct RuntimeCall { + Address addr; + + inline RuntimeCall(uint offset = uint(INT_MIN)); + bool isValid() const { return addr.offset >= 0; } + }; + // Explicit type to allow distinguishing between // pushing an address itself or the value it points // to onto the stack when calling functions. @@ -319,20 +945,29 @@ public: void addPatch(DataLabelPtr patch, IR::BasicBlock *target); void generateCJumpOnNonZero(RegisterID reg, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm64 right, - IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, - IR::BasicBlock *falseBlock); -#endif void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, TrustedImm32 right, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); void generateCJumpOnCompare(RelationalCondition cond, RegisterID left, RegisterID right, IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock); - Jump genTryDoubleConversion(IR::Expr *src, Assembler::FPRegisterID dest); - Assembler::Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); - Assembler::Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); + void generateCJumpOnUndefined(RelationalCondition cond, IR::Expr *right, + RegisterID scratchRegister, RegisterID tagRegister, + IR::BasicBlock *currentBlock, IR::BasicBlock *trueBlock, + IR::BasicBlock *falseBlock) + { + RegisterSizeDependentOps::generateCJumpOnUndefined(this, cond, right, scratchRegister, tagRegister, + _nextBlock, currentBlock, trueBlock, falseBlock); + } + + Jump generateIsDoubleCheck(RegisterID tagOrValueRegister) + { + return RegisterSizeDependentOps::generateIsDoubleCheck(this, tagOrValueRegister); + } + + Jump genTryDoubleConversion(IR::Expr *src, FPRegisterID dest); + Jump branchDouble(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); + Jump branchInt32(bool invertCondition, IR::AluOp op, IR::Expr *left, IR::Expr *right); Pointer loadAddress(RegisterID tmp, IR::Expr *t); Pointer loadTempAddress(IR::Temp *t); @@ -396,7 +1031,7 @@ public: void loadArgumentInRegister(PointerToValue temp, RegisterID dest, int argumentNumber) { if (!temp.value) { - move(TrustedImmPtr(0), dest); + RegisterSizeDependentOps::zeroRegister(this, dest); } else { Pointer addr = toAddress(dest, temp.value, argumentNumber); loadArgumentInRegister(addr, dest, argumentNumber); @@ -415,70 +1050,31 @@ public: loadArgumentInRegister(addr, dest, argumentNumber); } -#ifdef VALUE_FITS_IN_REGISTER void loadArgumentInRegister(IR::Temp* temp, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (temp) { - Pointer addr = loadTempAddress(temp); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, temp, dest, argumentNumber); } void loadArgumentInRegister(IR::ArgLocal* al, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (al) { - Pointer addr = loadArgLocalAddress(dest, al); - load64(addr, dest); - } else { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } + RegisterSizeDependentOps::loadArgumentInRegister(this, al, dest, argumentNumber); } void loadArgumentInRegister(IR::Const* c, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - QV4::Value v = convertToValue(c); - move(TrustedImm64(v.rawValue()), dest); + RegisterSizeDependentOps::loadArgumentInRegister(this, c, dest, argumentNumber); } void loadArgumentInRegister(IR::Expr* expr, RegisterID dest, int argumentNumber) { - Q_UNUSED(argumentNumber); - - if (!expr) { - QV4::Value undefined = QV4::Primitive::undefinedValue(); - move(TrustedImm64(undefined.rawValue()), dest); - } else if (IR::Temp *t = expr->asTemp()){ - loadArgumentInRegister(t, dest, argumentNumber); - } else if (IR::ArgLocal *al = expr->asArgLocal()) { - loadArgumentInRegister(al, dest, argumentNumber); - } else if (IR::Const *c = expr->asConst()) { - loadArgumentInRegister(c, dest, argumentNumber); - } else { - Q_ASSERT(!"unimplemented expression type in loadArgument"); - } - } -#else - void loadArgumentInRegister(IR::Expr*, RegisterID) - { - Q_ASSERT(!"unimplemented: expression in loadArgument"); + RegisterSizeDependentOps::loadArgumentInRegister(this, expr, dest, argumentNumber); } -#endif void loadArgumentInRegister(TrustedImm32 imm32, RegisterID dest, int argumentNumber) { Q_UNUSED(argumentNumber); - xorPtr(dest, dest); + RegisterSizeDependentOps::zeroRegister(this, dest); if (imm32.m_value) move(imm32, dest); } @@ -499,55 +1095,13 @@ public: void storeReturnValue(FPRegisterID dest) { -#ifdef VALUE_FITS_IN_REGISTER - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); -#elif defined(Q_PROCESSOR_ARM) - moveIntsToDouble(JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, dest, FPGpr0); -#elif defined(Q_PROCESSOR_X86) - moveIntsToDouble(JSC::X86Registers::eax, JSC::X86Registers::edx, dest, FPGpr0); -#elif defined(Q_PROCESSOR_MIPS) - moveIntsToDouble(JSC::MIPSRegisters::v0, JSC::MIPSRegisters::v1, dest, FPGpr0); -#else - subPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); - Pointer tmp(StackPointerRegister, 0); - storeReturnValue(tmp); - loadDouble(tmp, dest); - addPtr(TrustedImm32(sizeof(QV4::Value)), StackPointerRegister); -#endif + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#ifdef VALUE_FITS_IN_REGISTER - void storeReturnValue(const Pointer &dest) - { - store64(ReturnValueRegister, dest); - } -#elif defined(Q_PROCESSOR_X86) void storeReturnValue(const Pointer &dest) { - Pointer destination = dest; - store32(JSC::X86Registers::eax, destination); - destination.offset += 4; - store32(JSC::X86Registers::edx, destination); + RegisterSizeDependentOps::storeReturnValue(this, dest); } -#elif defined(Q_PROCESSOR_ARM) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::ARMRegisters::r0, destination); - destination.offset += 4; - store32(JSC::ARMRegisters::r1, destination); - } -#elif defined(Q_PROCESSOR_MIPS) - void storeReturnValue(const Pointer &dest) - { - Pointer destination = dest; - store32(JSC::MIPSRegisters::v0, destination); - destination.offset += 4; - store32(JSC::MIPSRegisters::v1, destination); - } -#endif void storeReturnValue(IR::Expr *target) { @@ -609,7 +1163,7 @@ public: Pointer ptr = toAddress(ScratchRegister, temp.value, argumentNumber); loadArgumentOnStack<StackSlot>(ptr, argumentNumber); } else { - poke(TrustedImmPtr(0), StackSlot); + RegisterSizeDependentOps::zeroStackSlot(this, StackSlot); } } @@ -648,38 +1202,18 @@ public: moveDouble(source, (FPRegisterID) targetTemp->index); return; } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - Pointer ptr = loadAddress(ScratchRegister, target); - store64(ReturnValueRegister, ptr); -#else - Pointer ptr = loadAddress(ScratchRegister, target); - storeDouble(source, ptr); -#endif + RegisterSizeDependentOps::storeDouble(this, source, target); } -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - // We need to (de)mangle the double + void loadDouble(Address addr, FPRegisterID dest) { - load64(addr, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - move64ToDouble(ReturnValueRegister, dest); + RegisterSizeDependentOps::loadDouble(this, addr, dest); } void storeDouble(FPRegisterID source, Address addr) { - moveDoubleTo64(source, ReturnValueRegister); - move(TrustedImm64(QV4::Value::NaNEncodeMask), ScratchRegister); - xor64(ScratchRegister, ReturnValueRegister); - store64(ReturnValueRegister, addr); + RegisterSizeDependentOps::storeDouble(this, source, addr); } -#else - using JSC::MacroAssembler::loadDouble; - using JSC::MacroAssembler::storeDouble; -#endif template <typename Result, typename Source> void copyValue(Result result, Source source); @@ -691,8 +1225,15 @@ public: { Q_ASSERT(!source->asTemp() || source->asTemp()->kind != IR::Temp::PhysicalRegister); Q_ASSERT(target.base != scratchRegister); - JSC::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); - JSC::MacroAssembler::storeDouble(FPGpr0, target); + TargetConfiguration::MacroAssembler::loadDouble(loadAddress(scratchRegister, source), FPGpr0); + TargetConfiguration::MacroAssembler::storeDouble(FPGpr0, target); + } + + // The scratch register is used to calculate the temp address for the source. + void memcopyValue(IR::Expr *target, Pointer source, FPRegisterID fpScratchRegister, RegisterID scratchRegister) + { + TargetConfiguration::MacroAssembler::loadDouble(source, fpScratchRegister); + TargetConfiguration::MacroAssembler::storeDouble(fpScratchRegister, loadAddress(scratchRegister, target)); } void storeValue(QV4::Primitive value, RegisterID destination) @@ -704,13 +1245,7 @@ public: void storeValue(QV4::Primitive value, Address destination) { -#ifdef VALUE_FITS_IN_REGISTER - store64(TrustedImm64(value.rawValue()), destination); -#else - store32(TrustedImm32(value.int_32()), destination); - destination.offset += 4; - store32(TrustedImm32(value.tag()), destination); -#endif + RegisterSizeDependentOps::storeValue(this, value, destination); } void storeValue(QV4::Primitive value, IR::Expr* temp); @@ -722,7 +1257,7 @@ public: void checkException() { load32(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, hasException)), ScratchRegister); - Jump exceptionThrown = branch32(NotEqual, ScratchRegister, TrustedImm32(0)); + Jump exceptionThrown = branch32(RelationalCondition::NotEqual, ScratchRegister, TrustedImm32(0)); if (catchBlock) addPatch(catchBlock, exceptionThrown); else @@ -781,6 +1316,27 @@ public: enum { Size = 0 }; }; + template <typename T> bool prepareCall(T &) + { return true; } + + bool prepareCall(LookupCall &lookupCall) + { + // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! + + // load the table from the context + loadPtr(Address(EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), ScratchRegister); + loadPtr(Address(ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), + lookupCall.addr.base); + // pre-calculate the indirect address for the lookupCall table: + if (lookupCall.addr.offset) + addPtr(TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); + // store it as the first argument + loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); + // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address + lookupCall.addr.offset = lookupCall.getterSetterOffset; + return false; + } + template <typename ArgRet, typename Callable, typename Arg1, typename Arg2, typename Arg3, typename Arg4, typename Arg5, typename Arg6> void generateFunctionCallImp(bool needsExceptionCheck, ArgRet r, const char* functionName, Callable function, Arg1 arg1, Arg2 arg2, Arg3 arg3, Arg4 arg4, Arg5 arg5, Arg6 arg6) { @@ -813,12 +1369,11 @@ public: loadArgumentOnStackOrRegister<2>(arg3); loadArgumentOnStackOrRegister<1>(arg2); - if (prepareCall(function, this)) + if (prepareCall(function)) loadArgumentOnStackOrRegister<0>(arg1); -#ifdef RESTORE_EBX_ON_CALL - load32(ebxAddressOnStack(), JSC::X86Registers::ebx); // restore the GOT ptr -#endif + if (JITTargetPlatform::gotRegister != -1) + load32(Address(JITTargetPlatform::FramePointerRegister, JITTargetPlatform::savedGOTRegisterSlotOnStack()), static_cast<RegisterID>(JITTargetPlatform::gotRegister)); // restore the GOT ptr callAbsolute(functionName, function); @@ -957,7 +1512,7 @@ public: void storeUInt32(RegisterID reg, Pointer addr) { // The UInt32 representation in QV4::Value is really convoluted. See also toUInt32Register. - Jump intRange = branch32(GreaterThanOrEqual, reg, TrustedImm32(0)); + Jump intRange = branch32(RelationalCondition::GreaterThanOrEqual, reg, TrustedImm32(0)); convertUInt32ToDouble(reg, FPGpr0, ReturnValueRegister); storeDouble(FPGpr0, addr); Jump done = jump(); @@ -980,15 +1535,7 @@ public: FPRegisterID toDoubleRegister(IR::Expr *e, FPRegisterID target = FPGpr0) { if (IR::Const *c = e->asConst()) { -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Q_STATIC_ASSERT(sizeof(int64_t) == sizeof(double)); - int64_t i; - memcpy(&i, &c->value, sizeof(double)); - move(TrustedImm64(i), ReturnValueRegister); - move64ToDouble(ReturnValueRegister, target); -#else - JSC::MacroAssembler::loadDouble(loadConstant(c, ScratchRegister), target); -#endif + RegisterSizeDependentOps::loadDoubleConstant(this, c, target); return target; } @@ -1047,7 +1594,7 @@ public: Pointer tagAddr = addr; tagAddr.offset += 4; load32(tagAddr, scratchReg); - Jump inIntRange = branch32(Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); + Jump inIntRange = branch32(RelationalCondition::Equal, scratchReg, TrustedImm32(QV4::Value::Integer_Type_Internal)); // it's not in signed int range, so load it as a double, and truncate it down loadDouble(addr, FPGpr0); @@ -1065,10 +1612,21 @@ public: return scratchReg; } + void returnFromFunction(IR::Ret *s, RegisterInformation regularRegistersToSave, RegisterInformation fpRegistersToSave); + JSC::MacroAssemblerCodeRef link(int *codeSize); void setStackLayout(int maxArgCountForBuiltins, int regularRegistersToSave, int fpRegistersToSave); const StackLayout &stackLayout() const { return *_stackLayout.data(); } + void initializeLocalVariables() + { + const int locals = _stackLayout->calculateJSStackFrameSize(); + if (locals <= 0) + return; + loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), JITTargetPlatform::LocalsRegister); + RegisterSizeDependentOps::initializeLocalVariables(this, locals); + storePtr(JITTargetPlatform::LocalsRegister, Address(JITTargetPlatform::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); + } Label exceptionReturnLabel; IR::BasicBlock * catchBlock; @@ -1095,22 +1653,16 @@ private: QV4::Compiler::JSUnitGenerator *_jsGenerator; }; +template <typename TargetConfiguration> template <typename Result, typename Source> -void Assembler::copyValue(Result result, Source source) +void Assembler<TargetConfiguration>::copyValue(Result result, Source source) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } +template <typename TargetConfiguration> template <typename Result> -void Assembler::copyValue(Result result, IR::Expr* source) +void Assembler<TargetConfiguration>::copyValue(Result result, IR::Expr* source) { if (source->type == IR::BoolType) { RegisterID reg = toInt32Register(source, ScratchRegister); @@ -1124,15 +1676,7 @@ void Assembler::copyValue(Result result, IR::Expr* source) } else if (source->type == IR::DoubleType) { storeDouble(toDoubleRegister(source), result); } else if (source->asTemp() || source->asArgLocal()) { -#ifdef VALUE_FITS_IN_REGISTER - // Use ReturnValueRegister as "scratch" register because loadArgument - // and storeArgument are functions that may need a scratch register themselves. - loadArgumentInRegister(source, ReturnValueRegister, 0); - storeReturnValue(result); -#else - loadDouble(source, FPGpr0); - storeDouble(FPGpr0, result); -#endif + RegisterSizeDependentOps::copyValueViaRegisters(this, source, result); } else if (IR::Const *c = source->asConst()) { QV4::Primitive v = convertToValue(c); storeValue(v, result); @@ -1141,34 +1685,12 @@ void Assembler::copyValue(Result result, IR::Expr* source) } } -inline RuntimeCall::RuntimeCall(uint offset) +template <typename TargetConfiguration> +inline Assembler<TargetConfiguration>::RuntimeCall::RuntimeCall(uint offset) : addr(Assembler::EngineRegister, offset + qOffsetOf(QV4::ExecutionEngine, runtime)) { } - - -template <typename T> inline bool prepareCall(T &, Assembler *) -{ return true; } - -template <> inline bool prepareCall(LookupCall &lookupCall, Assembler *as) -{ - // IMPORTANT! See generateLookupCall in qv4isel_masm_p.h for details! - - // load the table from the context - as->loadPtr(Assembler::Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - as->loadPtr(Assembler::Address(Assembler::ScratchRegister, qOffsetOf(QV4::Heap::ExecutionContext, lookups)), - lookupCall.addr.base); - // pre-calculate the indirect address for the lookupCall table: - if (lookupCall.addr.offset) - as->addPtr(Assembler::TrustedImm32(lookupCall.addr.offset), lookupCall.addr.base); - // store it as the first argument - as->loadArgumentOnStackOrRegister<0>(lookupCall.addr.base); - // set the destination addresses offset to the getterSetterOffset. The base is the lookupCall table's address - lookupCall.addr.offset = lookupCall.getterSetterOffset; - return false; -} - } // end of namespace JIT } // end of namespace QV4 diff --git a/src/qml/jit/qv4binop.cpp b/src/qml/jit/qv4binop.cpp index d2758c4a47..22067bbb13 100644 --- a/src/qml/jit/qv4binop.cpp +++ b/src/qml/jit/qv4binop.cpp @@ -41,8 +41,128 @@ #if ENABLE(ASSEMBLER) -using namespace QV4; -using namespace JIT; +QT_BEGIN_NAMESPACE + +namespace QV4 { +namespace JIT { + +template <typename JITAssembler> +struct ArchitectureSpecificBinaryOperation +{ + using FPRegisterID = typename JITAssembler::FPRegisterID; + + static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } + static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + Q_UNUSED(as); + Q_UNUSED(lhs); + Q_UNUSED(rhs); + Q_UNUSED(targetReg); + return false; + } +}; + +#if CPU(X86) +template <> +struct ArchitectureSpecificBinaryOperation<Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>> +{ + using JITAssembler = Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization>>; + using FPRegisterID = JITAssembler::FPRegisterID; + using Address = JITAssembler::Address; + + static bool doubleAdd(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->addDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->addDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleMul(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->mulDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->mulDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleSub(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->subDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->subDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } + static bool doubleDiv(JITAssembler *as, IR::Expr *lhs, IR::Expr *rhs, FPRegisterID targetReg) + { + if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + Address addr = as->loadConstant(c, JITAssembler::ScratchRegister); + as->divDouble(addr, targetReg); + return true; + } + if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] + if (t->kind != IR::Temp::PhysicalRegister) { + as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); + as->divDouble(as->loadTempAddress(t), targetReg); + return true; + } + } + return false; + } +}; +#endif #define OP(op) \ { "Runtime::" isel_stringIfy(op), offsetof(QV4::Runtime, op), INT_MIN, 0, 0, QV4::Runtime::Method_##op##_NeedsExceptionCheck } @@ -57,7 +177,8 @@ using namespace JIT; #define NULL_OP \ { 0, 0, 0, 0, 0, false } -const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { +template <typename JITAssembler> +const typename Binop<JITAssembler>::OpInfo Binop<JITAssembler>::operations[IR::LastAluOp + 1] = { NULL_OP, // OpInvalid NULL_OP, // OpIfTrue NULL_OP, // OpNot @@ -67,20 +188,20 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { NULL_OP, // OpIncrement NULL_OP, // OpDecrement - INLINE_OP(bitAnd, &Binop::inline_and32, &Binop::inline_and32), // OpBitAnd - INLINE_OP(bitOr, &Binop::inline_or32, &Binop::inline_or32), // OpBitOr - INLINE_OP(bitXor, &Binop::inline_xor32, &Binop::inline_xor32), // OpBitXor + INLINE_OP(bitAnd, &Binop<JITAssembler>::inline_and32, &Binop<JITAssembler>::inline_and32), // OpBitAnd + INLINE_OP(bitOr, &Binop<JITAssembler>::inline_or32, &Binop<JITAssembler>::inline_or32), // OpBitOr + INLINE_OP(bitXor, &Binop<JITAssembler>::inline_xor32, &Binop<JITAssembler>::inline_xor32), // OpBitXor - INLINE_OPCONTEXT(add, &Binop::inline_add32, &Binop::inline_add32), // OpAdd - INLINE_OP(sub, &Binop::inline_sub32, &Binop::inline_sub32), // OpSub - INLINE_OP(mul, &Binop::inline_mul32, &Binop::inline_mul32), // OpMul + INLINE_OPCONTEXT(add, &Binop<JITAssembler>::inline_add32, &Binop<JITAssembler>::inline_add32), // OpAdd + INLINE_OP(sub, &Binop<JITAssembler>::inline_sub32, &Binop<JITAssembler>::inline_sub32), // OpSub + INLINE_OP(mul, &Binop<JITAssembler>::inline_mul32, &Binop<JITAssembler>::inline_mul32), // OpMul OP(div), // OpDiv OP(mod), // OpMod - INLINE_OP(shl, &Binop::inline_shl32, &Binop::inline_shl32), // OpLShift - INLINE_OP(shr, &Binop::inline_shr32, &Binop::inline_shr32), // OpRShift - INLINE_OP(ushr, &Binop::inline_ushr32, &Binop::inline_ushr32), // OpURShift + INLINE_OP(shl, &Binop<JITAssembler>::inline_shl32, &Binop<JITAssembler>::inline_shl32), // OpLShift + INLINE_OP(shr, &Binop<JITAssembler>::inline_shr32, &Binop<JITAssembler>::inline_shr32), // OpRShift + INLINE_OP(ushr, &Binop<JITAssembler>::inline_ushr32, &Binop<JITAssembler>::inline_ushr32), // OpURShift OP(greaterThan), // OpGt OP(lessThan), // OpLt @@ -100,7 +221,8 @@ const Binop::OpInfo Binop::operations[IR::LastAluOp + 1] = { -void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +template <typename JITAssembler> +void Binop<JITAssembler>::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { if (op != IR::OpMod && lhs->type == IR::DoubleType && rhs->type == IR::DoubleType) { @@ -125,15 +247,15 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) info = stringAdd; } - RuntimeCall fallBack(info.fallbackImplementation); - RuntimeCall context(info.contextImplementation); + typename JITAssembler::RuntimeCall fallBack(info.fallbackImplementation); + typename JITAssembler::RuntimeCall context(info.contextImplementation); if (fallBack.isValid()) { as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, fallBack, PointerToValue(lhs), PointerToValue(rhs)); } else if (context.isValid()) { as->generateFunctionCallImp(info.needsExceptionCheck, target, info.name, context, - Assembler::EngineRegister, + JITAssembler::EngineRegister, PointerToValue(lhs), PointerToValue(rhs)); } else { @@ -145,119 +267,70 @@ void Binop::generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) } -void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) +template <typename JITAssembler> +void Binop<JITAssembler>::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); FPRegisterID targetReg; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) targetReg = (FPRegisterID) targetTemp->index; else - targetReg = Assembler::FPGpr0; + targetReg = JITAssembler::FPGpr0; switch (op) { case IR::OpAdd: if (lhs->asConst()) std::swap(lhs, rhs); // Y = constant + X -> Y = X + constant -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X + constant -> Y = X; Y += [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->addDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleAdd(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X + [temp-memory-address] -> Y = X; Y += [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->addDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif - as->addDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + + as->addDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpMul: if (lhs->asConst()) std::swap(lhs, rhs); // Y = constant * X -> Y = X * constant -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X * constant -> Y = X; Y *= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->mulDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleMul(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X * [temp-memory-address] -> Y = X; Y *= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->mulDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif - as->mulDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + + as->mulDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpSub: -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X - constant -> Y = X; Y -= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->subDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleSub(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X - [temp-memory-address] -> Y = X; Y -= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->subDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif + if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X - Y -> Tmp = Y; Y = X - Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); + as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); break; } - as->subDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->subDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; case IR::OpDiv: -#if CPU(X86) - if (IR::Const *c = rhs->asConst()) { // Y = X / constant -> Y = X; Y /= [constant-address] - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - Address addr = as->loadConstant(c, Assembler::ScratchRegister); - as->divDouble(addr, targetReg); + if (ArchitectureSpecificBinaryOperation<JITAssembler>::doubleDiv(as, lhs, rhs, targetReg)) break; - } - if (IR::Temp *t = rhs->asTemp()) { // Y = X / [temp-memory-address] -> Y = X; Y /= [temp-memory-address] - if (t->kind != IR::Temp::PhysicalRegister) { - as->moveDouble(as->toDoubleRegister(lhs, targetReg), targetReg); - as->divDouble(as->loadTempAddress(t), targetReg); - break; - } - } -#endif if (rhs->asTemp() && rhs->asTemp()->kind == IR::Temp::PhysicalRegister && targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister && targetTemp->index == rhs->asTemp()->index) { // Y = X / Y -> Tmp = Y; Y = X / Tmp - as->moveDouble(as->toDoubleRegister(rhs, Assembler::FPGpr1), Assembler::FPGpr1); - as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), Assembler::FPGpr1, targetReg); + as->moveDouble(as->toDoubleRegister(rhs, JITAssembler::FPGpr1), JITAssembler::FPGpr1); + as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), JITAssembler::FPGpr1, targetReg); break; } - as->divDouble(as->toDoubleRegister(lhs, Assembler::FPGpr0), as->toDoubleRegister(rhs, Assembler::FPGpr1), targetReg); + as->divDouble(as->toDoubleRegister(lhs, JITAssembler::FPGpr0), as->toDoubleRegister(rhs, JITAssembler::FPGpr1), targetReg); break; default: { - Q_ASSERT(target->type == IR::BoolType); Jump trueCase = as->branchDouble(false, op, lhs, rhs); as->storeBool(false, target); Jump done = as->jump(); @@ -271,8 +344,8 @@ void Binop::doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target) as->storeDouble(targetReg, target); } - -bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +bool Binop<JITAssembler>::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Q_ASSERT(leftSource->type == IR::SInt32Type); Q_ASSERT(rightSource->type == IR::SInt32Type); @@ -305,7 +378,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta bool inplaceOpWithAddress = false; IR::Temp *targetTemp = target->asTemp(); - RegisterID targetReg = Assembler::ReturnValueRegister; + RegisterID targetReg = JITAssembler::ReturnValueRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { IR::Temp *rhs = rightSource->asTemp(); if (!rhs || rhs->kind != IR::Temp::PhysicalRegister || rhs->index != targetTemp->index) { @@ -369,12 +442,12 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta && targetTemp->index == rightSource->asTemp()->index) { // X = Y - X -> Tmp = X; X = Y; X -= Tmp targetReg = (RegisterID) targetTemp->index; - as->move(targetReg, Assembler::ScratchRegister); + as->move(targetReg, JITAssembler::ScratchRegister); as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(Assembler::ScratchRegister, targetReg); + as->sub32(JITAssembler::ScratchRegister, targetReg); } else { as->move(as->toInt32Register(leftSource, targetReg), targetReg); - as->sub32(as->toInt32Register(rightSource, Assembler::ScratchRegister), targetReg); + as->sub32(as->toInt32Register(rightSource, JITAssembler::ScratchRegister), targetReg); } as->storeInt32(targetReg, target); return true; @@ -419,7 +492,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else if (inplaceOpWithAddress) { // All cases of X = X op [address-of-Y] - Pointer rhsAddr = as->loadAddress(Assembler::ScratchRegister, rightSource); + Pointer rhsAddr = as->loadAddress(JITAssembler::ScratchRegister, rightSource); switch (op) { case IR::OpBitAnd: as->and32(rhsAddr, targetReg); break; case IR::OpBitOr: as->or32 (rhsAddr, targetReg); break; @@ -433,7 +506,7 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return false; } } else { // All cases of Z = X op Y - RegisterID r = as->toInt32Register(rightSource, Assembler::ScratchRegister); + RegisterID r = as->toInt32Register(rightSource, JITAssembler::ScratchRegister); switch (op) { case IR::OpBitAnd: as->and32(l, r, targetReg); break; case IR::OpBitOr: as->or32 (l, r, targetReg); break; @@ -452,18 +525,18 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta // Not all CPUs accept shifts over more than 31 bits, and some CPUs (like ARM) will do // surprising stuff when shifting over 0 bits. #define CHECK_RHS(op) { \ - as->and32(TrustedImm32(0x1f), r, Assembler::ScratchRegister); \ - Jump notZero = as->branch32(RelationalCondition::NotEqual, Assembler::ScratchRegister, TrustedImm32(0)); \ + as->and32(TrustedImm32(0x1f), r, JITAssembler::ScratchRegister); \ + Jump notZero = as->branch32(RelationalCondition::NotEqual, JITAssembler::ScratchRegister, TrustedImm32(0)); \ as->move(l, targetReg); \ Jump done = as->jump(); \ notZero.link(as); \ op; \ done.link(as); \ } - case IR::OpLShift: CHECK_RHS(as->lshift32(l, Assembler::ScratchRegister, targetReg)); break; - case IR::OpRShift: CHECK_RHS(as->rshift32(l, Assembler::ScratchRegister, targetReg)); break; + case IR::OpLShift: CHECK_RHS(as->lshift32(l, JITAssembler::ScratchRegister, targetReg)); break; + case IR::OpRShift: CHECK_RHS(as->rshift32(l, JITAssembler::ScratchRegister, targetReg)); break; case IR::OpURShift: - CHECK_RHS(as->urshift32(l, Assembler::ScratchRegister, targetReg)); + CHECK_RHS(as->urshift32(l, JITAssembler::ScratchRegister, targetReg)); as->storeUInt32(targetReg, target); // IMPORTANT: do NOT do a break here! The stored type of an urshift is different from the other binary operations! return true; @@ -481,17 +554,19 @@ bool Binop::int32Binop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *ta return true; } -static inline Assembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) +template <typename JITAssembler> +inline typename JITAssembler::FPRegisterID getFreeFPReg(IR::Expr *shouldNotOverlap, unsigned hint) { if (IR::Temp *t = shouldNotOverlap->asTemp()) if (t->type == IR::DoubleType) if (t->kind == IR::Temp::PhysicalRegister) if (t->index == hint) - return Assembler::FPRegisterID(hint + 1); - return Assembler::FPRegisterID(hint); + return typename JITAssembler::FPRegisterID(hint + 1); + return typename JITAssembler::FPRegisterID(hint); } -Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +typename JITAssembler::Jump Binop<JITAssembler>::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { Jump done; @@ -505,8 +580,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc // register. switch (op) { case IR::OpAdd: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -520,8 +595,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpMul: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -535,8 +610,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpSub: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -550,8 +625,8 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc rightIsNoDbl.link(as); } break; case IR::OpDiv: { - FPRegisterID lReg = getFreeFPReg(rightSource, 2); - FPRegisterID rReg = getFreeFPReg(leftSource, 4); + FPRegisterID lReg = getFreeFPReg<JITAssembler>(rightSource, 2); + FPRegisterID rReg = getFreeFPReg<JITAssembler>(leftSource, 4); Jump leftIsNoDbl = as->genTryDoubleConversion(leftSource, lReg); Jump rightIsNoDbl = as->genTryDoubleConversion(rightSource, rReg); @@ -571,4 +646,20 @@ Assembler::Jump Binop::genInlineBinop(IR::Expr *leftSource, IR::Expr *rightSourc return done; } +template struct QV4::JIT::Binop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif +#if !CPU(ARM64) +template struct QV4::JIT::Binop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; +#endif +#endif + +} // end of namespace JIT +} // end of namespace QV4 + +QT_END_NAMESPACE + + #endif diff --git a/src/qml/jit/qv4binop_p.h b/src/qml/jit/qv4binop_p.h index 3742e99e5a..d2d9ba7753 100644 --- a/src/qml/jit/qv4binop_p.h +++ b/src/qml/jit/qv4binop_p.h @@ -61,21 +61,22 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler> struct Binop { - Binop(Assembler *assembler, IR::AluOp operation) + Binop(JITAssembler *assembler, IR::AluOp operation) : as(assembler) , op(operation) {} - using Jump = Assembler::Jump; - using Address = Assembler::Address; - using RegisterID = Assembler::RegisterID; - using FPRegisterID = Assembler::FPRegisterID; - using TrustedImm32 = Assembler::TrustedImm32; - using ResultCondition = Assembler::ResultCondition; - using RelationalCondition = Assembler::RelationalCondition; - using Pointer = Assembler::Pointer; - using PointerToValue = Assembler::PointerToValue; + using Jump = typename JITAssembler::Jump; + using Address = typename JITAssembler::Address; + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using ResultCondition = typename JITAssembler::ResultCondition; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using Pointer = typename JITAssembler::Pointer; + using PointerToValue = typename JITAssembler::PointerToValue; void generate(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); void doubleBinop(IR::Expr *lhs, IR::Expr *rhs, IR::Expr *target); @@ -103,8 +104,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchAdd32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchAdd32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchAdd32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -118,8 +119,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) return as->branchSub32(ResultCondition::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchSub32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchSub32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -131,10 +132,10 @@ struct Binop { Jump inline_mul32(Address addr, RegisterID reg) { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) - return as->branchMul32(Assembler::Overflow, addr, reg); + return as->branchMul32(JITAssembler::Overflow, addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - return as->branchMul32(ResultCondition::Overflow, Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + return as->branchMul32(ResultCondition::Overflow, JITAssembler::ScratchRegister, reg); #endif } @@ -145,9 +146,9 @@ struct Binop { Jump inline_shl32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->lshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->lshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -160,9 +161,9 @@ struct Binop { Jump inline_shr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->rshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->rshift32(JITAssembler::ScratchRegister, reg); return Jump(); } @@ -175,9 +176,9 @@ struct Binop { Jump inline_ushr32(Address addr, RegisterID reg) { - as->load32(addr, Assembler::ScratchRegister); - as->and32(TrustedImm32(0x1f), Assembler::ScratchRegister); - as->urshift32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(TrustedImm32(0x1f), JITAssembler::ScratchRegister); + as->urshift32(JITAssembler::ScratchRegister, reg); return as->branchTest32(ResultCondition::Signed, reg, reg); } @@ -193,8 +194,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->and32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->and32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->and32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -210,8 +211,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->or32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->or32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->or32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -227,8 +228,8 @@ struct Binop { #if HAVE(ALU_OPS_WITH_MEM_OPERAND) as->xor32(addr, reg); #else - as->load32(addr, Assembler::ScratchRegister); - as->xor32(Assembler::ScratchRegister, reg); + as->load32(addr, JITAssembler::ScratchRegister); + as->xor32(JITAssembler::ScratchRegister, reg); #endif return Jump(); } @@ -241,7 +242,7 @@ struct Binop { - Assembler *as; + JITAssembler *as; IR::AluOp op; }; diff --git a/src/qml/jit/qv4isel_masm.cpp b/src/qml/jit/qv4isel_masm.cpp index 20752b5c34..69d6951bb9 100644 --- a/src/qml/jit/qv4isel_masm.cpp +++ b/src/qml/jit/qv4isel_masm.cpp @@ -39,11 +39,7 @@ #include "qv4isel_masm_p.h" #include "qv4runtime_p.h" -#include "qv4object_p.h" -#include "qv4functionobject_p.h" -#include "qv4regexpobject_p.h" #include "qv4lookup_p.h" -#include "qv4function_p.h" #include "qv4ssa_p.h" #include "qv4regalloc_p.h" #include "qv4assembler_p.h" @@ -68,7 +64,8 @@ using namespace QV4; using namespace QV4::JIT; -InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) +template <typename JITAssembler> +InstructionSelection<JITAssembler>::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, Compiler::JSUnitGenerator *jsGenerator, EvalISelFactory *iselFactory) : EvalInstructionSelection(execAllocator, module, jsGenerator, iselFactory) , _block(0) , _as(0) @@ -79,12 +76,14 @@ InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::Ex module->unitFlags |= QV4::CompiledData::Unit::ContainsMachineCode; } -InstructionSelection::~InstructionSelection() +template <typename JITAssembler> +InstructionSelection<JITAssembler>::~InstructionSelection() { delete _as; } -void InstructionSelection::run(int functionIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::run(int functionIndex) { IR::Function *function = irModule->functions[functionIndex]; qSwap(_function, function); @@ -93,8 +92,8 @@ void InstructionSelection::run(int functionIndex) opt.run(qmlEngine); static const bool withRegisterAllocator = qEnvironmentVariableIsEmpty("QV4_NO_REGALLOC"); - if (Assembler::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { - RegisterAllocator regalloc(Assembler::getRegisterInfo()); + if (JITTargetPlatform::RegAllocIsSupported && opt.isInSSA() && withRegisterAllocator) { + RegisterAllocator regalloc(JITTargetPlatform::getRegisterInfo()); regalloc.run(_function, opt); calculateRegistersToSave(regalloc.usedRegisters()); } else { @@ -103,49 +102,24 @@ void InstructionSelection::run(int functionIndex) opt.convertOutOfSSA(); ConvertTemps().toStackSlots(_function); IR::Optimizer::showMeTheCode(_function, "After stack slot allocation"); - calculateRegistersToSave(Assembler::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. + calculateRegistersToSave(JITTargetPlatform::getRegisterInfo()); // FIXME: this saves all registers. We can probably do with a subset: those that are not used by the register allocator. } BitVector removableJumps = opt.calculateOptionalJumps(); qSwap(_removableJumps, removableJumps); - Assembler* oldAssembler = _as; - _as = new Assembler(jsGenerator, _function, executableAllocator); + JITAssembler* oldAssembler = _as; + _as = new JITAssembler(jsGenerator, _function, executableAllocator); _as->setStackLayout(6, // 6 == max argc for calls to built-ins with an argument array regularRegistersToSave.size(), fpRegistersToSave.size()); _as->enterStandardStackFrame(regularRegistersToSave, fpRegistersToSave); -#ifdef ARGUMENTS_IN_REGISTERS - _as->move(_as->registerForArgument(0), Assembler::EngineRegister); -#else - _as->loadPtr(addressForArgument(0), Assembler::EngineRegister); -#endif - - const int locals = _as->stackLayout().calculateJSStackFrameSize(); - if (locals > 0) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop)), Assembler::LocalsRegister); -#ifdef VALUE_FITS_IN_REGISTER - _as->move(Assembler::TrustedImm64(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store64(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add64(Assembler::TrustedImm32(8), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); - jump.linkTo(loop, _as); -#else - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm32(locals), Assembler::ScratchRegister); - Assembler::Label loop = _as->label(); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - _as->store32(Assembler::ReturnValueRegister, Assembler::Address(Assembler::LocalsRegister)); - _as->add32(Assembler::TrustedImm32(4), Assembler::LocalsRegister); - Assembler::Jump jump = _as->branchSub32(Assembler::NonZero, Assembler::TrustedImm32(1), Assembler::ScratchRegister); - jump.linkTo(loop, _as); -#endif - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::EngineRegister, qOffsetOf(ExecutionEngine, jsStackTop))); - } + if (JITTargetPlatform::RegisterArgumentCount > 0) + _as->move(_as->registerForArgument(0), JITTargetPlatform::EngineRegister); + else + _as->loadPtr(addressForArgument(0), JITTargetPlatform::EngineRegister); + _as->initializeLocalVariables(); int lastLine = 0; for (int i = 0, ei = _function->basicBlockCount(); i != ei; ++i) { @@ -158,9 +132,9 @@ void InstructionSelection::run(int functionIndex) for (IR::Stmt *s : _block->statements()) { if (s->location.isValid()) { if (int(s->location.startLine) != lastLine) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - Assembler::Address lineAddr(Assembler::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); - _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + Address lineAddr(JITTargetPlatform::ScratchRegister, qOffsetOf(QV4::ExecutionContext::Data, lineNumber)); + _as->store32(TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } } @@ -181,165 +155,187 @@ void InstructionSelection::run(int functionIndex) qSwap(_removableJumps, removableJumps); } -QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection::backendCompileStep() +template <typename JITAssembler> +QQmlRefPointer<QV4::CompiledData::CompilationUnit> InstructionSelection<JITAssembler>::backendCompileStep() { QQmlRefPointer<QV4::CompiledData::CompilationUnit> result; result.adopt(compilationUnit.take()); return result; } -void InstructionSelection::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinInvalid(IR::Name *func, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, 0); if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(result, callGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, callActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } } -void InstructionSelection::callBuiltinTypeofQmlContextProperty(IR::Expr *base, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::Expr *result) { if (kind == IR::Member::MemberOfQmlScopeObject) { - generateRuntimeCall(result, typeofScopeObjectProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofScopeObjectProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), + TrustedImm32(propertyIndex)); } else if (kind == IR::Member::MemberOfQmlContextObject) { - generateRuntimeCall(result, typeofContextObjectProperty, - Assembler::EngineRegister, Assembler::PointerToValue(base), - Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, result, typeofContextObjectProperty, + JITTargetPlatform::EngineRegister, PointerToValue(base), + TrustedImm32(propertyIndex)); } else { Q_UNREACHABLE(); } } -void InstructionSelection::callBuiltinTypeofMember(IR::Expr *base, const QString &name, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofMember, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofMember, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, typeofElement, - Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, typeofElement, + JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinTypeofName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, typeofName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, typeofName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinTypeofValue(IR::Expr *value, IR::Expr *result) { - generateRuntimeCall(result, typeofValue, Assembler::EngineRegister, - Assembler::PointerToValue(value)); + generateRuntimeCall(_as, result, typeofValue, JITTargetPlatform::EngineRegister, + PointerToValue(value)); } -void InstructionSelection::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteMember(IR::Expr *base, const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteMember, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteMember, JITTargetPlatform::EngineRegister, + Reference(base), StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteSubscript(IR::Expr *base, IR::Expr *index, IR::Expr *result) { - generateRuntimeCall(result, deleteElement, Assembler::EngineRegister, - Assembler::Reference(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, result, deleteElement, JITTargetPlatform::EngineRegister, + Reference(base), PointerToValue(index)); } -void InstructionSelection::callBuiltinDeleteName(const QString &name, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteName(const QString &name, IR::Expr *result) { - generateRuntimeCall(result, deleteName, Assembler::EngineRegister, - Assembler::StringToIndex(name)); + generateRuntimeCall(_as, result, deleteName, JITTargetPlatform::EngineRegister, + StringToIndex(name)); } -void InstructionSelection::callBuiltinDeleteValue(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeleteValue(IR::Expr *result) { _as->storeValue(Primitive::fromBoolean(false), result); } -void InstructionSelection::callBuiltinThrow(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinThrow(IR::Expr *arg) { - generateRuntimeCall(Assembler::ReturnValueRegister, throwException, Assembler::EngineRegister, - Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, throwException, JITTargetPlatform::EngineRegister, + PointerToValue(arg)); } -void InstructionSelection::callBuiltinReThrow() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinReThrow() { _as->jumpToExceptionHandler(); } -void InstructionSelection::callBuiltinUnwindException(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinUnwindException(IR::Expr *result) { - generateRuntimeCall(result, unwindException, Assembler::EngineRegister); + generateRuntimeCall(_as, result, unwindException, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPushCatchScope(const QString &exceptionName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushCatchScope(const QString &exceptionName) { - generateRuntimeCall(Assembler::Void, pushCatchScope, Assembler::EngineRegister, Assembler::StringToIndex(exceptionName)); + generateRuntimeCall(_as, JITAssembler::Void, pushCatchScope, JITTargetPlatform::EngineRegister, StringToIndex(exceptionName)); } -void InstructionSelection::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachIteratorObject(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachIterator, Assembler::EngineRegister, Assembler::PointerToValue(arg)); + generateRuntimeCall(_as, result, foreachIterator, JITTargetPlatform::EngineRegister, PointerToValue(arg)); } -void InstructionSelection::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinForeachNextPropertyname(IR::Expr *arg, IR::Expr *result) { Q_ASSERT(arg); Q_ASSERT(result); - generateRuntimeCall(result, foreachNextPropertyName, Assembler::Reference(arg)); + generateRuntimeCall(_as, result, foreachNextPropertyName, Reference(arg)); } -void InstructionSelection::callBuiltinPushWithScope(IR::Expr *arg) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPushWithScope(IR::Expr *arg) { Q_ASSERT(arg); - generateRuntimeCall(Assembler::Void, pushWithScope, Assembler::Reference(arg), Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, pushWithScope, Reference(arg), JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinPopScope() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinPopScope() { - generateRuntimeCall(Assembler::Void, popScope, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, popScope, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinDeclareVar(bool deletable, const QString &name) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDeclareVar(bool deletable, const QString &name) { - generateRuntimeCall(Assembler::Void, declareVar, Assembler::EngineRegister, - Assembler::TrustedImm32(deletable), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, JITAssembler::Void, declareVar, JITTargetPlatform::EngineRegister, + TrustedImm32(deletable), StringToIndex(name)); } -void InstructionSelection::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineArray(IR::Expr *result, IR::ExprList *args) { Q_ASSERT(result); int length = prepareVariableArguments(args); - generateRuntimeCall(result, arrayLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(length)); + generateRuntimeCall(_as, result, arrayLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(length)); } -void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinDefineObjectLiteral(IR::Expr *result, int keyValuePairCount, IR::ExprList *keyValuePairs, IR::ExprList *arrayEntries, bool needSparseArray) { Q_ASSERT(result); @@ -415,81 +411,83 @@ void InstructionSelection::callBuiltinDefineObjectLiteral(IR::Expr *result, int it = it->next; } - generateRuntimeCall(result, objectLiteral, Assembler::EngineRegister, - baseAddressForCallArguments(), Assembler::TrustedImm32(classId), - Assembler::TrustedImm32(arrayValueCount), Assembler::TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); + generateRuntimeCall(_as, result, objectLiteral, JITTargetPlatform::EngineRegister, + baseAddressForCallArguments(), TrustedImm32(classId), + TrustedImm32(arrayValueCount), TrustedImm32(arrayGetterSetterCount | (needSparseArray << 30))); } -void InstructionSelection::callBuiltinSetupArgumentObject(IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinSetupArgumentObject(IR::Expr *result) { - generateRuntimeCall(result, setupArgumentsObject, Assembler::EngineRegister); + generateRuntimeCall(_as, result, setupArgumentsObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callBuiltinConvertThisToObject() +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callBuiltinConvertThisToObject() { - generateRuntimeCall(Assembler::Void, convertThisToObject, Assembler::EngineRegister); + generateRuntimeCall(_as, JITAssembler::Void, convertThisToObject, JITTargetPlatform::EngineRegister); } -void InstructionSelection::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value); prepareCallData(args, 0); if (value->asConst()) - generateRuntimeCall(result, callValue, Assembler::EngineRegister, - Assembler::PointerToValue(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + PointerToValue(value), baseAddressForCallData()); else - generateRuntimeCall(result, callValue, Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, callValue, JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::loadThisObject(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadThisObject(IR::Expr *temp) { - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), Assembler::ScratchRegister); -#if defined(VALUE_FITS_IN_REGISTER) - _as->load64(Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject)), - Assembler::ReturnValueRegister); - _as->storeReturnValue(temp); -#else - _as->copyValue(temp, Pointer(Assembler::ScratchRegister, qOffsetOf(CallData, thisObject))); -#endif + _as->loadPtr(Address(JITTargetPlatform::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), JITTargetPlatform::ScratchRegister); + _as->loadPtr(Address(JITTargetPlatform::ScratchRegister, qOffsetOf(ExecutionContext::Data, callData)), JITTargetPlatform::ScratchRegister); + _as->copyValue(temp, Address(JITTargetPlatform::ScratchRegister, qOffsetOf(CallData, thisObject))); } -void InstructionSelection::loadQmlContext(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlContext(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlContext, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlContext, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlImportedScripts(IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlImportedScripts(IR::Expr *temp) { - generateRuntimeCall(temp, getQmlImportedScripts, Assembler::EngineRegister); + generateRuntimeCall(_as, temp, getQmlImportedScripts, JITTargetPlatform::EngineRegister); } -void InstructionSelection::loadQmlSingleton(const QString &name, IR::Expr *temp) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadQmlSingleton(const QString &name, IR::Expr *temp) { - generateRuntimeCall(temp, getQmlSingleton, Assembler::EngineRegister, Assembler::StringToIndex(name)); + generateRuntimeCall(_as, temp, getQmlSingleton, JITTargetPlatform::EngineRegister, StringToIndex(name)); } -void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadConst(IR::Const *sourceConst, IR::Expr *target) { if (IR::Temp *targetTemp = target->asTemp()) { if (targetTemp->kind == IR::Temp::PhysicalRegister) { if (targetTemp->type == IR::DoubleType) { Q_ASSERT(sourceConst->type == IR::DoubleType); - _as->toDoubleRegister(sourceConst, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(sourceConst, (FPRegisterID) targetTemp->index); } else if (targetTemp->type == IR::SInt32Type) { Q_ASSERT(sourceConst->type == IR::SInt32Type); - _as->toInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::UInt32Type) { Q_ASSERT(sourceConst->type == IR::UInt32Type); - _as->toUInt32Register(sourceConst, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(sourceConst, (RegisterID) targetTemp->index); } else if (targetTemp->type == IR::BoolType) { Q_ASSERT(sourceConst->type == IR::BoolType); - _as->move(Assembler::TrustedImm32(convertToValue(sourceConst).int_32()), - (Assembler::RegisterID) targetTemp->index); + _as->move(TrustedImm32(convertToValue(sourceConst).int_32()), + (RegisterID) targetTemp->index); } else { Q_UNREACHABLE(); } @@ -500,147 +498,155 @@ void InstructionSelection::loadConst(IR::Const *sourceConst, IR::Expr *target) _as->storeValue(convertToValue(sourceConst), target); } -void InstructionSelection::loadString(const QString &str, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadString(const QString &str, IR::Expr *target) { - Pointer srcAddr = _as->loadStringAddress(Assembler::ReturnValueRegister, str); - _as->loadPtr(srcAddr, Assembler::ReturnValueRegister); - Pointer destAddr = _as->loadAddress(Assembler::ScratchRegister, target); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->store64(Assembler::ReturnValueRegister, destAddr); -#else - _as->store32(Assembler::ReturnValueRegister, destAddr); - destAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(QV4::Value::Managed_Type_Internal), destAddr); -#endif + Pointer srcAddr = _as->loadStringAddress(JITTargetPlatform::ReturnValueRegister, str); + _as->loadPtr(srcAddr, JITTargetPlatform::ReturnValueRegister); + Pointer destAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, target); + JITAssembler::RegisterSizeDependentOps::loadManagedPointer(_as, JITTargetPlatform::ReturnValueRegister, destAddr); } -void InstructionSelection::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::loadRegexp(IR::RegExp *sourceRegexp, IR::Expr *target) { int id = registerRegExp(sourceRegexp); - generateRuntimeCall(target, regexpLiteral, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, regexpLiteral, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getActivationProperty(const IR::Name *name, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getActivationProperty(const IR::Name *name, IR::Expr *target) { if (useFastLookups && name->global) { uint index = registerGlobalGetterLookup(*name->id); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), Assembler::EngineRegister, Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, globalGetter), JITTargetPlatform::EngineRegister, JITAssembler::Void); return; } - generateRuntimeCall(target, getActivationProperty, Assembler::EngineRegister, Assembler::StringToIndex(*name->id)); + generateRuntimeCall(_as, target, getActivationProperty, JITTargetPlatform::EngineRegister, StringToIndex(*name->id)); } -void InstructionSelection::setActivationProperty(IR::Expr *source, const QString &targetName) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setActivationProperty(IR::Expr *source, const QString &targetName) { // ### should use a lookup call here - generateRuntimeCall(Assembler::Void, setActivationProperty, - Assembler::EngineRegister, Assembler::StringToIndex(targetName), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setActivationProperty, + JITTargetPlatform::EngineRegister, StringToIndex(targetName), PointerToValue(source)); } -void InstructionSelection::initClosure(IR::Closure *closure, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::initClosure(IR::Closure *closure, IR::Expr *target) { int id = closure->value; - generateRuntimeCall(target, closure, Assembler::EngineRegister, Assembler::TrustedImm32(id)); + generateRuntimeCall(_as, target, closure, JITTargetPlatform::EngineRegister, TrustedImm32(id)); } -void InstructionSelection::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getProperty(IR::Expr *base, const QString &name, IR::Expr *target) { if (useFastLookups) { uint index = registerGetterLookup(name); - generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::Void); + generateLookupCall(target, index, qOffsetOf(QV4::Lookup, getter), JITTargetPlatform::EngineRegister, PointerToValue(base), JITAssembler::Void); } else { - generateRuntimeCall(target, getProperty, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::StringToIndex(name)); + generateRuntimeCall(_as, target, getProperty, JITTargetPlatform::EngineRegister, + PointerToValue(base), StringToIndex(name)); } } -void InstructionSelection::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int index, bool captureRequired, IR::Expr *target) { if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(target, getQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(target, getQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index), Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index), TrustedImm32(captureRequired)); else if (kind == IR::Member::MemberOfIdObjectsArray) - generateRuntimeCall(target, getQmlIdObject, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(index)); + generateRuntimeCall(_as, target, getQmlIdObject, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(index)); else Q_ASSERT(false); } -void InstructionSelection::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getQObjectProperty(IR::Expr *base, int propertyIndex, bool captureRequired, bool isSingleton, int attachedPropertiesId, IR::Expr *target) { if (attachedPropertiesId != 0) - generateRuntimeCall(target, getQmlAttachedProperty, Assembler::EngineRegister, Assembler::TrustedImm32(attachedPropertiesId), Assembler::TrustedImm32(propertyIndex)); + generateRuntimeCall(_as, target, getQmlAttachedProperty, JITTargetPlatform::EngineRegister, TrustedImm32(attachedPropertiesId), TrustedImm32(propertyIndex)); else if (isSingleton) - generateRuntimeCall(target, getQmlSingletonQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlSingletonQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); else - generateRuntimeCall(target, getQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(base), Assembler::TrustedImm32(propertyIndex), - Assembler::TrustedImm32(captureRequired)); + generateRuntimeCall(_as, target, getQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(base), TrustedImm32(propertyIndex), + TrustedImm32(captureRequired)); } -void InstructionSelection::setProperty(IR::Expr *source, IR::Expr *targetBase, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setProperty(IR::Expr *source, IR::Expr *targetBase, const QString &targetName) { if (useFastLookups) { uint index = registerSetterLookup(targetName); - generateLookupCall(Assembler::Void, index, qOffsetOf(QV4::Lookup, setter), - Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, index, qOffsetOf(QV4::Lookup, setter), + JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), + PointerToValue(source)); } else { - generateRuntimeCall(Assembler::Void, setProperty, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::StringToIndex(targetName), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setProperty, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), StringToIndex(targetName), + PointerToValue(source)); } } -void InstructionSelection::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setQmlContextProperty(IR::Expr *source, IR::Expr *targetBase, IR::Member::MemberKind kind, int propertyIndex) { if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(Assembler::Void, setQmlScopeObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlScopeObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(Assembler::Void, setQmlContextObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlContextObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); else Q_ASSERT(false); } -void InstructionSelection::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setQObjectProperty(IR::Expr *source, IR::Expr *targetBase, int propertyIndex) { - generateRuntimeCall(Assembler::Void, setQmlQObjectProperty, Assembler::EngineRegister, Assembler::PointerToValue(targetBase), - Assembler::TrustedImm32(propertyIndex), Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setQmlQObjectProperty, JITTargetPlatform::EngineRegister, PointerToValue(targetBase), + TrustedImm32(propertyIndex), PointerToValue(source)); } -void InstructionSelection::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::getElement(IR::Expr *base, IR::Expr *index, IR::Expr *target) { if (useFastLookups) { uint lookup = registerIndexedGetterLookup(); generateLookupCall(target, lookup, qOffsetOf(QV4::Lookup, indexedGetter), - Assembler::PointerToValue(base), - Assembler::PointerToValue(index)); + PointerToValue(base), + PointerToValue(index)); return; } - generateRuntimeCall(target, getElement, Assembler::EngineRegister, - Assembler::PointerToValue(base), Assembler::PointerToValue(index)); + generateRuntimeCall(_as, target, getElement, JITTargetPlatform::EngineRegister, + PointerToValue(base), PointerToValue(index)); } -void InstructionSelection::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::setElement(IR::Expr *source, IR::Expr *targetBase, IR::Expr *targetIndex) { if (useFastLookups) { uint lookup = registerIndexedSetterLookup(); - generateLookupCall(Assembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateLookupCall(JITAssembler::Void, lookup, qOffsetOf(QV4::Lookup, indexedSetter), + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); return; } - generateRuntimeCall(Assembler::Void, setElement, Assembler::EngineRegister, - Assembler::PointerToValue(targetBase), Assembler::PointerToValue(targetIndex), - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITAssembler::Void, setElement, JITTargetPlatform::EngineRegister, + PointerToValue(targetBase), PointerToValue(targetIndex), + PointerToValue(source)); } -void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::copyValue(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -655,25 +661,25 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) { if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) { if (sourceTemp->type == IR::DoubleType) - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->moveDouble((FPRegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); else - _as->move((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _as->move((RegisterID) sourceTemp->index, + (RegisterID) targetTemp->index); return; } else { switch (sourceTemp->type) { case IR::DoubleType: - _as->storeDouble((Assembler::FPRegisterID) sourceTemp->index, target); + _as->storeDouble((FPRegisterID) sourceTemp->index, target); break; case IR::SInt32Type: - _as->storeInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeInt32((RegisterID) sourceTemp->index, target); break; case IR::UInt32Type: - _as->storeUInt32((Assembler::RegisterID) sourceTemp->index, target); + _as->storeUInt32((RegisterID) sourceTemp->index, target); break; case IR::BoolType: - _as->storeBool((Assembler::RegisterID) sourceTemp->index, target); + _as->storeBool((RegisterID) sourceTemp->index, target); break; default: Q_ASSERT(!"Unreachable"); @@ -685,19 +691,19 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) switch (targetTemp->type) { case IR::DoubleType: Q_ASSERT(source->type == IR::DoubleType); - _as->toDoubleRegister(source, (Assembler::FPRegisterID) targetTemp->index); + _as->toDoubleRegister(source, (FPRegisterID) targetTemp->index); return; case IR::BoolType: Q_ASSERT(source->type == IR::BoolType); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::SInt32Type: Q_ASSERT(source->type == IR::SInt32Type); - _as->toInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toInt32Register(source, (RegisterID) targetTemp->index); return; case IR::UInt32Type: Q_ASSERT(source->type == IR::UInt32Type); - _as->toUInt32Register(source, (Assembler::RegisterID) targetTemp->index); + _as->toUInt32Register(source, (RegisterID) targetTemp->index); return; default: Q_ASSERT(!"Unreachable"); @@ -706,10 +712,11 @@ void InstructionSelection::copyValue(IR::Expr *source, IR::Expr *target) } // The target is not a physical register, nor is the source. So we can do a memory-to-memory copy: - _as->memcopyValue(_as->loadAddress(Assembler::ReturnValueRegister, target), source, Assembler::ScratchRegister); + _as->memcopyValue(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, target), source, JITTargetPlatform::ScratchRegister); } -void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::swapValues(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); IR::Temp *targetTemp = target->asTemp(); @@ -719,26 +726,27 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(sourceTemp->type == targetTemp->type); if (sourceTemp->type == IR::DoubleType) { - _as->moveDouble((Assembler::FPRegisterID) targetTemp->index, Assembler::FPGpr0); - _as->moveDouble((Assembler::FPRegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) sourceTemp->index); + _as->moveDouble((FPRegisterID) targetTemp->index, JITTargetPlatform::FPGpr0); + _as->moveDouble((FPRegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); + _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) sourceTemp->index); } else { - _as->swap((Assembler::RegisterID) sourceTemp->index, - (Assembler::RegisterID) targetTemp->index); + _as->swap((RegisterID) sourceTemp->index, + (RegisterID) targetTemp->index); } return; } } else if (!sourceTemp || sourceTemp->kind == IR::Temp::StackSlot) { if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { // Note: a swap for two stack-slots can involve different types. - Assembler::Pointer sAddr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tAddr = _as->loadAddress(Assembler::ReturnValueRegister, target); + Pointer sAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target); // use the implementation in JSC::MacroAssembler, as it doesn't do bit swizzling - _as->JSC::MacroAssembler::loadDouble(sAddr, Assembler::FPGpr0); - _as->JSC::MacroAssembler::loadDouble(tAddr, Assembler::FPGpr1); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr1, sAddr); - _as->JSC::MacroAssembler::storeDouble(Assembler::FPGpr0, tAddr); + auto platformAs = static_cast<typename JITAssembler::MacroAssembler*>(_as); + platformAs->loadDouble(sAddr, JITTargetPlatform::FPGpr0); + platformAs->loadDouble(tAddr, JITTargetPlatform::FPGpr1); + platformAs->storeDouble(JITTargetPlatform::FPGpr1, sAddr); + platformAs->storeDouble(JITTargetPlatform::FPGpr0, tAddr); return; } } @@ -749,18 +757,18 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) Q_ASSERT(memExpr); Q_ASSERT(regTemp); - Assembler::Pointer addr = _as->loadAddress(Assembler::ReturnValueRegister, memExpr); + Pointer addr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, memExpr); if (regTemp->type == IR::DoubleType) { - _as->loadDouble(addr, Assembler::FPGpr0); - _as->storeDouble((Assembler::FPRegisterID) regTemp->index, addr); - _as->moveDouble(Assembler::FPGpr0, (Assembler::FPRegisterID) regTemp->index); + _as->loadDouble(addr, JITTargetPlatform::FPGpr0); + _as->storeDouble((FPRegisterID) regTemp->index, addr); + _as->moveDouble(JITTargetPlatform::FPGpr0, (FPRegisterID) regTemp->index); } else if (regTemp->type == IR::UInt32Type) { - _as->toUInt32Register(addr, Assembler::ScratchRegister); - _as->storeUInt32((Assembler::RegisterID) regTemp->index, addr); - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->toUInt32Register(addr, JITTargetPlatform::ScratchRegister); + _as->storeUInt32((RegisterID) regTemp->index, addr); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } else { - _as->load32(addr, Assembler::ScratchRegister); - _as->store32((Assembler::RegisterID) regTemp->index, addr); + _as->load32(addr, JITTargetPlatform::ScratchRegister); + _as->store32((RegisterID) regTemp->index, addr); if (regTemp->type != memExpr->type) { addr.offset += 4; quint32 tag; @@ -775,55 +783,59 @@ void InstructionSelection::swapValues(IR::Expr *source, IR::Expr *target) tag = 31337; // bogus value Q_UNREACHABLE(); } - _as->store32(Assembler::TrustedImm32(tag), addr); + _as->store32(TrustedImm32(tag), addr); } - _as->move(Assembler::ScratchRegister, (Assembler::RegisterID) regTemp->index); + _as->move(JITTargetPlatform::ScratchRegister, (RegisterID) regTemp->index); } } #define setOp(op, opName, operation) \ do { \ - op = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + op = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) #define setOpContext(op, opName, operation) \ do { \ - opContext = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ + opContext = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); opName = "Runtime::" isel_stringIfy(operation); \ needsExceptionCheck = QV4::Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void InstructionSelection::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::unop(IR::AluOp oper, IR::Expr *source, IR::Expr *target) { - QV4::JIT::Unop unop(_as, oper); + QV4::JIT::Unop<JITAssembler> unop(_as, oper); unop.generate(source, target); } -void InstructionSelection::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) { - QV4::JIT::Binop binop(_as, oper); + QV4::JIT::Binop<JITAssembler> binop(_as, oper); binop.generate(leftSource, rightSource, target); } -void InstructionSelection::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callQmlContextProperty(IR::Expr *base, IR::Member::MemberKind kind, int propertyIndex, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, base); if (kind == IR::Member::MemberOfQmlScopeObject) - generateRuntimeCall(result, callQmlScopeObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlScopeObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else if (kind == IR::Member::MemberOfQmlContextObject) - generateRuntimeCall(result, callQmlContextObjectProperty, - Assembler::EngineRegister, - Assembler::TrustedImm32(propertyIndex), + generateRuntimeCall(_as, result, callQmlContextObjectProperty, + JITTargetPlatform::EngineRegister, + TrustedImm32(propertyIndex), baseAddressForCallData()); else Q_ASSERT(false); } -void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(base != 0); @@ -832,29 +844,31 @@ void InstructionSelection::callProperty(IR::Expr *base, const QString &name, IR: if (useFastLookups) { uint index = registerGetterLookup(name); - generateRuntimeCall(result, callPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, callPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); } else { - generateRuntimeCall(result, callProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, callProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } } -void InstructionSelection::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::callSubscript(IR::Expr *base, IR::Expr *index, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(base != 0); prepareCallData(args, base); - generateRuntimeCall(result, callElement, Assembler::EngineRegister, - Assembler::PointerToValue(index), + generateRuntimeCall(_as, result, callElement, JITTargetPlatform::EngineRegister, + PointerToValue(index), baseAddressForCallData()); } -void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertType(IR::Expr *source, IR::Expr *target) { switch (target->type) { case IR::DoubleType: @@ -875,7 +889,8 @@ void InstructionSelection::convertType(IR::Expr *source, IR::Expr *target) } } -void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeSlowPath(IR::Expr *source, IR::Expr *target) { Q_ASSERT(target->type != IR::BoolType); @@ -885,7 +900,8 @@ void InstructionSelection::convertTypeSlowPath(IR::Expr *source, IR::Expr *targe copyValue(source, target); } -void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToDouble(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::SInt32Type: @@ -897,51 +913,37 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe convertUIntToDouble(source, target); break; case IR::UndefinedType: - _as->loadDouble(_as->loadAddress(Assembler::ScratchRegister, source), Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, target); + _as->loadDouble(_as->loadAddress(JITTargetPlatform::ScratchRegister, source), JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, target); break; case IR::StringType: case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); convertIntToDouble(source, target); - Assembler::Jump intDone = _as->jump(); + Jump intDone = _as->jump(); // not an int, check if it's NOT a double: isNoInt.link(_as); -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - _as->rshift32(Assembler::TrustedImm32(Value::IsDoubleTag_Shift), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(0)); -#else - _as->and32(Assembler::TrustedImm32(Value::NotDouble_Mask), Assembler::ScratchRegister); - Assembler::Jump isDbl = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::NotDouble_Mask)); -#endif + Jump isDbl = _as->generateIsDoubleCheck(JITTargetPlatform::ScratchRegister); - generateRuntimeCall(target, toDouble, Assembler::PointerToValue(source)); - Assembler::Jump noDoubleDone = _as->jump(); + generateRuntimeCall(_as, target, toDouble, PointerToValue(source)); + Jump noDoubleDone = _as->jump(); // it is a double: isDbl.link(_as); - Assembler::Pointer addr2 = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer addr2 = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); IR::Temp *targetTemp = target->asTemp(); if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { -#if Q_PROCESSOR_WORDSIZE == 8 - _as->load64(addr2, Assembler::ScratchRegister); - _as->store64(Assembler::ScratchRegister, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#else - _as->loadDouble(addr2, Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); -#endif + _as->memcopyValue(target, addr2, JITTargetPlatform::FPGpr0, JITTargetPlatform::ReturnValueRegister); } else { - _as->loadDouble(addr2, (Assembler::FPRegisterID) targetTemp->index); + _as->loadDouble(addr2, (FPRegisterID) targetTemp->index); } noDoubleDone.link(_as); @@ -953,7 +955,8 @@ void InstructionSelection::convertTypeToDouble(IR::Expr *source, IR::Expr *targe } } -void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToBool(IR::Expr *source, IR::Expr *target) { IR::Temp *sourceTemp = source->asTemp(); switch (source->type) { @@ -965,16 +968,16 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) // The source is in a register if the register allocator is used. If the register // allocator was not used, then that means that we can use any register for to // load the double into. - Assembler::FPRegisterID reg; + FPRegisterID reg; if (sourceTemp && sourceTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::FPRegisterID) sourceTemp->index; + reg = (FPRegisterID) sourceTemp->index; else - reg = _as->toDoubleRegister(source, (Assembler::FPRegisterID) 1); - Assembler::Jump nonZero = _as->branchDoubleNonZero(reg, Assembler::FPGpr0); + reg = _as->toDoubleRegister(source, (FPRegisterID) 1); + Jump nonZero = _as->branchDoubleNonZero(reg, JITTargetPlatform::FPGpr0); // it's 0, so false: _as->storeBool(false, target); - Assembler::Jump done = _as->jump(); + Jump done = _as->jump(); // it's non-zero, so true: nonZero.link(_as); @@ -988,283 +991,220 @@ void InstructionSelection::convertTypeToBool(IR::Expr *source, IR::Expr *target) _as->storeBool(false, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); - _as->storeBool(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); case IR::VarType: default: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + Pointer tagAddr = addr; tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); + _as->load32(tagAddr, JITTargetPlatform::ReturnValueRegister); // checkif it's a bool: - Assembler::Jump notBool = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Boolean_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump boolDone = _as->jump(); + Jump notBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Boolean_Type_Internal)); + _as->load32(addr, JITTargetPlatform::ReturnValueRegister); + Jump boolDone = _as->jump(); // check if it's an int32: notBool.link(_as); - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Jump isZero = _as->branch32(Assembler::Equal, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(0)); - _as->move(Assembler::TrustedImm32(1), Assembler::ReturnValueRegister); - Assembler::Jump intDone = _as->jump(); + Jump fallback = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(Value::Integer_Type_Internal)); + _as->load32(addr, JITTargetPlatform::ReturnValueRegister); + Jump isZero = _as->branch32(RelationalCondition::Equal, JITTargetPlatform::ReturnValueRegister, + TrustedImm32(0)); + _as->move(TrustedImm32(1), JITTargetPlatform::ReturnValueRegister); + Jump intDone = _as->jump(); // not an int: fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(source)); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(source)); isZero.link(_as); intDone.link(_as); boolDone.link(_as); - _as->storeBool(Assembler::ReturnValueRegister, target); + _as->storeBool(JITTargetPlatform::ReturnValueRegister, target); break; } } -void InstructionSelection::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToSInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { - -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->load64(addr, Assembler::ScratchRegister); - _as->move(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - - // check if it's integer convertible - _as->urshift64(Assembler::TrustedImm32(QV4::Value::IsIntegerConvertible_Shift), Assembler::ScratchRegister); - Assembler::Jump isIntConvertible = _as->branch32(Assembler::Equal, Assembler::ScratchRegister, Assembler::TrustedImm32(3)); - - // nope, not integer convertible, so check for a double: - _as->urshift64(Assembler::TrustedImm32( - QV4::Value::IsDoubleTag_Shift - QV4::Value::IsIntegerConvertible_Shift), - Assembler::ScratchRegister); - Assembler::Jump fallback = _as->branch32(Assembler::GreaterThan, Assembler::ScratchRegister, Assembler::TrustedImm32(0)); - - // it's a double - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - _as->move64ToDouble(Assembler::ReturnValueRegister, Assembler::FPGpr0); - Assembler::Jump success = - _as->branchTruncateDoubleToInt32(Assembler::FPGpr0, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - - isIntConvertible.link(_as); - success.link(_as); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->storeInt32(Assembler::ReturnValueRegister, target); - } -#else - // load the tag: - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - Assembler::Pointer tagAddr = addr; - tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ReturnValueRegister); - - // check if it's an int32: - Assembler::Jump fallback = _as->branch32(Assembler::NotEqual, Assembler::ReturnValueRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - IR::Temp *targetTemp = target->asTemp(); - if (!targetTemp || targetTemp->kind == IR::Temp::StackSlot) { - _as->load32(addr, Assembler::ReturnValueRegister); - Assembler::Pointer targetAddr = _as->loadAddress(Assembler::ScratchRegister, target); - _as->store32(Assembler::ReturnValueRegister, targetAddr); - targetAddr.offset += 4; - _as->store32(Assembler::TrustedImm32(Value::Integer_Type_Internal), targetAddr); - } else { - _as->load32(addr, (Assembler::RegisterID) targetTemp->index); - } - Assembler::Jump intDone = _as->jump(); - - // not an int: - fallback.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); - - intDone.link(_as); -#endif - + JITAssembler::RegisterSizeDependentOps::convertVarToSInt32(_as, source, target); } break; case IR::DoubleType: { - Assembler::Jump success = + Jump success = _as->branchTruncateDoubleToInt32(_as->toDoubleRegister(source), - Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToInt, - Assembler::PointerToValue(source)); + JITTargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToInt, + PointerToValue(source)); success.link(_as); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::UInt32Type: - _as->storeInt32(_as->toUInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toUInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::BoolType: - _as->storeInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; case IR::StringType: default: - generateRuntimeCall(Assembler::ReturnValueRegister, toInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); break; } // switch (source->type) } -void InstructionSelection::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::convertTypeToUInt32(IR::Expr *source, IR::Expr *target) { switch (source->type) { case IR::VarType: { // load the tag: - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, source); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); tagAddr.offset += 4; - _as->load32(tagAddr, Assembler::ScratchRegister); + _as->load32(tagAddr, JITTargetPlatform::ScratchRegister); // check if it's an int32: - Assembler::Jump isNoInt = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(Value::Integer_Type_Internal)); - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, source); - _as->storeUInt32(_as->toInt32Register(addr, Assembler::ScratchRegister), target); - Assembler::Jump intDone = _as->jump(); + Jump isNoInt = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(Value::Integer_Type_Internal)); + Pointer addr = _as->loadAddress(JITTargetPlatform::ScratchRegister, source); + _as->storeUInt32(_as->toInt32Register(addr, JITTargetPlatform::ScratchRegister), target); + Jump intDone = _as->jump(); // not an int: isNoInt.link(_as); - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - _as->loadAddress(Assembler::ScratchRegister, source)); - _as->storeInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + _as->loadAddress(JITTargetPlatform::ScratchRegister, source)); + _as->storeInt32(JITTargetPlatform::ReturnValueRegister, target); intDone.link(_as); } break; case IR::DoubleType: { - Assembler::FPRegisterID reg = _as->toDoubleRegister(source); - Assembler::Jump success = - _as->branchTruncateDoubleToUint32(reg, Assembler::ReturnValueRegister, - Assembler::BranchIfTruncateSuccessful); - generateRuntimeCall(Assembler::ReturnValueRegister, doubleToUInt, - Assembler::PointerToValue(source)); + FPRegisterID reg = _as->toDoubleRegister(source); + Jump success = + _as->branchTruncateDoubleToUint32(reg, JITTargetPlatform::ReturnValueRegister, + BranchTruncateType::BranchIfTruncateSuccessful); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, doubleToUInt, + PointerToValue(source)); success.link(_as); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); } break; case IR::NullType: case IR::UndefinedType: - _as->move(Assembler::TrustedImm32(0), Assembler::ReturnValueRegister); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + _as->move(TrustedImm32(0), JITTargetPlatform::ReturnValueRegister); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::StringType: - generateRuntimeCall(Assembler::ReturnValueRegister, toUInt, - Assembler::PointerToValue(source)); - _as->storeUInt32(Assembler::ReturnValueRegister, target); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toUInt, + PointerToValue(source)); + _as->storeUInt32(JITTargetPlatform::ReturnValueRegister, target); break; case IR::SInt32Type: case IR::BoolType: - _as->storeUInt32(_as->toInt32Register(source, Assembler::ReturnValueRegister), target); + _as->storeUInt32(_as->toInt32Register(source, JITTargetPlatform::ReturnValueRegister), target); break; default: break; } // switch (source->type) } -void InstructionSelection::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructActivationProperty(IR::Name *func, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(func != 0); prepareCallData(args, 0); if (useFastLookups && func->global) { uint index = registerGlobalGetterLookup(*func->id); - generateRuntimeCall(result, constructGlobalLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), baseAddressForCallData()); + generateRuntimeCall(_as, result, constructGlobalLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructActivationProperty, - Assembler::EngineRegister, - Assembler::StringToIndex(*func->id), + generateRuntimeCall(_as, result, constructActivationProperty, + JITTargetPlatform::EngineRegister, + StringToIndex(*func->id), baseAddressForCallData()); } -void InstructionSelection::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructProperty(IR::Expr *base, const QString &name, IR::ExprList *args, IR::Expr *result) { prepareCallData(args, base); if (useFastLookups) { uint index = registerGetterLookup(name); - generateRuntimeCall(result, constructPropertyLookup, - Assembler::EngineRegister, - Assembler::TrustedImm32(index), + generateRuntimeCall(_as, result, constructPropertyLookup, + JITTargetPlatform::EngineRegister, + TrustedImm32(index), baseAddressForCallData()); return; } - generateRuntimeCall(result, constructProperty, Assembler::EngineRegister, - Assembler::StringToIndex(name), + generateRuntimeCall(_as, result, constructProperty, JITTargetPlatform::EngineRegister, + StringToIndex(name), baseAddressForCallData()); } -void InstructionSelection::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::constructValue(IR::Expr *value, IR::ExprList *args, IR::Expr *result) { Q_ASSERT(value != 0); prepareCallData(args, 0); - generateRuntimeCall(result, constructValue, - Assembler::EngineRegister, - Assembler::Reference(value), + generateRuntimeCall(_as, result, constructValue, + JITTargetPlatform::EngineRegister, + Reference(value), baseAddressForCallData()); } -void InstructionSelection::visitJump(IR::Jump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitJump(IR::Jump *s) { if (!_removableJumps.at(_block->index())) _as->jumpToBlock(_block, s->target); } -void InstructionSelection::visitCJump(IR::CJump *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJump(IR::CJump *s) { IR::Temp *t = s->cond->asTemp(); if (t || s->cond->asArgLocal()) { - Assembler::RegisterID reg; + RegisterID reg; if (t && t->kind == IR::Temp::PhysicalRegister) { Q_ASSERT(t->type == IR::BoolType); - reg = (Assembler::RegisterID) t->index; + reg = (RegisterID) t->index; } else if (t && t->kind == IR::Temp::StackSlot && t->type == IR::BoolType) { - reg = Assembler::ReturnValueRegister; + reg = JITTargetPlatform::ReturnValueRegister; _as->toInt32Register(t, reg); } else { - Address temp = _as->loadAddress(Assembler::ScratchRegister, s->cond); + Address temp = _as->loadAddress(JITTargetPlatform::ScratchRegister, s->cond); Address tag = temp; tag.offset += QV4::Value::tagOffset(); - Assembler::Jump booleanConversion = _as->branch32(Assembler::NotEqual, tag, Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + Jump booleanConversion = _as->branch32(RelationalCondition::NotEqual, tag, TrustedImm32(QV4::Value::Boolean_Type_Internal)); Address data = temp; data.offset += QV4::Value::valueOffset(); - _as->load32(data, Assembler::ReturnValueRegister); - Assembler::Jump testBoolean = _as->jump(); + _as->load32(data, JITTargetPlatform::ReturnValueRegister); + Jump testBoolean = _as->jump(); booleanConversion.link(_as); - reg = Assembler::ReturnValueRegister; - generateRuntimeCall(reg, toBoolean, Assembler::Reference(s->cond)); + reg = JITTargetPlatform::ReturnValueRegister; + generateRuntimeCall(_as, reg, toBoolean, Reference(s->cond)); testBoolean.link(_as); } @@ -1274,9 +1214,9 @@ void InstructionSelection::visitCJump(IR::CJump *s) } else if (IR::Const *c = s->cond->asConst()) { // TODO: SSA optimization for constant condition evaluation should remove this. // See also visitCJump() in RegAllocInfo. - generateRuntimeCall(Assembler::ReturnValueRegister, toBoolean, - Assembler::PointerToValue(c)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, toBoolean, + PointerToValue(c)); + _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; } else if (IR::Binop *b = s->cond->asBinop()) { if (b->left->type == IR::DoubleType && b->right->type == IR::DoubleType @@ -1296,8 +1236,8 @@ void InstructionSelection::visitCJump(IR::CJump *s) return; } - RuntimeCall op; - RuntimeCall opContext; + typename JITAssembler::RuntimeCall op; + typename JITAssembler::RuntimeCall opContext; const char *opName = 0; bool needsExceptionCheck; switch (b->op) { @@ -1321,165 +1261,30 @@ void InstructionSelection::visitCJump(IR::CJump *s) // elimination (which isn't there either) would remove the whole else block. if (opContext.isValid()) _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, opContext, - Assembler::EngineRegister, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, opContext, + JITTargetPlatform::EngineRegister, + PointerToValue(b->left), + PointerToValue(b->right)); else _as->generateFunctionCallImp(needsExceptionCheck, - Assembler::ReturnValueRegister, opName, op, - Assembler::PointerToValue(b->left), - Assembler::PointerToValue(b->right)); + JITTargetPlatform::ReturnValueRegister, opName, op, + PointerToValue(b->left), + PointerToValue(b->right)); - _as->generateCJumpOnNonZero(Assembler::ReturnValueRegister, _block, s->iftrue, s->iffalse); + _as->generateCJumpOnNonZero(JITTargetPlatform::ReturnValueRegister, _block, s->iftrue, s->iffalse); return; } Q_UNREACHABLE(); } -void InstructionSelection::visitRet(IR::Ret *s) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitRet(IR::Ret *s) { - if (!s) { - // this only happens if the method doesn't have a return statement and can - // only exit through an exception - } else if (IR::Temp *t = s->expr->asTemp()) { -#if CPU(X86) || CPU(ARM) || CPU(MIPS) - -# if CPU(X86) - Assembler::RegisterID lowReg = JSC::X86Registers::eax; - Assembler::RegisterID highReg = JSC::X86Registers::edx; -# elif CPU(MIPS) - Assembler::RegisterID lowReg = JSC::MIPSRegisters::v0; - Assembler::RegisterID highReg = JSC::MIPSRegisters::v1; -# else // CPU(ARM) - Assembler::RegisterID lowReg = JSC::ARMRegisters::r0; - Assembler::RegisterID highReg = JSC::ARMRegisters::r1; -# endif - - if (t->kind == IR::Temp::PhysicalRegister) { - switch (t->type) { - case IR::DoubleType: - _as->moveDoubleToInts((Assembler::FPRegisterID) t->index, lowReg, highReg); - break; - case IR::UInt32Type: { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleToInts(Assembler::FPGpr0, lowReg, highReg); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->move(srcReg, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - done.link(_as); - } break; - case IR::SInt32Type: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), highReg); - break; - case IR::BoolType: - _as->move((Assembler::RegisterID) t->index, lowReg); - _as->move(Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal), highReg); - break; - default: - Q_UNREACHABLE(); - } - } else { - Pointer addr = _as->loadAddress(Assembler::ScratchRegister, t); - _as->load32(addr, lowReg); - addr.offset += 4; - _as->load32(addr, highReg); - } -#else - if (t->kind == IR::Temp::PhysicalRegister) { - if (t->type == IR::DoubleType) { - _as->moveDoubleTo64((Assembler::FPRegisterID) t->index, - Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), - Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - } else if (t->type == IR::UInt32Type) { - Assembler::RegisterID srcReg = (Assembler::RegisterID) t->index; - Assembler::Jump intRange = _as->branch32(Assembler::GreaterThanOrEqual, srcReg, Assembler::TrustedImm32(0)); - _as->convertUInt32ToDouble(srcReg, Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->moveDoubleTo64(Assembler::FPGpr0, Assembler::ReturnValueRegister); - _as->move(Assembler::TrustedImm64(QV4::Value::NaNEncodeMask), Assembler::ScratchRegister); - _as->xor64(Assembler::ScratchRegister, Assembler::ReturnValueRegister); - Assembler::Jump done = _as->jump(); - intRange.link(_as); - _as->zeroExtend32ToPtr(srcReg, Assembler::ReturnValueRegister); - quint64 tag = QV4::Value::Integer_Type_Internal; - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - done.link(_as); - } else { - _as->zeroExtend32ToPtr((Assembler::RegisterID) t->index, Assembler::ReturnValueRegister); - quint64 tag; - switch (t->type) { - case IR::SInt32Type: - tag = QV4::Value::Integer_Type_Internal; - break; - case IR::BoolType: - tag = QV4::Value::Boolean_Type_Internal; - break; - default: - tag = 31337; // bogus value - Q_UNREACHABLE(); - } - _as->or64(Assembler::TrustedImm64(tag << 32), - Assembler::ReturnValueRegister); - } - } else { - _as->copyValue(Assembler::ReturnValueRegister, t); - } -#endif - } else if (IR::Const *c = s->expr->asConst()) { - QV4::Primitive retVal = convertToValue(c); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - } else { - Q_UNREACHABLE(); - Q_UNUSED(s); - } - - Assembler::Label leaveStackFrame = _as->label(); - - const int locals = _as->stackLayout().calculateJSStackFrameSize(); - _as->subPtr(Assembler::TrustedImm32(sizeof(QV4::Value)*locals), Assembler::LocalsRegister); - _as->loadPtr(Address(Assembler::EngineRegister, qOffsetOf(QV4::ExecutionEngine, current)), Assembler::ScratchRegister); - _as->loadPtr(Address(Assembler::ScratchRegister, qOffsetOf(ExecutionContext::Data, engine)), Assembler::ScratchRegister); - _as->storePtr(Assembler::LocalsRegister, Address(Assembler::ScratchRegister, qOffsetOf(ExecutionEngine, jsStackTop))); - - _as->leaveStandardStackFrame(regularRegistersToSave, fpRegistersToSave); - _as->ret(); - - _as->exceptionReturnLabel = _as->label(); - QV4::Primitive retVal = Primitive::undefinedValue(); -#if CPU(X86) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::X86Registers::eax); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::X86Registers::edx); -#elif CPU(ARM) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::ARMRegisters::r0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::ARMRegisters::r1); -#elif CPU(MIPS) - _as->move(Assembler::TrustedImm32(retVal.int_32()), JSC::MIPSRegisters::v0); - _as->move(Assembler::TrustedImm32(retVal.tag()), JSC::MIPSRegisters::v1); -#else - _as->move(Assembler::TrustedImm64(retVal.rawValue()), Assembler::ReturnValueRegister); -#endif - _as->jump(leaveStackFrame); + _as->returnFromFunction(s, regularRegistersToSave, fpRegistersToSave); } -int InstructionSelection::prepareVariableArguments(IR::ExprList* args) +template <typename JITAssembler> +int InstructionSelection<JITAssembler>::prepareVariableArguments(IR::ExprList* args) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -1492,7 +1297,7 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } @@ -1500,7 +1305,8 @@ int InstructionSelection::prepareVariableArguments(IR::ExprList* args) return argc; } -int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) +template <typename JITAssembler> +int InstructionSelection<JITAssembler>::prepareCallData(IR::ExprList* args, IR::Expr *thisObject) { int argc = 0; for (IR::ExprList *it = args; it; it = it->next) { @@ -1508,9 +1314,9 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje } Pointer p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, tag)); - _as->store32(Assembler::TrustedImm32(QV4::Value::Integer_Type_Internal), p); + _as->store32(TrustedImm32(QV4::Value::Integer_Type_Internal), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, argc)); - _as->store32(Assembler::TrustedImm32(argc), p); + _as->store32(TrustedImm32(argc), p); p = _as->stackLayout().callDataAddress(qOffsetOf(CallData, thisObject)); if (!thisObject) _as->storeValue(QV4::Primitive::undefinedValue(), p); @@ -1523,25 +1329,24 @@ int InstructionSelection::prepareCallData(IR::ExprList* args, IR::Expr *thisObje Q_ASSERT(arg != 0); Pointer dst(_as->stackLayout().argumentAddressForCall(i)); if (arg->asTemp() && arg->asTemp()->kind != IR::Temp::PhysicalRegister) - _as->memcopyValue(dst, arg->asTemp(), Assembler::ScratchRegister); + _as->memcopyValue(dst, arg->asTemp(), JITTargetPlatform::ScratchRegister); else _as->copyValue(dst, arg); } return argc; } -void InstructionSelection::calculateRegistersToSave(const RegisterInformation &used) +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::calculateRegistersToSave(const RegisterInformation &used) { regularRegistersToSave.clear(); fpRegistersToSave.clear(); - for (const RegisterInfo &ri : Assembler::getRegisterInfo()) { -#if defined(RESTORE_EBX_ON_CALL) - if (ri.isRegularRegister() && ri.reg<JSC::X86Registers::RegisterID>() == JSC::X86Registers::ebx) { + for (const RegisterInfo &ri : JITTargetPlatform::getRegisterInfo()) { + if (JITTargetPlatform::gotRegister != -1 && ri.isRegularRegister() && ri.reg<RegisterID>() == JITTargetPlatform::gotRegister) { regularRegistersToSave.append(ri); continue; } -#endif // RESTORE_EBX_ON_CALL if (ri.isCallerSaved()) continue; if (ri.isRegularRegister()) { @@ -1564,35 +1369,38 @@ bool operator==(const Primitive &v1, const Primitive &v2) } // QV4 namespace QT_END_NAMESPACE -bool InstructionSelection::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpDouble(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (_as->nextBlock() == iftrue) { - Assembler::Jump target = _as->branchDouble(true, op, left, right); + Jump target = _as->branchDouble(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchDouble(false, op, left, right); + Jump target = _as->branchDouble(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -bool InstructionSelection::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpSInt32(IR::AluOp op, IR::Expr *left, IR::Expr *right, IR::BasicBlock *iftrue, IR::BasicBlock *iffalse) { if (_as->nextBlock() == iftrue) { - Assembler::Jump target = _as->branchInt32(true, op, left, right); + Jump target = _as->branchInt32(true, op, left, right); _as->addPatch(iffalse, target); } else { - Assembler::Jump target = _as->branchInt32(false, op, left, right); + Jump target = _as->branchInt32(false, op, left, right); _as->addPatch(iftrue, target); _as->jumpToBlock(_block, iffalse); } return true; } -void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { Q_ASSERT(binop->op == IR::OpStrictEqual || binop->op == IR::OpStrictNotEqual); @@ -1607,15 +1415,16 @@ void InstructionSelection::visitCJumpStrict(IR::Binop *binop, IR::BasicBlock *tr IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareStrictEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareStrictEqual, + PointerToValue(left), PointerToValue(right)); + _as->generateCJumpOnCompare(binop->op == IR::OpStrictEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, + JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), _block, trueBlock, falseBlock); } // Only load the non-null temp. -bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictNull(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1640,19 +1449,20 @@ bool InstructionSelection::visitCJumpStrictNull(IR::Binop *binop, return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ScratchRegister; + const RegisterID tagReg = JITTargetPlatform::ScratchRegister; _as->load32(tagAddr, tagReg); - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::TrustedImm32 tag(QV4::Value::Null_Type_Internal); + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; + const TrustedImm32 tag(QV4::Value::Null_Type_Internal); _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictUndefined(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1677,28 +1487,15 @@ bool InstructionSelection::visitCJumpStrictUndefined(IR::Binop *binop, return true; } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; -#ifdef QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer addr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load64(addr, tagReg); - const Assembler::TrustedImm64 tag(0); -#else // !QV4_USE_64_BIT_VALUE_ENCODING - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); - _as->load32(tagAddr, tagReg); - Assembler::Jump j = _as->branch32(Assembler::invert(cond), tagReg, Assembler::TrustedImm32(0)); - _as->addPatch(falseBlock, j); - - tagAddr.offset += 4; - _as->load32(tagAddr, tagReg); - const Assembler::TrustedImm32 tag(QV4::Value::Managed_Type_Internal); -#endif - _as->generateCJumpOnCompare(cond, tagReg, tag, _block, trueBlock, falseBlock); + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; + const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; + _as->generateCJumpOnUndefined(cond, varSrc, JITTargetPlatform::ScratchRegister, tagReg, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { IR::Expr *boolSrc = 0, *otherSrc = 0; @@ -1712,13 +1509,20 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // neither operands are statically typed as bool, so bail out. return false; } + if (otherSrc->type == IR::UnknownType) { + // Ok, we really need to call into the runtime. + // (This case doesn't happen when the optimizer ran, because everything will be typed (yes, + // possibly as "var" meaning anything), but it does happen for $0===true, which is generated + // for things where the optimizer didn't run (like functions with a try block).) + return false; + } - Assembler::RelationalCondition cond = binop->op == IR::OpStrictEqual ? Assembler::Equal - : Assembler::NotEqual; + RelationalCondition cond = binop->op == IR::OpStrictEqual ? RelationalCondition::Equal + : RelationalCondition::NotEqual; if (otherSrc->type == IR::BoolType) { // both are boolean - Assembler::RegisterID one = _as->toBoolRegister(boolSrc, Assembler::ReturnValueRegister); - Assembler::RegisterID two = _as->toBoolRegister(otherSrc, Assembler::ScratchRegister); + RegisterID one = _as->toBoolRegister(boolSrc, JITTargetPlatform::ReturnValueRegister); + RegisterID two = _as->toBoolRegister(otherSrc, JITTargetPlatform::ScratchRegister); _as->generateCJumpOnCompare(cond, one, two, _block, trueBlock, falseBlock); return true; } @@ -1728,13 +1532,13 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock return true; } - Assembler::Pointer otherAddr = _as->loadAddress(Assembler::ReturnValueRegister, otherSrc); + Pointer otherAddr = _as->loadAddress(JITTargetPlatform::ReturnValueRegister, otherSrc); otherAddr.offset += 4; // tag address // check if the tag of the var operand is indicates 'boolean' - _as->load32(otherAddr, Assembler::ScratchRegister); - Assembler::Jump noBool = _as->branch32(Assembler::NotEqual, Assembler::ScratchRegister, - Assembler::TrustedImm32(QV4::Value::Boolean_Type_Internal)); + _as->load32(otherAddr, JITTargetPlatform::ScratchRegister); + Jump noBool = _as->branch32(RelationalCondition::NotEqual, JITTargetPlatform::ScratchRegister, + TrustedImm32(QV4::Value::Boolean_Type_Internal)); if (binop->op == IR::OpStrictEqual) _as->addPatch(falseBlock, noBool); else @@ -1742,14 +1546,15 @@ bool InstructionSelection::visitCJumpStrictBool(IR::Binop *binop, IR::BasicBlock // ok, both are boolean, so let's load them and compare them. otherAddr.offset -= 4; // int_32 address - _as->load32(otherAddr, Assembler::ReturnValueRegister); - Assembler::RegisterID boolReg = _as->toBoolRegister(boolSrc, Assembler::ScratchRegister); - _as->generateCJumpOnCompare(cond, boolReg, Assembler::ReturnValueRegister, _block, trueBlock, + _as->load32(otherAddr, JITTargetPlatform::ReturnValueRegister); + RegisterID boolReg = _as->toBoolRegister(boolSrc, JITTargetPlatform::ScratchRegister); + _as->generateCJumpOnCompare(cond, boolReg, JITTargetPlatform::ReturnValueRegister, _block, trueBlock, falseBlock); return true; } -bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, +template <typename JITAssembler> +bool InstructionSelection<JITAssembler>::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { @@ -1776,18 +1581,18 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin return true; } - Assembler::Pointer tagAddr = _as->loadAddress(Assembler::ScratchRegister, varSrc); + Pointer tagAddr = _as->loadAddress(JITTargetPlatform::ScratchRegister, varSrc); tagAddr.offset += 4; - const Assembler::RegisterID tagReg = Assembler::ReturnValueRegister; + const RegisterID tagReg = JITTargetPlatform::ReturnValueRegister; _as->load32(tagAddr, tagReg); if (binop->op == IR::OpNotEqual) qSwap(trueBlock, falseBlock); - Assembler::Jump isNull = _as->branch32(Assembler::Equal, tagReg, Assembler::TrustedImm32(int(QV4::Value::Null_Type_Internal))); - Assembler::Jump isNotUndefinedTag = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(int(QV4::Value::Managed_Type_Internal))); + Jump isNull = _as->branch32(RelationalCondition::Equal, tagReg, TrustedImm32(int(QV4::Value::Null_Type_Internal))); + Jump isNotUndefinedTag = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(int(QV4::Value::Managed_Type_Internal))); tagAddr.offset -= 4; _as->load32(tagAddr, tagReg); - Assembler::Jump isNotUndefinedValue = _as->branch32(Assembler::NotEqual, tagReg, Assembler::TrustedImm32(0)); + Jump isNotUndefinedValue = _as->branch32(RelationalCondition::NotEqual, tagReg, TrustedImm32(0)); _as->addPatch(trueBlock, isNull); _as->addPatch(falseBlock, isNotUndefinedTag); _as->addPatch(falseBlock, isNotUndefinedValue); @@ -1797,7 +1602,8 @@ bool InstructionSelection::visitCJumpNullUndefined(IR::Type nullOrUndef, IR::Bin } -void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, +template <typename JITAssembler> +void InstructionSelection<JITAssembler>::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *trueBlock, IR::BasicBlock *falseBlock) { Q_ASSERT(binop->op == IR::OpEqual || binop->op == IR::OpNotEqual); @@ -1808,18 +1614,55 @@ void InstructionSelection::visitCJumpEqual(IR::Binop *binop, IR::BasicBlock *tru IR::Expr *left = binop->left; IR::Expr *right = binop->right; - generateRuntimeCall(Assembler::ReturnValueRegister, compareEqual, - Assembler::PointerToValue(left), Assembler::PointerToValue(right)); - _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? Assembler::NotEqual : Assembler::Equal, - Assembler::ReturnValueRegister, Assembler::TrustedImm32(0), + generateRuntimeCall(_as, JITTargetPlatform::ReturnValueRegister, compareEqual, + PointerToValue(left), PointerToValue(right)); + _as->generateCJumpOnCompare(binop->op == IR::OpEqual ? RelationalCondition::NotEqual : RelationalCondition::Equal, + JITTargetPlatform::ReturnValueRegister, TrustedImm32(0), _block, trueBlock, falseBlock); } -QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory::createUnitForLoading() +template <typename JITAssembler> +QQmlRefPointer<CompiledData::CompilationUnit> ISelFactory<JITAssembler>::createUnitForLoading() { QQmlRefPointer<CompiledData::CompilationUnit> result; result.adopt(new JIT::CompilationUnit); return result; } +QT_BEGIN_NAMESPACE +namespace QV4 { namespace JIT { +template class Q_QML_EXPORT InstructionSelection<>; +template class Q_QML_EXPORT ISelFactory<>; +#if defined(V4_BOOTSTRAP) + +Q_QML_EXPORT QV4::EvalISelFactory *createISelForArchitecture(const QString &architecture) +{ + using ARMv7CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>; + using ARM64CrossAssembler = QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>; + + if (architecture == QLatin1String("armv7")) + return new ISelFactory<ARMv7CrossAssembler>; + else if (architecture == QLatin1String("armv8")) + return new ISelFactory<ARM64CrossAssembler>; + + QString hostArch; +#if CPU(ARM_THUMB2) + hostArch = QStringLiteral("armv7"); +#elif CPU(MIPS) + hostArch = QStringLiteral("mips"); +#elif CPU(X86) + hostArch = QStringLiteral("x86"); +#elif CPU(X86_64) + hostArch = QStringLiteral("x86_64"); +#endif + if (!hostArch.isEmpty() && architecture == hostArch) + return new ISelFactory<>; + + return nullptr; +} + +#endif +} } +QT_END_NAMESPACE + #endif // ENABLE(ASSEMBLER) diff --git a/src/qml/jit/qv4isel_masm_p.h b/src/qml/jit/qv4isel_masm_p.h index 012745c5f2..5c046cb397 100644 --- a/src/qml/jit/qv4isel_masm_p.h +++ b/src/qml/jit/qv4isel_masm_p.h @@ -72,6 +72,7 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> class Q_QML_EXPORT InstructionSelection: protected IR::IRDecoder, public EvalInstructionSelection @@ -136,18 +137,31 @@ protected: void unop(IR::AluOp oper, IR::Expr *sourceTemp, IR::Expr *target) override; void binop(IR::AluOp oper, IR::Expr *leftSource, IR::Expr *rightSource, IR::Expr *target) override; - typedef Assembler::Address Address; - typedef Assembler::Pointer Pointer; + using Address = typename JITAssembler::Address; + using Pointer = typename JITAssembler::Pointer; + using PointerToValue = typename JITAssembler::PointerToValue; + using RegisterID = typename JITAssembler::RegisterID; + using FPRegisterID = typename JITAssembler::FPRegisterID; + using ResultCondition = typename JITAssembler::ResultCondition; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + using TrustedImm64 = typename JITAssembler::TrustedImm64; + using Label = typename JITAssembler::Label; + using Jump = typename JITAssembler::Jump; + using StringToIndex = typename JITAssembler::StringToIndex; + using Reference = typename JITAssembler::Reference; + using RelationalCondition = typename JITAssembler::RelationalCondition; + using BranchTruncateType = typename JITAssembler::BranchTruncateType; + using RuntimeCall = typename JITAssembler::RuntimeCall; + + using JITTargetPlatform = typename JITAssembler::JITTargetPlatform; -#if !defined(ARGUMENTS_IN_REGISTERS) Address addressForArgument(int index) const { // 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(Assembler::FramePointerRegister, (index + 2) * sizeof(void*)); + return Address(JITTargetPlatform::FramePointerRegister, (index + 2) * sizeof(void*)); } -#endif Pointer baseAddressForCallArguments() { @@ -192,61 +206,55 @@ private: if (targetTemp->kind == IR::Temp::PhysicalRegister) { if (IR::Temp *sourceTemp = source->asTemp()) { if (sourceTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertInt32ToDouble((Assembler::RegisterID) sourceTemp->index, - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble((RegisterID) sourceTemp->index, + (FPRegisterID) targetTemp->index); } else { - _as->convertInt32ToDouble(_as->loadAddress(Assembler::ReturnValueRegister, sourceTemp), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->loadAddress(JITTargetPlatform::ReturnValueRegister, sourceTemp), + (FPRegisterID) targetTemp->index); } } else { - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - (Assembler::FPRegisterID) targetTemp->index); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + (FPRegisterID) targetTemp->index); } return; } } - _as->convertInt32ToDouble(_as->toInt32Register(source, Assembler::ScratchRegister), - Assembler::FPGpr0); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(Assembler::ReturnValueRegister, target)); + _as->convertInt32ToDouble(_as->toInt32Register(source, JITTargetPlatform::ScratchRegister), + JITTargetPlatform::FPGpr0); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(JITTargetPlatform::ReturnValueRegister, target)); } void convertUIntToDouble(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID tmpReg = Assembler::ScratchRegister; - Assembler::RegisterID reg = _as->toInt32Register(source, tmpReg); + RegisterID tmpReg = JITTargetPlatform::ScratchRegister; + RegisterID reg = _as->toInt32Register(source, tmpReg); if (IR::Temp *targetTemp = target->asTemp()) { if (targetTemp->kind == IR::Temp::PhysicalRegister) { - _as->convertUInt32ToDouble(reg, (Assembler::FPRegisterID) targetTemp->index, tmpReg); + _as->convertUInt32ToDouble(reg, (FPRegisterID) targetTemp->index, tmpReg); return; } } _as->convertUInt32ToDouble(_as->toUInt32Register(source, tmpReg), - Assembler::FPGpr0, tmpReg); - _as->storeDouble(Assembler::FPGpr0, _as->loadAddress(tmpReg, target)); + JITTargetPlatform::FPGpr0, tmpReg); + _as->storeDouble(JITTargetPlatform::FPGpr0, _as->loadAddress(tmpReg, target)); } void convertIntToBool(IR::Expr *source, IR::Expr *target) { - Assembler::RegisterID reg = Assembler::ScratchRegister; + RegisterID reg = JITTargetPlatform::ScratchRegister; if (IR::Temp *targetTemp = target->asTemp()) if (targetTemp->kind == IR::Temp::PhysicalRegister) - reg = (Assembler::RegisterID) targetTemp->index; + reg = (RegisterID) targetTemp->index; _as->move(_as->toInt32Register(source, reg), reg); - _as->compare32(Assembler::NotEqual, reg, Assembler::TrustedImm32(0), reg); + _as->compare32(RelationalCondition::NotEqual, reg, TrustedImm32(0), reg); _as->storeBool(reg, target); } - #define isel_stringIfyx(s) #s - #define isel_stringIfy(s) isel_stringIfyx(s) - - #define generateRuntimeCall(t, function, ...) \ - _as->generateFunctionCallImp(Runtime::Method_##function##_NeedsExceptionCheck, t, "Runtime::" isel_stringIfy(function), RuntimeCall(qOffsetOf(QV4::Runtime, function)), __VA_ARGS__) - int prepareVariableArguments(IR::ExprList* args); int prepareCallData(IR::ExprList* args, IR::Expr *thisObject); @@ -259,22 +267,22 @@ private: // goes into the same register as the return value (currently only ARM), the prepareCall // will combine loading the looupAddr into the register and calculating the indirect call // address. - Assembler::Pointer lookupAddr(Assembler::ReturnValueRegister, index * sizeof(QV4::Lookup)); + Pointer lookupAddr(JITTargetPlatform::ReturnValueRegister, index * sizeof(QV4::Lookup)); _as->generateFunctionCallImp(true, retval, "lookup getter/setter", - LookupCall(lookupAddr, getterSetterOffset), lookupAddr, + typename JITAssembler::LookupCall(lookupAddr, getterSetterOffset), lookupAddr, arg1, arg2, arg3); } template <typename Retval, typename Arg1, typename Arg2> void generateLookupCall(Retval retval, uint index, uint getterSetterOffset, Arg1 arg1, Arg2 arg2) { - generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, Assembler::VoidType()); + generateLookupCall(retval, index, getterSetterOffset, arg1, arg2, typename JITAssembler::VoidType()); } IR::BasicBlock *_block; BitVector _removableJumps; - Assembler* _as; + JITAssembler* _as; QScopedPointer<CompilationUnit> compilationUnit; QQmlEnginePrivate *qmlEngine; @@ -282,13 +290,14 @@ private: RegisterInformation fpRegistersToSave; }; +template <typename JITAssembler = Assembler<DefaultAssemblerTargetConfiguration>> class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: ISelFactory() : EvalISelFactory(QStringLiteral("jit")) {} virtual ~ISelFactory() {} EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) Q_DECL_OVERRIDE Q_DECL_FINAL - { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator, this); } + { return new InstructionSelection<JITAssembler>(qmlEngine, execAllocator, module, jsGenerator, this); } bool jitCompileRegexps() const Q_DECL_OVERRIDE Q_DECL_FINAL { return true; } QQmlRefPointer<CompiledData::CompilationUnit> createUnitForLoading() Q_DECL_OVERRIDE Q_DECL_FINAL; diff --git a/src/qml/jit/qv4regalloc.cpp b/src/qml/jit/qv4regalloc.cpp index 3fb0815299..e5abaa7458 100644 --- a/src/qml/jit/qv4regalloc.cpp +++ b/src/qml/jit/qv4regalloc.cpp @@ -973,7 +973,15 @@ private: break; Q_ASSERT(!i->isFixedInterval()); - _liveIntervals.push_back(i); + auto it = _liveIntervals.begin(); + for (; it != _liveIntervals.end(); ++it) { + if ((*it)->temp() == i->temp()) { + *it = i; + break; + } + } + if (it == _liveIntervals.end()) + _liveIntervals.push_back(i); // qDebug() << "-- Activating interval for temp" << i->temp().index; _unprocessedReverseOrder.removeLast(); @@ -1521,7 +1529,7 @@ static inline int indexOfRangeCoveringPosition(const LifeTimeInterval::Ranges &r return -1; } -static inline int intersectionPosition(const LifeTimeInterval::Range &one, const LifeTimeInterval::Range &two) +static inline int intersectionPosition(const LifeTimeIntervalRange &one, const LifeTimeIntervalRange &two) { if (one.covers(two.start)) return two.start; @@ -1567,7 +1575,7 @@ static void longestAvailableReg(int *nextUses, int nextUseCount, int ®, int & #define CALLOC_ON_STACK(ty, ptr, sz, val) \ Q_ASSERT(sz > 0); \ - ty *ptr = reinterpret_cast<ty *>(alloca(sizeof(ty) * (sz))); \ + Q_ALLOCA_VAR(ty, ptr, sizeof(ty) * (sz)); \ for (ty *it = ptr, *eit = ptr + (sz); it != eit; ++it) \ *it = val; @@ -1779,9 +1787,9 @@ int RegisterAllocator::nextIntersection(const LifeTimeInterval ¤t, return -1; for (int currentEnd = currentRanges.size(); currentIt < currentEnd; ++currentIt) { - const LifeTimeInterval::Range currentRange = currentRanges.at(currentIt); + const LifeTimeIntervalRange currentRange = currentRanges.at(currentIt); for (int anotherIt = anotherItStart, anotherEnd = anotherRanges.size(); anotherIt < anotherEnd; ++anotherIt) { - const LifeTimeInterval::Range anotherRange = anotherRanges.at(anotherIt); + const LifeTimeIntervalRange anotherRange = anotherRanges.at(anotherIt); if (anotherRange.start > currentRange.end) break; int intersectPos = intersectionPosition(currentRange, anotherRange); diff --git a/src/qml/jit/qv4targetplatform_p.h b/src/qml/jit/qv4targetplatform_p.h index 7e265258d5..fcc600eb2e 100644 --- a/src/qml/jit/qv4targetplatform_p.h +++ b/src/qml/jit/qv4targetplatform_p.h @@ -63,6 +63,11 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { +enum TargetOperatingSystemSpecialization { + NoOperatingSystemSpecialization, + WindowsSpecialization +}; + // The TargetPlatform class describes how the stack and the registers work on a CPU+ABI combination. // // All combinations have a separate definition, guarded by #ifdefs. The exceptions are: @@ -79,25 +84,38 @@ namespace JIT { // a call, we add a load it right before emitting the call instruction. // // NOTE: When adding new architecture, do not forget to whitelist it in qv4global_p.h! +template <typename PlatformAssembler, TargetOperatingSystemSpecialization specialization = NoOperatingSystemSpecialization> class TargetPlatform { -public: +}; + #if CPU(X86) && (OS(LINUX) || OS(WINDOWS) || OS(QNX) || OS(FREEBSD) || defined(Q_OS_IOS)) - enum { RegAllocIsSupported = 1 }; +template <> +class TargetPlatform<JSC::MacroAssemblerX86, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::edi; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::esi; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::ecx; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + enum { RegAllocIsSupported = 1 }; - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::edi; + static const RegisterID EngineRegister = JSC::X86Registers::esi; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::ecx; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + static const RegisterID LowReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID HighReturnValueRegister = JSC::X86Registers::edx; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::X86Registers::edx, QStringLiteral("edx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::ebx, QStringLiteral("ebx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::edi, QStringLiteral("edi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) @@ -109,28 +127,31 @@ public: << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } # define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -# undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -# undef ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 0; - static JSC::MacroAssembler::RegisterID registerForArgument(int) { Q_UNREACHABLE(); } + static RegisterID registerForArgument(int) { Q_UNREACHABLE(); } static const int StackAlignment = 16; static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add32(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } #if OS(WINDOWS) || OS(QNX) || \ ((OS(LINUX) || OS(FREEBSD)) && (defined(__PIC__) || defined(__PIE__))) -#define RESTORE_EBX_ON_CALL - static JSC::MacroAssembler::Address ebxAddressOnStack() - { + static const int gotRegister = JSC::X86Registers::ebx; + static int savedGOTRegisterSlotOnStack() { static int ebxIdx = -1; if (ebxIdx == -1) { int calleeSaves = 0; @@ -146,34 +167,47 @@ public: Q_ASSERT(ebxIdx >= 0); ebxIdx += 1; } - return JSC::MacroAssembler::Address(FramePointerRegister, ebxIdx * -int(sizeof(void*))); + return ebxIdx * -int(sizeof(void*)); } +#else + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } #endif - -#endif // Windows on x86 +}; +#endif // x86 #if CPU(X86_64) && (OS(LINUX) || OS(MAC_OS_X) || OS(FREEBSD) || OS(QNX) || defined(Q_OS_IOS)) +template <> +class TargetPlatform<JSC::MacroAssemblerX86_64, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86_64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID EngineRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + // r11 is used as scratch register by the macro assembler << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) @@ -185,17 +219,16 @@ public: << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } #define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 6; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::edi, JSC::X86Registers::esi, JSC::X86Registers::edx, @@ -210,51 +243,72 @@ public: static const int StackAlignment = 16; static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // Linux/MacOS on x86_64 #if CPU(X86_64) && OS(WINDOWS) - // Register allocation is not (yet) supported on win64, because the ABI related stack handling - // is not completely implemented. Specifically, the saving of xmm registers, and the saving of - // incoming function parameters to the shadow space is missing. - enum { RegAllocIsSupported = 0 }; - - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::X86Registers::ebp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::X86Registers::esp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::X86Registers::r12; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::X86Registers::r14; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::X86Registers::eax; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::X86Registers::r10; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; - - static RegisterInformation getPlatformRegisterInfo() +template <> +class TargetPlatform<JSC::MacroAssemblerX86_64, WindowsSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerX86_64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + + enum { RegAllocIsSupported = 1 }; + + static const RegisterID FramePointerRegister = JSC::X86Registers::ebp; + static const RegisterID StackPointerRegister = JSC::X86Registers::esp; + static const RegisterID LocalsRegister = JSC::X86Registers::r12; + static const RegisterID EngineRegister = JSC::X86Registers::r14; + static const RegisterID ReturnValueRegister = JSC::X86Registers::eax; + static const RegisterID ScratchRegister = JSC::X86Registers::r10; + static const FPRegisterID FPGpr0 = JSC::X86Registers::xmm0; + static const FPRegisterID FPGpr1 = JSC::X86Registers::xmm1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() - << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) - << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) + static RegisterInformation info = RegisterInformation() + << RI(JSC::X86Registers::ebx, QStringLiteral("rbx"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edi, QStringLiteral("rdi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::esi, QStringLiteral("rsi"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::edx, QStringLiteral("rdx"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r8, QStringLiteral("r8"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::r9, QStringLiteral("r9"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) + // r11 is used as scratch register by the macro assembler << RI(JSC::X86Registers::r12, QStringLiteral("r12"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r13, QStringLiteral("r13"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::X86Registers::r14, QStringLiteral("r14"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) - << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::Predefined) + << RI(JSC::X86Registers::r15, QStringLiteral("r15"), RI::RegularRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm2, QStringLiteral("xmm2"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm3, QStringLiteral("xmm3"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm4, QStringLiteral("xmm4"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm5, QStringLiteral("xmm5"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm6, QStringLiteral("xmm6"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) + << RI(JSC::X86Registers::xmm7, QStringLiteral("xmm7"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #define HAVE_ALU_OPS_WITH_MEM_OPERAND 1 -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::X86Registers::ecx, JSC::X86Registers::edx, JSC::X86Registers::r8, @@ -262,16 +316,34 @@ public: }; Q_ASSERT(index >= 0 && index < RegisterArgumentCount); return regs[index]; - }; + } static const int StackAlignment = 16; static const int StackShadowSpace = 32; static const int StackSpaceAllocatedUponFunctionEntry = RegisterSize; // Return address is pushed onto stack by the CPU. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) { as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) { as->pop(FramePointerRegister); } + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(FramePointerRegister); } + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) + { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); + as->pop(FramePointerRegister); + } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // Windows on x86_64 -#if CPU(ARM) +#if CPU(ARM) || defined(V4_BOOTSTRAP) +template <> +class TargetPlatform<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerARMv7; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; // The AAPCS specifies that the platform ABI has to define the usage of r9. Known are: @@ -287,23 +359,25 @@ public: // is used for the subroutine: r7 for Thumb or Thumb2, and r11 for ARM. We assign the constants // accordingly, and assign the locals-register to the "other" register. #if CPU(ARM_THUMB2) - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r7; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r11; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r7; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r11; #else // Thumbs down - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARMRegisters::r11; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARMRegisters::r7; + static const RegisterID FramePointerRegister = JSC::ARMRegisters::r11; + static const RegisterID LocalsRegister = JSC::ARMRegisters::r7; #endif - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARMRegisters::r13; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARMRegisters::r5; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARMRegisters::r10; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; - - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID StackPointerRegister = JSC::ARMRegisters::r13; + static const RegisterID ScratchRegister = JSC::ARMRegisters::r5; + static const RegisterID EngineRegister = JSC::ARMRegisters::r10; + static const RegisterID ReturnValueRegister = JSC::ARMRegisters::r0; + static const FPRegisterID FPGpr0 = JSC::ARMRegisters::d0; + static const FPRegisterID FPGpr1 = JSC::ARMRegisters::d1; + static const RegisterID LowReturnValueRegister = JSC::ARMRegisters::r0; + static const RegisterID HighReturnValueRegister = JSC::ARMRegisters::r1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::ARMRegisters::r0, QStringLiteral("r0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) << RI(JSC::ARMRegisters::r1, QStringLiteral("r1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARMRegisters::r2, QStringLiteral("r2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -336,17 +410,16 @@ public: << RI(JSC::ARMRegisters::d14, QStringLiteral("d14"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::ARMRegisters::d15, QStringLiteral("d15"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARMRegisters::r0, JSC::ARMRegisters::r1, JSC::ARMRegisters::r2, @@ -361,35 +434,54 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::ARMRegisters::lr); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) { + // Work around bug in ARMv7Assembler.h where add32(imm, sp, sp) doesn't + // work well for large immediates. + as->move(TrustedImm32(frameSize), JSC::ARMRegisters::r3); + as->add32(JSC::ARMRegisters::r3, StackPointerRegister); + } as->pop(FramePointerRegister); as->pop(JSC::ARMRegisters::lr); } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // ARM (32 bit) -#if CPU(ARM64) +#if CPU(ARM64) || defined(V4_BOOTSTRAP) +template <> +class TargetPlatform<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerARM64; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; + enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::ARM64Registers::fp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::ARM64Registers::x28; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::ARM64Registers::sp; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::ARM64Registers::x9; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::ARM64Registers::x27; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; + static const RegisterID FramePointerRegister = JSC::ARM64Registers::fp; + static const RegisterID LocalsRegister = JSC::ARM64Registers::x28; + static const RegisterID StackPointerRegister = JSC::ARM64Registers::sp; + static const RegisterID ScratchRegister = JSC::ARM64Registers::x9; + static const RegisterID EngineRegister = JSC::ARM64Registers::x27; + static const RegisterID ReturnValueRegister = JSC::ARM64Registers::x0; + static const FPRegisterID FPGpr0 = JSC::ARM64Registers::q0; + static const FPRegisterID FPGpr1 = JSC::ARM64Registers::q1; - static RegisterInformation getPlatformRegisterInfo() + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() << RI(JSC::ARM64Registers::x0, QStringLiteral("x0"), RI::RegularRegister, RI::CallerSaved, RI::Predefined) << RI(JSC::ARM64Registers::x1, QStringLiteral("x1"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARM64Registers::x2, QStringLiteral("x2"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -447,17 +539,16 @@ public: << RI(JSC::ARM64Registers::q30, QStringLiteral("q30"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::ARM64Registers::q31, QStringLiteral("q31"), RI::FloatingPointRegister, RI::CallerSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#define VALUE_FITS_IN_REGISTER static const int RegisterSize = 8; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 8; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::ARM64Registers::x0, JSC::ARM64Registers::x1, JSC::ARM64Registers::x2, @@ -476,33 +567,49 @@ public: static const int StackShadowSpace = 0; static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->pushPair(FramePointerRegister, JSC::ARM64Registers::lr); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) + as->add64(TrustedImm32(frameSize), StackPointerRegister); as->popPair(FramePointerRegister, JSC::ARM64Registers::lr); } + + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } +}; #endif // ARM64 #if defined(Q_PROCESSOR_MIPS_32) && defined(Q_OS_LINUX) +template <> +class TargetPlatform<JSC::MacroAssemblerMIPS, NoOperatingSystemSpecialization> +{ +public: + using PlatformAssembler = JSC::MacroAssemblerMIPS; + using RegisterID = PlatformAssembler::RegisterID; + using FPRegisterID = PlatformAssembler::FPRegisterID; + using TrustedImm32 = PlatformAssembler::TrustedImm32; enum { RegAllocIsSupported = 1 }; - static const JSC::MacroAssembler::RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; - static const JSC::MacroAssembler::RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; - static const JSC::MacroAssembler::RegisterID LocalsRegister = JSC::MIPSRegisters::s0; - static const JSC::MacroAssembler::RegisterID EngineRegister = JSC::MIPSRegisters::s1; - static const JSC::MacroAssembler::RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; - static const JSC::MacroAssembler::RegisterID ScratchRegister = JSC::MIPSRegisters::s2; - static const JSC::MacroAssembler::FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; - static const JSC::MacroAssembler::FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; - - static RegisterInformation getPlatformRegisterInfo() + static const RegisterID FramePointerRegister = JSC::MIPSRegisters::fp; + static const RegisterID StackPointerRegister = JSC::MIPSRegisters::sp; + static const RegisterID LocalsRegister = JSC::MIPSRegisters::s0; + static const RegisterID EngineRegister = JSC::MIPSRegisters::s1; + static const RegisterID ReturnValueRegister = JSC::MIPSRegisters::v0; + static const RegisterID ScratchRegister = JSC::MIPSRegisters::s2; + static const FPRegisterID FPGpr0 = JSC::MIPSRegisters::f0; + static const FPRegisterID FPGpr1 = JSC::MIPSRegisters::f2; + static const RegisterID LowReturnValueRegister = JSC::MIPSRegisters::v0; + static const RegisterID HighReturnValueRegister = JSC::MIPSRegisters::v1; + + static RegisterInformation getRegisterInfo() { typedef RegisterInfo RI; - return RegisterInformation() + static RegisterInformation info = RegisterInformation() // Note: t0, t1, t2, t3 and f16 are already used by MacroAssemblerMIPS. << RI(JSC::MIPSRegisters::t4, QStringLiteral("t4"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) << RI(JSC::MIPSRegisters::t5, QStringLiteral("t5"), RI::RegularRegister, RI::CallerSaved, RI::RegAlloc) @@ -524,17 +631,16 @@ public: << RI(JSC::MIPSRegisters::f26, QStringLiteral("f26"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) << RI(JSC::MIPSRegisters::f28, QStringLiteral("f28"), RI::FloatingPointRegister, RI::CalleeSaved, RI::RegAlloc) ; + return info; } #undef HAVE_ALU_OPS_WITH_MEM_OPERAND -#undef VALUE_FITS_IN_REGISTER static const int RegisterSize = 4; -#define ARGUMENTS_IN_REGISTERS static const int RegisterArgumentCount = 4; - static JSC::MacroAssembler::RegisterID registerForArgument(int index) + static RegisterID registerForArgument(int index) { - static JSC::MacroAssembler::RegisterID regs[RegisterArgumentCount] = { + static RegisterID regs[RegisterArgumentCount] = { JSC::MIPSRegisters::a0, JSC::MIPSRegisters::a1, JSC::MIPSRegisters::a2, @@ -549,27 +655,25 @@ public: static const int StackShadowSpace = 4 * RegisterSize; // Stack space for 4 argument registers. static const int StackSpaceAllocatedUponFunctionEntry = 1 * RegisterSize; // Registers saved in platformEnterStandardStackFrame below. - static void platformEnterStandardStackFrame(JSC::MacroAssembler *as) + static void platformEnterStandardStackFrame(PlatformAssembler *as) { as->push(JSC::MIPSRegisters::ra); as->push(FramePointerRegister); } - static void platformLeaveStandardStackFrame(JSC::MacroAssembler *as) + static void platformLeaveStandardStackFrame(PlatformAssembler *as, int frameSize) { + if (frameSize > 0) + as->add32(TrustedImm32(frameSize), StackPointerRegister); as->pop(FramePointerRegister); as->pop(JSC::MIPSRegisters::ra); } -#endif // Linux on MIPS (32 bit) -public: // utility functions - static const RegisterInformation getRegisterInfo() - { - static const RegisterInformation info = getPlatformRegisterInfo(); - return info; - } + static const int gotRegister = -1; + static int savedGOTRegisterSlotOnStack() { return -1; } }; +#endif // Linux on MIPS (32 bit) } // JIT namespace } // QV4 namespace diff --git a/src/qml/jit/qv4unop.cpp b/src/qml/jit/qv4unop.cpp index 799103849b..76c6457d67 100644 --- a/src/qml/jit/qv4unop.cpp +++ b/src/qml/jit/qv4unop.cpp @@ -48,14 +48,15 @@ using namespace JIT; #define stringIfy(s) stringIfyx(s) #define setOp(operation) \ do { \ - call = RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ + call = typename JITAssembler::RuntimeCall(qOffsetOf(QV4::Runtime, operation)); name = "Runtime::" stringIfy(operation); \ needsExceptionCheck = Runtime::Method_##operation##_NeedsExceptionCheck; \ } while (0) -void Unop::generate(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generate(IR::Expr *source, IR::Expr *target) { bool needsExceptionCheck; - RuntimeCall call; + typename JITAssembler::RuntimeCall call; const char *name = 0; switch (op) { case IR::OpNot: @@ -75,17 +76,18 @@ void Unop::generate(IR::Expr *source, IR::Expr *target) } // switch Q_ASSERT(call.isValid()); - _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, Assembler::PointerToValue(source)); + _as->generateFunctionCallImp(needsExceptionCheck, target, name, call, PointerToValue(source)); } -void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateUMinus(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - Assembler::RegisterID sReg = _as->toInt32Register(source, tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + typename JITAssembler::RegisterID sReg = _as->toInt32Register(source, tReg); _as->move(sReg, tReg); _as->neg32(tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) @@ -93,26 +95,27 @@ void Unop::generateUMinus(IR::Expr *source, IR::Expr *target) return; } - generateRuntimeCall(target, uMinus, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uMinus, PointerToValue(source)); } -void Unop::generateNot(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateNot(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::BoolType) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->xor32(TrustedImm32(0x1), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeBool(tReg, target); return; } else if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->compare32(Assembler::Equal, - _as->toInt32Register(source, Assembler::ScratchRegister), Assembler::TrustedImm32(0), + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->compare32(RelationalCondition::Equal, + _as->toInt32Register(source, JITAssembler::ScratchRegister), TrustedImm32(0), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeBool(tReg, target); @@ -122,22 +125,33 @@ void Unop::generateNot(IR::Expr *source, IR::Expr *target) } // ## generic implementation testing for int/bool - generateRuntimeCall(target, uNot, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, uNot, PointerToValue(source)); } -void Unop::generateCompl(IR::Expr *source, IR::Expr *target) +template <typename JITAssembler> +void Unop<JITAssembler>::generateCompl(IR::Expr *source, IR::Expr *target) { IR::Temp *targetTemp = target->asTemp(); if (source->type == IR::SInt32Type) { - Assembler::RegisterID tReg = Assembler::ScratchRegister; + typename JITAssembler::RegisterID tReg = JITAssembler::ScratchRegister; if (targetTemp && targetTemp->kind == IR::Temp::PhysicalRegister) - tReg = (Assembler::RegisterID) targetTemp->index; - _as->xor32(Assembler::TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); + tReg = (typename JITAssembler::RegisterID) targetTemp->index; + _as->xor32(TrustedImm32(0xffffffff), _as->toInt32Register(source, tReg), tReg); if (!targetTemp || targetTemp->kind != IR::Temp::PhysicalRegister) _as->storeInt32(tReg, target); return; } - generateRuntimeCall(target, complement, Assembler::PointerToValue(source)); + generateRuntimeCall(_as, target, complement, PointerToValue(source)); } +template struct QV4::JIT::Unop<QV4::JIT::Assembler<DefaultAssemblerTargetConfiguration>>; +#if defined(V4_BOOTSTRAP) +#if !CPU(ARM_THUMB2) +template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARMv7, NoOperatingSystemSpecialization>>>; +#endif +#if !CPU(ARM64) +template struct QV4::JIT::Unop<QV4::JIT::Assembler<AssemblerTargetConfiguration<JSC::MacroAssemblerARM64, NoOperatingSystemSpecialization>>>; +#endif +#endif + #endif diff --git a/src/qml/jit/qv4unop_p.h b/src/qml/jit/qv4unop_p.h index 1141a84913..fb68f80eec 100644 --- a/src/qml/jit/qv4unop_p.h +++ b/src/qml/jit/qv4unop_p.h @@ -60,21 +60,25 @@ QT_BEGIN_NAMESPACE namespace QV4 { namespace JIT { -class Assembler; - +template <typename JITAssembler> struct Unop { - Unop(Assembler *assembler, IR::AluOp operation) + Unop(JITAssembler *assembler, IR::AluOp operation) : _as(assembler) , op(operation) {} + using RelationalCondition = typename JITAssembler::RelationalCondition; + using PointerToValue = typename JITAssembler::PointerToValue; + using RuntimeCall = typename JITAssembler::RuntimeCall; + using TrustedImm32 = typename JITAssembler::TrustedImm32; + void generate(IR::Expr *source, IR::Expr *target); void generateUMinus(IR::Expr *source, IR::Expr *target); void generateNot(IR::Expr *source, IR::Expr *target); void generateCompl(IR::Expr *source, IR::Expr *target); - Assembler *_as; + JITAssembler *_as; IR::AluOp op; }; diff --git a/src/qml/jsruntime/jsruntime.pri b/src/qml/jsruntime/jsruntime.pri index 919524d1ed..955cf585e4 100644 --- a/src/qml/jsruntime/jsruntime.pri +++ b/src/qml/jsruntime/jsruntime.pri @@ -35,7 +35,6 @@ SOURCES += \ $$PWD/qv4regexp.cpp \ $$PWD/qv4serialize.cpp \ $$PWD/qv4script.cpp \ - $$PWD/qv4executableallocator.cpp \ $$PWD/qv4sequenceobject.cpp \ $$PWD/qv4include.cpp \ $$PWD/qv4qobjectwrapper.cpp \ @@ -113,7 +112,8 @@ HEADERS += \ SOURCES += \ $$PWD/qv4runtime.cpp \ $$PWD/qv4string.cpp \ - $$PWD/qv4value.cpp + $$PWD/qv4value.cpp \ + $$PWD/qv4executableallocator.cpp valgrind { DEFINES += V4_USE_VALGRIND diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index 5a190d6690..7c1cc92a13 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -88,10 +88,12 @@ void ArgumentsObject::fullyCreate() Scope scope(engine()); Scoped<MemberData> md(scope, d()->mappedArguments); - d()->mappedArguments = md->allocate(engine(), numAccessors); - for (uint i = 0; i < numAccessors; ++i) { - d()->mappedArguments->data[i] = context()->callData->args[i]; - arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + if (numAccessors) { + d()->mappedArguments = md->allocate(engine(), numAccessors); + for (uint i = 0; i < numAccessors; ++i) { + d()->mappedArguments->data[i] = context()->callData->args[i]; + arraySet(i, context()->engine->argumentsAccessors + i, Attr_Accessor); + } } arrayPut(numAccessors, context()->callData->args + numAccessors, argCount - numAccessors); for (uint i = numAccessors; i < argCount; ++i) @@ -164,18 +166,17 @@ ReturnedValue ArgumentsObject::getIndexed(const Managed *m, uint index, bool *ha return Encode::undefined(); } -void ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) +bool ArgumentsObject::putIndexed(Managed *m, uint index, const Value &value) { ArgumentsObject *args = static_cast<ArgumentsObject *>(m); if (!args->fullyCreated() && index >= static_cast<uint>(args->context()->callData->argc)) args->fullyCreate(); - if (args->fullyCreated()) { - Object::putIndexed(m, index, value); - return; - } + if (args->fullyCreated()) + return Object::putIndexed(m, index, value); args->context()->callData->args[index] = value; + return true; } bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 0a2ea3b42a..f80ade9611 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -128,7 +128,7 @@ struct ArgumentsObject: Object { bool defineOwnProperty(ExecutionEngine *engine, uint index, const Property *desc, PropertyAttributes attrs); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); static bool deleteIndexedProperty(Managed *m, uint index); static PropertyAttributes queryIndexed(const Managed *m, uint index); static void markObjects(Heap::Base *that, ExecutionEngine *e); diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 60b90e4bf0..740ebbe359 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -155,7 +155,7 @@ void ExecutionContext::createMutableBinding(String *name, bool deletable) ctx = ctx->d()->outer; } - if (activation->hasProperty(name)) + if (activation->hasOwnProperty(name)) return; ScopedProperty desc(scope); PropertyAttributes attrs(Attr_Data); diff --git a/src/qml/jsruntime/qv4dateobject.cpp b/src/qml/jsruntime/qv4dateobject.cpp index b90c335b1c..c56d007028 100644 --- a/src/qml/jsruntime/qv4dateobject.cpp +++ b/src/qml/jsruntime/qv4dateobject.cpp @@ -340,7 +340,9 @@ static inline double TimeClip(double t) { if (! qt_is_finite(t) || fabs(t) > 8.64e15) return qt_qnan(); - return Primitive::toInteger(t); + + // +0 looks weird, but is correct. See ES6 20.3.1.15. We must not return -0. + return Primitive::toInteger(t) + 0; } static inline double ParseString(const QString &s) @@ -724,7 +726,7 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(7)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(7)); LocalTZA = getLocalTZA(); ctor->defineDefaultProperty(QStringLiteral("parse"), method_parse, 1); @@ -774,8 +776,21 @@ void DatePrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("setYear"), method_setYear, 1); defineDefaultProperty(QStringLiteral("setFullYear"), method_setFullYear, 3); defineDefaultProperty(QStringLiteral("setUTCFullYear"), method_setUTCFullYear, 3); - defineDefaultProperty(QStringLiteral("toUTCString"), method_toUTCString, 0); - defineDefaultProperty(QStringLiteral("toGMTString"), method_toUTCString, 0); + + // ES6: B.2.4.3 & 20.3.4.43: + // We have to use the *same object* for toUTCString and toGMTString + { + QString toUtcString(QStringLiteral("toUTCString")); + QString toGmtString(QStringLiteral("toGMTString")); + ScopedString us(scope, engine->newIdentifier(toUtcString)); + ScopedString gs(scope, engine->newIdentifier(toGmtString)); + ExecutionContext *global = engine->rootContext(); + ScopedFunctionObject toUtcGmtStringFn(scope, BuiltinFunction::create(global, us, method_toUTCString)); + toUtcGmtStringFn->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); + defineDefaultProperty(us, toUtcGmtStringFn); + defineDefaultProperty(gs, toUtcGmtStringFn); + } + defineDefaultProperty(QStringLiteral("toISOString"), method_toISOString, 0); defineDefaultProperty(QStringLiteral("toJSON"), method_toJSON, 1); } @@ -1025,6 +1040,7 @@ void DatePrototype::method_setTime(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(t)); scope.result = Encode(self->date()); } @@ -1036,7 +1052,9 @@ void DatePrototype::method_setMilliseconds(const BuiltinFunction *, Scope &scope THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms))))); scope.result = Encode(self->date()); } @@ -1048,7 +1066,9 @@ void DatePrototype::method_setUTCMilliseconds(const BuiltinFunction *, Scope &sc THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double ms = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); self->setDate(TimeClip(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), SecFromTime(t), ms)))); scope.result = Encode(self->date()); } @@ -1060,8 +1080,11 @@ void DatePrototype::method_setSeconds(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double sec = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double ms = (callData->argc < 2) ? msFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), MinFromTime(t), sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1088,9 +1111,13 @@ void DatePrototype::method_setMinutes(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double min = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double sec = (callData->argc < 2) ? SecFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 3) ? msFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(HourFromTime(t), min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1118,10 +1145,15 @@ void DatePrototype::method_setHours(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double hour = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double min = (callData->argc < 2) ? MinFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double sec = (callData->argc < 3) ? SecFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); double ms = (callData->argc < 4) ? msFromTime(t) : callData->args[3].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(Day(t), MakeTime(hour, min, sec, ms)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1150,7 +1182,9 @@ void DatePrototype::method_setDate(const BuiltinFunction *, Scope &scope, CallDa THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1163,7 +1197,9 @@ void DatePrototype::method_setUTCDate(const BuiltinFunction *, Scope &scope, Cal THROW_TYPE_ERROR(); double t = self->date(); + CHECK_EXCEPTION(); double date = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); t = TimeClip(MakeDate(MakeDay(YearFromTime(t), MonthFromTime(t), date), TimeWithinDay(t))); self->setDate(t); scope.result = Encode(self->date()); @@ -1176,8 +1212,11 @@ void DatePrototype::method_setMonth(const BuiltinFunction *, Scope &scope, CallD THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); double month = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double date = (callData->argc < 2) ? DateFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(YearFromTime(t), month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); @@ -1245,11 +1284,15 @@ void DatePrototype::method_setFullYear(const BuiltinFunction *, Scope &scope, Ca THROW_TYPE_ERROR(); double t = LocalTime(self->date()); + CHECK_EXCEPTION(); if (std::isnan(t)) t = 0; double year = callData->argc ? callData->args[0].toNumber() : qt_qnan(); + CHECK_EXCEPTION(); double month = (callData->argc < 2) ? MonthFromTime(t) : callData->args[1].toNumber(); + CHECK_EXCEPTION(); double date = (callData->argc < 3) ? DateFromTime(t) : callData->args[2].toNumber(); + CHECK_EXCEPTION(); t = TimeClip(UTC(MakeDate(MakeDay(year, month, date), TimeWithinDay(t)))); self->setDate(t); scope.result = Encode(self->date()); diff --git a/src/qml/jsruntime/qv4dateobject_p.h b/src/qml/jsruntime/qv4dateobject_p.h index a56d17f9b1..b32b2c3f66 100644 --- a/src/qml/jsruntime/qv4dateobject_p.h +++ b/src/qml/jsruntime/qv4dateobject_p.h @@ -112,7 +112,7 @@ struct DateCtor: FunctionObject static void call(const Managed *that, Scope &scope, CallData *); }; -struct DatePrototype: DateObject +struct DatePrototype: Object { void init(ExecutionEngine *engine, Object *ctor); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index a11f7f0875..39b433e5f9 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -91,7 +91,9 @@ #if USE(PTHREADS) # include <pthread.h> +#if !defined(Q_OS_INTEGRITY) # include <sys/resource.h> +#endif #if HAVE(PTHREAD_NP_H) # include <pthread_np.h> #endif @@ -168,7 +170,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) if (forceMoth) { factory = new Moth::ISelFactory; } else { - factory = new JIT::ISelFactory; + factory = new JIT::ISelFactory<>; jitDisabled = false; } #else // !V4_ENABLE_JIT @@ -256,6 +258,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) arrayClass = emptyClass->addMember(id_length(), Attr_NotConfigurable|Attr_NotEnumerable); jsObjects[ArrayProto] = memoryManager->allocObject<ArrayPrototype>(arrayClass, objectPrototype()); + jsObjects[PropertyListProto] = memoryManager->allocObject<PropertyListPrototype>(); InternalClass *argsClass = emptyClass->addMember(id_length(), Attr_NotEnumerable); argumentsObjectClass = argsClass->addMember(id_callee(), Attr_Data|Attr_NotEnumerable); @@ -358,6 +361,7 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) static_cast<NumberPrototype *>(numberPrototype())->init(this, numberCtor()); static_cast<BooleanPrototype *>(booleanPrototype())->init(this, booleanCtor()); static_cast<ArrayPrototype *>(arrayPrototype())->init(this, arrayCtor()); + static_cast<PropertyListPrototype *>(propertyListPrototype())->init(this); static_cast<DatePrototype *>(datePrototype())->init(this, dateCtor()); static_cast<FunctionPrototype *>(functionPrototype())->init(this, functionCtor()); static_cast<RegExpPrototype *>(regExpPrototype())->init(this, regExpCtor()); @@ -400,7 +404,8 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) globalObject->defineDefaultProperty(QStringLiteral("Object"), *objectCtor()); globalObject->defineDefaultProperty(QStringLiteral("String"), *stringCtor()); - globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberCtor()); + FunctionObject *numberObject = numberCtor(); + globalObject->defineDefaultProperty(QStringLiteral("Number"), *numberObject); globalObject->defineDefaultProperty(QStringLiteral("Boolean"), *booleanCtor()); globalObject->defineDefaultProperty(QStringLiteral("Array"), *arrayCtor()); globalObject->defineDefaultProperty(QStringLiteral("Function"), *functionCtor()); @@ -430,8 +435,26 @@ ExecutionEngine::ExecutionEngine(EvalISelFactory *factory) jsObjects[Eval_Function] = memoryManager->allocObject<EvalFunction>(global); globalObject->defineDefaultProperty(QStringLiteral("eval"), *evalFunction()); - globalObject->defineDefaultProperty(QStringLiteral("parseInt"), GlobalFunctions::method_parseInt, 2); - globalObject->defineDefaultProperty(QStringLiteral("parseFloat"), GlobalFunctions::method_parseFloat, 1); + // ES6: 20.1.2.12 & 20.1.2.13: + // parseInt and parseFloat must be the same FunctionObject on the global & + // Number object. + { + QString piString(QStringLiteral("parseInt")); + QString pfString(QStringLiteral("parseFloat")); + Scope scope(this); + ScopedString pi(scope, newIdentifier(piString)); + ScopedString pf(scope, newIdentifier(pfString)); + ExecutionContext *global = rootContext(); + ScopedFunctionObject parseIntFn(scope, BuiltinFunction::create(global, pi, GlobalFunctions::method_parseInt)); + ScopedFunctionObject parseFloatFn(scope, BuiltinFunction::create(global, pf, GlobalFunctions::method_parseFloat)); + parseIntFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(2)); + parseFloatFn->defineReadonlyConfigurableProperty(id_length(), Primitive::fromInt32(1)); + globalObject->defineDefaultProperty(piString, parseIntFn); + globalObject->defineDefaultProperty(pfString, parseFloatFn); + numberObject->defineDefaultProperty(piString, parseIntFn); + numberObject->defineDefaultProperty(pfString, parseFloatFn); + } + globalObject->defineDefaultProperty(QStringLiteral("isNaN"), GlobalFunctions::method_isNaN, 1); globalObject->defineDefaultProperty(QStringLiteral("isFinite"), GlobalFunctions::method_isFinite, 1); globalObject->defineDefaultProperty(QStringLiteral("decodeURI"), GlobalFunctions::method_decodeURI, 1); @@ -1109,7 +1132,7 @@ static QVariant toVariant(QV4::ExecutionEngine *e, const QV4::Value &value, int if (typeHint == qMetaTypeId<QJSValue>()) return QVariant::fromValue(QJSValue(e, value.asReturnedValue())); - if (value.as<Object>()) { + if (value.as<QV4::Object>()) { QV4::ScopedObject object(scope, value); if (typeHint == QMetaType::QJsonObject && !value.as<ArrayObject>() && !value.as<FunctionObject>()) { @@ -1755,7 +1778,7 @@ bool ExecutionEngine::metaTypeFromJS(const Value *value, int type, void *data) return false; } -static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, const QByteArray &targetType, void **result) +static bool convertToNativeQObject(QV4::ExecutionEngine *e, const QV4::Value &value, const QByteArray &targetType, void **result) { if (!targetType.endsWith('*')) return false; @@ -1770,7 +1793,7 @@ static bool convertToNativeQObject(QV4::ExecutionEngine *e, const Value &value, return false; } -static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const Value &value) +static QObject *qtObjectFromJS(QV4::ExecutionEngine *engine, const QV4::Value &value) { if (!value.isObject()) return 0; diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 1c20ad30aa..69aa389c44 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -157,6 +157,7 @@ public: IntegerNull, // Has to come after the RootContext to make the context stack safe ObjectProto, ArrayProto, + PropertyListProto, StringProto, NumberProto, BooleanProto, @@ -225,6 +226,7 @@ public: Object *objectPrototype() const { return reinterpret_cast<Object *>(jsObjects + ObjectProto); } Object *arrayPrototype() const { return reinterpret_cast<Object *>(jsObjects + ArrayProto); } + Object *propertyListPrototype() const { return reinterpret_cast<Object *>(jsObjects + PropertyListProto); } Object *stringPrototype() const { return reinterpret_cast<Object *>(jsObjects + StringProto); } Object *numberPrototype() const { return reinterpret_cast<Object *>(jsObjects + NumberProto); } Object *booleanPrototype() const { return reinterpret_cast<Object *>(jsObjects + BooleanProto); } diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index b2d89220ea..e9431ed25e 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -131,7 +131,7 @@ void FunctionObject::init(String *n, bool createProto) } if (n) - defineReadonlyProperty(s.engine->id_name(), *n); + defineReadonlyConfigurableProperty(s.engine->id_name(), *n); } ReturnedValue FunctionObject::name() const @@ -258,10 +258,10 @@ void FunctionPrototype::init(ExecutionEngine *engine, Object *ctor) Scope scope(engine); ScopedObject o(scope); - ctor->defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(1)); ctor->defineReadonlyProperty(engine->id_prototype(), (o = this)); - defineReadonlyProperty(engine->id_length(), Primitive::fromInt32(0)); + defineReadonlyConfigurableProperty(engine->id_length(), Primitive::fromInt32(0)); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); defineDefaultProperty(engine->id_toString(), method_toString, 0); defineDefaultProperty(QStringLiteral("apply"), method_apply, 2); @@ -557,7 +557,7 @@ void Heap::BoundFunction::init(QV4::ExecutionContext *scope, QV4::FunctionObject len -= boundArgs->size(); if (len < 0) len = 0; - f->defineReadonlyProperty(s.engine->id_length(), Primitive::fromInt32(len)); + f->defineReadonlyConfigurableProperty(s.engine->id_length(), Primitive::fromInt32(len)); ScopedProperty pd(s); pd->value = s.engine->thrower(); diff --git a/src/qml/jsruntime/qv4global_p.h b/src/qml/jsruntime/qv4global_p.h index 66861bf697..b0d14fc2b4 100644 --- a/src/qml/jsruntime/qv4global_p.h +++ b/src/qml/jsruntime/qv4global_p.h @@ -258,6 +258,7 @@ enum PropertyFlag { Attr_NotEnumerable = 0x4, Attr_NotConfigurable = 0x8, Attr_ReadOnly = Attr_NotWritable|Attr_NotEnumerable|Attr_NotConfigurable, + Attr_ReadOnly_ButConfigurable = Attr_NotWritable|Attr_NotEnumerable, Attr_Invalid = 0xff }; diff --git a/src/qml/jsruntime/qv4internalclass_p.h b/src/qml/jsruntime/qv4internalclass_p.h index dcda949c97..1d8ef4b0fb 100644 --- a/src/qml/jsruntime/qv4internalclass_p.h +++ b/src/qml/jsruntime/qv4internalclass_p.h @@ -234,7 +234,7 @@ struct InternalClassTransition { return id == other.id && flags == other.flags; } bool operator<(const InternalClassTransition &other) const - { return id < other.id; } + { return id < other.id || (id == other.id && flags < other.flags); } }; struct InternalClass : public QQmlJS::Managed { diff --git a/src/qml/jsruntime/qv4jsonobject.cpp b/src/qml/jsruntime/qv4jsonobject.cpp index 1d571f53f3..0f021c8bd0 100644 --- a/src/qml/jsruntime/qv4jsonobject.cpp +++ b/src/qml/jsruntime/qv4jsonobject.cpp @@ -705,7 +705,7 @@ QString Stringify::Str(const QString &key, const Value &v) if (replacerFunction) { ScopedObject holder(scope, v4->newObject()); - holder->put(scope.engine, QString(), scope.result); + holder->put(scope.engine->id_empty(), scope.result); ScopedCallData callData(scope, 2); callData->args[0] = v4->newString(key); callData->args[1] = scope.result; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 52f54e25f5..c5ee92fedd 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -54,8 +54,11 @@ #include "qv4runtime_p.h" #include "qv4engine_p.h" #include "qv4context_p.h" + +#if !defined(V4_BOOTSTRAP) #include "qv4object_p.h" #include "qv4internalclass_p.h" +#endif QT_BEGIN_NAMESPACE diff --git a/src/qml/jsruntime/qv4memberdata.cpp b/src/qml/jsruntime/qv4memberdata.cpp index f2a24f8179..db45c77472 100644 --- a/src/qml/jsruntime/qv4memberdata.cpp +++ b/src/qml/jsruntime/qv4memberdata.cpp @@ -55,6 +55,7 @@ void MemberData::markObjects(Heap::Base *that, ExecutionEngine *e) Heap::MemberData *MemberData::allocate(ExecutionEngine *e, uint n, Heap::MemberData *old) { Q_ASSERT(!old || old->size < n); + Q_ASSERT(n); size_t alloc = MemoryManager::align(sizeof(Heap::MemberData) + (n - 1)*sizeof(Value)); Heap::MemberData *m = e->memoryManager->allocManaged<MemberData>(alloc); diff --git a/src/qml/jsruntime/qv4numberobject.cpp b/src/qml/jsruntime/qv4numberobject.cpp index 09644c161d..8cfa930888 100644 --- a/src/qml/jsruntime/qv4numberobject.cpp +++ b/src/qml/jsruntime/qv4numberobject.cpp @@ -102,6 +102,8 @@ void NumberPrototype::init(ExecutionEngine *engine, Object *ctor) ctor->defineReadonlyProperty(QStringLiteral("POSITIVE_INFINITY"), Primitive::fromDouble(qInf())); ctor->defineReadonlyProperty(QStringLiteral("MAX_VALUE"), Primitive::fromDouble(1.7976931348623158e+308)); ctor->defineReadonlyProperty(QStringLiteral("EPSILON"), Primitive::fromDouble(std::numeric_limits<double>::epsilon())); + ctor->defineReadonlyProperty(QStringLiteral("MAX_SAFE_INTEGER"), Primitive::fromDouble(9007199254740991)); + ctor->defineReadonlyProperty(QStringLiteral("MIN_SAFE_INTEGER"), Primitive::fromDouble(-9007199254740991)); QT_WARNING_PUSH QT_WARNING_DISABLE_INTEL(239) @@ -109,15 +111,17 @@ QT_WARNING_DISABLE_INTEL(239) QT_WARNING_POP ctor->defineDefaultProperty(QStringLiteral("isFinite"), method_isFinite, 1); + ctor->defineDefaultProperty(QStringLiteral("isInteger"), method_isInteger, 1); + ctor->defineDefaultProperty(QStringLiteral("isSafeInteger"), method_isSafeInteger, 1); ctor->defineDefaultProperty(QStringLiteral("isNaN"), method_isNaN, 1); defineDefaultProperty(QStringLiteral("constructor"), (o = ctor)); - defineDefaultProperty(engine->id_toString(), method_toString); + defineDefaultProperty(engine->id_toString(), method_toString, 1); defineDefaultProperty(QStringLiteral("toLocaleString"), method_toLocaleString); defineDefaultProperty(engine->id_valueOf(), method_valueOf); defineDefaultProperty(QStringLiteral("toFixed"), method_toFixed, 1); - defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential); - defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision); + defineDefaultProperty(QStringLiteral("toExponential"), method_toExponential, 1); + defineDefaultProperty(QStringLiteral("toPrecision"), method_toPrecision, 1); } inline ReturnedValue thisNumberValue(Scope &scope, CallData *callData) @@ -155,6 +159,52 @@ void NumberPrototype::method_isFinite(const BuiltinFunction *, Scope &scope, Cal scope.result = Encode(!std::isnan(v) && !qt_is_inf(v)); } +void NumberPrototype::method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv); +} + +void NumberPrototype::method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + if (!callData->argc) { + scope.result = Encode(false); + return; + } + + const Value &v = callData->args[0]; + if (!v.isNumber()) { + scope.result = Encode(false); + return; + } + + double dv = v.toNumber(); + if (std::isnan(dv) || qt_is_inf(dv)) { + scope.result = Encode(false); + return; + } + + double iv = v.toInteger(); + scope.result = Encode(dv == iv && std::fabs(iv) <= (2^53)-1); +} + void NumberPrototype::method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData) { if (!callData->argc) { diff --git a/src/qml/jsruntime/qv4numberobject_p.h b/src/qml/jsruntime/qv4numberobject_p.h index 364b866a16..e18267c50c 100644 --- a/src/qml/jsruntime/qv4numberobject_p.h +++ b/src/qml/jsruntime/qv4numberobject_p.h @@ -88,6 +88,8 @@ struct NumberPrototype: NumberObject void init(ExecutionEngine *engine, Object *ctor); static void method_isFinite(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isInteger(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_isSafeInteger(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_isNaN(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toString(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_toLocaleString(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 5d6c479477..dd3bbccde3 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -61,7 +61,8 @@ DEFINE_OBJECT_VTABLE(Object); void Object::setInternalClass(InternalClass *ic) { d()->internalClass = ic; - if (!d()->memberData || (d()->memberData->size < ic->size)) + bool hasMD = d()->memberData != nullptr; + if ((!hasMD && ic->size) || (hasMD && d()->memberData->size < ic->size)) d()->memberData = MemberData::allocate(ic->engine, ic->size, d()->memberData); } @@ -92,13 +93,6 @@ bool Object::setPrototype(Object *proto) return true; } -void Object::put(ExecutionEngine *engine, const QString &name, const Value &value) -{ - Scope scope(engine); - ScopedString n(scope, engine->newString(name)); - put(n, value); -} - ReturnedValue Object::getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs) { if (!attrs.isAccessor()) @@ -114,11 +108,11 @@ ReturnedValue Object::getValue(const Value &thisObject, const Value &v, Property return scope.result.asReturnedValue(); } -void Object::putValue(uint memberIndex, const Value &value) +bool Object::putValue(uint memberIndex, const Value &value) { QV4::InternalClass *ic = internalClass(); if (ic->engine->hasException) - return; + return false; PropertyAttributes attrs = ic->propertyData[memberIndex]; @@ -131,7 +125,7 @@ void Object::putValue(uint memberIndex, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !ic->engine->hasException; } goto reject; } @@ -140,11 +134,12 @@ void Object::putValue(uint memberIndex, const Value &value) goto reject; *propertyData(memberIndex) = value; - return; + return true; reject: if (engine()->current->strictMode) engine()->throwTypeError(); + return false; } void Object::defineDefaultProperty(const QString &name, const Value &value) @@ -162,7 +157,7 @@ void Object::defineDefaultProperty(const QString &name, ReturnedValue (*code)(Ca ScopedString s(scope, e->newIdentifier(name)); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -173,7 +168,7 @@ void Object::defineDefaultProperty(const QString &name, void (*code)(const Built ScopedString s(scope, e->newIdentifier(name)); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, s, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(s, function); } @@ -183,7 +178,7 @@ void Object::defineDefaultProperty(String *name, ReturnedValue (*code)(CallConte Scope scope(e); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -193,7 +188,7 @@ void Object::defineDefaultProperty(String *name, void (*code)(const BuiltinFunct Scope scope(e); ExecutionContext *global = e->rootContext(); ScopedFunctionObject function(scope, BuiltinFunction::create(global, name, code)); - function->defineReadonlyProperty(e->id_length(), Primitive::fromInt32(argumentCount)); + function->defineReadonlyConfigurableProperty(e->id_length(), Primitive::fromInt32(argumentCount)); defineDefaultProperty(name, function); } @@ -250,6 +245,19 @@ void Object::defineReadonlyProperty(String *name, const Value &value) insertMember(name, value, Attr_ReadOnly); } +void Object::defineReadonlyConfigurableProperty(const QString &name, const Value &value) +{ + QV4::ExecutionEngine *e = engine(); + Scope scope(e); + ScopedString s(scope, e->newIdentifier(name)); + defineReadonlyConfigurableProperty(s, value); +} + +void Object::defineReadonlyConfigurableProperty(String *name, const Value &value) +{ + insertMember(name, value, Attr_ReadOnly_ButConfigurable); +} + void Object::markObjects(Heap::Base *that, ExecutionEngine *e) { Heap::Object *o = static_cast<Heap::Object *>(that); @@ -440,14 +448,14 @@ ReturnedValue Object::getIndexed(const Managed *m, uint index, bool *hasProperty return static_cast<const Object *>(m)->internalGetIndexed(index, hasProperty); } -void Object::put(Managed *m, String *name, const Value &value) +bool Object::put(Managed *m, String *name, const Value &value) { - static_cast<Object *>(m)->internalPut(name, value); + return static_cast<Object *>(m)->internalPut(name, value); } -void Object::putIndexed(Managed *m, uint index, const Value &value) +bool Object::putIndexed(Managed *m, uint index, const Value &value) { - static_cast<Object *>(m)->internalPutIndexed(index, value); + return static_cast<Object *>(m)->internalPutIndexed(index, value); } PropertyAttributes Object::query(const Managed *m, String *name) @@ -705,10 +713,10 @@ ReturnedValue Object::internalGetIndexed(uint index, bool *hasProperty) const // Section 8.12.5 -void Object::internalPut(String *name, const Value &value) +bool Object::internalPut(String *name, const Value &value) { if (internalClass()->engine->hasException) - return; + return false; uint idx = name->asArrayIndex(); if (idx != UINT_MAX) @@ -737,7 +745,7 @@ void Object::internalPut(String *name, const Value &value) uint l = value.asArrayLength(&ok); if (!ok) { engine()->throwRangeError(value); - return; + return false; } ok = setArrayLength(l); if (!ok) @@ -745,7 +753,7 @@ void Object::internalPut(String *name, const Value &value) } else { *v = value; } - return; + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; @@ -776,24 +784,26 @@ void Object::internalPut(String *name, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } insertMember(name, value); - return; + return true; reject: + // ### this should be removed once everything is ported to use Object::set() if (engine()->current->strictMode) { QString message = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); engine()->throwTypeError(message); } + return false; } -void Object::internalPutIndexed(uint index, const Value &value) +bool Object::internalPutIndexed(uint index, const Value &value) { if (internalClass()->engine->hasException) - return; + return false; PropertyAttributes attrs; @@ -815,7 +825,7 @@ void Object::internalPutIndexed(uint index, const Value &value) goto reject; else *v = value; - return; + return true; } else if (!prototype()) { if (!isExtensible()) goto reject; @@ -846,15 +856,17 @@ void Object::internalPutIndexed(uint index, const Value &value) callData->args[0] = value; callData->thisObject = this; setter->call(scope, callData); - return; + return !internalClass()->engine->hasException; } arraySet(index, value); - return; + return true; reject: + // ### this should be removed once everything is ported to use Object::setIndexed() if (engine()->current->strictMode) engine()->throwTypeError(); + return false; } // Section 8.12.7 @@ -1155,6 +1167,49 @@ uint Object::getLength(const Managed *m) return v->toUInt32(); } +// 'var' is 'V' in 15.3.5.3. +ReturnedValue Object::instanceOf(const Object *typeObject, const Value &var) +{ + QV4::ExecutionEngine *engine = typeObject->internalClass()->engine; + + // 15.3.5.3, Assume F is a Function object. + const FunctionObject *function = typeObject->as<FunctionObject>(); + if (!function) + return engine->throwTypeError(); + + Heap::FunctionObject *f = function->d(); + if (function->isBoundFunction()) + f = function->cast<BoundFunction>()->target(); + + // 15.3.5.3, 1: HasInstance can only be used on an object + const Object *lhs = var.as<Object>(); + if (!lhs) + return Encode(false); + + // 15.3.5.3, 2 + const Object *o = f->protoProperty(); + if (!o) // 15.3.5.3, 3 + return engine->throwTypeError(); + + Heap::Object *v = lhs->d(); + + // 15.3.5.3, 4 + while (v) { + // 15.3.5.3, 4, a + v = v->prototype; + + // 15.3.5.3, 4, b + if (!v) + break; // will return false + + // 15.3.5.3, 4, c + else if (o->d() == v) + return Encode(true); + } + + return Encode(false); +} + bool Object::setArrayLength(uint newLen) { Q_ASSERT(isArrayObject()); diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index 4a78690f47..0d17afbf41 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -130,8 +130,8 @@ struct ObjectVTable void (*construct)(const Managed *, Scope &scope, CallData *data); ReturnedValue (*get)(const Managed *, String *name, bool *hasProperty); ReturnedValue (*getIndexed)(const Managed *, uint index, bool *hasProperty); - void (*put)(Managed *, String *name, const Value &value); - void (*putIndexed)(Managed *, uint index, const Value &value); + bool (*put)(Managed *, String *name, const Value &value); + bool (*putIndexed)(Managed *, uint index, const Value &value); PropertyAttributes (*query)(const Managed *, String *name); PropertyAttributes (*queryIndexed)(const Managed *, uint index); bool (*deleteProperty)(Managed *m, String *name); @@ -140,6 +140,7 @@ struct ObjectVTable void (*setLookup)(Managed *m, Lookup *l, const Value &v); uint (*getLength)(const Managed *m); void (*advanceIterator)(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); + ReturnedValue (*instanceOf)(const Object *typeObject, const Value &var); }; #define DEFINE_OBJECT_VTABLE_BASE(classname) \ @@ -159,7 +160,8 @@ const QV4::ObjectVTable classname::static_vtbl = \ getLookup, \ setLookup, \ getLength, \ - advanceIterator \ + advanceIterator, \ + instanceOf \ } #define DEFINE_OBJECT_VTABLE(classname) \ @@ -221,8 +223,6 @@ struct Q_QML_EXPORT Object: Managed { // // helpers // - void put(ExecutionEngine *engine, const QString &name, const Value &value); - static ReturnedValue getValue(const Value &thisObject, const Value &v, PropertyAttributes attrs); ReturnedValue getValue(const Value &v, PropertyAttributes attrs) const { Scope scope(this->engine()); @@ -230,7 +230,7 @@ struct Q_QML_EXPORT Object: Managed { return getValue(t, v, attrs); } - void putValue(uint memberIndex, const Value &value); + bool putValue(uint memberIndex, const Value &value); /* The spec default: Writable: true, Enumerable: false, Configurable: true */ void defineDefaultProperty(String *name, const Value &value) { @@ -251,6 +251,10 @@ struct Q_QML_EXPORT Object: Managed { void defineReadonlyProperty(const QString &name, const Value &value); void defineReadonlyProperty(String *name, const Value &value); + /* Fixed: Writable: false, Enumerable: false, Configurable: true */ + void defineReadonlyConfigurableProperty(const QString &name, const Value &value); + void defineReadonlyConfigurableProperty(String *name, const Value &value); + void insertMember(String *s, const Value &v, PropertyAttributes attributes = Attr_Data) { Scope scope(engine()); ScopedProperty p(scope); @@ -332,10 +336,47 @@ public: { return vtable()->get(this, name, hasProperty); } inline ReturnedValue getIndexed(uint idx, bool *hasProperty = 0) const { return vtable()->getIndexed(this, idx, hasProperty); } - inline void put(String *name, const Value &v) - { vtable()->put(this, name, v); } - inline void putIndexed(uint idx, const Value &v) - { vtable()->putIndexed(this, idx, v); } + + // use the set variants instead, to customize throw behavior + inline bool put(String *name, const Value &v) + { return vtable()->put(this, name, v); } + inline bool putIndexed(uint idx, const Value &v) + { return vtable()->putIndexed(this, idx, v); } + + enum ThrowOnFailure { + DoThrowOnRejection, + DoNotThrow + }; + + // ES6: 7.3.3 Set (O, P, V, Throw) + inline bool set(String *name, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->put(this, name, v); + // ES6: 7.3.3, 6: If success is false and Throw is true, throw a TypeError exception. + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + QString message = QLatin1String("Cannot assign to read-only property \"") + + name->toQString() + QLatin1Char('\"'); + e->throwTypeError(message); + } + } + return ret; + } + + inline bool setIndexed(uint idx, const Value &v, ThrowOnFailure shouldThrow) + { + bool ret = vtable()->putIndexed(this, idx, v); + if (!ret && shouldThrow == ThrowOnFailure::DoThrowOnRejection) { + ExecutionEngine *e = engine(); + if (!e->hasException) { // allow a custom set impl to throw itself + e->throwTypeError(); + } + } + return ret; + } + + PropertyAttributes query(String *name) const { return vtable()->query(this, name); } PropertyAttributes queryIndexed(uint index) const @@ -351,6 +392,8 @@ public: void advanceIterator(ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes) { vtable()->advanceIterator(this, it, name, index, p, attributes); } uint getLength() const { return vtable()->getLength(this); } + ReturnedValue instanceOf(const Value &var) const + { return vtable()->instanceOf(this, var); } inline void construct(Scope &scope, CallData *d) const { return vtable()->construct(this, scope, d); } @@ -362,8 +405,8 @@ protected: static void call(const Managed *m, Scope &scope, CallData *); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool put(Managed *m, String *name, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); static PropertyAttributes query(const Managed *m, String *name); static PropertyAttributes queryIndexed(const Managed *m, uint index); static bool deleteProperty(Managed *m, String *name); @@ -372,12 +415,13 @@ protected: static void setLookup(Managed *m, Lookup *l, const Value &v); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); static uint getLength(const Managed *m); + static ReturnedValue instanceOf(const Object *typeObject, const Value &var); private: ReturnedValue internalGet(String *name, bool *hasProperty) const; ReturnedValue internalGetIndexed(uint index, bool *hasProperty) const; - void internalPut(String *name, const Value &value); - void internalPutIndexed(uint index, const Value &value); + bool internalPut(String *name, const Value &value); + bool internalPutIndexed(uint index, const Value &value); bool internalDeleteProperty(String *name); bool internalDeleteIndexedProperty(uint index); diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 97dbe24339..f650ffc7b1 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -89,10 +90,11 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) ScopedObject o(scope, this); ctor->defineReadonlyProperty(v4->id_prototype(), o); - ctor->defineReadonlyProperty(v4->id_length(), Primitive::fromInt32(1)); + ctor->defineReadonlyConfigurableProperty(v4->id_length(), Primitive::fromInt32(1)); ctor->defineDefaultProperty(QStringLiteral("getPrototypeOf"), method_getPrototypeOf, 1); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyDescriptor"), method_getOwnPropertyDescriptor, 2); ctor->defineDefaultProperty(QStringLiteral("getOwnPropertyNames"), method_getOwnPropertyNames, 1); + ctor->defineDefaultProperty(QStringLiteral("assign"), method_assign, 2); ctor->defineDefaultProperty(QStringLiteral("create"), method_create, 2); ctor->defineDefaultProperty(QStringLiteral("defineProperty"), method_defineProperty, 3); ctor->defineDefaultProperty(QStringLiteral("defineProperties"), method_defineProperties, 2); @@ -123,9 +125,8 @@ void ObjectPrototype::init(ExecutionEngine *v4, Object *ctor) void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedObject p(scope, o->prototype()); scope.result = !!p ? p->asReturnedValue() : Encode::null(); @@ -133,11 +134,8 @@ void ObjectPrototype::method_getPrototypeOf(const BuiltinFunction *, Scope &scop void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); - return; - } + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); if (ArgumentsObject::isNonStrictArgumentsObject(O)) static_cast<ArgumentsObject *>(O.getPointer())->fullyCreate(); @@ -154,13 +152,54 @@ void ObjectPrototype::method_getOwnPropertyDescriptor(const BuiltinFunction *, S void ObjectPrototype::method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject O(scope, callData->argument(0)); - if (!O) { - scope.result = scope.engine->throwTypeError(); + ScopedObject O(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); +} + +// 19.1.2.1 +void ObjectPrototype::method_assign(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + ScopedObject to(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); + + if (callData->argc == 1) { + scope.result = to; return; } - scope.result = getOwnPropertyNames(scope.engine, callData->args[0]); + for (int i = 1; i < callData->argc; ++i) { + if (callData->args[i].isUndefined() || callData->args[i].isNull()) + continue; + + ScopedObject from(scope, callData->args[i].toObject(scope.engine)); + CHECK_EXCEPTION(); + QV4::ScopedArrayObject keys(scope, QV4::ObjectPrototype::getOwnPropertyNames(scope.engine, from)); + quint32 length = keys->getLength(); + + ScopedString nextKey(scope); + ScopedValue propValue(scope); + for (quint32 i = 0; i < length; ++i) { + nextKey = Value::fromReturnedValue(keys->getIndexed(i)).toString(scope.engine); + + PropertyAttributes attrs; + ScopedProperty prop(scope); + from->getOwnProperty(nextKey, &attrs, prop); + + if (attrs == PropertyFlag::Attr_Invalid) + continue; + + if (!attrs.isEnumerable()) + continue; + + propValue = from->get(nextKey); + to->set(nextKey, propValue, Object::DoThrowOnRejection); + CHECK_EXCEPTION(); + } + } + + scope.result = to; } void ObjectPrototype::method_create(const BuiltinFunction *builtin, Scope &scope, CallData *callData) @@ -246,8 +285,11 @@ void ObjectPrototype::method_defineProperties(const BuiltinFunction *, Scope &sc void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.17, 1 + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->sealed()); @@ -265,8 +307,11 @@ void ObjectPrototype::method_seal(const BuiltinFunction *, Scope &scope, CallDat void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallData *callData) { ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + if (!o) { + // 19.1.2.5, 1 + scope.result = callData->argument(0); + return; + } if (ArgumentsObject::isNonStrictArgumentsObject(o)) static_cast<ArgumentsObject *>(o.getPointer())->fullyCreate(); @@ -287,9 +332,11 @@ void ObjectPrototype::method_freeze(const BuiltinFunction *, Scope &scope, CallD void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = callData->argument(0); + return; + } o->setInternalClass(o->internalClass()->nonExtensible()); scope.result = o; @@ -297,9 +344,11 @@ void ObjectPrototype::method_preventExtensions(const BuiltinFunction *, Scope &s void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -335,9 +384,11 @@ void ObjectPrototype::method_isSealed(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(true); + return; + } if (o->isExtensible()) { scope.result = Encode(false); @@ -373,18 +424,19 @@ void ObjectPrototype::method_isFrozen(const BuiltinFunction *, Scope &scope, Cal void ObjectPrototype::method_isExtensible(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + if (!o) { + scope.result = Encode(false); + return; + } scope.result = Encode((bool)o->isExtensible()); } void ObjectPrototype::method_keys(const BuiltinFunction *, Scope &scope, CallData *callData) { - ScopedObject o(scope, callData->argument(0)); - if (!o) - THROW_TYPE_ERROR(); + ScopedObject o(scope, callData->args[0].toObject(scope.engine)); + CHECK_EXCEPTION(); ScopedArrayObject a(scope, scope.engine->newArrayObject()); @@ -670,12 +722,12 @@ ReturnedValue ObjectPrototype::fromPropertyDescriptor(ExecutionEngine *engine, c return o.asReturnedValue(); } - +// es6: GetOwnPropertyKeys Heap::ArrayObject *ObjectPrototype::getOwnPropertyNames(ExecutionEngine *v4, const Value &o) { Scope scope(v4); ScopedArrayObject array(scope, v4->newArrayObject()); - ScopedObject O(scope, o); + ScopedObject O(scope, o.toObject(v4)); if (O) { ObjectIterator it(scope, O, ObjectIterator::NoFlags); ScopedValue name(scope); diff --git a/src/qml/jsruntime/qv4objectproto_p.h b/src/qml/jsruntime/qv4objectproto_p.h index 1db8615511..44b54267f3 100644 --- a/src/qml/jsruntime/qv4objectproto_p.h +++ b/src/qml/jsruntime/qv4objectproto_p.h @@ -81,6 +81,7 @@ struct ObjectPrototype: Object static void method_getPrototypeOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyDescriptor(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_getOwnPropertyNames(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_assign(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_create(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperty(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_defineProperties(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 889f4ea288..cdc29c8b9c 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -225,21 +225,19 @@ ReturnedValue QmlContextWrapper::get(const Managed *m, String *name, bool *hasPr return Encode::undefined(); } -void QmlContextWrapper::put(Managed *m, String *name, const Value &value) +bool QmlContextWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QmlContextWrapper>()); QmlContextWrapper *resource = static_cast<QmlContextWrapper *>(m); ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); if (scope.hasException()) - return; + return false; QV4::Scoped<QmlContextWrapper> wrapper(scope, resource); uint member = wrapper->internalClass()->find(name); - if (member < UINT_MAX) { - wrapper->putValue(member, value); - return; - } + if (member < UINT_MAX) + return wrapper->putValue(member, value); if (wrapper->d()->isNullWrapper) { if (wrapper && wrapper->d()->readOnly) { @@ -247,11 +245,10 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QLatin1Char('"'); ScopedString e(scope, v4->newString(error)); v4->throwError(e); - return; + return false; } - Object::put(m, name, value); - return; + return Object::put(m, name, value); } // Its possible we could delay the calculation of the "actual" context (in the case @@ -260,7 +257,7 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QQmlContextData *expressionContext = context; if (!context) - return; + return false; // See QV8ContextWrapper::Getter for resolution order @@ -270,18 +267,18 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) const QV4::IdentifierHash<int> &properties = context->propertyNames(); // Search context properties if (properties.count() && properties.value(name) != -1) - return; + return false; // Search scope object if (scopeObject && QV4::QObjectWrapper::setQmlProperty(v4, context, scopeObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; scopeObject = 0; // Search context object if (context->contextObject && QV4::QObjectWrapper::setQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, value)) - return; + return true; context = context->parent; } @@ -292,10 +289,10 @@ void QmlContextWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Invalid write to global property \"") + name->toQString() + QLatin1Char('"'); v4->throwError(error); - return; + return false; } - Object::put(m, name, value); + return Object::put(m, name, value); } void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QmlContextWrapper *qml) diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 9aec7467da..6e5e743609 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -100,7 +100,7 @@ struct Q_QML_EXPORT QmlContextWrapper : Object void setReadOnly(bool b) { d()->readOnly = b; } static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 7260e71fab..c9b4b433bd 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -625,13 +625,13 @@ QV4::ReturnedValue QObjectWrapper::get(const Managed *m, String *name, bool *has return that->getQmlProperty(qmlContext, name, IgnoreRevision, hasProperty, /*includeImports*/ true); } -void QObjectWrapper::put(Managed *m, String *name, const Value &value) +bool QObjectWrapper::put(Managed *m, String *name, const Value &value) { QObjectWrapper *that = static_cast<QObjectWrapper*>(m); ExecutionEngine *v4 = that->engine(); if (v4->hasException || QQmlData::wasDeleted(that->d()->object())) - return; + return false; QQmlContextData *qmlContext = v4->callingQmlContext(); if (!setQmlProperty(v4, qmlContext, that->d()->object(), name, QV4::QObjectWrapper::IgnoreRevision, value)) { @@ -642,10 +642,13 @@ void QObjectWrapper::put(Managed *m, String *name, const Value &value) QString error = QLatin1String("Cannot assign to non-existent property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); + return false; } else { - QV4::Object::put(m, name, value); + return QV4::Object::put(m, name, value); } } + + return true; } PropertyAttributes QObjectWrapper::query(const Managed *m, String *name) @@ -673,18 +676,24 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name QObjectWrapper *that = static_cast<QObjectWrapper*>(m); - if (that->d()->object()) { - const QMetaObject *mo = that->d()->object()->metaObject(); + QObject *thatObject = that->d()->object(); + if (thatObject && !QQmlData::wasDeleted(thatObject)) { + const QMetaObject *mo = thatObject->metaObject(); // These indices don't apply to gadgets, so don't block them. const bool preventDestruction = mo->superClass() || mo == &QObject::staticMetaObject; const int propertyCount = mo->propertyCount(); if (it->arrayIndex < static_cast<uint>(propertyCount)) { - Scope scope(that->engine()); - ScopedString propName(scope, that->engine()->newString(QString::fromUtf8(mo->property(it->arrayIndex).name()))); + ExecutionEngine *thatEngine = that->engine(); + Scope scope(thatEngine); + const QMetaProperty property = mo->property(it->arrayIndex); + ScopedString propName(scope, thatEngine->newString(QString::fromUtf8(property.name()))); name->setM(propName->d()); ++it->arrayIndex; *attributes = QV4::Attr_Data; - p->value = that->get(propName); + + QQmlPropertyData local; + local.load(property); + p->value = that->getProperty(thatEngine, thatObject, &local); return; } const int methodCount = mo->methodCount(); @@ -694,11 +703,15 @@ void QObjectWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name ++it->arrayIndex; if (method.access() == QMetaMethod::Private || (preventDestruction && (index == deleteLaterIdx || index == destroyedIdx1 || index == destroyedIdx2))) continue; - Scope scope(that->engine()); - ScopedString methodName(scope, that->engine()->newString(QString::fromUtf8(method.name()))); + ExecutionEngine *thatEngine = that->engine(); + Scope scope(thatEngine); + ScopedString methodName(scope, thatEngine->newString(QString::fromUtf8(method.name()))); name->setM(methodName->d()); *attributes = QV4::Attr_Data; - p->value = that->get(methodName); + + QQmlPropertyData local; + local.load(method); + p->value = that->getProperty(thatEngine, thatObject, &local); return; } } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index b09e06cec5..d81ef2a680 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -192,7 +192,7 @@ protected: QQmlPropertyData *findProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, QQmlPropertyData *local) const; static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + 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); diff --git a/src/qml/jsruntime/qv4regexpobject.cpp b/src/qml/jsruntime/qv4regexpobject.cpp index 40682aaa4b..0894d0c25b 100644 --- a/src/qml/jsruntime/qv4regexpobject.cpp +++ b/src/qml/jsruntime/qv4regexpobject.cpp @@ -367,7 +367,7 @@ void RegExpPrototype::method_exec(const BuiltinFunction *, Scope &scope, CallDat RETURN_RESULT(Encode::null()); } - uint* matchOffsets = (uint*)alloca(r->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, r->value()->captureCount() * 2 * sizeof(uint)); const int result = Scoped<RegExp>(scope, r->value())->match(s, offset, matchOffsets); Scoped<RegExpCtor> regExpCtor(scope, scope.engine->regExpCtor()); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 023a739e33..6590054bf3 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -343,35 +343,15 @@ ReturnedValue Runtime::method_deleteName(ExecutionEngine *engine, int nameIndex) return Encode(engine->currentContext->deleteProperty(name)); } -QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &left, const Value &right) +QV4::ReturnedValue Runtime::method_instanceof(ExecutionEngine *engine, const Value &lval, const Value &rval) { - const FunctionObject *function = right.as<FunctionObject>(); - if (!function) - return engine->throwTypeError(); - - Heap::FunctionObject *f = function->d(); - if (function->isBoundFunction()) - f = function->cast<BoundFunction>()->target(); - - const Object *o = left.as<Object>(); - if (!o) - return Encode(false); - Heap::Object *v = o->d(); - - o = f->protoProperty(); - if (!o) - return engine->throwTypeError(); - - while (v) { - v = v->prototype; - - if (!v) - break; - else if (o->d() == v) - return Encode(true); - } + // 11.8.6, 5: rval must be an Object + const Object *rhs = rval.as<Object>(); + if (!rhs) + return engine->throwTypeError(); - return Encode(false); + // 11.8.6, 7: call "HasInstance", which we term instanceOf, and return the result. + return rhs->instanceOf(lval); } QV4::ReturnedValue Runtime::method_in(ExecutionEngine *engine, const Value &left, const Value &right) @@ -1284,12 +1264,16 @@ ReturnedValue Runtime::method_unwindException(ExecutionEngine *engine) */ void Runtime::method_pushWithScope(const Value &o, NoThrowEngine *engine) { - engine->pushContext(engine->currentContext->newWithContext(o.toObject(engine))); + QV4::Value *v = engine->jsAlloca(1); + Heap::Object *withObject = o.toObject(engine); + *v = withObject; + engine->pushContext(engine->currentContext->newWithContext(withObject)); Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); } void Runtime::method_pushCatchScope(NoThrowEngine *engine, int exceptionVarNameIndex) { + engine->jsAlloca(1); // keep this symmetric with pushWithScope ExecutionContext *c = engine->currentContext; engine->pushContext(c->newCatchContext(c->d()->compilationUnit->runtimeStrings[exceptionVarNameIndex], engine->catchException(0))); Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); @@ -1299,7 +1283,7 @@ void Runtime::method_popScope(NoThrowEngine *engine) { Q_ASSERT(engine->jsStackTop == engine->currentContext + 2); engine->popContext(); - engine->jsStackTop -= 2; + engine->jsStackTop -= 3; } void Runtime::method_declareVar(ExecutionEngine *engine, bool deletable, int nameIndex) diff --git a/src/qml/jsruntime/qv4sequenceobject.cpp b/src/qml/jsruntime/qv4sequenceobject.cpp index 8ce10e326d..6d3110771e 100644 --- a/src/qml/jsruntime/qv4sequenceobject.cpp +++ b/src/qml/jsruntime/qv4sequenceobject.cpp @@ -274,20 +274,20 @@ public: return Encode::undefined(); } - void containerPutIndexed(uint index, const QV4::Value &value) + bool containerPutIndexed(uint index, const QV4::Value &value) { if (internalClass()->engine->hasException) - return; + return false; /* Qt containers have int (rather than uint) allowable indexes. */ if (index > INT_MAX) { generateWarning(engine(), QLatin1String("Index out of range during indexed set")); - return; + return false; } if (d()->isReference) { if (!d()->object) - return; + return false; loadReference(); } @@ -313,6 +313,7 @@ public: if (d()->isReference) storeReference(); + return true; } QV4::PropertyAttributes containerQueryIndexed(uint index) const @@ -540,8 +541,8 @@ public: static QV4::ReturnedValue getIndexed(const QV4::Managed *that, uint index, bool *hasProperty) { return static_cast<const QQmlSequence<Container> *>(that)->containerGetIndexed(index, hasProperty); } - static void putIndexed(Managed *that, uint index, const QV4::Value &value) - { static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } + static bool putIndexed(Managed *that, uint index, const QV4::Value &value) + { return static_cast<QQmlSequence<Container> *>(that)->containerPutIndexed(index, value); } static QV4::PropertyAttributes queryIndexed(const QV4::Managed *that, uint index) { return static_cast<const QQmlSequence<Container> *>(that)->containerQueryIndexed(index); } static bool deleteIndexedProperty(QV4::Managed *that, uint index) diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp index 3c6a24e035..72be11eca0 100644 --- a/src/qml/jsruntime/qv4stringobject.cpp +++ b/src/qml/jsruntime/qv4stringobject.cpp @@ -200,6 +200,7 @@ void StringPrototype::init(ExecutionEngine *engine, Object *ctor) defineDefaultProperty(QStringLiteral("lastIndexOf"), method_lastIndexOf, 1); defineDefaultProperty(QStringLiteral("localeCompare"), method_localeCompare, 1); defineDefaultProperty(QStringLiteral("match"), method_match, 1); + defineDefaultProperty(QStringLiteral("repeat"), method_repeat, 1); defineDefaultProperty(QStringLiteral("replace"), method_replace, 2); defineDefaultProperty(QStringLiteral("search"), method_search, 1); defineDefaultProperty(QStringLiteral("slice"), method_slice, 2); @@ -458,6 +459,21 @@ void StringPrototype::method_match(const BuiltinFunction *, Scope &scope, CallDa scope.result = a; } +void StringPrototype::method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + QString value = getThisString(scope, callData); + CHECK_EXCEPTION(); + + double repeats = callData->args[0].toInteger(); + + if (repeats < 0 || qIsInf(repeats)) { + scope.result = scope.engine->throwRangeError(QLatin1String("Invalid count value")); + return; + } + + scope.result = scope.engine->newString(value.repeated(int(repeats))); +} + static void appendReplacementString(QString *result, const QString &input, const QString& replaceValue, uint* matchOffsets, int captureCount) { result->reserve(result->length() + replaceValue.length()); @@ -634,7 +650,7 @@ void StringPrototype::method_search(const BuiltinFunction *, Scope &scope, CallD Q_ASSERT(regExp); } Scoped<RegExp> re(scope, regExp->value()); - uint* matchOffsets = (uint*)alloca(regExp->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, regExp->value()->captureCount() * 2 * sizeof(uint)); uint result = re->match(string, /*offset*/0, matchOffsets); if (result == JSC::Yarr::offsetNoMatch) scope.result = Encode(-1); @@ -705,7 +721,7 @@ void StringPrototype::method_split(const BuiltinFunction *, Scope &scope, CallDa ScopedString s(scope); if (re) { uint offset = 0; - uint* matchOffsets = (uint*)alloca(re->value()->captureCount() * 2 * sizeof(uint)); + Q_ALLOCA_VAR(uint, matchOffsets, re->value()->captureCount() * 2 * sizeof(uint)); while (true) { Scoped<RegExp> regexp(scope, re->value()); uint result = regexp->match(text, offset, matchOffsets); diff --git a/src/qml/jsruntime/qv4stringobject_p.h b/src/qml/jsruntime/qv4stringobject_p.h index 0ee7a6ece9..aed3bc1e28 100644 --- a/src/qml/jsruntime/qv4stringobject_p.h +++ b/src/qml/jsruntime/qv4stringobject_p.h @@ -121,6 +121,7 @@ struct StringPrototype: StringObject static void method_lastIndexOf(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_localeCompare(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_match(const BuiltinFunction *, Scope &scope, CallData *callData); + static void method_repeat(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_replace(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_search(const BuiltinFunction *, Scope &scope, CallData *callData); static void method_slice(const BuiltinFunction *, Scope &scope, CallData *callData); diff --git a/src/qml/jsruntime/qv4typedarray.cpp b/src/qml/jsruntime/qv4typedarray.cpp index cecd1e6958..5573a2e57f 100644 --- a/src/qml/jsruntime/qv4typedarray.cpp +++ b/src/qml/jsruntime/qv4typedarray.cpp @@ -398,11 +398,11 @@ ReturnedValue TypedArray::getIndexed(const Managed *m, uint index, bool *hasProp return a->d()->type->read(a->d()->buffer->data->data(), byteOffset); } -void TypedArray::putIndexed(Managed *m, uint index, const Value &value) +bool TypedArray::putIndexed(Managed *m, uint index, const Value &value) { ExecutionEngine *v4 = static_cast<Object *>(m)->engine(); if (v4->hasException) - return; + return false; Scope scope(v4); Scoped<TypedArray> a(scope, static_cast<TypedArray *>(m)); @@ -413,11 +413,12 @@ void TypedArray::putIndexed(Managed *m, uint index, const Value &value) goto reject; a->d()->type->write(scope.engine, a->d()->buffer->data->data(), byteOffset, value); - return; + return true; reject: if (scope.engine->current->strictMode) scope.engine->throwTypeError(); + return false; } void TypedArrayPrototype::init(ExecutionEngine *engine, TypedArrayCtor *ctor) diff --git a/src/qml/jsruntime/qv4typedarray_p.h b/src/qml/jsruntime/qv4typedarray_p.h index eefed2db4b..fbf13c9815 100644 --- a/src/qml/jsruntime/qv4typedarray_p.h +++ b/src/qml/jsruntime/qv4typedarray_p.h @@ -133,7 +133,7 @@ struct Q_QML_PRIVATE_EXPORT TypedArray : Object static void markObjects(Heap::Base *that, ExecutionEngine *e); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void putIndexed(Managed *m, uint index, const Value &value); + static bool putIndexed(Managed *m, uint index, const Value &value); }; struct TypedArrayCtor: FunctionObject diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 816b8fb11b..4ff0565f9b 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -254,34 +254,47 @@ public: Q_ASSERT(isDouble()); return Double_Type; } -#ifndef QV4_USE_64_BIT_VALUE_ENCODING + // Shared between 32-bit and 64-bit encoding + enum { + Tag_Shift = 32 + }; + + // Used only by 64-bit encoding + static const quint64 NaNEncodeMask = 0xfffc000000000000ll; + enum { + IsDouble_Shift = 64-14, + IsManagedOrUndefined_Shift = 64-15, + IsIntegerConvertible_Shift = 64-16, + IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, + Managed_Type_Internal_64 = 0 + }; + static const quint64 Immediate_Mask_64 = 0x00020000u; // bit 49 + + // Used only by 32-bit encoding enum Masks { SilentNaNBit = 0x00040000, - NaN_Mask = 0x7ff80000, NotDouble_Mask = 0x7ffa0000, - Immediate_Mask = NotDouble_Mask | 0x00020000u | SilentNaNBit, - Tag_Shift = 32 }; + static const quint64 Immediate_Mask_32 = NotDouble_Mask | 0x00020000u | SilentNaNBit; enum { - Managed_Type_Internal = NotDouble_Mask + Managed_Type_Internal_32 = NotDouble_Mask }; -#else - static const quint64 NaNEncodeMask = 0xfffc000000000000ll; - static const quint64 Immediate_Mask = 0x00020000u; // bit 49 - enum Masks { - NaN_Mask = 0x7ff80000, +#ifdef QV4_USE_64_BIT_VALUE_ENCODING + enum { + Managed_Type_Internal = Managed_Type_Internal_64 }; + static const quint64 Immediate_Mask = Immediate_Mask_64; +#else enum { - IsDouble_Shift = 64-14, - IsManagedOrUndefined_Shift = 64-15, - IsIntegerConvertible_Shift = 64-16, - Tag_Shift = 32, - IsDoubleTag_Shift = IsDouble_Shift - Tag_Shift, - Managed_Type_Internal = 0 + Managed_Type_Internal = Managed_Type_Internal_32 }; + static const quint64 Immediate_Mask = Immediate_Mask_32; #endif + enum { + NaN_Mask = 0x7ff80000, + }; enum ValueTypeInternal { Empty_Type_Internal = Immediate_Mask | 0, ConvertibleToInt = Immediate_Mask | 0x10000u, // bit 48 @@ -544,7 +557,7 @@ inline bool Value::asArrayIndex(uint &idx) const } double d = doubleValue(); idx = (uint)d; - return (idx == d); + return (idx == d && idx != UINT_MAX); } #endif diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index b9183313cd..be2772c23f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -402,7 +402,7 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code } } - QV4::Value **scopes = static_cast<QV4::Value **>(alloca(sizeof(QV4::Value *)*(2 + 2*scopeDepth))); + Q_ALLOCA_VAR(QV4::Value*, scopes, sizeof(QV4::Value *)*(2 + 2*scopeDepth)); { scopes[0] = const_cast<QV4::Value *>(context->d()->compilationUnit->constants); // stack gets setup in push instruction @@ -957,8 +957,6 @@ QV4::ReturnedValue VME::run(ExecutionEngine *engine, const uchar *code return QV4::Encode::undefined(); code = exceptionHandler; } - - } #ifdef MOTH_THREADED_INTERPRETER diff --git a/src/qml/memory/qv4mm.cpp b/src/qml/memory/qv4mm.cpp index 6330ef6038..a829e902fb 100644 --- a/src/qml/memory/qv4mm.cpp +++ b/src/qml/memory/qv4mm.cpp @@ -328,40 +328,65 @@ void Chunk::freeAll() void Chunk::sortIntoBins(HeapItem **bins, uint nBins) { +// qDebug() << "sortIntoBins:"; HeapItem *base = realBase(); #if QT_POINTER_SIZE == 8 const int start = 0; #else const int start = 1; #endif +#ifndef QT_NO_DEBUG + uint freeSlots = 0; + uint allocatedSlots = 0; +#endif for (int i = start; i < EntriesInBitmap; ++i) { quintptr usedSlots = (objectBitmap[i]|extendsBitmap[i]); #if QT_POINTER_SIZE == 8 if (!i) usedSlots |= (static_cast<quintptr>(1) << (HeaderSize/SlotSize)) - 1; #endif - uint index = qCountTrailingZeroBits(usedSlots + 1); - if (index == Bits) - continue; - uint freeStart = i*Bits + index; - usedSlots &= ~((static_cast<quintptr>(1) << index) - 1); - while (i < EntriesInBitmap && !usedSlots) { - ++i; - usedSlots = (objectBitmap[i]|extendsBitmap[i]); +#ifndef QT_NO_DEBUG + allocatedSlots += qPopulationCount(usedSlots); +// qDebug() << hex << " i=" << i << "used=" << usedSlots; +#endif + while (1) { + uint index = qCountTrailingZeroBits(usedSlots + 1); + if (index == Bits) + break; + uint freeStart = i*Bits + index; + usedSlots &= ~((static_cast<quintptr>(1) << index) - 1); + while (!usedSlots) { + ++i; + if (i == EntriesInBitmap) { + usedSlots = (quintptr)-1; + break; + } + usedSlots = (objectBitmap[i]|extendsBitmap[i]); +#ifndef QT_NO_DEBUG + allocatedSlots += qPopulationCount(usedSlots); +// qDebug() << hex << " i=" << i << "used=" << usedSlots; +#endif + } + HeapItem *freeItem = base + freeStart; + + index = qCountTrailingZeroBits(usedSlots); + usedSlots |= (quintptr(1) << index) - 1; + uint freeEnd = i*Bits + index; + uint nSlots = freeEnd - freeStart; +#ifndef QT_NO_DEBUG +// qDebug() << hex << " got free slots from" << freeStart << "to" << freeEnd << "n=" << nSlots << "usedSlots=" << usedSlots; + freeSlots += nSlots; +#endif + Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots); + freeItem->freeData.availableSlots = nSlots; + uint bin = qMin(nBins - 1, nSlots); + freeItem->freeData.next = bins[bin]; + bins[bin] = freeItem; } - if (i == EntriesInBitmap) - usedSlots = 1; - HeapItem *freeItem = base + freeStart; - - uint freeEnd = i*Bits + qCountTrailingZeroBits(usedSlots); - uint nSlots = freeEnd - freeStart; - Q_ASSERT(freeEnd > freeStart && freeEnd <= NumSlots); - freeItem->freeData.availableSlots = nSlots; - uint bin = qMin(nBins - 1, nSlots); - freeItem->freeData.next = bins[bin]; - bins[bin] = freeItem; - // DEBUG << "binnig item" << freeItem << nSlots << bin << freeItem->freeData.availableSlots; } +#ifndef QT_NO_DEBUG + Q_ASSERT(freeSlots + allocatedSlots == (EntriesInBitmap - start) * 8 * sizeof(quintptr)); +#endif } @@ -427,28 +452,7 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { goto done; } } -#if 0 - for (uint b = bin + 1; b < NumBins - 1; ++b) { - if ((m = freeBins[b])) { - Q_ASSERT(binForSlots(m->freeData.availableSlots) == b); - freeBins[b] = m->freeData.next; - // DEBUG << "looking for empty bin" << bin << "size" << size << "found" << b; - uint remainingSlots = m->freeData.availableSlots - slotsRequired; - // DEBUG << "found free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; - if (remainingSlots < 2) { - // avoid too much fragmentation and rather mark the memory as used - size += remainingSlots*Chunk::SlotSize; - goto done; - } - HeapItem *remainder = m + slotsRequired; - remainder->freeData.availableSlots = remainingSlots; - uint binForRemainder = binForSlots(remainingSlots); - remainder->freeData.next = freeBins[binForRemainder]; - freeBins[binForRemainder] = remainder; - goto done; - } - } -#endif + if (nFree >= slotsRequired) { // use bump allocation Q_ASSERT(nextFree); @@ -467,13 +471,11 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { size_t remainingSlots = m->freeData.availableSlots - slotsRequired; // DEBUG << "found large free slots of size" << m->freeData.availableSlots << m << "remaining" << remainingSlots; - if (remainingSlots < 2) { - // avoid too much fragmentation and rather mark the memory as used - size += remainingSlots*Chunk::SlotSize; + if (remainingSlots == 0) goto done; - } + HeapItem *remainder = m + slotsRequired; - if (remainingSlots >= 2*NumBins) { + if (remainingSlots > nFree) { if (nFree) { size_t bin = binForSlots(nFree); nextFree->freeData.next = freeBins[bin]; @@ -493,6 +495,24 @@ HeapItem *BlockAllocator::allocate(size_t size, bool forceAllocation) { last = &m->freeData.next; } + if (slotsRequired < NumBins - 1) { + // check if we can split up another slot + for (size_t i = slotsRequired + 1; i < NumBins - 1; ++i) { + m = freeBins[i]; + if (m) { + freeBins[i] = m->freeData.next; // take it out of the list +// qDebug() << "got item" << slotsRequired << "from slot" << i; + size_t remainingSlots = i - slotsRequired; + Q_ASSERT(remainingSlots < NumBins - 1); + HeapItem *remainder = m + slotsRequired; + remainder->freeData.availableSlots = remainingSlots; + remainder->freeData.next = freeBins[remainingSlots]; + freeBins[remainingSlots] = remainder; + goto done; + } + } + } + if (!m) { if (!forceAllocation) return 0; @@ -622,14 +642,24 @@ MemoryManager::MemoryManager(ExecutionEngine *engine) #endif } +#ifndef QT_NO_DEBUG +static size_t lastAllocRequestedSlots = 0; +#endif + Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) { - if (aggressiveGC) + const size_t stringSize = align(sizeof(Heap::String)); +#ifndef QT_NO_DEBUG + lastAllocRequestedSlots = stringSize >> Chunk::SlotSizeShift; +#endif + + bool didGCRun = false; + if (aggressiveGC) { runGC(); + didGCRun = true; + } - const size_t stringSize = align(sizeof(Heap::String)); unmanagedHeapSize += unmanagedSize; - bool didGCRun = false; if (unmanagedHeapSize > unmanagedHeapSizeGCLimit) { runGC(); @@ -655,8 +685,15 @@ Heap::Base *MemoryManager::allocString(std::size_t unmanagedSize) Heap::Base *MemoryManager::allocData(std::size_t size) { - if (aggressiveGC) +#ifndef QT_NO_DEBUG + lastAllocRequestedSlots = size >> Chunk::SlotSizeShift; +#endif + + bool didRunGC = false; + if (aggressiveGC) { runGC(); + didRunGC = true; + } #ifdef DETAILED_MM_STATS willAllocate(size); #endif // DETAILED_MM_STATS @@ -671,7 +708,7 @@ Heap::Base *MemoryManager::allocData(std::size_t size) HeapItem *m = blockAllocator.allocate(size); if (!m) { - if (shouldRunGC()) + if (!didRunGC && shouldRunGC()) runGC(); m = blockAllocator.allocate(size, true); } @@ -817,6 +854,26 @@ bool MemoryManager::shouldRunGC() const return false; } +size_t dumpBins(BlockAllocator *b, bool printOutput = true) +{ + size_t totalFragmentedSlots = 0; + if (printOutput) + qDebug() << "Fragmentation map:"; + for (uint i = 0; i < BlockAllocator::NumBins; ++i) { + uint nEntries = 0; + HeapItem *h = b->freeBins[i]; + while (h) { + ++nEntries; + totalFragmentedSlots += h->freeData.availableSlots; + h = h->freeData.next; + } + if (printOutput) + qDebug() << " number of entries in slot" << i << ":" << nEntries; + } + if (printOutput) + qDebug() << " total mem in bins" << totalFragmentedSlots*Chunk::SlotSize; + return totalFragmentedSlots*Chunk::SlotSize; +} void MemoryManager::runGC() { @@ -833,31 +890,56 @@ void MemoryManager::runGC() sweep(); // DEBUG << "RUN GC: allocated:" << allocator.allocatedMem() << "used before" << oldUsed << "used now" << allocator.usedMem(); } else { + bool triggeredByUnmanagedHeap = (unmanagedHeapSize > unmanagedHeapSizeGCLimit); + size_t oldUnmanagedSize = unmanagedHeapSize; const size_t totalMem = getAllocatedMem(); + const size_t usedBefore = getUsedMem(); + const size_t largeItemsBefore = getLargeItemsMem(); + + qDebug() << "========== GC =========="; +#ifndef QT_NO_DEBUG + qDebug() << " Triggered by alloc request of" << lastAllocRequestedSlots << "slots."; +#endif + qDebug() << "Allocated" << totalMem << "bytes in" << blockAllocator.chunks.size() << "chunks"; + qDebug() << "Fragmented memory before GC" << (totalMem - usedBefore); + dumpBins(&blockAllocator); QElapsedTimer t; t.start(); mark(); qint64 markTime = t.restart(); - const size_t usedBefore = getUsedMem(); - const size_t largeItemsBefore = getLargeItemsMem(); sweep(); const size_t usedAfter = getUsedMem(); const size_t largeItemsAfter = getLargeItemsMem(); qint64 sweepTime = t.elapsed(); - qDebug() << "========== GC =========="; + if (triggeredByUnmanagedHeap) { + qDebug() << "triggered by unmanaged heap:"; + qDebug() << " old unmanaged heap size:" << oldUnmanagedSize; + qDebug() << " new unmanaged heap:" << unmanagedHeapSize; + qDebug() << " unmanaged heap limit:" << unmanagedHeapSizeGCLimit; + } + size_t memInBins = dumpBins(&blockAllocator); qDebug() << "Marked object in" << markTime << "ms."; qDebug() << "Sweeped object in" << sweepTime << "ms."; - qDebug() << "Allocated" << totalMem << "bytes"; qDebug() << "Used memory before GC:" << usedBefore; qDebug() << "Used memory after GC:" << usedAfter; qDebug() << "Freed up bytes:" << (usedBefore - usedAfter); - qDebug() << "Large item memory before GC:" << largeItemsBefore; - qDebug() << "Large item memory after GC:" << largeItemsAfter; - qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); + size_t lost = blockAllocator.allocatedMem() - memInBins - usedAfter; + if (lost) + qDebug() << "!!!!!!!!!!!!!!!!!!!!! LOST MEM:" << lost << "!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"; + if (largeItemsBefore || largeItemsAfter) { + qDebug() << "Large item memory before GC:" << largeItemsBefore; + qDebug() << "Large item memory after GC:" << largeItemsAfter; + qDebug() << "Large item memory freed up:" << (largeItemsBefore - largeItemsAfter); + } qDebug() << "======== End GC ========"; } + + if (aggressiveGC) { + // ensure we don't 'loose' any memory + Q_ASSERT(blockAllocator.allocatedMem() == getUsedMem() + dumpBins(&blockAllocator, false)); + } } size_t MemoryManager::getUsedMem() const diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index 9cd212015e..ca84e0c157 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -71,7 +71,7 @@ %token T_VAR "var" T_VOID "void" T_WHILE "while" %token T_WITH "with" T_XOR "^" T_XOR_EQ "^=" %token T_NULL "null" T_TRUE "true" T_FALSE "false" -%token T_CONST "const" +%token T_CONST "const" T_LET "let" %token T_DEBUGGER "debugger" %token T_RESERVED_WORD "reserved word" %token T_MULTILINE_STRING_LITERAL "multiline string literal" @@ -1622,6 +1622,7 @@ ReservedIdentifier: T_VAR ; ReservedIdentifier: T_VOID ; ReservedIdentifier: T_WHILE ; ReservedIdentifier: T_CONST ; +ReservedIdentifier: T_LET ; ReservedIdentifier: T_DEBUGGER ; ReservedIdentifier: T_RESERVED_WORD ; ReservedIdentifier: T_WITH ; @@ -2486,14 +2487,26 @@ VariableStatement: VariableDeclarationKind VariableDeclarationList T_AUTOMATIC_S VariableStatement: VariableDeclarationKind VariableDeclarationList T_SEMICOLON ; /. case $rule_number: { - AST::VariableStatement *node = new (pool) AST::VariableStatement( - sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + if (sym(1).ival == T_LET) + s = AST::VariableDeclaration::BlockScope; + else if (sym(1).ival == T_CONST) + s = AST::VariableDeclaration::ReadOnlyBlockScope; + + AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); node->declarationKindToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; ./ +VariableDeclarationKind: T_LET ; +/. +case $rule_number: { + sym(1).ival = T_LET; +} break; +./ + VariableDeclarationKind: T_CONST ; /. case $rule_number: { @@ -2542,7 +2555,8 @@ case $rule_number: { VariableDeclaration: JsIdentifier InitialiserOpt ; /. case $rule_number: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; @@ -2551,7 +2565,8 @@ case $rule_number: { VariableDeclarationNotIn: JsIdentifier InitialiserNotInOpt ; /. case $rule_number: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; @@ -2677,8 +2692,9 @@ case $rule_number: { IterationStatement: T_FOR T_LPAREN T_VAR VariableDeclarationListNotIn T_SEMICOLON ExpressionOpt T_SEMICOLON ExpressionOpt T_RPAREN Statement ; /. case $rule_number: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(4).VariableDeclarationList->finish(s), sym(6).Expression, sym(8).Expression, sym(10).Statement); node->forToken = loc(1); node->lparenToken = loc(2); diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 9b06bf3d31..0de419d697 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -1315,10 +1315,18 @@ class QML_PARSER_EXPORT VariableDeclaration: public Node public: QQMLJS_DECLARE_AST_NODE(VariableDeclaration) - VariableDeclaration(const QStringRef &n, ExpressionNode *e): - name (n), expression (e), readOnly(false) + enum VariableScope { + FunctionScope, + BlockScope, // let + ReadOnlyBlockScope // const + }; + + VariableDeclaration(const QStringRef &n, ExpressionNode *e, VariableScope s): + name (n), expression (e), scope(s) { kind = K; } + bool isLexicallyScoped() const { return scope != FunctionScope; } + void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override @@ -1330,8 +1338,8 @@ public: // attributes QStringRef name; ExpressionNode *expression; - bool readOnly; SourceLocation identifierToken; + VariableScope scope; }; class QML_PARSER_EXPORT VariableDeclarationList: public Node @@ -1363,14 +1371,13 @@ public: return declaration->lastSourceLocation(); } - inline VariableDeclarationList *finish (bool readOnly) + inline VariableDeclarationList *finish(VariableDeclaration::VariableScope s) { VariableDeclarationList *front = next; next = 0; - if (readOnly) { - VariableDeclarationList *vdl; - for (vdl = front; vdl != 0; vdl = vdl->next) - vdl->declaration->readOnly = true; + VariableDeclarationList *vdl; + for (vdl = front; vdl != 0; vdl = vdl->next) { + vdl->declaration->scope = s; } return front; } diff --git a/src/qml/parser/qqmljsgrammar.cpp b/src/qml/parser/qqmljsgrammar.cpp index b27f4af080..ca5a4bbd85 100644 --- a/src/qml/parser/qqmljsgrammar.cpp +++ b/src/qml/parser/qqmljsgrammar.cpp @@ -51,48 +51,48 @@ const char *const QQmlJSGrammar::spell [] = { "||", "+", "+=", "++", "?", "}", "]", "%", "%=", "return", ")", ";", 0, "*", "*=", "string literal", "property", "signal", "readonly", "switch", "this", "throw", "~", "try", "typeof", "var", "void", "while", "with", "^", - "^=", "null", "true", "false", "const", "debugger", "reserved word", "multiline string literal", "comment", 0, - "public", "import", "pragma", "as", "on", "get", "set", 0, 0, 0, - 0, 0, 0, 0, 0, 0}; + "^=", "null", "true", "false", "const", "let", "debugger", "reserved word", "multiline string literal", "comment", + 0, "public", "import", "pragma", "as", "on", "get", "set", 0, 0, + 0, 0, 0, 0, 0, 0, 0}; const short QQmlJSGrammar::lhs [] = { - 106, 106, 106, 106, 106, 106, 107, 113, 113, 116, - 116, 116, 116, 119, 121, 117, 117, 118, 118, 118, - 118, 118, 118, 118, 118, 122, 123, 115, 114, 126, - 126, 127, 127, 128, 128, 125, 111, 111, 111, 111, - 130, 130, 130, 130, 130, 130, 130, 111, 138, 138, - 138, 138, 139, 139, 140, 140, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 111, 111, 111, 111, - 111, 111, 111, 111, 111, 111, 124, 124, 124, 124, - 124, 124, 124, 143, 143, 143, 143, 143, 143, 143, - 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, - 143, 129, 145, 145, 145, 145, 144, 144, 149, 149, - 149, 147, 147, 150, 150, 150, 150, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 153, 153, - 153, 153, 153, 153, 153, 153, 153, 153, 154, 154, - 120, 120, 120, 120, 120, 157, 157, 158, 158, 158, - 158, 156, 156, 159, 159, 160, 160, 161, 161, 161, - 162, 162, 162, 162, 162, 162, 162, 162, 162, 162, - 163, 163, 163, 163, 164, 164, 164, 165, 165, 165, - 165, 166, 166, 166, 166, 166, 166, 166, 167, 167, - 167, 167, 167, 167, 168, 168, 168, 168, 168, 169, - 169, 169, 169, 169, 170, 170, 171, 171, 172, 172, - 173, 173, 174, 174, 175, 175, 176, 176, 177, 177, - 178, 178, 179, 179, 180, 180, 181, 181, 148, 148, - 182, 182, 183, 183, 183, 183, 183, 183, 183, 183, - 183, 183, 183, 183, 109, 109, 184, 184, 185, 185, - 186, 186, 108, 108, 108, 108, 108, 108, 108, 108, - 108, 108, 108, 108, 108, 108, 108, 131, 195, 195, - 194, 194, 142, 142, 196, 196, 197, 197, 199, 199, - 198, 200, 203, 201, 201, 204, 202, 202, 132, 133, - 133, 134, 134, 187, 187, 187, 187, 187, 187, 187, - 187, 188, 188, 188, 188, 189, 189, 189, 189, 190, - 190, 135, 136, 205, 205, 208, 208, 206, 206, 209, - 207, 191, 192, 192, 137, 137, 137, 210, 211, 193, - 193, 212, 141, 155, 155, 213, 213, 152, 152, 151, - 151, 214, 112, 112, 215, 215, 110, 110, 146, 146, - 216}; + 107, 107, 107, 107, 107, 107, 108, 114, 114, 117, + 117, 117, 117, 120, 122, 118, 118, 119, 119, 119, + 119, 119, 119, 119, 119, 123, 124, 116, 115, 127, + 127, 128, 128, 129, 129, 126, 112, 112, 112, 112, + 131, 131, 131, 131, 131, 131, 131, 112, 139, 139, + 139, 139, 140, 140, 141, 141, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 112, 112, 112, 112, + 112, 112, 112, 112, 112, 112, 125, 125, 125, 125, + 125, 125, 125, 144, 144, 144, 144, 144, 144, 144, + 144, 144, 144, 144, 144, 144, 144, 144, 144, 144, + 144, 130, 146, 146, 146, 146, 145, 145, 150, 150, + 150, 148, 148, 151, 151, 151, 151, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 154, + 154, 154, 154, 154, 154, 154, 154, 154, 154, 155, + 155, 121, 121, 121, 121, 121, 158, 158, 159, 159, + 159, 159, 157, 157, 160, 160, 161, 161, 162, 162, + 162, 163, 163, 163, 163, 163, 163, 163, 163, 163, + 163, 164, 164, 164, 164, 165, 165, 165, 166, 166, + 166, 166, 167, 167, 167, 167, 167, 167, 167, 168, + 168, 168, 168, 168, 168, 169, 169, 169, 169, 169, + 170, 170, 170, 170, 170, 171, 171, 172, 172, 173, + 173, 174, 174, 175, 175, 176, 176, 177, 177, 178, + 178, 179, 179, 180, 180, 181, 181, 182, 182, 149, + 149, 183, 183, 184, 184, 184, 184, 184, 184, 184, + 184, 184, 184, 184, 184, 110, 110, 185, 185, 186, + 186, 187, 187, 109, 109, 109, 109, 109, 109, 109, + 109, 109, 109, 109, 109, 109, 109, 109, 132, 196, + 196, 195, 195, 143, 143, 197, 197, 197, 198, 198, + 200, 200, 199, 201, 204, 202, 202, 205, 203, 203, + 133, 134, 134, 135, 135, 188, 188, 188, 188, 188, + 188, 188, 188, 189, 189, 189, 189, 190, 190, 190, + 190, 191, 191, 136, 137, 206, 206, 209, 209, 207, + 207, 210, 208, 192, 193, 193, 138, 138, 138, 211, + 212, 194, 194, 213, 142, 156, 156, 214, 214, 153, + 153, 152, 152, 215, 113, 113, 216, 216, 111, 111, + 147, 147, 217}; const short QQmlJSGrammar::rhs [] = { 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, @@ -110,777 +110,798 @@ const short QQmlJSGrammar::rhs [] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 4, 3, 5, 1, 2, 4, 4, 4, - 3, 0, 1, 1, 3, 1, 1, 1, 2, 2, - 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, - 1, 3, 3, 3, 1, 3, 3, 1, 3, 3, - 3, 1, 3, 3, 3, 3, 3, 3, 1, 3, - 3, 3, 3, 3, 1, 3, 3, 3, 3, 1, - 3, 3, 3, 3, 1, 3, 1, 3, 1, 3, - 1, 3, 1, 3, 1, 3, 1, 3, 1, 3, - 1, 3, 1, 3, 1, 5, 1, 5, 1, 3, - 1, 3, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 3, 0, 1, 1, 3, - 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 3, 1, 2, - 0, 1, 3, 3, 1, 1, 1, 3, 1, 3, - 2, 2, 2, 0, 1, 2, 0, 1, 1, 2, - 2, 7, 5, 7, 7, 7, 5, 9, 10, 7, - 8, 2, 2, 3, 3, 2, 2, 3, 3, 3, - 3, 5, 5, 3, 5, 1, 2, 0, 1, 4, - 3, 3, 3, 3, 3, 3, 4, 5, 2, 2, - 2, 1, 8, 8, 7, 1, 3, 0, 1, 0, - 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, - 2}; + 1, 1, 1, 4, 3, 5, 1, 2, 4, 4, + 4, 3, 0, 1, 1, 3, 1, 1, 1, 2, + 2, 1, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 1, 3, 3, 3, 1, 3, 3, 1, 3, + 3, 3, 1, 3, 3, 3, 3, 3, 3, 1, + 3, 3, 3, 3, 3, 1, 3, 3, 3, 3, + 1, 3, 3, 3, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 3, 1, 3, 1, + 3, 1, 3, 1, 3, 1, 5, 1, 5, 1, + 3, 1, 3, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 3, 0, 1, 1, + 3, 0, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 3, 1, + 2, 0, 1, 3, 3, 1, 1, 1, 1, 3, + 1, 3, 2, 2, 2, 0, 1, 2, 0, 1, + 1, 2, 2, 7, 5, 7, 7, 7, 5, 9, + 10, 7, 8, 2, 2, 3, 3, 2, 2, 3, + 3, 3, 3, 5, 5, 3, 5, 1, 2, 0, + 1, 4, 3, 3, 3, 3, 3, 3, 4, 5, + 2, 2, 2, 1, 8, 8, 7, 1, 3, 0, + 1, 0, 1, 1, 1, 1, 1, 2, 1, 1, + 0, 1, 2}; const short QQmlJSGrammar::action_default [] = { - 0, 0, 28, 0, 0, 0, 28, 0, 188, 255, - 219, 227, 223, 167, 239, 215, 3, 152, 85, 168, - 231, 235, 156, 185, 166, 171, 151, 205, 192, 0, - 92, 93, 88, 0, 82, 77, 359, 0, 0, 0, + 0, 0, 28, 0, 0, 0, 28, 0, 189, 256, + 220, 228, 224, 168, 240, 216, 3, 153, 85, 169, + 232, 236, 157, 186, 167, 172, 152, 206, 193, 0, + 92, 93, 88, 0, 82, 77, 361, 0, 0, 0, 0, 90, 0, 0, 86, 89, 81, 0, 0, 78, - 80, 83, 79, 91, 84, 0, 87, 0, 0, 181, - 0, 0, 168, 187, 170, 169, 0, 0, 0, 183, - 184, 182, 186, 0, 216, 0, 0, 0, 0, 206, - 0, 0, 0, 0, 0, 0, 196, 0, 0, 0, - 190, 191, 189, 194, 198, 197, 195, 193, 208, 207, - 209, 0, 224, 0, 220, 0, 0, 162, 149, 161, - 150, 118, 119, 120, 145, 121, 146, 122, 123, 124, - 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, - 147, 135, 136, 137, 138, 139, 140, 141, 142, 143, - 144, 148, 0, 0, 160, 256, 163, 0, 164, 0, - 165, 159, 0, 252, 245, 243, 250, 251, 249, 248, - 254, 247, 246, 244, 253, 240, 0, 228, 0, 0, - 232, 0, 0, 236, 0, 0, 162, 154, 0, 153, - 0, 158, 172, 0, 348, 348, 349, 0, 346, 0, - 347, 0, 350, 263, 270, 269, 277, 265, 0, 266, - 0, 351, 0, 358, 267, 268, 85, 273, 271, 355, - 352, 357, 274, 0, 285, 0, 0, 0, 0, 342, - 0, 359, 257, 299, 0, 0, 0, 286, 0, 0, - 275, 276, 0, 264, 272, 300, 301, 0, 348, 0, - 0, 350, 0, 343, 344, 0, 332, 356, 0, 316, - 317, 318, 319, 0, 312, 313, 314, 315, 340, 341, - 0, 0, 0, 0, 0, 304, 305, 306, 261, 259, - 221, 229, 225, 241, 217, 262, 0, 168, 233, 237, - 210, 199, 0, 0, 218, 0, 0, 0, 0, 211, - 0, 0, 0, 0, 0, 203, 201, 204, 202, 200, - 213, 212, 214, 0, 226, 0, 222, 0, 260, 168, - 0, 242, 257, 258, 0, 257, 0, 0, 308, 0, - 0, 0, 310, 0, 230, 0, 0, 234, 0, 0, - 238, 297, 0, 289, 298, 292, 0, 296, 0, 257, - 290, 0, 257, 0, 0, 309, 0, 0, 0, 311, - 0, 0, 0, 303, 0, 302, 85, 112, 360, 0, - 0, 117, 279, 282, 0, 118, 285, 121, 146, 123, - 124, 88, 128, 129, 82, 130, 133, 86, 89, 257, - 83, 91, 136, 84, 138, 87, 140, 141, 286, 143, - 144, 148, 0, 114, 113, 116, 100, 115, 99, 0, - 109, 280, 278, 0, 0, 0, 350, 0, 110, 156, - 157, 162, 0, 155, 0, 320, 321, 0, 348, 0, - 0, 350, 0, 111, 0, 0, 0, 323, 328, 326, - 329, 0, 0, 327, 328, 0, 324, 0, 325, 281, - 331, 0, 281, 330, 0, 333, 334, 0, 281, 335, - 336, 0, 0, 337, 0, 0, 0, 338, 339, 174, - 173, 0, 0, 0, 307, 0, 0, 0, 322, 294, - 287, 0, 295, 291, 0, 293, 283, 0, 284, 288, - 0, 0, 350, 0, 345, 103, 0, 0, 107, 94, - 0, 96, 105, 0, 97, 106, 108, 98, 104, 95, - 0, 101, 178, 176, 180, 177, 175, 179, 353, 6, - 354, 4, 2, 75, 102, 0, 0, 78, 80, 79, - 37, 5, 0, 76, 0, 51, 50, 49, 0, 0, - 51, 0, 0, 0, 52, 0, 67, 68, 0, 65, - 0, 66, 41, 42, 43, 44, 46, 47, 71, 45, - 0, 51, 0, 0, 0, 0, 0, 61, 0, 62, - 0, 0, 32, 0, 0, 72, 33, 0, 36, 34, - 30, 0, 35, 31, 0, 63, 0, 64, 156, 0, - 69, 73, 0, 0, 0, 0, 156, 281, 0, 70, - 85, 118, 285, 121, 146, 123, 124, 88, 128, 129, - 130, 133, 86, 89, 257, 91, 136, 84, 138, 87, - 140, 141, 286, 143, 144, 148, 74, 0, 59, 53, - 60, 54, 0, 0, 0, 0, 56, 0, 57, 58, - 55, 0, 0, 0, 0, 48, 0, 38, 39, 0, - 40, 8, 0, 0, 9, 0, 11, 0, 10, 0, - 1, 27, 15, 14, 26, 13, 12, 29, 7, 0, - 18, 0, 19, 0, 24, 25, 0, 20, 21, 0, - 22, 23, 16, 17, 361}; + 80, 83, 79, 91, 84, 0, 87, 0, 0, 182, + 0, 0, 169, 188, 171, 170, 0, 0, 0, 184, + 185, 183, 187, 0, 217, 0, 0, 0, 0, 207, + 0, 0, 0, 0, 0, 0, 197, 0, 0, 0, + 191, 192, 190, 195, 199, 198, 196, 194, 209, 208, + 210, 0, 225, 0, 221, 0, 0, 163, 150, 162, + 151, 118, 119, 120, 145, 121, 147, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 146, 133, + 134, 148, 135, 136, 137, 138, 139, 140, 141, 142, + 143, 144, 149, 0, 0, 161, 257, 164, 0, 165, + 0, 166, 160, 0, 253, 246, 244, 251, 252, 250, + 249, 255, 248, 247, 245, 254, 241, 0, 229, 0, + 0, 233, 0, 0, 237, 0, 0, 163, 155, 0, + 154, 0, 159, 173, 0, 350, 350, 351, 0, 348, + 0, 349, 0, 352, 264, 271, 270, 278, 266, 0, + 267, 0, 353, 0, 360, 268, 269, 85, 274, 272, + 357, 354, 359, 275, 0, 287, 0, 0, 0, 0, + 344, 0, 361, 286, 258, 301, 0, 0, 0, 288, + 0, 0, 276, 277, 0, 265, 273, 302, 303, 0, + 350, 0, 0, 352, 0, 345, 346, 0, 334, 358, + 0, 318, 319, 320, 321, 0, 314, 315, 316, 317, + 342, 343, 0, 0, 0, 0, 0, 306, 307, 308, + 262, 260, 222, 230, 226, 242, 218, 263, 0, 169, + 234, 238, 211, 200, 0, 0, 219, 0, 0, 0, + 0, 212, 0, 0, 0, 0, 0, 204, 202, 205, + 203, 201, 214, 213, 215, 0, 227, 0, 223, 0, + 261, 169, 0, 243, 258, 259, 0, 258, 0, 0, + 310, 0, 0, 0, 312, 0, 231, 0, 0, 235, + 0, 0, 239, 299, 0, 291, 300, 294, 0, 298, + 0, 258, 292, 0, 258, 0, 0, 311, 0, 0, + 0, 313, 0, 0, 0, 305, 0, 304, 85, 112, + 362, 0, 0, 117, 280, 283, 0, 118, 287, 121, + 147, 123, 124, 88, 128, 129, 82, 130, 286, 133, + 86, 89, 258, 83, 91, 136, 84, 138, 87, 140, + 141, 288, 143, 144, 149, 0, 114, 113, 116, 100, + 115, 99, 0, 109, 281, 279, 0, 0, 0, 352, + 0, 110, 157, 158, 163, 0, 156, 0, 322, 323, + 0, 350, 0, 0, 352, 0, 111, 0, 0, 0, + 325, 330, 328, 331, 0, 0, 329, 330, 0, 326, + 0, 327, 282, 333, 0, 282, 332, 0, 335, 336, + 0, 282, 337, 338, 0, 0, 339, 0, 0, 0, + 340, 341, 175, 174, 0, 0, 0, 309, 0, 0, + 0, 324, 296, 289, 0, 297, 293, 0, 295, 284, + 0, 285, 290, 0, 0, 352, 0, 347, 103, 0, + 0, 107, 94, 0, 96, 105, 0, 97, 106, 108, + 98, 104, 95, 0, 101, 179, 177, 181, 178, 176, + 180, 355, 6, 356, 4, 2, 75, 102, 0, 0, + 78, 80, 79, 37, 5, 0, 76, 0, 51, 50, + 49, 0, 0, 51, 0, 0, 0, 52, 0, 67, + 68, 0, 65, 0, 66, 41, 42, 43, 44, 46, + 47, 71, 45, 0, 51, 0, 0, 0, 0, 0, + 61, 0, 62, 0, 0, 32, 0, 0, 72, 33, + 0, 36, 34, 30, 0, 35, 31, 0, 63, 0, + 64, 157, 0, 69, 73, 0, 0, 0, 0, 157, + 282, 0, 70, 85, 118, 287, 121, 147, 123, 124, + 88, 128, 129, 130, 286, 133, 86, 89, 258, 91, + 136, 84, 138, 87, 140, 141, 288, 143, 144, 149, + 74, 0, 59, 53, 60, 54, 0, 0, 0, 0, + 56, 0, 57, 58, 55, 0, 0, 0, 0, 48, + 0, 38, 39, 0, 40, 8, 0, 0, 9, 0, + 11, 0, 10, 0, 1, 27, 15, 14, 26, 13, + 12, 29, 7, 0, 18, 0, 19, 0, 24, 25, + 0, 20, 21, 0, 22, 23, 16, 17, 363}; const short QQmlJSGrammar::goto_default [] = { - 7, 650, 211, 198, 209, 521, 509, 645, 658, 508, - 644, 648, 646, 654, 22, 651, 649, 647, 18, 520, - 571, 561, 568, 563, 548, 193, 197, 199, 204, 234, - 212, 231, 552, 622, 621, 203, 233, 26, 487, 486, - 359, 358, 9, 357, 360, 202, 480, 361, 109, 17, - 147, 24, 13, 146, 19, 25, 59, 23, 8, 28, - 27, 280, 15, 274, 10, 270, 12, 272, 11, 271, - 20, 278, 21, 279, 14, 273, 269, 310, 414, 275, - 276, 205, 195, 194, 208, 207, 230, 196, 364, 363, - 232, 471, 470, 332, 333, 473, 335, 472, 334, 427, - 431, 434, 430, 429, 449, 450, 200, 186, 201, 210, + 7, 654, 212, 199, 210, 524, 512, 649, 662, 511, + 648, 652, 650, 658, 22, 655, 653, 651, 18, 523, + 574, 564, 571, 566, 551, 194, 198, 200, 205, 236, + 213, 233, 555, 626, 625, 204, 235, 26, 490, 489, + 361, 360, 9, 359, 362, 203, 483, 363, 109, 17, + 148, 24, 13, 147, 19, 25, 59, 23, 8, 28, + 27, 282, 15, 276, 10, 272, 12, 274, 11, 273, + 20, 280, 21, 281, 14, 275, 271, 312, 417, 277, + 278, 206, 196, 195, 209, 208, 232, 197, 366, 365, + 234, 474, 473, 334, 335, 476, 337, 475, 336, 430, + 434, 437, 433, 432, 452, 453, 201, 187, 202, 211, 0}; const short QQmlJSGrammar::action_index [] = { - 246, 1285, 2768, 2768, 2666, 998, 98, 198, 86, -106, - 26, -15, -39, 234, -106, 314, 54, -106, -106, 714, - 89, 151, 212, 219, -106, -106, -106, 412, 279, 1285, - -106, -106, -106, 525, -106, -106, 2360, 1675, 1285, 1285, - 1285, -106, 902, 1285, -106, -106, -106, 1285, 1285, -106, - -106, -106, -106, -106, -106, 1285, -106, 1285, 1285, -106, - 1285, 1285, 103, 197, -106, -106, 1285, 1285, 1285, -106, - -106, -106, 214, 1285, 297, 1285, 1285, 1285, 1285, 392, - 1285, 1285, 1285, 1285, 1285, 1285, 213, 1285, 1285, 1285, - 96, 100, 101, 279, 279, 195, 190, 181, 402, 372, - 382, 1285, -10, 1285, 106, 2258, 1285, 1285, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, -106, -106, -106, -106, - -106, -106, 136, 1285, -106, -106, 65, 29, -106, 1285, - -106, -106, 1285, -106, -106, -106, -106, -106, -106, -106, - -106, -106, -106, -106, -106, -106, 1285, -46, 1285, 1285, - 30, 27, 1285, -106, 2258, 1285, 1285, -106, 130, -106, - -31, -106, -106, -16, 520, 520, 71, 21, -106, 421, - -106, 38, 2768, -106, -106, -106, -106, -106, 237, -106, - 520, -106, -52, -106, -106, -106, 23, -106, -106, -106, - 2768, -106, -106, 596, -106, 588, 141, 2666, 2, 1, - -1, 2972, 1285, -106, 13, 1285, 28, -106, -28, -30, - -106, -106, 435, -106, -106, -106, -106, 60, 520, 52, - 67, 2768, 64, -106, -106, 2666, -106, -106, 126, -106, - -106, -106, -106, 73, -106, -106, -106, -106, -106, -106, - -18, 34, 1285, 156, 168, -106, -106, -106, 1479, -106, - 79, 40, 48, -106, 312, 75, 42, 672, 80, 143, - 331, 279, 442, 1285, 316, 1285, 1285, 1285, 1285, 341, - 1285, 1285, 1285, 1285, 1285, 279, 360, 360, 196, 225, - 443, 357, 351, 1285, -4, 1285, 63, 1285, -106, 714, - 1285, -106, 1285, 102, 68, 1285, 62, 2666, -106, 1285, - 147, 2666, -106, 1285, 56, 1285, 1285, 91, 87, 1285, - -106, 81, 149, 74, -106, -106, 1285, -106, 439, 1285, - -106, -44, 1285, -42, 2666, -106, 1285, 153, 2666, -106, - 1285, 154, 2666, 10, 2666, -106, 0, -106, 15, -54, - 92, -106, -106, 2666, -50, 539, -7, 536, 121, 1285, - 2666, 5, -8, 445, 2462, 24, 902, 51, 50, 1384, - 2462, 47, 20, 46, 1285, 44, 19, 1285, 41, 1285, - 3, -5, 2564, -106, -106, -106, -106, -106, -106, 1285, - -106, -106, -106, 6, -17, 11, 2768, -9, -106, 249, - -106, 1285, -13, -106, 105, -106, -106, -12, 520, -41, - -20, 2768, -45, -106, 1285, 115, 12, -106, 36, -106, - 31, 122, 1285, -106, 58, 4, -106, -51, -106, 2666, - -106, 146, 2666, -106, 235, -106, -106, 140, 2666, 93, - -106, 84, 76, -106, 520, 17, 33, -106, -106, -106, - -106, 1285, 134, 2666, -106, 1285, 125, 2666, -106, 55, - -106, 163, -106, -106, 1285, -106, -106, 520, -106, -106, - 7, 45, 2768, 32, -106, -106, 120, 1773, -106, -106, - 1577, -106, -106, 1871, -106, -106, -106, -106, -106, -106, - 113, -106, -106, -106, -106, -106, -106, -106, -106, -106, - 2768, -106, -106, -106, 109, 35, 808, 206, 49, 61, - -106, -106, 229, -106, 203, 37, -106, -106, 611, 183, - -106, 124, 39, 389, -106, 97, -106, -106, 252, -106, - 2061, -106, -106, -106, -106, -106, -106, -106, -106, -106, - 269, -23, 611, 243, 180, 424, 232, -106, 16, -106, - 808, 162, -106, 22, 808, -106, -106, 1190, -106, -106, - -106, 1094, -106, -106, 248, -106, 2061, -106, 305, -24, - -106, -106, 215, 457, 18, 2156, 292, 2870, -11, -106, - 14, 599, 9, 528, 119, 1285, 2666, 8, 70, 514, - 72, 902, 95, 90, 1384, 85, 59, 77, 1285, 110, - 83, 1285, 104, 1285, 82, 78, -106, 205, -106, 236, - -106, 57, 25, 611, 167, 517, -106, 107, -106, -106, - -106, 1966, 808, 1675, 43, -106, 155, -106, -106, 53, - -106, -106, 808, 808, 108, 808, -106, 289, -106, 117, - -106, -106, 150, 157, -106, -106, -106, -106, -106, 364, - -106, 226, -106, 69, -106, -106, 432, -106, -106, 88, - -106, -106, -106, -106, -106, + 308, 1392, 2787, 2787, 2890, 1102, 71, 6, 103, -107, + 10, -35, -64, 287, -107, 310, 11, -107, -107, 815, + 30, 112, 183, 214, -107, -107, -107, 463, 203, 1392, + -107, -107, -107, 536, -107, -107, 2478, 1786, 1392, 1392, + 1392, -107, 1005, 1392, -107, -107, -107, 1392, 1392, -107, + -107, -107, -107, -107, -107, 1392, -107, 1392, 1392, -107, + 1392, 1392, 75, 204, -107, -107, 1392, 1392, 1392, -107, + -107, -107, 221, 1392, 306, 1392, 1392, 1392, 1392, 463, + 1392, 1392, 1392, 1392, 1392, 1392, 200, 1392, 1392, 1392, + 149, 145, 108, 231, 241, 295, 379, 379, 463, 463, + 463, 1392, -70, 1392, 4, 2375, 1392, 1392, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 105, 1392, -107, -107, -5, -58, -107, + 1392, -107, -107, 1392, -107, -107, -107, -107, -107, -107, + -107, -107, -107, -107, -107, -107, -107, 1392, -44, 1392, + 1392, 5, 7, 1392, -107, 2375, 1392, 1392, -107, 134, + -107, -43, -107, -107, -16, 541, 541, 15, -36, -107, + 462, -107, -4, 2787, -107, -107, -107, -107, -107, 213, + -107, 449, -107, -20, -107, -107, -107, 31, -107, -107, + -107, 2787, -107, -107, 616, -107, 711, 144, 2890, 21, + 42, 43, 3096, -107, 1392, -107, 62, 1392, 101, -107, + 102, 99, -107, -107, 417, -107, -107, -107, -107, 93, + 441, 56, 92, 2787, 34, -107, -107, 2890, -107, -107, + 118, -107, -107, -107, -107, 125, -107, -107, -107, -107, + -107, -107, -14, 33, 1392, 137, 193, -107, -107, -107, + 1488, -107, 44, -1, -42, -107, 316, -8, -60, 718, + 97, 87, 368, 222, 359, 1392, 313, 1392, 1392, 1392, + 1392, 342, 1392, 1392, 1392, 1392, 1392, 271, 270, 263, + 262, 225, 346, 352, 362, 1392, -51, 1392, 29, 1392, + -107, 815, 1392, -107, 1392, 28, -22, 1392, -19, 2890, + -107, 1392, 160, 2890, -107, 1392, 0, 1392, 1392, 97, + 45, 1392, -107, 37, 142, 25, -107, -107, 1392, -107, + 541, 1392, -107, 9, 1392, 12, 2890, -107, 1392, 128, + 2890, -107, 1392, 124, 2890, 61, 2890, -107, 60, -107, + 67, 26, 73, -107, -107, 2890, 49, 544, 80, 556, + 114, 1392, 2890, 85, 58, 482, 2581, 64, 88, 1005, + 90, 94, 1588, 2581, 96, 70, 197, 1392, 100, 76, + 1392, 104, 1392, 82, 84, 2684, -107, -107, -107, -107, + -107, -107, 1392, -107, -107, -107, 95, 63, 91, 2787, + 53, -107, 217, -107, 1392, 50, -107, 120, -107, -107, + 40, 372, 8, 27, 2787, 3, -107, 1392, 141, 20, + -107, 46, -107, 41, 147, 1392, -107, 39, 36, -107, + -15, -107, 2890, -107, 297, 2890, -107, 175, -107, -107, + 187, 2890, 14, -107, -3, -2, -107, 459, -34, -6, + -107, -107, -107, -107, 1392, 139, 2890, -107, 1392, 132, + 2890, -107, 1, -107, 251, -107, -107, 1392, -107, -107, + 541, -107, -107, -48, -23, 2787, -47, -107, -107, 113, + 1984, -107, -107, 1885, -107, -107, 1687, -107, -107, -107, + -107, -107, -107, 107, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 2787, -107, -107, -107, 131, -50, 910, + 243, -45, -7, -107, -107, 232, -107, 206, -12, -107, + -107, 633, 189, -107, 198, 13, 385, -107, 153, -107, + -107, 184, -107, 2080, -107, -107, -107, -107, -107, -107, + -107, -107, -107, 208, 18, 633, 219, 129, 353, 292, + -107, 48, -107, 910, 122, -107, 81, 910, -107, -107, + 1296, -107, -107, -107, 1199, -107, -107, 224, -107, 2080, + -107, 311, 81, -107, -107, 205, 633, 98, 2176, 304, + 2993, 69, -107, 89, 613, 86, 597, 109, 1392, 2890, + 83, 55, 467, 52, 79, 804, 78, 77, 1588, 66, + 47, 59, 1392, 57, 32, 1392, 54, 1392, 38, 35, + -107, 255, -107, 228, -107, 51, 2, 524, 195, 532, + -107, 133, -107, -107, -107, 2272, 910, 1786, 17, -107, + 152, -107, -107, 16, -107, -107, 910, 910, 119, 910, + -107, 302, -107, 148, -107, -107, 143, 140, -107, -107, + -107, -107, -107, 369, -107, 249, -107, 111, -107, -107, + 364, -107, -107, 65, -107, -107, -107, -107, -107, - -111, 8, 65, 83, 84, 317, 1, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, -77, - -111, -111, -111, -111, -111, -111, -111, -111, -111, 96, - -111, -111, -111, 2, -111, -111, -5, -28, 12, 106, - 95, -111, 61, 55, -111, -111, -111, 63, 70, -111, - -111, -111, -111, -111, -111, 54, -111, 172, 177, -111, - 180, 191, -111, -111, -111, -111, 197, 202, 203, -111, - -111, -111, -111, 210, -111, 209, 195, 109, 116, -111, - 146, 125, 126, 127, 135, 138, -111, 141, 144, 110, + -111, 55, 62, 77, 71, 279, -7, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, -111, -111, -74, + -111, -111, -111, -111, -111, -111, -111, -111, -111, 70, + -111, -111, -111, -8, -111, -111, -6, -28, 12, 84, + 85, -111, 93, 100, -111, -111, -111, 101, 104, -111, + -111, -111, -111, -111, -111, 107, -111, 112, 118, -111, + 182, 184, -111, -111, -111, -111, 218, 215, 209, -111, + -111, -111, -111, 202, -111, 195, 193, 192, 191, -111, + 189, 183, 181, 175, 168, 155, -111, 170, 153, 150, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, 150, -111, 155, -111, 192, 4, -33, -111, -111, + -111, 151, -111, 142, -111, 172, 30, -4, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -9, -111, -111, -111, -111, -111, 7, - -111, -111, 40, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, 99, -111, 52, 31, - -111, -111, 30, -111, 253, 44, 87, -111, -111, -111, - -111, -111, -111, -111, 45, 86, -111, -111, -111, 41, - -111, -111, 5, -111, -111, -111, -111, -111, -111, -111, - 50, -111, -111, -111, -111, -111, -111, -111, -111, -111, - 154, -111, -111, 26, -111, 49, -111, 330, -111, 28, - -111, 248, 27, -111, -111, 124, 51, -111, -111, -111, - -111, -111, 46, -111, -111, -111, -111, -111, 196, -111, - -111, 185, -111, -111, -111, 194, -111, -111, -111, -111, + -111, -111, -111, -111, -2, -111, -111, -111, -111, -111, + 0, -111, -111, 9, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, -111, -111, 125, -111, 122, + 10, -111, -111, 22, -111, 236, 46, 127, -111, -111, + -111, -111, -111, -111, -111, 37, 124, -111, -111, -111, + 39, -111, -111, 42, -111, -111, -111, -111, -111, -111, + -111, 44, -111, -111, -111, -111, -111, -111, -111, -111, + -111, 94, -111, -111, 47, -111, 48, -111, 128, -111, + 50, -111, 91, -111, -3, -111, -111, 66, 53, -111, + -111, -111, -111, -111, 57, -111, -111, -111, -111, -111, + 79, -111, -111, 78, -111, -111, -111, 82, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, 15, -111, -111, -111, -111, -111, 74, -111, + -111, -111, -111, -111, 67, -111, -111, -111, -111, -111, + 61, -111, -111, -111, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, 59, 258, -111, 259, 268, 269, + 272, -111, 60, 63, 73, 74, 75, -111, -111, -111, + -111, -111, -111, -111, -111, 252, -111, 242, -111, 233, + -111, -111, 232, -111, 87, -111, -111, 89, -111, 133, + -111, 51, -111, 135, -111, 231, -111, 223, 222, -111, + -111, 221, -111, -111, -111, -111, -111, -111, 219, -111, + 92, 102, -111, -111, 110, -111, 171, -111, 40, -111, + 173, -111, 38, -111, 176, -111, 179, -111, -111, -111, + -111, -111, -111, -111, -111, 180, -111, 19, -111, 18, + -111, 145, 185, -111, -111, 17, 166, -111, -111, 65, + -111, -111, 29, 177, -111, -111, -111, 25, -111, 5, + 159, -111, 164, -111, -111, 207, -111, -111, -111, -111, + -111, -111, -18, -111, -111, -111, -111, -111, -111, 212, + -111, -111, -111, -111, 216, -111, -111, -111, -111, -111, + -111, 213, -111, -111, 86, -111, -111, 16, -111, -111, + -111, -111, -111, -85, -111, 14, -111, -84, -111, -111, + -111, -111, 286, -111, -111, 287, -111, -111, -111, -111, + -111, 214, -94, -111, -111, -16, -111, -10, -111, -19, + -111, -111, -111, -111, 2, -111, 83, -111, 105, -111, + 81, -111, -111, -111, -111, -111, -111, -41, -111, -111, + 131, -111, -111, -111, -111, 76, -111, -111, -111, -111, + -35, -111, -111, 64, -111, -111, -29, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, -16, 230, -111, 233, 269, 251, 265, -111, - 80, 81, 82, 88, 89, -111, -111, -111, -111, -111, - -111, -111, -111, 216, -111, 255, -111, 259, -111, -111, - 223, -111, 68, -111, -111, 217, -111, 236, -111, 24, - -111, 244, -111, 227, -111, 226, 257, -111, -111, 263, - -111, -111, -111, -111, -111, -111, 249, -111, 113, 163, - -111, -111, 212, -111, 173, -111, 48, -111, 211, -111, - 53, -111, 206, -111, 204, -111, -111, -111, -111, -111, - -111, -111, -111, 199, -111, 23, -111, 25, -111, 134, - 175, -111, -111, 37, 200, -111, 222, -111, -111, 57, - 56, -111, -111, -111, 124, -111, 32, 43, -111, 105, - -111, -111, 139, -111, -111, -111, -111, -111, -111, 38, - -111, -111, -111, -111, -111, -111, 75, -111, -111, -111, - -111, 71, -111, -111, -111, -111, -111, -111, 72, -111, - -111, 85, -111, -111, 59, -111, -111, -111, -111, -111, - -37, -111, 62, -111, -58, -111, -111, -111, -111, 286, - -111, -111, 250, -111, -111, -111, -111, -111, 153, -57, - -111, -111, 33, -111, 34, -111, 36, -111, -111, -111, - -111, 47, -111, 77, -111, 29, -111, 67, -111, -111, - -111, -111, -111, -111, 42, -111, -111, 214, -111, -111, - -111, -111, 229, -111, -111, -111, -111, 35, -111, -111, - 145, -111, -111, 3, -111, -111, -111, -111, -111, -111, + -111, -111, -111, 208, -111, -111, -111, -111, -111, 20, + -111, -111, -111, -111, -111, -111, -111, 13, -111, -111, + -111, 26, 15, -111, -111, -111, 32, -111, -111, -111, + -111, -111, -111, 329, -111, -111, -111, -111, -111, -111, + -111, -111, -111, -111, -111, 54, 56, -111, 58, -111, + -111, -111, -111, 68, -111, -111, -111, 72, -111, -111, + 330, -111, -111, -111, 327, -111, -111, -111, -111, 389, + -111, -111, 52, -111, -111, 31, 49, -111, 371, -111, + 134, 34, -111, -111, 43, -111, 41, -111, 108, 141, + -111, -111, 35, -111, -111, 97, -111, -111, 45, -111, + -111, -111, 36, -111, 21, 129, -111, 146, -111, -111, + -111, -111, -111, -1, -111, -111, -111, 11, -5, 7, + -111, -111, -111, -111, -111, 353, 311, 408, 4, -111, + -111, -111, -111, 1, -111, -111, 8, 6, 249, 248, -111, -111, -111, -111, -111, -111, -111, -111, -111, -111, - 207, -111, -111, -111, -111, -111, 39, -111, -111, -111, - -111, -111, -111, -111, -24, -111, -111, -111, -12, -27, - -111, -111, -111, -14, -111, -111, -111, -111, -111, -111, - 333, -111, -111, -111, -111, -111, -111, -111, -111, -111, - -111, -111, 6, 22, -111, 20, -111, -111, -111, -111, - 159, -111, -111, -111, 246, -111, -111, 332, -111, -111, - -111, 436, -111, -111, -111, -111, 388, -111, -111, 18, - -111, -111, -6, 19, -111, 352, -111, 225, 14, -111, - -111, 13, -111, 11, -111, 167, 136, -111, -111, 10, - -111, 64, -111, -111, 9, -111, -111, -111, 124, -111, - 0, 69, -111, 60, -111, -111, -111, -111, -111, -10, - -111, -111, -111, -1, -11, -2, -111, -111, -111, -111, - -111, 370, 142, 315, -3, -111, -111, -111, -111, 17, - -111, -111, -13, 21, 133, 221, -111, -111, -111, -111, - -111, -111, -111, -111, -111, -111, -111, -111, -111, 16, - -111, -111, -111, -111, -111, -111, -15, -111, -111, -111, - -111, -111, -111, -111, -111}; + -111, -111, -111, 3, -111, -111, -111, -111, -111, -111, + -14, -111, -111, -111, -111, -111, -111, -111, -111}; const short QQmlJSGrammar::action_info [] = { - -145, 398, 101, 244, 438, 402, 465, 245, 461, 567, - 423, 439, -126, 421, 553, -126, -145, 342, 344, 420, - 185, 245, 567, 392, 418, 585, 354, 73, 268, 181, - 245, 465, 166, 101, 172, 350, 432, 184, 268, 461, - 103, 432, 404, 405, 406, 428, 408, 413, -142, 424, - 560, -139, 448, -137, -115, 567, 424, -116, -134, 261, - 350, 448, 143, 432, 283, 624, 448, 481, 534, 103, - 262, 192, 474, 149, 529, 305, 567, 456, 482, 189, - 283, 191, 323, 307, -137, 627, 567, 484, 303, 151, - 617, 166, -115, 323, 329, 424, 238, -116, 336, 399, - 241, 524, -134, 312, 303, 346, 268, 73, 350, 448, - 143, -142, 240, 452, 465, 582, 448, -139, 461, 243, - 454, 143, 317, 143, 174, 0, 60, 305, 490, 315, - 665, 664, 435, 143, 257, 256, 60, 61, 143, 532, - 60, 60, 143, 175, 143, 64, 451, 61, 533, 671, - 670, 61, 61, 442, 143, 143, 65, 338, 537, 536, - 452, 143, 143, 564, 143, 174, 416, 415, 629, 628, - 564, 477, 174, 501, 0, 426, 491, 436, 673, 672, - 259, 258, 259, 258, 175, 467, 179, 252, 251, 642, - 643, 175, 144, 325, 463, 532, 530, 326, 674, 642, - 643, 168, 259, 258, 555, 169, 87, 321, 88, 66, - 339, 637, 530, 348, 352, 87, 264, 88, 565, 89, - 87, 87, 88, 88, 478, 476, 66, 174, 89, 267, - 265, 66, 525, 89, 89, 551, 631, 0, 87, 558, - 88, 619, 527, 143, 530, 143, 175, 0, 176, 105, - 87, 89, 88, 526, 67, 576, 0, 266, 527, 540, - 68, 0, 567, 89, 174, 530, 620, 618, 106, 526, - 107, 67, 530, 0, 0, 0, 67, 68, 527, 0, - 0, 527, 68, 175, 174, 411, 0, 668, 667, 526, - 527, 0, 526, 559, 557, 0, 446, 445, 236, 235, - 0, 526, 0, 175, 87, 411, 88, 174, 0, 577, - 575, 527, 0, 541, 539, 75, 76, 89, 527, 666, - 174, 0, 526, 632, 0, -102, 175, 0, 176, 526, - 285, 286, 75, 76, 285, 286, 661, 0, -102, 175, - 0, 176, 77, 78, 6, 5, 4, 1, 3, 2, - 662, 660, 0, 0, 290, 291, 0, 287, 288, 77, - 78, 287, 288, 292, 290, 291, 293, 0, 294, 0, - 0, 0, 0, 292, 290, 291, 293, 0, 294, 0, - 290, 291, 659, 292, 0, 87, 293, 88, 294, 292, - 0, 0, 293, 35, 294, 80, 81, 0, 89, 0, - 0, 0, 0, 82, 83, 80, 81, 84, 0, 85, - 0, 0, 0, 82, 83, 80, 81, 84, 35, 85, - 0, 0, 0, 82, 83, 80, 81, 84, 0, 85, - 49, 52, 50, 82, 83, 80, 81, 84, 0, 85, - 0, 0, 0, 82, 83, 0, 0, 84, 0, 85, - 35, 0, 0, 35, 0, 49, 52, 50, 46, 34, - 51, 35, 0, 0, 35, 0, 290, 291, 35, 0, - 0, 35, 532, 0, 35, 292, 0, 0, 293, 0, - 294, 184, 0, 46, 34, 51, 35, 49, 52, 50, + 309, 314, 152, 150, 101, 73, 678, 167, 487, 103, + 485, 73, 484, 101, 173, 103, 527, 182, 477, 144, + 186, 585, 621, 190, 192, 532, 459, 451, 307, 193, + 285, 451, 167, 457, 455, 246, 144, 307, 247, 317, + 441, 319, 537, 442, 435, 285, 435, 305, 305, 570, + 570, 435, 331, 431, 338, 556, 348, 270, 426, 628, + 424, -142, 631, 263, -139, 451, -137, 247, 423, 264, + 344, 468, 346, -115, 464, 395, 421, 356, 185, 352, + 402, 401, 563, 427, -116, -134, -146, -145, 352, 245, + -126, 270, -126, -145, 270, -146, 247, -134, 427, 325, + 352, -116, 570, -115, 405, 588, 427, -139, 411, 451, + 416, -142, 0, 144, 570, 144, 242, 64, 464, 0, + 468, 493, 0, 408, 409, 243, 675, 674, 65, 240, + 567, 407, 144, 0, 451, 468, 144, 327, 464, 0, + 144, 328, 144, 60, 535, 144, 175, 144, 60, 144, + 340, 0, 0, 558, 61, 175, 0, 438, 175, 61, + 567, 145, 169, 646, 647, 176, 170, 504, 144, 494, + 261, 260, 669, 668, 176, 261, 260, 176, 568, 254, + 253, 419, 418, 144, 354, 60, 259, 258, 350, 60, + 180, 543, 470, 454, 633, 632, 61, 266, 175, 466, + 61, 429, 439, 341, -137, 261, 260, 455, 641, 677, + 676, 646, 647, 535, 540, 539, 66, 176, 533, 177, + 323, 144, 536, 175, 533, 87, 66, 88, 87, 0, + 88, 579, 175, 66, 533, 528, 449, 448, 89, 635, + 0, 89, 176, 0, 414, 544, 542, 87, 533, 88, + 87, 176, 88, 414, 269, 267, 87, 533, 88, 480, + 89, 67, 0, 89, 530, 570, 87, 68, 88, 89, + 530, 67, 554, 0, 238, 237, 529, 68, 67, 89, + 530, 530, 529, 268, 68, 580, 578, 87, 87, 88, + 88, 623, 529, 529, 530, 87, 87, 88, 88, 561, + 89, 89, 105, 530, 445, 144, 529, 0, 89, 89, + 672, 671, 481, 479, 0, 529, 624, 622, 530, 175, + 87, 106, 88, 107, 75, 76, 175, 636, 75, 76, + 529, 287, 288, 89, 287, 288, 0, -102, 176, 0, + 177, 0, 0, 670, -102, 176, 0, 177, 0, 665, + 0, 77, 78, 562, 560, 77, 78, 0, 289, 290, + 0, 289, 290, 666, 664, 292, 293, 0, 0, 292, + 293, 0, 0, 0, 294, 292, 293, 295, 294, 296, + 0, 295, 35, 296, 294, 292, 293, 295, 35, 296, + 0, 292, 293, 35, 294, 0, 663, 295, 35, 296, + 294, 35, 0, 295, 87, 296, 88, 6, 5, 4, + 1, 3, 2, 0, 35, 0, 0, 89, 0, 49, + 52, 50, 0, 0, 0, 49, 52, 50, 0, 0, + 49, 52, 50, 0, 0, 49, 52, 50, 49, 52, + 50, 0, 0, 0, 0, 0, 35, 0, 46, 34, + 51, 49, 52, 50, 46, 34, 51, 0, 0, 46, + 34, 51, 0, 0, 46, 34, 51, 46, 34, 51, + 35, 0, 0, 0, 0, 0, 0, 0, 35, 0, + 46, 34, 51, 49, 52, 50, 80, 81, 35, 0, + 0, 35, 0, 0, 82, 83, 35, 0, 84, 0, + 85, 0, 0, 185, 0, 0, 0, 49, 52, 50, + 0, 35, 46, 34, 51, 49, 52, 50, 185, 0, + 0, 0, 0, 0, 0, 49, 52, 50, 49, 52, + 50, 0, 0, 49, 52, 50, 46, 34, 51, 535, + 0, 0, 0, 0, 46, 34, 51, 535, 49, 52, + 50, 0, 0, 35, 46, 34, 51, 46, 34, 51, + 0, 35, 46, 34, 51, 35, 0, 0, 0, 0, + 35, 0, 185, 35, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 0, 49, 52, 50, 0, 0, 0, 0, 0, 49, 52, - 50, 49, 52, 50, 0, 49, 52, 50, 49, 52, - 50, 49, 52, 50, 0, 46, 34, 51, 46, 34, - 51, 0, 0, 49, 52, 50, 46, 34, 51, 46, - 34, 51, 532, 46, 34, 51, 46, 34, 51, 46, - 34, 51, 0, 35, 0, 0, 35, 0, 0, 35, - 184, 46, 34, 51, 35, 0, 0, 35, 0, 0, - 0, 184, 0, 0, 0, 35, 0, 0, 35, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 49, 52, 50, 49, 52, 50, 49, 52, 50, 255, - 254, 49, 52, 50, 49, 52, 50, 255, 254, 0, - 250, 249, 49, 52, 50, 49, 52, 50, 46, 34, - 51, 46, 34, 51, 46, 34, 51, 35, 0, 46, - 34, 51, 46, 34, 51, 35, 532, 0, 35, 0, - 46, 34, 51, 46, 34, 51, 0, 0, 0, 0, - 35, 0, 0, 0, 0, 0, 0, 0, 0, 255, - 254, 0, 0, 0, 49, 52, 50, 250, 249, 0, - 250, 249, 49, 52, 50, 49, 52, 50, 0, 0, - 0, 0, 0, 0, 0, 153, 0, 49, 52, 50, - 0, 0, 46, 34, 51, 154, 0, 0, 0, 155, - 46, 34, 51, 46, 34, 51, 0, 0, 156, 0, - 157, 0, 0, 319, 0, 46, 34, 51, 0, 0, - 0, 158, 0, 159, 64, 0, 0, 153, 0, 0, - 0, 160, 0, 0, 161, 65, 0, 154, 0, 0, - 162, 155, 0, 0, 0, 0, 163, 0, 0, 0, - 156, 0, 157, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 164, 158, 0, 159, 64, 0, 0, 0, - 0, 0, 0, 160, 0, 0, 161, 65, 0, 0, - 0, 0, 162, 0, 0, 0, 0, 0, 163, 0, + 50, 0, 49, 52, 50, 252, 251, 49, 52, 50, + 49, 52, 50, 0, 0, 0, 0, 257, 256, 46, + 34, 51, 49, 52, 50, 0, 35, 46, 34, 51, + 0, 46, 34, 51, 0, 0, 46, 34, 51, 46, + 34, 51, 35, 0, 0, 35, 0, 0, 535, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 257, 256, + 0, 0, 35, 49, 52, 50, 0, 0, 0, 0, + 0, 0, 0, 0, 252, 251, 0, 252, 251, 49, + 52, 50, 49, 52, 50, 0, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 49, + 52, 50, 0, 0, 0, 0, 0, 0, 46, 34, + 51, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 154, 0, 0, 0, 0, 0, 0, 46, 34, + 51, 155, 0, 0, 0, 156, 0, 0, 0, 0, + 35, 0, 0, 0, 157, 0, 158, 0, 0, 321, + 0, 0, 0, 0, 0, 0, 0, 159, 0, 160, + 64, 0, 0, 0, 0, 0, 0, 161, 0, 0, + 162, 65, 257, 256, 0, 0, 163, 49, 52, 50, + 0, 0, 164, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 165, 0, + 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, + 0, 0, 0, 0, 0, 0, 30, 31, 154, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 155, 0, + 0, 0, 156, 35, 0, 0, 0, 36, 37, 0, + 38, 157, 0, 158, 0, 0, 0, 42, 0, 0, + 0, 45, 0, 0, 159, 0, 160, 64, 0, 0, + 0, 0, 0, 0, 161, 0, 0, 162, 65, 53, + 49, 52, 50, 163, 54, 0, 0, 0, 0, 164, + 0, 0, 0, 0, 0, 44, 56, 32, 0, 0, + 0, 0, 41, 0, 0, 165, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 164, 0, 0, 0, 0, 0, + 0, 0, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, + 0, 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, + 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 30, 31, 0, + 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, + 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, + 0, 38, 0, 0, 0, 0, 0, 0, 42, 0, + 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, - 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, + 53, 49, 52, 50, 0, 54, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 44, 56, 32, 0, + 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, + 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 518, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 220, 0, 0, 0, 0, 0, + 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, + 0, 0, 0, 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 44, - 56, 32, 0, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 53, 520, 522, + 521, 0, 54, 0, 0, 0, 0, 229, 0, 0, + 0, 0, 0, 44, 56, 32, 215, 223, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 518, + 0, 30, 31, 0, 0, 0, 0, 0, 0, 0, + 0, 220, 0, 0, 0, 0, 0, 0, 35, 0, + 0, 0, 36, 37, 0, 38, 0, 0, 0, 0, + 0, 0, 519, 0, 0, 0, 45, 0, 0, 0, + 0, 0, 0, 0, 575, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 53, 520, 522, 521, 0, 54, + 0, 0, 0, 0, 229, 0, 0, 0, 0, 0, + 44, 56, 32, 215, 223, 0, 0, 41, 0, 0, + 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 518, 0, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 220, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 0, 0, 0, 519, + 0, 0, 0, 45, 0, 0, 0, 0, 0, 0, + 0, 572, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 520, 522, 521, 0, 54, 0, 0, 0, + 0, 229, 0, 0, 0, 0, 0, 44, 56, 32, + 215, 223, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, - 0, 0, 0, 0, 0, 42, 0, 0, 0, 45, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, - 50, 0, 54, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 44, 56, 32, 0, 0, 0, 41, - 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 515, 0, + 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, + 0, 0, 0, 44, 56, 32, 0, 0, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, - 219, 0, 0, 0, 0, 0, 0, 35, 0, 0, - 0, 36, 37, 0, 38, 0, 0, 0, 0, 0, - 0, 516, 0, 0, 0, 45, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 53, 517, 519, 518, 0, 54, 0, - 0, 0, 0, 227, 0, 0, 0, 0, 0, 44, - 56, 32, 214, 0, 0, 41, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 515, 0, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 219, 0, 0, 0, - 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, - 38, 0, 0, 0, 0, 0, 0, 516, 0, 0, - 0, 45, 0, 0, 0, 0, 0, 0, 0, 572, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 517, 519, 518, 0, 54, 0, 0, 0, 0, 227, - 0, 0, 0, 0, 0, 44, 56, 32, 214, 0, - 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 515, 0, 30, 31, 0, 0, 0, 0, 0, 0, - 0, 0, 219, 0, 0, 0, 0, 0, 0, 35, - 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, - 0, 0, 0, 516, 0, 0, 0, 45, 0, 0, - 0, 0, 0, 0, 0, 569, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 53, 517, 519, 518, 0, - 54, 0, 0, 0, 0, 227, 0, 0, 0, 0, - 0, 44, 56, 32, 214, 0, 0, 41, 0, 0, - 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, - 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, - 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, - 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, - 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, - 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, -135, 0, 0, 0, 29, 30, 31, 0, 0, - 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, - 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, - 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, - 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 53, - 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, - 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, - 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, - 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, - 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, - 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, - 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, - 47, 0, 48, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, - 0, 55, 0, 57, 282, 58, 0, 0, 0, 0, - 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 284, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, + 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, + 0, 0, 0, 0, 0, -135, 0, 0, 0, 29, + 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, + 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, + 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, + 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, + 0, 48, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, + 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, + 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 488, 0, 0, 29, 30, + 0, 0, 0, 0, 0, 499, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, - 48, 0, 0, 494, 0, 0, 0, 0, 0, 0, + 48, 0, 0, 500, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, - 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, + 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 491, 0, 0, 29, 30, 31, + 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, + 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, + 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, + 0, 0, 492, 0, 0, 0, 0, 0, 0, 0, + 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, + 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 488, 0, 0, 29, 30, 31, 0, + 0, 0, 0, 491, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, 0, - 0, 489, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 497, 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, 0, 0, 44, 56, 32, 0, - 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, - 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 496, 0, 0, 29, 30, 31, 0, 0, 0, - 0, 0, 0, 0, 0, 33, 0, 0, 0, 0, - 0, 0, 35, 0, 0, 0, 36, 37, 0, 38, - 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, - 45, 0, 0, 0, 47, 0, 48, 0, 0, 499, - 0, 0, 0, 0, 0, 0, 0, 0, 53, 49, - 52, 50, 0, 54, 0, 55, 0, 57, 0, 58, - 0, 0, 0, 0, 44, 56, 32, 0, 0, 0, - 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 496, - 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 35, 0, 0, 0, 36, 37, 0, 38, 0, 0, - 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, - 0, 0, 47, 0, 48, 0, 0, 497, 0, 0, - 0, 0, 0, 0, 0, 0, 53, 49, 52, 50, - 0, 54, 0, 55, 0, 57, 0, 58, 0, 0, - 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, - 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, - 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 587, - 633, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 0, 55, 226, - 57, 0, 58, 0, 229, 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, - 0, 0, 0, 33, 0, 0, 0, 0, 0, 0, - 35, 220, 0, 0, 221, 37, 0, 38, 0, 0, - 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, - 0, 0, 47, 0, 48, 0, 0, 0, 0, 0, - 0, 0, 223, 0, 0, 0, 53, 49, 52, 50, - 224, 54, 0, 55, 226, 57, 0, 58, 0, 229, - 0, 0, 44, 56, 32, 0, 0, 0, 41, 0, + 0, 0, 499, 0, 0, 29, 30, 31, 0, 0, + 0, 0, 0, 0, 0, 0, 33, 0, 0, 0, + 0, 0, 0, 35, 0, 0, 0, 36, 37, 0, + 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, + 0, 45, 0, 0, 0, 47, 0, 48, 0, 0, + 502, 0, 0, 0, 0, 0, 0, 0, 0, 53, + 49, 52, 50, 0, 54, 0, 55, 0, 57, 0, + 58, 0, 0, 0, 0, 44, 56, 32, 0, 0, + 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, + 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, + 0, 0, 33, 0, 0, 0, 0, 0, 0, 35, + 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, + 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, + 0, 47, 0, 48, 0, 0, 0, 0, 0, 0, + 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, + 54, 0, 55, 228, 57, 0, 58, 0, 231, 0, + 0, 44, 56, 32, 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, 0, 0, 0, 0, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 587, + 0, 0, 0, 0, 0, 35, 221, 0, 0, 590, 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 0, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 0, 55, 226, - 57, 0, 58, 0, 229, 0, 0, 44, 56, 32, - 0, 0, 0, 41, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 225, 0, 0, + 0, 53, 49, 52, 50, 226, 54, 0, 55, 228, + 57, 0, 58, 0, 231, 0, 0, 44, 56, 32, + 0, 0, 0, 0, 41, 0, 0, 0, 0, 0, + 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 29, 30, 31, 0, 0, 0, 0, + 0, 0, 0, 0, 33, 0, 0, 0, 0, 0, + 0, 35, 221, 0, 0, 590, 637, 0, 38, 0, + 0, 0, 39, 0, 40, 42, 43, 0, 0, 45, + 0, 0, 0, 47, 0, 48, 0, 0, 0, 0, + 0, 0, 0, 225, 0, 0, 0, 53, 49, 52, + 50, 226, 54, 0, 55, 228, 57, 0, 58, 0, + 231, 0, 0, 44, 56, 32, 0, 0, 0, 0, + 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, + 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, + 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, + 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 129, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 132, 0, 0, 0, 0, 0, + 0, 49, 52, 50, 133, 134, 135, 0, 137, 138, + 139, 140, 141, 142, 0, 0, 130, 136, 121, 114, + 128, 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 128, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 131, 0, 0, - 0, 0, 0, 0, 49, 52, 50, 132, 133, 134, - 0, 136, 137, 138, 139, 140, 141, 0, 0, 129, - 135, 121, 114, 116, 130, 0, 0, 0, 0, 0, - 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, - 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, - 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, - 125, 126, 127, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 128, 0, 0, 0, 395, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, - 0, 0, 0, 0, 0, 397, 49, 52, 50, 132, - 133, 134, 0, 136, 137, 138, 139, 140, 141, 0, - 0, 129, 135, 121, 114, 116, 130, 0, 0, 0, - 0, 0, 0, 0, 46, 374, 380, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, - 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, - 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, - 0, 35, 125, 126, 127, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 128, 0, 0, 0, 395, + 0, 129, 0, 0, 0, 398, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 132, 0, 0, + 0, 0, 0, 400, 49, 52, 50, 133, 134, 135, + 0, 137, 138, 139, 140, 141, 142, 0, 0, 130, + 136, 121, 114, 128, 116, 131, 0, 0, 0, 0, + 0, 0, 0, 46, 376, 383, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, + 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, + 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, + 35, 125, 126, 127, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 129, 0, 0, 0, 398, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 131, 0, 0, 0, 0, 0, 397, 49, 52, - 50, 132, 133, 134, 0, 136, 137, 138, 139, 140, - 141, 0, 0, 129, 135, 121, 114, 116, 130, 0, + 132, 0, 0, 0, 0, 0, 400, 49, 52, 50, + 133, 134, 135, 0, 137, 138, 139, 140, 141, 142, + 0, 0, 130, 136, 121, 114, 128, 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, 0, 0, 111, 112, 113, 0, 0, 115, 117, 118, 0, 0, 119, 0, 120, 0, 0, 0, 122, 123, 124, 0, 0, 0, 0, 0, 0, 35, 125, 126, 127, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 128, 0, 0, - 0, 395, 0, 0, 0, 0, 0, 0, 0, 396, - 0, 0, 0, 131, 0, 0, 0, 0, 0, 397, - 49, 52, 50, 132, 133, 134, 0, 136, 137, 138, - 139, 140, 141, 0, 0, 129, 135, 121, 114, 116, - 130, 0, 0, 0, 0, 0, 0, 0, 46, 374, - 380, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 213, 0, 0, 0, 0, 215, 0, 29, 30, 31, - 217, 0, 0, 0, 0, 0, 0, 218, 33, 0, - 0, 0, 0, 0, 0, 35, 220, 0, 0, 221, - 37, 0, 38, 0, 0, 0, 39, 0, 40, 42, - 43, 0, 0, 45, 0, 0, 0, 47, 0, 48, - 0, 0, 0, 0, 0, 222, 0, 223, 0, 0, - 0, 53, 49, 52, 50, 224, 54, 225, 55, 226, - 57, 227, 58, 228, 229, 0, 0, 44, 56, 32, - 214, 216, 0, 41, 0, 0, 0, 0, 0, 0, - 46, 34, 51, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 213, 0, 0, 0, 0, 215, 0, 29, - 30, 31, 217, 0, 0, 0, 0, 0, 0, 218, - 219, 0, 0, 0, 0, 0, 0, 35, 220, 0, - 0, 221, 37, 0, 38, 0, 0, 0, 39, 0, - 40, 42, 43, 0, 0, 45, 0, 0, 0, 47, - 0, 48, 0, 0, 0, 0, 0, 222, 0, 223, - 0, 0, 0, 53, 49, 52, 50, 224, 54, 225, - 55, 226, 57, 227, 58, 228, 229, 0, 0, 44, - 56, 32, 214, 216, 0, 41, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, + 0, 398, 0, 0, 0, 0, 0, 0, 0, 399, + 0, 0, 0, 132, 0, 0, 0, 0, 0, 400, + 49, 52, 50, 133, 134, 135, 0, 137, 138, 139, + 140, 141, 142, 0, 0, 130, 136, 121, 114, 128, + 116, 131, 0, 0, 0, 0, 0, 0, 0, 46, + 376, 383, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 214, 0, 0, 0, 0, 216, 0, 29, 30, + 31, 218, 0, 0, 0, 0, 0, 0, 219, 220, + 0, 0, 0, 0, 0, 0, 35, 221, 0, 0, + 222, 37, 0, 38, 0, 0, 0, 39, 0, 40, + 42, 43, 0, 0, 45, 0, 0, 0, 47, 0, + 48, 0, 0, 0, 0, 0, 224, 0, 225, 0, + 0, 0, 53, 49, 52, 50, 226, 54, 227, 55, + 228, 57, 229, 58, 230, 231, 0, 0, 44, 56, + 32, 215, 223, 217, 0, 41, 0, 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 591, 112, 113, 0, 0, 593, - 117, 595, 30, 31, 596, 0, 120, 0, 0, 0, - 122, 598, 599, 0, 0, 0, 0, 0, 0, 35, - 600, 126, 127, 221, 37, 0, 38, 0, 0, 0, - 39, 0, 40, 601, 43, 0, 0, 603, 0, 0, - 0, 47, 0, 48, 0, 0, 0, 0, 0, 604, - 0, 223, 0, 0, 0, 605, 49, 52, 50, 606, - 607, 608, 55, 610, 611, 612, 613, 614, 615, 0, - 0, 602, 609, 597, 592, 594, 130, 41, 0, 0, - 0, 0, 0, 0, 46, 374, 380, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 365, 112, 113, 0, - 0, 367, 117, 369, 30, 31, 370, 0, 120, 0, - 0, 0, 122, 372, 373, 0, 0, 0, 0, 0, - 0, 35, 375, 126, 127, 221, 37, 0, 38, 0, - 0, 0, 39, 0, 40, 376, 43, 0, 0, 378, - 0, 0, 0, 47, 0, 48, 0, -281, 0, 0, - 0, 379, 0, 223, 0, 0, 0, 381, 49, 52, - 50, 382, 383, 384, 55, 386, 387, 388, 389, 390, - 391, 0, 0, 377, 385, 371, 366, 368, 130, 41, - 0, 0, 0, 0, 0, 0, 46, 374, 380, 0, - 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 214, 0, 0, 0, 0, 216, + 0, 29, 30, 31, 218, 0, 0, 0, 0, 0, + 0, 219, 33, 0, 0, 0, 0, 0, 0, 35, + 221, 0, 0, 222, 37, 0, 38, 0, 0, 0, + 39, 0, 40, 42, 43, 0, 0, 45, 0, 0, + 0, 47, 0, 48, 0, 0, 0, 0, 0, 224, + 0, 225, 0, 0, 0, 53, 49, 52, 50, 226, + 54, 227, 55, 228, 57, 229, 58, 230, 231, 0, + 0, 44, 56, 32, 215, 223, 217, 0, 41, 0, + 0, 0, 0, 0, 0, 46, 34, 51, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 594, 112, 113, + 0, 0, 596, 117, 598, 30, 31, 599, 0, 120, + 0, 0, 0, 122, 601, 602, 0, 0, 0, 0, + 0, 0, 35, 603, 126, 127, 222, 37, 0, 38, + 0, 0, 0, 39, 0, 40, 605, 43, 0, 0, + 607, 0, 0, 0, 47, 0, 48, 0, 0, 0, + 0, 0, 608, 0, 225, 0, 0, 0, 609, 49, + 52, 50, 610, 611, 612, 55, 614, 615, 616, 617, + 618, 619, 0, 0, 606, 613, 600, 595, 604, 597, + 131, 41, 0, 0, 0, 0, 0, 0, 46, 376, + 383, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 367, 112, 113, 0, 0, 369, 117, 371, 30, 31, + 372, 0, 120, 0, 0, 0, 122, 374, 375, 0, + 0, 0, 0, 0, 0, 35, 377, 126, 127, 222, + 37, 0, 38, 0, 0, 0, 39, 0, 40, 379, + 43, 0, 0, 381, 0, 0, 0, 47, 0, 48, + 0, -282, 0, 0, 0, 382, 0, 225, 0, 0, + 0, 384, 49, 52, 50, 385, 386, 387, 55, 389, + 390, 391, 392, 393, 394, 0, 0, 380, 388, 373, + 368, 378, 370, 131, 41, 0, 0, 0, 0, 0, + 0, 46, 376, 383, 0, 0, 0, 0, 0, 0, + 0, 0, 0, - 152, 652, 331, 669, 535, 531, 538, 142, 528, 148, - 641, 16, 313, 393, 485, 500, 626, 630, 263, 638, - 183, 625, 623, 206, 574, 447, 583, 320, 183, 253, - 313, 248, 466, 145, 663, 653, 616, 584, 556, 640, - 581, 248, 437, 253, 248, 495, 183, 178, 453, 150, - 462, 347, 455, 550, 554, 183, 351, 447, 458, 190, - 313, 457, 425, 188, 469, 441, 433, 253, 237, 468, - 0, 313, 173, 171, 393, 409, 447, 498, 409, 464, - 400, 0, 165, 206, 475, 206, 512, 511, 0, 0, - 188, 0, 0, 206, 0, 206, 0, 62, 0, 459, - 417, 206, 206, 206, 188, 0, 62, 0, 62, 62, - 507, 504, 410, 148, 62, 410, 460, 62, 419, 505, - 407, 412, 170, 62, 62, 459, 506, 444, 277, 148, - 422, 331, 187, 281, 62, 62, 62, 180, 260, 295, - 296, 297, 62, 62, 656, 655, 314, 298, 299, 62, - 62, 503, 182, 62, 206, 362, 514, 393, 247, 62, - 62, 460, 502, 62, 62, 639, 313, 167, 92, 99, - 62, 206, 206, 514, 510, 345, 100, 260, 562, 62, - 62, 62, 394, 493, 93, 94, 95, 492, 62, 62, - 182, 206, 62, 206, 96, 62, 246, 97, 62, 90, - 62, 401, 91, 206, 62, 86, 355, 340, 353, 62, - 108, 247, 206, 349, 188, 313, 102, 206, 393, 104, - 313, 62, 206, 182, 206, 206, 62, 362, 459, 206, - 242, 62, 469, 460, 62, 514, 409, 63, 318, 110, - 657, 341, 239, 590, 403, 62, 322, 206, 72, 62, - 362, 62, 362, 69, 206, 98, 62, 62, 70, 71, - 514, 0, 206, 62, 62, 566, 356, 0, 206, 79, - 62, 108, 74, 410, 483, 281, 0, 309, 0, 0, - 62, 62, 281, 304, 62, 281, 281, 62, 362, 281, - 343, 0, 281, 284, 289, 316, 324, 327, 0, 311, - 110, 177, 0, 309, 206, 62, 479, 0, 281, 62, - 281, 309, 301, 309, 281, 0, 281, 309, 281, 62, - 306, 0, 281, 62, 281, 337, 302, 0, 281, 578, - 300, 514, 260, 328, 562, 308, 636, 570, 443, 330, - 522, 0, 0, 0, 0, 0, 514, 0, 206, 0, - 0, 0, 513, 523, 0, 522, 0, 485, 542, 543, - 544, 545, 549, 546, 547, 0, 586, 513, 523, 0, - 0, 0, 0, 0, 440, 588, 589, 542, 543, 544, - 545, 549, 546, 547, 586, 0, 0, 0, 0, 0, - 0, 0, 0, 634, 635, 542, 543, 544, 545, 549, - 546, 547, 578, 0, 0, 0, 0, 0, 0, 0, - 0, 579, 580, 542, 543, 544, 545, 549, 546, 547, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 573, 0, 0, 0, 0, 0, 0, 0, 0, - 514, 0, 0, 0, 0, 0, 0, 0, 0, 522, + 315, 478, 645, 153, 673, 465, 460, 501, 458, 461, + 184, 456, 396, 498, 488, 503, 440, 444, 436, 428, + 657, 667, 656, 644, 403, 630, 642, 629, 447, 634, + 450, 627, 315, 143, 553, 184, 255, 250, 149, 447, + 146, 353, 151, 349, 541, 531, 450, 534, 315, 179, + 538, 166, 172, 184, 322, 189, 620, 191, 16, 255, + 207, 250, 239, 586, 174, 250, 255, 587, 184, 447, + 265, 0, 577, 515, 584, 472, 559, 333, 450, 412, + 207, 514, 517, 471, 248, 467, 517, 565, 557, 207, + 315, 569, 315, 364, 207, 207, 207, 189, 249, 207, + 207, 207, 496, 0, 207, 315, 495, 412, 469, 358, + 333, 412, 207, 315, 62, 279, 413, 62, 0, 297, + 283, 486, 298, 244, 62, 241, 183, 62, 62, 62, + 262, 425, 299, 300, 301, 320, 364, 324, 62, 62, + 505, 506, 189, 262, 413, 0, 207, 0, 413, 472, + 0, 207, 593, 207, 62, 62, 507, 508, 62, 207, + 509, 62, 62, 510, 183, 316, 62, 318, 462, 149, + 188, 513, 62, 347, 463, 351, 62, 181, 355, 62, + 343, 357, 404, 62, 396, 462, 342, 262, 345, 207, + 108, 207, 171, 168, 207, 396, 62, 207, 207, 62, + 62, 183, 463, 207, 62, 62, 104, 62, 92, 62, + 406, 91, 249, 62, 97, 462, 364, 102, 62, 110, + 463, 420, 62, 482, 62, 396, 207, 96, 90, 62, + 207, 189, 207, 0, 95, 62, 62, 62, 62, 63, + 94, 72, 93, 62, 0, 62, 62, 62, 86, 62, + 397, 100, 99, 98, 108, 79, 62, 410, 149, 422, + 660, 659, 517, 62, 74, 71, 415, 661, 0, 62, + 0, 70, 62, 311, 69, 311, 311, 62, 283, 0, + 283, 283, 283, 110, 178, 62, 311, 311, 364, 364, + 283, 283, 283, 517, 329, 339, 62, 332, 330, 0, + 326, 283, 525, 0, 207, 207, 62, 308, 313, 310, + 0, 283, 62, 62, 516, 526, 0, 283, 283, 306, + 291, 286, 62, 62, 0, 517, 62, 283, 283, 302, + 303, 283, 576, 304, 643, 573, 0, 0, 0, 0, + 0, 517, 0, 0, 517, 0, 0, 0, 0, 0, + 525, 0, 0, 525, 545, 546, 547, 548, 552, 549, + 550, 0, 516, 526, 0, 516, 526, 589, 0, 0, + 0, 0, 0, 0, 443, 446, 638, 639, 545, 546, + 547, 548, 552, 549, 550, 589, 0, 0, 0, 0, + 0, 0, 0, 0, 591, 592, 545, 546, 547, 548, + 552, 549, 550, 581, 0, 0, 0, 0, 0, 0, + 0, 0, 582, 583, 545, 546, 547, 548, 552, 549, + 550, 0, 581, 0, 0, 0, 0, 565, 0, 640, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 513, 523, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 488, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0}; + 0, 0, 0, 0, 0, 0, 0, 0, 0}; const short QQmlJSGrammar::action_check [] = { - 7, 55, 48, 55, 55, 55, 36, 7, 36, 33, - 55, 7, 7, 33, 37, 7, 7, 61, 60, 60, - 36, 7, 33, 8, 36, 7, 16, 1, 36, 60, - 7, 36, 2, 48, 7, 36, 5, 36, 36, 36, - 79, 5, 36, 60, 33, 33, 55, 60, 7, 36, - 34, 7, 33, 7, 7, 33, 36, 7, 7, 77, - 36, 33, 8, 5, 1, 8, 33, 60, 29, 79, - 36, 33, 17, 8, 37, 79, 33, 60, 33, 8, - 1, 60, 2, 8, 7, 60, 33, 55, 48, 60, - 29, 2, 7, 2, 7, 36, 36, 7, 17, 7, - 33, 66, 7, 61, 48, 31, 36, 1, 36, 33, - 8, 7, 60, 20, 36, 66, 33, 7, 36, 55, - 36, 8, 60, 8, 15, -1, 40, 79, 8, 61, - 61, 62, 10, 8, 61, 62, 40, 51, 8, 15, - 40, 40, 8, 34, 8, 42, 6, 51, 24, 61, - 62, 51, 51, 7, 8, 8, 53, 8, 61, 62, - 20, 8, 8, 8, 8, 15, 61, 62, 61, 62, - 8, 8, 15, 60, -1, 60, 56, 55, 61, 62, - 61, 62, 61, 62, 34, 60, 56, 61, 62, 91, - 92, 34, 56, 50, 60, 15, 29, 54, 0, 91, - 92, 50, 61, 62, 24, 54, 25, 60, 27, 12, - 61, 56, 29, 60, 60, 25, 60, 27, 56, 38, - 25, 25, 27, 27, 61, 62, 12, 15, 38, 61, - 62, 12, 29, 38, 38, 29, 7, -1, 25, 7, - 27, 36, 75, 8, 29, 8, 34, -1, 36, 15, - 25, 38, 27, 86, 57, 7, -1, 89, 75, 7, - 63, -1, 33, 38, 15, 29, 61, 62, 34, 86, - 36, 57, 29, -1, -1, -1, 57, 63, 75, -1, - -1, 75, 63, 34, 15, 36, -1, 61, 62, 86, - 75, -1, 86, 61, 62, -1, 61, 62, 61, 62, - -1, 86, -1, 34, 25, 36, 27, 15, -1, 61, - 62, 75, -1, 61, 62, 18, 19, 38, 75, 93, - 15, -1, 86, 94, -1, 33, 34, -1, 36, 86, - 18, 19, 18, 19, 18, 19, 47, -1, 33, 34, - -1, 36, 45, 46, 98, 99, 100, 101, 102, 103, - 61, 62, -1, -1, 23, 24, -1, 45, 46, 45, - 46, 45, 46, 32, 23, 24, 35, -1, 37, -1, - -1, -1, -1, 32, 23, 24, 35, -1, 37, -1, - 23, 24, 93, 32, -1, 25, 35, 27, 37, 32, - -1, -1, 35, 29, 37, 23, 24, -1, 38, -1, - -1, -1, -1, 31, 32, 23, 24, 35, -1, 37, - -1, -1, -1, 31, 32, 23, 24, 35, 29, 37, - -1, -1, -1, 31, 32, 23, 24, 35, -1, 37, - 66, 67, 68, 31, 32, 23, 24, 35, -1, 37, - -1, -1, -1, 31, 32, -1, -1, 35, -1, 37, - 29, -1, -1, 29, -1, 66, 67, 68, 94, 95, - 96, 29, -1, -1, 29, -1, 23, 24, 29, -1, - -1, 29, 15, -1, 29, 32, -1, -1, 35, -1, - 37, 36, -1, 94, 95, 96, 29, 66, 67, 68, + 8, 61, 60, 8, 48, 1, 0, 2, 55, 79, + 33, 1, 60, 48, 7, 79, 66, 60, 17, 8, + 36, 66, 29, 8, 60, 37, 60, 33, 79, 33, + 1, 33, 2, 36, 20, 55, 8, 79, 7, 61, + 55, 60, 29, 7, 5, 1, 5, 48, 48, 33, + 33, 5, 7, 33, 17, 37, 31, 36, 55, 8, + 33, 7, 60, 77, 7, 33, 7, 7, 60, 36, + 61, 36, 60, 7, 36, 8, 36, 16, 36, 36, + 7, 55, 34, 36, 7, 7, 7, 7, 36, 55, + 7, 36, 7, 7, 36, 7, 7, 7, 36, 2, + 36, 7, 33, 7, 55, 7, 36, 7, 55, 33, + 60, 7, -1, 8, 33, 8, 60, 42, 36, -1, + 36, 8, -1, 60, 33, 33, 61, 62, 53, 36, + 8, 36, 8, -1, 33, 36, 8, 50, 36, -1, + 8, 54, 8, 40, 15, 8, 15, 8, 40, 8, + 8, -1, -1, 24, 51, 15, -1, 10, 15, 51, + 8, 56, 50, 92, 93, 34, 54, 60, 8, 56, + 61, 62, 61, 62, 34, 61, 62, 34, 56, 61, + 62, 61, 62, 8, 60, 40, 61, 62, 60, 40, + 56, 7, 60, 6, 61, 62, 51, 60, 15, 60, + 51, 60, 55, 61, 7, 61, 62, 20, 56, 61, + 62, 92, 93, 15, 61, 62, 12, 34, 29, 36, + 60, 8, 24, 15, 29, 25, 12, 27, 25, -1, + 27, 7, 15, 12, 29, 29, 61, 62, 38, 7, + -1, 38, 34, -1, 36, 61, 62, 25, 29, 27, + 25, 34, 27, 36, 61, 62, 25, 29, 27, 8, + 38, 57, -1, 38, 75, 33, 25, 63, 27, 38, + 75, 57, 29, -1, 61, 62, 87, 63, 57, 38, + 75, 75, 87, 90, 63, 61, 62, 25, 25, 27, + 27, 36, 87, 87, 75, 25, 25, 27, 27, 7, + 38, 38, 15, 75, 7, 8, 87, -1, 38, 38, + 61, 62, 61, 62, -1, 87, 61, 62, 75, 15, + 25, 34, 27, 36, 18, 19, 15, 95, 18, 19, + 87, 18, 19, 38, 18, 19, -1, 33, 34, -1, + 36, -1, -1, 94, 33, 34, -1, 36, -1, 47, + -1, 45, 46, 61, 62, 45, 46, -1, 45, 46, + -1, 45, 46, 61, 62, 23, 24, -1, -1, 23, + 24, -1, -1, -1, 32, 23, 24, 35, 32, 37, + -1, 35, 29, 37, 32, 23, 24, 35, 29, 37, + -1, 23, 24, 29, 32, -1, 94, 35, 29, 37, + 32, 29, -1, 35, 25, 37, 27, 99, 100, 101, + 102, 103, 104, -1, 29, -1, -1, 38, -1, 66, + 67, 68, -1, -1, -1, 66, 67, 68, -1, -1, + 66, 67, 68, -1, -1, 66, 67, 68, 66, 67, + 68, -1, -1, -1, -1, -1, 29, -1, 95, 96, + 97, 66, 67, 68, 95, 96, 97, -1, -1, 95, + 96, 97, -1, -1, 95, 96, 97, 95, 96, 97, + 29, -1, -1, -1, -1, -1, -1, -1, 29, -1, + 95, 96, 97, 66, 67, 68, 23, 24, 29, -1, + -1, 29, -1, -1, 31, 32, 29, -1, 35, -1, + 37, -1, -1, 36, -1, -1, -1, 66, 67, 68, + -1, 29, 95, 96, 97, 66, 67, 68, 36, -1, + -1, -1, -1, -1, -1, 66, 67, 68, 66, 67, + 68, -1, -1, 66, 67, 68, 95, 96, 97, 15, + -1, -1, -1, -1, 95, 96, 97, 15, 66, 67, + 68, -1, -1, 29, 95, 96, 97, 95, 96, 97, + -1, 29, 95, 96, 97, 29, -1, -1, -1, -1, + 29, -1, 36, 29, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, 29, -1, -1, -1, -1, 66, 67, 68, -1, -1, -1, -1, -1, 66, 67, - 68, 66, 67, 68, -1, 66, 67, 68, 66, 67, - 68, 66, 67, 68, -1, 94, 95, 96, 94, 95, - 96, -1, -1, 66, 67, 68, 94, 95, 96, 94, - 95, 96, 15, 94, 95, 96, 94, 95, 96, 94, - 95, 96, -1, 29, -1, -1, 29, -1, -1, 29, - 36, 94, 95, 96, 29, -1, -1, 29, -1, -1, - -1, 36, -1, -1, -1, 29, -1, -1, 29, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 66, 67, 68, 66, 67, 68, 66, 67, 68, 61, - 62, 66, 67, 68, 66, 67, 68, 61, 62, -1, - 61, 62, 66, 67, 68, 66, 67, 68, 94, 95, - 96, 94, 95, 96, 94, 95, 96, 29, -1, 94, - 95, 96, 94, 95, 96, 29, 15, -1, 29, -1, - 94, 95, 96, 94, 95, 96, -1, -1, -1, -1, - 29, -1, -1, -1, -1, -1, -1, -1, -1, 61, - 62, -1, -1, -1, 66, 67, 68, 61, 62, -1, - 61, 62, 66, 67, 68, 66, 67, 68, -1, -1, - -1, -1, -1, -1, -1, 3, -1, 66, 67, 68, - -1, -1, 94, 95, 96, 13, -1, -1, -1, 17, - 94, 95, 96, 94, 95, 96, -1, -1, 26, -1, - 28, -1, -1, 31, -1, 94, 95, 96, -1, -1, - -1, 39, -1, 41, 42, -1, -1, 3, -1, -1, - -1, 49, -1, -1, 52, 53, -1, 13, -1, -1, - 58, 17, -1, -1, -1, -1, 64, -1, -1, -1, - 26, -1, 28, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 80, 39, -1, 41, 42, -1, -1, -1, - -1, -1, -1, 49, -1, -1, 52, 53, -1, -1, - -1, -1, 58, -1, -1, -1, -1, -1, 64, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 80, -1, -1, -1, -1, -1, + 68, -1, 66, 67, 68, 61, 62, 66, 67, 68, + 66, 67, 68, -1, -1, -1, -1, 61, 62, 95, + 96, 97, 66, 67, 68, -1, 29, 95, 96, 97, + -1, 95, 96, 97, -1, -1, 95, 96, 97, 95, + 96, 97, 29, -1, -1, 29, -1, -1, 15, -1, + -1, 95, 96, 97, -1, -1, -1, -1, 61, 62, + -1, -1, 29, 66, 67, 68, -1, -1, -1, -1, + -1, -1, -1, -1, 61, 62, -1, 61, 62, 66, + 67, 68, 66, 67, 68, -1, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, 66, + 67, 68, -1, -1, -1, -1, -1, -1, 95, 96, + 97, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, 3, -1, -1, -1, -1, -1, -1, 95, 96, + 97, 13, -1, -1, -1, 17, -1, -1, -1, -1, + 29, -1, -1, -1, 26, -1, 28, -1, -1, 31, + -1, -1, -1, -1, -1, -1, -1, 39, -1, 41, + 42, -1, -1, -1, -1, -1, -1, 49, -1, -1, + 52, 53, 61, 62, -1, -1, 58, 66, 67, 68, + -1, -1, 64, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 80, -1, + -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, + -1, -1, -1, -1, -1, -1, 12, 13, 3, -1, + -1, -1, -1, -1, -1, -1, 22, -1, 13, -1, + -1, -1, 17, 29, -1, -1, -1, 33, 34, -1, + 36, 26, -1, 28, -1, -1, -1, 43, -1, -1, + -1, 47, -1, -1, 39, -1, 41, 42, -1, -1, + -1, -1, -1, -1, 49, -1, -1, 52, 53, 65, + 66, 67, 68, 58, 70, -1, -1, -1, -1, 64, + -1, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, -1, 88, -1, -1, 80, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, + -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, - 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, + 70, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 12, 13, -1, + -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, + -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, + -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 81, - 82, 83, -1, -1, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 12, 13, -1, -1, -1, -1, + 65, 66, 67, 68, -1, 70, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 81, 82, 83, -1, + -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, - 68, -1, 70, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 81, 82, 83, -1, -1, -1, 87, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, - -1, -1, -1, -1, -1, -1, -1, -1, 10, -1, + 68, -1, 70, -1, -1, -1, -1, 75, -1, -1, + -1, -1, -1, 81, 82, 83, 84, 85, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 10, + -1, 12, 13, -1, -1, -1, -1, -1, -1, -1, + -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, + -1, -1, 33, 34, -1, 36, -1, -1, -1, -1, + -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, + -1, -1, -1, -1, 55, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, + -1, -1, -1, -1, 75, -1, -1, -1, -1, -1, + 81, 82, 83, 84, 85, -1, -1, 88, -1, -1, + -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 10, -1, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, -1, -1, -1, 43, + -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, + -1, 55, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, -1, -1, + -1, 75, -1, -1, -1, -1, -1, 81, 82, 83, + 84, 85, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, -1, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, + 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, + -1, -1, -1, 81, 82, 83, -1, -1, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, - -1, 33, 34, -1, 36, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, - -1, -1, -1, 75, -1, -1, -1, -1, -1, 81, - 82, 83, 84, -1, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 10, -1, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, -1, -1, -1, 43, -1, -1, - -1, 47, -1, -1, -1, -1, -1, -1, -1, 55, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, -1, -1, -1, 75, - -1, -1, -1, -1, -1, 81, 82, 83, 84, -1, - -1, 87, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 10, -1, 12, 13, -1, -1, -1, -1, -1, -1, - -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, - -1, -1, -1, 33, 34, -1, 36, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, 55, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, - 70, -1, -1, -1, -1, 75, -1, -1, -1, -1, - -1, 81, 82, 83, 84, -1, -1, 87, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, - -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, - -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, - -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, - -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, - -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, 87, -1, -1, -1, -1, -1, -1, 94, - 95, 96, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 7, -1, -1, -1, 11, 12, 13, -1, -1, - -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, - -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, - 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, - -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 65, - 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, - 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, - -1, 87, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, - -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, - -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, - -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, - 51, -1, 53, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, - -1, 72, -1, 74, 75, 76, -1, -1, -1, -1, - 81, 82, 83, -1, -1, -1, 87, -1, -1, -1, - -1, -1, -1, 94, 95, 96, -1, -1, -1, -1, + 72, -1, 74, 75, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 7, -1, -1, -1, 11, + 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, + 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, + -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, + 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, + -1, 53, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, + 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, + 82, 83, -1, -1, -1, -1, 88, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, @@ -889,8 +910,18 @@ const short QQmlJSGrammar::action_check [] = { 53, -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, - 83, -1, -1, -1, 87, -1, -1, -1, -1, -1, - -1, 94, 95, 96, -1, -1, -1, -1, -1, -1, + 83, -1, -1, -1, -1, 88, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, + -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, + -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, -1, 56, -1, -1, -1, -1, -1, -1, -1, + -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, + 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, -1, -1, -1, 33, 34, @@ -899,46 +930,27 @@ const short QQmlJSGrammar::action_check [] = { -1, 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, -1, -1, 81, 82, 83, -1, - -1, -1, 87, -1, -1, -1, -1, -1, -1, 94, - 95, 96, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 8, -1, -1, 11, 12, 13, -1, -1, -1, - -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, - -1, -1, 29, -1, -1, -1, 33, 34, -1, 36, - -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, - 47, -1, -1, -1, 51, -1, 53, -1, -1, 56, - -1, -1, -1, -1, -1, -1, -1, -1, 65, 66, - 67, 68, -1, 70, -1, 72, -1, 74, -1, 76, - -1, -1, -1, -1, 81, 82, 83, -1, -1, -1, - 87, -1, -1, -1, -1, -1, -1, 94, 95, 96, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 8, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, -1, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, 56, -1, -1, - -1, -1, -1, -1, -1, -1, 65, 66, 67, 68, - -1, 70, -1, 72, -1, 74, -1, 76, -1, -1, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, -1, 94, 95, 96, -1, -1, - -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, - -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, - -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, -1, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, - 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, - -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, - -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, - 29, 30, -1, -1, 33, 34, -1, 36, -1, -1, - -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, - -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, - -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, - 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, - -1, -1, 81, 82, 83, -1, -1, -1, 87, -1, - -1, -1, -1, -1, -1, 94, 95, 96, -1, -1, + -1, -1, -1, 88, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 8, -1, -1, 11, 12, 13, -1, -1, + -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, + -1, -1, -1, 29, -1, -1, -1, 33, 34, -1, + 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, + -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, + 56, -1, -1, -1, -1, -1, -1, -1, -1, 65, + 66, 67, 68, -1, 70, -1, 72, -1, 74, -1, + 76, -1, -1, -1, -1, 81, 82, 83, -1, -1, + -1, -1, 88, -1, -1, -1, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, + -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, + 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, + -1, 51, -1, 53, -1, -1, -1, -1, -1, -1, + -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, + 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, + -1, 81, 82, 83, -1, -1, -1, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, @@ -947,38 +959,48 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, -1, -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, 78, -1, -1, 81, 82, 83, - -1, -1, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 11, 12, 13, -1, -1, -1, -1, + -1, -1, -1, -1, 22, -1, -1, -1, -1, -1, + -1, 29, 30, -1, -1, 33, 34, -1, 36, -1, + -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, + -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, + -1, -1, -1, 61, -1, -1, -1, 65, 66, 67, + 68, 69, 70, -1, 72, 73, 74, -1, 76, -1, + 78, -1, -1, 81, 82, 83, -1, -1, -1, -1, + 88, -1, -1, -1, -1, -1, -1, 95, 96, 97, + -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, + 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, + -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, + -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, 43, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 59, -1, -1, -1, -1, -1, + -1, 66, 67, 68, 69, 70, 71, -1, 73, 74, + 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, + 85, 86, 87, -1, -1, -1, -1, -1, -1, -1, + 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 43, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, -1, -1, - -1, -1, -1, -1, 66, 67, 68, 69, 70, 71, + -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, 86, -1, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, - 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, - 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, 31, 32, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, 59, - -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, - 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, -1, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, - -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, - -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, - -1, 29, 30, 31, 32, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, 43, -1, -1, -1, 47, + 82, 83, 84, 85, 86, 87, -1, -1, -1, -1, + -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, + 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, + -1, 20, 21, 22, -1, -1, -1, -1, -1, -1, + 29, 30, 31, 32, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 43, -1, -1, -1, 47, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 59, -1, -1, -1, -1, -1, 65, 66, 67, - 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, - 78, -1, -1, 81, 82, 83, 84, 85, 86, -1, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, + 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, + 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, + -1, -1, 81, 82, 83, 84, 85, 86, 87, -1, + -1, -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, 10, 11, -1, -1, 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, -1, -1, @@ -988,104 +1010,102 @@ const short QQmlJSGrammar::action_check [] = { -1, -1, -1, 59, -1, -1, -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, -1, 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, 84, 85, - 86, -1, -1, -1, -1, -1, -1, -1, 94, 95, - 96, -1, -1, -1, -1, -1, -1, -1, -1, -1, - 4, -1, -1, -1, -1, 9, -1, 11, 12, 13, - 14, -1, -1, -1, -1, -1, -1, 21, 22, -1, - -1, -1, -1, -1, -1, 29, 30, -1, -1, 33, - 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, - 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, - -1, -1, -1, -1, -1, 59, -1, 61, -1, -1, - -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, - 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, - 84, 85, -1, 87, -1, -1, -1, -1, -1, -1, - 94, 95, 96, -1, -1, -1, -1, -1, -1, -1, - -1, -1, 4, -1, -1, -1, -1, 9, -1, 11, - 12, 13, 14, -1, -1, -1, -1, -1, -1, 21, - 22, -1, -1, -1, -1, -1, -1, 29, 30, -1, - -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, - 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, - -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, - -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, - 72, 73, 74, 75, 76, 77, 78, -1, -1, 81, - 82, 83, 84, 85, -1, 87, -1, -1, -1, -1, - -1, -1, 94, 95, 96, -1, -1, -1, -1, -1, - -1, -1, -1, -1, 4, 5, 6, -1, -1, 9, - 10, 11, 12, 13, 14, -1, 16, -1, -1, -1, - 20, 21, 22, -1, -1, -1, -1, -1, -1, 29, - 30, 31, 32, 33, 34, -1, 36, -1, -1, -1, + 86, 87, -1, -1, -1, -1, -1, -1, -1, 95, + 96, 97, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 4, -1, -1, -1, -1, 9, -1, 11, 12, + 13, 14, -1, -1, -1, -1, -1, -1, 21, 22, + -1, -1, -1, -1, -1, -1, 29, 30, -1, -1, + 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, + 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, + 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, + -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 74, 75, 76, 77, 78, -1, -1, 81, 82, + 83, 84, 85, 86, -1, 88, -1, -1, -1, -1, + -1, -1, 95, 96, 97, -1, -1, -1, -1, -1, + -1, -1, -1, -1, 4, -1, -1, -1, -1, 9, + -1, 11, 12, 13, 14, -1, -1, -1, -1, -1, + -1, 21, 22, -1, -1, -1, -1, -1, -1, 29, + 30, -1, -1, 33, 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, -1, - -1, 81, 82, 83, 84, 85, 86, 87, -1, -1, - -1, -1, -1, -1, 94, 95, 96, -1, -1, -1, - -1, -1, -1, -1, -1, -1, 4, 5, 6, -1, - -1, 9, 10, 11, 12, 13, 14, -1, 16, -1, - -1, -1, 20, 21, 22, -1, -1, -1, -1, -1, - -1, 29, 30, 31, 32, 33, 34, -1, 36, -1, - -1, -1, 40, -1, 42, 43, 44, -1, -1, 47, - -1, -1, -1, 51, -1, 53, -1, 55, -1, -1, - -1, 59, -1, 61, -1, -1, -1, 65, 66, 67, - 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, - 78, -1, -1, 81, 82, 83, 84, 85, 86, 87, - -1, -1, -1, -1, -1, -1, 94, 95, 96, -1, - -1, -1, -1, -1, -1, -1, -1, -1, + -1, 81, 82, 83, 84, 85, 86, -1, 88, -1, + -1, -1, -1, -1, -1, 95, 96, 97, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 4, 5, 6, + -1, -1, 9, 10, 11, 12, 13, 14, -1, 16, + -1, -1, -1, 20, 21, 22, -1, -1, -1, -1, + -1, -1, 29, 30, 31, 32, 33, 34, -1, 36, + -1, -1, -1, 40, -1, 42, 43, 44, -1, -1, + 47, -1, -1, -1, 51, -1, 53, -1, -1, -1, + -1, -1, 59, -1, 61, -1, -1, -1, 65, 66, + 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, + 77, 78, -1, -1, 81, 82, 83, 84, 85, 86, + 87, 88, -1, -1, -1, -1, -1, -1, 95, 96, + 97, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 4, 5, 6, -1, -1, 9, 10, 11, 12, 13, + 14, -1, 16, -1, -1, -1, 20, 21, 22, -1, + -1, -1, -1, -1, -1, 29, 30, 31, 32, 33, + 34, -1, 36, -1, -1, -1, 40, -1, 42, 43, + 44, -1, -1, 47, -1, -1, -1, 51, -1, 53, + -1, 55, -1, -1, -1, 59, -1, 61, -1, -1, + -1, 65, 66, 67, 68, 69, 70, 71, 72, 73, + 74, 75, 76, 77, 78, -1, -1, 81, 82, 83, + 84, 85, 86, 87, 88, -1, -1, -1, -1, -1, + -1, 95, 96, 97, -1, -1, -1, -1, -1, -1, + -1, -1, -1, - 77, 14, 18, 18, 18, 32, 18, 3, 32, 42, - 9, 3, 3, 18, 42, 3, 18, 18, 3, 22, - 18, 32, 32, 18, 18, 25, 32, 3, 18, 18, - 3, 18, 3, 42, 18, 14, 22, 18, 18, 22, - 22, 18, 100, 18, 18, 42, 18, 3, 105, 42, - 3, 3, 18, 14, 32, 18, 3, 25, 25, 18, - 3, 25, 3, 18, 18, 3, 103, 18, 18, 2, - -1, 3, 42, 42, 18, 14, 25, 42, 14, 2, - 42, -1, 42, 18, 42, 18, 2, 4, -1, -1, - 18, -1, -1, 18, -1, 18, -1, 54, -1, 56, - 44, 18, 18, 18, 18, -1, 54, -1, 54, 54, - 56, 56, 51, 42, 54, 51, 56, 54, 46, 56, - 45, 50, 70, 54, 54, 56, 56, 3, 54, 42, - 45, 18, 46, 59, 54, 54, 54, 50, 2, 59, - 59, 59, 54, 54, 11, 12, 78, 59, 59, 54, - 54, 56, 56, 54, 18, 2, 14, 18, 4, 54, - 54, 56, 56, 54, 54, 23, 3, 68, 58, 60, - 54, 18, 18, 14, 109, 2, 60, 2, 19, 54, - 54, 54, 43, 38, 59, 59, 59, 42, 54, 54, - 56, 18, 54, 18, 59, 54, 2, 59, 54, 58, - 54, 2, 58, 18, 54, 59, 2, 94, 2, 54, - 18, 4, 18, 2, 18, 3, 66, 18, 18, 64, - 3, 54, 18, 56, 18, 18, 54, 2, 56, 18, - 45, 54, 18, 56, 54, 14, 14, 57, 2, 47, - 19, 78, 46, 18, 44, 54, 2, 18, 57, 54, - 2, 54, 2, 56, 18, 60, 54, 54, 56, 56, - 14, -1, 18, 54, 54, 19, 18, -1, 18, 60, - 54, 18, 62, 51, 45, 59, -1, 54, -1, -1, - 54, 54, 59, 67, 54, 59, 59, 54, 2, 59, - 78, -1, 59, 63, 61, 78, 69, 71, -1, 76, - 47, 48, -1, 54, 18, 54, 92, -1, 59, 54, - 59, 54, 61, 54, 59, -1, 59, 54, 59, 54, - 65, -1, 59, 54, 59, 76, 61, -1, 59, 14, - 61, 14, 2, 76, 19, 76, 21, 5, 88, 76, - 23, -1, -1, -1, -1, -1, 14, -1, 18, -1, - -1, -1, 35, 36, -1, 23, -1, 42, 25, 26, - 27, 28, 29, 30, 31, -1, 14, 35, 36, -1, - -1, -1, -1, -1, 88, 23, 24, 25, 26, 27, - 28, 29, 30, 31, 14, -1, -1, -1, -1, -1, - -1, -1, -1, 23, 24, 25, 26, 27, 28, 29, - 30, 31, 14, -1, -1, -1, -1, -1, -1, -1, - -1, 23, 24, 25, 26, 27, 28, 29, 30, 31, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 5, -1, -1, -1, -1, -1, -1, -1, -1, - 14, -1, -1, -1, -1, -1, -1, -1, -1, 23, + 3, 42, 9, 77, 18, 3, 25, 42, 18, 25, + 18, 105, 18, 42, 42, 3, 100, 3, 103, 3, + 14, 18, 14, 22, 42, 18, 22, 32, 3, 18, + 25, 32, 3, 3, 14, 18, 18, 18, 42, 3, + 42, 3, 42, 3, 18, 32, 25, 32, 3, 3, + 18, 42, 42, 18, 3, 18, 22, 18, 3, 18, + 18, 18, 18, 32, 42, 18, 18, 18, 18, 3, + 3, -1, 18, 2, 22, 18, 18, 18, 25, 14, + 18, 4, 14, 2, 2, 2, 14, 19, 32, 18, + 3, 19, 3, 2, 18, 18, 18, 18, 4, 18, + 18, 18, 38, -1, 18, 3, 42, 14, 3, 18, + 18, 14, 18, 3, 54, 54, 51, 54, -1, 59, + 59, 45, 59, 45, 54, 46, 56, 54, 54, 54, + 2, 45, 59, 59, 59, 2, 2, 2, 54, 54, + 56, 56, 18, 2, 51, -1, 18, -1, 51, 18, + -1, 18, 18, 18, 54, 54, 56, 56, 54, 18, + 56, 54, 54, 56, 56, 78, 54, 78, 56, 42, + 46, 109, 54, 2, 56, 2, 54, 50, 2, 54, + 78, 2, 2, 54, 18, 56, 94, 2, 78, 18, + 18, 18, 70, 68, 18, 18, 54, 18, 18, 54, + 54, 56, 56, 18, 54, 54, 64, 54, 58, 54, + 44, 58, 4, 54, 59, 56, 2, 66, 54, 47, + 56, 44, 54, 92, 54, 18, 18, 59, 58, 54, + 18, 18, 18, -1, 59, 54, 54, 54, 54, 57, + 59, 57, 59, 54, -1, 54, 54, 54, 59, 54, + 43, 60, 60, 60, 18, 60, 54, 45, 42, 46, + 11, 12, 14, 54, 62, 56, 50, 19, -1, 54, + -1, 56, 54, 54, 56, 54, 54, 54, 59, -1, + 59, 59, 59, 47, 48, 54, 54, 54, 2, 2, + 59, 59, 59, 14, 71, 76, 54, 76, 76, -1, + 69, 59, 23, -1, 18, 18, 54, 65, 76, 76, + -1, 59, 54, 54, 35, 36, -1, 59, 59, 67, + 61, 63, 54, 54, -1, 14, 54, 59, 59, 61, + 61, 59, 5, 61, 23, 5, -1, -1, -1, -1, + -1, 14, -1, -1, 14, -1, -1, -1, -1, -1, + 23, -1, -1, 23, 25, 26, 27, 28, 29, 30, + 31, -1, 35, 36, -1, 35, 36, 14, -1, -1, + -1, -1, -1, -1, 88, 88, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 14, -1, -1, -1, -1, + -1, -1, -1, -1, 23, 24, 25, 26, 27, 28, + 29, 30, 31, 14, -1, -1, -1, -1, -1, -1, + -1, -1, 23, 24, 25, 26, 27, 28, 29, 30, + 31, -1, 14, -1, -1, -1, -1, 19, -1, 21, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, 35, 36, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + 42, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1}; + -1, -1, -1, -1, -1, -1, -1, -1, -1}; QT_END_NAMESPACE diff --git a/src/qml/parser/qqmljsgrammar_p.h b/src/qml/parser/qqmljsgrammar_p.h index 050ef6c288..b4f762d28b 100644 --- a/src/qml/parser/qqmljsgrammar_p.h +++ b/src/qml/parser/qqmljsgrammar_p.h @@ -61,23 +61,23 @@ class QQmlJSGrammar public: enum VariousConstants { EOF_SYMBOL = 0, - REDUCE_HERE = 105, - SHIFT_THERE = 104, + REDUCE_HERE = 106, + SHIFT_THERE = 105, T_AND = 1, T_AND_AND = 2, T_AND_EQ = 3, - T_AS = 93, + T_AS = 94, T_AUTOMATIC_SEMICOLON = 62, T_BREAK = 4, T_CASE = 5, T_CATCH = 6, T_COLON = 7, T_COMMA = 8, - T_COMMENT = 88, - T_COMPATIBILITY_SEMICOLON = 89, + T_COMMENT = 89, + T_COMPATIBILITY_SEMICOLON = 90, T_CONST = 84, T_CONTINUE = 9, - T_DEBUGGER = 85, + T_DEBUGGER = 86, T_DEFAULT = 10, T_DELETE = 11, T_DIVIDE_ = 12, @@ -88,19 +88,19 @@ public: T_EQ = 17, T_EQ_EQ = 18, T_EQ_EQ_EQ = 19, - T_ERROR = 97, + T_ERROR = 98, T_FALSE = 83, - T_FEED_JS_EXPRESSION = 101, - T_FEED_JS_PROGRAM = 103, - T_FEED_JS_SOURCE_ELEMENT = 102, - T_FEED_JS_STATEMENT = 100, - T_FEED_UI_OBJECT_MEMBER = 99, - T_FEED_UI_PROGRAM = 98, + T_FEED_JS_EXPRESSION = 102, + T_FEED_JS_PROGRAM = 104, + T_FEED_JS_SOURCE_ELEMENT = 103, + T_FEED_JS_STATEMENT = 101, + T_FEED_UI_OBJECT_MEMBER = 100, + T_FEED_UI_PROGRAM = 99, T_FINALLY = 20, T_FOR = 21, T_FUNCTION = 22, T_GE = 23, - T_GET = 95, + T_GET = 96, T_GT = 24, T_GT_GT = 25, T_GT_GT_EQ = 26, @@ -108,12 +108,13 @@ public: T_GT_GT_GT_EQ = 28, T_IDENTIFIER = 29, T_IF = 30, - T_IMPORT = 91, + T_IMPORT = 92, T_IN = 31, T_INSTANCEOF = 32, T_LBRACE = 33, T_LBRACKET = 34, T_LE = 35, + T_LET = 85, T_LPAREN = 36, T_LT = 37, T_LT_LT = 38, @@ -121,34 +122,34 @@ public: T_MINUS = 40, T_MINUS_EQ = 41, T_MINUS_MINUS = 42, - T_MULTILINE_STRING_LITERAL = 87, + T_MULTILINE_STRING_LITERAL = 88, T_NEW = 43, T_NOT = 44, T_NOT_EQ = 45, T_NOT_EQ_EQ = 46, T_NULL = 81, T_NUMERIC_LITERAL = 47, - T_ON = 94, + T_ON = 95, T_OR = 48, T_OR_EQ = 49, T_OR_OR = 50, T_PLUS = 51, T_PLUS_EQ = 52, T_PLUS_PLUS = 53, - T_PRAGMA = 92, + T_PRAGMA = 93, T_PROPERTY = 66, - T_PUBLIC = 90, + T_PUBLIC = 91, T_QUESTION = 54, T_RBRACE = 55, T_RBRACKET = 56, T_READONLY = 68, T_REMAINDER = 57, T_REMAINDER_EQ = 58, - T_RESERVED_WORD = 86, + T_RESERVED_WORD = 87, T_RETURN = 59, T_RPAREN = 60, T_SEMICOLON = 61, - T_SET = 96, + T_SET = 97, T_SIGNAL = 67, T_STAR = 63, T_STAR_EQ = 64, @@ -167,15 +168,15 @@ public: T_XOR = 79, T_XOR_EQ = 80, - ACCEPT_STATE = 674, - RULE_COUNT = 361, - STATE_COUNT = 675, - TERMINAL_COUNT = 106, + ACCEPT_STATE = 678, + RULE_COUNT = 363, + STATE_COUNT = 679, + TERMINAL_COUNT = 107, NON_TERMINAL_COUNT = 111, - GOTO_INDEX_OFFSET = 675, - GOTO_INFO_OFFSET = 3078, - GOTO_CHECK_OFFSET = 3078 + GOTO_INDEX_OFFSET = 679, + GOTO_INFO_OFFSET = 3203, + GOTO_CHECK_OFFSET = 3203 }; static const char *const spell []; diff --git a/src/qml/parser/qqmljskeywords_p.h b/src/qml/parser/qqmljskeywords_p.h index 84ebe5f210..8b789526a5 100644 --- a/src/qml/parser/qqmljskeywords_p.h +++ b/src/qml/parser/qqmljskeywords_p.h @@ -106,6 +106,13 @@ static inline int classify3(const QChar *s, bool qmlMode) { } } } + else if (s[0].unicode() == 'l') { + if (s[1].unicode() == 'e') { + if (s[2].unicode() == 't') { + return int(Lexer::T_LET); + } + } + } else if (s[0].unicode() == 'n') { if (s[1].unicode() == 'e') { if (s[2].unicode() == 'w') { @@ -278,7 +285,7 @@ static inline int classify5(const QChar *s, bool qmlMode) { if (s[2].unicode() == 'n') { if (s[3].unicode() == 's') { if (s[4].unicode() == 't') { - return qmlMode ? int(Lexer::T_CONST) : int(Lexer::T_RESERVED_WORD); + return int(Lexer::T_CONST); } } } diff --git a/src/qml/parser/qqmljsparser.cpp b/src/qml/parser/qqmljsparser.cpp index 50518a92ee..636b959097 100644 --- a/src/qml/parser/qqmljsparser.cpp +++ b/src/qml/parser/qqmljsparser.cpp @@ -914,21 +914,21 @@ case 116: { sym(1).Node = node; } break; -case 152: { +case 153: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 153: { +case 154: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 154: { +case 155: { AST::NewMemberExpression *node = new (pool) AST::NewMemberExpression(sym(2).Expression, sym(4).ArgumentList); node->newToken = loc(1); node->lparenToken = loc(3); @@ -936,384 +936,384 @@ case 154: { sym(1).Node = node; } break; -case 156: { +case 157: { AST::NewExpression *node = new (pool) AST::NewExpression(sym(2).Expression); node->newToken = loc(1); sym(1).Node = node; } break; -case 157: { +case 158: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 158: { +case 159: { AST::CallExpression *node = new (pool) AST::CallExpression(sym(1).Expression, sym(3).ArgumentList); node->lparenToken = loc(2); node->rparenToken = loc(4); sym(1).Node = node; } break; -case 159: { +case 160: { AST::ArrayMemberExpression *node = new (pool) AST::ArrayMemberExpression(sym(1).Expression, sym(3).Expression); node->lbracketToken = loc(2); node->rbracketToken = loc(4); sym(1).Node = node; } break; -case 160: { +case 161: { AST::FieldMemberExpression *node = new (pool) AST::FieldMemberExpression(sym(1).Expression, stringRef(3)); node->dotToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 161: { +case 162: { sym(1).Node = 0; } break; -case 162: { +case 163: { sym(1).Node = sym(1).ArgumentList->finish(); } break; -case 163: { +case 164: { sym(1).Node = new (pool) AST::ArgumentList(sym(1).Expression); } break; -case 164: { +case 165: { AST::ArgumentList *node = new (pool) AST::ArgumentList(sym(1).ArgumentList, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 168: { +case 169: { AST::PostIncrementExpression *node = new (pool) AST::PostIncrementExpression(sym(1).Expression); node->incrementToken = loc(2); sym(1).Node = node; } break; -case 169: { +case 170: { AST::PostDecrementExpression *node = new (pool) AST::PostDecrementExpression(sym(1).Expression); node->decrementToken = loc(2); sym(1).Node = node; } break; -case 171: { +case 172: { AST::DeleteExpression *node = new (pool) AST::DeleteExpression(sym(2).Expression); node->deleteToken = loc(1); sym(1).Node = node; } break; -case 172: { +case 173: { AST::VoidExpression *node = new (pool) AST::VoidExpression(sym(2).Expression); node->voidToken = loc(1); sym(1).Node = node; } break; -case 173: { +case 174: { AST::TypeOfExpression *node = new (pool) AST::TypeOfExpression(sym(2).Expression); node->typeofToken = loc(1); sym(1).Node = node; } break; -case 174: { +case 175: { AST::PreIncrementExpression *node = new (pool) AST::PreIncrementExpression(sym(2).Expression); node->incrementToken = loc(1); sym(1).Node = node; } break; -case 175: { +case 176: { AST::PreDecrementExpression *node = new (pool) AST::PreDecrementExpression(sym(2).Expression); node->decrementToken = loc(1); sym(1).Node = node; } break; -case 176: { +case 177: { AST::UnaryPlusExpression *node = new (pool) AST::UnaryPlusExpression(sym(2).Expression); node->plusToken = loc(1); sym(1).Node = node; } break; -case 177: { +case 178: { AST::UnaryMinusExpression *node = new (pool) AST::UnaryMinusExpression(sym(2).Expression); node->minusToken = loc(1); sym(1).Node = node; } break; -case 178: { +case 179: { AST::TildeExpression *node = new (pool) AST::TildeExpression(sym(2).Expression); node->tildeToken = loc(1); sym(1).Node = node; } break; -case 179: { +case 180: { AST::NotExpression *node = new (pool) AST::NotExpression(sym(2).Expression); node->notToken = loc(1); sym(1).Node = node; } break; -case 181: { +case 182: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mul, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 182: { +case 183: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Div, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 183: { +case 184: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Mod, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 185: { +case 186: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Add, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 186: { +case 187: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Sub, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 188: { +case 189: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::LShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 189: { +case 190: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::RShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 190: { +case 191: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::URShift, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 192: { +case 193: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 193: { +case 194: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 194: { +case 195: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 195: { +case 196: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 196: { +case 197: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 197: { +case 198: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::In, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 199: { +case 200: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Lt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 200: { +case 201: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Gt, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 201: { +case 202: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Le, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 202: { +case 203: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Ge, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 203: { +case 204: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::InstanceOf, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 205: { +case 206: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 206: { +case 207: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 207: { +case 208: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 208: { +case 209: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 210: { +case 211: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Equal, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 211: { +case 212: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::NotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 212: { +case 213: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 213: { +case 214: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::StrictNotEqual, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 215: { +case 216: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 217: { +case 218: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitAnd, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 219: { +case 220: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 221: { +case 222: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitXor, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 223: { +case 224: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 225: { +case 226: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::BitOr, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 227: { +case 228: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 229: { +case 230: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::And, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 231: { +case 232: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 233: { +case 234: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, QSOperator::Or, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 235: { +case 236: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1321,7 +1321,7 @@ case 235: { sym(1).Node = node; } break; -case 237: { +case 238: { AST::ConditionalExpression *node = new (pool) AST::ConditionalExpression(sym(1).Expression, sym(3).Expression, sym(5).Expression); node->questionToken = loc(2); @@ -1329,189 +1329,200 @@ case 237: { sym(1).Node = node; } break; -case 239: { +case 240: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 241: { +case 242: { AST::BinaryExpression *node = new (pool) AST::BinaryExpression(sym(1).Expression, sym(2).ival, sym(3).Expression); node->operatorToken = loc(2); sym(1).Node = node; } break; -case 242: { +case 243: { sym(1).ival = QSOperator::Assign; } break; -case 243: { +case 244: { sym(1).ival = QSOperator::InplaceMul; } break; -case 244: { +case 245: { sym(1).ival = QSOperator::InplaceDiv; } break; -case 245: { +case 246: { sym(1).ival = QSOperator::InplaceMod; } break; -case 246: { +case 247: { sym(1).ival = QSOperator::InplaceAdd; } break; -case 247: { +case 248: { sym(1).ival = QSOperator::InplaceSub; } break; -case 248: { +case 249: { sym(1).ival = QSOperator::InplaceLeftShift; } break; -case 249: { +case 250: { sym(1).ival = QSOperator::InplaceRightShift; } break; -case 250: { +case 251: { sym(1).ival = QSOperator::InplaceURightShift; } break; -case 251: { +case 252: { sym(1).ival = QSOperator::InplaceAnd; } break; -case 252: { +case 253: { sym(1).ival = QSOperator::InplaceXor; } break; -case 253: { +case 254: { sym(1).ival = QSOperator::InplaceOr; } break; -case 255: { +case 256: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 256: { +case 257: { sym(1).Node = 0; } break; -case 259: { +case 260: { AST::Expression *node = new (pool) AST::Expression(sym(1).Expression, sym(3).Expression); node->commaToken = loc(2); sym(1).Node = node; } break; -case 260: { +case 261: { sym(1).Node = 0; } break; -case 277: { +case 278: { AST::Block *node = new (pool) AST::Block(sym(2).StatementList); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 278: { +case 279: { sym(1).Node = new (pool) AST::StatementList(sym(1).Statement); } break; -case 279: { +case 280: { sym(1).Node = new (pool) AST::StatementList(sym(1).StatementList, sym(2).Statement); } break; -case 280: { +case 281: { sym(1).Node = 0; } break; -case 281: { +case 282: { sym(1).Node = sym(1).StatementList->finish (); } break; -case 283: { - AST::VariableStatement *node = new (pool) AST::VariableStatement( - sym(2).VariableDeclarationList->finish (/*readOnly=*/sym(1).ival == T_CONST)); +case 284: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + if (sym(1).ival == T_LET) + s = AST::VariableDeclaration::BlockScope; + else if (sym(1).ival == T_CONST) + s = AST::VariableDeclaration::ReadOnlyBlockScope; + + AST::VariableStatement *node = new (pool) AST::VariableStatement(sym(2).VariableDeclarationList->finish(s)); node->declarationKindToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 284: { +case 285: { + sym(1).ival = T_LET; +} break; + +case 286: { sym(1).ival = T_CONST; } break; -case 285: { +case 287: { sym(1).ival = T_VAR; } break; -case 286: { +case 288: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 287: { +case 289: { AST::VariableDeclarationList *node = new (pool) AST::VariableDeclarationList( sym(1).VariableDeclarationList, sym(3).VariableDeclaration); node->commaToken = loc(2); sym(1).Node = node; } break; -case 288: { +case 290: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclaration); } break; -case 289: { +case 291: { sym(1).Node = new (pool) AST::VariableDeclarationList(sym(1).VariableDeclarationList, sym(3).VariableDeclaration); } break; -case 290: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); +case 292: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 291: { - AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression); +case 293: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; + AST::VariableDeclaration *node = new (pool) AST::VariableDeclaration(stringRef(1), sym(2).Expression, s); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 292: { +case 294: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 293: { +case 295: { sym(1).Node = 0; } break; -case 295: { +case 297: { // ### TODO: AST for initializer sym(1) = sym(2); } break; -case 296: { +case 298: { sym(1).Node = 0; } break; -case 298: { +case 300: { AST::EmptyStatement *node = new (pool) AST::EmptyStatement(); node->semicolonToken = loc(1); sym(1).Node = node; } break; -case 300: { +case 302: { AST::ExpressionStatement *node = new (pool) AST::ExpressionStatement(sym(1).Expression); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 301: { +case 303: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement, sym(7).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1520,7 +1531,7 @@ case 301: { sym(1).Node = node; } break; -case 302: { +case 304: { AST::IfStatement *node = new (pool) AST::IfStatement(sym(3).Expression, sym(5).Statement); node->ifToken = loc(1); node->lparenToken = loc(2); @@ -1528,7 +1539,7 @@ case 302: { sym(1).Node = node; } break; -case 305: { +case 307: { AST::DoWhileStatement *node = new (pool) AST::DoWhileStatement(sym(2).Statement, sym(5).Expression); node->doToken = loc(1); node->whileToken = loc(3); @@ -1538,7 +1549,7 @@ case 305: { sym(1).Node = node; } break; -case 306: { +case 308: { AST::WhileStatement *node = new (pool) AST::WhileStatement(sym(3).Expression, sym(5).Statement); node->whileToken = loc(1); node->lparenToken = loc(2); @@ -1546,7 +1557,7 @@ case 306: { sym(1).Node = node; } break; -case 307: { +case 309: { AST::ForStatement *node = new (pool) AST::ForStatement(sym(3).Expression, sym(5).Expression, sym(7).Expression, sym(9).Statement); node->forToken = loc(1); @@ -1557,9 +1568,10 @@ case 307: { sym(1).Node = node; } break; -case 308: { +case 310: { + AST::VariableDeclaration::VariableScope s = AST::VariableDeclaration::FunctionScope; AST::LocalForStatement *node = new (pool) AST::LocalForStatement( - sym(4).VariableDeclarationList->finish (/*readOnly=*/false), sym(6).Expression, + sym(4).VariableDeclarationList->finish(s), sym(6).Expression, sym(8).Expression, sym(10).Statement); node->forToken = loc(1); node->lparenToken = loc(2); @@ -1570,7 +1582,7 @@ case 308: { sym(1).Node = node; } break; -case 309: { +case 311: { AST:: ForEachStatement *node = new (pool) AST::ForEachStatement(sym(3).Expression, sym(5).Expression, sym(7).Statement); node->forToken = loc(1); @@ -1580,7 +1592,7 @@ case 309: { sym(1).Node = node; } break; -case 310: { +case 312: { AST::LocalForEachStatement *node = new (pool) AST::LocalForEachStatement( sym(4).VariableDeclaration, sym(6).Expression, sym(8).Statement); node->forToken = loc(1); @@ -1591,14 +1603,14 @@ case 310: { sym(1).Node = node; } break; -case 312: { +case 314: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(); node->continueToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 314: { +case 316: { AST::ContinueStatement *node = new (pool) AST::ContinueStatement(stringRef(2)); node->continueToken = loc(1); node->identifierToken = loc(2); @@ -1606,14 +1618,14 @@ case 314: { sym(1).Node = node; } break; -case 316: { +case 318: { AST::BreakStatement *node = new (pool) AST::BreakStatement(QStringRef()); node->breakToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 318: { +case 320: { AST::BreakStatement *node = new (pool) AST::BreakStatement(stringRef(2)); node->breakToken = loc(1); node->identifierToken = loc(2); @@ -1621,14 +1633,14 @@ case 318: { sym(1).Node = node; } break; -case 320: { +case 322: { AST::ReturnStatement *node = new (pool) AST::ReturnStatement(sym(2).Expression); node->returnToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 321: { +case 323: { AST::WithStatement *node = new (pool) AST::WithStatement(sym(3).Expression, sym(5).Statement); node->withToken = loc(1); node->lparenToken = loc(2); @@ -1636,7 +1648,7 @@ case 321: { sym(1).Node = node; } break; -case 322: { +case 324: { AST::SwitchStatement *node = new (pool) AST::SwitchStatement(sym(3).Expression, sym(5).CaseBlock); node->switchToken = loc(1); node->lparenToken = loc(2); @@ -1644,83 +1656,83 @@ case 322: { sym(1).Node = node; } break; -case 323: { +case 325: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(3); sym(1).Node = node; } break; -case 324: { +case 326: { AST::CaseBlock *node = new (pool) AST::CaseBlock(sym(2).CaseClauses, sym(3).DefaultClause, sym(4).CaseClauses); node->lbraceToken = loc(1); node->rbraceToken = loc(5); sym(1).Node = node; } break; -case 325: { +case 327: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClause); } break; -case 326: { +case 328: { sym(1).Node = new (pool) AST::CaseClauses(sym(1).CaseClauses, sym(2).CaseClause); } break; -case 327: { +case 329: { sym(1).Node = 0; } break; -case 328: { +case 330: { sym(1).Node = sym(1).CaseClauses->finish (); } break; -case 329: { +case 331: { AST::CaseClause *node = new (pool) AST::CaseClause(sym(2).Expression, sym(4).StatementList); node->caseToken = loc(1); node->colonToken = loc(3); sym(1).Node = node; } break; -case 330: { +case 332: { AST::DefaultClause *node = new (pool) AST::DefaultClause(sym(3).StatementList); node->defaultToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 331: { +case 333: { AST::LabelledStatement *node = new (pool) AST::LabelledStatement(stringRef(1), sym(3).Statement); node->identifierToken = loc(1); node->colonToken = loc(2); sym(1).Node = node; } break; -case 333: { +case 335: { AST::ThrowStatement *node = new (pool) AST::ThrowStatement(sym(2).Expression); node->throwToken = loc(1); node->semicolonToken = loc(3); sym(1).Node = node; } break; -case 334: { +case 336: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch); node->tryToken = loc(1); sym(1).Node = node; } break; -case 335: { +case 337: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 336: { +case 338: { AST::TryStatement *node = new (pool) AST::TryStatement(sym(2).Statement, sym(3).Catch, sym(4).Finally); node->tryToken = loc(1); sym(1).Node = node; } break; -case 337: { +case 339: { AST::Catch *node = new (pool) AST::Catch(stringRef(3), sym(5).Block); node->catchToken = loc(1); node->lparenToken = loc(2); @@ -1729,20 +1741,20 @@ case 337: { sym(1).Node = node; } break; -case 338: { +case 340: { AST::Finally *node = new (pool) AST::Finally(sym(2).Block); node->finallyToken = loc(1); sym(1).Node = node; } break; -case 340: { +case 342: { AST::DebuggerStatement *node = new (pool) AST::DebuggerStatement(); node->debuggerToken = loc(1); node->semicolonToken = loc(2); sym(1).Node = node; } break; -case 342: { +case 344: { AST::FunctionDeclaration *node = new (pool) AST::FunctionDeclaration(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); node->identifierToken = loc(2); @@ -1753,7 +1765,7 @@ case 342: { sym(1).Node = node; } break; -case 343: { +case 345: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(stringRef(2), sym(4).FormalParameterList, sym(7).FunctionBody); node->functionToken = loc(1); if (! stringRef(2).isNull()) @@ -1765,7 +1777,7 @@ case 343: { sym(1).Node = node; } break; -case 344: { +case 346: { AST::FunctionExpression *node = new (pool) AST::FunctionExpression(QStringRef(), sym(3).FormalParameterList, sym(6).FunctionBody); node->functionToken = loc(1); node->lparenToken = loc(2); @@ -1775,56 +1787,56 @@ case 344: { sym(1).Node = node; } break; -case 345: { +case 347: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(stringRef(1)); node->identifierToken = loc(1); sym(1).Node = node; } break; -case 346: { +case 348: { AST::FormalParameterList *node = new (pool) AST::FormalParameterList(sym(1).FormalParameterList, stringRef(3)); node->commaToken = loc(2); node->identifierToken = loc(3); sym(1).Node = node; } break; -case 347: { +case 349: { sym(1).Node = 0; } break; -case 348: { +case 350: { sym(1).Node = sym(1).FormalParameterList->finish (); } break; -case 349: { +case 351: { sym(1).Node = 0; } break; -case 351: { +case 353: { sym(1).Node = new (pool) AST::FunctionBody(sym(1).SourceElements->finish ()); } break; -case 353: { +case 355: { sym(1).Node = new (pool) AST::Program(sym(1).SourceElements->finish ()); } break; -case 354: { +case 356: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElement); } break; -case 355: { +case 357: { sym(1).Node = new (pool) AST::SourceElements(sym(1).SourceElements, sym(2).SourceElement); } break; -case 356: { +case 358: { sym(1).Node = new (pool) AST::StatementSourceElement(sym(1).Statement); } break; -case 357: { +case 359: { sym(1).Node = new (pool) AST::FunctionSourceElement(sym(1).FunctionDeclaration); } break; -case 358: { +case 360: { sym(1).Node = 0; } break; diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index f0973338ea..ddb4af0b81 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -238,6 +238,44 @@ int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int ve return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); } +template<typename T, typename E, int metaObjectRevision> +int qmlRegisterExtendedUncreatableType(const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason) +{ + QML_GETTYPENAMES + + QQmlAttachedPropertiesFunc attached = QQmlPrivate::attachedPropertiesFunc<E>(); + const QMetaObject * attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<E>(); + if (!attached) { + attached = QQmlPrivate::attachedPropertiesFunc<T>(); + attachedMetaObject = QQmlPrivate::attachedPropertiesMetaObject<T>(); + } + + QQmlPrivate::RegisterType type = { + 1, + + qRegisterNormalizedMetaType<T *>(pointerName.constData()), + qRegisterNormalizedMetaType<QQmlListProperty<T> >(listName.constData()), + 0, + Q_NULLPTR, + reason, + + uri, versionMajor, versionMinor, qmlName, &T::staticMetaObject, + + attached, + attachedMetaObject, + + QQmlPrivate::StaticCastSelector<T,QQmlParserStatus>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueSource>::cast(), + QQmlPrivate::StaticCastSelector<T,QQmlPropertyValueInterceptor>::cast(), + + QQmlPrivate::createParent<E>, &E::staticMetaObject, + + Q_NULLPTR, + metaObjectRevision + }; + + return QQmlPrivate::qmlregister(QQmlPrivate::TypeRegistration, &type); +} Q_QML_EXPORT int qmlRegisterUncreatableMetaObject(const QMetaObject &staticMetaObject, const char *uri, int versionMajor, int versionMinor, const char *qmlName, const QString& reason); diff --git a/src/qml/qml/qqmlapplicationengine.cpp b/src/qml/qml/qqmlapplicationengine.cpp index 2a96d96302..a10dda166c 100644 --- a/src/qml/qml/qqmlapplicationengine.cpp +++ b/src/qml/qml/qqmlapplicationengine.cpp @@ -66,8 +66,6 @@ void QQmlApplicationEnginePrivate::cleanUp() void QQmlApplicationEnginePrivate::init() { Q_Q(QQmlApplicationEngine); - q->connect(&statusMapper, SIGNAL(mapped(QObject*)), - q, SLOT(_q_finishLoad(QObject*))); q->connect(q, SIGNAL(quit()), QCoreApplication::instance(), SLOT(quit())); q->connect(q, &QQmlApplicationEngine::exit, QCoreApplication::instance(), &QCoreApplication::exit); #if QT_CONFIG(translation) @@ -113,20 +111,15 @@ void QQmlApplicationEnginePrivate::startLoad(const QUrl &url, const QByteArray & c->loadUrl(url); if (!c->isLoading()) { - _q_finishLoad(c); + finishLoad(c); return; } - statusMapper.setMapping(c, c); - q->connect(c, SIGNAL(statusChanged(QQmlComponent::Status)), - &statusMapper, SLOT(map())); + QObject::connect(c, &QQmlComponent::statusChanged, q, [this, c] { this->finishLoad(c); }); } -void QQmlApplicationEnginePrivate::_q_finishLoad(QObject *o) +void QQmlApplicationEnginePrivate::finishLoad(QQmlComponent *c) { Q_Q(QQmlApplicationEngine); - QQmlComponent *c = qobject_cast<QQmlComponent *>(o); - if (!c) - return; switch (c->status()) { case QQmlComponent::Error: qWarning() << "QQmlApplicationEngine failed to load component"; diff --git a/src/qml/qml/qqmlapplicationengine.h b/src/qml/qml/qqmlapplicationengine.h index e64d7495cd..6c57f46c72 100644 --- a/src/qml/qml/qqmlapplicationengine.h +++ b/src/qml/qml/qqmlapplicationengine.h @@ -74,7 +74,6 @@ Q_SIGNALS: private: Q_DISABLE_COPY(QQmlApplicationEngine) Q_DECLARE_PRIVATE(QQmlApplicationEngine) - Q_PRIVATE_SLOT(d_func(), void _q_finishLoad(QObject*)) }; QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlapplicationengine_p.h b/src/qml/qml/qqmlapplicationengine_p.h index 8c342a43a9..4795170bed 100644 --- a/src/qml/qml/qqmlapplicationengine_p.h +++ b/src/qml/qml/qqmlapplicationengine_p.h @@ -42,7 +42,6 @@ #include "qqmlapplicationengine.h" #include "qqmlengine_p.h" -#include <QSignalMapper> #include <QCoreApplication> #include <QFileInfo> #include <QLibraryInfo> @@ -73,9 +72,8 @@ public: void startLoad(const QUrl &url, const QByteArray &data = QByteArray(), bool dataFlag = false); void loadTranslations(const QUrl &rootFile); - void _q_finishLoad(QObject *component); + void finishLoad(QQmlComponent *component); QList<QObject *> objects; - QSignalMapper statusMapper; QObject *appObj; #if QT_CONFIG(translation) diff --git a/src/qml/qml/qqmlboundsignal.cpp b/src/qml/qml/qqmlboundsignal.cpp index 4fbd828307..19ece44beb 100644 --- a/src/qml/qml/qqmlboundsignal.cpp +++ b/src/qml/qml/qqmlboundsignal.cpp @@ -199,7 +199,7 @@ void QQmlBoundSignalExpression::evaluate(void **a) // for several cases (such as QVariant type and QObject-derived types) //args[ii] = engine->metaTypeToJS(type, a[ii + 1]); if (type == qMetaTypeId<QJSValue>()) { - if (QV4::Value *v4Value = QJSValuePrivate::getValue(reinterpret_cast<QJSValue *>(a[ii + 1]))) + if (QV4::Value *v4Value = QJSValuePrivate::valueForData(reinterpret_cast<QJSValue *>(a[ii + 1]), &callData->args[ii])) callData->args[ii] = *v4Value; else callData->args[ii] = QV4::Encode::undefined(); diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index e1fa97b52f..e99335a117 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -65,6 +65,7 @@ #include <QtCore/qmetaobject.h> #include <QDebug> #include <QtCore/qcoreapplication.h> +#include <QtCore/qcryptographichash.h> #include <QtCore/qdir.h> #include <QtCore/qmutex.h> #include <QtCore/qthread.h> @@ -564,7 +565,7 @@ The following functions are also on the Qt object. \l{Screen} attached object. In practice the array corresponds to the screen list returned by QGuiApplication::screens(). In addition to examining properties like name, width, height, etc., the array elements can also be - assigned to the targetScreen property of Window items, thus serving as an + assigned to the screen property of Window items, thus serving as an alternative to the C++ side's QWindow::setScreen(). This property has been added in Qt 5.9. @@ -585,7 +586,7 @@ The following functions are also on the Qt object. \li application.font \endlist - \sa Screen, Window, Window.targetScreen + \sa Screen, Window, Window.screen */ /*! @@ -760,7 +761,7 @@ class QQmlThreadNotifierProxyObject : public QObject public: QPointer<QObject> target; - virtual int qt_metacall(QMetaObject::Call, int methodIndex, void **a) { + int qt_metacall(QMetaObject::Call, int methodIndex, void **a) override { if (!target) return -1; @@ -2178,6 +2179,27 @@ QString QQmlEngine::offlineStoragePath() const return d->offlineStoragePath; } +/*! + Returns the file path where a \l{QtQuick.LocalStorage}{Local Storage} + database with the identifier \a databaseName is (or would be) located. + + \sa LocalStorage.openDatabaseSync() + \since 5.9 +*/ +QString QQmlEngine::offlineStorageDatabaseFilePath(const QString &databaseName) const +{ + Q_D(const QQmlEngine); + QCryptographicHash md5(QCryptographicHash::Md5); + md5.addData(databaseName.toUtf8()); + return d->offlineStorageDatabaseDirectory() + QLatin1String(md5.result().toHex()); +} + +QString QQmlEnginePrivate::offlineStorageDatabaseDirectory() const +{ + Q_Q(const QQmlEngine); + return q->offlineStoragePath() + QDir::separator() + QLatin1String("Databases") + QDir::separator(); +} + QQmlPropertyCache *QQmlEnginePrivate::createCache(QQmlType *type, int minorVersion) { QList<QQmlType *> types; diff --git a/src/qml/qml/qqmlengine.h b/src/qml/qml/qqmlengine.h index 3102a20fac..8cada954fe 100644 --- a/src/qml/qml/qqmlengine.h +++ b/src/qml/qml/qqmlengine.h @@ -136,6 +136,7 @@ public: void setOfflineStoragePath(const QString& dir); QString offlineStoragePath() const; + QString offlineStorageDatabaseFilePath(const QString &databaseName) const; QUrl baseUrl() const; void setBaseUrl(const QUrl &); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 916566b6c7..1bdeacd524 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -205,6 +205,7 @@ public: inline void deleteInEngineThread(T *); template<typename T> inline static void deleteInEngineThread(QQmlEngine *, T *); + QString offlineStorageDatabaseDirectory() const; // These methods may be called from the loader thread inline QQmlPropertyCache *cache(QQmlType *, int); diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index 8712b638c5..c07d5c740a 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1,5 +1,6 @@ /**************************************************************************** ** +** Copyright (C) 2017 Crimson AS <info@crimson.no> ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** @@ -130,42 +131,74 @@ bool isPathAbsolute(const QString &path) #endif } -// If the type does not already exist as a file import, add the type and return the new type -QQmlType *getTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, +/* + \internal + + Fetches the QQmlType instance registered for \a urlString, creating a + registration for it if it is not already registered, using the associated + \a typeName, \a isCompositeSingleton, \a majorVersion and \a minorVersion + details. + + Errors (if there are any) are placed into \a errors, if it is nonzero. Note + that errors are treated as fatal if \a errors is not set. +*/ +QQmlType *fetchOrCreateTypeForUrl(const QString &urlString, const QHashedStringRef& typeName, bool isCompositeSingleton, QList<QQmlError> *errors, int majorVersion=-1, int minorVersion=-1) { - QUrl url(urlString); + QUrl url(urlString); // ### unfortunate (costly) conversion QQmlType *ret = QQmlMetaType::qmlType(url); - if (!ret) { //QQmlType not yet existing for composite or composite singleton type - int dot = typeName.indexOf(QLatin1Char('.')); - QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); - - //XXX: The constData of the string ref is pointing somewhere unsafe in qmlregister, so we need to create a temporary copy - QByteArray buf(unqualifiedtype.toString().toUtf8()); - - if (isCompositeSingleton) { - QQmlPrivate::RegisterCompositeSingletonType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); - } else { - QQmlPrivate::RegisterCompositeType reg = { - url, - "", //Empty URI indicates loaded via file imports - majorVersion, - minorVersion, - buf.constData() - }; - ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); - } + if (ret) + return ret; + + int dot = typeName.indexOf(QLatin1Char('.')); + QHashedStringRef unqualifiedtype = dot < 0 ? typeName : QHashedStringRef(typeName.constData() + dot + 1, typeName.length() - dot - 1); + + // We need a pointer, but we were passed a string. Take a copy so we + // can guarentee it will live long enough to reach qmlregister. + QByteArray buf(unqualifiedtype.toString().toUtf8()); + + // Register the type. Note that the URI parameters here are empty; for + // file type imports, we do not place them in a URI as we don't + // necessarily have a good and unique one (picture a library import, + // which may be found in multiple plugin locations on disk), but there + // are other reasons for this too. + // + // By not putting them in a URI, we prevent the types from being + // registered on a QQmlTypeModule; this is important, as once types are + // placed on there, they cannot be easily removed, meaning if the + // developer subsequently loads a different import (meaning different + // types) with the same URI (using, say, a different plugin path), it is + // very undesirable that we continue to associate the types from the + // "old" URI with that new module. + // + // Not having URIs also means that the types cannot be found by name + // etc, the only way to look them up is through QQmlImports -- for + // better or worse. + if (isCompositeSingleton) { + QQmlPrivate::RegisterCompositeSingletonType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeSingletonRegistration, ®)); + } else { + QQmlPrivate::RegisterCompositeType reg = { + url, + "", // uri + majorVersion, + minorVersion, + buf.constData() + }; + ret = QQmlMetaType::qmlTypeFromIndex(QQmlPrivate::qmlregister(QQmlPrivate::CompositeRegistration, ®)); } - if (!ret) {//Usually when a type name is "found" but invalid - //qDebug() << ret << urlString << QQmlMetaType::qmlType(url); + + // This means that the type couldn't be found by URL, but could not be + // registered either, meaning we most likely were passed some kind of bad + // data. + if (!ret) { if (!errors) // Cannot list errors properly, just quit qFatal("%s", QQmlMetaType::typeRegistrationFailures().join('\n').toLatin1().constData()); QQmlError error; @@ -204,44 +237,38 @@ void qmlClearEnginePlugins() typedef QPair<QStaticPlugin, QJsonArray> StaticPluginPair; #endif -class QQmlImportNamespace -{ -public: - QQmlImportNamespace() : nextNamespace(0) {} - ~QQmlImportNamespace() { qDeleteAll(imports); } - - struct Import { - QString uri; - QString url; - int majversion; - int minversion; - bool isLibrary; - QQmlDirComponents qmlDirComponents; - QQmlDirScripts qmlDirScripts; - - bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, - QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); - - static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); - - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, bool *typeRecursionDetected = 0) const; - }; - QList<Import *> imports; +/*! + \internal + \class QQmlImportInstance - Import *findImport(const QString &uri) const; + A QQmlImportType represents a single import of a document, held within a + namespace. - bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, - int *vmajor, int *vminor, QQmlType** type_return, - QString *base = 0, QList<QQmlError> *errors = 0); + \note The uri here may not necessarily be unique (e.g. for file imports). - // Prefix when used as a qualified import. Otherwise empty. - QHashedString prefix; + \note Version numbers may be -1 for file imports: this means that no + version was specified as part of the import. Type resolution will be + responsible for attempting to find the "best" possible version. +*/ - // Used by QQmlImportsPrivate::qualifiedSets - QQmlImportNamespace *nextNamespace; -}; +/*! + \internal + \class QQmlImportNamespace + + A QQmlImportNamespace is a way of seperating imports into a local namespace. + + Within a QML document, there is at least one namespace (the + "unqualified set") where imports without a qualifier are placed, i.e: + + import QtQuick 2.6 + + will have a single namespace (the unqualified set) containing a single import + for QtQuick 2.6. However, there may be others if an import statement gives + a qualifier, i.e the following will result in an additional new + QQmlImportNamespace in the qualified set: + + import MyFoo 1.0 as Foo +*/ class QQmlImportsPrivate { @@ -273,9 +300,12 @@ public: QString base; int ref; + // storage of data related to imports without a namespace mutable QQmlImportNamespace unqualifiedset; QQmlImportNamespace *findQualifiedNamespace(const QHashedStringRef &) const; + + // storage of data related to imports with a namespace mutable QFieldList<QQmlImportNamespace, &QQmlImportNamespace::nextNamespace> qualifiedSets; QQmlTypeLoader *typeLoader; @@ -284,21 +314,21 @@ public: QQmlImportDatabase *database, QString *outQmldirFilePath, QString *outUrl); - static bool validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, + static bool validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors); bool importExtension(const QString &absoluteFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors); bool getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors); + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors); QString resolvedUri(const QString &dir_arg, QQmlImportDatabase *database); - QQmlImportNamespace::Import *addImportToNamespace(QQmlImportNamespace *nameSpace, + QQmlImportInstance *addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence = false); @@ -363,12 +393,22 @@ QUrl QQmlImports::baseUrl() const return d->baseUrl; } +/* + \internal + + This method is responsible for populating data of all types visible in this + document's imports into the \a cache for resolution elsewhere (e.g. in JS, + or when loading additional types). + + \note This is for C++ types only. Composite types are handled separately, + as they do not have a QQmlTypeModule. +*/ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const { const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { cache->m_anonymousImports.append(QQmlTypeModuleVersion(module, import->minversion)); @@ -379,11 +419,14 @@ void QQmlImports::populateCache(QQmlTypeNameCache *cache) const const QQmlImportNamespace &set = *ns; + // positioning is important; we must create the namespace even if there is no module. + QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; + typeimport.m_qualifier = set.prefix; + for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); QQmlTypeModule *module = QQmlMetaType::typeModule(import->uri, import->majversion); if (module) { - QQmlTypeNameCache::Import &typeimport = cache->m_namedImports[set.prefix]; typeimport.modules.append(QQmlTypeModuleVersion(module, import->minversion)); } } @@ -412,7 +455,7 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: typedef QQmlDirComponents::const_iterator ConstIterator; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); const QQmlDirComponents &components = import->qmlDirComponents; @@ -430,6 +473,15 @@ void findCompositeSingletons(const QQmlImportNamespace &set, QList<QQmlImports:: } } +/* + \internal + + Returns a list of all composite singletons present in this document's + imports. + + This information is used by QQmlTypeLoader to ensure that composite singletons + are marked as dependencies during type loading. +*/ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSingletons() const { QList<QQmlImports::CompositeSingletonReference> compositeSingletons; @@ -445,6 +497,12 @@ QList<QQmlImports::CompositeSingletonReference> QQmlImports::resolvedCompositeSi return compositeSingletons; } +/* + \internal + + Returns a list of scripts imported by this document. This is used by + QQmlTypeLoader to properly handle dependencies on imported scripts. +*/ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const { QList<QQmlImports::ScriptReference> scripts; @@ -452,7 +510,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = d->unqualifiedset; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -466,7 +524,7 @@ QList<QQmlImports::ScriptReference> QQmlImports::resolvedScripts() const const QQmlImportNamespace &set = *ns; for (int ii = set.imports.count() - 1; ii >= 0; --ii) { - const QQmlImportNamespace::Import *import = set.imports.at(ii); + const QQmlImportInstance *import = set.imports.at(ii); for (const QQmlDirParser::Script &script : import->qmlDirScripts) { ScriptReference ref; @@ -590,7 +648,7 @@ bool QQmlImports::resolveType(const QHashedStringRef &type, return false; } -bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoader::QmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) +bool QQmlImportInstance::setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, QQmlImportNamespace *nameSpace, QList<QQmlError> *errors) { Q_ASSERT(resolvedUrl.endsWith(Slash)); url = resolvedUrl; @@ -600,7 +658,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c const QQmlDirScripts &scripts = qmldir->scripts(); if (!scripts.isEmpty()) { // Verify that we haven't imported these scripts already - for (QList<QQmlImportNamespace::Import *>::const_iterator it = nameSpace->imports.constBegin(); + for (QList<QQmlImportInstance *>::const_iterator it = nameSpace->imports.constBegin(); it != nameSpace->imports.constEnd(); ++it) { if ((*it != this) && ((*it)->uri == uri)) { QQmlError error; @@ -616,7 +674,7 @@ bool QQmlImportNamespace::Import::setQmldirContent(const QString &resolvedUrl, c return true; } -QQmlDirScripts QQmlImportNamespace::Import::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) +QQmlDirScripts QQmlImportInstance::getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin) { QMap<QString, QQmlDirParser::Script> versioned; @@ -652,7 +710,7 @@ bool QQmlImports::resolveType(QQmlImportNamespace* ns, const QHashedStringRef &t return ns->resolveType(d->typeLoader,type,vmaj,vmin,type_return); } -bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, +bool QQmlImportInstance::resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, int *vmajor, int *vminor, QQmlType** type_return, QString *base, bool *typeRecursionDetected) const { @@ -683,15 +741,17 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if ((candidate == end) || (c.majorVersion > candidate->majorVersion) || ((c.majorVersion == candidate->majorVersion) && (c.minorVersion > candidate->minorVersion))) { - componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); - if (c.internal && base) { - if (resolveLocalUrl(*base, c.fileName) != componentUrl) - continue; // failed attempt to access an internal type - } - if (base && (*base == componentUrl)) { - if (typeRecursionDetected) - *typeRecursionDetected = true; - continue; // no recursion + if (base) { + componentUrl = resolveLocalUrl(QString(url + c.typeName + dotqml_string), c.fileName); + if (c.internal) { + if (resolveLocalUrl(*base, c.fileName) != componentUrl) + continue; // failed attempt to access an internal type + } + if (*base == componentUrl) { + if (typeRecursionDetected) + *typeRecursionDetected = true; + continue; // no recursion + } } // This is our best candidate so far @@ -702,9 +762,11 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, } if (candidate != end) { + if (!base) // ensure we have a componentUrl + componentUrl = resolveLocalUrl(QString(url + candidate->typeName + dotqml_string), candidate->fileName); int major = vmajor ? *vmajor : -1; int minor = vminor ? *vminor : -1; - QQmlType *returnType = getTypeForUrl(componentUrl, type, isCompositeSingleton, 0, + QQmlType *returnType = fetchOrCreateTypeForUrl(componentUrl, type, isCompositeSingleton, 0, major, minor); if (type_return) *type_return = returnType; @@ -732,7 +794,7 @@ bool QQmlImportNamespace::Import::resolveType(QQmlTypeLoader *typeLoader, if (typeRecursionDetected) *typeRecursionDetected = true; } else { - QQmlType *returnType = getTypeForUrl(qmlUrl, type, false, 0); + QQmlType *returnType = fetchOrCreateTypeForUrl(qmlUrl, type, false, 0); if (type_return) *type_return = returnType; return returnType != 0; @@ -777,7 +839,7 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return true; if (s->imports.count() == 1 && !s->imports.at(0)->isLibrary && type_return && s != &unqualifiedset) { // qualified, and only 1 url - *type_return = getTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); + *type_return = fetchOrCreateTypeForUrl(resolveLocalUrl(s->imports.at(0)->url, unqualifiedtype.toString() + QLatin1String(".qml")), type, false, errors); return (*type_return != 0); } } @@ -785,9 +847,9 @@ bool QQmlImportsPrivate::resolveType(const QHashedStringRef& type, int *vmajor, return false; } -QQmlImportNamespace::Import *QQmlImportNamespace::findImport(const QString &uri) const +QQmlImportInstance *QQmlImportNamespace::findImport(const QString &uri) const { - for (Import *import : imports) { + for (QQmlImportInstance *import : imports) { if (import->uri == uri) return import; } @@ -800,13 +862,13 @@ bool QQmlImportNamespace::resolveType(QQmlTypeLoader *typeLoader, const QHashedS { bool typeRecursionDetected = false; for (int i=0; i<imports.count(); ++i) { - const Import *import = imports.at(i); + const QQmlImportInstance *import = imports.at(i); if (import->resolveType(typeLoader, type, vmajor, vminor, type_return, base, &typeRecursionDetected)) { if (qmlCheckTypes()) { // check for type clashes for (int j = i+1; j<imports.count(); ++j) { - const Import *import2 = imports.at(j); + const QQmlImportInstance *import2 = imports.at(j); if (import2->resolveType(typeLoader, type, vmajor, vminor, 0, base)) { if (errors) { QString u1 = import->url; @@ -949,10 +1011,12 @@ bool QQmlImportsPrivate::populatePluginPairVector(QVector<StaticPluginPair> &res } #endif +#if defined(QT_SHARED) || !QT_CONFIG(library) static inline QString msgCannotLoadPlugin(const QString &uri, const QString &why) { return QQmlImportDatabase::tr("plugin cannot be loaded for module \"%1\": %2").arg(uri, why); } +#endif /*! Import an extension defined by a qmldir file. @@ -963,7 +1027,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, const QString &uri, int vmaj, int vmin, QQmlImportDatabase *database, - const QQmlTypeLoader::QmldirContent *qmldir, + const QQmlTypeLoaderQmldirContent *qmldir, QList<QQmlError> *errors) { #if QT_CONFIG(library) @@ -1099,7 +1163,7 @@ bool QQmlImportsPrivate::importExtension(const QString &qmldirFilePath, } bool QQmlImportsPrivate::getQmldirContent(const QString &qmldirIdentifier, const QString &uri, - const QQmlTypeLoader::QmldirContent **qmldir, QList<QQmlError> *errors) + const QQmlTypeLoaderQmldirContent **qmldir, QList<QQmlError> *errors) { Q_ASSERT(errors); Q_ASSERT(qmldir); @@ -1224,7 +1288,7 @@ bool QQmlImportsPrivate::locateQmldir(const QString &uri, int vmaj, int vmin, QQ return false; } -bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoader::QmldirContent *qmldir, const QString &uri, int vmaj, int vmin, +bool QQmlImportsPrivate::validateQmldirVersion(const QQmlTypeLoaderQmldirContent *qmldir, const QString &uri, int vmaj, int vmin, QList<QQmlError> *errors) { int lowest_min = INT_MAX; @@ -1307,7 +1371,7 @@ QQmlImportNamespace *QQmlImportsPrivate::importNamespace(const QString &prefix) return nameSpace; } -QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, +QQmlImportInstance *QQmlImportsPrivate::addImportToNamespace(QQmlImportNamespace *nameSpace, const QString &uri, const QString &url, int vmaj, int vmin, QV4::CompiledData::Import::ImportType type, QList<QQmlError> *errors, bool lowPrecedence) @@ -1317,7 +1381,7 @@ QQmlImportNamespace::Import *QQmlImportsPrivate::addImportToNamespace(QQmlImport Q_UNUSED(errors); Q_ASSERT(url.isEmpty() || url.endsWith(Slash)); - QQmlImportNamespace::Import *import = new QQmlImportNamespace::Import; + QQmlImportInstance *import = new QQmlImportInstance; import->uri = uri; import->url = url; import->majversion = vmaj; @@ -1343,11 +1407,11 @@ bool QQmlImportsPrivate::addLibraryImport(const QString& uri, const QString &pre QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, uri, qmldirUrl, vmaj, vmin, QV4::CompiledData::Import::ImportLibrary, errors); Q_ASSERT(inserted); if (!incomplete) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!qmldirIdentifier.isEmpty()) { if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) @@ -1444,11 +1508,11 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix if (!url.endsWith(Slash) && !url.endsWith(Backslash)) url += Slash; - QQmlImportNamespace::Import *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); + QQmlImportInstance *inserted = addImportToNamespace(nameSpace, importUri, url, vmaj, vmin, QV4::CompiledData::Import::ImportFile, errors, isImplicitImport); Q_ASSERT(inserted); if (!incomplete && !qmldirIdentifier.isEmpty()) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, importUri, &qmldir, errors)) return false; @@ -1471,8 +1535,8 @@ bool QQmlImportsPrivate::updateQmldirContent(const QString &uri, const QString & QQmlImportNamespace *nameSpace = importNamespace(prefix); Q_ASSERT(nameSpace); - if (QQmlImportNamespace::Import *import = nameSpace->findImport(uri)) { - const QQmlTypeLoader::QmldirContent *qmldir = 0; + if (QQmlImportInstance *import = nameSpace->findImport(uri)) { + const QQmlTypeLoaderQmldirContent *qmldir = 0; if (!getQmldirContent(qmldirIdentifier, uri, &qmldir, errors)) return false; diff --git a/src/qml/qml/qqmlimport_p.h b/src/qml/qml/qqmlimport_p.h index 0e7848730f..7c691a468c 100644 --- a/src/qml/qml/qqmlimport_p.h +++ b/src/qml/qml/qqmlimport_p.h @@ -68,6 +68,48 @@ class QQmlImportNamespace; class QQmlImportsPrivate; class QQmlImportDatabase; class QQmlTypeLoader; +class QQmlTypeLoaderQmldirContent; + +struct QQmlImportInstance +{ + QString uri; // e.g. QtQuick + QString url; // the base path of the import + int majversion; // the major version imported + int minversion; // the minor version imported + bool isLibrary; // true means that this is not a file import + QQmlDirComponents qmlDirComponents; // a copy of the components listed in the qmldir + QQmlDirScripts qmlDirScripts; // a copy of the scripts in the qmldir + + bool setQmldirContent(const QString &resolvedUrl, const QQmlTypeLoaderQmldirContent *qmldir, + QQmlImportNamespace *nameSpace, QList<QQmlError> *errors); + + static QQmlDirScripts getVersionedScripts(const QQmlDirScripts &qmldirscripts, int vmaj, int vmin); + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef &type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, bool *typeRecursionDetected = 0) const; +}; + +class QQmlImportNamespace +{ +public: + QQmlImportNamespace() : nextNamespace(0) {} + ~QQmlImportNamespace() { qDeleteAll(imports); } + + QList<QQmlImportInstance *> imports; + + QQmlImportInstance *findImport(const QString &uri) const; + + bool resolveType(QQmlTypeLoader *typeLoader, const QHashedStringRef& type, + int *vmajor, int *vminor, QQmlType** type_return, + QString *base = 0, QList<QQmlError> *errors = 0); + + // Prefix when used as a qualified import. Otherwise empty. + QHashedString prefix; + + // Used by QQmlImportsPrivate::qualifiedSets + QQmlImportNamespace *nextNamespace; +}; class Q_QML_PRIVATE_EXPORT QQmlImports { diff --git a/src/qml/qml/qqmllistwrapper.cpp b/src/qml/qml/qqmllistwrapper.cpp index 8aa107dc17..43677e0d78 100644 --- a/src/qml/qml/qqmllistwrapper.cpp +++ b/src/qml/qml/qqmllistwrapper.cpp @@ -140,12 +140,13 @@ ReturnedValue QmlListWrapper::getIndexed(const Managed *m, uint index, bool *has return Primitive::undefinedValue().asReturnedValue(); } -void QmlListWrapper::put(Managed *m, String *name, const Value &value) +bool QmlListWrapper::put(Managed *m, String *name, const Value &value) { // doesn't do anything. Should we throw? Q_UNUSED(m); Q_UNUSED(name); Q_UNUSED(value); + return false; } void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attrs) @@ -165,4 +166,29 @@ void QmlListWrapper::advanceIterator(Managed *m, ObjectIterator *it, Value *name return QV4::Object::advanceIterator(m, it, name, index, p, attrs); } +void PropertyListPrototype::init(ExecutionEngine *) +{ + defineDefaultProperty(QStringLiteral("push"), method_push, 1); +} + +void PropertyListPrototype::method_push(const BuiltinFunction *, Scope &scope, CallData *callData) +{ + ScopedObject instance(scope, callData->thisObject.toObject(scope.engine)); + if (!instance) + RETURN_UNDEFINED(); + QmlListWrapper *w = instance->as<QmlListWrapper>(); + if (!w) + RETURN_UNDEFINED(); + if (!w->d()->property().append) + THROW_GENERIC_ERROR("List doesn't define an Append function"); + + QV4::ScopedObject so(scope); + for (int i = 0; i < callData->argc; ++i) + { + so = callData->args[i].toObject(scope.engine); + if (QV4::QObjectWrapper *wrapper = so->as<QV4::QObjectWrapper>()) + w->d()->property().append(&w->d()->property(), wrapper->object() ); + } +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmllistwrapper_p.h b/src/qml/qml/qqmllistwrapper_p.h index d01b332159..84dadba01a 100644 --- a/src/qml/qml/qqmllistwrapper_p.h +++ b/src/qml/qml/qqmllistwrapper_p.h @@ -86,6 +86,7 @@ struct Q_QML_EXPORT QmlListWrapper : Object { V4_OBJECT2(QmlListWrapper, Object) V4_NEEDS_DESTROY + V4_PROTOTYPE(propertyListPrototype) static ReturnedValue create(ExecutionEngine *engine, QObject *object, int propId, int propType); static ReturnedValue create(ExecutionEngine *engine, const QQmlListProperty<QObject> &prop, int propType); @@ -94,10 +95,17 @@ struct Q_QML_EXPORT QmlListWrapper : Object static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static ReturnedValue getIndexed(const Managed *m, uint index, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); }; +struct PropertyListPrototype : Object +{ + void init(ExecutionEngine *engine); + + static void method_push(const BuiltinFunction *, Scope &, CallData *callData); +}; + } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 520c44f4da..bd6b9a1599 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -79,7 +79,7 @@ struct QQmlMetaTypeData Files urlToNonFileImportType; // For non-file imported composite and composite // singleton types. This way we can locate any // of them by url, even if it was registered as - // a module via qmlRegisterCompositeType. + // a module via QQmlPrivate::RegisterCompositeType typedef QHash<const QMetaObject *, QQmlType *> MetaObjects; MetaObjects metaObjectToType; typedef QHash<int, QQmlMetaType::StringConverter> StringConverters; diff --git a/src/qml/qml/qqmlobjectcreator.cpp b/src/qml/qml/qqmlobjectcreator.cpp index 38a16b8cde..85fbd86dc4 100644 --- a/src/qml/qml/qqmlobjectcreator.cpp +++ b/src/qml/qml/qqmlobjectcreator.cpp @@ -170,7 +170,7 @@ QObject *QQmlObjectCreator::create(int subComponentIndex, QObject *parent, QQmlI context = new QQmlContextData; context->isInternal = true; - context->imports = compilationUnit->importCache; + context->imports = compilationUnit->typeNameCache; context->initFromTypeCompilationUnit(compilationUnit, subComponentIndex); context->setParent(parentContext); @@ -1150,7 +1150,7 @@ QObject *QQmlObjectCreator::createInstance(int index, QObject *parent, bool isCo if (customParser && obj->flags & QV4::CompiledData::Object::HasCustomParserBindings) { customParser->engine = QQmlEnginePrivate::get(engine); - customParser->imports = compilationUnit->importCache; + customParser->imports = compilationUnit->typeNameCache; QList<const QV4::CompiledData::Binding *> bindings; const QV4::CompiledData::Object *obj = qmlUnit->objectAt(index); diff --git a/src/qml/qml/qqmlpropertycache.cpp b/src/qml/qml/qqmlpropertycache.cpp index 562aa1c88a..88ce2fa1b9 100644 --- a/src/qml/qml/qqmlpropertycache.cpp +++ b/src/qml/qml/qqmlpropertycache.cpp @@ -76,7 +76,8 @@ public: int argumentsValid:1; QList<QByteArray> *names; - int arguments[0]; + + int arguments[1]; }; // Flags that do *NOT* depend on the property's QMetaProperty::userType() and thus are quick @@ -919,7 +920,7 @@ static int EnumType(const QMetaObject *metaobj, const QByteArray &str, int type) QQmlPropertyCacheMethodArguments *QQmlPropertyCache::createArgumentsObject(int argc, const QList<QByteArray> &names) { typedef QQmlPropertyCacheMethodArguments A; - A *args = static_cast<A *>(malloc(sizeof(A) + (argc + 1) * sizeof(int))); + A *args = static_cast<A *>(malloc(sizeof(A) + (argc) * sizeof(int))); args->arguments[0] = argc; args->argumentsValid = false; args->signalParameterStringForJS = 0; diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 5b1bba46dd..f4f04e12c0 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -1362,7 +1362,7 @@ bool QQmlTypeLoader::Blob::updateQmldir(QQmlQmldirData *data, const QV4::Compile if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirIdentifier); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1410,7 +1410,7 @@ bool QQmlTypeLoader::Blob::addImport(const QV4::CompiledData::Import *import, QL if (!importQualifier.isEmpty()) { // Does this library contain any qualified scripts? QUrl libraryUrl(qmldirUrl); - const QmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); + const QQmlTypeLoaderQmldirContent *qmldir = typeLoader()->qmldirContent(qmldirFilePath); const auto qmldirScripts = qmldir->scripts(); for (const QQmlDirParser::Script &script : qmldirScripts) { QUrl scriptUrl = libraryUrl.resolved(QUrl(script.fileName)); @@ -1539,57 +1539,57 @@ bool QQmlTypeLoader::Blob::qmldirDataAvailable(QQmlQmldirData *data, QList<QQmlE } -QQmlTypeLoader::QmldirContent::QmldirContent() +QQmlTypeLoaderQmldirContent::QQmlTypeLoaderQmldirContent() { } -bool QQmlTypeLoader::QmldirContent::hasError() const +bool QQmlTypeLoaderQmldirContent::hasError() const { return m_parser.hasError(); } -QList<QQmlError> QQmlTypeLoader::QmldirContent::errors(const QString &uri) const +QList<QQmlError> QQmlTypeLoaderQmldirContent::errors(const QString &uri) const { return m_parser.errors(uri); } -QString QQmlTypeLoader::QmldirContent::typeNamespace() const +QString QQmlTypeLoaderQmldirContent::typeNamespace() const { return m_parser.typeNamespace(); } -void QQmlTypeLoader::QmldirContent::setContent(const QString &location, const QString &content) +void QQmlTypeLoaderQmldirContent::setContent(const QString &location, const QString &content) { m_location = location; m_parser.parse(content); } -void QQmlTypeLoader::QmldirContent::setError(const QQmlError &error) +void QQmlTypeLoaderQmldirContent::setError(const QQmlError &error) { m_parser.setError(error); } -QQmlDirComponents QQmlTypeLoader::QmldirContent::components() const +QQmlDirComponents QQmlTypeLoaderQmldirContent::components() const { return m_parser.components(); } -QQmlDirScripts QQmlTypeLoader::QmldirContent::scripts() const +QQmlDirScripts QQmlTypeLoaderQmldirContent::scripts() const { return m_parser.scripts(); } -QQmlDirPlugins QQmlTypeLoader::QmldirContent::plugins() const +QQmlDirPlugins QQmlTypeLoaderQmldirContent::plugins() const { return m_parser.plugins(); } -QString QQmlTypeLoader::QmldirContent::pluginLocation() const +QString QQmlTypeLoaderQmldirContent::pluginLocation() const { return m_location; } -bool QQmlTypeLoader::QmldirContent::designerSupported() const +bool QQmlTypeLoaderQmldirContent::designerSupported() const { return m_parser.designerSupported(); } @@ -1861,13 +1861,13 @@ bool QQmlTypeLoader::directoryExists(const QString &path) /*! -Return a QmldirContent for absoluteFilePath. The QmldirContent may be cached. +Return a QQmlTypeLoaderQmldirContent for absoluteFilePath. The QQmlTypeLoaderQmldirContent may be cached. \a filePath is a local file path. It can also be a remote path for a remote directory import, but it will have been cached by now in this case. */ -const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) +const QQmlTypeLoaderQmldirContent *QQmlTypeLoader::qmldirContent(const QString &filePathIn) { QUrl url(filePathIn); //May already contain http scheme if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) @@ -1883,10 +1883,10 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString else filePath = url.path(); - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(filePath); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(filePath); if (!val) { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; #define ERROR(description) { QQmlError e; e.setDescription(description); qmldir->setError(e); } #define NOT_READABLE_ERROR QString(QLatin1String("module \"$$URI$$\" definition \"%1\" not readable")) @@ -1916,12 +1916,12 @@ const QQmlTypeLoader::QmldirContent *QQmlTypeLoader::qmldirContent(const QString void QQmlTypeLoader::setQmldirContent(const QString &url, const QString &content) { - QmldirContent *qmldir; - QmldirContent **val = m_importQmlDirCache.value(url); + QQmlTypeLoaderQmldirContent *qmldir; + QQmlTypeLoaderQmldirContent **val = m_importQmlDirCache.value(url); if (val) { qmldir = *val; } else { - qmldir = new QmldirContent; + qmldir = new QQmlTypeLoaderQmldirContent; m_importQmlDirCache.insert(url, qmldir); } @@ -2075,6 +2075,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() } } + if (unit->data->flags & QV4::CompiledData::Unit::PendingTypeCompilation) { + restoreIR(unit); + return true; + } + m_compiledData = unit; for (int i = 0, count = m_compiledData->objectCount(); i < count; ++i) @@ -2127,11 +2132,11 @@ bool QQmlTypeData::tryLoadFromDiskCache() return true; } -void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, +void QQmlTypeData::createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData); - m_compiledData->importCache = importCache; + m_compiledData->typeNameCache = typeNameCache; m_compiledData->resolvedTypes = resolvedTypeCache; QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2217,10 +2222,10 @@ void QQmlTypeData::done() } } - QQmlRefPointer<QQmlTypeNameCache> importCache; + QQmlRefPointer<QQmlTypeNameCache> typeNameCache; QV4::CompiledData::ResolvedTypeReferenceMap resolvedTypeCache; { - QQmlCompileError error = buildTypeResolutionCaches(&importCache, &resolvedTypeCache); + QQmlCompileError error = buildTypeResolutionCaches(&typeNameCache, &resolvedTypeCache); if (error.isSet()) { setError(error); return; @@ -2240,9 +2245,9 @@ void QQmlTypeData::done() if (!m_document.isNull()) { // Compile component - compile(importCache, resolvedTypeCache); + compile(typeNameCache, resolvedTypeCache); } else { - createTypeAndPropertyCaches(importCache, resolvedTypeCache); + createTypeAndPropertyCaches(typeNameCache, resolvedTypeCache); } if (isError()) @@ -2303,7 +2308,7 @@ void QQmlTypeData::done() qualifier = qualifier.mid(lastDotIndex+1); } - m_compiledData->importCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); + m_compiledData->typeNameCache->add(qualifier.toString(), scriptIndex, enclosingNamespace); QQmlScriptData *scriptData = script.script->scriptData(); scriptData->addref(); m_compiledData->dependentScripts << scriptData; @@ -2397,6 +2402,15 @@ bool QQmlTypeData::loadFromSource() return true; } +void QQmlTypeData::restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit) +{ + m_document.reset(new QmlIR::Document(isDebugging())); + QmlIR::IRLoader loader(unit->data, m_document.data()); + loader.load(); + m_document->javaScriptCompilationUnit = unit; + continueLoadFromIR(); +} + void QQmlTypeData::continueLoadFromIR() { m_typeReferences.collectFromObjects(m_document->objects.constBegin(), m_document->objects.constEnd()); @@ -2489,12 +2503,12 @@ QString QQmlTypeData::stringAt(int index) const return m_document->jsGenerator.stringTable.stringForIndex(index); } -void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) +void QQmlTypeData::compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache) { Q_ASSERT(m_compiledData.isNull()); QQmlEnginePrivate * const enginePrivate = QQmlEnginePrivate::get(typeLoader()->engine()); - QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), importCache, resolvedTypeCache); + QQmlTypeCompiler compiler(enginePrivate, this, m_document.data(), typeNameCache, resolvedTypeCache); m_compiledData = compiler.compile(); if (!m_compiledData) { setError(compiler.compilationErrors()); @@ -2598,20 +2612,20 @@ void QQmlTypeData::resolveTypes() } QQmlCompileError QQmlTypeData::buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const { - importCache->adopt(new QQmlTypeNameCache); + typeNameCache->adopt(new QQmlTypeNameCache(m_importCache)); for (const QString &ns: m_namespaces) - (*importCache)->add(ns); + (*typeNameCache)->add(ns); // Add any Composite Singletons that were used to the import cache for (const QQmlTypeData::TypeReference &singleton: m_compositeSingletons) - (*importCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); + (*typeNameCache)->add(singleton.type->qmlTypeName(), singleton.type->sourceUrl(), singleton.prefix); - m_importCache.populateCache(*importCache); + m_importCache.populateCache(*typeNameCache); QQmlEnginePrivate * const engine = QQmlEnginePrivate::get(typeLoader()->engine()); @@ -2710,7 +2724,7 @@ void QQmlTypeData::scriptImported(QQmlScriptBlob *blob, const QV4::CompiledData: } QQmlScriptData::QQmlScriptData() - : importCache(0) + : typeNameCache(0) , m_loaded(false) , m_program(0) { @@ -2767,8 +2781,8 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent // For backward compatibility, if there are no imports, we need to use the // imports from the parent context. See QTBUG-17518. - if (!importCache->isEmpty()) { - ctxt->imports = importCache; + if (!typeNameCache->isEmpty()) { + ctxt->imports = typeNameCache; } else if (effectiveCtxt) { ctxt->imports = effectiveCtxt->imports; ctxt->importedScripts = effectiveCtxt->importedScripts; @@ -2823,9 +2837,9 @@ QV4::ReturnedValue QQmlScriptData::scriptValueForContext(QQmlContextData *parent void QQmlScriptData::clear() { - if (importCache) { - importCache->release(); - importCache = 0; + if (typeNameCache) { + typeNameCache->release(); + typeNameCache = 0; } for (int ii = 0; ii < scripts.count(); ++ii) @@ -2946,7 +2960,7 @@ void QQmlScriptBlob::done() } } - m_scriptData->importCache = new QQmlTypeNameCache(); + m_scriptData->typeNameCache = new QQmlTypeNameCache(m_importCache); QSet<QString> ns; @@ -2958,13 +2972,13 @@ void QQmlScriptBlob::done() if (!script.nameSpace.isNull()) { if (!ns.contains(script.nameSpace)) { ns.insert(script.nameSpace); - m_scriptData->importCache->add(script.nameSpace); + m_scriptData->typeNameCache->add(script.nameSpace); } } - m_scriptData->importCache->add(script.qualifier, scriptIndex, script.nameSpace); + m_scriptData->typeNameCache->add(script.qualifier, scriptIndex, script.nameSpace); } - m_importCache.populateCache(m_scriptData->importCache); + m_importCache.populateCache(m_scriptData->typeNameCache); } QString QQmlScriptBlob::stringAt(int index) const diff --git a/src/qml/qml/qqmltypeloader_p.h b/src/qml/qml/qqmltypeloader_p.h index c60435a2d6..915b1bcc4c 100644 --- a/src/qml/qml/qqmltypeloader_p.h +++ b/src/qml/qml/qqmltypeloader_p.h @@ -218,6 +218,34 @@ private: class QQmlTypeLoaderThread; +class QQmlTypeLoaderQmldirContent +{ +private: + friend class QQmlTypeLoader; + QQmlTypeLoaderQmldirContent(); + + void setContent(const QString &location, const QString &content); + void setError(const QQmlError &); + +public: + bool hasError() const; + QList<QQmlError> errors(const QString &uri) const; + + QString typeNamespace() const; + + QQmlDirComponents components() const; + QQmlDirScripts scripts() const; + QQmlDirPlugins plugins() const; + + QString pluginLocation() const; + + bool designerSupported() const; + +private: + QQmlDirParser m_parser; + QString m_location; +}; + class Q_AUTOTEST_EXPORT QQmlTypeLoader { Q_DECLARE_TR_FUNCTIONS(QQmlTypeLoader) @@ -256,34 +284,6 @@ public: QList<QQmlQmldirData *> m_qmldirs; }; - class QmldirContent - { - private: - friend class QQmlTypeLoader; - QmldirContent(); - - void setContent(const QString &location, const QString &content); - void setError(const QQmlError &); - - public: - bool hasError() const; - QList<QQmlError> errors(const QString &uri) const; - - QString typeNamespace() const; - - QQmlDirComponents components() const; - QQmlDirScripts scripts() const; - QQmlDirPlugins plugins() const; - - QString pluginLocation() const; - - bool designerSupported() const; - - private: - QQmlDirParser m_parser; - QString m_location; - }; - QQmlTypeLoader(QQmlEngine *); ~QQmlTypeLoader(); @@ -298,7 +298,7 @@ public: QString absoluteFilePath(const QString &path); bool directoryExists(const QString &path); - const QmldirContent *qmldirContent(const QString &filePath); + const QQmlTypeLoaderQmldirContent *qmldirContent(const QString &filePath); void setQmldirContent(const QString &filePath, const QString &content); void clearCache(); @@ -363,7 +363,7 @@ private: typedef QHash<QUrl, QQmlQmldirData *> QmldirCache; typedef QStringHash<bool> StringSet; typedef QStringHash<StringSet*> ImportDirCache; - typedef QStringHash<QmldirContent *> ImportQmlDirCache; + typedef QStringHash<QQmlTypeLoaderQmldirContent *> ImportQmlDirCache; QQmlEngine *m_engine; QQmlTypeLoaderThread *m_thread; @@ -446,15 +446,16 @@ protected: private: bool tryLoadFromDiskCache(); bool loadFromSource(); + void restoreIR(QQmlRefPointer<QV4::CompiledData::CompilationUnit> unit); void continueLoadFromIR(); void resolveTypes(); QQmlCompileError buildTypeResolutionCaches( - QQmlRefPointer<QQmlTypeNameCache> *importCache, + QQmlRefPointer<QQmlTypeNameCache> *typeNameCache, QV4::CompiledData::ResolvedTypeReferenceMap *resolvedTypeCache ) const; - void compile(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + void compile(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); - void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &importCache, + void createTypeAndPropertyCaches(const QQmlRefPointer<QQmlTypeNameCache> &typeNameCache, const QV4::CompiledData::ResolvedTypeReferenceMap &resolvedTypeCache); bool resolveType(const QString &typeName, int &majorVersion, int &minorVersion, TypeReference &ref, int lineNumber = -1, int columnNumber = -1, bool reportErrors = true); @@ -504,7 +505,7 @@ public: QUrl url; QString urlString; - QQmlTypeNameCache *importCache; + QQmlTypeNameCache *typeNameCache; QList<QQmlScriptBlob *> scripts; QV4::ReturnedValue scriptValueForContext(QQmlContextData *parentCtxt); diff --git a/src/qml/qml/qqmltypenamecache.cpp b/src/qml/qml/qqmltypenamecache.cpp index c2098bc9a1..c8e2b92c29 100644 --- a/src/qml/qml/qqmltypenamecache.cpp +++ b/src/qml/qml/qqmltypenamecache.cpp @@ -43,7 +43,8 @@ QT_BEGIN_NAMESPACE -QQmlTypeNameCache::QQmlTypeNameCache() +QQmlTypeNameCache::QQmlTypeNameCache(const QQmlImports &importCache) + : m_imports(importCache) { } @@ -70,6 +71,7 @@ void QQmlTypeNameCache::add(const QHashedString &name, int importedScriptIndex, { Import import; import.scriptIndex = importedScriptIndex; + import.m_qualifier = name; if (nameSpace.length() != 0) { Import *i = m_namedImports.value(nameSpace); @@ -94,6 +96,18 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name) if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(name, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -109,6 +123,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QHashedStringRef &name, if (!result.isValid()) result = query(i->compositeSingletons, name); + if (!result.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name.toString(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return result; } @@ -122,6 +150,19 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name) cons if (!result.isValid()) result = query(m_anonymousCompositeSingletons, name); + if (!result.isValid()) { + // Look up anonymous types from the imports of this document + QString typeName = name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(typeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + + } + return result; } @@ -143,6 +184,20 @@ QQmlTypeNameCache::Result QQmlTypeNameCache::query(const QV4::String *name, cons if (!r.isValid()) r = query(i->compositeSingletons, name); + if (!r.isValid()) { + // Look up types from the imports of this document + // ### it would be nice if QQmlImports allowed us to resolve a namespace + // first, and then types on it. + QString qualifiedTypeName = i->m_qualifier + QLatin1Char('.') + name->toQStringNoThrow(); + QQmlImportNamespace *typeNamespace = 0; + QList<QQmlError> errors; + QQmlType *t = 0; + bool typeFound = m_imports.resolveType(qualifiedTypeName, &t, 0, 0, &typeNamespace, &errors); + if (typeFound) { + return Result(t); + } + } + return r; } diff --git a/src/qml/qml/qqmltypenamecache_p.h b/src/qml/qml/qqmltypenamecache_p.h index 8a387bed5f..7cdcbe91b6 100644 --- a/src/qml/qml/qqmltypenamecache_p.h +++ b/src/qml/qml/qqmltypenamecache_p.h @@ -56,6 +56,7 @@ #include "qqmlmetatype_p.h" #include <private/qhashedstring_p.h> +#include <private/qqmlimport_p.h> #include <QtCore/qvector.h> @@ -66,7 +67,7 @@ class QQmlEngine; class QQmlTypeNameCache : public QQmlRefCount { public: - QQmlTypeNameCache(); + QQmlTypeNameCache(const QQmlImports &imports); virtual ~QQmlTypeNameCache(); inline bool isEmpty() const; @@ -105,6 +106,9 @@ private: // Or, imported compositeSingletons QStringHash<QUrl> compositeSingletons; + + // The qualifier of this import + QString m_qualifier; }; template<typename Key> @@ -112,6 +116,7 @@ private: { Import *i = imports.value(key); if (i) { + Q_ASSERT(!i->m_qualifier.isEmpty()); if (i->scriptIndex != -1) { return Result(i->scriptIndex); } else { @@ -151,6 +156,7 @@ private: QMap<const Import *, QStringHash<Import> > m_namespacedImports; QVector<QQmlTypeModuleVersion> m_anonymousImports; QStringHash<QUrl> m_anonymousCompositeSingletons; + QQmlImports m_imports; }; QQmlTypeNameCache::Result::Result() diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index fd1e9cc2be..c4422afa9c 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -275,13 +275,13 @@ ReturnedValue QmlTypeWrapper::get(const Managed *m, String *name, bool *hasPrope } -void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QmlTypeWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QmlTypeWrapper>()); QmlTypeWrapper *w = static_cast<QmlTypeWrapper *>(m); QV4::ExecutionEngine *v4 = w->engine(); if (v4->hasException) - return; + return false; QV4::Scope scope(v4); QQmlContextData *context = v4->callingQmlContext(); @@ -292,7 +292,8 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QQmlEngine *e = scope.engine->qmlEngine(); QObject *ao = qmlAttachedPropertiesObjectById(type->attachedPropertiesId(QQmlEnginePrivate::get(e)), object); if (ao) - QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(v4, context, ao, name, QV4::QObjectWrapper::IgnoreRevision, value); + return false; } else if (type && type->isSingleton()) { QQmlEngine *e = scope.engine->qmlEngine(); QQmlType::SingletonInstanceInfo *siinfo = type->singletonInstanceInfo(); @@ -300,18 +301,20 @@ void QmlTypeWrapper::put(Managed *m, String *name, const Value &value) QObject *qobjectSingleton = siinfo->qobjectApi(e); if (qobjectSingleton) { - QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); + return QV4::QObjectWrapper::setQmlProperty(v4, context, qobjectSingleton, name, QV4::QObjectWrapper::IgnoreRevision, value); } else if (!siinfo->scriptApi(e).isUndefined()) { QV4::ScopedObject apiprivate(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); if (!apiprivate) { QString error = QLatin1String("Cannot assign to read-only property \"") + name->toQString() + QLatin1Char('\"'); v4->throwError(error); - return; + return false; } else { - apiprivate->put(name, value); + return apiprivate->put(name, value); } } } + + return false; } PropertyAttributes QmlTypeWrapper::query(const Managed *m, String *name) diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index 3b0ae04cc1..cfb6cb0ec9 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -100,7 +100,7 @@ struct Q_QML_EXPORT QmlTypeWrapper : Object static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static PropertyAttributes query(const Managed *, String *name); static bool isEqualTo(Managed *that, Managed *o); diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index 44b612e7d2..d262b230e2 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -290,9 +290,11 @@ int QQmlValueTypeWrapper::typeId() const bool QQmlValueTypeWrapper::write(QObject *target, int propertyIndex) const { bool destructGadgetOnExit = false; + Q_ALLOCA_DECLARE(void, gadget); if (const QQmlValueTypeReference *ref = as<const QQmlValueTypeReference>()) { if (!d()->gadgetPtr) { - d()->gadgetPtr = alloca(d()->valueType->metaType.sizeOf()); + Q_ALLOCA_ASSIGN(void, gadget, d()->valueType->metaType.sizeOf()); + d()->gadgetPtr = gadget; d()->valueType->metaType.construct(d()->gadgetPtr, 0); destructGadgetOnExit = true; } @@ -407,13 +409,13 @@ ReturnedValue QQmlValueTypeWrapper::get(const Managed *m, String *name, bool *ha #undef VALUE_TYPE_ACCESSOR } -void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) +bool QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) { Q_ASSERT(m->as<QQmlValueTypeWrapper>()); ExecutionEngine *v4 = static_cast<QQmlValueTypeWrapper *>(m)->engine(); Scope scope(v4); if (scope.hasException()) - return; + return false; Scoped<QQmlValueTypeWrapper> r(scope, static_cast<QQmlValueTypeWrapper *>(m)); Scoped<QQmlValueTypeReference> reference(scope, m->d()); @@ -424,7 +426,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaProperty writebackProperty = reference->d()->object->metaObject()->property(reference->d()->property); if (!writebackProperty.isWritable() || !reference->readReferenceValue()) - return; + return false; writeBackPropertyType = writebackProperty.userType(); } @@ -432,7 +434,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); const QQmlPropertyData *pd = r->d()->propertyCache()->property(name, 0, 0); if (!pd) - return; + return false; if (reference) { QV4::ScopedFunctionObject f(scope, value); @@ -442,7 +444,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QString error = QStringLiteral("Cannot assign JavaScript function to value-type property"); ScopedString e(scope, v4->newString(error)); v4->throwError(e); - return; + return false; } QQmlContextData *context = v4->callingQmlContext(); @@ -459,7 +461,7 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) newBinding->setSourceLocation(bindingFunction->currentLocation()); newBinding->setTarget(reference->d()->object, cacheData, pd); QQmlPropertyPrivate::setBinding(newBinding); - return; + return true; } else { QQmlPropertyPrivate::removeBinding(reference->d()->object, QQmlPropertyIndex(reference->d()->property, pd->coreIndex())); } @@ -493,6 +495,8 @@ void QQmlValueTypeWrapper::put(Managed *m, String *name, const Value &value) QMetaObject::metacall(reference->d()->object, QMetaObject::WriteProperty, reference->d()->property, a); } } + + return true; } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 87f9116056..c8aac719ab 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -106,7 +106,7 @@ public: bool write(QObject *target, int propertyIndex) const; static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); - static void put(Managed *m, String *name, const Value &value); + static bool put(Managed *m, String *name, const Value &value); static bool isEqualTo(Managed *m, Managed *other); static PropertyAttributes query(const Managed *, String *name); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); diff --git a/src/qml/qml/qqmlvmemetaobject.cpp b/src/qml/qml/qqmlvmemetaobject.cpp index 545daa96f8..490a4e19ab 100644 --- a/src/qml/qml/qqmlvmemetaobject.cpp +++ b/src/qml/qml/qqmlvmemetaobject.cpp @@ -325,9 +325,12 @@ QQmlVMEMetaObject::QQmlVMEMetaObject(QObject *obj, if (compiledObject->nProperties || compiledObject->nFunctions) { Q_ASSERT(cache && cache->engine); QV4::ExecutionEngine *v4 = cache->engine; - QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, compiledObject->nProperties + compiledObject->nFunctions); - propertyAndMethodStorage.set(v4, data); - std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + uint size = compiledObject->nProperties + compiledObject->nFunctions; + if (size) { + QV4::Heap::MemberData *data = QV4::MemberData::allocate(v4, size); + propertyAndMethodStorage.set(v4, data); + std::fill(data->data, data->data + data->size, QV4::Encode::undefined()); + } // Need JS wrapper to ensure properties/methods are marked. ensureQObjectWrapper(); diff --git a/src/qml/qtqmlglobal_p.h b/src/qml/qtqmlglobal_p.h index 63585fd62e..026be5a703 100644 --- a/src/qml/qtqmlglobal_p.h +++ b/src/qml/qtqmlglobal_p.h @@ -55,6 +55,46 @@ #include <QtQml/private/qtqml-config_p.h> #include <QtQml/qtqmlglobal.h> +// Define Q_ALLOCA_VAR macro to be used instead of #ifdeffing +// the occurrences of alloca() in case it's not supported. +// Q_ALLOCA_DECLARE and Q_ALLOCA_ASSIGN macros separate +// memory allocation from the declaration and RAII. +#define Q_ALLOCA_VAR(type, name, size) \ + Q_ALLOCA_DECLARE(type, name); \ + Q_ALLOCA_ASSIGN(type, name, size) + +#if QT_CONFIG(alloca) + +#define Q_ALLOCA_DECLARE(type, name) \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + name = static_cast<type*>(alloca(size)) + +#else +QT_BEGIN_NAMESPACE +class Qt_AllocaWrapper +{ +public: + Qt_AllocaWrapper() { m_data = 0; } + ~Qt_AllocaWrapper() { free(m_data); } + void *data() { return m_data; } + void allocate(int size) { m_data = malloc(size); } +private: + void *m_data; +}; +QT_END_NAMESPACE + +#define Q_ALLOCA_DECLARE(type, name) \ + Qt_AllocaWrapper _qt_alloca_##name; \ + type *name = 0 + +#define Q_ALLOCA_ASSIGN(type, name, size) \ + _qt_alloca_##name.allocate(size); \ + name = static_cast<type*>(_qt_alloca_##name.data()) + +#endif + #if defined(QT_BUILD_QMLDEVTOOLS_LIB) || defined(QT_QMLDEVTOOLS_LIB) # define Q_QML_PRIVATE_EXPORT #else diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index cc4ccbaeb1..efc2828dc5 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1268,7 +1268,7 @@ void ModelNodeMetaObject::updateValues() const int roleCount = m_model->m_listModel->roleCount(); if (!m_initialized) { if (roleCount) { - int *changedRoles = reinterpret_cast<int *>(alloca(roleCount * sizeof(int))); + Q_ALLOCA_VAR(int, changedRoles, roleCount * sizeof(int)); for (int i = 0; i < roleCount; ++i) changedRoles[i] = i; emitDirectNotifies(changedRoles, roleCount); @@ -1333,7 +1333,7 @@ void ModelNodeMetaObject::emitDirectNotifies(const int *changedRoles, int roleCo namespace QV4 { -void ModelObject::put(Managed *m, String *name, const Value &value) +bool ModelObject::put(Managed *m, String *name, const Value &value) { ModelObject *that = static_cast<ModelObject*>(m); @@ -1347,6 +1347,7 @@ void ModelObject::put(Managed *m, String *name, const Value &value) ModelNodeMetaObject *mo = ModelNodeMetaObject::get(that->object()); if (mo->initialized()) mo->emitPropertyNotification(name->toQString().toUtf8()); + return true; } ReturnedValue ModelObject::get(const Managed *m, String *name, bool *hasProperty) diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index cdce78e542..44583df2a6 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -179,7 +179,7 @@ struct ModelObject : public QObjectWrapper { struct ModelObject : public QObjectWrapper { - static void put(Managed *m, String *name, const Value& value); + static bool put(Managed *m, String *name, const Value& value); static ReturnedValue get(const Managed *m, String *name, bool *hasProperty); static void advanceIterator(Managed *m, ObjectIterator *it, Value *name, uint *index, Property *p, PropertyAttributes *attributes); diff --git a/src/qml/types/qquickpackage.cpp b/src/qml/types/qquickpackage.cpp index 47d9f2f483..17eff5ac40 100644 --- a/src/qml/types/qquickpackage.cpp +++ b/src/qml/types/qquickpackage.cpp @@ -89,7 +89,7 @@ public: { DataGuard(QObject *obj, QList<DataGuard> *l) : list(l) { (QQmlGuard<QObject>&)*this = obj; } QList<DataGuard> *list; - void objectDestroyed(QObject *) { + void objectDestroyed(QObject *) override { // we assume priv will always be destroyed after objectDestroyed calls list->removeOne(*this); } diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index b9d312d41f..c77495a1a7 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -103,8 +103,8 @@ public: virtual QVariant value(int role) const = 0; virtual void setValue(int role, const QVariant &value) = 0; - void setValue(const QString &role, const QVariant &value); - bool resolveIndex(const QQmlAdaptorModel &model, int idx); + void setValue(const QString &role, const QVariant &value) override; + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override; static QV4::ReturnedValue get_property(QV4::CallContext *ctx, uint propertyId); static QV4::ReturnedValue set_property(QV4::CallContext *ctx, uint propertyId); @@ -141,7 +141,7 @@ public: const QList<QQmlDelegateModelItem *> &items, int index, int count, - const QVector<int> &roles) const + const QVector<int> &roles) const override { bool changed = roles.isEmpty() && !watchedRoles.isEmpty(); if (!changed && !watchedRoles.isEmpty() && watchedRoleIds.isEmpty()) { @@ -185,7 +185,7 @@ public: void replaceWatchedRoles( QQmlAdaptorModel &, const QList<QByteArray> &oldRoles, - const QList<QByteArray> &newRoles) const + const QList<QByteArray> &newRoles) const override { VDMModelDelegateDataType *dataType = const_cast<VDMModelDelegateDataType *>(this); @@ -239,12 +239,12 @@ public: // QAbstractDynamicMetaObject - void objectDestroyed(QObject *) + void objectDestroyed(QObject *) override { release(); } - int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) + int metaCall(QObject *object, QMetaObject::Call call, int id, void **arguments) override { return static_cast<QQmlDMCachedModelData *>(object)->metaCall(call, id, arguments); } @@ -415,18 +415,18 @@ public: } } - QVariant value(int role) const + QVariant value(int role) const override { return type->model->aim()->index(index, 0, type->model->rootIndex).data(role); } - void setValue(int role, const QVariant &value) + void setValue(int role, const QVariant &value) override { type->model->aim()->setData( type->model->aim()->index(index, 0, type->model->rootIndex), value, role); } - QV4::ReturnedValue get() + QV4::ReturnedValue get() override { if (type->prototype.isUndefined()) { QQmlAdaptorModelEngineData * const data = engineData(v4); @@ -449,12 +449,12 @@ public: { } - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.aim()->rowCount(model.rootIndex); } - void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const + void cleanup(QQmlAdaptorModel &model, QQmlDelegateModel *vdm) const override { QAbstractItemModel * const aim = model.aim(); if (aim && vdm) { @@ -477,7 +477,7 @@ public: const_cast<VDMAbstractItemModelDataType *>(this)->release(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { QHash<QByteArray, int>::const_iterator it = roleNames.find(role.toUtf8()); if (it != roleNames.end()) { @@ -489,26 +489,26 @@ public: } } - QVariant parentModelIndex(const QQmlAdaptorModel &model) const + QVariant parentModelIndex(const QQmlAdaptorModel &model) const override { return model ? QVariant::fromValue(model.aim()->parent(model.rootIndex)) : QVariant(); } - QVariant modelIndex(const QQmlAdaptorModel &model, int index) const + QVariant modelIndex(const QQmlAdaptorModel &model, int index) const override { return model ? QVariant::fromValue(model.aim()->index(index, 0, model.rootIndex)) : QVariant(); } - bool canFetchMore(const QQmlAdaptorModel &model) const + bool canFetchMore(const QQmlAdaptorModel &model) const override { return model && model.aim()->canFetchMore(model.rootIndex); } - void fetchMore(QQmlAdaptorModel &model) const + void fetchMore(QQmlAdaptorModel &model) const override { if (model) model.aim()->fetchMore(model.rootIndex); @@ -518,7 +518,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *engine, - int index) const + int index) const override { VDMAbstractItemModelDataType *dataType = const_cast<VDMAbstractItemModelDataType *>(this); if (!metaObject) @@ -606,7 +606,7 @@ public: return QV4::Encode::undefined(); } - QV4::ReturnedValue get() + QV4::ReturnedValue get() override { QQmlAdaptorModelEngineData *data = engineData(v4); QV4::Scope scope(v4); @@ -617,13 +617,13 @@ public: return o.asReturnedValue(); } - void setValue(const QString &role, const QVariant &value) + void setValue(const QString &role, const QVariant &value) override { if (role == QLatin1String("modelData")) cachedData = value; } - bool resolveIndex(const QQmlAdaptorModel &model, int idx) + bool resolveIndex(const QQmlAdaptorModel &model, int idx) override { if (index == -1) { index = idx; @@ -650,12 +650,12 @@ class VDMListDelegateDataType : public QQmlAdaptorModel::Accessors public: inline VDMListDelegateDataType() {} - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.list.count(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { return role == QLatin1String("modelData") ? model.list.at(index) @@ -666,7 +666,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *, - int index) const + int index) const override { return new QQmlDMListAccessorData( metaType, @@ -693,7 +693,7 @@ public: QObject *object); QObject *modelData() const { return object; } - QObject *proxiedObject() { return object; } + QObject *proxiedObject() override { return object; } QPointer<QObject> object; }; @@ -735,12 +735,12 @@ public: free(metaObject); } - int count(const QQmlAdaptorModel &model) const + int count(const QQmlAdaptorModel &model) const override { return model.list.count(); } - QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const + QVariant value(const QQmlAdaptorModel &model, int index, const QString &role) const override { if (QObject *object = model.list.at(index).value<QObject *>()) return object->property(role.toUtf8()); @@ -751,7 +751,7 @@ public: QQmlAdaptorModel &model, QQmlDelegateModelItemMetaType *metaType, QQmlEngine *, - int index) const + int index) const override { VDMObjectDelegateDataType *dataType = const_cast<VDMObjectDelegateDataType *>(this); if (!metaObject) @@ -768,7 +768,7 @@ public: metaObject = builder.toMetaObject(); } - void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const + void cleanup(QQmlAdaptorModel &, QQmlDelegateModel *) const override { const_cast<VDMObjectDelegateDataType *>(this)->release(); } @@ -792,7 +792,7 @@ public: m_type->release(); } - int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) + int metaCall(QObject *o, QMetaObject::Call call, int id, void **arguments) override { Q_ASSERT(o == m_data); Q_UNUSED(o); @@ -813,7 +813,7 @@ public: } } - int createProperty(const char *name, const char *) + int createProperty(const char *name, const char *) override { if (!m_data->object) return -1; |