diff options
-rw-r--r-- | src/qml/compiler/qv4codegen.cpp | 110 | ||||
-rw-r--r-- | src/qml/compiler/qv4codegen_p.h | 23 | ||||
-rw-r--r-- | tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp | 8 |
3 files changed, 136 insertions, 5 deletions
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 82023ce6cc..3fa422a579 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -287,7 +287,11 @@ void Codegen::statement(Statement *ast) RegisterScope scope(this); bytecodeGenerator->setLocation(ast->firstSourceLocation()); + + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); accept(ast); + qSwap(_volataleMemoryLocations, vLocs); } void Codegen::statement(ExpressionNode *ast) @@ -299,10 +303,16 @@ void Codegen::statement(ExpressionNode *ast) } else { Result r(nx); qSwap(_expr, r); + VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast); + qSwap(_volataleMemoryLocations, vLocs); + accept(ast); + + qSwap(_volataleMemoryLocations, vLocs); + qSwap(_expr, r); + if (hasError) return; - qSwap(_expr, r); if (r.result().loadTriggersSideEffect()) r.result().loadInAccumulator(); // triggers side effects } @@ -1482,7 +1492,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) const int argIdx = c->findArgument(name); if (argIdx != -1) { Q_ASSERT(!c->argumentsCanEscape && c->usesArgumentsObject != Context::ArgumentsObjectUsed); - return Reference::fromArgument(this, argIdx); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); } c = c->parent; } @@ -1510,7 +1520,7 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs) return Reference::fromScopedLocal(this, idx, scope); } else { Q_ASSERT(scope == 0); - return Reference::fromArgument(this, argIdx); + return Reference::fromArgument(this, argIdx, _volataleMemoryLocations.isVolatile(name)); } } @@ -2848,6 +2858,97 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading() return result; } +class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor +{ + VolatileMemoryLocations locs; + +public: + Codegen::VolatileMemoryLocations scan(AST::Node *s) + { + s->accept(this); + return locs; + } + + bool visit(ArrayMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(FieldMemberExpression *) Q_DECL_OVERRIDE + { + locs.setAllVolatile(); + return false; + } + + bool visit(PostIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PostDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->base); + return false; + } + + bool visit(PreIncrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(PreDecrementExpression *e) Q_DECL_OVERRIDE + { + collectIdentifiers(locs.specificLocations, e->expression); + return false; + } + + bool visit(BinaryExpression *e) Q_DECL_OVERRIDE + { + switch (e->op) { + case QSOperator::InplaceAnd: + case QSOperator::InplaceSub: + case QSOperator::InplaceDiv: + case QSOperator::InplaceAdd: + case QSOperator::InplaceLeftShift: + case QSOperator::InplaceMod: + case QSOperator::InplaceMul: + case QSOperator::InplaceOr: + case QSOperator::InplaceRightShift: + case QSOperator::InplaceURightShift: + case QSOperator::InplaceXor: + collectIdentifiers(locs.specificLocations, e); + return false; + + default: + return true; + } + } + +private: + void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const { + class Collector: public QQmlJS::AST::Visitor { + QVector<QStringView> &ids; + public: + Collector(QVector<QStringView> &ids): ids(ids) {} + virtual bool visit(IdentifierExpression *ie) { + ids.append(ie->name); + return false; + } + }; + Collector collector(ids); + node->accept(&collector); + } +}; + +Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const +{ + VolatileMemoryLocationScanner scanner; + return scanner.scan(ast); +} + #ifndef V4_BOOTSTRAP @@ -2954,6 +3055,7 @@ Codegen::Reference &Codegen::Reference::operator =(const Reference &other) codegen = other.codegen; isReadonly = other.isReadonly; stackSlotIsLocalOrArgument = other.stackSlotIsLocalOrArgument; + isVolatile = other.isVolatile; global = other.global; return *this; } @@ -3044,7 +3146,7 @@ void Codegen::Reference::storeOnStack(int slotIndex) const Codegen::Reference Codegen::Reference::doStoreOnStack(int slotIndex) const { - if (isStackSlot() && slotIndex == -1) + if (isStackSlot() && slotIndex == -1 && !(stackSlotIsLocalOrArgument && isVolatile)) return *this; if (isStackSlot()) { // temp-to-temp move diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 78f804013f..881fa08ff8 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -103,6 +103,21 @@ public: CompilationMode mode = GlobalCode); public: + class VolatileMemoryLocationScanner; + class VolatileMemoryLocations { + friend VolatileMemoryLocationScanner; + bool allVolatile = false; + QVector<QStringView> specificLocations; + public: + bool isVolatile(const QStringView &name) { + if (allVolatile) + return true; + return specificLocations.contains(name); + } + + void add(const QStringRef &name) { if (!allVolatile) specificLocations.append(name); } + void setAllVolatile() { allVolatile = true; } + }; class RValue { Codegen *codegen; enum Type { @@ -215,10 +230,11 @@ public: r.stackSlotIsLocalOrArgument = isLocal; return r; } - static Reference fromArgument(Codegen *cg, int index) { + static Reference fromArgument(Codegen *cg, int index, bool isVolatile) { Reference r(cg, StackSlot); r.theStackSlot = Moth::StackSlot::createRegister(index + sizeof(CallData)/sizeof(Value) - 1); r.stackSlotIsLocalOrArgument = true; + r.isVolatile = isVolatile; return r; } static Reference fromScopedLocal(Codegen *cg, int index, int scope) { @@ -327,6 +343,7 @@ public: mutable bool isArgOrEval = false; bool isReadonly = false; bool stackSlotIsLocalOrArgument = false; + bool isVolatile = false; bool global = false; Codegen *codegen; @@ -625,6 +642,7 @@ protected: friend struct ControlFlowCatch; friend struct ControlFlowFinally; Result _expr; + VolatileMemoryLocations _volataleMemoryLocations; Module *_module; int _returnAddress; Context *_context; @@ -638,6 +656,9 @@ protected: bool _fileNameIsUrl; bool hasError; QList<QQmlJS::DiagnosticMessage> _errors; + +private: + VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const; }; } diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp index 5f6b0a056e..7583579de6 100644 --- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp +++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp @@ -343,6 +343,7 @@ private slots: void freeze_empty_object(); void singleBlockLoops(); void qtbug_60547(); + void delayLoadingArgs(); private: // static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter); @@ -8371,6 +8372,13 @@ void tst_qqmlecmascript::qtbug_60547() QCOMPARE(object->property("counter"), QVariant(int(1))); } +void tst_qqmlecmascript::delayLoadingArgs() +{ + QJSEngine engine; + QJSValue ret = engine.evaluate("(function(x){return x + (x+=2)})(20)"); + QCOMPARE(ret.toInt(), 42); // esp. not 44. +} + QTEST_MAIN(tst_qqmlecmascript) #include "tst_qqmlecmascript.moc" |