aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/compiler/qv4codegen.cpp110
-rw-r--r--src/qml/compiler/qv4codegen_p.h23
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp8
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"