diff options
author | Qt Forward Merge Bot <qt_forward_merge_bot@qt-project.org> | 2019-03-16 01:00:16 +0100 |
---|---|---|
committer | Ulf Hermann <ulf.hermann@qt.io> | 2019-03-18 13:49:23 +0100 |
commit | e912c646e22f845cf14f82532fb2c05628371974 (patch) | |
tree | 55f82c3f5b3d30f0d13cc0b92bacddf7f66e0baa | |
parent | dc3e3090d21339d78abc706369117b3396c843af (diff) | |
parent | f396cc753da75c68c6a501379a18df3099697f42 (diff) |
Merge remote-tracking branch 'origin/5.12' into 5.13
Conflicts:
src/qml/compiler/qv4codegen.cpp
Change-Id: I66b7db42bf208855889094ace0267326595ce03c
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 ¤tExpr() 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 |