aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorQt Forward Merge Bot <qt_forward_merge_bot@qt-project.org>2019-03-16 01:00:16 +0100
committerUlf Hermann <ulf.hermann@qt.io>2019-03-18 13:49:23 +0100
commite912c646e22f845cf14f82532fb2c05628371974 (patch)
tree55f82c3f5b3d30f0d13cc0b92bacddf7f66e0baa
parentdc3e3090d21339d78abc706369117b3396c843af (diff)
parentf396cc753da75c68c6a501379a18df3099697f42 (diff)
Merge remote-tracking branch 'origin/5.12' into 5.13
Conflicts: src/qml/compiler/qv4codegen.cpp Change-Id: I66b7db42bf208855889094ace0267326595ce03c
-rw-r--r--dist/changes-5.12.2101
-rw-r--r--src/qml/compiler/qqmlirbuilder.cpp2
-rw-r--r--src/qml/compiler/qqmlirbuilder_p.h6
-rw-r--r--src/qml/compiler/qv4codegen.cpp262
-rw-r--r--src/qml/compiler/qv4codegen_p.h120
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions.cpp27
-rw-r--r--src/qml/compiler/qv4compilerscanfunctions_p.h7
-rw-r--r--src/qml/jsruntime/qv4lookup.cpp80
-rw-r--r--src/qml/jsruntime/qv4object.cpp90
-rw-r--r--src/qml/jsruntime/qv4object_p.h8
-rw-r--r--src/qml/jsruntime/qv4runtimecodegen_p.h1
-rw-r--r--src/qml/jsruntime/qv4stringobject.cpp2
-rw-r--r--src/qml/jsruntime/qv4vme_moth.cpp24
-rw-r--r--src/qml/jsruntime/qv4vtable_p.h14
-rw-r--r--src/qml/parser/qqmljs.g10
-rw-r--r--src/qml/parser/qqmljsast.cpp15
-rw-r--r--src/qml/parser/qqmljsast_p.h24
-rw-r--r--src/qml/parser/qqmljsastvisitor.cpp2
-rw-r--r--src/qml/parser/qqmljsastvisitor_p.h36
-rw-r--r--src/qml/util/qqmladaptormodel.cpp6
-rw-r--r--src/quick/doc/src/concepts/statesanimations/states.qdoc2
-rw-r--r--tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp22
-rw-r--r--tests/auto/qml/qqmlparser/tst_qqmlparser.cpp5
-rw-r--r--tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp6
-rw-r--r--tools/qmlcachegen/qmlcachegen.cpp13
25 files changed, 558 insertions, 327 deletions
diff --git a/dist/changes-5.12.2 b/dist/changes-5.12.2
new file mode 100644
index 0000000000..b092aed80d
--- /dev/null
+++ b/dist/changes-5.12.2
@@ -0,0 +1,101 @@
+Qt 5.12.2 is a bug-fix release. It maintains both forward and backward
+compatibility (source and binary) with Qt 5.12.0 through 5.12.1.
+
+For more details, refer to the online documentation included in this
+distribution. The documentation is also available online:
+
+https://doc.qt.io/qt-5/index.html
+
+The Qt version 5.12 series is binary compatible with the 5.11.x series.
+Applications compiled for 5.11 will continue to run with 5.12.
+
+Some of the changes listed in this file include issue tracking numbers
+corresponding to tasks in the Qt Bug Tracker:
+
+https://bugreports.qt.io/
+
+Each of these identifiers can be entered in the bug tracker to obtain more
+information about a particular change.
+
+****************************************************************************
+* QtQml *
+****************************************************************************
+
+ - Important Behavior Changes:
+ * The parameters passed to C++ functions from QML are now checked for
+ compatibility with the expected arguments. If they cannot be
+ converted, a warning is printed. In future versions of Qt a type error
+ will be thrown in JavaScript and the function will not be invoked.
+
+ * [QTBUG-73239] Removed revisions from the new Qt.labs.settings methods
+ and properties that were introduced in 5.12. Qt.labs plugins are
+ intended to always have revision 1.0 until they graduate.
+
+ - QQmlApplicationEngine:
+ * [QTBUG-73649] QQmlApplicationEngine connects quit() and exit() signals
+ with queued connections to avoid problems with AutoConnection, when
+ connecting to QCoreApplication slots.
+
+ - [QTBUG-69340] QML cache files are not unnecessarily re-generated.
+ - [QTBUG-71325] Fixed a crash in V4 string to number conversion on 32-bit
+ platforms.
+ - [QTBUG-72137] Fixed a crash in QML garbage collector when accessing
+ other items from Component.onDestruction.
+ - [QTBUG-72352] QML can be built with -no-feature-translation.
+ - [QTBUG-72407] We now annotate stack traces when frames are elided
+ through tail calls.
+ - [QTBUG-72734] Fixed a crash in the parser on certain kinds of bad input.
+ - [QTBUG-72858] Exception handlers are correctly scoped for try blocks and
+ for "for ... in" loops.
+ - [QTBUG-72908] QML can be built with -c++std=c++11 again.
+ - [QTBUG-72972] QQmlMetaType deletes attached properties in its destructor
+ to avoid a crash.
+ - [QTBUG-73009] Fixed a crash with qt.qml.binding.removal.info=true.
+ - [QTBUG-73013] If a signal sender is deleted during the handling of the
+ signal in QML, the QML engine won't crash anymore.
+ - [QTBUG-73152] Brought behavior of String.replace() in line with other
+ JS engines: "x".replace("x", "$1") gives "$1" in both JSC and V8, as there
+ are no captures that could be used as a replacement for $1. Two-digit
+ captures ($nm) get applied if $nm captures exist. If there are less than nm
+ but more than n captures available $n is replaced by the n'th capture and m
+ is copied over verbatim.
+ - [QTBUG-73425] Fixed allocation of large arrays at startup.
+ - [QTBUG-73733] Fixed an access-after-delete crash in DelegateModel.
+ - [QTBUG-73734] When a Q_GADGET type, marked as a primitive type with
+ Q_DECLARE_METATYPE, is emitted with a signal, it can now be accessed in QML.
+ - [QTBUG-73750] Fixed undefined Q_ENUM value in QML Connections object.
+ - [QTBUG-73821] Fixed a failing assert on 32bit platforms.
+ - The JIT compiler is disabled for the IPL32 (or X32) ABI. It did not work
+ before.
+ - The tail call optimization correctly counts method arguments on 32bit
+ platforms now.
+ - The JavaScript engine now tolerates UINT_MAX as array index.
+
+****************************************************************************
+* QtQuick *
+****************************************************************************
+
+ - TextInput/security:
+ * When the TextInput is used for password input, preallocate a buffer
+ for the string that stores the entered value and zero-out the string
+ on TextInput destruction to avoid leaking sensitive data to process
+ memory
+
+ - [QTBUG-63271] If a MouseArea sets itself invisible or disabled while
+ handling a mouse press, it does not acquire the exclusive grab
+ - [QTBUG-71887] TapHandler now consistently forgets touchpoints that occur
+ outside its parent's bounds. This eliminates the warning "pointId is
+ missing from current event, but was neither canceled nor released" when
+ tapping quickly and having some of the taps fall out of bounds.
+ (The warning still exists though, in case there are other scenarios where
+ Handlers remember "wanting" certain touchpoints and then they go missing.)
+ - [QTBUG-72822] PinchHandler now correctly holds its target in place when
+ axes are disabled.
+ - [QTBUG-71918] PointerHandlers are declared as direct children of
+ Flickable (ListView, TableView etc.) now get the pointer events properly.
+ - [QTBUG-42155] Canvas now handles switching between object and string
+ based colors
+ - [QTBUG-73113] Fixed a crash when reparenting QML Canvas items
+ - [QTBUG-73013] Fixed a crash in QuickView on setSource while deleting
+ the sender.
+ - [QT3DS-1419] Improved quality of Qt 3D Studio text labels.
diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp
index ea5efcfc66..7bd5aeb188 100644
--- a/src/qml/compiler/qqmlirbuilder.cpp
+++ b/src/qml/compiler/qqmlirbuilder.cpp
@@ -566,7 +566,7 @@ bool IRBuilder::visit(QQmlJS::AST::UiQualifiedId *id)
void IRBuilder::accept(QQmlJS::AST::Node *node)
{
- QQmlJS::AST::Node::acceptChild(node, this);
+ QQmlJS::AST::Node::accept(node, this);
}
bool IRBuilder::defineQMLObject(int *objectIndex, QQmlJS::AST::UiQualifiedId *qualifiedTypeNameId, const QQmlJS::AST::SourceLocation &location, QQmlJS::AST::UiObjectInitializer *initializer, Object *declarationsOverride)
diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h
index fd607aee8d..7c5944931e 100644
--- a/src/qml/compiler/qqmlirbuilder_p.h
+++ b/src/qml/compiler/qqmlirbuilder_p.h
@@ -435,6 +435,12 @@ public:
bool visit(QQmlJS::AST::UiScriptBinding *ast) override;
bool visit(QQmlJS::AST::UiSourceElement *ast) override;
+ void throwRecursionDepthError() override
+ {
+ recordError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
+
void accept(QQmlJS::AST::Node *node);
// returns index in _objects
diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp
index ac763e592f..009545eceb 100644
--- a/src/qml/compiler/qv4codegen.cpp
+++ b/src/qml/compiler/qv4codegen.cpp
@@ -100,9 +100,10 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict)
, hasError(false)
{
jsUnitGenerator->codeGeneratorName = QStringLiteral("moth");
+ pushExpr();
}
-const char *globalNames[] = {
+const char *Codegen::s_globalNames[] = {
"isNaN",
"parseFloat",
"String",
@@ -182,7 +183,7 @@ void Codegen::generateFromProgram(const QString &fileName,
//
// Since this can be called from the loader thread we can't get the list
// directly from the engine, so let's hardcode the most important ones here
- for (const char **g = globalNames; *g != nullptr; ++g)
+ for (const char **g = s_globalNames; *g != nullptr; ++g)
m_globalNames << QString::fromLatin1(*g);
}
@@ -264,7 +265,7 @@ Context *Codegen::enterBlock(Node *node)
Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
{
if (hasError)
- return _expr.result();
+ return exprResult();
if (expr.isConstant()) {
auto v = Value::fromReturnedValue(expr.constant);
@@ -310,7 +311,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
return Reference::fromAccumulator(this);
}
case PostIncrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::UPlus uplus;
@@ -330,13 +331,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Increment inc = {};
bytecodeGenerator->addTracingInstruction(inc);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
}
case PostDecrement:
- if (!_expr.accept(nx) || requiresReturnValue) {
+ if (!exprAccept(nx) || requiresReturnValue) {
Reference e = expr.asLValue();
e.loadInAccumulator();
Instruction::UPlus uplus;
@@ -356,7 +357,7 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
e.loadInAccumulator();
Instruction::Decrement dec = {};
bytecodeGenerator->addTracingInstruction(dec);
- if (_expr.accept(nx))
+ if (exprAccept(nx))
return e.storeConsumeAccumulator();
else
return e.storeRetainAccumulator();
@@ -368,22 +369,13 @@ Codegen::Reference Codegen::unop(UnaryOperation op, const Reference &expr)
void Codegen::addCJump()
{
- bytecodeGenerator->addCJumpInstruction(_expr.trueBlockFollowsCondition(),
- _expr.iftrue(), _expr.iffalse());
-}
-
-void Codegen::accept(Node *node)
-{
- if (hasError)
- return;
-
- if (node)
- node->accept(this);
+ const Result &expression = currentExpr();
+ bytecodeGenerator->addCJumpInstruction(expression.trueBlockFollowsCondition(),
+ expression.iftrue(), expression.iffalse());
}
void Codegen::statement(Statement *ast)
{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
bytecodeGenerator->setLocation(ast->firstSourceLocation());
@@ -399,23 +391,21 @@ void Codegen::statement(ExpressionNode *ast)
if (! ast) {
return;
} else {
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
RegisterScope scope(this);
- Result r(nx);
- qSwap(_expr, r);
+ pushExpr(Result(nx));
VolatileMemoryLocations vLocs = scanVolatileMemoryLocations(ast);
qSwap(_volatileMemoryLocations, vLocs);
accept(ast);
qSwap(_volatileMemoryLocations, vLocs);
- qSwap(_expr, r);
+ Reference result = popResult();
if (hasError)
return;
- if (r.result().loadTriggersSideEffect())
- r.result().loadInAccumulator(); // triggers side effects
+ if (result.loadTriggersSideEffect())
+ result.loadInAccumulator(); // triggers side effects
}
}
@@ -428,11 +418,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
if (!ast)
return;
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r(iftrue, iffalse, trueBlockFollowsCondition);
- qSwap(_expr, r);
+ pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition));
accept(ast);
- qSwap(_expr, r);
+ Result r = popExpr();
if (hasError)
return;
@@ -450,18 +438,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift
}
}
-Codegen::Reference Codegen::expression(ExpressionNode *ast)
-{
- RecursionDepthCheck depthCheck(this, ast->lastSourceLocation());
- Result r;
- if (ast) {
- qSwap(_expr, r);
- accept(ast);
- qSwap(_expr, r);
- }
- return r.result();
-}
-
void Codegen::program(Program *ast)
{
if (ast) {
@@ -875,17 +851,13 @@ bool Codegen::visit(ExportDeclaration *ast)
Reference exportedValue;
if (auto *fdecl = AST::cast<FunctionDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<FunctionExpression*>(fdecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (auto *classDecl = AST::cast<ClassDeclaration*>(ast->variableStatementOrDeclaration)) {
- Result r;
- qSwap(_expr, r);
+ pushExpr();
visit(static_cast<ClassExpression*>(classDecl));
- qSwap(_expr, r);
- exportedValue = r.result();
+ exportedValue = popResult();
} else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) {
exportedValue = expression(expr);
}
@@ -1068,7 +1040,7 @@ bool Codegen::visit(ClassExpression *ast)
(void) ctor.storeRetainAccumulator();
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1151,7 +1123,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
if (!it) {
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
Q_ASSERT(it->element && it->element->type == PatternElement::SpreadElement);
@@ -1245,7 +1217,7 @@ bool Codegen::visit(ArrayPattern *ast)
}
array.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -1261,7 +1233,7 @@ bool Codegen::visit(ArrayMemberExpression *ast)
return false;
if (base.isSuper()) {
Reference index = expression(ast->expression).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(index));
+ setExprResult(Reference::fromSuperProperty(index));
return false;
}
base = base.storeOnStack();
@@ -1271,17 +1243,17 @@ bool Codegen::visit(ArrayMemberExpression *ast)
QString s = str->value.toString();
uint arrayIndex = QV4::String::toArrayIndex(s);
if (arrayIndex == UINT_MAX) {
- _expr.setResult(Reference::fromMember(base, str->value.toString()));
+ setExprResult(Reference::fromMember(base, str->value.toString()));
return false;
}
Reference index = Reference::fromConst(this, QV4::Encode(arrayIndex));
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
Reference index = expression(ast->expression);
if (hasError)
return false;
- _expr.setResult(Reference::fromSubscript(base, index));
+ setExprResult(Reference::fromSubscript(base, index));
return false;
}
@@ -1312,12 +1284,13 @@ bool Codegen::visit(BinaryExpression *ast)
TailCallBlocker blockTailCalls(this);
if (ast->op == QSOperator::And) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iftrue = bytecodeGenerator->newLabel();
- condition(ast->left, &iftrue, _expr.iffalse(), true);
+ condition(ast->left, &iftrue, currentExpr().iffalse(), true);
iftrue.link();
blockTailCalls.unblock();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iftrue = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1339,15 +1312,16 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Or) {
- if (_expr.accept(cx)) {
+ if (exprAccept(cx)) {
auto iffalse = bytecodeGenerator->newLabel();
- condition(ast->left, _expr.iftrue(), &iffalse, false);
+ condition(ast->left, currentExpr().iftrue(), &iffalse, false);
iffalse.link();
- condition(ast->right, _expr.iftrue(), _expr.iffalse(), _expr.trueBlockFollowsCondition());
+ const Result &expr = currentExpr();
+ condition(ast->right, expr.iftrue(), expr.iffalse(), expr.trueBlockFollowsCondition());
} else {
auto iffalse = bytecodeGenerator->newLabel();
auto endif = bytecodeGenerator->newLabel();
@@ -1369,7 +1343,7 @@ bool Codegen::visit(BinaryExpression *ast)
endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
} else if (ast->op == QSOperator::Assign) {
@@ -1380,9 +1354,9 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
right = right.storeOnStack();
destructurePattern(p, right);
- if (!_expr.accept(nx)) {
+ if (!exprAccept(nx)) {
right.loadInAccumulator();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
return false;
}
@@ -1402,10 +1376,10 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
r.loadInAccumulator();
- if (_expr.accept(nx))
- _expr.setResult(left.storeConsumeAccumulator());
+ if (exprAccept(nx))
+ setExprResult(left.storeConsumeAccumulator());
else
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
return false;
}
@@ -1448,7 +1422,7 @@ bool Codegen::visit(BinaryExpression *ast)
return false;
binopHelper(baseOp(ast->op), tempLeft, right).loadInAccumulator();
- _expr.setResult(left.storeRetainAccumulator());
+ setExprResult(left.storeRetainAccumulator());
break;
}
@@ -1460,7 +1434,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right = expression(ast->right);
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), right, left));
break;
}
// intentional fall-through!
@@ -1486,7 +1460,7 @@ bool Codegen::visit(BinaryExpression *ast)
Reference right;
if (AST::NumericLiteral *rhs = AST::cast<AST::NumericLiteral *>(ast->right)) {
visit(rhs);
- right = _expr.result();
+ right = exprResult();
} else {
left = left.storeOnStack(); // force any loads of the lhs, so the rhs won't clobber it
right = expression(ast->right);
@@ -1494,7 +1468,7 @@ bool Codegen::visit(BinaryExpression *ast)
if (hasError)
return false;
- _expr.setResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
+ setExprResult(binopHelper(static_cast<QSOperator::Op>(ast->op), left, right));
break;
}
@@ -1671,7 +1645,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictEqual cmp;
@@ -1682,7 +1656,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::StrictNotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpStrictNotEqual cmp;
@@ -1693,7 +1667,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Equal: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpEq cmp;
@@ -1704,7 +1678,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::NotEqual: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpNe cmp;
@@ -1715,7 +1689,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Gt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGt cmp;
@@ -1726,7 +1700,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Ge: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpGe cmp;
@@ -1737,7 +1711,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Lt: {
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLt cmp;
@@ -1748,7 +1722,7 @@ Codegen::Reference Codegen::binopHelper(QSOperator::Op oper, Reference &left, Re
break;
}
case QSOperator::Le:
- if (_expr.accept(cx))
+ if (exprAccept(cx))
return jumpBinop(oper, left, right);
Instruction::CmpLe cmp;
@@ -1952,7 +1926,7 @@ bool Codegen::visit(CallExpression *ast)
bytecodeGenerator->addInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2045,7 +2019,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio
bytecodeGenerator->addTracingInstruction(call);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
Codegen::Arguments Codegen::pushArgs(ArgumentList *args)
@@ -2141,7 +2115,7 @@ bool Codegen::visit(ConditionalExpression *ast)
ko.loadInAccumulator();
jump_endif.link();
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2171,7 +2145,7 @@ bool Codegen::visit(DeleteExpression *ast)
throwSyntaxError(ast->deleteToken, QStringLiteral("Delete of an unqualified identifier in strict mode."));
return false;
}
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
case Reference::Name: {
if (_context->isStrict) {
@@ -2181,7 +2155,7 @@ bool Codegen::visit(DeleteExpression *ast)
Instruction::DeleteName del;
del.name = expr.nameAsIndex();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Member: {
@@ -2196,7 +2170,7 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.propertyBase.stackSlot();
del.index = index.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
case Reference::Subscript: {
@@ -2206,14 +2180,14 @@ bool Codegen::visit(DeleteExpression *ast)
del.base = expr.elementBase;
del.index = expr.elementSubscript.stackSlot();
bytecodeGenerator->addInstruction(del);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
default:
break;
}
// [[11.4.1]] Return true if it's not a reference
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2222,7 +2196,7 @@ bool Codegen::visit(FalseLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(false)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(false)));
return false;
}
@@ -2231,7 +2205,7 @@ bool Codegen::visit(SuperLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromSuper(this));
+ setExprResult(Reference::fromSuper(this));
return false;
}
@@ -2249,12 +2223,12 @@ bool Codegen::visit(FieldMemberExpression *ast)
if (_context->isArrowFunction || _context->contextType == ContextType::Eval) {
Reference r = referenceForName(QStringLiteral("new.target"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
Reference r = Reference::fromStackSlot(this, CallData::NewTarget);
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
}
@@ -2267,10 +2241,10 @@ bool Codegen::visit(FieldMemberExpression *ast)
load.stringId = registerString(ast->name.toString());
bytecodeGenerator->addInstruction(load);
Reference property = Reference::fromAccumulator(this).storeOnStack();
- _expr.setResult(Reference::fromSuperProperty(property));
+ setExprResult(Reference::fromSuperProperty(property));
return false;
}
- _expr.setResult(Reference::fromMember(base, ast->name.toString()));
+ setExprResult(Reference::fromMember(base, ast->name.toString()));
return false;
}
@@ -2280,12 +2254,15 @@ bool Codegen::visit(TaggedTemplate *ast)
return false;
RegisterScope scope(this);
+ return handleTaggedTemplate(expression(ast->base), ast);
+}
- int functionObject = -1, thisObject = -1;
-
- Reference base = expression(ast->base);
+bool Codegen::handleTaggedTemplate(Reference base, TaggedTemplate *ast)
+{
if (hasError)
return false;
+
+ int functionObject = -1, thisObject = -1;
switch (base.type) {
case Reference::Member:
case Reference::Subscript:
@@ -2346,7 +2323,7 @@ bool Codegen::visit(FunctionExpression *ast)
if (hasError)
return false;
loadClosure(function);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2412,7 +2389,7 @@ bool Codegen::visit(IdentifierExpression *ast)
if (hasError)
return false;
- _expr.setResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
+ setExprResult(referenceForName(ast->name.toString(), false, ast->firstSourceLocation()));
return false;
}
@@ -2462,7 +2439,7 @@ void Codegen::handleConstruct(const Reference &base, ArgumentList *arguments)
// set the result up as the thisObject
Reference::fromAccumulator(this).storeOnStack(CallData::This);
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
}
bool Codegen::visit(NewExpression *ast)
@@ -2511,7 +2488,7 @@ bool Codegen::visit(NotExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Not, expression(ast->expression)));
+ setExprResult(unop(Not, expression(ast->expression)));
return false;
}
@@ -2520,10 +2497,10 @@ bool Codegen::visit(NullExpression *)
if (hasError)
return false;
- if (_expr.accept(cx))
- bytecodeGenerator->jump().link(*_expr.iffalse());
+ if (exprAccept(cx))
+ bytecodeGenerator->jump().link(*currentExpr().iffalse());
else
- _expr.setResult(Reference::fromConst(this, Encode::null()));
+ setExprResult(Reference::fromConst(this, Encode::null()));
return false;
}
@@ -2533,7 +2510,7 @@ bool Codegen::visit(NumericLiteral *ast)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
+ setExprResult(Reference::fromConst(this, QV4::Encode::smallestNumber(ast->value)));
return false;
}
@@ -2645,8 +2622,7 @@ bool Codegen::visit(ObjectPattern *ast)
call.argc = argc;
call.args = Moth::StackSlot::createRegister(args);
bytecodeGenerator->addInstruction(call);
- Reference result = Reference::fromAccumulator(this);
- _expr.setResult(result);
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2665,7 +2641,7 @@ bool Codegen::visit(PostDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PostDecrement, expr));
+ setExprResult(unop(PostDecrement, expr));
return false;
}
@@ -2685,7 +2661,7 @@ bool Codegen::visit(PostIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PostIncrement, expr));
+ setExprResult(unop(PostIncrement, expr));
return false;
}
@@ -2703,7 +2679,7 @@ bool Codegen::visit(PreDecrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken))
return false;
- _expr.setResult(unop(PreDecrement, expr));
+ setExprResult(unop(PreDecrement, expr));
return false;
}
@@ -2722,7 +2698,7 @@ bool Codegen::visit(PreIncrementExpression *ast)
if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken))
return false;
- _expr.setResult(unop(PreIncrement, expr));
+ setExprResult(unop(PreIncrement, expr));
return false;
}
@@ -2733,7 +2709,7 @@ bool Codegen::visit(RegExpLiteral *ast)
auto r = Reference::fromStackSlot(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::MoveRegExp instr;
instr.regExpId = jsUnitGenerator->registerRegExp(ast);
@@ -2749,7 +2725,7 @@ bool Codegen::visit(StringLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
Instruction::LoadRuntimeString instr;
instr.stringId = registerString(ast->value.toString());
@@ -2799,7 +2775,7 @@ bool Codegen::visit(TemplateLiteral *ast)
auto r = Reference::fromAccumulator(this);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
@@ -2812,10 +2788,10 @@ bool Codegen::visit(ThisExpression *)
if (_context->isArrowFunction) {
Reference r = referenceForName(QStringLiteral("this"), false);
r.isReadonly = true;
- _expr.setResult(r);
+ setExprResult(r);
return false;
}
- _expr.setResult(Reference::fromThis(this));
+ setExprResult(Reference::fromThis(this));
return false;
}
@@ -2825,7 +2801,7 @@ bool Codegen::visit(TildeExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(Compl, expression(ast->expression)));
+ setExprResult(unop(Compl, expression(ast->expression)));
return false;
}
@@ -2834,7 +2810,7 @@ bool Codegen::visit(TrueLiteral *)
if (hasError)
return false;
- _expr.setResult(Reference::fromConst(this, QV4::Encode(true)));
+ setExprResult(Reference::fromConst(this, QV4::Encode(true)));
return false;
}
@@ -2860,7 +2836,7 @@ bool Codegen::visit(TypeOfExpression *ast)
Instruction::TypeofValue instr;
bytecodeGenerator->addInstruction(instr);
}
- _expr.setResult(Reference::fromAccumulator(this));
+ setExprResult(Reference::fromAccumulator(this));
return false;
}
@@ -2871,7 +2847,7 @@ bool Codegen::visit(UnaryMinusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UMinus, expression(ast->expression)));
+ setExprResult(unop(UMinus, expression(ast->expression)));
return false;
}
@@ -2881,7 +2857,7 @@ bool Codegen::visit(UnaryPlusExpression *ast)
return false;
TailCallBlocker blockTailCalls(this);
- _expr.setResult(unop(UPlus, expression(ast->expression)));
+ setExprResult(unop(UPlus, expression(ast->expression)));
return false;
}
@@ -2894,7 +2870,7 @@ bool Codegen::visit(VoidExpression *ast)
TailCallBlocker blockTailCalls(this);
statement(ast->expression);
- _expr.setResult(Reference::fromConst(this, Encode::undefined()));
+ setExprResult(Reference::fromConst(this, Encode::undefined()));
return false;
}
@@ -2908,7 +2884,7 @@ bool Codegen::visit(FunctionDeclaration * ast)
if (_functionContext->contextType == ContextType::Binding)
referenceForName(ast->name.toString(), true).loadInAccumulator();
- _expr.accept(nx);
+ exprAccept(nx);
return false;
}
@@ -2964,7 +2940,7 @@ bool Codegen::visit(YieldExpression *ast)
done.link();
lhsValue.loadInAccumulator();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -2975,7 +2951,7 @@ bool Codegen::visit(YieldExpression *ast)
BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume);
emitReturn(acc);
jump.link();
- _expr.setResult(acc);
+ setExprResult(acc);
return false;
}
@@ -3850,8 +3826,14 @@ QQmlRefPointer<CompiledData::CompilationUnit> Codegen::createUnitForLoading()
class Codegen::VolatileMemoryLocationScanner: protected QQmlJS::AST::Visitor
{
VolatileMemoryLocations locs;
+ Codegen *parent;
public:
+ VolatileMemoryLocationScanner(Codegen *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()),
+ parent(parent)
+ {}
+
Codegen::VolatileMemoryLocations scan(AST::Node *s)
{
s->accept(this);
@@ -3916,25 +3898,41 @@ public:
}
}
+ void throwRecursionDepthError() override
+ {
+ parent->throwRecursionDepthError();
+ }
+
private:
- void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) const {
+ void collectIdentifiers(QVector<QStringView> &ids, AST::Node *node) {
class Collector: public QQmlJS::AST::Visitor {
+ private:
QVector<QStringView> &ids;
+ VolatileMemoryLocationScanner *parent;
+
public:
- Collector(QVector<QStringView> &ids): ids(ids) {}
- virtual bool visit(IdentifierExpression *ie) {
+ Collector(QVector<QStringView> &ids, VolatileMemoryLocationScanner *parent) :
+ QQmlJS::AST::Visitor(parent->recursionDepth()), ids(ids), parent(parent)
+ {}
+
+ bool visit(IdentifierExpression *ie) final {
ids.append(ie->name);
return false;
}
+
+ void throwRecursionDepthError() final
+ {
+ parent->throwRecursionDepthError();
+ }
};
- Collector collector(ids);
+ Collector collector(ids, this);
node->accept(&collector);
}
};
-Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast) const
+Codegen::VolatileMemoryLocations Codegen::scanVolatileMemoryLocations(AST::Node *ast)
{
- VolatileMemoryLocationScanner scanner;
+ VolatileMemoryLocationScanner scanner(this);
return scanner.scan(ast);
}
diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h
index 4d7001fe64..0b25d9c53d 100644
--- a/src/qml/compiler/qv4codegen_p.h
+++ b/src/qml/compiler/qv4codegen_p.h
@@ -192,8 +192,24 @@ public:
bool isLValue() const { return !isReadonly && type > Accumulator; }
- Reference(Codegen *cg, Type type = Invalid) : type(type), constant(0), codegen(cg) {}
- Reference(): constant(0) {}
+ Reference(Codegen *cg, Type t = Invalid) : Reference()
+ {
+ type = t;
+ codegen = cg;
+ }
+
+ Reference() :
+ constant(0),
+ isArgOrEval(false),
+ isReadonly(false),
+ isReferenceToConst(false),
+ requiresTDZCheck(false),
+ subscriptRequiresTDZCheck(false),
+ stackSlotIsLocalOrArgument(false),
+ isVolatile(false),
+ global(false)
+ {}
+
Reference(const Reference &) = default;
Reference(Reference &&) = default;
Reference &operator =(const Reference &) = default;
@@ -395,16 +411,17 @@ public:
Moth::StackSlot property; // super property
};
QString name;
- mutable bool isArgOrEval = false;
- bool isReadonly = false;
- bool isReferenceToConst = false;
- bool requiresTDZCheck = false;
- bool subscriptRequiresTDZCheck = false;
- bool stackSlotIsLocalOrArgument = false;
- bool isVolatile = false;
- bool global = false;
Codegen *codegen = nullptr;
+ quint32 isArgOrEval:1;
+ quint32 isReadonly:1;
+ quint32 isReferenceToConst:1;
+ quint32 requiresTDZCheck:1;
+ quint32 subscriptRequiresTDZCheck:1;
+ quint32 stackSlotIsLocalOrArgument:1;
+ quint32 isVolatile:1;
+ quint32 global:1;
+
private:
void storeAccumulator() const;
Reference doStoreOnStack(int tempIndex) const;
@@ -499,6 +516,10 @@ protected:
void setResult(const Reference &result) {
_result = result;
}
+
+ void setResult(Reference &&result) {
+ _result = std::move(result);
+ }
};
void enterContext(AST::Node *node);
@@ -544,9 +565,22 @@ protected:
void condition(AST::ExpressionNode *ast, const BytecodeGenerator::Label *iftrue,
const BytecodeGenerator::Label *iffalse,
bool trueBlockFollowsCondition);
- Reference expression(AST::ExpressionNode *ast);
- void accept(AST::Node *node);
+ inline Reference expression(AST::ExpressionNode *ast)
+ {
+ if (!ast || hasError)
+ return Reference();
+
+ pushExpr();
+ ast->accept(this);
+ return popResult();
+ }
+
+ inline void accept(AST::Node *node)
+ {
+ if (!hasError && node)
+ node->accept(this);
+ }
void program(AST::Program *ast);
void statementList(AST::StatementList *ast);
@@ -670,6 +704,11 @@ protected:
bool throwSyntaxErrorOnEvalOrArgumentsInStrictMode(const Reference &r, const AST::SourceLocation &loc);
virtual void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail);
virtual void throwReferenceError(const AST::SourceLocation &loc, const QString &detail);
+ void throwRecursionDepthError() override
+ {
+ throwSyntaxError(AST::SourceLocation(),
+ QStringLiteral("Maximum statement or expression depth exceeded"));
+ }
public:
QList<DiagnosticMessage> errors() const;
@@ -684,6 +723,7 @@ public:
void handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject);
Arguments pushTemplateArgs(AST::TemplateLiteral *args);
+ bool handleTaggedTemplate(Reference base, AST::TaggedTemplate *ast);
void createTemplateObject(AST::TemplateLiteral *t);
void setUseFastLookups(bool b) { useFastLookups = b; }
@@ -714,13 +754,40 @@ public:
m_globalNames = globalNames;
}
+ static const char *s_globalNames[];
protected:
friend class ScanFunctions;
friend struct ControlFlow;
friend struct ControlFlowCatch;
friend struct ControlFlowFinally;
- Result _expr;
+
+ inline void setExprResult(const Reference &result) { m_expressions.back().setResult(result); }
+ inline void setExprResult(Reference &&result) { m_expressions.back().setResult(std::move(result)); }
+ inline Reference exprResult() const { return m_expressions.back().result(); }
+
+ inline bool exprAccept(Format f) { return m_expressions.back().accept(f); }
+
+ inline const Result &currentExpr() const { return m_expressions.back(); }
+
+ inline void pushExpr(Result &&expr) { m_expressions.push_back(std::move(expr)); }
+ inline void pushExpr(const Result &expr) { m_expressions.push_back(expr); }
+ inline void pushExpr() { m_expressions.emplace_back(); }
+
+ inline Result popExpr()
+ {
+ const Result result = m_expressions.back();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ inline Reference popResult() {
+ const Reference result = m_expressions.back().result();
+ m_expressions.pop_back();
+ return result;
+ }
+
+ std::vector<Result> m_expressions;
VolatileMemoryLocations _volatileMemoryLocations;
Module *_module;
int _returnAddress;
@@ -769,33 +836,8 @@ protected:
bool _onoff;
};
- class RecursionDepthCheck {
- public:
- RecursionDepthCheck(Codegen *cg, const AST::SourceLocation &loc)
- : _cg(cg)
- {
-#ifdef QT_NO_DEBUG
- const int depthLimit = 4000; // limit to ~1000 deep
-#else
- const int depthLimit = 1000; // limit to ~250 deep
-#endif // QT_NO_DEBUG
-
- ++_cg->_recursionDepth;
- if (_cg->_recursionDepth > depthLimit)
- _cg->throwSyntaxError(loc, QStringLiteral("Maximum statement or expression depth exceeded"));
- }
-
- ~RecursionDepthCheck()
- { --_cg->_recursionDepth; }
-
- private:
- Codegen *_cg;
- };
- int _recursionDepth = 0;
- friend class RecursionDepthCheck;
-
private:
- VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast) const;
+ VolatileMemoryLocations scanVolatileMemoryLocations(AST::Node *ast);
void handleConstruct(const Reference &base, AST::ArgumentList *args);
};
diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp
index e0eaa8867b..04593f202a 100644
--- a/src/qml/compiler/qv4compilerscanfunctions.cpp
+++ b/src/qml/compiler/qv4compilerscanfunctions.cpp
@@ -57,7 +57,8 @@ using namespace QV4::Compiler;
using namespace QQmlJS::AST;
ScanFunctions::ScanFunctions(Codegen *cg, const QString &sourceCode, ContextType defaultProgramType)
- : _cg(cg)
+ : QQmlJS::AST::Visitor(cg->recursionDepth())
+ , _cg(cg)
, _sourceCode(sourceCode)
, _context(nullptr)
, _allowFuncDecls(true)
@@ -96,25 +97,6 @@ void ScanFunctions::leaveEnvironment()
_context = _contextStack.isEmpty() ? nullptr : _contextStack.top();
}
-bool ScanFunctions::preVisit(Node *ast)
-{
- if (_cg->hasError)
- return false;
- ++_recursionDepth;
-
- if (_recursionDepth > 1000) {
- _cg->throwSyntaxError(ast->lastSourceLocation(), QStringLiteral("Maximum statement or expression depth exceeded"));
- return false;
- }
-
- return true;
-}
-
-void ScanFunctions::postVisit(Node *)
-{
- --_recursionDepth;
-}
-
void ScanFunctions::checkDirectivePrologue(StatementList *ast)
{
for (StatementList *it = ast; it; it = it->next) {
@@ -893,3 +875,8 @@ void ScanFunctions::calcEscapingVariables()
}
}
}
+
+void ScanFunctions::throwRecursionDepthError()
+{
+ _cg->throwRecursionDepthError();
+}
diff --git a/src/qml/compiler/qv4compilerscanfunctions_p.h b/src/qml/compiler/qv4compilerscanfunctions_p.h
index 28ad846bcd..0f7bf1818a 100644
--- a/src/qml/compiler/qv4compilerscanfunctions_p.h
+++ b/src/qml/compiler/qv4compilerscanfunctions_p.h
@@ -96,9 +96,6 @@ protected:
using Visitor::visit;
using Visitor::endVisit;
- bool preVisit(AST::Node *ast) override;
- void postVisit(AST::Node *) override;
-
void checkDirectivePrologue(AST::StatementList *ast);
void checkName(const QStringRef &name, const AST::SourceLocation &loc);
@@ -160,6 +157,8 @@ protected:
bool visit(AST::WithStatement *ast) override;
void endVisit(AST::WithStatement *ast) override;
+ void throwRecursionDepthError() override;
+
protected:
bool enterFunction(AST::Node *ast, const QString &name, AST::FormalParameterList *formals, AST::StatementList *body, bool enterName);
@@ -173,8 +172,6 @@ protected:
bool _allowFuncDecls;
ContextType defaultProgramType;
- unsigned _recursionDepth = 0;
-
private:
static constexpr AST::Node *astNodeForGlobalEnvironment = nullptr;
};
diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp
index 994daa864b..54bce7d7b3 100644
--- a/src/qml/jsruntime/qv4lookup.cpp
+++ b/src/qml/jsruntime/qv4lookup.cpp
@@ -69,37 +69,7 @@ void Lookup::resolveProtoGetter(PropertyKey name, const Heap::Object *proto)
ReturnedValue Lookup::resolveGetter(ExecutionEngine *engine, const Object *object)
{
- Heap::Object *obj = object->d();
- PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
- if (name.isArrayIndex()) {
- indexedLookup.index = name.asArrayIndex();
- getter = getterIndexed;
- return getter(this, engine, *object);
- }
-
- auto index = obj->internalClass->findValueOrGetter(name);
- if (index.isValid()) {
- PropertyAttributes attrs = index.attrs;
- uint nInline = obj->vtable()->nInlineProperties;
- if (attrs.isData()) {
- if (index.index < obj->vtable()->nInlineProperties) {
- index.index += obj->vtable()->inlinePropertyOffset;
- getter = getter0Inline;
- } else {
- index.index -= nInline;
- getter = getter0MemberData;
- }
- } else {
- getter = getterAccessor;
- }
- objectLookup.ic = obj->internalClass;
- objectLookup.offset = index.index;
- return getter(this, engine, *object);
- }
-
- protoLookup.protoId = obj->internalClass->protoId;
- resolveProtoGetter(name, obj->prototype());
- return getter(this, engine, *object);
+ return object->resolveLookupGetter(engine, this);
}
ReturnedValue Lookup::resolvePrimitiveGetter(ExecutionEngine *engine, const Value &object)
@@ -409,7 +379,7 @@ ReturnedValue Lookup::getterIndexed(Lookup *l, ExecutionEngine *engine, const Va
ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId)
return l->primitiveLookup.data->asReturnedValue();
@@ -420,7 +390,7 @@ ReturnedValue Lookup::primitiveGetterProto(Lookup *l, ExecutionEngine *engine, c
ReturnedValue Lookup::primitiveGetterAccessor(Lookup *l, ExecutionEngine *engine, const Value &object)
{
- if (object.type() == l->primitiveLookup.type) {
+ if (object.type() == l->primitiveLookup.type && !object.isObject()) {
Heap::Object *o = l->primitiveLookup.proto;
if (l->primitiveLookup.protoId == o->internalClass->protoId) {
const Value *getter = l->primitiveLookup.data;
@@ -473,49 +443,7 @@ ReturnedValue Lookup::globalGetterProtoAccessor(Lookup *l, ExecutionEngine *engi
bool Lookup::resolveSetter(ExecutionEngine *engine, Object *object, const Value &value)
{
- Scope scope(engine);
- ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]);
-
- Heap::InternalClass *c = object->internalClass();
- PropertyKey key = name->toPropertyKey();
- auto idx = c->findValueOrSetter(key);
- if (idx.isValid()) {
- if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
- Q_ASSERT(!idx.attrs.isAccessor());
- setter = arrayLengthSetter;
- return setter(this, engine, *object, value);
- } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
- objectLookup.ic = object->internalClass();
- objectLookup.offset = idx.index;
- setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
- return setter(this, engine, *object, value);
- } else {
- // ### handle setter
- setter = setterFallback;
- }
- return setter(this, engine, *object, value);
- }
-
- insertionLookup.protoId = c->protoId;
- if (!object->put(key, value)) {
- setter = Lookup::setterFallback;
- return false;
- }
-
- if (object->internalClass() == c) {
- // ### setter in the prototype, should handle this
- setter = setterFallback;
- return true;
- }
- idx = object->internalClass()->findValueOrSetter(key);
- if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
- setter = setterFallback;
- return false;
- }
- insertionLookup.newClass = object->internalClass();
- insertionLookup.offset = idx.index;
- setter = setterInsert;
- return true;
+ return object->resolveLookupSetter(engine, this, value);
}
bool Lookup::setterGeneric(Lookup *l, ExecutionEngine *engine, Value &object, const Value &value)
diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp
index 3d2d54f651..7dd0a247d6 100644
--- a/src/qml/jsruntime/qv4object.cpp
+++ b/src/qml/jsruntime/qv4object.cpp
@@ -320,8 +320,11 @@ bool Object::virtualDeleteProperty(Managed *m, PropertyKey id)
PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, PropertyAttributes *attrs)
{
if (arrayIndex != UINT_MAX && o->arrayData()) {
- if (!arrayIndex)
- arrayNode = o->sparseBegin();
+ SparseArrayNode *arrayNode = nullptr;
+ if (o->arrayType() == Heap::ArrayData::Sparse) {
+ SparseArray *sparse = o->arrayData()->sparse;
+ arrayNode = arrayIndex ? sparse->lowerBound(arrayIndex) : sparse->begin();
+ }
// sparse arrays
if (arrayNode) {
@@ -339,7 +342,6 @@ PropertyKey ObjectOwnPropertyKeyIterator::next(const Object *o, Property *pd, Pr
*attrs = a;
return PropertyKey::fromArrayIndex(k);
}
- arrayNode = nullptr;
arrayIndex = UINT_MAX;
}
// dense arrays
@@ -734,6 +736,88 @@ ReturnedValue Object::virtualInstanceOf(const Object *typeObject, const Value &v
return checkedInstanceOf(engine, function, var);
}
+ReturnedValue Object::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup)
+{
+ Heap::Object *obj = object->d();
+ PropertyKey name = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+ if (name.isArrayIndex()) {
+ lookup->indexedLookup.index = name.asArrayIndex();
+ lookup->getter = Lookup::getterIndexed;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ auto index = obj->internalClass->findValueOrGetter(name);
+ if (index.isValid()) {
+ PropertyAttributes attrs = index.attrs;
+ uint nInline = obj->vtable()->nInlineProperties;
+ if (attrs.isData()) {
+ if (index.index < obj->vtable()->nInlineProperties) {
+ index.index += obj->vtable()->inlinePropertyOffset;
+ lookup->getter = Lookup::getter0Inline;
+ } else {
+ index.index -= nInline;
+ lookup->getter = Lookup::getter0MemberData;
+ }
+ } else {
+ lookup->getter = Lookup::getterAccessor;
+ }
+ lookup->objectLookup.ic = obj->internalClass;
+ lookup->objectLookup.offset = index.index;
+ return lookup->getter(lookup, engine, *object);
+ }
+
+ lookup->protoLookup.protoId = obj->internalClass->protoId;
+ lookup->resolveProtoGetter(name, obj->prototype());
+ return lookup->getter(lookup, engine, *object);
+}
+
+bool Object::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value)
+{
+ Scope scope(engine);
+ ScopedString name(scope, scope.engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]);
+
+ Heap::InternalClass *c = object->internalClass();
+ PropertyKey key = name->toPropertyKey();
+ auto idx = c->findValueOrSetter(key);
+ if (idx.isValid()) {
+ if (object->isArrayObject() && idx.index == Heap::ArrayObject::LengthPropertyIndex) {
+ Q_ASSERT(!idx.attrs.isAccessor());
+ lookup->setter = Lookup::arrayLengthSetter;
+ return lookup->setter(lookup, engine, *object, value);
+ } else if (idx.attrs.isData() && idx.attrs.isWritable()) {
+ lookup->objectLookup.ic = object->internalClass();
+ lookup->objectLookup.offset = idx.index;
+ lookup->setter = idx.index < object->d()->vtable()->nInlineProperties ? Lookup::setter0Inline : Lookup::setter0;
+ return lookup->setter(lookup, engine, *object, value);
+ } else {
+ // ### handle setter
+ lookup->setter = Lookup::setterFallback;
+ }
+ return lookup->setter(lookup, engine, *object, value);
+ }
+
+ lookup->insertionLookup.protoId = c->protoId;
+ if (!object->put(key, value)) {
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+
+ if (object->internalClass() == c) {
+ // ### setter in the prototype, should handle this
+ lookup->setter = Lookup::setterFallback;
+ return true;
+ }
+ idx = object->internalClass()->findValueOrSetter(key);
+ if (!idx.isValid() || idx.attrs.isAccessor()) { // ### can this even happen?
+ lookup->setter = Lookup::setterFallback;
+ return false;
+ }
+ lookup->insertionLookup.newClass = object->internalClass();
+ lookup->insertionLookup.offset = idx.index;
+ lookup->setter = Lookup::setterInsert;
+ return true;
+}
+
ReturnedValue Object::checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *f, const Value &var)
{
Scope scope(engine);
diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h
index ff47810994..c3f1cb2c35 100644
--- a/src/qml/jsruntime/qv4object_p.h
+++ b/src/qml/jsruntime/qv4object_p.h
@@ -375,6 +375,11 @@ public:
bool setProtoFromNewTarget(const Value *newTarget);
+ ReturnedValue resolveLookupGetter(ExecutionEngine *engine, Lookup *lookup) const
+ { return vtable()->resolveLookupGetter(this, engine, lookup); }
+ ReturnedValue resolveLookupSetter(ExecutionEngine *engine, Lookup *lookup, const Value &value)
+ { return vtable()->resolveLookupSetter(this, engine, lookup, value); }
+
protected:
static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver,bool *hasProperty);
static bool virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver);
@@ -389,6 +394,8 @@ protected:
static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target);
static qint64 virtualGetLength(const Managed *m);
static ReturnedValue virtualInstanceOf(const Object *typeObject, const Value &var);
+ static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup);
+ static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value);
public:
// qv4runtime uses this directly
static ReturnedValue checkedInstanceOf(ExecutionEngine *engine, const FunctionObject *typeObject, const Value &var);
@@ -408,7 +415,6 @@ struct ObjectOwnPropertyKeyIterator : OwnPropertyKeyIterator
uint arrayIndex = 0;
uint memberIndex = 0;
bool iterateOverSymbols = false;
- SparseArrayNode *arrayNode = nullptr;
~ObjectOwnPropertyKeyIterator() override = default;
PropertyKey next(const Object *o, Property *pd = nullptr, PropertyAttributes *attrs = nullptr) override;
diff --git a/src/qml/jsruntime/qv4runtimecodegen_p.h b/src/qml/jsruntime/qv4runtimecodegen_p.h
index be66dc57ca..006a6a3cde 100644
--- a/src/qml/jsruntime/qv4runtimecodegen_p.h
+++ b/src/qml/jsruntime/qv4runtimecodegen_p.h
@@ -71,6 +71,7 @@ public:
void throwSyntaxError(const AST::SourceLocation &loc, const QString &detail) override;
void throwReferenceError(const AST::SourceLocation &loc, const QString &detail) override;
+
private:
ExecutionEngine *engine;
};
diff --git a/src/qml/jsruntime/qv4stringobject.cpp b/src/qml/jsruntime/qv4stringobject.cpp
index 8186153ba4..dee6a67792 100644
--- a/src/qml/jsruntime/qv4stringobject.cpp
+++ b/src/qml/jsruntime/qv4stringobject.cpp
@@ -130,7 +130,7 @@ PropertyKey StringObjectOwnPropertyKeyIterator::next(const QV4::Object *o, Prope
return PropertyKey::fromArrayIndex(index);
} else if (arrayIndex == slen) {
if (s->arrayData()) {
- arrayNode = s->sparseBegin();
+ SparseArrayNode *arrayNode = s->sparseBegin();
// iterate until we're past the end of the string
while (arrayNode && arrayNode->key() < slen)
arrayNode = arrayNode->nextNode();
diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp
index d69a0fe34e..b7d2902b1d 100644
--- a/src/qml/jsruntime/qv4vme_moth.cpp
+++ b/src/qml/jsruntime/qv4vme_moth.cpp
@@ -679,7 +679,17 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(GetLookup)
STORE_IP();
STORE_ACC();
+
QV4::Lookup *l = function->compilationUnit->runtimeLookups + index;
+
+ if (accumulator.isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot read property '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(accumulator.toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
acc = l->getter(l, engine, accumulator);
CHECK_EXCEPTION;
traceValue(acc, function, traceSlot);
@@ -812,11 +822,23 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine,
MOTH_BEGIN_INSTR(CallPropertyLookup)
STORE_IP();
Lookup *l = function->compilationUnit->runtimeLookups + lookupIndex;
+
+ if (stack[base].isNullOrUndefined()) {
+ QString message = QStringLiteral("Cannot call method '%1' of %2")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
+ goto handleUnwind;
+ }
+
// ok to have the value on the stack here
Value f = Value::fromReturnedValue(l->getter(l, engine, stack[base]));
if (Q_UNLIKELY(!f.isFunctionObject())) {
- acc = engine->throwTypeError();
+ QString message = QStringLiteral("Property '%1' of object %2 is not a function")
+ .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString())
+ .arg(stack[base].toQStringNoThrow());
+ acc = engine->throwTypeError(message);
goto handleUnwind;
}
diff --git a/src/qml/jsruntime/qv4vtable_p.h b/src/qml/jsruntime/qv4vtable_p.h
index 4acefecdf5..aff1ae82d7 100644
--- a/src/qml/jsruntime/qv4vtable_p.h
+++ b/src/qml/jsruntime/qv4vtable_p.h
@@ -56,6 +56,8 @@ QT_BEGIN_NAMESPACE
namespace QV4 {
+struct Lookup;
+
struct OwnPropertyKeyIterator {
virtual ~OwnPropertyKeyIterator() = 0;
virtual PropertyKey next(const Object *o, Property *p = nullptr, PropertyAttributes *attrs = nullptr) = 0;
@@ -84,6 +86,9 @@ struct VTable
typedef ReturnedValue (*Call)(const FunctionObject *, const Value *thisObject, const Value *argv, int argc);
typedef ReturnedValue (*CallAsConstructor)(const FunctionObject *, const Value *argv, int argc, const Value *newTarget);
+ typedef ReturnedValue (*ResolveLookupGetter)(const Object *, ExecutionEngine *, Lookup *);
+ typedef bool (*ResolveLookupSetter)(Object *, ExecutionEngine *, Lookup *, const Value &);
+
const VTable * const parent;
quint32 inlinePropertyOffset : 16;
quint32 nInlineProperties : 16;
@@ -118,6 +123,9 @@ struct VTable
Call call;
CallAsConstructor callAsConstructor;
+
+ ResolveLookupGetter resolveLookupGetter;
+ ResolveLookupSetter resolveLookupSetter;
};
@@ -142,6 +150,9 @@ protected:
static constexpr VTable::Call virtualCall = nullptr;
static constexpr VTable::CallAsConstructor virtualCallAsConstructor = nullptr;
+
+ static constexpr VTable::ResolveLookupGetter virtualResolveLookupGetter = nullptr;
+ static constexpr VTable::ResolveLookupSetter virtualResolveLookupSetter = nullptr;
};
#define DEFINE_MANAGED_VTABLE_INT(classname, parentVTable) \
@@ -181,6 +192,9 @@ protected:
\
classname::virtualCall, \
classname::virtualCallAsConstructor, \
+ \
+ classname::virtualResolveLookupGetter, \
+ classname::virtualResolveLookupSetter \
}
#define DEFINE_MANAGED_VTABLE(classname) \
diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g
index b86dba6daa..8ae51a795f 100644
--- a/src/qml/parser/qqmljs.g
+++ b/src/qml/parser/qqmljs.g
@@ -614,16 +614,8 @@ bool Parser::parse(int startToken)
program = 0;
do {
- if (++tos == stack_size) {
+ if (++tos == stack_size)
reallocateStack();
- if (stack_size > 10000) {
- // We're now in some serious right-recursive stuff, which will probably result in
- // an AST that's so deep that recursively visiting it will run out of stack space.
- const QString msg = QCoreApplication::translate("QQmlParser", "Maximum statement or expression depth exceeded");
- diagnostic_messages.append(DiagnosticMessage(DiagnosticMessage::Error, token_buffer[0].loc, msg));
- return false;
- }
- }
state_stack[tos] = action;
diff --git a/src/qml/parser/qqmljsast.cpp b/src/qml/parser/qqmljsast.cpp
index 4ebb2d3b5c..54a1200493 100644
--- a/src/qml/parser/qqmljsast.cpp
+++ b/src/qml/parser/qqmljsast.cpp
@@ -65,21 +65,6 @@ ClassExpression *asAnonymousClassDefinition(Node *n)
return c;
}
-
-void Node::accept(Visitor *visitor)
-{
- if (visitor->preVisit(this)) {
- accept0(visitor);
- }
- visitor->postVisit(this);
-}
-
-void Node::accept(Node *node, Visitor *visitor)
-{
- if (node)
- node->accept(visitor);
-}
-
ExpressionNode *Node::expressionCast()
{
return nullptr;
diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h
index 43aeec6525..e84c62af2f 100644
--- a/src/qml/parser/qqmljsast_p.h
+++ b/src/qml/parser/qqmljsast_p.h
@@ -271,11 +271,29 @@ public:
virtual FunctionExpression *asFunctionDefinition();
virtual ClassExpression *asClassDefinition();
- void accept(Visitor *visitor);
- static void accept(Node *node, Visitor *visitor);
+ inline void accept(Visitor *visitor)
+ {
+ Visitor::RecursionDepthCheck recursionCheck(visitor);
+ if (recursionCheck()) {
+ if (visitor->preVisit(this))
+ accept0(visitor);
+ visitor->postVisit(this);
+ } else {
+ visitor->throwRecursionDepthError();
+ }
+ }
+ inline static void accept(Node *node, Visitor *visitor)
+ {
+ if (node)
+ node->accept(visitor);
+ }
+
+ // ### Remove when we can. This is part of the qmldevtools library, though.
inline static void acceptChild(Node *node, Visitor *visitor)
- { return accept(node, visitor); } // ### remove
+ {
+ return accept(node, visitor);
+ }
virtual void accept0(Visitor *visitor) = 0;
virtual SourceLocation firstSourceLocation() const = 0;
diff --git a/src/qml/parser/qqmljsastvisitor.cpp b/src/qml/parser/qqmljsastvisitor.cpp
index eec151298e..666623eecc 100644
--- a/src/qml/parser/qqmljsastvisitor.cpp
+++ b/src/qml/parser/qqmljsastvisitor.cpp
@@ -43,7 +43,7 @@ QT_QML_BEGIN_NAMESPACE
namespace QQmlJS { namespace AST {
-Visitor::Visitor()
+Visitor::Visitor(quint16 parentRecursionDepth) : m_recursionDepth(parentRecursionDepth)
{
}
diff --git a/src/qml/parser/qqmljsastvisitor_p.h b/src/qml/parser/qqmljsastvisitor_p.h
index c925096de6..9c69f88e0c 100644
--- a/src/qml/parser/qqmljsastvisitor_p.h
+++ b/src/qml/parser/qqmljsastvisitor_p.h
@@ -61,7 +61,33 @@ namespace QQmlJS { namespace AST {
class QML_PARSER_EXPORT Visitor
{
public:
- Visitor();
+ class RecursionDepthCheck
+ {
+ Q_DISABLE_COPY(RecursionDepthCheck)
+ public:
+ RecursionDepthCheck(RecursionDepthCheck &&) = delete;
+ RecursionDepthCheck &operator=(RecursionDepthCheck &&) = delete;
+
+ RecursionDepthCheck(Visitor *visitor) : m_visitor(visitor)
+ {
+ ++(m_visitor->m_recursionDepth);
+ }
+
+ ~RecursionDepthCheck()
+ {
+ --(m_visitor->m_recursionDepth);
+ }
+
+ bool operator()() const {
+ return m_visitor->m_recursionDepth < s_recursionLimit;
+ }
+
+ private:
+ static const quint16 s_recursionLimit = 4096;
+ Visitor *m_visitor;
+ };
+
+ Visitor(quint16 parentRecursionDepth = 0);
virtual ~Visitor();
virtual bool preVisit(Node *) { return true; }
@@ -374,6 +400,14 @@ public:
virtual bool visit(DebuggerStatement *) { return true; }
virtual void endVisit(DebuggerStatement *) {}
+
+ virtual void throwRecursionDepthError() = 0;
+
+ quint16 recursionDepth() const { return m_recursionDepth; }
+
+protected:
+ quint16 m_recursionDepth = 0;
+ friend class RecursionDepthCheck;
};
} } // namespace AST
diff --git a/src/qml/util/qqmladaptormodel.cpp b/src/qml/util/qqmladaptormodel.cpp
index d9cb6506b8..a9a38c5381 100644
--- a/src/qml/util/qqmladaptormodel.cpp
+++ b/src/qml/util/qqmladaptormodel.cpp
@@ -525,7 +525,7 @@ public:
metaObject.reset(builder.toMetaObject());
*static_cast<QMetaObject *>(this) = *metaObject;
- propertyCache = new QQmlPropertyCache(metaObject.data(), model.modelItemRevision);
+ propertyCache.adopt(new QQmlPropertyCache(metaObject.data(), model.modelItemRevision));
}
};
@@ -659,8 +659,8 @@ public:
{
VDMListDelegateDataType *dataType = const_cast<VDMListDelegateDataType *>(this);
if (!propertyCache) {
- dataType->propertyCache = new QQmlPropertyCache(
- &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision);
+ dataType->propertyCache.adopt(new QQmlPropertyCache(
+ &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision));
}
return new QQmlDMListAccessorData(
diff --git a/src/quick/doc/src/concepts/statesanimations/states.qdoc b/src/quick/doc/src/concepts/statesanimations/states.qdoc
index b695713091..5592ccd25d 100644
--- a/src/quick/doc/src/concepts/statesanimations/states.qdoc
+++ b/src/quick/doc/src/concepts/statesanimations/states.qdoc
@@ -120,7 +120,7 @@ interpolation behaviors are definable. The
{Animation and Transitions} article has more information about creating state
animations.
-The \l {animation/states}{States and Transitions example}
+The \l {Qt Quick Examples - Animation}{Animation} example
demonstrates how to declare a basic set of states and apply animated
transitions between them.
diff --git a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
index fdef1c0956..e6603a1a19 100644
--- a/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
+++ b/tests/auto/qml/qqmlecmascript/tst_qqmlecmascript.cpp
@@ -364,6 +364,7 @@ private slots:
void arrayAndException();
void numberToStringWithRadix();
void tailCallWithArguments();
+ void deleteSparseInIteration();
private:
// static void propertyVarWeakRefCallback(v8::Persistent<v8::Value> object, void* parameter);
@@ -8914,6 +8915,27 @@ void tst_qqmlecmascript::tailCallWithArguments()
QCOMPARE(value.toInt(), 1);
}
+void tst_qqmlecmascript::deleteSparseInIteration()
+{
+ QJSEngine engine;
+ const QJSValue value = engine.evaluate(
+ "(function() {\n"
+ " var obj = { 1: null, 2: null, 4096: null };\n"
+ " var iterated = [];\n"
+ " for (var t in obj) {\n"
+ " if (t == 2)\n"
+ " delete obj[t];\n"
+ " iterated.push(t);\n"
+ " }\n"
+ " return iterated;"
+ "})()");
+ QVERIFY(value.isArray());
+ QCOMPARE(value.property("length").toInt(), 3);
+ QCOMPARE(value.property("0").toInt(), 1);
+ QCOMPARE(value.property("1").toInt(), 2);
+ QCOMPARE(value.property("2").toInt(), 4096);
+}
+
QTEST_MAIN(tst_qqmlecmascript)
#include "tst_qqmlecmascript.moc"
diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
index c2c73935c0..71dd900073 100644
--- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
+++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp
@@ -105,6 +105,11 @@ public:
{
nodeStack.removeLast();
}
+
+ void throwRecursionDepthError() final
+ {
+ QFAIL("Maximum statement or expression depth exceeded");
+ }
};
}
diff --git a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
index f6ca999cf5..b34612ee88 100644
--- a/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
+++ b/tests/auto/quick/qquickrectangle/tst_qquickrectangle.cpp
@@ -134,14 +134,12 @@ void tst_qquickrectangle::gradient_separate()
// Start off clean
QQuickItemPrivate *rectPriv = QQuickItemPrivate::get(rect);
- bool isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(!isDirty);
+ QTRY_COMPARE(rectPriv->dirtyAttributes & QQuickItemPrivate::Content, 0);
QMetaObject::invokeMethod(rect, "changeGradient");
// Changing the gradient should have scheduled an update of the item.
- isDirty = rectPriv->dirtyAttributes & QQuickItemPrivate::Content;
- QVERIFY(isDirty);
+ QVERIFY((rectPriv->dirtyAttributes & QQuickItemPrivate::Content) != 0);
}
// When a gradient is changed, every Rectangle connected to it must update.
diff --git a/tools/qmlcachegen/qmlcachegen.cpp b/tools/qmlcachegen/qmlcachegen.cpp
index f7891e7d4b..cf32eb1aaa 100644
--- a/tools/qmlcachegen/qmlcachegen.cpp
+++ b/tools/qmlcachegen/qmlcachegen.cpp
@@ -55,17 +55,8 @@ QSet<QString> illegalNames;
void setupIllegalNames()
{
- // #### this in incomplete
- illegalNames.insert(QStringLiteral("Math"));
- illegalNames.insert(QStringLiteral("Array"));
- illegalNames.insert(QStringLiteral("String"));
- illegalNames.insert(QStringLiteral("Function"));
- illegalNames.insert(QStringLiteral("Boolean"));
- illegalNames.insert(QStringLiteral("Number"));
- illegalNames.insert(QStringLiteral("Date"));
- illegalNames.insert(QStringLiteral("RegExp"));
- illegalNames.insert(QStringLiteral("Error"));
- illegalNames.insert(QStringLiteral("Object"));
+ for (const char **g = QV4::Compiler::Codegen::s_globalNames; *g != nullptr; ++g)
+ illegalNames.insert(QString::fromLatin1(*g));
}
struct Error