diff options
60 files changed, 1122 insertions, 629 deletions
diff --git a/.qmake.conf b/.qmake.conf index b0bc49d477..a0d132328a 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -2,5 +2,5 @@ load(qt_build_config) CONFIG += qt_example_installs CONFIG += warning_clean -MODULE_VERSION = 5.2.0 +MODULE_VERSION = 5.2.1 diff --git a/examples/quick/scenegraph/openglunderqml/squircle.cpp b/examples/quick/scenegraph/openglunderqml/squircle.cpp index 8ceb9c5f9e..91d69c90a4 100644 --- a/examples/quick/scenegraph/openglunderqml/squircle.cpp +++ b/examples/quick/scenegraph/openglunderqml/squircle.cpp @@ -129,7 +129,10 @@ void Squircle::paint() m_program->setAttributeArray(0, GL_FLOAT, values, 2); m_program->setUniformValue("t", (float) m_thread_t); - glViewport(0, 0, window()->width(), window()->height()); + qreal ratio = window()->devicePixelRatio(); + int w = int(ratio * window()->width()); + int h = int(ratio * window()->height()); + glViewport(0, 0, w, h); glDisable(GL_DEPTH_TEST); diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp index 2215551b95..c32ad2958d 100644 --- a/src/qml/compiler/qqmlcodegenerator.cpp +++ b/src/qml/compiler/qqmlcodegenerator.cpp @@ -1208,6 +1208,12 @@ JSCodeGen::JSCodeGen(QQmlEnginePrivate *enginePrivate, const QString &fileName, , jsEngine(jsEngine) , qmlRoot(qmlRoot) , imports(imports) + , _contextObject(0) + , _scopeObject(0) + , _contextObjectTemp(-1) + , _scopeObjectTemp(-1) + , _importedScriptsTemp(-1) + , _idArrayTemp(-1) { _module = jsModule; _module->setFileName(fileName); @@ -1318,51 +1324,104 @@ QQmlPropertyData *JSCodeGen::lookupQmlCompliantProperty(QQmlPropertyCache *cache return pd; } -V4IR::Expr *JSCodeGen::member(V4IR::Expr *base, const QString *name) -{ - V4IR::Member *baseAsMember = base->asMember(); - if (baseAsMember) { - QQmlPropertyCache *cache = 0; - - if (baseAsMember->type == V4IR::Member::MemberOfQObject - && baseAsMember->property->isQObject()) { +static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); - bool propertySuitable = baseAsMember->property->isFinal(); +enum MetaObjectResolverFlags { + AllPropertiesAreFinal = 0x1, + LookupsIncludeEnums = 0x2, + LookupsExcludeProperties = 0x4 +}; - if (!propertySuitable) { - // Properties of the scope or context object do not need to be final, as we - // intend to find the version of a property available at compile time, not at run-time. - if (V4IR::Name *baseName = baseAsMember->base->asName()) - propertySuitable = baseName->builtin == V4IR::Name::builtin_qml_scope_object || baseName->builtin == V4IR::Name::builtin_qml_context_object; +static V4IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member) +{ + V4IR::Type result = V4IR::VarType; + QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data); + + if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) { + const QMetaObject *mo = metaObject->createMetaObject(); + QByteArray enumName = member->name->toUtf8(); + for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) { + QMetaEnum metaEnum = mo->enumerator(ii); + bool ok; + int value = metaEnum.keyToValue(enumName.constData(), &ok); + if (ok) { + member->memberIsEnum = true; + member->enumValue = value; + resolver->clear(); + return V4IR::SInt32Type; } + } + } - // Check if it's suitable for caching - if (propertySuitable) - cache = engine->propertyCacheForType(baseAsMember->property->propType); - } else if (baseAsMember->type == V4IR::Member::MemberOfQmlContext) { - // Similarly, properties of an id referenced object also don't need to be final, because - // we intend to find the version of a property available at compile time, not at run-time. - foreach (const IdMapping &mapping, _idObjects) { - if (baseAsMember->memberIndex == mapping.idIndex) { - cache = mapping.type; - break; + if (qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { + QQmlPropertyData *property = member->property; + if (!property && metaObject) { + if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) { + const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal)) + && !candidate->isFunction(); + if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) { + property = candidate; + member->property = candidate; // Cache for next iteration and isel needs it. } } } - if (cache) { - if (QQmlPropertyData *pd = lookupQmlCompliantProperty(cache, *name)) { - const unsigned baseTemp = _block->newTemp(); - move(_block->TEMP(baseTemp), base); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(baseTemp), name, pd); + if (property) { + // Enums cannot be mapped to IR types, they need to go through the run-time handling + // of accepting strings that will then be converted to the right values. + if (property->isEnum()) + return V4IR::VarType; + + switch (property->propType) { + case QMetaType::Bool: result = V4IR::BoolType; break; + case QMetaType::Int: result = V4IR::SInt32Type; break; + case QMetaType::Double: result = V4IR::DoubleType; break; + case QMetaType::QString: result = V4IR::StringType; break; + default: + if (property->isQObject()) { + if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType)) { + initMetaObjectResolver(resolver, cache); + return V4IR::QObjectType; + } + } + break; } } } - return QQmlJS::Codegen::member(base, name); + resolver->clear(); + return result; +} + +static void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject) +{ + resolver->resolveMember = &resolveMetaObjectProperty; + resolver->data = metaObject; + resolver->flags = 0; +} + +void JSCodeGen::beginFunctionBodyHook() +{ + _contextObjectTemp = _block->newTemp(); + _scopeObjectTemp = _block->newTemp(); + _importedScriptsTemp = _block->newTemp(); + _idArrayTemp = _block->newTemp(); + + V4IR::Temp *temp = _block->TEMP(_contextObjectTemp); + initMetaObjectResolver(&temp->memberResolver, _contextObject); + move(temp, _block->NAME(V4IR::Name::builtin_qml_context_object, 0, 0)); + + temp = _block->TEMP(_scopeObjectTemp); + initMetaObjectResolver(&temp->memberResolver, _scopeObject); + move(temp, _block->NAME(V4IR::Name::builtin_qml_scope_object, 0, 0)); + + move(_block->TEMP(_importedScriptsTemp), _block->NAME(V4IR::Name::builtin_qml_imported_scripts_object, 0, 0)); + move(_block->TEMP(_idArrayTemp), _block->NAME(V4IR::Name::builtin_qml_id_array, 0, 0)); } V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col) { + Q_UNUSED(line) + Q_UNUSED(col) // Implement QML lookup semantics in the current file context. // // Note: We do not check if properties of the qml scope object or context object @@ -1378,17 +1437,52 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col foreach (const IdMapping &mapping, _idObjects) if (name == mapping.name) { _function->idObjectDependencies.insert(mapping.idIndex); - return _block->QML_CONTEXT_MEMBER(_block->NAME(V4IR::Name::builtin_qml_id_scope, line, col), - _function->newString(mapping.name), mapping.idIndex); + V4IR::Expr *s = subscript(_block->TEMP(_idArrayTemp), _block->CONST(V4IR::SInt32Type, mapping.idIndex)); + V4IR::Temp *result = _block->TEMP(_block->newTemp()); + initMetaObjectResolver(&result->memberResolver, mapping.type); + _block->MOVE(result, s); + result = _block->TEMP(result->index); + result->isReadOnly = true; // don't allow use as lvalue + return result; } { QQmlTypeNameCache::Result r = imports->query(name); if (r.isValid()) { - if (r.scriptIndex != -1) - return subscript(_block->NAME(V4IR::Name::builtin_qml_imported_scripts_object, line, col), _block->CONST(V4IR::NumberType, r.scriptIndex)); - else + if (r.scriptIndex != -1) { + return subscript(_block->TEMP(_importedScriptsTemp), _block->CONST(V4IR::SInt32Type, r.scriptIndex)); + } else if (r.type) { + V4IR::Name *typeName = _block->NAME(name, line, col); + V4IR::Temp *result = _block->TEMP(_block->newTemp()); + + if (r.type->isSingleton()) { + if (r.type->isCompositeSingleton()) { + QQmlTypeData *tdata = engine->typeLoader.getType(r.type->singletonInstanceInfo()->url); + Q_ASSERT(tdata); + Q_ASSERT(tdata->isComplete()); + initMetaObjectResolver(&result->memberResolver, engine->propertyCacheForType(tdata->compiledData()->metaTypeId)); + result->memberResolver.flags |= AllPropertiesAreFinal; + } else { + const QMetaObject *singletonMo = r.type->singletonInstanceInfo()->instanceMetaObject; + if (!singletonMo) // We can only accelerate C++ singletons that were registered with their meta-type + return 0; + initMetaObjectResolver(&result->memberResolver, engine->cache(singletonMo)); + } + + // Instruct the isel to not load this as activation property but through the + // run-time's singleton getter. + typeName->qmlSingleton = true; + } else { + initMetaObjectResolver(&result->memberResolver,engine->cache(r.type->metaObject())); + result->memberResolver.flags |= LookupsExcludeProperties; + } + typeName->freeOfSideEffects = true; + result->memberResolver.flags |= LookupsIncludeEnums; + _block->MOVE(result, typeName); + return _block->TEMP(result->index); + } else { return 0; // TODO: We can't do fast lookup for these yet. + } } } @@ -1400,9 +1494,9 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col if (pd) { if (!pd->isConstant()) _function->scopeObjectDependencies.insert(pd); - int base = _block->newTemp(); - move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_scope_object, line, col)); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), _function->newString(name), pd); + V4IR::Temp *base = _block->TEMP(_scopeObjectTemp); + initMetaObjectResolver(&base->memberResolver, _scopeObject); + return _block->MEMBER(base, _function->newString(name), pd); } } @@ -1414,9 +1508,9 @@ V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col if (pd) { if (!pd->isConstant()) _function->contextObjectDependencies.insert(pd); - int base = _block->newTemp(); - move(_block->TEMP(base), _block->NAME(V4IR::Name::builtin_qml_context_object, line, col)); - return _block->QML_QOBJECT_PROPERTY(_block->TEMP(base), _function->newString(name), pd); + V4IR::Temp *base = _block->TEMP(_contextObjectTemp); + initMetaObjectResolver(&base->memberResolver, _contextObject); + return _block->MEMBER(base, _function->newString(name), pd); } } diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h index a5fec65111..f16f910078 100644 --- a/src/qml/compiler/qqmlcodegenerator_p.h +++ b/src/qml/compiler/qqmlcodegenerator_p.h @@ -364,10 +364,8 @@ struct Q_QML_EXPORT JSCodeGen : public QQmlJS::Codegen // Returns mapping from input functions to index in V4IR::Module::functions / compiledData->runtimeFunctions QVector<int> generateJSCodeForFunctionsAndBindings(const QList<AST::Node*> &functions, const QHash<int, QString> &functionNames); - // Resolve QObject members with the help of QQmlEngine's meta type registry - virtual V4IR::Expr *member(V4IR::Expr *base, const QString *name); - protected: + virtual void beginFunctionBodyHook(); virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); private: @@ -382,6 +380,10 @@ private: ObjectIdMapping _idObjects; QQmlPropertyCache *_contextObject; QQmlPropertyCache *_scopeObject; + int _contextObjectTemp; + int _scopeObjectTemp; + int _importedScriptsTemp; + int _idArrayTemp; }; } // namespace QtQml diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 893abc9659..a8338f8656 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -2050,6 +2050,8 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, ast->firstSourceLocation().startLine, ast->firstSourceLocation().startColumn), 0)); } + beginFunctionBodyHook(); + sourceElements(body); _function->insertBasicBlock(_exitBlock); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index de22e8904b..32f1f1bfd4 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -293,7 +293,7 @@ protected: _exceptionHandlers.pop(); } - virtual V4IR::Expr *member(V4IR::Expr *base, const QString *name); // Re-implemented by QML to resolve QObject property members + V4IR::Expr *member(V4IR::Expr *base, const QString *name); V4IR::Expr *subscript(V4IR::Expr *base, V4IR::Expr *index); V4IR::Expr *argument(V4IR::Expr *expr); V4IR::Expr *reference(V4IR::Expr *expr); @@ -330,6 +330,7 @@ protected: V4IR::Expr *identifier(const QString &name, int line = 0, int col = 0); // Hook provided to implement QML lookup semantics virtual V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col); + virtual void beginFunctionBodyHook() {} // nodes virtual bool visit(AST::ArgumentList *ast); diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 9baf7f89ca..180391ff33 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -98,6 +98,7 @@ QT_BEGIN_NAMESPACE F(CallBuiltinConvertThisToObject, callBuiltinConvertThisToObject) \ F(CreateValue, createValue) \ F(CreateProperty, createProperty) \ + F(ConstructPropertyLookup, constructPropertyLookup) \ F(CreateActivationProperty, createActivationProperty) \ F(ConstructGlobalLookup, constructGlobalLookup) \ F(Jump, jump) \ @@ -125,10 +126,11 @@ QT_BEGIN_NAMESPACE F(MulNumberParams, mulNumberParams) \ F(SubNumberParams, subNumberParams) \ F(LoadThis, loadThis) \ - F(LoadQmlIdObject, loadQmlIdObject) \ + F(LoadQmlIdArray, loadQmlIdArray) \ F(LoadQmlImportedScripts, loadQmlImportedScripts) \ F(LoadQmlContextObject, loadQmlContextObject) \ - F(LoadQmlScopeObject, loadQmlScopeObject) + F(LoadQmlScopeObject, loadQmlScopeObject) \ + F(LoadQmlSingleton, loadQmlSingleton) #if defined(Q_CC_GNU) && (!defined(Q_CC_INTEL) || __INTEL_COMPILER >= 1200) # define MOTH_THREADED_INTERPRETER @@ -491,6 +493,14 @@ union Instr Param base; Param result; }; + struct instr_constructPropertyLookup { + MOTH_INSTR_HEADER + int index; + quint32 argc; + quint32 callData; + Param base; + Param result; + }; struct instr_createActivationProperty { MOTH_INSTR_HEADER int name; @@ -645,10 +655,9 @@ union Instr MOTH_INSTR_HEADER Param result; }; - struct instr_loadQmlIdObject { + struct instr_loadQmlIdArray { MOTH_INSTR_HEADER Param result; - int id; }; struct instr_loadQmlImportedScripts { MOTH_INSTR_HEADER @@ -662,6 +671,11 @@ union Instr MOTH_INSTR_HEADER Param result; }; + struct instr_loadQmlSingleton { + MOTH_INSTR_HEADER + Param result; + int name; + }; instr_common common; instr_ret ret; @@ -712,6 +726,7 @@ union Instr instr_callBuiltinConvertThisToObject callBuiltinConvertThisToObject; instr_createValue createValue; instr_createProperty createProperty; + instr_constructPropertyLookup constructPropertyLookup; instr_createActivationProperty createActivationProperty; instr_constructGlobalLookup constructGlobalLookup; instr_jump jump; @@ -739,10 +754,11 @@ union Instr instr_mulNumberParams mulNumberParams; instr_subNumberParams subNumberParams; instr_loadThis loadThis; - instr_loadQmlIdObject loadQmlIdObject; + instr_loadQmlIdArray loadQmlIdArray; instr_loadQmlImportedScripts loadQmlImportedScripts; instr_loadQmlContextObject loadQmlContextObject; instr_loadQmlScopeObject loadQmlScopeObject; + instr_loadQmlSingleton loadQmlSingleton; static int size(Type type); }; diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp index a999dd4da1..ed57852cd6 100644 --- a/src/qml/compiler/qv4isel_masm.cpp +++ b/src/qml/compiler/qv4isel_masm.cpp @@ -439,15 +439,6 @@ static void printDisassembledOutputWithCalls(const char* output, const QHash<voi } #endif -void Assembler::recordLineNumber(int lineNumber) -{ - CodeLineNumerMapping mapping; - mapping.location = label(); - mapping.lineNumber = lineNumber; - codeLineNumberMappings << mapping; -} - - JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) { Label endOfCode = label(); @@ -467,14 +458,6 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) JSC::JSGlobalData dummy(_executableAllocator); JSC::LinkBuffer linkBuffer(dummy, this, 0); - QVector<uint> lineNumberMapping(codeLineNumberMappings.count() * 2); - - for (int i = 0; i < codeLineNumberMappings.count(); ++i) { - lineNumberMapping[i * 2] = linkBuffer.offsetOf(codeLineNumberMappings.at(i).location); - lineNumberMapping[i * 2 + 1] = codeLineNumberMappings.at(i).lineNumber; - } - _isel->jsUnitGenerator()->registerLineNumberMapping(_function, lineNumberMapping); - QHash<void*, const char*> functions; foreach (CallToLink ctl, _callsToLink) { linkBuffer.link(ctl.call, ctl.externalFunction); @@ -556,10 +539,11 @@ JSC::MacroAssemblerCodeRef Assembler::link(int *codeSize) return codeRef; } -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, Compiler::JSUnitGenerator *jsGenerator) : EvalInstructionSelection(execAllocator, module, jsGenerator) , _block(0) , _as(0) + , qmlEngine(qmlEngine) { compilationUnit = new CompilationUnit; compilationUnit->codeRefs.resize(module->functions.size()); @@ -578,7 +562,7 @@ void InstructionSelection::run(int functionIndex) qSwap(_function, function); V4IR::Optimizer opt(_function); - opt.run(); + opt.run(qmlEngine); #if (CPU(X86_64) && (OS(MAC_OS_X) || OS(LINUX))) || (CPU(X86) && OS(LINUX)) static const bool withRegisterAllocator = qgetenv("QV4_NO_REGALLOC").isEmpty(); @@ -642,9 +626,9 @@ void InstructionSelection::run(int functionIndex) foreach (V4IR::Stmt *s, _block->statements) { if (s->location.isValid()) { - _as->recordLineNumber(s->location.startLine); if (int(s->location.startLine) != lastLine) { - _as->saveInstructionPointer(Assembler::ScratchRegister); + Assembler::Address lineAddr(Assembler::ContextRegister, qOffsetOf(QV4::ExecutionContext, lineNumber)); + _as->store32(Assembler::TrustedImm32(s->location.startLine), lineAddr); lastLine = s->location.startLine; } } @@ -896,9 +880,9 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) #endif } -void InstructionSelection::loadQmlIdObject(int id, V4IR::Temp *temp) +void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp) { - generateFunctionCall(temp, __qmljs_get_id_object, Assembler::ContextRegister, Assembler::TrustedImm32(id)); + generateFunctionCall(temp, __qmljs_get_id_array, Assembler::ContextRegister); } void InstructionSelection::loadQmlImportedScripts(V4IR::Temp *temp) @@ -916,6 +900,11 @@ void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) generateFunctionCall(temp, __qmljs_get_scope_object, Assembler::ContextRegister); } +void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp) +{ + generateFunctionCall(temp, __qmljs_get_qml_singleton, Assembler::ContextRegister, Assembler::PointerToString(name)); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { if (targetTemp->kind == V4IR::Temp::PhysicalRegister) { @@ -1786,9 +1775,18 @@ void InstructionSelection::constructActivationProperty(V4IR::Name *func, V4IR::E void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) { - prepareCallData(args, 0); + prepareCallData(args, base); + if (useFastLookups) { + uint index = registerGetterLookup(name); + generateFunctionCall(result, __qmljs_construct_property_lookup, + Assembler::ContextRegister, + Assembler::TrustedImm32(index), + baseAddressForCallData()); + return; + } + generateFunctionCall(result, __qmljs_construct_property, Assembler::ContextRegister, - Assembler::Reference(base), Assembler::PointerToString(name), + Assembler::PointerToString(name), baseAddressForCallData()); } diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h index e3b41857ea..4b0d19df07 100644 --- a/src/qml/compiler/qv4isel_masm_p.h +++ b/src/qml/compiler/qv4isel_masm_p.h @@ -466,23 +466,6 @@ public: V4IR::BasicBlock *block; }; - void saveInstructionPointer(RegisterID freeScratchRegister) { - Address ipAddr(ContextRegister, qOffsetOf(QV4::ExecutionContext, jitInstructionPointer)); - RegisterID sourceRegister = freeScratchRegister; - -#if CPU(X86_64) || CPU(X86) - callToRetrieveIP(); - peek(sourceRegister); - pop(); -#elif CPU(ARM) - move(JSC::ARMRegisters::pc, sourceRegister); -#else -#error "Port me!" -#endif - - storePtr(sourceRegister, ipAddr); - } - void callAbsolute(const char* functionName, FunctionPtr function) { CallToLink ctl; ctl.call = call(); @@ -1397,8 +1380,6 @@ public: JSC::MacroAssemblerCodeRef link(int *codeSize); - void recordLineNumber(int lineNumber); - const StackLayout stackLayout() const { return _stackLayout; } ConstantTable &constantTable() { return _constTable; } @@ -1424,13 +1405,6 @@ private: QV4::ExecutableAllocator *_executableAllocator; InstructionSelection *_isel; - - struct CodeLineNumerMapping - { - Assembler::Label location; - int lineNumber; - }; - QVector<CodeLineNumerMapping> codeLineNumberMappings; }; template <typename T> inline void prepareRelativeCall(const T &, Assembler *){} @@ -1445,7 +1419,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -1483,10 +1457,11 @@ protected: virtual void callSubscript(V4IR::Expr *base, V4IR::Expr *index, V4IR::ExprList *args, V4IR::Temp *result); virtual void convertType(V4IR::Temp *source, V4IR::Temp *target); virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadQmlIdObject(int id, V4IR::Temp *temp); + virtual void loadQmlIdArray(V4IR::Temp *temp); virtual void loadQmlImportedScripts(V4IR::Temp *temp); virtual void loadQmlContextObject(V4IR::Temp *temp); virtual void loadQmlScopeObject(V4IR::Temp *temp); + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); @@ -1655,14 +1630,15 @@ private: Assembler* _as; CompilationUnit *compilationUnit; + QQmlEnginePrivate *qmlEngine; }; class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(execAllocator, module, jsGenerator); } + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return true; } }; diff --git a/src/qml/compiler/qv4isel_moth.cpp b/src/qml/compiler/qv4isel_moth.cpp index 6db8f11a9e..ee7e8ce2e8 100644 --- a/src/qml/compiler/qv4isel_moth.cpp +++ b/src/qml/compiler/qv4isel_moth.cpp @@ -152,8 +152,9 @@ inline bool isBoolType(V4IR::Expr *e) } // anonymous namespace -InstructionSelection::InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) +InstructionSelection::InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) : EvalInstructionSelection(execAllocator, module, jsGenerator) + , qmlEngine(qmlEngine) , _block(0) , _codeStart(0) , _codeNext(0) @@ -191,7 +192,7 @@ void InstructionSelection::run(int functionIndex) qSwap(codeEnd, _codeEnd); V4IR::Optimizer opt(_function); - opt.run(); + opt.run(qmlEngine); if (opt.isInSSA()) { opt.convertOutOfSSA(); opt.showMeTheCode(_function); @@ -351,6 +352,16 @@ void InstructionSelection::constructActivationProperty(V4IR::Name *func, void InstructionSelection::constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) { + if (useFastLookups) { + Instruction::ConstructPropertyLookup call; + call.base = getParam(base); + call.index = registerGetterLookup(name); + prepareCallArgs(args, call.argc); + call.callData = callDataStart(); + call.result = getResultParam(result); + addInstruction(call); + return; + } Instruction::CreateProperty create; create.base = getParam(base); create.name = registerString(name); @@ -377,11 +388,10 @@ void InstructionSelection::loadThisObject(V4IR::Temp *temp) addInstruction(load); } -void InstructionSelection::loadQmlIdObject(int id, V4IR::Temp *temp) +void InstructionSelection::loadQmlIdArray(V4IR::Temp *temp) { - Instruction::LoadQmlIdObject load; + Instruction::LoadQmlIdArray load; load.result = getResultParam(temp); - load.id = id; addInstruction(load); } @@ -406,6 +416,14 @@ void InstructionSelection::loadQmlScopeObject(V4IR::Temp *temp) addInstruction(load); } +void InstructionSelection::loadQmlSingleton(const QString &name, V4IR::Temp *temp) +{ + Instruction::LoadQmlSingleton load; + load.result = getResultParam(temp); + load.name = registerString(name); + addInstruction(load); +} + void InstructionSelection::loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { assert(sourceConst); diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h index ffb8ff4539..031c2d838a 100644 --- a/src/qml/compiler/qv4isel_moth_p.h +++ b/src/qml/compiler/qv4isel_moth_p.h @@ -68,7 +68,7 @@ class Q_QML_EXPORT InstructionSelection: public EvalInstructionSelection { public: - InstructionSelection(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); + InstructionSelection(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator); ~InstructionSelection(); virtual void run(int functionIndex); @@ -112,10 +112,11 @@ protected: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result); virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result); virtual void loadThisObject(V4IR::Temp *temp); - virtual void loadQmlIdObject(int id, V4IR::Temp *temp); + virtual void loadQmlIdArray(V4IR::Temp *temp); virtual void loadQmlImportedScripts(V4IR::Temp *temp); virtual void loadQmlContextObject(V4IR::Temp *temp); virtual void loadQmlScopeObject(V4IR::Temp *temp); + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp); virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp); virtual void loadString(const QString &str, V4IR::Temp *targetTemp); virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp); @@ -168,6 +169,8 @@ private: void patchJumpAddresses(); QByteArray squeezeCode() const; + QQmlEnginePrivate *qmlEngine; + V4IR::BasicBlock *_block; V4IR::BasicBlock *_nextBlock; @@ -189,8 +192,8 @@ class Q_QML_EXPORT ISelFactory: public EvalISelFactory { public: virtual ~ISelFactory() {} - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) - { return new InstructionSelection(execAllocator, module, jsGenerator); } + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) + { return new InstructionSelection(qmlEngine, execAllocator, module, jsGenerator); } virtual bool jitCompileRegexps() const { return false; } }; diff --git a/src/qml/compiler/qv4isel_p.cpp b/src/qml/compiler/qv4isel_p.cpp index 45b1e9f3b0..96a3370903 100644 --- a/src/qml/compiler/qv4isel_p.cpp +++ b/src/qml/compiler/qv4isel_p.cpp @@ -103,12 +103,16 @@ void IRDecoder::visitMove(V4IR::Move *s) if (V4IR::Name *n = s->source->asName()) { if (n->id && *n->id == QStringLiteral("this")) // TODO: `this' should be a builtin. loadThisObject(t); + else if (n->builtin == V4IR::Name::builtin_qml_id_array) + loadQmlIdArray(t); else if (n->builtin == V4IR::Name::builtin_qml_context_object) loadQmlContextObject(t); else if (n->builtin == V4IR::Name::builtin_qml_scope_object) loadQmlScopeObject(t); else if (n->builtin == V4IR::Name::builtin_qml_imported_scripts_object) loadQmlImportedScripts(t); + else if (n->qmlSingleton) + loadQmlSingleton(*n->id, t); else getActivationProperty(n, t); return; @@ -142,15 +146,7 @@ void IRDecoder::visitMove(V4IR::Move *s) return; } } else if (V4IR::Member *m = s->source->asMember()) { - if (m->type == V4IR::Member::MemberOfQmlContext) { - V4IR::Name *base = m->base->asName(); - Q_ASSERT(base); - - if (base->builtin == V4IR::Name::builtin_qml_id_scope) { - loadQmlIdObject(m->memberIndex, t); - return; - } - } else if (m->type == V4IR::Member::MemberOfQObject) { + if (m->property) { bool captureRequired = true; if (_function) { captureRequired = !_function->contextObjectDependencies.contains(m->property) @@ -195,7 +191,7 @@ void IRDecoder::visitMove(V4IR::Move *s) } else if (V4IR::Member *m = s->target->asMember()) { if (m->base->asTemp() || m->base->asConst()) { if (s->source->asTemp() || s->source->asConst()) { - if (m->type == V4IR::Member::MemberOfQObject) { + if (m->property) { setQObjectProperty(s->source, m->base, m->property->coreIndex); return; } else { diff --git a/src/qml/compiler/qv4isel_p.h b/src/qml/compiler/qv4isel_p.h index 23ef7cc69e..7ee5cbba4e 100644 --- a/src/qml/compiler/qv4isel_p.h +++ b/src/qml/compiler/qv4isel_p.h @@ -52,6 +52,8 @@ QT_BEGIN_NAMESPACE +class QQmlEnginePrivate; + namespace QV4 { class ExecutableAllocator; struct Function; @@ -92,7 +94,7 @@ class Q_QML_EXPORT EvalISelFactory { public: virtual ~EvalISelFactory() = 0; - virtual EvalInstructionSelection *create(QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; + virtual EvalInstructionSelection *create(QQmlEnginePrivate *qmlEngine, QV4::ExecutableAllocator *execAllocator, V4IR::Module *module, QV4::Compiler::JSUnitGenerator *jsGenerator) = 0; virtual bool jitCompileRegexps() const = 0; }; @@ -142,10 +144,11 @@ public: // to implement by subclasses: virtual void constructProperty(V4IR::Temp *base, const QString &name, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void constructValue(V4IR::Temp *value, V4IR::ExprList *args, V4IR::Temp *result) = 0; virtual void loadThisObject(V4IR::Temp *temp) = 0; - virtual void loadQmlIdObject(int id, V4IR::Temp *temp) = 0; + virtual void loadQmlIdArray(V4IR::Temp *temp) = 0; virtual void loadQmlImportedScripts(V4IR::Temp *temp) = 0; virtual void loadQmlContextObject(V4IR::Temp *temp) = 0; virtual void loadQmlScopeObject(V4IR::Temp *temp) = 0; + virtual void loadQmlSingleton(const QString &name, V4IR::Temp *temp) = 0; virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) = 0; virtual void loadString(const QString &str, V4IR::Temp *targetTemp) = 0; virtual void loadRegexp(V4IR::RegExp *sourceRegexp, V4IR::Temp *targetTemp) = 0; diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp index 75261b2469..41502dbd4f 100644 --- a/src/qml/compiler/qv4jsir.cpp +++ b/src/qml/compiler/qv4jsir.cpp @@ -72,6 +72,7 @@ QString typeName(Type t) case NumberType: return QStringLiteral("number"); case StringType: return QStringLiteral("string"); case VarType: return QStringLiteral("var"); + case QObjectType: return QStringLiteral("qobject"); default: return QStringLiteral("multiple"); } } @@ -274,8 +275,16 @@ static QString dumpStart(const Expr *e) { if (e->type == UnknownType) // return QStringLiteral("**UNKNOWN**"); return QString(); - else - return typeName(e->type) + QStringLiteral("{"); + + QString result = typeName(e->type); + const Temp *temp = const_cast<Expr*>(e)->asTemp(); + if (e->type == QObjectType && temp && temp->memberResolver.data) { + result += QLatin1Char('<'); + result += QString::fromUtf8(static_cast<QQmlPropertyCache*>(temp->memberResolver.data)->className()); + result += QLatin1Char('>'); + } + result += QLatin1Char('{'); + return result; } static const char *dumpEnd(const Expr *e) { @@ -363,6 +372,8 @@ void Name::initGlobal(const QString *id, quint32 line, quint32 column) this->id = id; this->builtin = builtin_invalid; this->global = true; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -372,6 +383,8 @@ void Name::init(const QString *id, quint32 line, quint32 column) this->id = id; this->builtin = builtin_invalid; this->global = false; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -381,6 +394,8 @@ void Name::init(Builtin builtin, quint32 line, quint32 column) this->id = 0; this->builtin = builtin; this->global = false; + this->qmlSingleton = false; + this->freeOfSideEffects = false; this->line = line; this->column = column; } @@ -424,8 +439,8 @@ static const char *builtin_to_string(Name::Builtin b) return "builtin_setup_argument_object"; case V4IR::Name::builtin_convert_this_to_object: return "builtin_convert_this_to_object"; - case V4IR::Name::builtin_qml_id_scope: - return "builtin_qml_id_scope"; + case V4IR::Name::builtin_qml_id_array: + return "builtin_qml_id_array"; case V4IR::Name::builtin_qml_imported_scripts_object: return "builtin_qml_imported_scripts_object"; case V4IR::Name::builtin_qml_scope_object: @@ -541,7 +556,7 @@ void Member::dump(QTextStream &out) const { base->dump(out); out << '.' << *name; - if (type == MemberOfQObject) + if (property) out << " (meta-property " << property->coreIndex << " <" << QMetaType::typeName(property->propType) << ">)"; } @@ -825,24 +840,10 @@ Expr *BasicBlock::SUBSCRIPT(Expr *base, Expr *index) return e; } -Expr *BasicBlock::MEMBER(Expr *base, const QString *name) -{ - Member*e = function->New<Member>(); - e->init(base, name); - return e; -} - -Expr *BasicBlock::QML_CONTEXT_MEMBER(Expr *base, const QString *id, int memberIndex) +Expr *BasicBlock::MEMBER(Expr *base, const QString *name, QQmlPropertyData *property) { Member*e = function->New<Member>(); - e->initQmlContextMember(base, id, memberIndex); - return e; -} - -Expr *BasicBlock::QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property) -{ - Member*e = function->New<Member>(); - e->initMetaProperty(base, id, property); + e->init(base, name, property); return e; } @@ -1032,14 +1033,7 @@ void CloneExpr::visitSubscript(Subscript *e) void CloneExpr::visitMember(Member *e) { - if (e->type == Member::MemberByName) - cloned = block->MEMBER(clone(e->base), e->name); - else if (e->type == Member::MemberOfQmlContext) - cloned = block->QML_CONTEXT_MEMBER(clone(e->base), e->name, e->memberIndex); - else if (e->type == Member::MemberOfQObject) - cloned = block->QML_QOBJECT_PROPERTY(clone(e->base), e->name, e->property); - else - Q_ASSERT(!"Unimplemented!"); + cloned = block->MEMBER(clone(e->base), e->name, e->property); } } // end of namespace IR diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h index 9a1bd87a1d..aa85c4cf3c 100644 --- a/src/qml/compiler/qv4jsir_p.h +++ b/src/qml/compiler/qv4jsir_p.h @@ -55,6 +55,7 @@ #include "private/qv4global_p.h" #include <private/qqmljsmemorypool_p.h> #include <private/qqmljsastfwd_p.h> +#include <private/qflagpointer_p.h> #include <QtCore/QVector> #include <QtCore/QString> @@ -73,6 +74,8 @@ QT_BEGIN_NAMESPACE class QTextStream; class QQmlType; class QQmlPropertyData; +class QQmlPropertyCache; +class QQmlEnginePrivate; namespace QV4 { struct ExecutionContext; @@ -122,6 +125,14 @@ struct CJump; struct Ret; struct Phi; +// Flag pointer: +// * The first flag indicates whether the meta object is final. +// If final, then none of its properties themselves need to +// be final when considering for lookups in QML. +// * The second flag indicates whether enums should be included +// in the lookup of properties or not. The default is false. +typedef QFlagPointer<QQmlPropertyCache> IRMetaObject; + enum AluOp { OpInvalid = 0, @@ -181,7 +192,8 @@ enum Type { NumberType = SInt32Type | UInt32Type | DoubleType, StringType = 1 << 7, - VarType = 1 << 8 + QObjectType = 1 << 8, + VarType = 1 << 9 }; inline bool strictlyEqualTypes(Type t1, Type t2) @@ -218,6 +230,22 @@ struct StmtVisitor { virtual void visitPhi(Phi *) = 0; }; + +struct MemberExpressionResolver +{ + typedef Type (*ResolveFunction)(QQmlEnginePrivate *engine, MemberExpressionResolver *resolver, Member *member); + + MemberExpressionResolver() + : resolveMember(0), data(0), flags(0) {} + + bool isValid() const { return !!resolveMember; } + void clear() { *this = MemberExpressionResolver(); } + + ResolveFunction resolveMember; + void *data; // Could be pointer to meta object, QQmlTypeNameCache, etc. - depends on resolveMember implementation + int flags; +}; + struct Expr { Type type; @@ -325,7 +353,7 @@ struct Name: Expr { builtin_define_object_literal, builtin_setup_argument_object, builtin_convert_this_to_object, - builtin_qml_id_scope, + builtin_qml_id_array, builtin_qml_imported_scripts_object, builtin_qml_context_object, builtin_qml_scope_object @@ -333,7 +361,9 @@ struct Name: Expr { const QString *id; Builtin builtin; - bool global; + bool global : 1; + bool qmlSingleton : 1; + bool freeOfSideEffects : 1; quint32 line; quint32 column; @@ -360,9 +390,12 @@ struct Temp: Expr { }; unsigned index; - unsigned scope : 28; // how many scopes outside the current one? + unsigned scope : 27; // how many scopes outside the current one? unsigned kind : 3; unsigned isArgumentsOrEval : 1; + unsigned isReadOnly : 1; + // Used when temp is used as base in member expression + MemberExpressionResolver memberResolver; void init(unsigned kind, unsigned index, unsigned scope) { @@ -374,10 +407,11 @@ struct Temp: Expr { this->index = index; this->scope = scope; this->isArgumentsOrEval = false; + this->isReadOnly = false; } virtual void accept(ExprVisitor *v) { v->visitTemp(this); } - virtual bool isLValue() { return true; } + virtual bool isLValue() { return !isReadOnly; } virtual Temp *asTemp() { return this; } virtual void dump(QTextStream &out) const; @@ -518,47 +552,22 @@ struct Subscript: Expr { }; struct Member: Expr { - enum MemberType { - MemberByName, - // QML extensions - MemberOfQmlContext, // lookup in context's id values - MemberOfQObject - }; - - MemberType type; Expr *base; const QString *name; - int memberIndex; // used if type == MemberOfQmlContext QQmlPropertyData *property; + int enumValue; + bool memberIsEnum; - void init(Expr *base, const QString *name) + void init(Expr *base, const QString *name, QQmlPropertyData *property = 0) { - this->type = MemberByName; - this->base = base; - this->name = name; - this->memberIndex = -1; - this->property = 0; - } - - void initQmlContextMember(Expr *base, const QString *name, int memberIndex) - { - this->type = MemberOfQmlContext; - this->base = base; - this->name = name; - this->memberIndex = memberIndex; - this->property = 0; - } - - void initMetaProperty(Expr *base, const QString *name, QQmlPropertyData *property) - { - this->type = MemberOfQObject; this->base = base; this->name = name; this->property = property; + this->memberIsEnum = false; } virtual void accept(ExprVisitor *v) { v->visitMember(this); } - virtual bool isLValue() { return type != MemberOfQmlContext; } + virtual bool isLValue() { return true; } virtual Member *asMember() { return this; } virtual void dump(QTextStream &out) const; @@ -860,9 +869,7 @@ struct BasicBlock { Expr *CALL(Expr *base, ExprList *args = 0); Expr *NEW(Expr *base, ExprList *args = 0); Expr *SUBSCRIPT(Expr *base, Expr *index); - Expr *MEMBER(Expr *base, const QString *name); - Expr *QML_CONTEXT_MEMBER(Expr *base, const QString *id, int memberIndex); - Expr *QML_QOBJECT_PROPERTY(Expr *base, const QString *id, QQmlPropertyData *property); + Expr *MEMBER(Expr *base, const QString *name, QQmlPropertyData *property = 0); Stmt *EXP(Expr *expr); @@ -927,6 +934,8 @@ public: newName->id = n->id; newName->builtin = n->builtin; newName->global = n->global; + newName->qmlSingleton = n->qmlSingleton; + newName->freeOfSideEffects = n->freeOfSideEffects; newName->line = n->line; newName->column = n->column; return newName; @@ -937,6 +946,7 @@ public: Temp *newTemp = f->New<Temp>(); newTemp->init(t->kind, t->index, t->scope); newTemp->type = t->type; + newTemp->memberResolver = t->memberResolver; return newTemp; } diff --git a/src/qml/compiler/qv4regalloc.cpp b/src/qml/compiler/qv4regalloc.cpp index a6e66d2722..5d1dc14500 100644 --- a/src/qml/compiler/qv4regalloc.cpp +++ b/src/qml/compiler/qv4regalloc.cpp @@ -338,9 +338,8 @@ protected: // IRDecoder addDef(temp); } - virtual void loadQmlIdObject(int id, V4IR::Temp *temp) + virtual void loadQmlIdArray(V4IR::Temp *temp) { - Q_UNUSED(id); addDef(temp); addCall(); } @@ -365,6 +364,14 @@ protected: // IRDecoder addCall(); } + virtual void loadQmlSingleton(const QString &/*name*/, Temp *temp) + { + Q_UNUSED(temp); + + addDef(temp); + addCall(); + } + virtual void loadConst(V4IR::Const *sourceConst, V4IR::Temp *targetTemp) { Q_UNUSED(sourceConst); @@ -589,6 +596,7 @@ private: Q_ASSERT(!_defs.contains(*t)); bool canHaveReg = true; switch (t->type) { + case QObjectType: case VarType: case StringType: case UndefinedType: diff --git a/src/qml/compiler/qv4ssa.cpp b/src/qml/compiler/qv4ssa.cpp index 6b1169d30a..f9acf99a65 100644 --- a/src/qml/compiler/qv4ssa.cpp +++ b/src/qml/compiler/qv4ssa.cpp @@ -52,6 +52,7 @@ #include <qv4runtime_p.h> #include <qv4context_p.h> #include <private/qqmlpropertycache_p.h> +#include <private/qqmlengine_p.h> #include <cmath> #include <iostream> #include <cassert> @@ -1188,6 +1189,8 @@ protected: virtual void visitName(Name *e) { + if (e->freeOfSideEffects) + return; // TODO: maybe we can distinguish between built-ins of which we know that they do not have // a side-effect. if (e->builtin == Name::builtin_invalid || (e->id && *e->id != QStringLiteral("this"))) @@ -1209,6 +1212,7 @@ protected: e->expr->accept(this); switch (e->expr->type) { + case QObjectType: case StringType: case VarType: markAsSideEffect(); @@ -1227,7 +1231,7 @@ protected: case OpNot: case OpIncrement: case OpDecrement: - if (e->expr->type == VarType || e->expr->type == StringType) + if (e->expr->type == VarType || e->expr->type == StringType || e->expr->type == QObjectType) markAsSideEffect(); break; @@ -1243,8 +1247,8 @@ protected: _sideEffect = checkForSideEffects(e->left); _sideEffect |= checkForSideEffects(e->right); - if (e->left->type == VarType || e->left->type == StringType - || e->right->type == VarType || e->right->type == StringType) + if (e->left->type == VarType || e->left->type == StringType || e->left->type == QObjectType + || e->right->type == VarType || e->right->type == StringType || e->right->type == QObjectType) markAsSideEffect(); } @@ -1275,22 +1279,42 @@ protected: }; class TypeInference: public StmtVisitor, public ExprVisitor { + struct DiscoveredType { + int type; + MemberExpressionResolver memberResolver; + + DiscoveredType() : type(UnknownType) {} + DiscoveredType(Type t) : type(t) { Q_ASSERT(type != QObjectType); } + explicit DiscoveredType(int t) : type(t) { Q_ASSERT(type != QObjectType); } + explicit DiscoveredType(MemberExpressionResolver memberResolver) : type(QObjectType), memberResolver(memberResolver) {} + + bool test(Type t) const { return type & t; } + bool isNumber() const { return (type & NumberType) && !(type & ~NumberType); } + + bool operator!=(Type other) const { return type != other; } + bool operator==(Type other) const { return type == other; } + bool operator==(const DiscoveredType &other) const { return type == other.type; } + bool operator!=(const DiscoveredType &other) const { return type != other.type; } + }; + + QQmlEnginePrivate *qmlEngine; bool _variablesCanEscape; const DefUsesCalculator &_defUses; - QHash<Temp, int> _tempTypes; + QHash<Temp, DiscoveredType> _tempTypes; QSet<Stmt *> _worklist; struct TypingResult { - int type; + DiscoveredType type; bool fullyTyped; - TypingResult(int type, bool fullyTyped): type(type), fullyTyped(fullyTyped) {} - explicit TypingResult(int type = UnknownType): type(type), fullyTyped(type != UnknownType) {} + TypingResult(const DiscoveredType &type = DiscoveredType()) : type(type), fullyTyped(type.type != UnknownType) {} + explicit TypingResult(MemberExpressionResolver memberResolver): type(memberResolver), fullyTyped(true) {} }; TypingResult _ty; public: - TypeInference(const DefUsesCalculator &defUses) - : _defUses(defUses) + TypeInference(QQmlEnginePrivate *qmlEngine, const DefUsesCalculator &defUses) + : qmlEngine(qmlEngine) + , _defUses(defUses) , _ty(UnknownType) {} @@ -1336,7 +1360,7 @@ private: class PropagateTempTypes: public StmtVisitor, ExprVisitor { public: - PropagateTempTypes(const QHash<Temp, int> &tempTypes) + PropagateTempTypes(const QHash<Temp, DiscoveredType> &tempTypes) : _tempTypes(tempTypes) {} @@ -1352,7 +1376,11 @@ private: virtual void visitString(String *) {} virtual void visitRegExp(RegExp *) {} virtual void visitName(Name *) {} - virtual void visitTemp(Temp *e) { e->type = (Type) _tempTypes[*e]; } + virtual void visitTemp(Temp *e) { + DiscoveredType t = _tempTypes[*e]; + e->type = (Type) t.type; + e->memberResolver = t.memberResolver; + } virtual void visitClosure(Closure *) {} virtual void visitConvert(Convert *e) { e->expr->accept(this); } virtual void visitUnop(Unop *e) { e->expr->accept(this); } @@ -1393,7 +1421,7 @@ private: } private: - QHash<Temp, int> _tempTypes; + QHash<Temp, DiscoveredType> _tempTypes; }; private: @@ -1429,13 +1457,13 @@ private: } } - void setType(Expr *e, int ty) { + void setType(Expr *e, DiscoveredType ty) { if (Temp *t = e->asTemp()) { #if defined(SHOW_SSA) qout<<"Setting type for "<< (t->scope?"scoped temp ":"temp ") <<t->index<< " to "<<typeName(Type(ty)) << " (" << ty << ")" << endl; #endif if (isAlwaysAnObject(t)) - ty = VarType; + ty = DiscoveredType(VarType); if (_tempTypes[*t] != ty) { _tempTypes[*t] = ty; @@ -1450,7 +1478,7 @@ private: _worklist += QSet<Stmt *>::fromList(_defUses.uses(*t)); } } else { - e->type = (Type) ty; + e->type = (Type) ty.type; } } @@ -1472,8 +1500,10 @@ protected: virtual void visitTemp(Temp *e) { if (isAlwaysAnObject(e)) _ty = TypingResult(VarType); + else if (e->memberResolver.isValid()) + _ty = TypingResult(e->memberResolver); else - _ty = TypingResult(_tempTypes.value(*e, UnknownType)); + _ty = TypingResult(_tempTypes.value(*e)); setType(e, _ty.type); } virtual void visitClosure(Closure *) { _ty = TypingResult(VarType); } @@ -1505,9 +1535,9 @@ protected: switch (e->op) { case OpAdd: - if (leftTy.type & VarType || rightTy.type & VarType) + if (leftTy.type.test(VarType) || leftTy.type.test(QObjectType) || rightTy.type.test(VarType) || rightTy.type.test(QObjectType)) _ty.type = VarType; - else if (leftTy.type & StringType || rightTy.type & StringType) + else if (leftTy.type.test(StringType) || rightTy.type.test(StringType)) _ty.type = StringType; else if (leftTy.type != UnknownType && rightTy.type != UnknownType) _ty.type = DoubleType; @@ -1574,14 +1604,13 @@ protected: } virtual void visitMember(Member *e) { - if (e->type == Member::MemberOfQObject - && !e->property->isEnum() // Enums need to go through run-time getters/setters to ensure correct string handling. - ) { - _ty = TypingResult(irTypeFromPropertyType(e->property->propType)); - return; - } _ty = run(e->base); - _ty.type = VarType; + + if (_ty.fullyTyped && _ty.type.memberResolver.isValid()) { + MemberExpressionResolver &resolver = _ty.type.memberResolver; + _ty.type.type = resolver.resolveMember(qmlEngine, &resolver, e); + } else + _ty.type = VarType; } virtual void visitExp(Exp *s) { _ty = run(s->expr); } @@ -1611,11 +1640,13 @@ protected: _ty.fullyTyped = false; break; } - _ty.type |= ty.type; + _ty.type.type |= ty.type.type; _ty.fullyTyped &= ty.fullyTyped; + if (_ty.type.test(QObjectType)) + _ty.type.memberResolver.clear(); // ### TODO: find common ancestor meta-object } - switch (_ty.type) { + switch (_ty.type.type) { case UnknownType: case UndefinedType: case NullType: @@ -1624,13 +1655,14 @@ protected: case UInt32Type: case DoubleType: case StringType: + case QObjectType: case VarType: // The type is not a combination of two or more types, so we're done. break; default: // There are multiple types involved, so: - if ((_ty.type & NumberType) && !(_ty.type & ~NumberType)) + if (_ty.type.isNumber()) // The type is any combination of double/int32/uint32, but nothing else. So we can // type it as double. _ty.type = DoubleType; @@ -1641,18 +1673,6 @@ protected: setType(s->targetTemp, _ty.type); } - - static int irTypeFromPropertyType(int propType) - { - switch (propType) { - case QMetaType::Bool: return BoolType; - case QMetaType::Int: return SInt32Type; - case QMetaType::Double: return DoubleType; - case QMetaType::QString: return StringType; - default: break; - } - return VarType; - } }; void convertConst(Const *c, Type targetType) @@ -1889,7 +1909,7 @@ protected: // when writing undefined to them, and an exception is thrown when they're missing // a reset function. const Member *targetMember = s->target->asMember(); - const bool inhibitConversion = targetMember && targetMember->type == Member::MemberOfQObject && targetMember->property; + const bool inhibitConversion = targetMember && targetMember->property; run(s->source, s->target->type, !inhibitConversion); } @@ -2356,10 +2376,10 @@ bool tryOptimizingComparison(Expr *&expr) if (!b) return false; Const *leftConst = b->left->asConst(); - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType) + if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) return false; Const *rightConst = b->right->asConst(); - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType) + if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) return false; QV4::Primitive l = convertToValue(leftConst); @@ -2494,6 +2514,17 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) } continue; } + if (Member *potentialEnumMember = m->source->asMember()) { + if (potentialEnumMember->memberIsEnum) { + Const *c = function->New<Const>(); + c->init(SInt32Type, potentialEnumMember->enumValue); + W += replaceUses(targetTemp, c); + defUses.removeDef(*targetTemp); + *ref[s] = 0; + defUses.removeUse(s, *potentialEnumMember->base->asTemp()); + continue; + } + } // copy propagation: if (Temp *sourceTemp = unescapableTemp(m->source, variablesCanEscape)) { @@ -2570,10 +2601,10 @@ void optimizeSSA(Function *function, DefUsesCalculator &defUses) // TODO: If the result of the move is only used in one single cjump, then // inline the binop into the cjump. Const *leftConst = binop->left->asConst(); - if (!leftConst || leftConst->type == StringType || leftConst->type == VarType) + if (!leftConst || leftConst->type == StringType || leftConst->type == VarType || leftConst->type == QObjectType) continue; Const *rightConst = binop->right->asConst(); - if (!rightConst || rightConst->type == StringType || rightConst->type == VarType) + if (!rightConst || rightConst->type == StringType || rightConst->type == VarType || rightConst->type == QObjectType) continue; QV4::Primitive lc = convertToValue(leftConst); @@ -2957,7 +2988,7 @@ bool LifeTimeInterval::lessThanForTemp(const LifeTimeInterval &r1, const LifeTim return r1.temp() < r2.temp(); } -void Optimizer::run() +void Optimizer::run(QQmlEnginePrivate *qmlEngine) { #if defined(SHOW_SSA) qout << "##### NOW IN FUNCTION " << (function->name ? qPrintable(*function->name) : "anonymous!") @@ -2999,7 +3030,7 @@ void Optimizer::run() // showMeTheCode(function); // qout << "Running type inference..." << endl; - TypeInference(defUses).run(function); + TypeInference(qmlEngine, defUses).run(function); // showMeTheCode(function); // qout << "Doing type propagation..." << endl; diff --git a/src/qml/compiler/qv4ssa_p.h b/src/qml/compiler/qv4ssa_p.h index aa713d3fec..dcbc83ae65 100644 --- a/src/qml/compiler/qv4ssa_p.h +++ b/src/qml/compiler/qv4ssa_p.h @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE class QTextStream; +class QQmlEnginePrivate; namespace QQmlJS { namespace V4IR { @@ -129,7 +130,7 @@ public: , inSSA(false) {} - void run(); + void run(QQmlEnginePrivate *qmlEngine); void convertOutOfSSA(); bool isInSSA() const diff --git a/src/qml/jsruntime/qv4argumentsobject.cpp b/src/qml/jsruntime/qv4argumentsobject.cpp index ed010b1230..749509c353 100644 --- a/src/qml/jsruntime/qv4argumentsobject.cpp +++ b/src/qml/jsruntime/qv4argumentsobject.cpp @@ -47,10 +47,11 @@ using namespace QV4; DEFINE_MANAGED_VTABLE(ArgumentsObject); ArgumentsObject::ArgumentsObject(CallContext *context) - : Object(context->engine), context(context) + : Object(context->engine), context(context), fullyCreated(false) { vtbl = &static_vtbl; type = Type_ArgumentsObject; + flags &= ~SimpleArray; ExecutionEngine *v4 = context->engine; Scope scope(v4); @@ -60,8 +61,8 @@ ArgumentsObject::ArgumentsObject(CallContext *context) internalClass = v4->strictArgumentsObjectClass; Property pd = Property::fromAccessor(v4->thrower, v4->thrower); - assert(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); - assert(CallerPropertyIndex == internalClass->find(context->engine->id_caller)); + Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); + Q_ASSERT(CallerPropertyIndex == internalClass->find(context->engine->id_caller)); memberData[CalleePropertyIndex] = pd; memberData[CallerPropertyIndex] = pd; @@ -69,27 +70,12 @@ ArgumentsObject::ArgumentsObject(CallContext *context) for (int i = 0; i < context->callData->argc; ++i) arrayData[i].value = context->callData->args[i]; arrayDataLen = context->callData->argc; + fullyCreated = true; } else { internalClass = engine()->argumentsObjectClass; Q_ASSERT(CalleePropertyIndex == internalClass->find(context->engine->id_callee)); memberData[CalleePropertyIndex].value = context->function->asReturnedValue(); isNonStrictArgumentsObject = true; - - uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); - uint argCount = qMin(context->realArgumentCount, context->callData->argc); - arrayReserve(argCount); - ensureArrayAttributes(); - context->engine->requireArgumentsAccessors(numAccessors); - for (uint i = 0; i < (uint)numAccessors; ++i) { - mappedArguments.append(context->callData->args[i]); - arrayData[i] = context->engine->argumentsAccessors.at(i); - arrayAttributes[i] = Attr_Accessor; - } - for (uint i = numAccessors; i < argCount; ++i) { - arrayData[i] = Property::fromValue(context->callData->args[i]); - arrayAttributes[i] = Attr_Data; - } - arrayDataLen = argCount; } Q_ASSERT(LengthPropertyIndex == internalClass->find(context->engine->id_length)); Property *lp = memberData + ArrayObject::LengthPropertyIndex; @@ -101,8 +87,34 @@ void ArgumentsObject::destroy(Managed *that) static_cast<ArgumentsObject *>(that)->~ArgumentsObject(); } +void ArgumentsObject::fullyCreate() +{ + if (fullyCreated) + return; + + uint numAccessors = qMin((int)context->function->formalParameterCount, context->realArgumentCount); + uint argCount = qMin(context->realArgumentCount, context->callData->argc); + arrayReserve(argCount); + ensureArrayAttributes(); + context->engine->requireArgumentsAccessors(numAccessors); + for (uint i = 0; i < (uint)numAccessors; ++i) { + mappedArguments.append(context->callData->args[i]); + arrayData[i] = context->engine->argumentsAccessors.at(i); + arrayAttributes[i] = Attr_Accessor; + } + for (uint i = numAccessors; i < argCount; ++i) { + arrayData[i] = Property::fromValue(context->callData->args[i]); + arrayAttributes[i] = Attr_Data; + } + arrayDataLen = argCount; + + fullyCreated = true; +} + bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs) { + fullyCreate(); + Scope scope(ctx); uint pidx = propertyIndexFromArrayIndex(index); Property *pd = arrayData + pidx; @@ -143,6 +155,57 @@ bool ArgumentsObject::defineOwnProperty(ExecutionContext *ctx, uint index, const return result; } +ReturnedValue ArgumentsObject::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + if (args->fullyCreated) + return Object::getIndexed(m, index, hasProperty); + + if (index < static_cast<uint>(args->context->callData->argc)) { + if (hasProperty) + *hasProperty = true; + return args->context->callData->args[index].asReturnedValue(); + } + return Encode::undefined(); +} + +void ArgumentsObject::putIndexed(Managed *m, uint index, const ValueRef 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; + } + + args->context->callData->args[index] = value; +} + +bool ArgumentsObject::deleteIndexedProperty(Managed *m, uint index) +{ + ArgumentsObject *args = static_cast<ArgumentsObject *>(m); + if (!args->fullyCreated) + args->fullyCreate(); + return Object::deleteIndexedProperty(m, index); +} + +PropertyAttributes ArgumentsObject::queryIndexed(const Managed *m, uint index) +{ + const ArgumentsObject *args = static_cast<const ArgumentsObject *>(m); + if (args->fullyCreated) + return Object::queryIndexed(m, index); + + uint numAccessors = qMin((int)args->context->function->formalParameterCount, args->context->realArgumentCount); + uint argCount = qMin(args->context->realArgumentCount, args->context->callData->argc); + if (index >= argCount) + return PropertyAttributes(); + if (index >= numAccessors) + return Attr_Data; + return Attr_Accessor; +} + DEFINE_MANAGED_VTABLE(ArgumentsGetterFunction); ReturnedValue ArgumentsGetterFunction::call(Managed *getter, CallData *callData) @@ -177,7 +240,7 @@ ReturnedValue ArgumentsSetterFunction::call(Managed *setter, CallData *callData) void ArgumentsObject::markObjects(Managed *that, ExecutionEngine *e) { ArgumentsObject *o = static_cast<ArgumentsObject *>(that); - o->context->mark(); + o->context->mark(e); for (int i = 0; i < o->mappedArguments.size(); ++i) o->mappedArguments.at(i).mark(e); diff --git a/src/qml/jsruntime/qv4argumentsobject_p.h b/src/qml/jsruntime/qv4argumentsobject_p.h index 7a5b0817a3..7c58c48bcc 100644 --- a/src/qml/jsruntime/qv4argumentsobject_p.h +++ b/src/qml/jsruntime/qv4argumentsobject_p.h @@ -74,20 +74,26 @@ struct ArgumentsSetterFunction: FunctionObject struct ArgumentsObject: Object { Q_MANAGED CallContext *context; + bool fullyCreated; QVector<SafeValue> mappedArguments; ArgumentsObject(CallContext *context); ~ArgumentsObject() {} + enum { LengthPropertyIndex = 0, CalleePropertyIndex = 1, CallerPropertyIndex = 2 }; bool defineOwnProperty(ExecutionContext *ctx, uint index, const Property &desc, PropertyAttributes attrs); - + static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static void putIndexed(Managed *m, uint index, const ValueRef value); + static bool deleteIndexedProperty(Managed *m, uint index); + static PropertyAttributes queryIndexed(const Managed *m, uint index); static void markObjects(Managed *that, ExecutionEngine *e); -protected: static void destroy(Managed *); + + void fullyCreate(); }; } diff --git a/src/qml/jsruntime/qv4context.cpp b/src/qml/jsruntime/qv4context.cpp index 97247ad368..372c9ce54b 100644 --- a/src/qml/jsruntime/qv4context.cpp +++ b/src/qml/jsruntime/qv4context.cpp @@ -51,9 +51,33 @@ using namespace QV4; +const ManagedVTable ExecutionContext::static_vtbl = +{ + call, + construct, + markObjects, + destroy, + 0 /*collectDeletables*/, + hasInstance, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + isEqualTo, + 0, + "ExecutionContext", +}; + CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData *callData) { - CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocContext(requiredMemoryForExecutionContect(function, callData->argc))); + CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocManaged(requiredMemoryForExecutionContect(function, callData->argc))); + c->init(); engine->current = c; @@ -63,7 +87,6 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData c->realArgumentCount = callData->argc; c->strictMode = function->strictMode; - c->marked = false; c->outer = function->scope; #ifndef QT_NO_DEBUG assert(c->outer->next != (ExecutionContext *)0x1); @@ -92,7 +115,7 @@ CallContext *ExecutionContext::newCallContext(FunctionObject *function, CallData WithContext *ExecutionContext::newWithContext(ObjectRef with) { - WithContext *w = static_cast<WithContext *>(engine->memoryManager->allocContext(sizeof(WithContext))); + WithContext *w = new (engine->memoryManager) WithContext; engine->current = w; w->initWithContext(this, with); return w; @@ -100,7 +123,7 @@ WithContext *ExecutionContext::newWithContext(ObjectRef with) CatchContext *ExecutionContext::newCatchContext(const StringRef exceptionVarName, const ValueRef exceptionValue) { - CatchContext *c = static_cast<CatchContext *>(engine->memoryManager->allocContext(sizeof(CatchContext))); + CatchContext *c = new (engine->memoryManager) CatchContext; engine->current = c; c->initCatchContext(this, exceptionVarName, exceptionValue); return c; @@ -108,7 +131,8 @@ CatchContext *ExecutionContext::newCatchContext(const StringRef exceptionVarName CallContext *ExecutionContext::newQmlContext(FunctionObject *f, ObjectRef qml) { - CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocContext(requiredMemoryForExecutionContect(f, 0))); + CallContext *c = static_cast<CallContext *>(engine->memoryManager->allocManaged(requiredMemoryForExecutionContect(f, 0))); + c->init(); engine->current = c; c->initQmlContext(this, qml, f); @@ -184,6 +208,7 @@ void GlobalContext::initGlobalContext(ExecutionEngine *eng) callData->tag = QV4::Value::_Integer_Type; callData->argc = 0; callData->thisObject = eng->globalObject; + callData->args[0] = Encode::undefined(); global = 0; } @@ -222,7 +247,6 @@ void CallContext::initQmlContext(ExecutionContext *parentContext, ObjectRef qml, this->callData->thisObject = Primitive::undefinedValue(); strictMode = true; - marked = false; this->outer = function->scope; #ifndef QT_NO_DEBUG assert(outer->next != (ExecutionContext *)0x1); @@ -285,36 +309,34 @@ bool CallContext::needsOwnArguments() const return function->needsActivation || callData->argc < static_cast<int>(function->formalParameterCount); } -void ExecutionContext::mark() +void ExecutionContext::markObjects(Managed *m, ExecutionEngine *engine) { - if (marked) - return; - marked = true; + ExecutionContext *ctx = static_cast<ExecutionContext *>(m); - if (outer) - outer->mark(); + if (ctx->outer) + ctx->outer->mark(engine); // ### shouldn't need these 3 lines - callData->thisObject.mark(engine); - for (int arg = 0; arg < callData->argc; ++arg) - callData->args[arg].mark(engine); + ctx->callData->thisObject.mark(engine); + for (int arg = 0; arg < ctx->callData->argc; ++arg) + ctx->callData->args[arg].mark(engine); - if (type >= Type_CallContext) { - QV4::CallContext *c = static_cast<CallContext *>(this); + if (ctx->type >= Type_CallContext) { + QV4::CallContext *c = static_cast<CallContext *>(ctx); for (unsigned local = 0, lastLocal = c->variableCount(); local < lastLocal; ++local) c->locals[local].mark(engine); if (c->activation) c->activation->mark(engine); c->function->mark(engine); - } else if (type == Type_WithContext) { - WithContext *w = static_cast<WithContext *>(this); + } else if (ctx->type == Type_WithContext) { + WithContext *w = static_cast<WithContext *>(ctx); w->withObject->mark(engine); - } else if (type == Type_CatchContext) { - CatchContext *c = static_cast<CatchContext *>(this); + } else if (ctx->type == Type_CatchContext) { + CatchContext *c = static_cast<CatchContext *>(ctx); c->exceptionVarName->mark(engine); c->exceptionValue.mark(engine); - } else if (type == Type_GlobalContext) { - GlobalContext *g = static_cast<GlobalContext *>(this); + } else if (ctx->type == Type_GlobalContext) { + GlobalContext *g = static_cast<GlobalContext *>(ctx); g->global->mark(engine); } } diff --git a/src/qml/jsruntime/qv4context_p.h b/src/qml/jsruntime/qv4context_p.h index ccb5cf98f8..e7f5ee9a9e 100644 --- a/src/qml/jsruntime/qv4context_p.h +++ b/src/qml/jsruntime/qv4context_p.h @@ -66,8 +66,21 @@ struct CallContext; struct CatchContext; struct WithContext; -struct Q_QML_EXPORT ExecutionContext +struct Q_QML_EXPORT ExecutionContext : public Managed { + Q_MANAGED + ExecutionContext() + : Managed(0) { + vtbl = &static_vtbl; + } + void init() { + _data = 0; + internalClass = 0; + inUse = 1; + extensible = 1; + vtbl = &static_vtbl; + } + enum Type { Type_GlobalContext = 0x1, Type_CatchContext = 0x2, @@ -79,7 +92,6 @@ struct Q_QML_EXPORT ExecutionContext Type type; bool strictMode; - bool marked; CallData *callData; @@ -98,13 +110,12 @@ struct Q_QML_EXPORT ExecutionContext EvalCode *currentEvalCode; const uchar **interpreterInstructionPointer; - char *jitInstructionPointer; + int lineNumber; void initBaseContext(Type type, ExecutionEngine *engine, ExecutionContext *parentContext) { this->type = type; strictMode = false; - marked = false; this->engine = engine; parent = parentContext; outer = 0; @@ -112,7 +123,7 @@ struct Q_QML_EXPORT ExecutionContext compilationUnit = 0; currentEvalCode = 0; interpreterInstructionPointer = 0; - jitInstructionPointer = 0; + lineNumber = -1; } CallContext *newCallContext(FunctionObject *f, CallData *callData); @@ -130,11 +141,11 @@ struct Q_QML_EXPORT ExecutionContext ReturnedValue throwError(const QV4::ValueRef value); ReturnedValue throwError(const QString &message); ReturnedValue throwSyntaxError(const QString &message); - ReturnedValue throwSyntaxError(const QString &message, const QString &fileName, int line, int column); + ReturnedValue throwSyntaxError(const QString &message, const QString &fileName, int lineNumber, int column); ReturnedValue throwTypeError(); ReturnedValue throwTypeError(const QString &message); ReturnedValue throwReferenceError(const ValueRef value); - ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int line, int column); + ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column); ReturnedValue throwRangeError(const ValueRef value); ReturnedValue throwRangeError(const QString &message); ReturnedValue throwURIError(const ValueRef msg); @@ -148,10 +159,10 @@ struct Q_QML_EXPORT ExecutionContext // Can only be called from within catch(...), rethrows if no JS exception. ReturnedValue catchException(StackTrace *trace = 0); - void mark(); - inline CallContext *asCallContext(); inline const CallContext *asCallContext() const; + + static void markObjects(Managed *m, ExecutionEngine *e); }; struct CallContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index 8cd059dd2b..f5a515a0ae 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -389,7 +389,8 @@ void ExecutionEngine::enableDebugger() void ExecutionEngine::initRootContext() { - rootContext = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext) + sizeof(CallData))); + rootContext = static_cast<GlobalContext *>(memoryManager->allocManaged(sizeof(GlobalContext) + sizeof(CallData))); + rootContext->init(); current = rootContext; current->parent = 0; rootContext->initGlobalContext(this); @@ -402,9 +403,9 @@ InternalClass *ExecutionEngine::newClass(const InternalClass &other) ExecutionContext *ExecutionEngine::pushGlobalContext() { - GlobalContext *g = static_cast<GlobalContext *>(memoryManager->allocContext(sizeof(GlobalContext))); + GlobalContext *g = new (memoryManager) GlobalContext; ExecutionContext *oldNext = g->next; - *g = *rootContext; + memcpy(g, rootContext, sizeof(GlobalContext)); g->next = oldNext; g->parent = current; current = g; @@ -627,11 +628,12 @@ namespace { void resolve(StackFrame *frame, ExecutionContext *context, Function *function) { qptrdiff offset; - if (context->interpreterInstructionPointer) + if (context->interpreterInstructionPointer) { offset = *context->interpreterInstructionPointer - 1 - function->codeData; - else - offset = context->jitInstructionPointer - (char*)function->codePtr; - frame->line = function->lineNumberForProgramCounter(offset); + frame->line = function->lineNumberForProgramCounter(offset); + } else { + frame->line = context->lineNumber; + } } }; } @@ -754,7 +756,7 @@ void ExecutionEngine::markObjects() ExecutionContext *c = current; while (c) { - c->mark(); + c->mark(this); c = c->parent; } diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index b4972904ee..c37d4f125d 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -388,6 +388,7 @@ struct ExecutionContextSaver inline void Managed::mark(QV4::ExecutionEngine *engine) { + Q_ASSERT(inUse); if (markBit) return; markBit = 1; diff --git a/src/qml/jsruntime/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp index aa1cb89a44..037f06cd35 100644 --- a/src/qml/jsruntime/qv4functionobject.cpp +++ b/src/qml/jsruntime/qv4functionobject.cpp @@ -55,6 +55,7 @@ #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> #include <private/qqmlcontextwrapper_p.h> +#include <private/qqmlengine_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> #include "private/qlocale_tools_p.h" @@ -213,7 +214,7 @@ void FunctionObject::markObjects(Managed *that, ExecutionEngine *e) // formalParameterList[i]->mark(); // for (uint i = 0; i < varCount; ++i) // varList[i]->mark(); - o->scope->mark(); + o->scope->mark(e); if (o->function) o->function->mark(e); @@ -281,7 +282,7 @@ ReturnedValue FunctionCtor::construct(Managed *that, CallData *callData) cg.generateFromFunctionExpression(QString(), function, fe, &module); QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); QV4::Function *vmf = compilationUnit->linkToEngine(v4); @@ -348,7 +349,7 @@ ReturnedValue FunctionPrototype::method_apply(CallContext *ctx) ScopedCallData callData(scope, len); if (len) { - if (arr->protoHasArray() || arr->hasAccessorProperty) { + if (!(arr->flags & SimpleArray) || arr->protoHasArray() || arr->hasAccessorProperty) { for (quint32 i = 0; i < len; ++i) callData->args[i] = arr->getIndexed(i); } else { diff --git a/src/qml/jsruntime/qv4mm.cpp b/src/qml/jsruntime/qv4mm.cpp index 9eb3ae7ec9..9cac0cf90c 100644 --- a/src/qml/jsruntime/qv4mm.cpp +++ b/src/qml/jsruntime/qv4mm.cpp @@ -155,6 +155,19 @@ struct MemoryManager::Data QVector<Chunk> heapChunks; + + struct LargeItem { + LargeItem *next; + void *data; + + Managed *managed() { + return reinterpret_cast<Managed *>(&data); + } + }; + + LargeItem *largeItems; + + // statistics: #ifdef DETAILED_MM_STATS QVector<unsigned> allocSizeCounters; @@ -167,6 +180,7 @@ struct MemoryManager::Data , stackTop(0) , totalItems(0) , totalAlloc(0) + , largeItems(0) { memset(smallItems, 0, sizeof(smallItems)); memset(nChunks, 0, sizeof(nChunks)); @@ -200,7 +214,6 @@ bool operator<(const MemoryManager::Data::Chunk &a, const MemoryManager::Data::C MemoryManager::MemoryManager() : m_d(new Data(true)) - , m_contextList(0) , m_persistentValues(0) , m_weakValues(0) { @@ -258,8 +271,14 @@ Managed *MemoryManager::alloc(std::size_t size) size_t pos = size >> 4; - // fits into a small bucket - Q_ASSERT(size < MemoryManager::Data::MaxItemSize); + // doesn't fit into a small bucket + if (size >= MemoryManager::Data::MaxItemSize) { + // we use malloc for this + MemoryManager::Data::LargeItem *item = static_cast<MemoryManager::Data::LargeItem *>(malloc(size + sizeof(MemoryManager::Data::LargeItem))); + item->next = m_d->largeItems; + m_d->largeItems = item; + return item->managed(); + } Managed *m = m_d->smallItems[pos]; if (m) @@ -447,18 +466,21 @@ void MemoryManager::sweep(bool lastSweep) for (QVector<Data::Chunk>::iterator i = m_d->heapChunks.begin(), ei = m_d->heapChunks.end(); i != ei; ++i) sweep(reinterpret_cast<char*>(i->memory.base()), i->memory.size(), i->chunkSize, &deletable); - ExecutionContext *ctx = m_contextList; - ExecutionContext **n = &m_contextList; - while (ctx) { - ExecutionContext *next = ctx->next; - if (!ctx->marked) { - free(ctx); - *n = next; - } else { - ctx->marked = false; - n = &ctx->next; + Data::LargeItem *i = m_d->largeItems; + Data::LargeItem **last = &m_d->largeItems; + while (i) { + Managed *m = i->managed(); + Q_ASSERT(m->inUse); + if (m->markBit) { + m->markBit = 0; + last = &i->next; + i = i->next; + continue; } - ctx = next; + + *last = i->next; + free(i); + i = *last; } deletable = *firstDeletable; diff --git a/src/qml/jsruntime/qv4mm_p.h b/src/qml/jsruntime/qv4mm_p.h index f3258519de..7d28319536 100644 --- a/src/qml/jsruntime/qv4mm_p.h +++ b/src/qml/jsruntime/qv4mm_p.h @@ -103,8 +103,6 @@ public: return o; } - ExecutionContext *allocContext(uint size); - bool isGCBlocked() const; void setGCBlocked(bool blockGC); void runGC(); @@ -134,21 +132,11 @@ private: protected: QScopedPointer<Data> m_d; - ExecutionContext *m_contextList; public: PersistentValuePrivate *m_persistentValues; PersistentValuePrivate *m_weakValues; }; -inline ExecutionContext *MemoryManager::allocContext(uint size) -{ - ExecutionContext *newContext = (ExecutionContext *)malloc(size); - newContext->next = m_contextList; - m_contextList = newContext; - return newContext; -} - - } QT_END_NAMESPACE diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index cca7d2b26a..e4df95716d 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -1133,7 +1133,7 @@ ReturnedValue Object::arrayIndexOf(const ValueRef v, uint fromIndex, uint endInd Scope scope(engine()); ScopedValue value(scope); - if (!(flags & SimpleArray) || o->protoHasArray() || o->arrayAttributes) { + if (!(o->flags & SimpleArray) || o->protoHasArray()) { // lets be safe and slow for (uint i = fromIndex; i < endIndex; ++i) { bool exists; diff --git a/src/qml/jsruntime/qv4objectiterator.cpp b/src/qml/jsruntime/qv4objectiterator.cpp index 62595b5176..04fa504991 100644 --- a/src/qml/jsruntime/qv4objectiterator.cpp +++ b/src/qml/jsruntime/qv4objectiterator.cpp @@ -42,6 +42,7 @@ #include "qv4object_p.h" #include "qv4stringobject_p.h" #include "qv4identifier_p.h" +#include "qv4argumentsobject_p.h" using namespace QV4; @@ -56,6 +57,11 @@ ObjectIterator::ObjectIterator(SafeObject *scratch1, SafeObject *scratch2, const object = o; current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); + + if (object && object->isNonStrictArgumentsObject) { + Scope scope(object->engine()); + Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); + } } ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) @@ -69,6 +75,11 @@ ObjectIterator::ObjectIterator(Scope &scope, const ObjectRef o, uint flags) object = o; current = o; tmpDynamicProperty.value = Primitive::undefinedValue(); + + if (object && object->isNonStrictArgumentsObject) { + Scope scope(object->engine()); + Scoped<ArgumentsObject> (scope, object->asReturnedValue())->fullyCreate(); + } } Property *ObjectIterator::next(StringRef name, uint *index, PropertyAttributes *attrs) diff --git a/src/qml/jsruntime/qv4objectproto.cpp b/src/qml/jsruntime/qv4objectproto.cpp index 4369267278..f17bd7d5ba 100644 --- a/src/qml/jsruntime/qv4objectproto.cpp +++ b/src/qml/jsruntime/qv4objectproto.cpp @@ -41,6 +41,7 @@ #include "qv4objectproto_p.h" +#include "qv4argumentsobject_p.h" #include "qv4mm_p.h" #include "qv4scopedvalue_p.h" #include <QtCore/qnumeric.h> @@ -156,6 +157,9 @@ ReturnedValue ObjectPrototype::method_getOwnPropertyDescriptor(CallContext *ctx) if (!O) return ctx->throwTypeError(); + if (O->isNonStrictArgumentsObject) + Scoped<ArgumentsObject>(scope, O)->fullyCreate(); + ScopedValue v(scope, ctx->argument(1)); Scoped<String> name(scope, v->toString(ctx)); if (scope.hasException()) @@ -283,6 +287,9 @@ ReturnedValue ObjectPrototype::method_freeze(CallContext *ctx) if (!o) return ctx->throwTypeError(); + if (o->isNonStrictArgumentsObject) + Scoped<ArgumentsObject>(scope, o)->fullyCreate(); + o->extensible = false; o->internalClass = o->internalClass->frozen(); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index a8cabcb374..a5a93e1f84 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -966,10 +966,10 @@ ReturnedValue __qmljs_construct_value(ExecutionContext *context, const ValueRef return f->construct(callData); } -ReturnedValue __qmljs_construct_property(ExecutionContext *context, const ValueRef base, const StringRef name, CallDataRef callData) +ReturnedValue __qmljs_construct_property(ExecutionContext *context, const StringRef name, CallDataRef callData) { Scope scope(context); - ScopedObject thisObject(scope, base->toObject(context)); + ScopedObject thisObject(scope, callData->thisObject.toObject(context)); if (scope.engine->hasException) return Encode::undefined(); @@ -980,6 +980,18 @@ ReturnedValue __qmljs_construct_property(ExecutionContext *context, const ValueR return f->construct(callData); } +ReturnedValue __qmljs_construct_property_lookup(ExecutionContext *context, uint index, CallDataRef callData) +{ + Lookup *l = context->lookups + index; + SafeValue v; + v = l->getter(l, callData->thisObject); + if (!v.isManaged()) + return context->throwTypeError(); + + return v.managed()->construct(callData); +} + + void __qmljs_throw(ExecutionContext *context, const ValueRef value) { if (!value->isEmpty()) @@ -1230,10 +1242,9 @@ ReturnedValue __qmljs_lookup_runtime_regexp(ExecutionContext *ctx, int id) return ctx->compilationUnit->runtimeRegularExpressions[id].asReturnedValue(); } -ReturnedValue __qmljs_get_id_object(NoThrowContext *ctx, int id) +ReturnedValue __qmljs_get_id_array(NoThrowContext *ctx) { - QQmlContextData *context = QmlContextWrapper::callingContext(ctx->engine); - return QObjectWrapper::wrap(ctx->engine, context->idValues[id].data()); + return ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()->idObjectsArray(); } ReturnedValue __qmljs_get_context_object(NoThrowContext *ctx) @@ -1277,6 +1288,11 @@ ReturnedValue __qmljs_get_imported_scripts(NoThrowContext *ctx) return context->importedScripts.value(); } +QV4::ReturnedValue __qmljs_get_qml_singleton(QV4::NoThrowContext *ctx, const QV4::StringRef name) +{ + return ctx->engine->qmlContextObject()->getPointer()->as<QmlContextWrapper>()->qmlSingletonWrapper(name); +} + void __qmljs_builtin_convert_this_to_object(ExecutionContext *ctx) { SafeValue *t = &ctx->callData->thisObject; diff --git a/src/qml/jsruntime/qv4runtime_p.h b/src/qml/jsruntime/qv4runtime_p.h index b5567693e5..da596b180c 100644 --- a/src/qml/jsruntime/qv4runtime_p.h +++ b/src/qml/jsruntime/qv4runtime_p.h @@ -125,7 +125,8 @@ QV4::ReturnedValue __qmljs_call_element(ExecutionContext *context, const ValueRe QV4::ReturnedValue __qmljs_call_value(QV4::ExecutionContext *context, const QV4::ValueRef func, CallDataRef callData); QV4::ReturnedValue __qmljs_construct_activation_property(QV4::ExecutionContext *, const QV4::StringRef name, CallDataRef callData); -QV4::ReturnedValue __qmljs_construct_property(QV4::ExecutionContext *context, const QV4::ValueRef base, const QV4::StringRef name, CallDataRef callData); +QV4::ReturnedValue __qmljs_construct_property(QV4::ExecutionContext *context, const QV4::StringRef name, CallDataRef callData); +QV4::ReturnedValue __qmljs_construct_property_lookup(ExecutionContext *context, uint index, CallDataRef callData); QV4::ReturnedValue __qmljs_construct_value(QV4::ExecutionContext *context, const QV4::ValueRef func, CallDataRef callData); QV4::ReturnedValue __qmljs_builtin_typeof(QV4::ExecutionContext *ctx, const QV4::ValueRef val); @@ -170,12 +171,13 @@ QV4::ReturnedValue __qmljs_construct_global_lookup(QV4::ExecutionContext *contex QV4::ReturnedValue __qmljs_get_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index); void __qmljs_set_element(QV4::ExecutionContext *ctx, const QV4::ValueRef object, const QV4::ValueRef index, const QV4::ValueRef value); -QV4::ReturnedValue __qmljs_get_id_object(NoThrowContext *ctx, int id); +QV4::ReturnedValue __qmljs_get_id_array(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_imported_scripts(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_context_object(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_scope_object(NoThrowContext *ctx); QV4::ReturnedValue __qmljs_get_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex, bool captureRequired); void __qmljs_set_qobject_property(ExecutionContext *ctx, const ValueRef object, int propertyIndex, const ValueRef value); +QV4::ReturnedValue __qmljs_get_qml_singleton(NoThrowContext *ctx, const QV4::StringRef name); // For each QV4::ReturnedValue __qmljs_foreach_iterator_object(QV4::ExecutionContext *ctx, const QV4::ValueRef in); diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp index 25791cff61..c65f1baf2b 100644 --- a/src/qml/jsruntime/qv4script.cpp +++ b/src/qml/jsruntime/qv4script.cpp @@ -51,6 +51,7 @@ #include <private/qqmljslexer_p.h> #include <private/qqmljsparser_p.h> #include <private/qqmljsast_p.h> +#include <private/qqmlengine_p.h> #include <qv4jsir_p.h> #include <qv4codegen_p.h> @@ -62,11 +63,18 @@ using namespace QV4; QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, ObjectRef qml) : FunctionObject(scope, scope->engine->id_eval) , qml(qml) + , qmlContext(0) { + Q_ASSERT(scope->inUse); + vtbl = &static_vtbl; function = f; function->compilationUnit->ref(); needsActivation = function->needsActivation(); + + Scope s(scope); + ScopedValue protectThis(s, this); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(1)); qmlContext = scope->engine->current->newQmlContext(this, qml); @@ -76,10 +84,17 @@ QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, Function *f, Objec QmlBindingWrapper::QmlBindingWrapper(ExecutionContext *scope, ObjectRef qml) : FunctionObject(scope, scope->engine->id_eval) , qml(qml) + , qmlContext(0) { + Q_ASSERT(scope->inUse); + vtbl = &static_vtbl; function = 0; needsActivation = false; + + Scope s(scope); + ScopedValue protectThis(s, this); + defineReadonlyProperty(scope->engine->id_length, Primitive::fromInt32(1)); qmlContext = scope->engine->current->newQmlContext(this, qml); @@ -110,7 +125,8 @@ void QmlBindingWrapper::markObjects(Managed *m, ExecutionEngine *e) if (wrapper->qml) wrapper->qml->mark(e); FunctionObject::markObjects(m, e); - wrapper->qmlContext->mark(); + if (wrapper->qmlContext) + wrapper->qmlContext->mark(e); } DEFINE_MANAGED_VTABLE(QmlBindingWrapper); @@ -214,7 +230,7 @@ void Script::parse() return; QV4::Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &module, &jsGenerator)); + QScopedPointer<EvalInstructionSelection> isel(v4->iselFactory->create(QQmlEnginePrivate::get(v4), v4->executableAllocator, &module, &jsGenerator)); if (inheritContext) isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *compilationUnit = isel->compile(); @@ -340,7 +356,7 @@ CompiledData::CompilationUnit *Script::precompile(ExecutionEngine *engine, const } Compiler::JSUnitGenerator jsGenerator(&module); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(engine->iselFactory->create(engine->executableAllocator, &module, &jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(engine->iselFactory->create(QQmlEnginePrivate::get(engine), engine->executableAllocator, &module, &jsGenerator)); isel->setUseFastLookups(false); return isel->compile(); } diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 72db469ee6..00672fea0f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -503,10 +503,19 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); callData->tag = QV4::Value::Integer_Type; callData->argc = instr.argc; - callData->thisObject = QV4::Primitive::undefinedValue(); - STOREVALUE(instr.result, __qmljs_construct_property(context, VALUEPTR(instr.base), runtimeStrings[instr.name], callData)); + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, __qmljs_construct_property(context, runtimeStrings[instr.name], callData)); MOTH_END_INSTR(CreateProperty) + MOTH_BEGIN_INSTR(ConstructPropertyLookup) + Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); + QV4::CallData *callData = reinterpret_cast<QV4::CallData *>(stack + instr.callData); + callData->tag = QV4::Value::Integer_Type; + callData->argc = instr.argc; + callData->thisObject = VALUE(instr.base); + STOREVALUE(instr.result, __qmljs_construct_property_lookup(context, instr.index, callData)); + MOTH_END_INSTR(ConstructPropertyLookup) + MOTH_BEGIN_INSTR(CreateActivationProperty) TRACE(inline, "property name = %s, args = %d, argc = %d", runtimeStrings[instr.name]->toQString().toUtf8().constData(), instr.args, instr.argc); Q_ASSERT(instr.callData + instr.argc + qOffsetOf(QV4::CallData, args)/sizeof(QV4::SafeValue) <= stackSize); @@ -648,9 +657,9 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = context->callData->thisObject; MOTH_END_INSTR(LoadThis) - MOTH_BEGIN_INSTR(LoadQmlIdObject) - VALUE(instr.result) = __qmljs_get_id_object(static_cast<QV4::NoThrowContext*>(context), instr.id); - MOTH_END_INSTR(LoadQmlIdObject) + MOTH_BEGIN_INSTR(LoadQmlIdArray) + VALUE(instr.result) = __qmljs_get_id_array(static_cast<QV4::NoThrowContext*>(context)); + MOTH_END_INSTR(LoadQmlIdArray) MOTH_BEGIN_INSTR(LoadQmlImportedScripts) VALUE(instr.result) = __qmljs_get_imported_scripts(static_cast<QV4::NoThrowContext*>(context)); @@ -664,6 +673,10 @@ QV4::ReturnedValue VME::run(QV4::ExecutionContext *context, const uchar *code, VALUE(instr.result) = __qmljs_get_scope_object(static_cast<QV4::NoThrowContext*>(context)); MOTH_END_INSTR(LoadScopeObject) + MOTH_BEGIN_INSTR(LoadQmlSingleton) + VALUE(instr.result) = __qmljs_get_qml_singleton(static_cast<QV4::NoThrowContext*>(context), runtimeStrings[instr.name]); + MOTH_END_INSTR(LoadQmlSingleton) + #ifdef MOTH_THREADED_INTERPRETER // nothing to do #else diff --git a/src/qml/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp index 93ec2516c8..54fd002f7b 100644 --- a/src/qml/qml/qqmlcompiler.cpp +++ b/src/qml/qml/qqmlcompiler.cpp @@ -919,7 +919,7 @@ void QQmlCompiler::compileTree(QQmlScript::Object *tree) if (!jsModule->functions.isEmpty()) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(engine); QV4::Compiler::JSUnitGenerator jsUnitGenerator(jsModule.data()); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, jsModule.data(), &jsUnitGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, v4->executableAllocator, jsModule.data(), &jsUnitGenerator)); isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/true); output->compilationUnit = jsUnit; diff --git a/src/qml/qml/qqmlcontextwrapper.cpp b/src/qml/qml/qqmlcontextwrapper.cpp index 406826a6f6..0b3b282061 100644 --- a/src/qml/qml/qqmlcontextwrapper.cpp +++ b/src/qml/qml/qqmlcontextwrapper.cpp @@ -63,7 +63,7 @@ DEFINE_MANAGED_VTABLE(QmlContextWrapper); QmlContextWrapper::QmlContextWrapper(QV8Engine *engine, QQmlContextData *context, QObject *scopeObject, bool ownsContext) : Object(QV8Engine::getV4(engine)), v8(engine), readOnly(true), ownsContext(ownsContext), isNullWrapper(false), - context(context), scopeObject(scopeObject) + context(context), scopeObject(scopeObject), idObjectsWrapper(0) { vtbl = &static_vtbl; } @@ -355,6 +355,14 @@ void QmlContextWrapper::destroy(Managed *that) static_cast<QmlContextWrapper *>(that)->~QmlContextWrapper(); } +void QmlContextWrapper::markObjects(Managed *m, ExecutionEngine *engine) +{ + QmlContextWrapper *This = static_cast<QmlContextWrapper*>(m); + if (This->idObjectsWrapper) + This->idObjectsWrapper->mark(engine); + Object::markObjects(m, engine); +} + void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const CompiledData::Function *compiledFunction) { // Let the caller check and avoid the function call :) @@ -395,4 +403,71 @@ void QmlContextWrapper::registerQmlDependencies(ExecutionEngine *engine, const C } +ReturnedValue QmlContextWrapper::idObjectsArray() +{ + if (!idObjectsWrapper) { + ExecutionEngine *v4 = engine(); + idObjectsWrapper = new (v4->memoryManager) QQmlIdObjectsArray(v4, this); + } + return idObjectsWrapper->asReturnedValue(); +} + +ReturnedValue QmlContextWrapper::qmlSingletonWrapper(const StringRef &name) +{ + if (!context->imports) + return Encode::undefined(); + // Search for attached properties, enums and imported scripts + QQmlTypeNameCache::Result r = context->imports->query(name); + + Q_ASSERT(r.isValid()); + Q_ASSERT(r.type); + Q_ASSERT(r.type->isSingleton()); + + QQmlEngine *e = v8->engine(); + QQmlType::SingletonInstanceInfo *siinfo = r.type->singletonInstanceInfo(); + siinfo->init(e); + + QObject *qobjectSingleton = siinfo->qobjectApi(e); + return QV4::QObjectWrapper::wrap(engine(), qobjectSingleton); +} + +DEFINE_MANAGED_VTABLE(QQmlIdObjectsArray); + +QQmlIdObjectsArray::QQmlIdObjectsArray(ExecutionEngine *engine, QmlContextWrapper *contextWrapper) + : Object(engine) + , contextWrapper(contextWrapper) +{ + vtbl = &static_vtbl; +} + +ReturnedValue QQmlIdObjectsArray::getIndexed(Managed *m, uint index, bool *hasProperty) +{ + QQmlIdObjectsArray *This = static_cast<QQmlIdObjectsArray*>(m); + QQmlContextData *context = This->contextWrapper->getContext(); + if (!context) { + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); + } + if (index >= (uint)context->idValueCount) { + if (hasProperty) + *hasProperty = false; + return Encode::undefined(); + } + + ExecutionEngine *v4 = m->engine(); + QQmlEnginePrivate *ep = v4->v8Engine->engine() ? QQmlEnginePrivate::get(v4->v8Engine->engine()) : 0; + if (ep) + ep->captureProperty(&context->idValues[index].bindings); + + return QObjectWrapper::wrap(This->engine(), context->idValues[index].data()); +} + +void QQmlIdObjectsArray::markObjects(Managed *that, ExecutionEngine *engine) +{ + QQmlIdObjectsArray *This = static_cast<QQmlIdObjectsArray*>(that); + This->contextWrapper->mark(engine); + Object::markObjects(that, engine); +} + QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlcontextwrapper_p.h b/src/qml/qml/qqmlcontextwrapper_p.h index d85f440b15..89ace7090c 100644 --- a/src/qml/qml/qqmlcontextwrapper_p.h +++ b/src/qml/qml/qqmlcontextwrapper_p.h @@ -69,6 +69,8 @@ namespace CompiledData { struct Function; } +struct QQmlIdObjectsArray; + struct Q_QML_EXPORT QmlContextWrapper : Object { Q_MANAGED @@ -90,9 +92,12 @@ struct Q_QML_EXPORT QmlContextWrapper : Object static ReturnedValue get(Managed *m, const StringRef name, bool *hasProperty); static void put(Managed *m, const StringRef name, const ValueRef value); static void destroy(Managed *that); + static void markObjects(Managed *m, ExecutionEngine *engine); static void registerQmlDependencies(ExecutionEngine *context, const CompiledData::Function *compiledFunction); + ReturnedValue idObjectsArray(); + ReturnedValue qmlSingletonWrapper(const StringRef &name); QV8Engine *v8; // ### temporary, remove bool readOnly; @@ -101,6 +106,19 @@ struct Q_QML_EXPORT QmlContextWrapper : Object QQmlGuardedContextData context; QPointer<QObject> scopeObject; +private: + QQmlIdObjectsArray *idObjectsWrapper; +}; + +struct QQmlIdObjectsArray : public Object +{ + Q_MANAGED + QQmlIdObjectsArray(ExecutionEngine *engine, QmlContextWrapper *contextWrapper); + + static ReturnedValue getIndexed(Managed *m, uint index, bool *hasProperty); + static void markObjects(Managed *that, ExecutionEngine *engine); + + QmlContextWrapper *contextWrapper; }; } diff --git a/src/qml/qml/qqmlengine.cpp b/src/qml/qml/qqmlengine.cpp index d082b9a8fd..1eec710c84 100644 --- a/src/qml/qml/qqmlengine.cpp +++ b/src/qml/qml/qqmlengine.cpp @@ -556,7 +556,7 @@ QQmlEnginePrivate::QQmlEnginePrivate(QQmlEngine *e) workerScriptEngine(0), activeVME(0), activeObjectCreator(0), networkAccessManager(0), networkAccessManagerFactory(0), urlInterceptor(0), - scarceResourcesRefCount(0), typeLoader(e), importDatabase(e), uniqueId(1), + scarceResourcesRefCount(0), importDatabase(e), typeLoader(e), uniqueId(1), incubatorCount(0), incubationController(0), mutex(QMutex::Recursive) { useNewCompiler = qmlUseNewCompiler(); diff --git a/src/qml/qml/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h index 5a2d6c4e00..19eb320fbe 100644 --- a/src/qml/qml/qqmlengine_p.h +++ b/src/qml/qml/qqmlengine_p.h @@ -186,8 +186,8 @@ public: void referenceScarceResources(); void dereferenceScarceResources(); - QQmlTypeLoader typeLoader; QQmlImportDatabase importDatabase; + QQmlTypeLoader typeLoader; QString offlineStoragePath; @@ -257,6 +257,7 @@ public: inline static QQmlEnginePrivate *get(QQmlContext *c); inline static QQmlEnginePrivate *get(QQmlContextData *c); inline static QQmlEngine *get(QQmlEnginePrivate *p); + inline static QQmlEnginePrivate *get(QV4::ExecutionEngine *e); static void registerBaseTypes(const char *uri, int versionMajor, int versionMinor); static void registerQtQuick2Types(const char *uri, int versionMajor, int versionMinor); @@ -516,7 +517,17 @@ QQmlEngine *QQmlEnginePrivate::get(QQmlEnginePrivate *p) { Q_ASSERT(p); - return p->q_func(); + return p->q_func(); +} + +QQmlEnginePrivate *QQmlEnginePrivate::get(QV4::ExecutionEngine *e) +{ + if (!e->v8Engine) + return 0; + QQmlEngine *qmlEngine = e->v8Engine->engine(); + if (!qmlEngine) + return 0; + return get(qmlEngine); } void QQmlEnginePrivate::captureProperty(QQmlNotifier *n) diff --git a/src/qml/qml/qqmlmetatype.cpp b/src/qml/qml/qqmlmetatype.cpp index 342d1dc69c..ed0c0afd6f 100644 --- a/src/qml/qml/qqmlmetatype.cpp +++ b/src/qml/qml/qqmlmetatype.cpp @@ -225,17 +225,21 @@ public: void QQmlType::SingletonInstanceInfo::init(QQmlEngine *e) { QV4::ExecutionEngine *v4 = QV8Engine::getV4(e->handle()); - v4->pushGlobalContext(); if (scriptCallback && scriptApi(e).isUndefined()) { + v4->pushGlobalContext(); setScriptApi(e, scriptCallback(e, e)); + v4->popContext(); } else if (qobjectCallback && !qobjectApi(e)) { + v4->pushGlobalContext(); setQObjectApi(e, qobjectCallback(e, e)); + v4->popContext(); } else if (!url.isEmpty() && !qobjectApi(e)) { + v4->pushGlobalContext(); QQmlComponent component(e, url, QQmlComponent::PreferSynchronous); QObject *o = component.create(); setQObjectApi(e, o); + v4->popContext(); } - v4->popContext(); } void QQmlType::SingletonInstanceInfo::destroy(QQmlEngine *e) diff --git a/src/qml/qml/qqmltypeloader.cpp b/src/qml/qml/qqmltypeloader.cpp index 895a2a9cd6..911761d9fd 100644 --- a/src/qml/qml/qqmltypeloader.cpp +++ b/src/qml/qml/qqmltypeloader.cpp @@ -2377,7 +2377,7 @@ void QQmlTypeData::compile() QV4::ExecutionEngine *v4 = QV8Engine::getV4(m_typeLoader->engine()); - QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); + QScopedPointer<QQmlJS::EvalInstructionSelection> isel(v4->iselFactory->create(enginePrivate, v4->executableAllocator, &parsedQML->jsModule, &parsedQML->jsGenerator)); isel->setUseFastLookups(false); QV4::CompiledData::CompilationUnit *jsUnit = isel->compile(/*generated unit data*/false); diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp index 9cd5709a49..38fdffdde6 100644 --- a/src/qml/util/qqmladaptormodel.cpp +++ b/src/qml/util/qqmladaptormodel.cpp @@ -902,7 +902,7 @@ void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, list.setList(variant, engine); - if (QObject *object = qvariant_cast<QObject *>(variant)) { + if (QObject *object = qvariant_cast<QObject *>(list.list())) { setObject(object); if (QAbstractItemModel *model = qobject_cast<QAbstractItemModel *>(object)) { accessors = new VDMAbstractItemModelDataType(this); @@ -927,8 +927,8 @@ void QQmlAdaptorModel::setModel(const QVariant &variant, QQmlDelegateModel *vdm, } else if (list.type() == QQmlListAccessor::ListProperty) { setObject(static_cast<const QQmlListReference *>(variant.constData())->object()); accessors = new VDMObjectDelegateDataType; - } else if (list.type() != QQmlListAccessor::Invalid) { - Q_ASSERT(list.type() != QQmlListAccessor::Instance); // Should have cast to QObject. + } else if (list.type() != QQmlListAccessor::Invalid + && list.type() != QQmlListAccessor::Instance) { // Null QObject setObject(0); accessors = &qt_vdm_list_accessors; } else { diff --git a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc index 384e8209c1..16075f0db3 100644 --- a/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc +++ b/src/quick/doc/src/concepts/visualcanvas/scenegraph.qdoc @@ -724,6 +724,11 @@ with multiple windows. \li Some material flags prevent batching, the most limiting one being QSGMaterial::RequiresFullMatrix which prevents all batching. + \li Applications with a monochrome background should set it using + QQuickWindow::setColor() rather than using a top-level Rectangle item. + QQuickWindow::setColor() will be used in a call to \c glClear(), + which is potentially faster. + \endlist If an application performs poorly, make sure that rendering is diff --git a/src/quick/items/qquickflickable.cpp b/src/quick/items/qquickflickable.cpp index f0a68b184a..ae174d86e0 100644 --- a/src/quick/items/qquickflickable.cpp +++ b/src/quick/items/qquickflickable.cpp @@ -1116,10 +1116,10 @@ void QQuickFlickablePrivate::handleMouseMoveEvent(QMouseEvent *event) hData.velocity = 0; } - if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) { - draggingStarting(); + draggingStarting(); + + if ((hMoved && !prevHMoved) || (vMoved && !prevVMoved)) q->movementStarting(); - } qint64 currentTimestamp = computeCurrentTime(event); qreal elapsed = qreal(currentTimestamp - (lastPos.isNull() ? lastPressTime : lastPosTime)) / 1000.; diff --git a/src/quick/items/qquickitemview.cpp b/src/quick/items/qquickitemview.cpp index 513f8f3857..d9ff024d9f 100644 --- a/src/quick/items/qquickitemview.cpp +++ b/src/quick/items/qquickitemview.cpp @@ -1118,11 +1118,13 @@ void QQuickItemViewPrivate::itemGeometryChanged(QQuickItem *item, const QRectF & if (header && header->item == item) { updateHeader(); markExtentsDirty(); + updateViewport(); if (!q->isMoving() && !q->isFlicking()) fixupPosition(); } else if (footer && footer->item == item) { updateFooter(); markExtentsDirty(); + updateViewport(); if (!q->isMoving() && !q->isFlicking()) fixupPosition(); } diff --git a/src/quick/items/qquickshadereffect.cpp b/src/quick/items/qquickshadereffect.cpp index 9c766a1622..9a20703f18 100644 --- a/src/quick/items/qquickshadereffect.cpp +++ b/src/quick/items/qquickshadereffect.cpp @@ -559,6 +559,7 @@ void QQuickShaderEffectCommon::propertyChanged(QQuickItem *item, int mappedId, \li QVector4D -> vec4 \li QTransform -> mat3 \li QMatrix4x4 -> mat4 + \li QQuaternion -> vec4, scalar value is \c w. \li \l Image, \l ShaderEffectSource -> sampler2D - Origin is in the top-left corner, and the color values are premultiplied. \endlist diff --git a/src/quick/items/qquickshadereffectnode.cpp b/src/quick/items/qquickshadereffectnode.cpp index 8788fa8362..3ab13dbbc7 100644 --- a/src/quick/items/qquickshadereffectnode.cpp +++ b/src/quick/items/qquickshadereffectnode.cpp @@ -197,6 +197,12 @@ void QQuickCustomMaterialShader::updateState(const RenderState &state, QSGMateri case QMetaType::QVector4D: program()->setUniformValue(loc, qvariant_cast<QVector4D>(d.value)); break; + case QMetaType::QQuaternion: + { + QQuaternion q = qvariant_cast<QQuaternion>(d.value); + program()->setUniformValue(loc, q.x(), q.y(), q.z(), q.scalar()); + } + break; case QMetaType::QMatrix4x4: program()->setUniformValue(loc, qvariant_cast<QMatrix4x4>(d.value)); break; diff --git a/src/quick/items/qquickwindow.cpp b/src/quick/items/qquickwindow.cpp index 848eeca2a6..3a8e177bbb 100644 --- a/src/quick/items/qquickwindow.cpp +++ b/src/quick/items/qquickwindow.cpp @@ -665,14 +665,18 @@ void QQuickWindowPrivate::setFocusInScope(QQuickItem *scope, QQuickItem *item, Q QVarLengthArray<QQuickItem *, 20> changed; // Does this change the active focus? - if (item == contentItem || (scopePrivate->activeFocus && item->isEnabled())) { + if (item == contentItem || scopePrivate->activeFocus) { QQuickItem *oldActiveFocusItem = 0; oldActiveFocusItem = activeFocusItem; - newActiveFocusItem = item; - while (newActiveFocusItem->isFocusScope() - && newActiveFocusItem->scopedFocusItem() - && newActiveFocusItem->scopedFocusItem()->isEnabled()) { - newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); + if (item->isEnabled()) { + newActiveFocusItem = item; + while (newActiveFocusItem->isFocusScope() + && newActiveFocusItem->scopedFocusItem() + && newActiveFocusItem->scopedFocusItem()->isEnabled()) { + newActiveFocusItem = newActiveFocusItem->scopedFocusItem(); + } + } else { + newActiveFocusItem = scope; } if (oldActiveFocusItem) { @@ -2795,6 +2799,7 @@ QImage QQuickWindow::grabWindow() QOpenGLContext context; context.setFormat(requestedFormat()); + context.setShareContext(QSGContext::sharedOpenGLContext()); context.create(); context.makeCurrent(this); d->context->initialize(&context); diff --git a/src/quick/scenegraph/qsgrenderloop.cpp b/src/quick/scenegraph/qsgrenderloop.cpp index 9c39ef65f9..aa0d7b5a6c 100644 --- a/src/quick/scenegraph/qsgrenderloop.cpp +++ b/src/quick/scenegraph/qsgrenderloop.cpp @@ -277,6 +277,7 @@ void QSGGuiThreadRenderLoop::renderWindow(QQuickWindow *window) if (QSGContext::sharedOpenGLContext()) gl->setShareContext(QSGContext::sharedOpenGLContext()); if (!gl->create()) { + qWarning("QtQuick: failed to create OpenGL context"); delete gl; gl = 0; } else { diff --git a/src/quick/scenegraph/qsgwindowsrenderloop.cpp b/src/quick/scenegraph/qsgwindowsrenderloop.cpp index 03ee4992bc..0c128d5cae 100644 --- a/src/quick/scenegraph/qsgwindowsrenderloop.cpp +++ b/src/quick/scenegraph/qsgwindowsrenderloop.cpp @@ -180,13 +180,20 @@ void QSGWindowsRenderLoop::show(QQuickWindow *window) m_gl->setFormat(window->requestedFormat()); if (QSGContext::sharedOpenGLContext()) m_gl->setShareContext(QSGContext::sharedOpenGLContext()); - m_gl->create(); + bool created = m_gl->create(); + if (!created) { + qWarning("QtQuick: failed to create OpenGL context"); + delete m_gl; + m_gl = 0; + return; + } QSG_RENDER_TIMING_SAMPLE(time_created); RLDEBUG(" - making current"); - m_gl->makeCurrent(window); + bool current = m_gl->makeCurrent(window); RLDEBUG(" - initializing SG"); QSG_RENDER_TIMING_SAMPLE(time_current); - QQuickWindowPrivate::get(window)->context->initialize(m_gl); + if (current) + m_rc->initialize(m_gl); #ifndef QSG_NO_RENDER_TIMING if (qsg_render_timing) { diff --git a/src/quick/util/qquicksmoothedanimation.cpp b/src/quick/util/qquicksmoothedanimation.cpp index 017fe39003..9e9e6cb419 100644 --- a/src/quick/util/qquicksmoothedanimation.cpp +++ b/src/quick/util/qquicksmoothedanimation.cpp @@ -242,6 +242,9 @@ qreal QSmoothedAnimation::easeFollow(qreal time_seconds) void QSmoothedAnimation::updateCurrentTime(int t) { + if (!isRunning() && !isPaused()) // This can happen if init() stops the animation in some cases + return; + qreal time_seconds = useDelta ? qreal(QQmlAnimationTimer::instance()->currentDelta()) / 1000. : qreal(t - lastTime) / 1000.; if (useDelta) useDelta = false; diff --git a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp index a832c58ae3..5a1148e92e 100644 --- a/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp +++ b/tests/auto/qml/animation/qpauseanimationjob/tst_qpauseanimationjob.cpp @@ -211,19 +211,8 @@ void tst_QPauseAnimationJob::multiplePauseAnimations() #endif QCOMPARE(animation2.m_updateCurrentTimeCount, 2); - QTest::qWait(550); - -#ifdef Q_OS_WIN - if (animation2.state() != QAbstractAnimationJob::Stopped) - QEXPECT_FAIL("", winTimerError, Abort); -#endif - QVERIFY(animation2.state() == QAbstractAnimationJob::Stopped); - -#ifdef Q_OS_WIN - if (animation2.m_updateCurrentTimeCount != 3) - QEXPECT_FAIL("", winTimerError, Abort); -#endif - QCOMPARE(animation2.m_updateCurrentTimeCount, 3); + QTRY_COMPARE(animation2.state(), QAbstractAnimationJob::Stopped); + QVERIFY(animation2.m_updateCurrentTimeCount >= 3); } void tst_QPauseAnimationJob::pauseAndPropertyAnimations() @@ -240,19 +229,17 @@ void tst_QPauseAnimationJob::pauseAndPropertyAnimations() QTest::qWait(100); animation.start(); - QVERIFY(animation.state() == QAbstractAnimationJob::Running); - QVERIFY(pause.state() == QAbstractAnimationJob::Running); - QCOMPARE(pause.m_updateCurrentTimeCount, 2); + QCOMPARE(animation.state(), QAbstractAnimationJob::Running); - QTest::qWait(animation.totalDuration() + 100); + QTRY_COMPARE(animation.state(), QAbstractAnimationJob::Running); + QVERIFY(pause.state() == QAbstractAnimationJob::Running); + QVERIFY2(pause.m_updateCurrentTimeCount >= 2, + QByteArrayLiteral("pause.m_updateCurrentTimeCount=") + QByteArray::number(pause.m_updateCurrentTimeCount)); -#ifdef Q_OS_WIN - if (animation.state() != QAbstractAnimationJob::Stopped) - QEXPECT_FAIL("", winTimerError, Abort); -#endif - QVERIFY(animation.state() == QAbstractAnimationJob::Stopped); - QVERIFY(pause.state() == QAbstractAnimationJob::Stopped); - QVERIFY(pause.m_updateCurrentTimeCount > 3); + QTRY_COMPARE(animation.state(), QAbstractAnimationJob::Stopped); + QCOMPARE(pause.state(), QAbstractAnimationJob::Stopped); + QVERIFY2(pause.m_updateCurrentTimeCount > 3, + QByteArrayLiteral("pause.m_updateCurrentTimeCount=") + QByteArray::number(pause.m_updateCurrentTimeCount)); } void tst_QPauseAnimationJob::pauseResume() @@ -260,19 +247,15 @@ void tst_QPauseAnimationJob::pauseResume() TestablePauseAnimation animation; animation.setDuration(400); animation.start(); - QVERIFY(animation.state() == QAbstractAnimationJob::Running); + QCOMPARE(animation.state(), QAbstractAnimationJob::Running); QTest::qWait(200); animation.pause(); - QVERIFY(animation.state() == QAbstractAnimationJob::Paused); + QCOMPARE(animation.state(), QAbstractAnimationJob::Paused); animation.start(); QTest::qWait(300); - QVERIFY(animation.state() == QAbstractAnimationJob::Stopped); - -#ifdef Q_OS_WIN - if (animation.m_updateCurrentTimeCount != 3) - QEXPECT_FAIL("", winTimerError, Abort); -#endif - QCOMPARE(animation.m_updateCurrentTimeCount, 3); + QTRY_VERIFY(animation.state() == QAbstractAnimationJob::Stopped); + QVERIFY2(animation.m_updateCurrentTimeCount >= 3, + QByteArrayLiteral("animation.m_updateCurrentTimeCount=") + QByteArray::number(animation.m_updateCurrentTimeCount)); } void tst_QPauseAnimationJob::sequentialPauseGroup() @@ -423,7 +406,7 @@ void tst_QPauseAnimationJob::multipleSequentialGroups() if (group.state() != QAbstractAnimationJob::Stopped) QEXPECT_FAIL("", winTimerError, Abort); #endif - QVERIFY(group.state() == QAbstractAnimationJob::Stopped); + QTRY_VERIFY(group.state() == QAbstractAnimationJob::Stopped); #ifdef Q_OS_WIN if (subgroup1.state() != QAbstractAnimationJob::Stopped) diff --git a/tests/auto/qml/debugger/shared/qqmldebugclient.cpp b/tests/auto/qml/debugger/shared/qqmldebugclient.cpp index 95674ce6d7..6474a04796 100644 --- a/tests/auto/qml/debugger/shared/qqmldebugclient.cpp +++ b/tests/auto/qml/debugger/shared/qqmldebugclient.cpp @@ -449,6 +449,7 @@ QString QQmlDebugClient::stateString() const case Unavailable: return QLatin1String("Unavailable"); case Enabled: return QLatin1String("Enabled"); } + return QLatin1String("Invalid"); } void QQmlDebugClient::sendMessage(const QByteArray &message) diff --git a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp index 3ae27fe9c3..9c67e6e2e9 100644 --- a/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp +++ b/tests/auto/qml/qqmlxmlhttprequest/tst_qqmlxmlhttprequest.cpp @@ -127,8 +127,8 @@ private: void tst_qqmlxmlhttprequest::domExceptionCodes() { QQmlComponent component(&engine, testFileUrl("domExceptionCodes.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("index_size_err").toInt(), 1); QCOMPARE(object->property("domstring_size_err").toInt(), 2); @@ -147,8 +147,6 @@ void tst_qqmlxmlhttprequest::domExceptionCodes() QCOMPARE(object->property("invalid_access_err").toInt(), 15); QCOMPARE(object->property("validation_err").toInt(), 16); QCOMPARE(object->property("type_mismatch_err").toInt(), 17); - - delete object; } void tst_qqmlxmlhttprequest::callbackException_data() @@ -172,15 +170,13 @@ void tst_qqmlxmlhttprequest::callbackException() QTest::ignoreMessage(QtWarningMsg, expect.toLatin1()); QQmlComponent component(&engine, testFileUrl("callbackException.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); object->setProperty("which", which); component.completeCreate(); QTRY_VERIFY(object->property("threw").toBool() == true); - - delete object; } // Test that the state value properties on the XMLHttpRequest constructor have the correct values. @@ -188,61 +184,53 @@ void tst_qqmlxmlhttprequest::callbackException() void tst_qqmlxmlhttprequest::staticStateValues() { QQmlComponent component(&engine, testFileUrl("staticStateValues.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("unsent").toInt(), 0); QCOMPARE(object->property("opened").toInt(), 1); QCOMPARE(object->property("headers_received").toInt(), 2); QCOMPARE(object->property("loading").toInt(), 3); QCOMPARE(object->property("done").toInt(), 4); - - delete object; } // Test that the state value properties on instances have the correct values. void tst_qqmlxmlhttprequest::instanceStateValues() { QQmlComponent component(&engine, testFileUrl("instanceStateValues.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("unsent").toInt(), 0); QCOMPARE(object->property("opened").toInt(), 1); QCOMPARE(object->property("headers_received").toInt(), 2); QCOMPARE(object->property("loading").toInt(), 3); QCOMPARE(object->property("done").toInt(), 4); - - delete object; } // Test calling constructor void tst_qqmlxmlhttprequest::constructor() { QQmlComponent component(&engine, testFileUrl("constructor.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("calledAsConstructor").toBool(), true); QCOMPARE(object->property("calledAsFunction").toBool(), true); - - delete object; } // Test that all the properties are set correctly before any request is sent void tst_qqmlxmlhttprequest::defaultState() { QQmlComponent component(&engine, testFileUrl("defaultState.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("readState").toInt(), 0); QCOMPARE(object->property("statusIsException").toBool(), true); QCOMPARE(object->property("statusTextIsException").toBool(), true); QCOMPARE(object->property("responseText").toString(), QString()); QCOMPARE(object->property("responseXMLIsNull").toBool(), true); - - delete object; } // Test valid XMLHttpRequest.open() calls @@ -262,8 +250,8 @@ void tst_qqmlxmlhttprequest::open() } QQmlComponent component(&engine, qmlFile); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", url); component.completeCreate(); @@ -275,8 +263,6 @@ void tst_qqmlxmlhttprequest::open() QCOMPARE(object->property("responseXML").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } void tst_qqmlxmlhttprequest::open_data() @@ -297,24 +283,20 @@ void tst_qqmlxmlhttprequest::open_data() void tst_qqmlxmlhttprequest::open_invalid_method() { QQmlComponent component(&engine, testFileUrl("open_invalid_method.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("exceptionThrown").toBool(), true); - - delete object; } // Test that calling XMLHttpRequest.open() with sync raises an exception void tst_qqmlxmlhttprequest::open_sync() { QQmlComponent component(&engine, testFileUrl("open_sync.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("exceptionThrown").toBool(), true); - - delete object; } // Calling with incorrect arg count raises an exception @@ -322,22 +304,18 @@ void tst_qqmlxmlhttprequest::open_arg_count() { { QQmlComponent component(&engine, testFileUrl("open_arg_count.1.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("exceptionThrown").toBool(), true); - - delete object; } { QQmlComponent component(&engine, testFileUrl("open_arg_count.2.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("exceptionThrown").toBool(), true); - - delete object; } } @@ -351,14 +329,12 @@ void tst_qqmlxmlhttprequest::setRequestHeader() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("setRequestHeader.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test valid setRequestHeader() calls with different header cases @@ -371,25 +347,21 @@ void tst_qqmlxmlhttprequest::setRequestHeader_caseInsensitive() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("setRequestHeader_caseInsensitive.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test setting headers before open() throws exception void tst_qqmlxmlhttprequest::setRequestHeader_unsent() { QQmlComponent component(&engine, testFileUrl("setRequestHeader_unsent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } void tst_qqmlxmlhttprequest::setRequestHeader_illegalName_data() @@ -432,8 +404,8 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("setRequestHeader_illegalName.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); object->setProperty("header", name); component.completeCreate(); @@ -446,8 +418,6 @@ void tst_qqmlxmlhttprequest::setRequestHeader_illegalName() QCOMPARE(object->property("responseXML").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test that attempting to set a header after a request is sent throws an exception @@ -460,53 +430,45 @@ void tst_qqmlxmlhttprequest::setRequestHeader_sent() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("setRequestHeader_sent.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QCOMPARE(object->property("test").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Invalid arg count throws exception void tst_qqmlxmlhttprequest::setRequestHeader_args() { QQmlComponent component(&engine, testFileUrl("setRequestHeader_args.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("exceptionThrown").toBool(), true); - - delete object; } // Test that calling send() in UNSENT state throws an exception void tst_qqmlxmlhttprequest::send_unsent() { QQmlComponent component(&engine, testFileUrl("send_unsent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } // Test attempting to resend a sent request throws an exception void tst_qqmlxmlhttprequest::send_alreadySent() { QQmlComponent component(&engine, testFileUrl("send_alreadySent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test that sends for GET, HEAD and DELETE ignore data @@ -520,15 +482,13 @@ void tst_qqmlxmlhttprequest::send_ignoreData() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("reqType", "GET"); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } { @@ -539,15 +499,13 @@ void tst_qqmlxmlhttprequest::send_ignoreData() QUrl())); QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("reqType", "HEAD"); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } { @@ -558,15 +516,13 @@ void tst_qqmlxmlhttprequest::send_ignoreData() QUrl())); QQmlComponent component(&engine, testFileUrl("send_ignoreData.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("reqType", "DELETE"); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } } @@ -583,14 +539,12 @@ void tst_qqmlxmlhttprequest::send_withdata() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl(file_qml)); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } void tst_qqmlxmlhttprequest::send_withdata_data() @@ -611,8 +565,8 @@ void tst_qqmlxmlhttprequest::send_withdata_data() void tst_qqmlxmlhttprequest::abort_unsent() { QQmlComponent component(&engine, testFileUrl("abort_unsent.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -624,16 +578,14 @@ void tst_qqmlxmlhttprequest::abort_unsent() QCOMPARE(object->property("responseXML").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test abort() cancels an open (but unsent) request void tst_qqmlxmlhttprequest::abort_opened() { QQmlComponent component(&engine, testFileUrl("abort_opened.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "testdocument.html"); component.completeCreate(); @@ -645,8 +597,6 @@ void tst_qqmlxmlhttprequest::abort_opened() QCOMPARE(object->property("responseXML").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } // Test abort() aborts in progress send @@ -659,8 +609,8 @@ void tst_qqmlxmlhttprequest::abort() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("abort.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("urlDummy", "http://127.0.0.1:14449/testdocument.html"); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); @@ -670,8 +620,6 @@ void tst_qqmlxmlhttprequest::abort() QCOMPARE(object->property("endStateUnsent").toBool(), true); QTRY_VERIFY(object->property("dataOK").toBool() == true); - - delete object; } void tst_qqmlxmlhttprequest::getResponseHeader() @@ -686,8 +634,8 @@ void tst_qqmlxmlhttprequest::getResponseHeader() QQmlComponent component(&engine, testFileUrl("getResponseHeader.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); @@ -709,44 +657,36 @@ void tst_qqmlxmlhttprequest::getResponseHeader() QCOMPARE(object->property("doneValidHeader").toBool(), true); QCOMPARE(object->property("doneMultiValidHeader").toBool(), true); QCOMPARE(object->property("doneCookieHeader").toBool(), true); - - delete object; } // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_unsent() { QQmlComponent component(&engine, testFileUrl("getResponseHeader_unsent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } // Test getResponseHeader throws an exception in an invalid state void tst_qqmlxmlhttprequest::getResponseHeader_sent() { QQmlComponent component(&engine, testFileUrl("getResponseHeader_sent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getResponseHeader_args() { QQmlComponent component(&engine, testFileUrl("getResponseHeader_args.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("exceptionThrown").toBool() == true); - - delete object; } void tst_qqmlxmlhttprequest::getAllResponseHeaders() @@ -760,8 +700,8 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); component.completeCreate(); @@ -777,44 +717,36 @@ void tst_qqmlxmlhttprequest::getAllResponseHeaders() QCOMPARE(object->property("doneState").toBool(), true); QCOMPARE(object->property("doneHeader").toBool(), true); - - delete object; } // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_unsent() { QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_unsent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } // Test getAllResponseHeaders throws an exception in an invalid state void tst_qqmlxmlhttprequest::getAllResponseHeaders_sent() { QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_sent.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("test").toBool(), true); - - delete object; } // Invalid arg count throws exception void tst_qqmlxmlhttprequest::getAllResponseHeaders_args() { QQmlComponent component(&engine, testFileUrl("getAllResponseHeaders_args.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("exceptionThrown").toBool() == true); - - delete object; } void tst_qqmlxmlhttprequest::status() @@ -829,8 +761,8 @@ void tst_qqmlxmlhttprequest::status() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("status.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); object->setProperty("expectedStatus", status); component.completeCreate(); @@ -844,8 +776,6 @@ void tst_qqmlxmlhttprequest::status() QCOMPARE(object->property("loading").toBool(), true); QCOMPARE(object->property("done").toBool(), true); QCOMPARE(object->property("resetException").toBool(), true); - - delete object; } void tst_qqmlxmlhttprequest::status_data() @@ -870,8 +800,8 @@ void tst_qqmlxmlhttprequest::statusText() testFileUrl("testdocument.html"))); QQmlComponent component(&engine, testFileUrl("statusText.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); object->setProperty("expectedStatus", statusText); component.completeCreate(); @@ -885,8 +815,6 @@ void tst_qqmlxmlhttprequest::statusText() QCOMPARE(object->property("loading").toBool(), true); QCOMPARE(object->property("done").toBool(), true); QCOMPARE(object->property("resetException").toBool(), true); - - delete object; } void tst_qqmlxmlhttprequest::statusText_data() @@ -912,8 +840,8 @@ void tst_qqmlxmlhttprequest::responseText() bodyUrl)); QQmlComponent component(&engine, testFileUrl("responseText.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/testdocument.html"); object->setProperty("expectedText", responseText); component.completeCreate(); @@ -927,8 +855,6 @@ void tst_qqmlxmlhttprequest::responseText() QCOMPARE(object->property("loading").toBool(), true); QCOMPARE(object->property("done").toBool(), true); QCOMPARE(object->property("reset").toBool(), true); - - delete object; } void tst_qqmlxmlhttprequest::responseText_data() @@ -950,11 +876,11 @@ void tst_qqmlxmlhttprequest::nonUtf8() QFETCH(QString, xmlRootNodeValue); QQmlComponent component(&engine, testFileUrl("utf16.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); object->setProperty("fileName", fileName); - QMetaObject::invokeMethod(object, "startRequest"); + QMetaObject::invokeMethod(object.data(), "startRequest"); QTRY_VERIFY(object->property("dataOK").toBool() == true); @@ -964,8 +890,6 @@ void tst_qqmlxmlhttprequest::nonUtf8() QString rootNodeValue = object->property("responseXmlRootNodeValue").toString(); QCOMPARE(rootNodeValue, xmlRootNodeValue); } - - delete object; } void tst_qqmlxmlhttprequest::nonUtf8_data() @@ -989,8 +913,8 @@ void tst_qqmlxmlhttprequest::nonUtf8_data() void tst_qqmlxmlhttprequest::invalidMethodUsage() { QQmlComponent component(&engine, testFileUrl("invalidMethodUsage.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QCOMPARE(object->property("readyState").toBool(), true); QCOMPARE(object->property("status").toBool(), true); @@ -1004,8 +928,6 @@ void tst_qqmlxmlhttprequest::invalidMethodUsage() QCOMPARE(object->property("abort").toBool(), true); QCOMPARE(object->property("getResponseHeader").toBool(), true); QCOMPARE(object->property("getAllResponseHeaders").toBool(), true); - - delete object; } // Test that XMLHttpRequest transparently redirects @@ -1018,16 +940,14 @@ void tst_qqmlxmlhttprequest::redirects() server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFileUrl("redirects.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); object->setProperty("expectedText", ""); component.completeCreate(); QTRY_VERIFY(object->property("done").toBool() == true); QCOMPARE(object->property("dataOK").toBool(), true); - - delete object; } { @@ -1037,16 +957,14 @@ void tst_qqmlxmlhttprequest::redirects() server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFileUrl("redirectError.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); object->setProperty("expectedText", ""); component.completeCreate(); QTRY_VERIFY(object->property("done").toBool() == true); QCOMPARE(object->property("dataOK").toBool(), true); - - delete object; } { @@ -1056,8 +974,8 @@ void tst_qqmlxmlhttprequest::redirects() server.serveDirectory(dataDirectory()); QQmlComponent component(&engine, testFileUrl("redirectRecur.qml")); - QObject *object = component.beginCreate(engine.rootContext()); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.beginCreate(engine.rootContext())); + QVERIFY(!object.isNull()); object->setProperty("url", "http://127.0.0.1:14445/redirect.html"); object->setProperty("expectedText", ""); component.completeCreate(); @@ -1069,92 +987,78 @@ void tst_qqmlxmlhttprequest::redirects() QVERIFY(object->property("done").toBool() == true); QCOMPARE(object->property("dataOK").toBool(), true); - - delete object; } } void tst_qqmlxmlhttprequest::responseXML_invalid() { QQmlComponent component(&engine, testFileUrl("responseXML_invalid.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlNull").toBool(), true); - - delete object; } // Test the Document DOM element void tst_qqmlxmlhttprequest::document() { QQmlComponent component(&engine, testFileUrl("document.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlTest").toBool(), true); - - delete object; } // Test the Element DOM element void tst_qqmlxmlhttprequest::element() { QQmlComponent component(&engine, testFileUrl("element.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlTest").toBool(), true); - - delete object; } // Test the Attr DOM element void tst_qqmlxmlhttprequest::attr() { QQmlComponent component(&engine, testFileUrl("attr.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlTest").toBool(), true); - - delete object; } // Test the Text DOM element void tst_qqmlxmlhttprequest::text() { QQmlComponent component(&engine, testFileUrl("text.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlTest").toBool(), true); - - delete object; } // Test the CDataSection DOM element void tst_qqmlxmlhttprequest::cdata() { QQmlComponent component(&engine, testFileUrl("cdata.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); QTRY_VERIFY(object->property("dataOK").toBool() == true); QCOMPARE(object->property("xmlTest").toBool(), true); - - delete object; } void tst_qqmlxmlhttprequest::stateChangeCallingContext() @@ -1171,11 +1075,10 @@ void tst_qqmlxmlhttprequest::stateChangeCallingContext() server.serveDirectory(dataDirectory(), TestHTTPServer::Delay); QQmlComponent component(&engine, testFileUrl("stateChangeCallingContext.qml")); - QObject *object = component.create(); - QVERIFY(object != 0); + QScopedPointer<QObject> object(component.create()); + QVERIFY(!object.isNull()); server.sendDelayedItem(); QTRY_VERIFY(object->property("success").toBool() == true); - delete object; } QTEST_MAIN(tst_qqmlxmlhttprequest) diff --git a/tests/auto/quick/qquickitem/tst_qquickitem.cpp b/tests/auto/quick/qquickitem/tst_qquickitem.cpp index 97e002361b..2c6dcd72ba 100644 --- a/tests/auto/quick/qquickitem/tst_qquickitem.cpp +++ b/tests/auto/quick/qquickitem/tst_qquickitem.cpp @@ -44,7 +44,6 @@ #include <QtQuick/qquickitem.h> #include <QtQuick/qquickwindow.h> #include <QtQuick/qquickview.h> -#include <QtWidgets/QGraphicsSceneMouseEvent> #include "private/qquickfocusscope_p.h" #include "private/qquickitem_p.h" #include <qpa/qwindowsysteminterface.h> @@ -1151,6 +1150,30 @@ void tst_qquickitem::enabledFocus() QCOMPARE(child2.hasFocus(), false); QCOMPARE(child2.hasActiveFocus(), false); QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&child1)); + + child2.setFocus(true); + QCOMPARE(root.isEnabled(), true); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), true); + QCOMPARE(child1.isEnabled(), true); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(window.activeFocusItem(), static_cast<QQuickItem *>(&root)); + + root.setEnabled(false); + QCOMPARE(root.isEnabled(), false); + QCOMPARE(root.hasFocus(), true); + QCOMPARE(root.hasActiveFocus(), false); + QCOMPARE(child1.isEnabled(), false); + QCOMPARE(child1.hasFocus(), false); + QCOMPARE(child1.hasActiveFocus(), false); + QCOMPARE(child2.isEnabled(), false); + QCOMPARE(child2.hasFocus(), true); + QCOMPARE(child2.hasActiveFocus(), false); + QCOMPARE(window.activeFocusItem(), window.contentItem()); } static inline QByteArray msgItem(const QQuickItem *item) diff --git a/tests/auto/quick/qquicklistview/data/headerchangesviewport.qml b/tests/auto/quick/qquicklistview/data/headerchangesviewport.qml new file mode 100644 index 0000000000..b32f7a1236 --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/headerchangesviewport.qml @@ -0,0 +1,19 @@ +import QtQuick 2.0 + +Rectangle { + width: 240 + height: 320 + color: "#ffffff" + ListView { + id: list + anchors.fill: parent + objectName: "list" + delegate: Text {} + header: Text { + objectName: "header" + text: "ninjas" + height: headerHeight + width: headerWidth + } + } +} diff --git a/tests/auto/quick/qquicklistview/data/typedModel.qml b/tests/auto/quick/qquicklistview/data/typedModel.qml new file mode 100644 index 0000000000..d2b3f7e42f --- /dev/null +++ b/tests/auto/quick/qquicklistview/data/typedModel.qml @@ -0,0 +1,23 @@ +import QtQuick 2.0 + +ListView { + width: 100 + height: 100 + + delegate: Item { + width: 100 + height: 10 + } + model: listModel + + ListModel { + id: listModel + + ListElement { label: "a" } + ListElement { label: "b" } + ListElement { label: "c" } + ListElement { label: "d" } + ListElement { label: "e" } + ListElement { label: "f" } + } +} diff --git a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp index f62151c2d8..fb0b1c95ca 100644 --- a/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp +++ b/tests/auto/quick/qquicklistview/tst_qquicklistview.cpp @@ -150,6 +150,7 @@ private slots: void header(); void header_data(); void header_delayItemCreation(); + void headerChangesViewport(); void footer(); void footer_data(); void extents(); @@ -213,6 +214,8 @@ private slots: void outsideViewportChangeNotAffectingView(); void testProxyModelChangedAfterMove(); + void typedModel(); + private: template <class T> void items(const QUrl &source); template <class T> void changed(const QUrl &source); @@ -3655,6 +3658,35 @@ void tst_QQuickListView::header_delayItemCreation() delete window; } +void tst_QQuickListView::headerChangesViewport() +{ + QQuickView *window = getView(); + window->rootContext()->setContextProperty("headerHeight", 20); + window->rootContext()->setContextProperty("headerWidth", 240); + window->setSource(testFileUrl("headerchangesviewport.qml")); + + QQuickListView *listview = findItem<QQuickListView>(window->rootObject(), "list"); + QTRY_VERIFY(listview != 0); + QTRY_COMPARE(QQuickItemPrivate::get(listview)->polishScheduled, false); + + QQuickItem *contentItem = listview->contentItem(); + QTRY_VERIFY(contentItem != 0); + + QQuickText *header = 0; + QTRY_VERIFY(header = findItem<QQuickText>(contentItem, "header")); + QVERIFY(header == listview->headerItem()); + + QCOMPARE(header->height(), 20.); + QCOMPARE(listview->contentHeight(), 20.); + + // change height + window->rootContext()->setContextProperty("headerHeight", 50); + + // verify that list content height updates also + QCOMPARE(header->height(), 50.); + QCOMPARE(listview->contentHeight(), 50.); +} + void tst_QQuickListView::footer() { QFETCH(QQuickListView::Orientation, orientation); @@ -6960,6 +6992,24 @@ void tst_QQuickListView::testProxyModelChangedAfterMove() delete window; } +void tst_QQuickListView::typedModel() +{ + QQmlEngine engine; + QQmlComponent component(&engine, testFileUrl("typedModel.qml")); + + QScopedPointer<QObject> object(component.create()); + + QQuickListView *listview = qobject_cast<QQuickListView *>(object.data()); + QVERIFY(listview); + + QCOMPARE(listview->count(), 6); + + QQmlListModel *listModel = 0; + + listview->setModel(QVariant::fromValue(listModel)); + QCOMPARE(listview->count(), 0); +} + QTEST_MAIN(tst_QQuickListView) #include "tst_qquicklistview.moc" diff --git a/tests/auto/shared/testhttpserver.cpp b/tests/auto/shared/testhttpserver.cpp index fd681710de..20df0c12f5 100644 --- a/tests/auto/shared/testhttpserver.cpp +++ b/tests/auto/shared/testhttpserver.cpp @@ -88,7 +88,7 @@ The following request urls will then result in the appropriate action: \endtable */ TestHTTPServer::TestHTTPServer(quint16 port) -: m_hasFailed(false) +: m_state(AwaitingHeader) { QObject::connect(&server, SIGNAL(newConnection()), this, SLOT(newConnection())); @@ -122,7 +122,8 @@ void TestHTTPServer::addRedirect(const QString &filename, const QString &redirec bool TestHTTPServer::wait(const QUrl &expect, const QUrl &reply, const QUrl &body) { - m_hasFailed = false; + m_state = AwaitingHeader; + m_data.clear(); QFile expectFile(expect.toLocalFile()); if (!expectFile.open(QIODevice::ReadOnly)) return false; @@ -175,7 +176,7 @@ bool TestHTTPServer::wait(const QUrl &expect, const QUrl &reply, const QUrl &bod bool TestHTTPServer::hasFailed() const { - return m_hasFailed; + return m_state == Failed; } void TestHTTPServer::newConnection() @@ -216,33 +217,41 @@ void TestHTTPServer::readyRead() return; } - if (m_hasFailed || (waitData.body.isEmpty() && waitData.headers.count() == 0)) { + if (m_state == Failed || (waitData.body.isEmpty() && waitData.headers.count() == 0)) { qWarning() << "TestHTTPServer: Unexpected data" << socket->readAll(); return; } - QByteArray line; - while (!(line = socket->readLine()).isEmpty()) { - line.replace('\r', ""); - if (line.at(0) == '\n') { - QByteArray data = socket->readAll(); - if (waitData.body != data) { - qWarning() << "TestHTTPServer: Unexpected data" << data << "\nExpected: " << waitData.body; - m_hasFailed = true; - socket->disconnectFromHost(); - return; + if (m_state == AwaitingHeader) { + QByteArray line; + while (!(line = socket->readLine()).isEmpty()) { + line.replace('\r', ""); + if (line.at(0) == '\n') { + m_state = AwaitingData; + m_data += socket->readAll(); + break; + } else { + if (!waitData.headers.contains(line)) { + qWarning() << "TestHTTPServer: Unexpected header:" << line << "\nExpected headers: " << waitData.headers; + m_state = Failed; + socket->disconnectFromHost(); + return; + } } } - else if (!waitData.headers.contains(line)) { - qWarning() << "TestHTTPServer: Unexpected header:" << line << "\nExpected headers: " << waitData.headers; - m_hasFailed = true; - socket->disconnectFromHost(); - return; - } + } else { + m_data += socket->readAll(); } - socket->write(replyData); - socket->disconnectFromHost(); + if (!m_data.isEmpty() || waitData.body.isEmpty()) { + if (waitData.body != m_data) { + qWarning() << "TestHTTPServer: Unexpected data" << m_data << "\nExpected: " << waitData.body; + m_state = Failed; + } else { + socket->write(replyData); + } + socket->disconnectFromHost(); + } } bool TestHTTPServer::reply(QTcpSocket *socket, const QByteArray &fileName) diff --git a/tests/auto/shared/testhttpserver.h b/tests/auto/shared/testhttpserver.h index ce0501f170..ae7d137143 100644 --- a/tests/auto/shared/testhttpserver.h +++ b/tests/auto/shared/testhttpserver.h @@ -74,6 +74,12 @@ private slots: void sendOne(); private: + enum State { + AwaitingHeader, + AwaitingData, + Failed + }; + void serveGET(QTcpSocket *, const QByteArray &); bool reply(QTcpSocket *, const QByteArray &); @@ -87,7 +93,8 @@ private: } waitData; QByteArray replyData; QByteArray bodyData; - bool m_hasFailed; + QByteArray m_data; + State m_state; QHash<QString,QString> aliases; QHash<QString,QString> redirects; |