aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSimon Hausmann <simon.hausmann@digia.com>2013-10-30 16:49:32 +0100
committerThe Qt Project <gerrit-noreply@qt-project.org>2013-11-25 22:04:42 +0100
commitcf5185d1872f75b82876dd4e4ecdc3d27240f942 (patch)
tree9c8bbf9597bfa38a4b56d4b6557797a7b2def501
parent222e06bf4ed509e72c1533cbe1d4859ca96933f3 (diff)
Improve type interference for QObject properties
Propagate QObject properties in member expressions across temporaries as part of the type interference SSA pass. This replaces the earlier attempt to resolving QObject properties in fieldMemberExpression() in the codegen, but it was incomplete and now things like the following are fully resolved: var tmp = blah.somePropertyThatReturnsAQQuickItem; <-- QQuickItem property return type propagated into tmp var width = tmp.width; <-- and picked up here again to resolve the index of width instead of by name With this patch Temp gets a helper structure with a function pointer, initialized to aid the resolution of properties in Qt meta objects. This structure is propagated into the temps until it reaches the next member expression that uses the temp. Similarly QObjectType is added as IR type, next to VarType. The resolution inside the SSA type interference pass also requires passing through the QQmlEngine from the upper caller levels, in order to resolve the property type to a potential QMetaObject property. Change-Id: I14c98fa455db57603da46613ce49c174d0944291 Reviewed-by: Lars Knoll <lars.knoll@digia.com>
-rw-r--r--src/qml/compiler/qqmlcodegenerator.cpp93
-rw-r--r--src/qml/compiler/qqmlcodegenerator_p.h3
-rw-r--r--src/qml/compiler/qv4codegen_p.h2
-rw-r--r--src/qml/compiler/qv4isel_masm.cpp5
-rw-r--r--src/qml/compiler/qv4isel_masm_p.h7
-rw-r--r--src/qml/compiler/qv4isel_moth.cpp5
-rw-r--r--src/qml/compiler/qv4isel_moth_p.h8
-rw-r--r--src/qml/compiler/qv4isel_p.h4
-rw-r--r--src/qml/compiler/qv4jsir.cpp13
-rw-r--r--src/qml/compiler/qv4jsir_p.h25
-rw-r--r--src/qml/compiler/qv4regalloc.cpp1
-rw-r--r--src/qml/compiler/qv4ssa.cpp110
-rw-r--r--src/qml/compiler/qv4ssa_p.h3
-rw-r--r--src/qml/jsruntime/qv4functionobject.cpp3
-rw-r--r--src/qml/jsruntime/qv4script.cpp5
-rw-r--r--src/qml/qml/qqmlcompiler.cpp2
-rw-r--r--src/qml/qml/qqmlengine_p.h13
-rw-r--r--src/qml/qml/qqmltypeloader.cpp2
18 files changed, 192 insertions, 112 deletions
diff --git a/src/qml/compiler/qqmlcodegenerator.cpp b/src/qml/compiler/qqmlcodegenerator.cpp
index c16dd5daea..7d6a5ad1bc 100644
--- a/src/qml/compiler/qqmlcodegenerator.cpp
+++ b/src/qml/compiler/qqmlcodegenerator.cpp
@@ -1311,47 +1311,55 @@ static QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, co
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()) {
-
- bool propertySuitable = baseAsMember->property->isFinal();
-
- 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 void initMetaObjectResolver(V4IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject);
- // 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;
- }
+static V4IR::Type resolveMetaObjectProperty(QQmlEnginePrivate *qmlEngine, V4IR::MemberExpressionResolver *resolver, V4IR::Member *member)
+{
+ V4IR::Type result = V4IR::VarType;
+ // Try to resolve members of QObjects in QML mode
+ if (qmlEngine) {
+ QQmlPropertyCache *metaObject = static_cast<QQmlPropertyCache*>(resolver->data);
+ QQmlPropertyData *property = member->property;
+
+ if (!property && metaObject) {
+ QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0);
+ if (candidate && candidate->isFinal() && metaObject->isAllowedInRevision(candidate)
+ && !candidate->isFunction()) {
+ 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;
}
V4IR::Expr *JSCodeGen::fallbackNameLookup(const QString &name, int line, int col)
@@ -1393,9 +1401,12 @@ 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);
+ int temp = _block->newTemp();
+ _block->MOVE(_block->TEMP(temp), _block->NAME(V4IR::Name::builtin_qml_scope_object, line, col));
+ V4IR::Temp *base = _block->TEMP(temp);
+ initMetaObjectResolver(&base->memberResolver, _scopeObject);
+ return _block->QML_QOBJECT_PROPERTY(base,
+ _function->newString(name), pd);
}
}
@@ -1407,9 +1418,11 @@ 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);
+ int temp = _block->newTemp();
+ _block->MOVE(_block->TEMP(temp), _block->NAME(V4IR::Name::builtin_qml_context_object, line, col));
+ V4IR::Temp *base = _block->TEMP(temp);
+ initMetaObjectResolver(&base->memberResolver, _contextObject);
+ return _block->QML_QOBJECT_PROPERTY(base, _function->newString(name), pd);
}
}
diff --git a/src/qml/compiler/qqmlcodegenerator_p.h b/src/qml/compiler/qqmlcodegenerator_p.h
index 1cc5b1e2f6..03c69efb80 100644
--- a/src/qml/compiler/qqmlcodegenerator_p.h
+++ b/src/qml/compiler/qqmlcodegenerator_p.h
@@ -364,9 +364,6 @@ 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 V4IR::Expr *fallbackNameLookup(const QString &name, int line, int col);
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index de22e8904b..736ac9871d 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);
diff --git a/src/qml/compiler/qv4isel_masm.cpp b/src/qml/compiler/qv4isel_masm.cpp
index aca28eac2c..6f8bf13bd7 100644
--- a/src/qml/compiler/qv4isel_masm.cpp
+++ b/src/qml/compiler/qv4isel_masm.cpp
@@ -539,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());
@@ -561,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();
diff --git a/src/qml/compiler/qv4isel_masm_p.h b/src/qml/compiler/qv4isel_masm_p.h
index 570400656a..0c2c994e32 100644
--- a/src/qml/compiler/qv4isel_masm_p.h
+++ b/src/qml/compiler/qv4isel_masm_p.h
@@ -1419,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);
@@ -1629,14 +1629,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 40ccc358f7..1b973b2359 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);
diff --git a/src/qml/compiler/qv4isel_moth_p.h b/src/qml/compiler/qv4isel_moth_p.h
index ffb8ff4539..ef26ba875d 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);
@@ -168,6 +168,8 @@ private:
void patchJumpAddresses();
QByteArray squeezeCode() const;
+ QQmlEnginePrivate *qmlEngine;
+
V4IR::BasicBlock *_block;
V4IR::BasicBlock *_nextBlock;
@@ -189,8 +191,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.h b/src/qml/compiler/qv4isel_p.h
index 23ef7cc69e..647d85996d 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;
};
diff --git a/src/qml/compiler/qv4jsir.cpp b/src/qml/compiler/qv4jsir.cpp
index 75261b2469..dc982e72cc 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) {
diff --git a/src/qml/compiler/qv4jsir_p.h b/src/qml/compiler/qv4jsir_p.h
index 9a1bd87a1d..cb8e9d9195 100644
--- a/src/qml/compiler/qv4jsir_p.h
+++ b/src/qml/compiler/qv4jsir_p.h
@@ -73,6 +73,8 @@ QT_BEGIN_NAMESPACE
class QTextStream;
class QQmlType;
class QQmlPropertyData;
+class QQmlPropertyCache;
+class QQmlEnginePrivate;
namespace QV4 {
struct ExecutionContext;
@@ -181,7 +183,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 +221,21 @@ struct StmtVisitor {
virtual void visitPhi(Phi *) = 0;
};
+
+struct MemberExpressionResolver
+{
+ typedef Type (*ResolveFunction)(QQmlEnginePrivate *engine, MemberExpressionResolver *resolver, Member *member);
+
+ MemberExpressionResolver()
+ : resolveMember(0), data(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
+};
+
struct Expr {
Type type;
@@ -363,6 +381,8 @@ struct Temp: Expr {
unsigned scope : 28; // how many scopes outside the current one?
unsigned kind : 3;
unsigned isArgumentsOrEval : 1;
+ // Used when temp is used as base in member expression
+ MemberExpressionResolver memberResolver;
void init(unsigned kind, unsigned index, unsigned scope)
{
@@ -536,7 +556,6 @@ struct Member: Expr {
this->type = MemberByName;
this->base = base;
this->name = name;
- this->memberIndex = -1;
this->property = 0;
}
@@ -554,6 +573,7 @@ struct Member: Expr {
this->type = MemberOfQObject;
this->base = base;
this->name = name;
+ this->memberIndex = -1;
this->property = property;
}
@@ -937,6 +957,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..a0245c6808 100644
--- a/src/qml/compiler/qv4regalloc.cpp
+++ b/src/qml/compiler/qv4regalloc.cpp
@@ -589,6 +589,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..ab2334d7dc 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>
@@ -1209,6 +1210,7 @@ protected:
e->expr->accept(this);
switch (e->expr->type) {
+ case QObjectType:
case StringType:
case VarType:
markAsSideEffect();
@@ -1227,7 +1229,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 +1245,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 +1277,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 +1358,7 @@ private:
class PropagateTempTypes: public StmtVisitor, ExprVisitor
{
public:
- PropagateTempTypes(const QHash<Temp, int> &tempTypes)
+ PropagateTempTypes(const QHash<Temp, DiscoveredType> &tempTypes)
: _tempTypes(tempTypes)
{}
@@ -1352,7 +1374,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 +1419,7 @@ private:
}
private:
- QHash<Temp, int> _tempTypes;
+ QHash<Temp, DiscoveredType> _tempTypes;
};
private:
@@ -1429,13 +1455,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 +1476,7 @@ private:
_worklist += QSet<Stmt *>::fromList(_defUses.uses(*t));
}
} else {
- e->type = (Type) ty;
+ e->type = (Type) ty.type;
}
}
@@ -1472,8 +1498,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 +1533,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 +1602,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 +1638,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 +1653,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 +1671,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)
@@ -2356,10 +2374,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);
@@ -2570,10 +2588,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 +2975,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 +3017,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/qv4functionobject.cpp b/src/qml/jsruntime/qv4functionobject.cpp
index 99797ec56b..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"
@@ -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);
diff --git a/src/qml/jsruntime/qv4script.cpp b/src/qml/jsruntime/qv4script.cpp
index 2f015ac83a..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>
@@ -229,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();
@@ -355,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/qml/qqmlcompiler.cpp b/src/qml/qml/qqmlcompiler.cpp
index 7b33849e67..79fc3ba303 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/qqmlengine_p.h b/src/qml/qml/qqmlengine_p.h
index 22ca8b8057..19eb320fbe 100644
--- a/src/qml/qml/qqmlengine_p.h
+++ b/src/qml/qml/qqmlengine_p.h
@@ -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/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);