From 744fe84d890479962ccb85d0ed6c4515a6ea11f4 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 28 Feb 2019 15:11:36 +0100 Subject: Save some stack space during code generation Result objects are rather large, 96 bytes here. In a recursive algorithm such as our parser, we should not keep too many of them on the stack. Also, the size of Reference can be reduced by employing a bit field rather than a number of booleans. Also, try to convince the compiler to inline the accept() functions. The extra stack frames those create are unnecessary. Task-number: QTBUG-74087 Change-Id: I5c064491172366bb0abef99ffe9314080401a7d1 Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder.cpp | 2 +- src/qml/compiler/qv4codegen.cpp | 221 +++++++++++++++++-------------------- src/qml/compiler/qv4codegen_p.h | 88 ++++++++++++--- src/qml/parser/qqmljsast.cpp | 15 --- src/qml/parser/qqmljsast_p.h | 19 +++- 5 files changed, 192 insertions(+), 153 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 2421e8c1ea..59b9968bf6 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/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 256ab6e0be..b4da05eb9a 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -100,6 +100,7 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) , hasError(false) { jsUnitGenerator->codeGeneratorName = QStringLiteral("moth"); + pushExpr(); } const char *globalNames[] = { @@ -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->addInstruction(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->addInstruction(dec); - if (_expr.accept(nx)) + if (exprAccept(nx)) return e.storeConsumeAccumulator(); else return e.storeRetainAccumulator(); @@ -368,17 +369,9 @@ 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) @@ -402,20 +395,19 @@ void Codegen::statement(ExpressionNode *ast) 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 } } @@ -429,10 +421,9 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift 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 +441,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 +854,13 @@ bool Codegen::visit(ExportDeclaration *ast) Reference exportedValue; if (auto *fdecl = AST::cast(ast->variableStatementOrDeclaration)) { - Result r; - qSwap(_expr, r); + pushExpr(); visit(static_cast(fdecl)); - qSwap(_expr, r); - exportedValue = r.result(); + exportedValue = popResult(); } else if (auto *classDecl = AST::cast(ast->variableStatementOrDeclaration)) { - Result r; - qSwap(_expr, r); + pushExpr(); visit(static_cast(classDecl)); - qSwap(_expr, r); - exportedValue = r.result(); + exportedValue = popResult(); } else if (ExpressionNode *expr = ast->variableStatementOrDeclaration->expressionCast()) { exportedValue = expression(expr); } @@ -1068,7 +1043,7 @@ bool Codegen::visit(ClassExpression *ast) (void) ctor.storeRetainAccumulator(); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -1151,7 +1126,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 +1220,7 @@ bool Codegen::visit(ArrayPattern *ast) } array.loadInAccumulator(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -1261,7 +1236,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 +1246,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 +1287,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 +1315,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 +1346,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 +1357,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 +1379,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 +1425,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 +1437,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference right = expression(ast->right); if (hasError) return false; - _expr.setResult(binopHelper(static_cast(ast->op), right, left)); + setExprResult(binopHelper(static_cast(ast->op), right, left)); break; } // intentional fall-through! @@ -1486,7 +1463,7 @@ bool Codegen::visit(BinaryExpression *ast) Reference right; if (AST::NumericLiteral *rhs = AST::cast(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 +1471,7 @@ bool Codegen::visit(BinaryExpression *ast) if (hasError) return false; - _expr.setResult(binopHelper(static_cast(ast->op), left, right)); + setExprResult(binopHelper(static_cast(ast->op), left, right)); break; } @@ -1671,7 +1648,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 +1659,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 +1670,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 +1681,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 +1692,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 +1703,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 +1714,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 +1725,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 +1929,7 @@ bool Codegen::visit(CallExpression *ast) bytecodeGenerator->addInstruction(call); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2045,7 +2022,7 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio bytecodeGenerator->addInstruction(call); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); } Codegen::Arguments Codegen::pushArgs(ArgumentList *args) @@ -2141,7 +2118,7 @@ bool Codegen::visit(ConditionalExpression *ast) ko.loadInAccumulator(); jump_endif.link(); - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2171,7 +2148,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 +2158,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 +2173,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 +2183,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 +2199,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 +2208,7 @@ bool Codegen::visit(SuperLiteral *) if (hasError) return false; - _expr.setResult(Reference::fromSuper(this)); + setExprResult(Reference::fromSuper(this)); return false; } @@ -2249,12 +2226,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 +2244,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 +2257,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 +2326,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 +2392,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 +2442,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 +2491,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 +2500,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 +2513,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; } @@ -2648,8 +2628,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; } @@ -2668,7 +2647,7 @@ bool Codegen::visit(PostDecrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - _expr.setResult(unop(PostDecrement, expr)); + setExprResult(unop(PostDecrement, expr)); return false; } @@ -2688,7 +2667,7 @@ bool Codegen::visit(PostIncrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - _expr.setResult(unop(PostIncrement, expr)); + setExprResult(unop(PostIncrement, expr)); return false; } @@ -2706,7 +2685,7 @@ bool Codegen::visit(PreDecrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->decrementToken)) return false; - _expr.setResult(unop(PreDecrement, expr)); + setExprResult(unop(PreDecrement, expr)); return false; } @@ -2725,7 +2704,7 @@ bool Codegen::visit(PreIncrementExpression *ast) if (throwSyntaxErrorOnEvalOrArgumentsInStrictMode(expr, ast->incrementToken)) return false; - _expr.setResult(unop(PreIncrement, expr)); + setExprResult(unop(PreIncrement, expr)); return false; } @@ -2736,7 +2715,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); @@ -2752,7 +2731,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()); @@ -2802,7 +2781,7 @@ bool Codegen::visit(TemplateLiteral *ast) auto r = Reference::fromAccumulator(this); r.isReadonly = true; - _expr.setResult(r); + setExprResult(r); return false; } @@ -2815,10 +2794,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; } @@ -2828,7 +2807,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; } @@ -2837,7 +2816,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; } @@ -2863,7 +2842,7 @@ bool Codegen::visit(TypeOfExpression *ast) Instruction::TypeofValue instr; bytecodeGenerator->addInstruction(instr); } - _expr.setResult(Reference::fromAccumulator(this)); + setExprResult(Reference::fromAccumulator(this)); return false; } @@ -2874,7 +2853,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; } @@ -2884,7 +2863,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; } @@ -2897,7 +2876,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; } @@ -2911,7 +2890,7 @@ bool Codegen::visit(FunctionDeclaration * ast) if (_functionContext->contextType == ContextType::Binding) referenceForName(ast->name.toString(), true).loadInAccumulator(); - _expr.accept(nx); + exprAccept(nx); return false; } @@ -2967,7 +2946,7 @@ bool Codegen::visit(YieldExpression *ast) done.link(); lhsValue.loadInAccumulator(); - _expr.setResult(acc); + setExprResult(acc); return false; } @@ -2978,7 +2957,7 @@ bool Codegen::visit(YieldExpression *ast) BytecodeGenerator::Jump jump = bytecodeGenerator->addJumpInstruction(resume); emitReturn(acc); jump.link(); - _expr.setResult(acc); + setExprResult(acc); return false; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 4d7001fe64..c1063bc0d0 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,23 @@ 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(); + + RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); + 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); @@ -684,6 +719,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; } @@ -720,7 +756,33 @@ protected: 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 m_expressions; VolatileMemoryLocations _volatileMemoryLocations; Module *_module; int _returnAddress; 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..0978ab523a 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -271,11 +271,24 @@ public: virtual FunctionExpression *asFunctionDefinition(); virtual ClassExpression *asClassDefinition(); - void accept(Visitor *visitor); - static void accept(Node *node, Visitor *visitor); + inline void accept(Visitor *visitor) + { + if (visitor->preVisit(this)) + accept0(visitor); + visitor->postVisit(this); + } + 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; -- cgit v1.2.3 From 7e32be87c0f53ce2e5d3c1e351a559d6c600f1bf Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 4 Mar 2019 15:36:57 +0100 Subject: Don't keep raw pointers to SparseArrayNode The nodes are owned by the SparseArrayData and will be freed whenever an item is deleted from the array. Therefore, we have to look up the node for each iteration. This is slightly slower, but at least it doesn't crash. Fixes: QTBUG-74188 Change-Id: Id24324a8c83b00b3ad1212cdaabccabd6c8a999f Reviewed-by: Simon Hausmann Reviewed-by: Lars Knoll --- src/qml/jsruntime/qv4object.cpp | 8 +++++--- src/qml/jsruntime/qv4object_p.h | 1 - src/qml/jsruntime/qv4stringobject.cpp | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4object.cpp b/src/qml/jsruntime/qv4object.cpp index 3d2d54f651..9d6cb730a9 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 diff --git a/src/qml/jsruntime/qv4object_p.h b/src/qml/jsruntime/qv4object_p.h index ff47810994..9f24449c91 100644 --- a/src/qml/jsruntime/qv4object_p.h +++ b/src/qml/jsruntime/qv4object_p.h @@ -408,7 +408,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/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(); -- cgit v1.2.3 From 73231fe953145ac0df4e62f173e1a90076466012 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Wed, 27 Feb 2019 16:01:54 +0100 Subject: Unify the JavaScript parsing recursion checks We only need to check in one central location and we can allow for more recursion. 4k recursions seem tolerable. A common default for stack sizes is 8MB. Each recursion step takes up to 1k stack space in debug mode. So, exhausting this would burn about half of the available stack size. We don't report the exact source location in this case as finding the source location may itself trigger a deep recursion. Fixes: QTBUG-74087 Change-Id: I43e6e20b322f6035c7136a6f381230ec285c30ae Reviewed-by: Simon Hausmann --- src/qml/compiler/qqmlirbuilder_p.h | 6 +++++ src/qml/compiler/qv4codegen.cpp | 37 ++++++++++++++++++++------- src/qml/compiler/qv4codegen_p.h | 33 +++++------------------- src/qml/compiler/qv4compilerscanfunctions.cpp | 27 +++++-------------- src/qml/compiler/qv4compilerscanfunctions_p.h | 7 ++--- src/qml/jsruntime/qv4runtimecodegen_p.h | 1 + src/qml/parser/qqmljs.g | 10 +------- src/qml/parser/qqmljsast_p.h | 11 +++++--- src/qml/parser/qqmljsastvisitor.cpp | 2 +- src/qml/parser/qqmljsastvisitor_p.h | 36 +++++++++++++++++++++++++- 10 files changed, 95 insertions(+), 75 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 1a3ca4163e..6affca3ca6 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -498,6 +498,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 b4da05eb9a..0ca452b93e 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -376,7 +376,6 @@ void Codegen::addCJump() void Codegen::statement(Statement *ast) { - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); RegisterScope scope(this); bytecodeGenerator->setLocation(ast->firstSourceLocation()); @@ -392,7 +391,6 @@ void Codegen::statement(ExpressionNode *ast) if (! ast) { return; } else { - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); RegisterScope scope(this); pushExpr(Result(nx)); @@ -420,7 +418,6 @@ void Codegen::condition(ExpressionNode *ast, const BytecodeGenerator::Label *ift if (!ast) return; - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); pushExpr(Result(iftrue, iffalse, trueBlockFollowsCondition)); accept(ast); Result r = popExpr(); @@ -3825,8 +3822,14 @@ QQmlRefPointer 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); @@ -3891,25 +3894,41 @@ public: } } + void throwRecursionDepthError() override + { + parent->throwRecursionDepthError(); + } + private: - void collectIdentifiers(QVector &ids, AST::Node *node) const { + void collectIdentifiers(QVector &ids, AST::Node *node) { class Collector: public QQmlJS::AST::Visitor { + private: QVector &ids; + VolatileMemoryLocationScanner *parent; + public: - Collector(QVector &ids): ids(ids) {} - virtual bool visit(IdentifierExpression *ie) { + Collector(QVector &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 c1063bc0d0..a6355bf93a 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -571,7 +571,6 @@ protected: if (!ast || hasError) return Reference(); - RecursionDepthCheck depthCheck(this, ast->lastSourceLocation()); pushExpr(); ast->accept(this); return popResult(); @@ -705,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 errors() const; @@ -831,33 +835,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/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/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_p.h b/src/qml/parser/qqmljsast_p.h index 0978ab523a..e84c62af2f 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -273,9 +273,14 @@ public: inline void accept(Visitor *visitor) { - if (visitor->preVisit(this)) - accept0(visitor); - visitor->postVisit(this); + 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) 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 -- cgit v1.2.3 From a79d1038a14db01037d80b9701a530c3654c7adc Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Dec 2018 11:26:45 +0100 Subject: Make object property lookup resolution virtual This allows sub-classes of Object to have their own lookup resolution and verification logic, instead of squeezing it all into qv4lookup.cpp. The typical fallbacks are still in qv4lookup.cpp though. Task-number: QTBUG-69898 Change-Id: I32a8884982b37e2065090666a7bf05b198f5b7fd Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup.cpp | 76 +------------------------------------- src/qml/jsruntime/qv4object.cpp | 82 +++++++++++++++++++++++++++++++++++++++++ src/qml/jsruntime/qv4object_p.h | 7 ++++ src/qml/jsruntime/qv4vtable_p.h | 14 +++++++ 4 files changed, 105 insertions(+), 74 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 994daa864b..790a5843c2 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) @@ -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 9d6cb730a9..7dd0a247d6 100644 --- a/src/qml/jsruntime/qv4object.cpp +++ b/src/qml/jsruntime/qv4object.cpp @@ -736,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 9f24449c91..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); 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) \ -- cgit v1.2.3 From 8a69735c4914d02be9448b6ae98e4c550e1f187a Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 22 Dec 2018 09:09:57 +0100 Subject: Fix type error exception messages when using member lookups Our tests expect those exceptions to have the same message as when not using lookups. Task-number: QTBUG-69898 Change-Id: Iab36519844a3a49ef9e34346e9afeb2eee9f9ced Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4vme_moth.cpp | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 937a535b83..000e1ab9d5 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -594,7 +594,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; MOTH_END_INSTR(GetLookup) @@ -723,11 +733,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; } -- cgit v1.2.3 From 24ec7b6c8352600230c40b61be4bfce07076c9a5 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 22 Dec 2018 09:41:10 +0100 Subject: Fix up global name determination when compiling ahead of time The list of names is still suboptimal, but at least it's shared now. Task-number: QTBUG-69898 Change-Id: I16c9839c4a1f097053b28caea894b67757972826 Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4codegen.cpp | 4 ++-- src/qml/compiler/qv4codegen_p.h | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 0ca452b93e..66c334d197 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -103,7 +103,7 @@ Codegen::Codegen(QV4::Compiler::JSUnitGenerator *jsUnitGenerator, bool strict) pushExpr(); } -const char *globalNames[] = { +const char *Codegen::s_globalNames[] = { "isNaN", "parseFloat", "String", @@ -183,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); } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index a6355bf93a..0b25d9c53d 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -754,6 +754,7 @@ public: m_globalNames = globalNames; } + static const char *s_globalNames[]; protected: friend class ScanFunctions; -- cgit v1.2.3 From 5f24122c780b462d7091abc12c9861d1e9713dca Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Sat, 22 Dec 2018 10:56:52 +0100 Subject: Fix lookups the transition between primitive strings and string objects Suppose we have function foo(x) { return x.constructor; } and we call it first with foo("hello") then the lookup will be initialized with a primitive getter from the prototype. When we subsequently call foo(new String("world")) then the primitiveGetterProto() will check that the provided object is of the same type as last time, which erroneously succeeds. Indeed, both are of Managed type. However now we're passing a full-fledged object, which is not a primitive anymore - hence the additional check to ensure that we fall back to the generic getter. Task-number: QTBUG-69898 Change-Id: I3c7a8384bfdf0e31b7c6247cce80fe7448b627b3 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup.cpp b/src/qml/jsruntime/qv4lookup.cpp index 790a5843c2..54bce7d7b3 100644 --- a/src/qml/jsruntime/qv4lookup.cpp +++ b/src/qml/jsruntime/qv4lookup.cpp @@ -379,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(); @@ -390,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; -- cgit v1.2.3 From dc3e3090d21339d78abc706369117b3396c843af Mon Sep 17 00:00:00 2001 From: Friedemann Kleint Date: Fri, 15 Mar 2019 09:39:58 +0100 Subject: Fix qdoc errors Add ',' to enumerator and rearrange Q_QDOC #ifdefs, fixing: LOG Build & visit PCH for QtQuickDoc src/quick/scenegraph/coreapi/qsgnode.h:97:49: error: missing ',' between enumerators src/qml/qml/qqml.h:593:17: error: expected namespace name Change-Id: I4491c5885c5cdb8a156d9a7abcca5db4d68d1c2e Reviewed-by: Ulf Hermann Reviewed-by: Paul Wicking --- src/qml/qml/qqml.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'src/qml') diff --git a/src/qml/qml/qqml.h b/src/qml/qml/qqml.h index 05a9f70247..bf9330856d 100644 --- a/src/qml/qml/qqml.h +++ b/src/qml/qml/qqml.h @@ -584,7 +584,6 @@ namespace QtQml { const QMetaObject *, bool create); #ifndef Q_QDOC } -#endif QT_WARNING_PUSH QT_WARNING_DISABLE_CLANG("-Wheader-hygiene") @@ -594,6 +593,8 @@ using namespace QtQml; QT_WARNING_POP +#endif // Q_QDOC + //The C++ version of protected namespaces in qmldir Q_QML_EXPORT bool qmlProtectModule(const char* uri, int majVersion); Q_QML_EXPORT void qmlRegisterModule(const char *uri, int versionMajor, int versionMinor); -- cgit v1.2.3 From f396cc753da75c68c6a501379a18df3099697f42 Mon Sep 17 00:00:00 2001 From: Kirill Burtsev Date: Thu, 14 Mar 2019 18:41:03 +0100 Subject: Fix leaking propertyCache inside QQmlAdaptorModel::Accessors QQmlRefPointer works in AddRef mode for assignment of a raw pointer. This creates additional reference to original object and it will never be garbage collected in this case. Amends change 99e2356a73. Task-number: QTBUG-74148 Change-Id: Ic0f03e842965c5c82b357d10ab8b0d6a62411fc8 Reviewed-by: Simon Hausmann --- src/qml/util/qqmladaptormodel.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/qml') 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(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(this); if (!propertyCache) { - dataType->propertyCache = new QQmlPropertyCache( - &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision); + dataType->propertyCache.adopt(new QQmlPropertyCache( + &QQmlDMListAccessorData::staticMetaObject, model.modelItemRevision)); } return new QQmlDMListAccessorData( -- cgit v1.2.3 From f4649ebfe5be81c24a384c0806fd015e756a4dca Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Mon, 25 Feb 2019 16:01:03 +0100 Subject: Update JavaScript documentation Update function list and refer to the 7th edition of the ECMAScript standard. Fixes: QTBUG-73837 Change-Id: I4332de3ded0373393107b6d5bab95363a0b9ebb9 Reviewed-by: Simon Hausmann --- src/qml/doc/src/javascript/functionlist.qdoc | 61 +++++++++++++++++++++++++ src/qml/doc/src/javascript/hostenvironment.qdoc | 5 +- 2 files changed, 63 insertions(+), 3 deletions(-) (limited to 'src/qml') diff --git a/src/qml/doc/src/javascript/functionlist.qdoc b/src/qml/doc/src/javascript/functionlist.qdoc index 24ff640284..7a6a922480 100644 --- a/src/qml/doc/src/javascript/functionlist.qdoc +++ b/src/qml/doc/src/javascript/functionlist.qdoc @@ -55,6 +55,8 @@ \li decodeURIComponent(encodedURIComponent) \li encodeURI(uri) \li encodeURIComponent(uriComponent) + \li escape(string) + \li unescape(string) \endlist \section2 Constructor Properties @@ -63,11 +65,20 @@ \li Object \li Function \li Array + \li ArrayBuffer \li String \li Boolean \li Number + \li DataView \li Date + \li Promise \li RegExp + \li Map + \li WeakMap + \li Set + \li WeakSet + \li SharedArrayBuffer + \li Symbol \li Error \li EvalError \li RangeError @@ -80,8 +91,11 @@ \section2 Other Properties \list + \li Atomics \li Math \li JSON + \li Reflect + \li Proxy \endlist \section1 The Object Object @@ -92,12 +106,19 @@ \list \li getPrototypeOf(O) + \li setPrototypeOf(O, P) \li getOwnPropertyDescriptor(O, P) + \li getOwnPropertyDescriptors(O) \li getOwnPropertyNames(O) + \li getOwnPropertySymbols(O) + \li assign(O [, Properties]) \li create(O [, Properties]) \li defineProperty(O, P, Attributes) \li defineProperties(O, Properties) + \li entries(O) + \li is(V1, V2) \li keys(O) + \li values(O) \li seal(O) \li isSealed(O) \li freeze(O) @@ -117,6 +138,8 @@ \li hasOwnProperty(V) \li isPrototypeOf(V) \li propertyIsEnumerable(V) + \li __defineGetter__(P, F) + \li __defineSetter__(P, F) \endlist \section1 Function Objects @@ -130,6 +153,7 @@ \li apply(thisArg, argArray) \li call(thisArg [, arg1 [, arg2, ...]]) \li bind((thisArg [, arg1 [, arg2, …]]) + \li [Symbol.hasInstance](O) \endlist \section1 Array Objects @@ -142,9 +166,14 @@ \li toString() \li toLocaleString() \li concat([item1 [, item2 [, ...]]]) + \li copyWithin([item1 [, item2 [, ...]]]) + \li entries() + \li fill(item [, index1 [, index2]]) \li join(separator) \li find(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9 \li findIndex(callbackfn [, thisArg]) // ECMAScript 6: Added in Qt 5.9 + \li includes(item) + \li keys() \li pop() \li push([item1 [, item2 [, ...]]]) \li reverse() @@ -162,6 +191,8 @@ \li filter(callbackfn [, thisArg]) \li reduce(callbackfn [, initialValue]) \li reduceRight(callbackfn [, initialValue]) + \li values() + \li [Symbol.iterator]() \endlist \section1 String Objects @@ -175,6 +206,7 @@ \li valueOf() \li charAt(pos) \li charCodeAt(pos) + \li codePointAt(pos) \li concat([string1 [, string2 [, ...]]]) \li endsWith(searchString [, endPosition ]) // ECMAScript 6: Added in Qt 5.8 \li includes(searchString [, position ]) // ECMAScript 6: Added in 5.8 @@ -182,18 +214,23 @@ \li lastIndexOf(searchString, position) \li localeCompare(that) \li match(regexp) + \li normalize() + \li padEnd(length [, string]) + \li padStart(length [, string]) \li repeat(count) // ECMAScript 6: Added in Qt 5.9 \li replace(searchValue, replaceValue) \li search(regexp) \li slice(start, end) \li split(separator, limit) \li startsWith(searchString [, position ]) // ECMAScript 6: Added in Qt 5.8 + \li substr(start, length) \li substring(start, end) \li toLowerCase() \li toLocaleLowerCase() \li toUpperCase() \li toLocaleUpperCase() \li trim() + \li [Symbol.iterator]() \endlist Additionally, the QML engine adds the following functions to the \l String prototype: @@ -222,6 +259,7 @@ \list \li toString(radix) \li toLocaleString() + \li valueOf() \li toFixed(fractionDigits) \li toExponential(fractionDigits) \li toPrecision(precision) @@ -245,12 +283,16 @@ \li MAX_VALUE \li MIN_VALUE \li EPSILON // ECMAScript 6: Added in Qt 5.8 + \li MAX_SAFE_INTEGER + \li MIN_SAFE_INTEGER \endlist \section3 Function Properties \list \li isFinite(x) // ECMAScript 6: Added in Qt 5.8 + \li isInteger(x) + \li isSafeInteger(x) \li isNaN(x) // ECMAScript 6: Added in Qt 5.8 \endlist @@ -274,14 +316,27 @@ \list \li abs(x) \li acos(x) + \li acosh(x) \li asin(x) + \li asinh(x) \li atan(x) + \li atanh(x) \li atan2(y, x) + \li cbrt(x) \li ceil(x) + \li clz32(x) \li cos(x) + \li cosh(x) \li exp(x) + \li expm1(x) \li floor(x) + \li fround(x) + \li hypot(x, y) + \li imul(x, y) \li log(x) + \li log10(x) + \li log1p(x) + \li log2(x) \li max([value1 [, value2 [, ...]]]) \li min([value1 [, value2 [, ...]]]) \li pow(x, y) @@ -289,8 +344,11 @@ \li round(x) \li sign(x) // ECMAScript 6: Added in Qt 5.8 \li sin(x) + \li sinh(x) \li sqrt(x) \li tan(x) + \li tanh(x) + \li trunc(x) \endlist \section1 Date Objects @@ -338,11 +396,14 @@ \li setUTCDate(date) \li setMonth(month [, date]) \li setUTCMonth(month [, date]) + \li setYear(year) \li setFullYear(year [, month [, date]]) \li setUTCFullYear(year [, month [, date]]) \li toUTCString() + \li toGMTString() \li toISOString() \li toJSON() + \li [Symbol.toPrimitive](hint) \endlist Additionally, the QML engine adds the following functions to the \l Date prototype: diff --git a/src/qml/doc/src/javascript/hostenvironment.qdoc b/src/qml/doc/src/javascript/hostenvironment.qdoc index eb40f10065..c22c392b80 100644 --- a/src/qml/doc/src/javascript/hostenvironment.qdoc +++ b/src/qml/doc/src/javascript/hostenvironment.qdoc @@ -40,11 +40,10 @@ not provide a \c window object or \c{DOM API} as commonly found in a browser env Like a browser or server-side JavaScript environment, the QML runtime implements the \l{ECMA-262}{ECMAScript Language Specification} standard. This provides access to all of the built-in types and functions defined by the standard, such as Object, Array, and Math. -The QML runtime implements the 5th edition of the standard, which is the same edition commonly -implemented by browsers. +The QML runtime implements the 7th edition of the standard. The standard ECMAScript built-ins are not explicitly documented in the QML documentation. For more -information on their use, please refer to the ECMA-262 5th edition standard or one of the many online +information on their use, please refer to the ECMA-262 7th edition standard or one of the many online JavaScript reference and tutorial sites, such as the \l{W3Schools JavaScript Reference} (JavaScript Objects Reference section). Many sites focus on JavaScript in the browser, so in some cases you may need to double check the specification to determine whether a given function or object is part of standard ECMAScript or -- cgit v1.2.3 From 0dd884aca1fffcd94fbe55006c94363415aa0965 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 14 Mar 2019 10:42:19 +0100 Subject: Baseline JIT: Save accumulator in toInt32LhsAcc() toInt32LhsAcc convertes both the lhs and the accumulator to int32. If the accumulator is not saved, a GC run during the conversion of the lhs might trash its value. Fixes: QTBUG-74058 Change-Id: Ic42693061c7d483bb430d77bcc095de6ff9a6843 Reviewed-by: Simon Hausmann --- src/qml/jit/qv4baselineassembler.cpp | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jit/qv4baselineassembler.cpp b/src/qml/jit/qv4baselineassembler.cpp index 5c08c42977..b13f646360 100644 --- a/src/qml/jit/qv4baselineassembler.cpp +++ b/src/qml/jit/qv4baselineassembler.cpp @@ -208,17 +208,20 @@ public: isNumber.link(this); } + // this converts both the lhs and the accumulator to int32 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) { load64(lhs, lhsTarget); urshift64(lhsTarget, TrustedImm32(Value::QuickType_Shift), ScratchRegister2); auto lhsIsInt = branch32(Equal, TrustedImm32(Value::QT_Int), ScratchRegister2); - pushAligned(AccumulatorRegister); + const Address accumulatorStackAddress(JSStackFrameRegister, + offsetof(CallData, accumulator)); + storeAccumulator(accumulatorStackAddress); move(lhsTarget, registerForArg(0)); callHelper(toInt32Helper); move(ReturnValueRegister, lhsTarget); - popAligned(AccumulatorRegister); + loadAccumulator(accumulatorStackAddress); lhsIsInt.link(this); urshift64(AccumulatorRegister, TrustedImm32(Value::QuickType_Shift), ScratchRegister2); @@ -498,6 +501,7 @@ public: isNumber.link(this); } + // this converts both the lhs and the accumulator to int32 void toInt32LhsAcc(Address lhs, RegisterID lhsTarget) { bool accumulatorNeedsSaving = AccumulatorRegisterValue == ReturnValueRegisterValue @@ -510,32 +514,28 @@ public: auto lhsIsInt = jump(); lhsIsNotInt.link(this); - if (accumulatorNeedsSaving) { - push(AccumulatorRegisterTag); - push(AccumulatorRegisterValue); - } + + // Save accumulator from being garbage collected, no matter if we will reuse the register. + const Address accumulatorStackAddress(JSStackFrameRegister, + offsetof(CallData, accumulator)); + storeAccumulator(accumulatorStackAddress); if (ArgInRegCount < 2) { - if (!accumulatorNeedsSaving) - subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); + subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); push(lhsTarget); load32(lhs, lhsTarget); push(lhsTarget); } else { - if (accumulatorNeedsSaving) - subPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); move(lhsTarget, registerForArg(1)); load32(lhs, registerForArg(0)); } callHelper(toInt32Helper); move(ReturnValueRegisterValue, lhsTarget); - if (accumulatorNeedsSaving) { - addPtr(TrustedImm32(2 * PointerSize), StackPointerRegister); - pop(AccumulatorRegisterValue); - pop(AccumulatorRegisterTag); - } else if (ArgInRegCount < 2) { + if (ArgInRegCount < 2) addPtr(TrustedImm32(4 * PointerSize), StackPointerRegister); - } + + if (accumulatorNeedsSaving) // otherwise it's still the same + loadAccumulator(accumulatorStackAddress); lhsIsInt.link(this); -- cgit v1.2.3 From db292d1fe70a0cfaf315a72d099441cf3969e284 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 21 Dec 2018 16:00:48 +0100 Subject: Enable lookups in QML The main feature that needs to be implemented in order to enable lookups in QML files is to respect that the QObject wrapper has its own storage layer (meta-object properties). Lookups need to be able to index those when the base is a QObject. This is done by caching the property data and guarding the validity by comparing property cache pointers. The same lookup logic is also implemented for value type wrappers. OVerall there's more that can be done with lookups in meta-objects, for constant properties for example. For "global" lookups we have a safeguard in place that generates a LoadName instruction for property access that should end up in the qml context wrapper. So no changes are needed here at first, but the lookup in the QML context can be optimized in the future. The way of storing the property cache in the lookup itself trades ugliness on destruction against the creation of less internal classes. Another option would be to store the property cache in the internal class and let QObjectWrapper always transition via the property cache. Task-number: QTBUG-69898 Change-Id: I9c378c071acc6d7d4a34a2a76616f9594119d515 Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmltypecompiler.cpp | 1 - src/qml/compiler/qv4compileddata.cpp | 15 +++ src/qml/jsruntime/qv4lookup_p.h | 12 +++ src/qml/jsruntime/qv4qobjectwrapper.cpp | 173 +++++++++++++++++++++++++------- src/qml/jsruntime/qv4qobjectwrapper_p.h | 7 +- src/qml/jsruntime/qv4value_p.h | 16 +++ src/qml/qml/qqmlvaluetypewrapper.cpp | 151 +++++++++++++++++++++------- src/qml/qml/qqmlvaluetypewrapper_p.h | 3 + src/qml/types/qqmllistmodel.cpp | 7 ++ src/qml/types/qqmllistmodel_p_p.h | 2 + 10 files changed, 311 insertions(+), 76 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index 75ed8dd10f..f753c78b1a 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -147,7 +147,6 @@ QQmlRefPointer QQmlTypeCompiler::compile() document->jsModule.finalUrl = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); - v4CodeGenerator.setUseFastLookups(false); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 65690ec1f6..e428340f62 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include "qv4compilationunitmapper_p.h" #include #include @@ -270,6 +272,19 @@ void CompilationUnit::unlink() propertyCaches.clear(); + if (runtimeLookups) { + for (uint i = 0; i < data->lookupTableSize; ++i) { + QV4::Lookup &l = runtimeLookups[i]; + if (l.getter == QV4::QObjectWrapper::lookupGetter) { + if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) + pc->release(); + } else if (l.getter == QQmlValueTypeWrapper::lookupGetter) { + if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) + pc->release(); + } + } + } + dependentScripts.clear(); typeNameCache = nullptr; diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 83e561863b..b0d5706dea 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -116,6 +116,18 @@ struct Lookup { quintptr _unused2; uint index; } indexedLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + QQmlPropertyCache *propertyCache; + QQmlPropertyData *propertyData; + } qobjectLookup; + struct { + Heap::InternalClass *ic; + quintptr unused; + QQmlPropertyCache *propertyCache; + QQmlPropertyData *propertyData; + } qgadgetLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 40be6f41c8..711c910906 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -56,6 +56,8 @@ #include #include #include +#include +#include #if QT_CONFIG(qml_sequence_object) #include @@ -269,9 +271,53 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje } } +static OptionalReturnedValue getDestroyOrToStringMethod(ExecutionEngine *v4, String *name, QObject *qobj, bool *hasProperty = nullptr) +{ + int index = 0; + if (name->equals(v4->id_destroy())) + index = QV4::QObjectMethod::DestroyMethod; + else if (name->equals(v4->id_toString())) + index = QV4::QObjectMethod::ToStringMethod; + else + return OptionalReturnedValue(); + + if (hasProperty) + *hasProperty = true; + ExecutionContext *global = v4->rootContext(); + return OptionalReturnedValue(QV4::QObjectMethod::create(global, qobj, index)); +} + +static OptionalReturnedValue getPropertyFromImports(ExecutionEngine *v4, String *name, QQmlContextData *qmlContext, QObject *qobj, + bool *hasProperty = nullptr) +{ + if (!qmlContext || !qmlContext->imports) + return OptionalReturnedValue(); + + QQmlTypeNameCache::Result r = qmlContext->imports->query(name); + + if (hasProperty) + *hasProperty = true; + + if (!r.isValid()) + return OptionalReturnedValue(); + + if (r.scriptIndex != -1) { + return OptionalReturnedValue(QV4::Encode::undefined()); + } else if (r.type.isValid()) { + return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj,r.type, Heap::QQmlTypeWrapper::ExcludeEnums)); + } else if (r.importNamespace) { + return OptionalReturnedValue(QQmlTypeWrapper::create(v4, qobj, qmlContext->imports, r.importNamespace, + Heap::QQmlTypeWrapper::ExcludeEnums)); + } + Q_UNREACHABLE(); + return OptionalReturnedValue(); +} + ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, bool includeImports) const { + // Keep this code in sync with ::virtualResolveLookupGetter + if (QQmlData::wasDeleted(d()->object())) { if (hasProperty) *hasProperty = false; @@ -280,39 +326,17 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String ExecutionEngine *v4 = engine(); - if (name->equals(v4->id_destroy()) || name->equals(v4->id_toString())) { - int index = name->equals(v4->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - if (hasProperty) - *hasProperty = true; - ExecutionContext *global = v4->rootContext(); - return QV4::QObjectMethod::create(global, d()->object(), index); - } + if (auto methodValue = getDestroyOrToStringMethod(v4, name, d()->object(), hasProperty)) + return *methodValue; QQmlPropertyData local; QQmlPropertyData *result = findProperty(v4, qmlContext, name, revisionMode, &local); if (!result) { + // Check for attached properties if (includeImports && name->startsWithUpper()) { - // Check for attached properties - if (qmlContext && qmlContext->imports) { - QQmlTypeNameCache::Result r = qmlContext->imports->query(name); - - if (hasProperty) - *hasProperty = true; - - if (r.isValid()) { - if (r.scriptIndex != -1) { - return QV4::Encode::undefined(); - } else if (r.type.isValid()) { - return QQmlTypeWrapper::create(v4, d()->object(), - r.type, Heap::QQmlTypeWrapper::ExcludeEnums); - } else if (r.importNamespace) { - return QQmlTypeWrapper::create(v4, d()->object(), - qmlContext->imports, r.importNamespace, Heap::QQmlTypeWrapper::ExcludeEnums); - } - Q_ASSERT(!"Unreachable"); - } - } + if (auto importProperty = getPropertyFromImports(v4, name, qmlContext, d()->object(), hasProperty)) + return *importProperty; } return QV4::Object::virtualGet(this, name->propertyKey(), this, hasProperty); } @@ -361,13 +385,8 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC return QV4::Encode::null(); } - if (name->equals(engine->id_destroy()) || name->equals(engine->id_toString())) { - int index = name->equals(engine->id_destroy()) ? QV4::QObjectMethod::DestroyMethod : QV4::QObjectMethod::ToStringMethod; - if (hasProperty) - *hasProperty = true; - ExecutionContext *global = engine->rootContext(); - return QV4::QObjectMethod::create(global, object, index); - } + if (auto methodValue = getDestroyOrToStringMethod(engine, name, object, hasProperty)) + return *methodValue; QQmlData *ddata = QQmlData::get(object, false); QQmlPropertyData local; @@ -829,6 +848,86 @@ OwnPropertyKeyIterator *QObjectWrapper::virtualOwnPropertyKeys(const Object *m, return new QObjectWrapperOwnPropertyKeyIterator; } +ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + // Keep this code in sync with ::getQmlProperty + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + Scope scope(engine); + + const QObjectWrapper *This = static_cast(object); + ScopedString name(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = engine->callingQmlContext(); + + QObject * const qobj = This->d()->object(); + + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + if (auto methodValue = getDestroyOrToStringMethod(engine, name, qobj)) + return *methodValue; + + QQmlData *ddata = QQmlData::get(qobj, false); + if (!ddata || !ddata->propertyCache) { + QQmlPropertyData local; + QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local); + return getProperty(engine, qobj, property, /*captureRequired*/true); + } + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); + + if (!property) { + // Check for attached properties + if (name->startsWithUpper()) { + if (auto importProperty = getPropertyFromImports(engine, name, qmlContext, qobj)) + return *importProperty; + } + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); + } + + lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QV4::QObjectWrapper::lookupGetter; + return lookup->getter(lookup, engine, *object); +} + +ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qobjectLookup.propertyCache->release(); + lookup->qobjectLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (!o || o->internalClass != lookup->qobjectLookup.ic) + return revertLookup(); + + const Heap::QObjectWrapper *This = static_cast(o); + QObject *qobj = This->object(); + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + QQmlData *ddata = QQmlData::get(qobj, /*create*/false); + if (!ddata || ddata->propertyCache != lookup->qobjectLookup.propertyCache) + return revertLookup(); + + QQmlPropertyData *property = lookup->qobjectLookup.propertyData; + return getProperty(engine, qobj, property, /*captureRequired = */true); +} + +bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, + const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + namespace QV4 { struct QObjectSlotDispatcher : public QtPrivate::QSlotObjectBase @@ -1920,13 +2019,13 @@ ReturnedValue QObjectMethod::create(ExecutionContext *scope, QObject *object, in return method.asReturnedValue(); } -ReturnedValue QObjectMethod::create(ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index) +ReturnedValue QObjectMethod::create(ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index) { Scope valueScope(scope); Scoped method(valueScope, valueScope.engine->memoryManager->allocate(scope)); - method->d()->setPropertyCache(valueType->d()->propertyCache()); + method->d()->setPropertyCache(valueType->propertyCache()); method->d()->index = index; - method->d()->valueTypeWrapper.set(valueScope.engine, valueType->d()); + method->d()->valueTypeWrapper.set(valueScope.engine, valueType); return method.asReturnedValue(); } diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 6465ee0fa6..a09e7b6e95 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -183,6 +183,11 @@ struct Q_QML_EXPORT QObjectWrapper : public Object void destroyObject(bool lastCall); static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); + + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + protected: static void setProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, const Value &value); @@ -228,7 +233,7 @@ struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject enum { DestroyMethod = -1, ToStringMethod = -2 }; static ReturnedValue create(QV4::ExecutionContext *scope, QObject *object, int index); - static ReturnedValue create(QV4::ExecutionContext *scope, const QQmlValueTypeWrapper *valueType, int index); + static ReturnedValue create(QV4::ExecutionContext *scope, Heap::QQmlValueTypeWrapper *valueType, int index); int methodIndex() const { return d()->index; } QObject *object() const { return d()->object(); } diff --git a/src/qml/jsruntime/qv4value_p.h b/src/qml/jsruntime/qv4value_p.h index 20a84beccd..b4a045edfb 100644 --- a/src/qml/jsruntime/qv4value_p.h +++ b/src/qml/jsruntime/qv4value_p.h @@ -863,6 +863,22 @@ struct ValueArray { // have wrong offsets between host and target. Q_STATIC_ASSERT(offsetof(ValueArray<0>, values) == 8); +class OptionalReturnedValue { + ReturnedValue value; +public: + + OptionalReturnedValue() : value(Value::emptyValue().asReturnedValue()) {} + explicit OptionalReturnedValue(ReturnedValue v) + : value(v) + { + Q_ASSERT(!Value::fromReturnedValue(v).isEmpty()); + } + + ReturnedValue operator->() const { return value; } + ReturnedValue operator*() const { return value; } + explicit operator bool() const { return !Value::fromReturnedValue(value).isEmpty(); } +}; + } QT_END_NAMESPACE diff --git a/src/qml/qml/qqmlvaluetypewrapper.cpp b/src/qml/qml/qqmlvaluetypewrapper.cpp index b503d75a47..b22d1530e2 100644 --- a/src/qml/qml/qqmlvaluetypewrapper.cpp +++ b/src/qml/qml/qqmlvaluetypewrapper.cpp @@ -51,6 +51,8 @@ #include #include #include +#include +#include #include QT_BEGIN_NAMESPACE @@ -372,6 +374,117 @@ ReturnedValue QQmlValueTypeWrapper::method_toString(const FunctionObject *b, con return Encode(b->engine()->newString(result)); } +Q_ALWAYS_INLINE static ReturnedValue getGadgetProperty(ExecutionEngine *engine, + Heap::QQmlValueTypeWrapper *valueTypeWrapper, + QQmlPropertyData *property) +{ + if (property->isFunction()) { + // calling a Q_INVOKABLE function of a value type + return QV4::QObjectMethod::create(engine->rootContext(), valueTypeWrapper, property->coreIndex()); + } + +#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ + if (property->propType() == metatype) { \ + cpptype v; \ + void *args[] = { &v, nullptr }; \ + metaObject->d.static_metacall(reinterpret_cast(valueTypeWrapper->gadgetPtr), \ + QMetaObject::ReadProperty, index, args); \ + return QV4::Encode(constructor(v)); \ + } + + const QMetaObject *metaObject = valueTypeWrapper->propertyCache()->metaObject(); + + int index = property->coreIndex(); + QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); + + // These four types are the most common used by the value type wrappers + VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); + VALUE_TYPE_LOAD(QMetaType::Int || property->isEnum(), int, int); + VALUE_TYPE_LOAD(QMetaType::Int, int, int); + VALUE_TYPE_LOAD(QMetaType::QString, QString, engine->newString); + VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); + + QVariant v; + void *args[] = { nullptr, nullptr }; + if (property->propType() == QMetaType::QVariant) { + args[0] = &v; + } else { + v = QVariant(property->propType(), static_cast(nullptr)); + args[0] = v.data(); + } + metaObject->d.static_metacall(reinterpret_cast(valueTypeWrapper->gadgetPtr), QMetaObject::ReadProperty, + index, args); + return engine->fromVariant(v); +#undef VALUE_TYPE_LOAD +} + +ReturnedValue QQmlValueTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, + Lookup *lookup) +{ + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + + const QQmlValueTypeWrapper *r = static_cast(object); + QV4::ExecutionEngine *v4 = r->engine(); + Scope scope(v4); + ScopedString name(scope, id.asStringOrSymbol()); + + // Note: readReferenceValue() can change the reference->type. + if (const QQmlValueTypeReference *reference = r->as()) { + if (!reference->readReferenceValue()) + return Value::undefinedValue().asReturnedValue(); + } + + QQmlPropertyData *result = r->d()->propertyCache()->property(name.getPointer(), nullptr, nullptr); + if (!result) + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); + + lookup->qgadgetLookup.ic = r->internalClass(); + lookup->qgadgetLookup.propertyCache = r->d()->propertyCache(); + lookup->qgadgetLookup.propertyCache->addref(); + lookup->qgadgetLookup.propertyData = result; + lookup->getter = QQmlValueTypeWrapper::lookupGetter; + return lookup->getter(lookup, engine, *object); +} + +ReturnedValue QQmlValueTypeWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object) +{ + const auto revertLookup = [lookup, engine, &object]() { + lookup->qgadgetLookup.propertyCache->release(); + lookup->qgadgetLookup.propertyCache = nullptr; + lookup->getter = Lookup::getterGeneric; + return Lookup::getterGeneric(lookup, engine, object); + }; + + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (!o || o->internalClass != lookup->qgadgetLookup.ic) + return revertLookup(); + + Heap::QQmlValueTypeWrapper *valueTypeWrapper = + const_cast(static_cast(o)); + if (valueTypeWrapper->propertyCache() != lookup->qgadgetLookup.propertyCache) + return revertLookup(); + + if (lookup->qgadgetLookup.ic->vtable == QQmlValueTypeReference::staticVTable()) { + Scope scope(engine); + Scoped referenceWrapper(scope, valueTypeWrapper); + referenceWrapper->readReferenceValue(); + } + + QQmlPropertyData *property = lookup->qgadgetLookup.propertyData; + return getGadgetProperty(engine, valueTypeWrapper, property); +} + +bool QQmlValueTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, + const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { Q_ASSERT(m->as()); @@ -397,43 +510,7 @@ ReturnedValue QQmlValueTypeWrapper::virtualGet(const Managed *m, PropertyKey id, if (hasProperty) *hasProperty = true; - if (result->isFunction()) - // calling a Q_INVOKABLE function of a value type - return QV4::QObjectMethod::create(v4->rootContext(), r, result->coreIndex()); - -#define VALUE_TYPE_LOAD(metatype, cpptype, constructor) \ - if (result->propType() == metatype) { \ - cpptype v; \ - void *args[] = { &v, 0 }; \ - metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); \ - return QV4::Encode(constructor(v)); \ - } - - const QMetaObject *metaObject = r->d()->propertyCache()->metaObject(); - - int index = result->coreIndex(); - QQmlMetaObject::resolveGadgetMethodOrPropertyIndex(QMetaObject::ReadProperty, &metaObject, &index); - - void *gadget = r->d()->gadgetPtr; - - // These four types are the most common used by the value type wrappers - VALUE_TYPE_LOAD(QMetaType::QReal, qreal, qreal); - VALUE_TYPE_LOAD(QMetaType::Int || result->isEnum(), int, int); - VALUE_TYPE_LOAD(QMetaType::Int, int, int); - VALUE_TYPE_LOAD(QMetaType::QString, QString, v4->newString); - VALUE_TYPE_LOAD(QMetaType::Bool, bool, bool); - - QVariant v; - void *args[] = { nullptr, nullptr }; - if (result->propType() == QMetaType::QVariant) { - args[0] = &v; - } else { - v = QVariant(result->propType(), static_cast(nullptr)); - args[0] = v.data(); - } - metaObject->d.static_metacall(reinterpret_cast(gadget), QMetaObject::ReadProperty, index, args); - return v4->fromVariant(v); -#undef VALUE_TYPE_ACCESSOR + return getGadgetProperty(v4, r->d(), result); } bool QQmlValueTypeWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) diff --git a/src/qml/qml/qqmlvaluetypewrapper_p.h b/src/qml/qml/qqmlvaluetypewrapper_p.h index 8db9474132..baac129afa 100644 --- a/src/qml/qml/qqmlvaluetypewrapper_p.h +++ b/src/qml/qml/qqmlvaluetypewrapper_p.h @@ -112,6 +112,9 @@ public: static PropertyAttributes virtualGetOwnProperty(const Managed *m, PropertyKey id, Property *p); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); static ReturnedValue method_toString(const FunctionObject *b, const Value *thisObject, const Value *argv, int argc); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); + static ReturnedValue lookupGetter(Lookup *lookup, ExecutionEngine *engine, const Value &object); static void initProto(ExecutionEngine *v4); }; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index debf14df97..006825cc93 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -52,6 +52,7 @@ #include #include #include +#include #include #include @@ -1613,6 +1614,12 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va return that->engine()->fromVariant(value); } +ReturnedValue ModelObject::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + lookup->getter = Lookup::getterFallback; + return lookup->getter(lookup, engine, *object); +} + struct ModelObjectOwnPropertyKeyIterator : ObjectOwnPropertyKeyIterator { int roleNameIndex = 0; diff --git a/src/qml/types/qqmllistmodel_p_p.h b/src/qml/types/qqmllistmodel_p_p.h index ff52ee049f..2876c71de6 100644 --- a/src/qml/types/qqmllistmodel_p_p.h +++ b/src/qml/types/qqmllistmodel_p_p.h @@ -181,6 +181,8 @@ struct ModelObject : public QObjectWrapper protected: static bool virtualPut(Managed *m, PropertyKey id, const Value& value, Value *receiver); static ReturnedValue virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); static OwnPropertyKeyIterator *virtualOwnPropertyKeys(const Object *m, Value *target); }; -- cgit v1.2.3 From 0b7e479235aec74f051af4d5ef95e74753b59c6d Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Fri, 22 Feb 2019 18:13:34 +0100 Subject: Create import directory from intercepted URL, not orignial one If the qmldir URL got intercepted, we should use the intercepted URL to get to the contents to be loaded. Fixes: QTBUG-73843 Change-Id: I51715575e767ed429a8237517f47196677409fe0 Reviewed-by: Simon Hausmann --- src/qml/qml/qqmlimport.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/qml') diff --git a/src/qml/qml/qqmlimport.cpp b/src/qml/qml/qqmlimport.cpp index e379d416fd..a31dbd1e08 100644 --- a/src/qml/qml/qqmlimport.cpp +++ b/src/qml/qml/qqmlimport.cpp @@ -1546,7 +1546,7 @@ bool QQmlImportsPrivate::addFileImport(const QString& uri, const QString &prefix QString localFileOrQrc = QQmlFile::urlToLocalFileOrQrc(qmldirUrl); Q_ASSERT(!localFileOrQrc.isEmpty()); - QString dir = QQmlFile::urlToLocalFileOrQrc(resolveLocalUrl(base, importUri)); + const QString dir = localFileOrQrc.left(localFileOrQrc.lastIndexOf(Slash) + 1); if (!typeLoader->directoryExists(dir)) { if (!isImplicitImport) { QQmlError error; -- cgit v1.2.3 From fdd988494aedba9f58b54e958fab91f566186ed1 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Thu, 14 Mar 2019 13:44:47 +0100 Subject: Doc: Clarify section about qmlRegisterInterface() There is very little you can do with things registered as interfaces. Change-Id: I5e4dcf8529c2d7c8012db3fa1dcfc23563cc2cba Fixes: QTBUG-74318 Reviewed-by: Simon Hausmann --- src/qml/doc/src/cppintegration/definetypes.qdoc | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/qml') diff --git a/src/qml/doc/src/cppintegration/definetypes.qdoc b/src/qml/doc/src/cppintegration/definetypes.qdoc index f6f630c749..41bc9fd140 100644 --- a/src/qml/doc/src/cppintegration/definetypes.qdoc +++ b/src/qml/doc/src/cppintegration/definetypes.qdoc @@ -142,9 +142,10 @@ types: \li qmlRegisterType() (with no parameters) registers a C++ type that is not instantiable and cannot be referred to from QML. This enables the engine to coerce any inherited types that are instantiable from QML. -\li qmlRegisterInterface() registers a Qt interface type with a specific QML -type name. The type is not instantiable from QML but can be referred to by its -type name. +\li qmlRegisterInterface() registers an existing Qt interface type. The type is +not instantiable from QML, and you cannot declare QML properties with it. Using +C++ properties of this type from QML will do the expected interface casts, +though. \li qmlRegisterUncreatableType() registers a named C++ type that is not instantiable but should be identifiable as a type to the QML type system. This is useful if a type's enums or attached properties should be accessible from QML -- cgit v1.2.3 From c9e6251cc8dfcf002f64b07e48dd68b7edd3f630 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Wed, 2 Jan 2019 16:09:56 +0100 Subject: Implement dummy QML lookups for "global" variables When resolving names in the context of QML bindings, we now direct runtime access to QQmlContextWrapper::resolveQmlPropertyLookupGetter. At the moment this does basically the same as Runtime::method_loadName, which we called earlier. However this now provides the opportunity to optimize lookups in the QML context in a central place. When performing a call on a scope or context object property, we also did not use a CallName() instruction - which would have gotten the thisObject wrong - but instead we use a dedicated CallScopeObjectProperty and CallContextObjectProperty instruction. These rely on identifying these properties at compile time, which goes away with lookups (and also doesn't work when using ahead-of-time compilation). Therefore the qml context property lookup is using a getPropertyAndBase style signature and Runtime::method_callQmlContextPropertyLookup uses that. For the tests to pass, some error expectations need adjusting. In particular the compile-time detection of write attempts to id objects is now delayed to the run-time. The old code path is still there and will be removed separately in the next commit (as it is massive). Task-number: QTBUG-69898 Change-Id: Iad1ff93d3758c4db984a7c2d003beee21ed2275c Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlirbuilder.cpp | 3 +- src/qml/compiler/qv4bytecodehandler.cpp | 6 ++++ src/qml/compiler/qv4codegen.cpp | 35 +++++++++++++------ src/qml/compiler/qv4codegen_p.h | 5 ++- src/qml/compiler/qv4compileddata.cpp | 2 ++ src/qml/compiler/qv4compileddata_p.h | 7 ++-- src/qml/compiler/qv4compiler.cpp | 12 ++++--- src/qml/compiler/qv4compiler_p.h | 2 +- src/qml/compiler/qv4compilercontext.cpp | 7 ++-- src/qml/compiler/qv4compilercontext_p.h | 1 + src/qml/compiler/qv4instr_moth.cpp | 8 +++++ src/qml/compiler/qv4instr_moth_p.h | 4 +++ src/qml/jit/qv4baselinejit.cpp | 22 ++++++++++++ src/qml/jit/qv4baselinejit_p.h | 2 ++ src/qml/jit/qv4jithelpers.cpp | 6 ++++ src/qml/jit/qv4jithelpers_p.h | 1 + src/qml/jsruntime/qv4engine.cpp | 8 +++++ src/qml/jsruntime/qv4engine_p.h | 1 + src/qml/jsruntime/qv4lookup_p.h | 1 + src/qml/jsruntime/qv4qmlcontext.cpp | 62 ++++++++++++++++++++++++++++----- src/qml/jsruntime/qv4qmlcontext_p.h | 4 +++ src/qml/jsruntime/qv4runtime.cpp | 49 ++++++++++++++++---------- src/qml/jsruntime/qv4runtimeapi_p.h | 1 + src/qml/jsruntime/qv4vme_moth.cpp | 13 +++++++ 24 files changed, 213 insertions(+), 49 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 59b9968bf6..4b4d3c27e8 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -2228,7 +2228,8 @@ void JSCodeGen::beginFunctionBodyHook() QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name) { #ifndef V4_BOOTSTRAP - if (_disableAcceleratedLookups) + // FIXME: Remove this function. + if (_disableAcceleratedLookups || true) return Reference(); // Implement QML lookup semantics in the current file context. diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index af86b70014..a34472010b 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -192,6 +192,9 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(LoadGlobalLookup) COLLECTOR_END_INSTR(LoadGlobalLookup) + COLLECTOR_BEGIN_INSTR(LoadQmlContextPropertyLookup) + COLLECTOR_END_INSTR(LoadQmlContextPropertyLookup) + COLLECTOR_BEGIN_INSTR(StoreNameSloppy) COLLECTOR_END_INSTR(StoreNameSloppy) @@ -270,6 +273,9 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(CallGlobalLookup) COLLECTOR_END_INSTR(CallGlobalLookup) + COLLECTOR_BEGIN_INSTR(CallQmlContextPropertyLookup) + COLLECTOR_END_INSTR(CallQmlContextPropertyLookup) + COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty) COLLECTOR_END_INSTR(CallScopeObjectProperty) diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index 66c334d197..de87d6d48c 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1982,11 +1982,19 @@ void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunctio call.argv = calldata.argv; bytecodeGenerator->addInstruction(call); } else if (!disable_lookups && useFastLookups && base.global) { - Instruction::CallGlobalLookup call; - call.index = registerGlobalGetterLookup(base.nameAsIndex()); - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); + if (base.qmlGlobal) { + Instruction::CallQmlContextPropertyLookup call; + call.index = registerQmlContextPropertyGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } else { + Instruction::CallGlobalLookup call; + call.index = registerGlobalGetterLookup(base.nameAsIndex()); + call.argc = calldata.argc; + call.argv = calldata.argv; + bytecodeGenerator->addInstruction(call); + } } else { Instruction::CallName call; call.name = base.nameAsIndex(); @@ -2361,8 +2369,9 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co return fallback; Reference r = Reference::fromName(this, name); - r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global); - if (!r.global && canAccelerateGlobalLookups() && m_globalNames.contains(name)) + r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal); + r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal; + if (!r.global && !r.qmlGlobal && m_globalNames.contains(name)) r.global = true; return r; } @@ -4385,9 +4394,15 @@ QT_WARNING_POP } } if (!disable_lookups && global) { - Instruction::LoadGlobalLookup load; - load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); - codegen->bytecodeGenerator->addInstruction(load); + if (qmlGlobal) { + Instruction::LoadQmlContextPropertyLookup load; + load.index = codegen->registerQmlContextPropertyGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addInstruction(load); + } else { + Instruction::LoadGlobalLookup load; + load.index = codegen->registerGlobalGetterLookup(nameAsIndex()); + codegen->bytecodeGenerator->addInstruction(load); + } } else { Instruction::LoadName load; load.name = nameAsIndex(); diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index 0b25d9c53d..b1cc4c090a 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -207,7 +207,8 @@ public: subscriptRequiresTDZCheck(false), stackSlotIsLocalOrArgument(false), isVolatile(false), - global(false) + global(false), + qmlGlobal(false) {} Reference(const Reference &) = default; @@ -421,6 +422,7 @@ public: quint32 stackSlotIsLocalOrArgument:1; quint32 isVolatile:1; quint32 global:1; + quint32 qmlGlobal:1; private: void storeAccumulator() const; @@ -553,6 +555,7 @@ public: int registerGetterLookup(int nameIndex) { return jsUnitGenerator->registerGetterLookup(nameIndex); } int registerSetterLookup(int nameIndex) { return jsUnitGenerator->registerSetterLookup(nameIndex); } int registerGlobalGetterLookup(int nameIndex) { return jsUnitGenerator->registerGlobalGetterLookup(nameIndex); } + int registerQmlContextPropertyGetterLookup(int nameIndex) { return jsUnitGenerator->registerQmlContextPropertyGetterLookup(nameIndex); } // Returns index in _module->functions virtual int defineFunction(const QString &name, AST::Node *ast, diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index e428340f62..6701443971 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -170,6 +170,8 @@ QV4::Function *CompilationUnit::linkToEngine(ExecutionEngine *engine) l->setter = QV4::Lookup::setterGeneric; else if (type == CompiledData::Lookup::Type_GlobalGetter) l->globalGetter = QV4::Lookup::globalGetterGeneric; + else if (type == CompiledData::Lookup::Type_QmlContextPropertyGetter) + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; l->nameIndex = compiledLookups[i].nameIndex; } } diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index c4821daa86..d63b1fd2b9 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -160,9 +160,10 @@ static_assert(sizeof(RegExp) == 4, "RegExp structure needs to have the expected struct Lookup { enum Type : unsigned int { - Type_Getter = 0x0, - Type_Setter = 0x1, - Type_GlobalGetter = 2 + Type_Getter = 0, + Type_Setter = 1, + Type_GlobalGetter = 2, + Type_QmlContextPropertyGetter = 3 }; union { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index 3076c6b526..a3c9347c67 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -154,15 +154,19 @@ int QV4::Compiler::JSUnitGenerator::registerSetterLookup(int nameIndex) return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(const QString &name) +int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) { - return registerGlobalGetterLookup(registerString(name)); + CompiledData::Lookup l; + l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; + l.nameIndex = nameIndex; + lookups << l; + return lookups.size() - 1; } -int QV4::Compiler::JSUnitGenerator::registerGlobalGetterLookup(int nameIndex) +int QV4::Compiler::JSUnitGenerator::registerQmlContextPropertyGetterLookup(int nameIndex) { CompiledData::Lookup l; - l.type_and_flags = CompiledData::Lookup::Type_GlobalGetter; + l.type_and_flags = CompiledData::Lookup::Type_QmlContextPropertyGetter; l.nameIndex = nameIndex; lookups << l; return lookups.size() - 1; diff --git a/src/qml/compiler/qv4compiler_p.h b/src/qml/compiler/qv4compiler_p.h index 2f5889ab53..49e334bb81 100644 --- a/src/qml/compiler/qv4compiler_p.h +++ b/src/qml/compiler/qv4compiler_p.h @@ -118,8 +118,8 @@ struct Q_QML_PRIVATE_EXPORT JSUnitGenerator { int registerGetterLookup(int nameIndex); int registerSetterLookup(const QString &name); int registerSetterLookup(int nameIndex); - int registerGlobalGetterLookup(const QString &name); int registerGlobalGetterLookup(int nameIndex); + int registerQmlContextPropertyGetterLookup(int nameIndex); int registerRegExp(QQmlJS::AST::RegExpLiteral *regexp); diff --git a/src/qml/compiler/qv4compilercontext.cpp b/src/qml/compiler/qv4compilercontext.cpp index 5772bff7bf..84c5d67e8d 100644 --- a/src/qml/compiler/qv4compilercontext.cpp +++ b/src/qml/compiler/qv4compilercontext.cpp @@ -187,10 +187,13 @@ Context::ResolvedName Context::resolveName(const QString &name, const QQmlJS::AS } // ### can we relax the restrictions here? - if (c->contextType == ContextType::Eval || c->contextType == ContextType::Binding) + if (c->contextType == ContextType::Eval) return result; - result.type = ResolvedName::Global; + if (c->contextType == ContextType::Binding) + result.type = ResolvedName::QmlGlobal; + else + result.type = ResolvedName::Global; return result; } diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index 328715da07..c9e54c0d1b 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -336,6 +336,7 @@ struct Context { struct ResolvedName { enum Type { Unresolved, + QmlGlobal, Global, Local, Stack, diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index 83aca3a02c..d2e95b83c4 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -286,6 +286,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index; MOTH_END_INSTR(LoadGlobalLookup) + MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) + d << index; + MOTH_END_INSTR(LoadQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(StoreNameSloppy) d << name; MOTH_END_INSTR(StoreNameSloppy) @@ -388,6 +392,10 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallGlobalLookup) + MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) + d << index << dumpArguments(argc, argv, nFormals); + MOTH_END_INSTR(CallQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallScopeObjectProperty) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 2ca8f692b8..1c77e6050c 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -86,6 +86,7 @@ QT_BEGIN_NAMESPACE #define INSTR_LoadClosure(op) INSTRUCTION(op, LoadClosure, 1, value) #define INSTR_LoadName(op) INSTRUCTION(op, LoadName, 1, name) #define INSTR_LoadGlobalLookup(op) INSTRUCTION(op, LoadGlobalLookup, 1, index) +#define INSTR_LoadQmlContextPropertyLookup(op) INSTRUCTION(op, LoadQmlContextPropertyLookup, 1, index) #define INSTR_StoreNameSloppy(op) INSTRUCTION(op, StoreNameSloppy, 1, name) #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) #define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) @@ -113,6 +114,7 @@ QT_BEGIN_NAMESPACE #define INSTR_CallName(op) INSTRUCTION(op, CallName, 3, name, argc, argv) #define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) +#define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) #define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv) #define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv) #define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) @@ -228,6 +230,7 @@ QT_BEGIN_NAMESPACE F(LoadClosure) \ F(LoadName) \ F(LoadGlobalLookup) \ + F(LoadQmlContextPropertyLookup) \ F(StoreNameSloppy) \ F(StoreNameStrict) \ F(LoadElement) \ @@ -296,6 +299,7 @@ QT_BEGIN_NAMESPACE F(CallName) \ F(CallPossiblyDirectEval) \ F(CallGlobalLookup) \ + F(CallQmlContextPropertyLookup) \ F(CallScopeObjectProperty) \ F(CallContextObjectProperty) \ F(CallWithSpread) \ diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 098bbfc6c6..47cef3b3bd 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -213,6 +213,16 @@ void BaselineJIT::generate_LoadGlobalLookup(int index) as->checkException(); } +void BaselineJIT::generate_LoadQmlContextPropertyLookup(int index) +{ + as->prepareCallWithArgCount(3); + as->passInt32AsArg(index, 2); + as->passEngineAsArg(1); + as->passFunctionAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(Helpers::loadQmlContextPropertyLookup, CallResultDestination::InAccumulator); + as->checkException(); +} + void BaselineJIT::generate_StoreNameSloppy(int name) { STORE_IP(); @@ -514,6 +524,18 @@ void BaselineJIT::generate_CallGlobalLookup(int index, int argc, int argv) as->checkException(); } +void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int argv) +{ + STORE_IP(); + as->prepareCallWithArgCount(4); + as->passInt32AsArg(argc, 3); + as->passJSSlotAsArg(argv, 2); + as->passInt32AsArg(index, 1); + as->passEngineAsArg(0); + BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextPropertyLookup, CallResultDestination::InAccumulator); + as->checkException(); +} + void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) { STORE_IP(); diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 98d23f4517..4db1eb1806 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -97,6 +97,7 @@ public: void generate_LoadClosure(int value) override; void generate_LoadName(int name) override; void generate_LoadGlobalLookup(int index) override; + void generate_LoadQmlContextPropertyLookup(int index) override; void generate_StoreNameSloppy(int name) override; void generate_StoreNameStrict(int name) override; void generate_LoadElement(int base) override; @@ -128,6 +129,7 @@ public: void generate_CallName(int name, int argc, int argv) override; void generate_CallPossiblyDirectEval(int argc, int argv) override; void generate_CallGlobalLookup(int index, int argc, int argv) override; + void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override; void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override; void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; diff --git a/src/qml/jit/qv4jithelpers.cpp b/src/qml/jit/qv4jithelpers.cpp index f43f37ad70..674fd8c8c8 100644 --- a/src/qml/jit/qv4jithelpers.cpp +++ b/src/qml/jit/qv4jithelpers.cpp @@ -70,6 +70,12 @@ ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index) return l->globalGetter(l, engine); } +ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index) +{ + Lookup *l = f->compilationUnit->runtimeLookups + index; + return l->qmlContextPropertyGetter(l, engine, nullptr); +} + ReturnedValue toObject(ExecutionEngine *engine, const Value &obj) { if (obj.isObject()) diff --git a/src/qml/jit/qv4jithelpers_p.h b/src/qml/jit/qv4jithelpers_p.h index bd5f65034d..d9abfc071e 100644 --- a/src/qml/jit/qv4jithelpers_p.h +++ b/src/qml/jit/qv4jithelpers_p.h @@ -66,6 +66,7 @@ namespace Helpers { void convertThisToObject(ExecutionEngine *engine, Value *t); ReturnedValue loadGlobalLookup(Function *f, ExecutionEngine *engine, int index); +ReturnedValue loadQmlContextPropertyLookup(Function *f, ExecutionEngine *engine, int index); ReturnedValue toObject(ExecutionEngine *engine, const Value &obj); ReturnedValue exp(const Value &base, const Value &exp); ReturnedValue getLookup(Function *f, ExecutionEngine *engine, const Value &base, int index); diff --git a/src/qml/jsruntime/qv4engine.cpp b/src/qml/jsruntime/qv4engine.cpp index f00578aa70..1d0c7c13fa 100644 --- a/src/qml/jsruntime/qv4engine.cpp +++ b/src/qml/jsruntime/qv4engine.cpp @@ -1169,6 +1169,14 @@ ReturnedValue ExecutionEngine::throwTypeError(const QString &message) return throwError(error); } +ReturnedValue ExecutionEngine::throwReferenceError(const QString &name) +{ + Scope scope(this); + QString msg = name + QLatin1String(" is not defined"); + ScopedObject error(scope, newReferenceErrorObject(msg)); + return throwError(error); +} + ReturnedValue ExecutionEngine::throwReferenceError(const Value &value) { Scope scope(this); diff --git a/src/qml/jsruntime/qv4engine_p.h b/src/qml/jsruntime/qv4engine_p.h index 64c7e2f32b..86367c0ece 100644 --- a/src/qml/jsruntime/qv4engine_p.h +++ b/src/qml/jsruntime/qv4engine_p.h @@ -569,6 +569,7 @@ public: ReturnedValue throwTypeError(); ReturnedValue throwTypeError(const QString &message); ReturnedValue throwReferenceError(const Value &value); + ReturnedValue throwReferenceError(const QString &name); ReturnedValue throwReferenceError(const QString &value, const QString &fileName, int lineNumber, int column); ReturnedValue throwRangeError(const Value &value); ReturnedValue throwRangeError(const QString &message); diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index b0d5706dea..e4d9ad8328 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -68,6 +68,7 @@ struct Lookup { union { ReturnedValue (*getter)(Lookup *l, ExecutionEngine *engine, const Value &object); ReturnedValue (*globalGetter)(Lookup *l, ExecutionEngine *engine); + ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject); bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; union { diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 88b0822f42..ae458c604a 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -55,6 +55,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -77,14 +79,11 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base) { - Q_ASSERT(m->as()); - if (!id.isString()) - return Object::virtualGet(m, id, receiver, hasProperty); + return Object::virtualGet(resource, id, receiver, hasProperty); - const QQmlContextWrapper *resource = static_cast(m); QV4::ExecutionEngine *v4 = resource->engine(); QV4::Scope scope(v4); @@ -100,11 +99,11 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c } } - return Object::virtualGet(m, id, receiver, hasProperty); + return Object::virtualGet(resource, id, receiver, hasProperty); } bool hasProp = false; - ScopedValue result(scope, Object::virtualGet(m, id, receiver, &hasProp)); + ScopedValue result(scope, Object::virtualGet(resource, id, receiver, &hasProp)); if (hasProp) { if (hasProperty) *hasProperty = hasProp; @@ -234,6 +233,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c if (hasProp) { if (hasProperty) *hasProperty = true; + if (base) + *base = QV4::QObjectWrapper::wrap(v4, scopeObject); return result->asReturnedValue(); } } @@ -247,6 +248,8 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c if (hasProp) { if (hasProperty) *hasProperty = true; + if (base) + *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); return result->asReturnedValue(); } } @@ -264,6 +267,13 @@ ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, c return Encode::undefined(); } +ReturnedValue QQmlContextWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) +{ + Q_ASSERT(m->as()); + const QQmlContextWrapper *This = static_cast(m); + return getPropertyAndBase(This, id, receiver, hasProperty, /*base*/nullptr); +} + bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &value, Value *receiver) { Q_ASSERT(m->as()); @@ -298,8 +308,16 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val while (context) { const QV4::IdentifierHash &properties = context->propertyNames(); // Search context properties - if (properties.count() && properties.value(name) != -1) - return false; + if (properties.count()) { + const int propertyIndex = properties.value(name); + if (propertyIndex != -1) { + if (propertyIndex < context->idValueCount) { + v4->throwError(QLatin1String("left-hand side of assignment operator is not an lvalue")); + return false; + } + return false; + } + } // Search scope object if (scopeObject && @@ -323,6 +341,32 @@ bool QQmlContextWrapper::virtualPut(Managed *m, PropertyKey id, const Value &val return false; } +ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Scope scope(engine); + PropertyKey name =engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit-> + runtimeStrings[l->nameIndex]); + + // Special hack for bounded signal expressions, where the parameters of signals are injected + // into the handler expression through the locals of the call context. So for onClicked: { ... } + // the parameters of the clicked signal are injected and we must allow for them to be found here + // before any other property from the QML context. + ExecutionContext &ctx = static_cast(engine->currentStackFrame->jsFrame->context); + if (ctx.d()->type == Heap::ExecutionContext::Type_CallContext) { + uint index = ctx.d()->internalClass->indexOfValueOrGetter(name); + if (index < UINT_MAX) + return static_cast(ctx.d())->locals[index].asReturnedValue(); + } + + Scoped qmlContext(scope, engine->qmlContext()->qml()); + bool hasProperty = false; + ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr, + &hasProperty, base)); + if (!hasProperty) + return engine->throwReferenceError(name.toQString()); + return result->asReturnedValue(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index dd6de3323d..f704280bf4 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -99,8 +99,12 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object inline QObject *getScopeObject() const { return d()->scopeObject; } inline QQmlContextData *getContext() const { return *d()->context; } + static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, + bool *hasProperty, Value *base); 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); + + static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 53dd3a66dd..7163a51af1 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1301,18 +1301,42 @@ uint Runtime::method_compareIn(ExecutionEngine *engine, const Value &left, const return v->booleanValue(); } +static ReturnedValue throwPropertyIsNotAFunctionTypeError(ExecutionEngine *engine, Value *thisObject, const QString &propertyName) +{ + QString objectAsString = QStringLiteral("[null]"); + if (!thisObject->isUndefined()) + objectAsString = thisObject->toQStringNoThrow(); + QString msg = QStringLiteral("Property '%1' of object %2 is not a function") + .arg(propertyName, objectAsString); + return engine->throwTypeError(msg); +} ReturnedValue Runtime::method_callGlobalLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) { + Scope scope(engine); Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; Value function = Value::fromReturnedValue(l->globalGetter(l, engine)); + Value thisObject = Value::undefinedValue(); if (!function.isFunctionObject()) - return engine->throwTypeError(); + return throwPropertyIsNotAFunctionTypeError(engine, &thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); - Value thisObject = Value::undefinedValue(); return static_cast(function).call(&thisObject, argv, argc); } +ReturnedValue Runtime::method_callQmlContextPropertyLookup(ExecutionEngine *engine, uint index, Value *argv, int argc) +{ + Scope scope(engine); + ScopedValue thisObject(scope); + Lookup *l = engine->currentStackFrame->v4Function->compilationUnit->runtimeLookups + index; + Value function = Value::fromReturnedValue(l->qmlContextPropertyGetter(l, engine, thisObject)); + if (!function.isFunctionObject()) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[l->nameIndex]->toQString()); + + return static_cast(function).call(thisObject, argv, argc); +} + ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Value *argv, int argc) { Scope scope(engine); @@ -1323,13 +1347,8 @@ ReturnedValue Runtime::method_callPossiblyDirectEval(ExecutionEngine *engine, Va if (engine->hasException) return Encode::undefined(); - if (!function) { - QString objectAsString = QStringLiteral("[null]"); - if (!thisObject->isUndefined()) - objectAsString = thisObject->toQStringNoThrow(); - QString msg = QStringLiteral("Property 'eval' of object %2 is not a function").arg(objectAsString); - return engine->throwTypeError(msg); - } + if (!function) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, QLatin1String("eval")); if (function->d() == engine->evalFunction()->d()) return static_cast(function.getPointer())->evalCall(thisObject, argv, argc, true); @@ -1348,15 +1367,9 @@ ReturnedValue Runtime::method_callName(ExecutionEngine *engine, int nameIndex, V if (engine->hasException) return Encode::undefined(); - if (!f) { - QString objectAsString = QStringLiteral("[null]"); - if (!thisObject->isUndefined()) - objectAsString = thisObject->toQStringNoThrow(); - QString msg = QStringLiteral("Property '%1' of object %2 is not a function") - .arg(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString(), - objectAsString); - return engine->throwTypeError(msg); - } + if (!f) + return throwPropertyIsNotAFunctionTypeError(engine, thisObject, + engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[nameIndex]->toQString()); return f->call(thisObject, argv, argc); } diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index 06e8a8a960..aa4ac27621 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -93,6 +93,7 @@ struct ExceptionCheck { #define FOR_EACH_RUNTIME_METHOD(F) \ /* call */ \ F(ReturnedValue, callGlobalLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ + F(ReturnedValue, callQmlContextPropertyLookup, (ExecutionEngine *engine, uint index, Value *argv, int argc)) \ F(ReturnedValue, callName, (ExecutionEngine *engine, int nameIndex, Value *argv, int argc)) \ F(ReturnedValue, callProperty, (ExecutionEngine *engine, Value *base, int nameIndex, Value *argv, int argc)) \ F(ReturnedValue, callPropertyLookup, (ExecutionEngine *engine, Value *base, uint index, Value *argv, int argc)) \ diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 000e1ab9d5..2d9337b03e 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -556,6 +556,13 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(LoadGlobalLookup) + MOTH_BEGIN_INSTR(LoadQmlContextPropertyLookup) + STORE_IP(); + QV4::Lookup *l = function->compilationUnit->runtimeLookups + index; + acc = l->qmlContextPropertyGetter(l, engine, nullptr); + CHECK_EXCEPTION; + MOTH_END_INSTR(LoadQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(StoreNameStrict) STORE_IP(); STORE_ACC(); @@ -781,6 +788,12 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(CallGlobalLookup) + MOTH_BEGIN_INSTR(CallQmlContextPropertyLookup) + STORE_IP(); + acc = Runtime::method_callQmlContextPropertyLookup(engine, index, stack + argv, argc); + CHECK_EXCEPTION; + MOTH_END_INSTR(CallQmlContextPropertyLookup) + MOTH_BEGIN_INSTR(CallScopeObjectProperty) STORE_IP(); acc = Runtime::method_callQmlScopeObjectProperty(engine, stack + base, name, stack + argv, argc); -- cgit v1.2.3 From 5cfccf30898aed5ca96c0f8779b0f8a1117118b7 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2019 15:44:34 +0100 Subject: Remove dead compile time QML context/scope property and id object code After enabling lookups in QML files, we can remove all the code that tries to deal with (type) compile time detection of access to id objects and properties of the scope/context object. This also allows removing quite a bit of run-time code paths and even byte code instructions. Task-number: QTBUG-69898 Change-Id: I7b26d7983393594a3ef56466d3e633f1822b76f4 Reviewed-by: Ulf Hermann --- src/qml/compiler/qqmlirbuilder.cpp | 410 +------------------------------ src/qml/compiler/qqmlirbuilder_p.h | 34 +-- src/qml/compiler/qqmltypecompiler.cpp | 33 +-- src/qml/compiler/qv4bytecodehandler.cpp | 27 -- src/qml/compiler/qv4codegen.cpp | 71 +----- src/qml/compiler/qv4codegen_p.h | 58 +---- src/qml/compiler/qv4compileddata_p.h | 27 +- src/qml/compiler/qv4compiler.cpp | 45 +--- src/qml/compiler/qv4compilercontext_p.h | 5 - src/qml/compiler/qv4instr_moth.cpp | 36 --- src/qml/compiler/qv4instr_moth_p.h | 16 -- src/qml/jit/qv4baselinejit.cpp | 103 -------- src/qml/jit/qv4baselinejit_p.h | 13 - src/qml/jsruntime/qv4function.cpp | 1 - src/qml/jsruntime/qv4function_p.h | 1 - src/qml/jsruntime/qv4qobjectwrapper.cpp | 28 +-- src/qml/jsruntime/qv4qobjectwrapper_p.h | 3 +- src/qml/jsruntime/qv4runtime.cpp | 86 ------- src/qml/jsruntime/qv4runtimeapi_p.h | 14 +- src/qml/jsruntime/qv4vme_moth.cpp | 51 ---- src/qml/qml/qqmlbinding.cpp | 32 ++- src/qml/qml/qqmljavascriptexpression.cpp | 82 +------ src/qml/qml/qqmljavascriptexpression_p.h | 21 +- src/qml/types/qqmllistmodel.cpp | 3 +- 24 files changed, 41 insertions(+), 1159 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qqmlirbuilder.cpp b/src/qml/compiler/qqmlirbuilder.cpp index 4b4d3c27e8..ab43ea350b 100644 --- a/src/qml/compiler/qqmlirbuilder.cpp +++ b/src/qml/compiler/qqmlirbuilder.cpp @@ -974,7 +974,6 @@ bool IRBuilder::visit(QQmlJS::AST::UiSourceElement *node) foe->node = funDecl; foe->parentNode = funDecl; foe->nameIndex = registerString(funDecl->name.toString()); - foe->disableAcceleratedLookups = false; const int index = _object->functionsAndExpressions->append(foe); Function *f = New(); @@ -1095,7 +1094,6 @@ void IRBuilder::setBindingValue(QV4::CompiledData::Binding *binding, QQmlJS::AST expr->parentNode = parentNode; expr->nameIndex = registerString(QLatin1String("expression for ") + stringAt(binding->propertyNameIndex)); - expr->disableAcceleratedLookups = false; const int index = bindingsTarget()->functionsAndExpressions->append(expr); binding->value.compiledScriptIndex = index; // We don't need to store the binding script as string, except for script strings @@ -1822,19 +1820,13 @@ char *QmlUnitGenerator::writeBindings(char *bindingPtr, const Object *o, Binding JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, - QQmlJS::AST::UiProgram *qmlRoot, QQmlTypeNameCache *imports, + QQmlJS::AST::UiProgram *qmlRoot, const QV4::Compiler::StringTableGenerator *stringPool, const QSet &globalNames) : QV4::Compiler::Codegen(jsUnitGenerator, /*strict mode*/false) , sourceCode(sourceCode) , jsEngine(jsEngine) , qmlRoot(qmlRoot) - , imports(imports) , stringPool(stringPool) - , _disableAcceleratedLookups(false) - , _contextObject(nullptr) - , _scopeObject(nullptr) - , _qmlContextSlot(-1) - , _importedScriptsSlot(-1) { m_globalNames = globalNames; @@ -1842,18 +1834,6 @@ JSCodeGen::JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator * _fileNameIsUrl = true; } -void JSCodeGen::beginContextScope(const JSCodeGen::ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject) -{ - _idObjects = objectIds; - _contextObject = contextObject; - _scopeObject = nullptr; -} - -void JSCodeGen::beginObjectScope(QQmlPropertyCache *scopeObject) -{ - _scopeObject = scopeObject; -} - QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QList &functions) { auto qmlName = [&](const CompiledFunctionOrExpression &c) { @@ -1918,7 +1898,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListfinish(); } - _disableAcceleratedLookups = qmlFunction.disableAcceleratedLookups; int idx = defineFunction(name, function ? function : qmlFunction.parentNode, function ? function->formals : nullptr, body); @@ -1928,392 +1907,6 @@ QVector JSCodeGen::generateJSCodeForFunctionsAndBindings(const QListproperty(name, /*object*/nullptr, /*context*/nullptr); - - if (pd && !cache->isAllowedInRevision(pd)) - return nullptr; - - return pd; -} - -enum MetaObjectResolverFlags { - AllPropertiesAreFinal = 0x1, - LookupsIncludeEnums = 0x2, - LookupsExcludeProperties = 0x4, - ResolveTypeInformationOnly = 0x8 -}; - -#if 0 -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject); - -static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index); - -static QV4::IR::DiscoveredType resolveQmlType(QQmlEnginePrivate *qmlEngine, - const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - - QQmlType type = resolver->qmlType; - - if (member->name->constData()->isUpper()) { - bool ok = false; - int value = type.enumValue(qmlEngine, *member->name, &ok); - if (ok) { - member->setEnumValue(value); - return QV4::IR::SInt32Type; - } else { - int index = type.scopedEnumIndex(qmlEngine, *member->name, &ok); - if (ok) { - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initScopedEnumResolver(newResolver, type, index); - return QV4::IR::DiscoveredType(newResolver); - } - } - } - - if (type.isCompositeSingleton()) { - QQmlRefPointer tdata = qmlEngine->typeLoader.getType(type.singletonInstanceInfo()->url); - Q_ASSERT(tdata); - tdata->release(); // Decrease the reference count added from QQmlTypeLoader::getType() - // When a singleton tries to reference itself, it may not be complete yet. - if (tdata->isComplete()) { - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->propertyCacheForType(tdata->compilationUnit()->metaTypeId)); - newResolver->flags |= AllPropertiesAreFinal; - return newResolver->resolveMember(qmlEngine, newResolver, member); - } - } else if (type.isSingleton()) { - const QMetaObject *singletonMeta = type.singletonInstanceInfo()->instanceMetaObject; - if (singletonMeta) { // QJSValue-based singletons cannot be accelerated - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, qmlEngine->cache(singletonMeta)); - member->kind = QV4::IR::Member::MemberOfSingletonObject; - return newResolver->resolveMember(qmlEngine, newResolver, member); - } - } -#if 0 - else if (const QMetaObject *attachedMeta = type->attachedPropertiesType(qmlEngine)) { - // Right now the attached property IDs are not stable and cannot be embedded in the - // code that is cached on disk. - QQmlPropertyCache *cache = qmlEngine->cache(attachedMeta); - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - member->setAttachedPropertiesId(type->attachedPropertiesId(qmlEngine)); - return newResolver->resolveMember(qmlEngine, newResolver, member); - } -#endif - - return result; -} - -static void initQmlTypeResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveQmlType; - resolver->qmlType = qmlType; - resolver->typenameCache = 0; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveImportNamespace( - QQmlEnginePrivate *, const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlTypeNameCache *typeNamespace = resolver->typenameCache; - const QQmlImportRef *importNamespace = resolver->import; - - QQmlTypeNameCache::Result r = typeNamespace->query(*member->name, importNamespace); - if (r.isValid()) { - member->freeOfSideEffects = true; - if (r.scriptIndex != -1) { - // TODO: remember the index and replace with subscript later. - result = QV4::IR::VarType; - } else if (r.type.isValid()) { - // TODO: Propagate singleton information, so that it is loaded - // through the singleton getter in the run-time. Until then we - // can't accelerate access :( - if (!r.type.isSingleton()) { - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initQmlTypeResolver(newResolver, r.type); - return QV4::IR::DiscoveredType(newResolver); - } - } else { - Q_ASSERT(false); // How can this happen? - } - } - - return result; -} - -static void initImportNamespaceResolver(QV4::IR::MemberExpressionResolver *resolver, - QQmlTypeNameCache *imports, const QQmlImportRef *importNamespace) -{ - resolver->resolveMember = &resolveImportNamespace; - resolver->import = importNamespace; - resolver->typenameCache = imports; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveMetaObjectProperty( - QQmlEnginePrivate *qmlEngine, const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - QV4::IR::Type result = QV4::IR::VarType; - QQmlPropertyCache *metaObject = resolver->propertyCache; - - if (member->name->constData()->isUpper() && (resolver->flags & LookupsIncludeEnums)) { - const QMetaObject *mo = metaObject->createMetaObject(); - QByteArray enumName = member->name->toUtf8(); - for (int ii = mo->enumeratorCount() - 1; ii >= 0; --ii) { - QMetaEnum metaEnum = mo->enumerator(ii); - bool ok; - int value = metaEnum.keyToValue(enumName.constData(), &ok); - if (ok) { - member->setEnumValue(value); - return QV4::IR::SInt32Type; - } - } - } - - if (member->kind != QV4::IR::Member::MemberOfIdObjectsArray && member->kind != QV4::IR::Member::MemberOfSingletonObject && - qmlEngine && !(resolver->flags & LookupsExcludeProperties)) { - QQmlPropertyData *property = member->property; - if (!property && metaObject) { - if (QQmlPropertyData *candidate = metaObject->property(*member->name, /*object*/0, /*context*/0)) { - const bool isFinalProperty = (candidate->isFinal() || (resolver->flags & AllPropertiesAreFinal)) - && !candidate->isFunction(); - - if (lookupHints() - && !(resolver->flags & AllPropertiesAreFinal) - && !candidate->isFinal() - && !candidate->isFunction() - && candidate->isDirect()) { - qWarning() << "Hint: Access to property" << *member->name << "of" << metaObject->className() << "could be accelerated if it was marked as FINAL"; - } - - if (isFinalProperty && metaObject->isAllowedInRevision(candidate)) { - property = candidate; - member->inhibitTypeConversionOnWrite = true; - if (!(resolver->flags & ResolveTypeInformationOnly)) - member->property = candidate; // Cache for next iteration and isel needs it. - } - } - } - - if (property) { - // Enums cannot be mapped to IR types, they need to go through the run-time handling - // of accepting strings that will then be converted to the right values. - if (property->isEnum()) - return QV4::IR::VarType; - - switch (property->propType()) { - case QMetaType::Bool: result = QV4::IR::BoolType; break; - case QMetaType::Int: result = QV4::IR::SInt32Type; break; - case QMetaType::Double: result = QV4::IR::DoubleType; break; - case QMetaType::QString: result = QV4::IR::StringType; break; - default: - if (property->isQObject()) { - if (QQmlPropertyCache *cache = qmlEngine->propertyCacheForType(property->propType())) { - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - return QV4::IR::DiscoveredType(newResolver); - } - } else if (const QMetaObject *valueTypeMetaObject = QQmlValueTypeFactory::metaObjectForMetaType(property->propType())) { - if (QQmlPropertyCache *cache = qmlEngine->cache(valueTypeMetaObject)) { - auto newResolver = resolver->owner->New(); - newResolver->owner = resolver->owner; - initMetaObjectResolver(newResolver, cache); - newResolver->flags |= ResolveTypeInformationOnly; - return QV4::IR::DiscoveredType(newResolver); - } - } - break; - } - } - } - - return result; -} - -static void initMetaObjectResolver(QV4::IR::MemberExpressionResolver *resolver, QQmlPropertyCache *metaObject) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveMetaObjectProperty; - resolver->propertyCache = metaObject; - resolver->flags = 0; -} - -static QV4::IR::DiscoveredType resolveScopedEnum(QQmlEnginePrivate *qmlEngine, - const QV4::IR::MemberExpressionResolver *resolver, - QV4::IR::Member *member) -{ - if (!member->name->constData()->isUpper()) - return QV4::IR::VarType; - - QQmlType type = resolver->qmlType; - int index = resolver->flags; - - bool ok = false; - int value = type.scopedEnumValue(qmlEngine, index, *member->name, &ok); - if (!ok) - return QV4::IR::VarType; - member->setEnumValue(value); - return QV4::IR::SInt32Type; -} - -static void initScopedEnumResolver(QV4::IR::MemberExpressionResolver *resolver, const QQmlType &qmlType, int index) -{ - Q_ASSERT(resolver); - - resolver->resolveMember = &resolveScopedEnum; - resolver->qmlType = qmlType; - resolver->flags = index; -} -#endif - -#endif // V4_BOOTSTRAP - -void JSCodeGen::beginFunctionBodyHook() -{ - _qmlContextSlot = bytecodeGenerator->newRegister(); - _importedScriptsSlot = bytecodeGenerator->newRegister(); - -#ifndef V4_BOOTSTRAP - Instruction::LoadQmlContext load; - load.result = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); - bytecodeGenerator->addInstruction(load); - -#if 0 - temp->type = QV4::IR::QObjectType; - temp->memberResolver = _function->New(); - initMetaObjectResolver(temp->memberResolver, _scopeObject); - auto name = _block->NAME(QV4::IR::Name::builtin_qml_context, 0, 0); - name->type = temp->type; -#endif - - Instruction::LoadQmlImportedScripts loadScripts; - loadScripts.result = Reference::fromStackSlot(this, _importedScriptsSlot).stackSlot(); - bytecodeGenerator->addInstruction(loadScripts); -#endif -} - -QV4::Compiler::Codegen::Reference JSCodeGen::fallbackNameLookup(const QString &name) -{ -#ifndef V4_BOOTSTRAP - // FIXME: Remove this function. - if (_disableAcceleratedLookups || true) - return Reference(); - - // Implement QML lookup semantics in the current file context. - // - // Note: We do not check if properties of the qml scope object or context object - // are final. That's because QML tries to get as close as possible to lexical scoping, - // which means in terms of properties that only those visible at compile time are chosen. - // I.e. access to a "foo" property declared within the same QML component as "property int foo" - // will always access that instance and as integer. If a sub-type implements its own property string foo, - // then that one is not chosen for accesses from within this file, because it wasn't visible at compile - // time. This corresponds to the logic in QQmlPropertyCache::findProperty to find the property associated - // with the correct QML context. - - // Look for IDs first. - for (const IdMapping &mapping : qAsConst(_idObjects)) { - if (name == mapping.name) { - if (_context->contextType == QV4::Compiler::ContextType::Binding) - _context->idObjectDependencies.insert(mapping.idIndex); - - Instruction::LoadIdObject load; - load.base = Reference::fromStackSlot(this, _qmlContextSlot).stackSlot(); - load.index = mapping.idIndex; - - Reference result = Reference::fromAccumulator(this); - bytecodeGenerator->addInstruction(load); - result.isReadonly = true; - return result; - } - } - - if (name.at(0).isUpper()) { - QQmlTypeNameCache::Result r = imports->query(name); - if (r.isValid()) { - if (r.scriptIndex != -1) { - Reference imports = Reference::fromStackSlot(this, _importedScriptsSlot); - return Reference::fromSubscript(imports, Reference::fromConst(this, QV4::Encode(r.scriptIndex))); - } else if (r.type.isValid()) { - return Reference::fromName(this, name); - } else { - Q_ASSERT(r.importNamespace); - return Reference::fromName(this, name); - } - } - } - - if (_scopeObject) { - QQmlPropertyData *data = lookupQmlCompliantProperty(_scopeObject, name); - if (data) { - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (data->isFunction()) - return Reference::fromName(this, name); - - Reference base = Reference::fromStackSlot(this, _qmlContextSlot); - Reference::PropertyCapturePolicy capturePolicy; - if (!data->isConstant() && !data->isQmlBinding()) - capturePolicy = Reference::CaptureAtRuntime; - else - capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime; - return Reference::fromQmlScopeObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy); - } - } - - if (_contextObject) { - QQmlPropertyData *data = lookupQmlCompliantProperty(_contextObject, name); - if (data) { - // Q_INVOKABLEs can't be FINAL, so we have to look them up at run-time - if (data->isFunction()) - return Reference::fromName(this, name); - - Reference base = Reference::fromStackSlot(this, _qmlContextSlot); - Reference::PropertyCapturePolicy capturePolicy; - if (!data->isConstant() && !data->isQmlBinding()) - capturePolicy = Reference::CaptureAtRuntime; - else - capturePolicy = data->isConstant() ? Reference::DontCapture : Reference::CaptureAheadOfTime; - return Reference::fromQmlContextObject(base, data->coreIndex(), data->notifyIndex(), capturePolicy); - } - } -#else - Q_UNUSED(name) -#endif // V4_BOOTSTRAP - return Reference(); -} - #ifndef V4_BOOTSTRAP QQmlPropertyData *PropertyResolver::property(const QString &name, bool *notInRevision, RevisionCheck check) const @@ -2433,7 +2026,6 @@ QmlIR::Object *IRLoader::loadObject(const QV4::CompiledData::Object *serializedO b->value.compiledScriptIndex = functionIndices.count() - 1; QmlIR::CompiledFunctionOrExpression *foe = pool->New(); - foe->disableAcceleratedLookups = true; foe->nameIndex = 0; QQmlJS::AST::ExpressionNode *expr; diff --git a/src/qml/compiler/qqmlirbuilder_p.h b/src/qml/compiler/qqmlirbuilder_p.h index 6affca3ca6..56724bcda5 100644 --- a/src/qml/compiler/qqmlirbuilder_p.h +++ b/src/qml/compiler/qqmlirbuilder_p.h @@ -345,7 +345,6 @@ struct Q_QML_PRIVATE_EXPORT CompiledFunctionOrExpression QQmlJS::AST::Node *parentNode = nullptr; // FunctionDeclaration, Statement or Expression QQmlJS::AST::Node *node = nullptr; // FunctionDeclaration, Statement or Expression quint32 nameIndex = 0; - bool disableAcceleratedLookups = false; CompiledFunctionOrExpression *next = nullptr; }; @@ -606,47 +605,16 @@ struct Q_QML_PRIVATE_EXPORT JSCodeGen : public QV4::Compiler::Codegen { JSCodeGen(const QString &sourceCode, QV4::Compiler::JSUnitGenerator *jsUnitGenerator, QV4::Compiler::Module *jsModule, QQmlJS::Engine *jsEngine, QQmlJS::AST::UiProgram *qmlRoot, - QQmlTypeNameCache *imports, const QV4::Compiler::StringTableGenerator *stringPool, const QSet &globalNames); - - struct IdMapping - { - QString name; - int idIndex; - QQmlPropertyCache *type; - }; - typedef QVector ObjectIdMapping; - - void beginContextScope(const ObjectIdMapping &objectIds, QQmlPropertyCache *contextObject); - void beginObjectScope(QQmlPropertyCache *scopeObject); + const QV4::Compiler::StringTableGenerator *stringPool, const QSet &globalNames); // Returns mapping from input functions to index in IR::Module::functions / compiledData->runtimeFunctions QVector generateJSCodeForFunctionsAndBindings(const QList &functions); - int defineFunction(const QString &name, AST::Node *ast, - AST::FormalParameterList *formals, - AST::StatementList *body) override; - -protected: - void beginFunctionBodyHook() override; - bool canAccelerateGlobalLookups() const override { return !_disableAcceleratedLookups; } - Reference fallbackNameLookup(const QString &name) override; - private: - // returns nullptr if lookup needs to happen by name - QQmlPropertyData *lookupQmlCompliantProperty(QQmlPropertyCache *cache, const QString &name); - QString sourceCode; QQmlJS::Engine *jsEngine; // needed for memory pool QQmlJS::AST::UiProgram *qmlRoot; - QQmlTypeNameCache *imports; const QV4::Compiler::StringTableGenerator *stringPool; - - bool _disableAcceleratedLookups; - ObjectIdMapping _idObjects; - QQmlPropertyCache *_contextObject; - QQmlPropertyCache *_scopeObject; - int _qmlContextSlot; - int _importedScriptsSlot; }; struct Q_QML_PRIVATE_EXPORT IRLoader { diff --git a/src/qml/compiler/qqmltypecompiler.cpp b/src/qml/compiler/qqmltypecompiler.cpp index f753c78b1a..66d3afc7a0 100644 --- a/src/qml/compiler/qqmltypecompiler.cpp +++ b/src/qml/compiler/qqmltypecompiler.cpp @@ -146,7 +146,7 @@ QQmlRefPointer QQmlTypeCompiler::compile() document->jsModule.fileName = typeData->urlString(); document->jsModule.finalUrl = typeData->finalUrlString(); QmlIR::JSCodeGen v4CodeGenerator(document->code, &document->jsGenerator, &document->jsModule, &document->jsParserEngine, - document->program, typeNameCache.data(), &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); + document->program, &document->jsGenerator.stringTable, engine->v8engine()->illegalNames()); QQmlJSCodeGenerator jsCodeGen(this, &v4CodeGenerator); if (!jsCodeGen.generateCodeForComponents()) return nullptr; @@ -766,10 +766,6 @@ void QQmlScriptStringScanner::scan() if (!pd || pd->propType() != scriptStringMetaType) continue; - QmlIR::CompiledFunctionOrExpression *foe = obj->functionsAndExpressions->slowAt(binding->value.compiledScriptIndex); - if (foe) - foe->disableAcceleratedLookups = true; - QString script = compiler->bindingAsString(obj, binding->value.compiledScriptIndex); binding->stringIndex = compiler->registerString(script); } @@ -1323,24 +1319,6 @@ bool QQmlJSCodeGenerator::compileComponent(int contextObject) contextObject = componentBinding->value.objectIndex; } - QmlIR::JSCodeGen::ObjectIdMapping idMapping; - idMapping.reserve(obj->namedObjectsInComponent.count); - for (int i = 0; i < obj->namedObjectsInComponent.count; ++i) { - const int objectIndex = obj->namedObjectsInComponent.at(i); - QmlIR::JSCodeGen::IdMapping m; - const QmlIR::Object *obj = qmlObjects.at(objectIndex); - m.name = stringAt(obj->idNameIndex); - m.idIndex = obj->id; - m.type = propertyCaches->at(objectIndex); - - auto *tref = resolvedType(obj->inheritedTypeNameIndex); - if (tref && tref->isFullyDynamicType) - m.type = nullptr; - - idMapping << m; - } - v4CodeGen->beginContextScope(idMapping, propertyCaches->at(contextObject)); - if (!compileJavaScriptCodeInObjectsRecursively(contextObject, contextObject)) return false; @@ -1354,16 +1332,9 @@ bool QQmlJSCodeGenerator::compileJavaScriptCodeInObjectsRecursively(int objectIn return true; if (object->functionsAndExpressions->count > 0) { - QQmlPropertyCache *scopeObject = propertyCaches->at(scopeObjectIndex); - v4CodeGen->beginObjectScope(scopeObject); - QList functionsToCompile; - for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) { - const bool haveCustomParser = customParsers.contains(object->inheritedTypeNameIndex); - if (haveCustomParser) - foe->disableAcceleratedLookups = true; + for (QmlIR::CompiledFunctionOrExpression *foe = object->functionsAndExpressions->first; foe; foe = foe->next) functionsToCompile << *foe; - } const QVector runtimeFunctionIndices = v4CodeGen->generateJSCodeForFunctionsAndBindings(functionsToCompile); const QList jsErrors = v4CodeGen->qmlErrors(); if (!jsErrors.isEmpty()) { diff --git a/src/qml/compiler/qv4bytecodehandler.cpp b/src/qml/compiler/qv4bytecodehandler.cpp index a34472010b..e1fc0c6ee3 100644 --- a/src/qml/compiler/qv4bytecodehandler.cpp +++ b/src/qml/compiler/qv4bytecodehandler.cpp @@ -225,21 +225,6 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(StoreSuperProperty) COLLECTOR_END_INSTR(StoreSuperProperty) - COLLECTOR_BEGIN_INSTR(StoreScopeObjectProperty) - COLLECTOR_END_INSTR(StoreScopeObjectProperty) - - COLLECTOR_BEGIN_INSTR(LoadScopeObjectProperty) - COLLECTOR_END_INSTR(LoadScopeObjectProperty) - - COLLECTOR_BEGIN_INSTR(StoreContextObjectProperty) - COLLECTOR_END_INSTR(StoreContextObjectProperty) - - COLLECTOR_BEGIN_INSTR(LoadContextObjectProperty) - COLLECTOR_END_INSTR(LoadContextObjectProperty) - - COLLECTOR_BEGIN_INSTR(LoadIdObject) - COLLECTOR_END_INSTR(LoadIdObject) - COLLECTOR_BEGIN_INSTR(Yield) COLLECTOR_END_INSTR(Yield) @@ -276,12 +261,6 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(CallQmlContextPropertyLookup) COLLECTOR_END_INSTR(CallQmlContextPropertyLookup) - COLLECTOR_BEGIN_INSTR(CallScopeObjectProperty) - COLLECTOR_END_INSTR(CallScopeObjectProperty) - - COLLECTOR_BEGIN_INSTR(CallContextObjectProperty) - COLLECTOR_END_INSTR(CallContextObjectProperty) - COLLECTOR_BEGIN_INSTR(CallWithSpread) COLLECTOR_END_INSTR(CallWithSpread) @@ -546,12 +525,6 @@ std::vector ByteCodeHandler::collectLabelsInBytecode(const char *code, uint COLLECTOR_BEGIN_INSTR(GetTemplateObject) COLLECTOR_END_INSTR(GetTemplateObject) - COLLECTOR_BEGIN_INSTR(LoadQmlContext) - COLLECTOR_END_INSTR(LoadQmlContext) - - COLLECTOR_BEGIN_INSTR(LoadQmlImportedScripts) - COLLECTOR_END_INSTR(LoadQmlImportedScripts) - COLLECTOR_BEGIN_INSTR(TailCall) COLLECTOR_END_INSTR(TailCall) } diff --git a/src/qml/compiler/qv4codegen.cpp b/src/qml/compiler/qv4codegen.cpp index de87d6d48c..b0bec5b6f2 100644 --- a/src/qml/compiler/qv4codegen.cpp +++ b/src/qml/compiler/qv4codegen.cpp @@ -1875,8 +1875,6 @@ bool Codegen::visit(CallExpression *ast) switch (base.type) { case Reference::Member: case Reference::Subscript: - case Reference::QmlScopeObject: - case Reference::QmlContextObject: base = base.asLValue(); break; case Reference::Name: @@ -1938,21 +1936,7 @@ bool Codegen::visit(CallExpression *ast) void Codegen::handleCall(Reference &base, Arguments calldata, int slotForFunction, int slotForThisObject) { //### Do we really need all these call instructions? can's we load the callee in a temp? - if (base.type == Reference::QmlScopeObject) { - Instruction::CallScopeObjectProperty call; - call.base = base.qmlBase.stackSlot(); - call.name = base.qmlCoreIndex; - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); - } else if (base.type == Reference::QmlContextObject) { - Instruction::CallContextObjectProperty call; - call.base = base.qmlBase.stackSlot(); - call.name = base.qmlCoreIndex; - call.argc = calldata.argc; - call.argv = calldata.argv; - bytecodeGenerator->addInstruction(call); - } else if (base.type == Reference::Member) { + if (base.type == Reference::Member) { if (!disable_lookups && useFastLookups) { Instruction::CallPropertyLookup call; call.base = base.propertyBase.stackSlot(); @@ -2363,11 +2347,6 @@ Codegen::Reference Codegen::referenceForName(const QString &name, bool isLhs, co return r; } - // This hook allows implementing QML lookup semantics - Reference fallback = fallbackNameLookup(name); - if (fallback.type != Reference::Invalid) - return fallback; - Reference r = Reference::fromName(this, name); r.global = useFastLookups && (resolved.type == Context::ResolvedName::Global || resolved.type == Context::ResolvedName::QmlGlobal); r.qmlGlobal = resolved.type == Context::ResolvedName::QmlGlobal; @@ -2387,12 +2366,6 @@ void Codegen::loadClosure(int closureId) } } -Codegen::Reference Codegen::fallbackNameLookup(const QString &name) -{ - Q_UNUSED(name) - return Reference(); -} - bool Codegen::visit(IdentifierExpression *ast) { if (hasError) @@ -3101,8 +3074,6 @@ int Codegen::defineFunction(const QString &name, AST::Node *ast, bytecodeGenerator->addInstruction(yield); } - beginFunctionBodyHook(); - statementList(body); if (!hasError) { @@ -4039,10 +4010,6 @@ bool Codegen::Reference::operator==(const Codegen::Reference &other) const return index == other.index; case Const: return constant == other.constant; - case QmlScopeObject: - case QmlContextObject: - return qmlCoreIndex == other.qmlCoreIndex && qmlNotifyIndex == other.qmlNotifyIndex - && capturePolicy == other.capturePolicy; } return true; } @@ -4100,9 +4067,7 @@ Codegen::Reference Codegen::Reference::storeConsumeAccumulator() const Codegen::Reference Codegen::Reference::baseObject() const { - if (type == Reference::QmlScopeObject || type == Reference::QmlContextObject) { - return Reference::fromStackSlot(codegen, qmlBase.stackSlot()); - } else if (type == Reference::Member) { + if (type == Reference::Member) { RValue rval = propertyBase; if (!rval.isValid()) return Reference::fromConst(codegen, Encode::undefined()); @@ -4187,8 +4152,6 @@ bool Codegen::Reference::storeWipesAccumulator() const case Name: case Member: case Subscript: - case QmlScopeObject: - case QmlContextObject: return true; } } @@ -4268,18 +4231,6 @@ void Codegen::Reference::storeAccumulator() const store.index = elementSubscript.stackSlot(); codegen->bytecodeGenerator->addInstruction(store); } return; - case QmlScopeObject: { - Instruction::StoreScopeObjectProperty store; - store.base = qmlBase; - store.propertyIndex = qmlCoreIndex; - codegen->bytecodeGenerator->addInstruction(store); - } return; - case QmlContextObject: { - Instruction::StoreContextObjectProperty store; - store.base = qmlBase; - store.propertyIndex = qmlCoreIndex; - codegen->bytecodeGenerator->addInstruction(store); - } return; case Invalid: case Accumulator: case Const: @@ -4436,24 +4387,6 @@ QT_WARNING_POP load.base = elementBase; codegen->bytecodeGenerator->addInstruction(load); } return; - case QmlScopeObject: { - Instruction::LoadScopeObjectProperty load; - load.base = qmlBase; - load.propertyIndex = qmlCoreIndex; - load.captureRequired = capturePolicy == CaptureAtRuntime; - codegen->bytecodeGenerator->addInstruction(load); - if (capturePolicy == CaptureAheadOfTime) - codegen->_context->scopeObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); - } return; - case QmlContextObject: { - Instruction::LoadContextObjectProperty load; - load.base = qmlBase; - load.propertyIndex = qmlCoreIndex; - load.captureRequired = capturePolicy == CaptureAtRuntime; - codegen->bytecodeGenerator->addInstruction(load); - if (capturePolicy == CaptureAheadOfTime) - codegen->_context->contextObjectPropertyDependencies.insert(qmlCoreIndex, qmlNotifyIndex); - } return; case Invalid: break; } diff --git a/src/qml/compiler/qv4codegen_p.h b/src/qml/compiler/qv4codegen_p.h index b1cc4c090a..ad86483132 100644 --- a/src/qml/compiler/qv4codegen_p.h +++ b/src/qml/compiler/qv4codegen_p.h @@ -184,9 +184,7 @@ public: Member, Subscript, Import, - QmlScopeObject, - QmlContextObject, - LastLValue = QmlContextObject, + LastLValue = Import, Const } type = Invalid; @@ -223,10 +221,6 @@ public: bool isValid() const { return type != Invalid; } bool loadTriggersSideEffect() const { switch (type) { - case QmlScopeObject: - return capturePolicy != DontCapture; - case QmlContextObject: - return capturePolicy != DontCapture; case Name: case Member: case Subscript: @@ -245,28 +239,6 @@ public: return isStackSlot(); } - enum PropertyCapturePolicy { - /* - We're reading a property from the scope or context object, but it's a CONSTANT property, - so we don't need to register a dependency at all. - */ - DontCapture, - /* - We're reading the property of a QObject, and we know that it's the - scope object or context object, which we know very well. Instead of registering a - property capture every time, we can do that ahead of time and then register all those - captures in one shot in registerQmlDependencies(). - */ - CaptureAheadOfTime, - /* - We're reading the property of a QObject, and we're not quite sure where - the QObject comes from or what it is. So, when reading that property at run-time, - make sure that we capture where we read that property so that if it changes we can - re-evaluate the entire expression. - */ - CaptureAtRuntime - }; - static Reference fromAccumulator(Codegen *cg) { return Reference(cg, Accumulator); } @@ -333,22 +305,6 @@ public: r.isReadonly = true; return r; } - static Reference fromQmlScopeObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) { - Reference r(base.codegen, QmlScopeObject); - r.qmlBase = base.storeOnStack().stackSlot(); - r.qmlCoreIndex = coreIndex; - r.qmlNotifyIndex = notifyIndex; - r.capturePolicy = capturePolicy; - return r; - } - static Reference fromQmlContextObject(const Reference &base, qint16 coreIndex, qint16 notifyIndex, PropertyCapturePolicy capturePolicy) { - Reference r(base.codegen, QmlContextObject); - r.qmlBase = base.storeOnStack().stackSlot(); - r.qmlCoreIndex = coreIndex; - r.qmlNotifyIndex = notifyIndex; - r.capturePolicy = capturePolicy; - return r; - } static Reference fromThis(Codegen *cg) { Reference r = fromStackSlot(cg, CallData::This); r.isReadonly = true; @@ -403,12 +359,6 @@ public: Moth::StackSlot elementBase; RValue elementSubscript; }; - struct { // QML scope/context object case - Moth::StackSlot qmlBase; - qint16 qmlCoreIndex; - qint16 qmlNotifyIndex; - PropertyCapturePolicy capturePolicy; - }; Moth::StackSlot property; // super property }; QString name; @@ -598,12 +548,6 @@ protected: Reference referenceForPropertyName(const Codegen::Reference &object, AST::PropertyName *name); - // Hooks provided to implement QML lookup semantics - virtual bool canAccelerateGlobalLookups() const { return true; } - virtual Reference fallbackNameLookup(const QString &name); - - virtual void beginFunctionBodyHook() {} - void emitReturn(const Reference &expr); // nodes diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index d63b1fd2b9..1341c91e97 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -73,7 +73,7 @@ QT_BEGIN_NAMESPACE // Bump this whenever the compiler data structures change in an incompatible way. -#define QV4_DATA_STRUCTURE_VERSION 0x19 +#define QV4_DATA_STRUCTURE_VERSION 0x20 class QIODevice; class QQmlPropertyCache; @@ -288,21 +288,10 @@ struct Function quint16_le nRegisters; Location location; - // Qml Extensions Begin - // Array of resolved ID objects - size_t dependingIdObjectsOffset() const { return lineNumberOffset() + nLineNumbers * sizeof(CodeOffsetToLine); } - quint16_le nDependingIdObjects; - quint16_le nDependingContextProperties; - // Array of int pairs (property index and notify index) - size_t dependingContextPropertiesOffset() const { return dependingIdObjectsOffset() + nDependingIdObjects * sizeof(quint32); } - quint16_le nDependingScopeProperties; - // Array of int pairs (property index and notify index) - size_t dependingScopePropertiesOffset() const { return dependingContextPropertiesOffset() + nDependingContextProperties * sizeof(quint32); } - // Qml Extensions End - // Keep all unaligned data at the end quint8 flags; quint8 padding1; + quint16 padding2; // quint32 formalsIndex[nFormals] // quint32 localsIndex[nLocals] @@ -310,9 +299,6 @@ struct Function const quint32_le *formalsTable() const { return reinterpret_cast(reinterpret_cast(this) + formalsOffset); } const quint32_le *localsTable() const { return reinterpret_cast(reinterpret_cast(this) + localsOffset); } const CodeOffsetToLine *lineNumberTable() const { return reinterpret_cast(reinterpret_cast(this) + lineNumberOffset()); } - const quint32_le *qmlIdObjectDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingIdObjectsOffset()); } - const quint32_le *qmlContextPropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingContextPropertiesOffset()); } - const quint32_le *qmlScopePropertiesDependencyTable() const { return reinterpret_cast(reinterpret_cast(this) + dependingScopePropertiesOffset()); } // --- QQmlPropertyCacheCreator interface const quint32_le *formalsBegin() const { return formalsTable(); } @@ -321,11 +307,8 @@ struct Function const char *code() const { return reinterpret_cast(this) + codeOffset; } - inline bool hasQmlDependencies() const { return nDependingIdObjects > 0 || nDependingContextProperties > 0 || nDependingScopeProperties > 0; } - - static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int nIdObjectDependencies, int nPropertyDependencies, int codeSize) { - int trailingData = (nFormals + nLocals + nInnerfunctions + nIdObjectDependencies + - 2 * nPropertyDependencies)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); + static int calculateSize(int nFormals, int nLocals, int nLines, int nInnerfunctions, int codeSize) { + int trailingData = (nFormals + nLocals + nInnerfunctions)*sizeof (quint32) + nLines*sizeof(CodeOffsetToLine); size_t size = align(align(sizeof(Function)) + size_t(trailingData)) + align(codeSize); Q_ASSERT(size < INT_MAX); return int(size); @@ -335,7 +318,7 @@ struct Function return (a + 7) & ~size_t(7); } }; -static_assert(sizeof(Function) == 52, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); +static_assert(sizeof(Function) == 48, "Function structure needs to have the expected size to be binary compatible on disk when generated by host compiler and loaded by target"); struct Method { enum Type { diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index a3c9347c67..4d85f25d4b 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -427,28 +427,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->nRegisters = irFunction->registerCountInFunction; - function->nDependingIdObjects = 0; - function->nDependingContextProperties = 0; - function->nDependingScopeProperties = 0; - - if (!irFunction->idObjectDependencies.isEmpty()) { - function->nDependingIdObjects = irFunction->idObjectDependencies.count(); - Q_ASSERT(function->dependingIdObjectsOffset() == currentOffset); - currentOffset += function->nDependingIdObjects * sizeof(quint32); - } - - if (!irFunction->contextObjectPropertyDependencies.isEmpty()) { - function->nDependingContextProperties = irFunction->contextObjectPropertyDependencies.count(); - Q_ASSERT(function->dependingContextPropertiesOffset() == currentOffset); - currentOffset += function->nDependingContextProperties * sizeof(quint32) * 2; - } - - if (!irFunction->scopeObjectPropertyDependencies.isEmpty()) { - function->nDependingScopeProperties = irFunction->scopeObjectPropertyDependencies.count(); - Q_ASSERT(function->dependingScopePropertiesOffset() == currentOffset); - currentOffset += function->nDependingScopeProperties * sizeof(quint32) * 2; - } - function->location.line = irFunction->line; function->location.column = irFunction->column; @@ -468,25 +446,6 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte // write line numbers memcpy(f + function->lineNumberOffset(), irFunction->lineNumberMapping.constData(), irFunction->lineNumberMapping.size()*sizeof(CompiledData::CodeOffsetToLine)); - // write QML dependencies - quint32_le *writtenDeps = (quint32_le *)(f + function->dependingIdObjectsOffset()); - for (int id : irFunction->idObjectDependencies) { - Q_ASSERT(id >= 0); - *writtenDeps++ = static_cast(id); - } - - writtenDeps = (quint32_le *)(f + function->dependingContextPropertiesOffset()); - for (auto property : irFunction->contextObjectPropertyDependencies) { - *writtenDeps++ = property.key(); // property index - *writtenDeps++ = property.value(); // notify index - } - - writtenDeps = (quint32_le *)(f + function->dependingScopePropertiesOffset()); - for (auto property : irFunction->scopeObjectPropertyDependencies) { - *writtenDeps++ = property.key(); // property index - *writtenDeps++ = property.value(); // notify index - } - // write byte code memcpy(f + function->codeOffset, irFunction->code.constData(), irFunction->code.size()); } @@ -683,10 +642,8 @@ QV4::CompiledData::Unit QV4::Compiler::JSUnitGenerator::generateHeader(QV4::Comp Context *f = module->functions.at(i); blockAndFunctionOffsets[i] = nextOffset; - const int qmlIdDepsCount = f->idObjectDependencies.count(); - const int qmlPropertyDepsCount = f->scopeObjectPropertyDependencies.count() + f->contextObjectPropertyDependencies.count(); quint32 size = QV4::CompiledData::Function::calculateSize(f->arguments.size(), f->locals.size(), f->lineNumberMapping.size(), f->nestedContexts.size(), - qmlIdDepsCount, qmlPropertyDepsCount, f->code.size()); + f->code.size()); functionSize += size - f->code.size(); nextOffset += size; } diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index c9e54c0d1b..8f3b60e395 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -276,11 +276,6 @@ struct Context { } }; - // Qml extension: - SmallSet idObjectDependencies; - PropertyDependencyMap contextObjectPropertyDependencies; - PropertyDependencyMap scopeObjectPropertyDependencies; - Context(Context *parent, ContextType type) : parent(parent) , contextType(type) diff --git a/src/qml/compiler/qv4instr_moth.cpp b/src/qml/compiler/qv4instr_moth.cpp index d2e95b83c4..c6c8caffba 100644 --- a/src/qml/compiler/qv4instr_moth.cpp +++ b/src/qml/compiler/qv4instr_moth.cpp @@ -330,26 +330,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << dumpRegister(property, nFormals); MOTH_END_INSTR(StoreSuperProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; - MOTH_END_INSTR(StoreScopeObjectProperty) - - MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); - MOTH_END_INSTR(LoadScopeObjectProperty) - - MOTH_BEGIN_INSTR(StoreContextObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]"; - MOTH_END_INSTR(StoreContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadContextObjectProperty) - d << dumpRegister(base, nFormals) << "[" << propertyIndex << "]" << (captureRequired ? " (capture)" : " (no capture)"); - MOTH_END_INSTR(LoadContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadIdObject) - d << dumpRegister(base, nFormals) << "[" << index << "]"; - MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(Yield) MOTH_END_INSTR(Yield) @@ -396,14 +376,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallQmlContextPropertyLookup) - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - d << dumpRegister(base, nFormals) << "." << name << dumpArguments(argc, argv, nFormals); - MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(CallWithSpread) d << "new" << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(CallWithSpread) @@ -724,14 +696,6 @@ void dumpBytecode(const char *code, int len, int nLocals, int nFormals, int /*st d << index; MOTH_END_INSTR(GetTemplateObject) - MOTH_BEGIN_INSTR(LoadQmlContext) - d << dumpRegister(result, nFormals); - MOTH_END_INSTR(LoadQmlContext) - - MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - d << dumpRegister(result, nFormals); - MOTH_END_INSTR(LoadQmlImportedScripts) - MOTH_BEGIN_INSTR(TailCall) d << dumpRegister(func, nFormals) << dumpRegister(thisObject, nFormals) << dumpArguments(argc, argv, nFormals); MOTH_END_INSTR(TailCall) diff --git a/src/qml/compiler/qv4instr_moth_p.h b/src/qml/compiler/qv4instr_moth_p.h index 1c77e6050c..25fca3c0b0 100644 --- a/src/qml/compiler/qv4instr_moth_p.h +++ b/src/qml/compiler/qv4instr_moth_p.h @@ -91,8 +91,6 @@ QT_BEGIN_NAMESPACE #define INSTR_StoreNameStrict(op) INSTRUCTION(op, StoreNameStrict, 1, name) #define INSTR_LoadProperty(op) INSTRUCTION(op, LoadProperty, 1, name) #define INSTR_GetLookup(op) INSTRUCTION(op, GetLookup, 1, index) -#define INSTR_LoadScopeObjectProperty(op) INSTRUCTION(op, LoadScopeObjectProperty, 3, propertyIndex, base, captureRequired) -#define INSTR_LoadContextObjectProperty(op) INSTRUCTION(op, LoadContextObjectProperty, 3, propertyIndex, base, captureRequired) #define INSTR_LoadIdObject(op) INSTRUCTION(op, LoadIdObject, 2, index, base) #define INSTR_Yield(op) INSTRUCTION(op, Yield, 0) #define INSTR_YieldStar(op) INSTRUCTION(op, YieldStar, 0) @@ -102,8 +100,6 @@ QT_BEGIN_NAMESPACE #define INSTR_SetLookup(op) INSTRUCTION(op, SetLookup, 2, index, base) #define INSTR_LoadSuperProperty(op) INSTRUCTION(op, LoadSuperProperty, 1, property) #define INSTR_StoreSuperProperty(op) INSTRUCTION(op, StoreSuperProperty, 1, property) -#define INSTR_StoreScopeObjectProperty(op) INSTRUCTION(op, StoreScopeObjectProperty, 2, base, propertyIndex) -#define INSTR_StoreContextObjectProperty(op) INSTRUCTION(op, StoreContextObjectProperty, 2, base, propertyIndex) #define INSTR_LoadElement(op) INSTRUCTION(op, LoadElement, 1, base) #define INSTR_StoreElement(op) INSTRUCTION(op, StoreElement, 2, base, index) #define INSTR_CallValue(op) INSTRUCTION(op, CallValue, 3, name, argc, argv) @@ -115,8 +111,6 @@ QT_BEGIN_NAMESPACE #define INSTR_CallPossiblyDirectEval(op) INSTRUCTION(op, CallPossiblyDirectEval, 2, argc, argv) #define INSTR_CallGlobalLookup(op) INSTRUCTION(op, CallGlobalLookup, 3, index, argc, argv) #define INSTR_CallQmlContextPropertyLookup(op) INSTRUCTION(op, CallQmlContextPropertyLookup, 3, index, argc, argv) -#define INSTR_CallScopeObjectProperty(op) INSTRUCTION(op, CallScopeObjectProperty, 4, name, base, argc, argv) -#define INSTR_CallContextObjectProperty(op) INSTRUCTION(op, CallContextObjectProperty, 4, name, base, argc, argv) #define INSTR_CallWithSpread(op) INSTRUCTION(op, CallWithSpread, 4, func, thisObject, argc, argv) #define INSTR_Construct(op) INSTRUCTION(op, Construct, 3, func, argc, argv) #define INSTR_ConstructWithSpread(op) INSTRUCTION(op, ConstructWithSpread, 3, func, argc, argv) @@ -196,7 +190,6 @@ QT_BEGIN_NAMESPACE #define INSTR_Div(op) INSTRUCTION(op, Div, 1, lhs) #define INSTR_Mod(op) INSTRUCTION(op, Mod, 1, lhs) #define INSTR_Sub(op) INSTRUCTION(op, Sub, 1, lhs) -#define INSTR_LoadQmlContext(op) INSTRUCTION(op, LoadQmlContext, 1, result) #define INSTR_LoadQmlImportedScripts(op) INSTRUCTION(op, LoadQmlImportedScripts, 1, result) #define INSTR_InitializeBlockDeadTemporalZone(op) INSTRUCTION(op, InitializeBlockDeadTemporalZone, 2, firstReg, count) #define INSTR_ThrowOnNullOrUndefined(op) INSTRUCTION(op, ThrowOnNullOrUndefined, 0) @@ -241,11 +234,6 @@ QT_BEGIN_NAMESPACE F(SetLookup) \ F(LoadSuperProperty) \ F(StoreSuperProperty) \ - F(StoreScopeObjectProperty) \ - F(StoreContextObjectProperty) \ - F(LoadScopeObjectProperty) \ - F(LoadContextObjectProperty) \ - F(LoadIdObject) \ F(ConvertThisToObject) \ F(ToObject) \ F(Jump) \ @@ -300,8 +288,6 @@ QT_BEGIN_NAMESPACE F(CallPossiblyDirectEval) \ F(CallGlobalLookup) \ F(CallQmlContextPropertyLookup) \ - F(CallScopeObjectProperty) \ - F(CallContextObjectProperty) \ F(CallWithSpread) \ F(Construct) \ F(ConstructWithSpread) \ @@ -332,8 +318,6 @@ QT_BEGIN_NAMESPACE F(CreateMappedArgumentsObject) \ F(CreateUnmappedArgumentsObject) \ F(CreateRestParameter) \ - F(LoadQmlContext) \ - F(LoadQmlImportedScripts) \ F(Yield) \ F(YieldStar) \ F(Resume) \ diff --git a/src/qml/jit/qv4baselinejit.cpp b/src/qml/jit/qv4baselinejit.cpp index 47cef3b3bd..1e4288e3c9 100644 --- a/src/qml/jit/qv4baselinejit.cpp +++ b/src/qml/jit/qv4baselinejit.cpp @@ -347,66 +347,6 @@ void BaselineJIT::generate_StoreSuperProperty(int property) as->checkException(); } - -void BaselineJIT::generate_StoreScopeObjectProperty(int base, int propertyIndex) -{ - STORE_ACC(); - as->prepareCallWithArgCount(4); - as->passAccumulatorAsArg(3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlScopeObjectProperty, CallResultDestination::Ignore); - as->checkException(); -} - -void BaselineJIT::generate_StoreContextObjectProperty(int base, int propertyIndex) -{ - STORE_ACC(); - as->prepareCallWithArgCount(4); - as->passAccumulatorAsArg(3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_storeQmlContextObjectProperty, CallResultDestination::Ignore); - as->checkException(); -} - -void BaselineJIT::generate_LoadScopeObjectProperty(int propertyIndex, int base, int captureRequired) -{ - STORE_IP(); - as->prepareCallWithArgCount(4); - as->passInt32AsArg(captureRequired, 3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlScopeObjectProperty, CallResultDestination::InAccumulator); - as->checkException(); -} - -void BaselineJIT::generate_LoadContextObjectProperty(int propertyIndex, int base, int captureRequired) -{ - STORE_IP(); - as->prepareCallWithArgCount(4); - as->passInt32AsArg(captureRequired, 3); - as->passInt32AsArg(propertyIndex, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContextObjectProperty, CallResultDestination::InAccumulator); - as->checkException(); -} - -void BaselineJIT::generate_LoadIdObject(int index, int base) -{ - STORE_IP(); - as->prepareCallWithArgCount(3); - as->passInt32AsArg(index, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlIdObject, CallResultDestination::InAccumulator); - as->checkException(); -} - void BaselineJIT::generate_Yield() { // ##### @@ -536,33 +476,6 @@ void BaselineJIT::generate_CallQmlContextPropertyLookup(int index, int argc, int as->checkException(); } -void BaselineJIT::generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) -{ - STORE_IP(); - as->prepareCallWithArgCount(5); - as->passInt32AsArg(argc, 4); - as->passJSSlotAsArg(argv, 3); - as->passInt32AsArg(propIdx, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlScopeObjectProperty, CallResultDestination::InAccumulator); - as->checkException(); -} - -void BaselineJIT::generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) -{ - STORE_IP(); - as->prepareCallWithArgCount(5); - as->passInt32AsArg(argc, 4); - as->passJSSlotAsArg(argv, 3); - as->passInt32AsArg(propIdx, 2); - as->passJSSlotAsArg(base, 1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_callQmlContextObjectProperty, CallResultDestination::InAccumulator); - as->checkException(); -} - - void BaselineJIT::generate_CallWithSpread(int func, int thisObject, int argc, int argv) { STORE_IP(); @@ -981,22 +894,6 @@ void BaselineJIT::generate_Sub(int lhs) { as->sub(lhs); } // as->checkException(); //} -void BaselineJIT::generate_LoadQmlContext(int result) -{ - as->prepareCallWithArgCount(1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlContext, CallResultDestination::InAccumulator); - as->storeReg(result); -} - -void BaselineJIT::generate_LoadQmlImportedScripts(int result) -{ - as->prepareCallWithArgCount(1); - as->passEngineAsArg(0); - BASELINEJIT_GENERATE_RUNTIME_CALL(Runtime::method_loadQmlImportedScripts, CallResultDestination::InAccumulator); - as->storeReg(result); -} - void BaselineJIT::generate_InitializeBlockDeadTemporalZone(int firstReg, int count) { as->loadValue(Value::emptyValue().rawValue()); diff --git a/src/qml/jit/qv4baselinejit_p.h b/src/qml/jit/qv4baselinejit_p.h index 4db1eb1806..6646eb713e 100644 --- a/src/qml/jit/qv4baselinejit_p.h +++ b/src/qml/jit/qv4baselinejit_p.h @@ -108,15 +108,6 @@ public: void generate_SetLookup(int index, int base) override; void generate_LoadSuperProperty(int property) override; void generate_StoreSuperProperty(int property) override; - void generate_StoreScopeObjectProperty(int base, - int propertyIndex) override; - void generate_StoreContextObjectProperty(int base, - int propertyIndex) override; - void generate_LoadScopeObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadContextObjectProperty(int propertyIndex, int base, - int captureRequired) override; - void generate_LoadIdObject(int index, int base) override; void generate_Yield() override; void generate_YieldStar() override; void generate_Resume(int) override; @@ -130,8 +121,6 @@ public: void generate_CallPossiblyDirectEval(int argc, int argv) override; void generate_CallGlobalLookup(int index, int argc, int argv) override; void generate_CallQmlContextPropertyLookup(int index, int argc, int argv) override; - void generate_CallScopeObjectProperty(int propIdx, int base, int argc, int argv) override; - void generate_CallContextObjectProperty(int propIdx, int base, int argc, int argv) override; void generate_CallWithSpread(int func, int thisObject, int argc, int argv) override; void generate_TailCall(int func, int thisObject, int argc, int argv) override; void generate_Construct(int func, int argc, int argv) override; @@ -213,8 +202,6 @@ public: void generate_Div(int lhs) override; void generate_Mod(int lhs) override; void generate_Sub(int lhs) override; - void generate_LoadQmlContext(int result) override; - void generate_LoadQmlImportedScripts(int result) override; void generate_InitializeBlockDeadTemporalZone(int firstReg, int count) override; void generate_ThrowOnNullOrUndefined() override; void generate_GetTemplateObject(int index) override; diff --git a/src/qml/jsruntime/qv4function.cpp b/src/qml/jsruntime/qv4function.cpp index 941c37de5b..51a9b92967 100644 --- a/src/qml/jsruntime/qv4function.cpp +++ b/src/qml/jsruntime/qv4function.cpp @@ -78,7 +78,6 @@ Function::Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, , codeData(function->code()) , jittedCode(nullptr) , codeRef(nullptr) - , hasQmlDependencies(function->hasQmlDependencies()) { Scope scope(engine); Scoped ic(scope, engine->internalClasses(EngineBase::Class_CallContext)); diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index 029dd7786b..62c5d24fc4 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -80,7 +80,6 @@ struct Q_QML_EXPORT Function { Heap::InternalClass *internalClass; uint nFormals; int interpreterCallCount = 0; - bool hasQmlDependencies; bool isEval = false; Function(ExecutionEngine *engine, CompiledData::CompilationUnit *unit, const CompiledData::Function *function); diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 711c910906..053581226f 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -233,7 +233,7 @@ QQmlPropertyData *QObjectWrapper::findProperty(ExecutionEngine *engine, QObject return result; } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired) +ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property) { QQmlData::flushPendingBinding(object, QQmlPropertyIndex(property->coreIndex())); @@ -259,7 +259,7 @@ ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *obje QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; - if (captureRequired && ep && ep->propertyCapture && !property->isConstant()) + if (ep && ep->propertyCapture && !property->isConstant()) ep->propertyCapture->captureProperty(object, property->coreIndex(), property->notifyIndex()); if (property->isVarProperty()) { @@ -357,26 +357,6 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return getProperty(v4, d()->object(), result); } -ReturnedValue QObjectWrapper::getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired) -{ - if (QQmlData::wasDeleted(object)) - return QV4::Encode::null(); - QQmlData *ddata = QQmlData::get(object, /*create*/false); - if (!ddata) - return QV4::Encode::undefined(); - - if (Q_UNLIKELY(!ddata->propertyCache)) { - ddata->propertyCache = QQmlEnginePrivate::get(engine)->cache(object->metaObject()); - ddata->propertyCache->addref(); - } - - QQmlPropertyCache *cache = ddata->propertyCache; - Q_ASSERT(cache); - QQmlPropertyData *property = cache->property(propertyIndex); - Q_ASSERT(property); // We resolved this property earlier, so it better exist! - return getProperty(engine, object, property, captureRequired); -} - ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) { if (QQmlData::wasDeleted(object)) { @@ -873,7 +853,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E if (!ddata || !ddata->propertyCache) { QQmlPropertyData local; QQmlPropertyData *property = QQmlPropertyCache::property(engine->jsEngine(), qobj, name, qmlContext, local); - return getProperty(engine, qobj, property, /*captureRequired*/true); + return getProperty(engine, qobj, property); } QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobj, qmlContext); @@ -919,7 +899,7 @@ ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engi return revertLookup(); QQmlPropertyData *property = lookup->qobjectLookup.propertyData; - return getProperty(engine, qobj, property, /*captureRequired = */true); + return getProperty(engine, qobj, property); } bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index a09e7b6e95..471f352c2a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -176,13 +176,12 @@ struct Q_QML_EXPORT QObjectWrapper : public Object using Object::get; - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, bool captureRequired); static void setProperty(ExecutionEngine *engine, QObject *object, int propertyIndex, const Value &value); void setProperty(ExecutionEngine *engine, int propertyIndex, const Value &value); void destroyObject(bool lastCall); - static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property, bool captureRequired = true); + static ReturnedValue getProperty(ExecutionEngine *engine, QObject *object, QQmlPropertyData *property); static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); diff --git a/src/qml/jsruntime/qv4runtime.cpp b/src/qml/jsruntime/qv4runtime.cpp index 7163a51af1..85d06bcabe 100644 --- a/src/qml/jsruntime/qv4runtime.cpp +++ b/src/qml/jsruntime/qv4runtime.cpp @@ -1457,38 +1457,6 @@ ReturnedValue Runtime::method_callWithReceiver(ExecutionEngine *engine, const Va return static_cast(func).call(thisObject, argv, argc); } -ReturnedValue Runtime::method_callQmlScopeObjectProperty(ExecutionEngine *engine, Value *base, - int propertyIndex, Value *argv, int argc) -{ - Scope scope(engine); - ScopedFunctionObject fo(scope, method_loadQmlScopeObjectProperty(engine, *base, propertyIndex, - /*captureRequired*/true)); - if (!fo) { - QString error = QStringLiteral("Property '%1' of scope object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } - - QObject *qmlScopeObj = static_cast(base)->d()->qml()->scopeObject; - ScopedValue qmlScopeValue(scope, QObjectWrapper::wrap(engine, qmlScopeObj)); - return fo->call(qmlScopeValue, argv, argc); -} - -ReturnedValue Runtime::method_callQmlContextObjectProperty(ExecutionEngine *engine, Value *base, - int propertyIndex, Value *argv, int argc) -{ - Scope scope(engine); - ScopedFunctionObject fo(scope, method_loadQmlContextObjectProperty(engine, *base, propertyIndex, - /*captureRequired*/true)); - if (!fo) { - QString error = QStringLiteral("Property '%1' of context object is not a function").arg(propertyIndex); - return engine->throwTypeError(error); - } - - QObject *qmlContextObj = static_cast(base)->d()->qml()->context->contextData()->contextObject; - ScopedValue qmlContextValue(scope, QObjectWrapper::wrap(engine, qmlContextObj)); - return fo->call(qmlContextValue, argv, argc); -} - struct CallArgs { Value *argv; int argc; @@ -1904,65 +1872,11 @@ QV4::ReturnedValue Runtime::method_createRestParameter(ExecutionEngine *engine, return engine->newArrayObject(values, nValues)->asReturnedValue(); } - -ReturnedValue Runtime::method_loadQmlContext(NoThrowEngine *engine) -{ - Heap::QmlContext *ctx = engine->qmlContext(); - Q_ASSERT(ctx); - return ctx->asReturnedValue(); -} - ReturnedValue Runtime::method_regexpLiteral(ExecutionEngine *engine, int id) { Heap::RegExpObject *ro = engine->newRegExpObject(engine->currentStackFrame->v4Function->compilationUnit->runtimeRegularExpressions[id].as()); return ro->asReturnedValue(); } - -ReturnedValue Runtime::method_loadQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) -{ - const QmlContext &c = static_cast(context); - return QV4::QObjectWrapper::getProperty(engine, c.d()->qml()->scopeObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::method_loadQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired) -{ - const QmlContext &c = static_cast(context); - return QV4::QObjectWrapper::getProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, captureRequired); -} - -ReturnedValue Runtime::method_loadQmlIdObject(ExecutionEngine *engine, const Value &c, uint index) -{ - const QmlContext &qmlContext = static_cast(c); - QQmlContextData *context = *qmlContext.d()->qml()->context; - if (!context || index >= (uint)context->idValueCount) - return Encode::undefined(); - - QQmlEnginePrivate *ep = engine->qmlEngine() ? QQmlEnginePrivate::get(engine->qmlEngine()) : nullptr; - if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(&context->idValues[index].bindings); - - return QObjectWrapper::wrap(engine, context->idValues[index].data()); -} - -void Runtime::method_storeQmlScopeObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) -{ - const QmlContext &c = static_cast(context); - return QV4::QObjectWrapper::setProperty(engine, c.d()->qml()->scopeObject, propertyIndex, value); -} - -void Runtime::method_storeQmlContextObjectProperty(ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value) -{ - const QmlContext &c = static_cast(context); - return QV4::QObjectWrapper::setProperty(engine, (*c.d()->qml()->context)->contextObject, propertyIndex, value); -} - -ReturnedValue Runtime::method_loadQmlImportedScripts(NoThrowEngine *engine) -{ - QQmlContextData *context = engine->callingQmlContext(); - if (!context) - return Encode::undefined(); - return context->importedScripts.value(); -} #endif // V4_BOOTSTRAP ReturnedValue Runtime::method_uMinus(const Value &value) diff --git a/src/qml/jsruntime/qv4runtimeapi_p.h b/src/qml/jsruntime/qv4runtimeapi_p.h index aa4ac27621..4b3905c56f 100644 --- a/src/qml/jsruntime/qv4runtimeapi_p.h +++ b/src/qml/jsruntime/qv4runtimeapi_p.h @@ -195,19 +195,7 @@ struct ExceptionCheck { F(Bool, compareInstanceof, (ExecutionEngine *engine, const Value &left, const Value &right)) \ F(Bool, compareIn, (ExecutionEngine *engine, const Value &left, const Value &right)) \ \ - F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) \ - \ - /* qml */ \ - F(ReturnedValue, loadQmlContext, (NoThrowEngine *engine)) \ - F(ReturnedValue, loadQmlImportedScripts, (NoThrowEngine *engine)) \ - F(ReturnedValue, loadQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, loadQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, bool captureRequired)) \ - F(ReturnedValue, loadQmlIdObject, (ExecutionEngine *engine, const Value &context, uint index)) \ - F(ReturnedValue, callQmlScopeObjectProperty, (ExecutionEngine *engine, Value *base, int propertyIndex, Value *argv, int argc)) \ - F(ReturnedValue, callQmlContextObjectProperty, (ExecutionEngine *engine, Value *base, int propertyIndex, Value *argv, int argc)) \ - \ - F(void, storeQmlScopeObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ - F(void, storeQmlContextObjectProperty, (ExecutionEngine *engine, const Value &context, int propertyIndex, const Value &value)) \ + F(ReturnedValue, regexpLiteral, (ExecutionEngine *engine, int id)) struct Q_QML_PRIVATE_EXPORT Runtime { Runtime(); diff --git a/src/qml/jsruntime/qv4vme_moth.cpp b/src/qml/jsruntime/qv4vme_moth.cpp index 2d9337b03e..ea2217499f 100644 --- a/src/qml/jsruntime/qv4vme_moth.cpp +++ b/src/qml/jsruntime/qv4vme_moth.cpp @@ -646,37 +646,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(StoreSuperProperty) - MOTH_BEGIN_INSTR(StoreScopeObjectProperty) - STORE_ACC(); - Runtime::method_storeQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreScopeObjectProperty) - - MOTH_BEGIN_INSTR(LoadScopeObjectProperty) - STORE_IP(); - acc = Runtime::method_loadQmlScopeObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadScopeObjectProperty) - - MOTH_BEGIN_INSTR(StoreContextObjectProperty) - STORE_IP(); - STORE_ACC(); - Runtime::method_storeQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, accumulator); - CHECK_EXCEPTION; - MOTH_END_INSTR(StoreContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadContextObjectProperty) - STORE_IP(); - acc = Runtime::method_loadQmlContextObjectProperty(engine, STACK_VALUE(base), propertyIndex, captureRequired); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadContextObjectProperty) - - MOTH_BEGIN_INSTR(LoadIdObject) - STORE_IP(); - acc = Runtime::method_loadQmlIdObject(engine, STACK_VALUE(base), index); - CHECK_EXCEPTION; - MOTH_END_INSTR(LoadIdObject) - MOTH_BEGIN_INSTR(Yield) frame->yield = code; frame->yieldIsIterator = false; @@ -794,18 +763,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, CHECK_EXCEPTION; MOTH_END_INSTR(CallQmlContextPropertyLookup) - MOTH_BEGIN_INSTR(CallScopeObjectProperty) - STORE_IP(); - acc = Runtime::method_callQmlScopeObjectProperty(engine, stack + base, name, stack + argv, argc); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallScopeObjectProperty) - - MOTH_BEGIN_INSTR(CallContextObjectProperty) - STORE_IP(); - acc = Runtime::method_callQmlContextObjectProperty(engine, stack + base, name, stack + argv, argc); - CHECK_EXCEPTION; - MOTH_END_INSTR(CallContextObjectProperty) - MOTH_BEGIN_INSTR(CallWithSpread) STORE_IP(); acc = Runtime::method_callWithSpread(engine, STACK_VALUE(func), STACK_VALUE(thisObject), stack + argv, argc); @@ -1425,14 +1382,6 @@ QV4::ReturnedValue VME::interpret(CppStackFrame *frame, ExecutionEngine *engine, #endif // QT_CONFIG(qml_debug) MOTH_END_INSTR(Debug) - MOTH_BEGIN_INSTR(LoadQmlContext) - STACK_VALUE(result) = Runtime::method_loadQmlContext(static_cast(engine)); - MOTH_END_INSTR(LoadQmlContext) - - MOTH_BEGIN_INSTR(LoadQmlImportedScripts) - STACK_VALUE(result) = Runtime::method_loadQmlImportedScripts(static_cast(engine)); - MOTH_END_INSTR(LoadQmlImportedScripts) - handleUnwind: Q_ASSERT(engine->hasException || frame->unwindLevel); if (!frame->unwindHandler) { diff --git a/src/qml/qml/qqmlbinding.cpp b/src/qml/qml/qqmlbinding.cpp index eea3670191..7f0442d034 100644 --- a/src/qml/qml/qqmlbinding.cpp +++ b/src/qml/qml/qqmlbinding.cpp @@ -260,8 +260,6 @@ protected: } else { clearError(); } - - cancelPermanentGuards(); } ep->dereferenceScarceResources(); @@ -643,24 +641,22 @@ QVector QQmlBinding::dependencies() const if (!m_target.data()) return dependencies; - for (const auto &guardList : { permanentGuards, activeGuards }) { - for (QQmlJavaScriptExpressionGuard *guard = guardList.first(); guard; guard = guardList.next(guard)) { - if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*. - continue; + for (QQmlJavaScriptExpressionGuard *guard = activeGuards.first(); guard; guard = activeGuards.next(guard)) { + if (guard->signalIndex() == -1) // guard's sender is a QQmlNotifier, not a QObject*. + continue; - QObject *senderObject = guard->senderAsObject(); - if (!senderObject) - continue; + QObject *senderObject = guard->senderAsObject(); + if (!senderObject) + continue; - const QMetaObject *senderMeta = senderObject->metaObject(); - if (!senderMeta) - continue; + const QMetaObject *senderMeta = senderObject->metaObject(); + if (!senderMeta) + continue; - for (int i = 0; i < senderMeta->propertyCount(); i++) { - QMetaProperty property = senderMeta->property(i); - if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) { - dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name()))); - } + for (int i = 0; i < senderMeta->propertyCount(); i++) { + QMetaProperty property = senderMeta->property(i); + if (property.notifySignalIndex() == QMetaObjectPrivate::signal(senderMeta, guard->signalIndex()).methodIndex()) { + dependencies.push_back(QQmlProperty(senderObject, QString::fromUtf8(senderObject->metaObject()->property(i).name()))); } } } @@ -670,7 +666,7 @@ QVector QQmlBinding::dependencies() const bool QQmlBinding::hasDependencies() const { - return !permanentGuards.isEmpty() || !activeGuards.isEmpty() || translationsCaptured(); + return !activeGuards.isEmpty() || translationsCaptured(); } class QObjectPointerBinding: public QQmlNonbindingBinding diff --git a/src/qml/qml/qqmljavascriptexpression.cpp b/src/qml/qml/qqmljavascriptexpression.cpp index 380163202a..9a3a5218e0 100644 --- a/src/qml/qml/qqmljavascriptexpression.cpp +++ b/src/qml/qml/qqmljavascriptexpression.cpp @@ -109,7 +109,6 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() } clearActiveGuards(); - clearPermanentGuards(); clearError(); if (m_scopeObject.isT2()) // notify DeleteWatcher of our deletion. m_scopeObject.asT2()->_s = nullptr; @@ -118,12 +117,8 @@ QQmlJavaScriptExpression::~QQmlJavaScriptExpression() void QQmlJavaScriptExpression::setNotifyOnValueChanged(bool v) { activeGuards.setFlagValue(v); - permanentGuards.setFlagValue(v); - if (!v) { + if (!v) clearActiveGuards(); - clearPermanentGuards(); - m_permanentDependenciesRegistered = false; - } } void QQmlJavaScriptExpression::resetNotifyOnValueChanged() @@ -216,10 +211,6 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b QV4::ReturnedValue res = v4Function->call(&callData->thisObject, callData->args, callData->argc(), static_cast(m_qmlScope.valueRef())); QV4::Scope scope(v4); QV4::ScopedValue result(scope, res); - if (v4Function->hasQmlDependencies) { - QV4::Heap::QmlContext *qc = m_qmlScope.as()->d(); - QQmlPropertyCapture::registerQmlDependencies(qc, v4, v4Function->compiledFunction); - } if (scope.hasException()) { if (watcher.wasDeleted()) @@ -254,7 +245,7 @@ QV4::ReturnedValue QQmlJavaScriptExpression::evaluate(QV4::CallData *callData, b return result->asReturnedValue(); } -void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) +void QQmlPropertyCapture::captureProperty(QQmlNotifier *n) { if (watcher->wasDeleted()) return; @@ -274,17 +265,14 @@ void QQmlPropertyCapture::captureProperty(QQmlNotifier *n, Duration duration) g->connect(n); } - if (duration == Permanently) - expression->permanentGuards.prepend(g); - else - expression->activeGuards.prepend(g); + expression->activeGuards.prepend(g); } /*! \internal \a n is in the signal index range (see QObjectPrivate::signalIndex()). */ -void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration duration, bool doNotify) +void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, bool doNotify) { if (watcher->wasDeleted()) return; @@ -323,61 +311,8 @@ void QQmlPropertyCapture::captureProperty(QObject *o, int c, int n, Duration dur g->connect(o, n, engine, doNotify); } - if (duration == Permanently) - expression->permanentGuards.prepend(g); - else - expression->activeGuards.prepend(g); - } -} - -void QQmlPropertyCapture::registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction) -{ - // Let the caller check and avoid the function call :) - Q_ASSERT(compiledFunction->hasQmlDependencies()); - - QQmlEnginePrivate *ep = QQmlEnginePrivate::get(engine->qmlEngine()); - if (!ep) - return; - QQmlPropertyCapture *capture = ep->propertyCapture; - if (!capture || capture->watcher->wasDeleted()) - return; - - if (capture->expression->m_permanentDependenciesRegistered) - return; - - capture->expression->m_permanentDependenciesRegistered = true; - - QV4::Heap::QQmlContextWrapper *wrapper = context->qml(); - QQmlContextData *qmlContext = wrapper->context->contextData(); - - const quint32_le *idObjectDependency = compiledFunction->qmlIdObjectDependencyTable(); - const int idObjectDependencyCount = compiledFunction->nDependingIdObjects; - for (int i = 0; i < idObjectDependencyCount; ++i, ++idObjectDependency) { - Q_ASSERT(int(*idObjectDependency) < qmlContext->idValueCount); - capture->captureProperty(&qmlContext->idValues[*idObjectDependency].bindings, - QQmlPropertyCapture::Permanently); - } - - Q_ASSERT(qmlContext->contextObject); - const quint32_le *contextPropertyDependency = compiledFunction->qmlContextPropertiesDependencyTable(); - const int contextPropertyDependencyCount = compiledFunction->nDependingContextProperties; - for (int i = 0; i < contextPropertyDependencyCount; ++i) { - const int propertyIndex = *contextPropertyDependency++; - const int notifyIndex = *contextPropertyDependency++; - capture->captureProperty(qmlContext->contextObject, propertyIndex, notifyIndex, - QQmlPropertyCapture::Permanently); - } - - QObject *scopeObject = wrapper->scopeObject; - const quint32_le *scopePropertyDependency = compiledFunction->qmlScopePropertiesDependencyTable(); - const int scopePropertyDependencyCount = compiledFunction->nDependingScopeProperties; - for (int i = 0; i < scopePropertyDependencyCount; ++i) { - const int propertyIndex = *scopePropertyDependency++; - const int notifyIndex = *scopePropertyDependency++; - capture->captureProperty(scopeObject, propertyIndex, notifyIndex, - QQmlPropertyCapture::Permanently); + expression->activeGuards.prepend(g); } - } QQmlError QQmlJavaScriptExpression::error(QQmlEngine *engine) const @@ -471,13 +406,6 @@ void QQmlJavaScriptExpression::clearActiveGuards() g->Delete(); } -void QQmlJavaScriptExpression::clearPermanentGuards() -{ - m_permanentDependenciesRegistered = false; - while (QQmlJavaScriptExpressionGuard *g = permanentGuards.takeFirst()) - g->Delete(); -} - void QQmlJavaScriptExpressionGuard_callback(QQmlNotifierEndpoint *e, void **) { QQmlJavaScriptExpression *expression = diff --git a/src/qml/qml/qqmljavascriptexpression_p.h b/src/qml/qml/qqmljavascriptexpression_p.h index de3fba0774..453c8ab8a8 100644 --- a/src/qml/qml/qqmljavascriptexpression_p.h +++ b/src/qml/qml/qqmljavascriptexpression_p.h @@ -144,7 +144,6 @@ public: QQmlError error(QQmlEngine *) const; void clearError(); void clearActiveGuards(); - void clearPermanentGuards(); QQmlDelayedError *delayedError(); static QV4::ReturnedValue evalFunction(QQmlContextData *ctxt, QObject *scope, @@ -153,14 +152,6 @@ public: protected: void createQmlBinding(QQmlContextData *ctxt, QObject *scope, const QString &code, const QString &filename, quint16 line); - void cancelPermanentGuards() const - { - if (m_permanentDependenciesRegistered) { - for (QQmlJavaScriptExpressionGuard *it = permanentGuards.first(); it; it = permanentGuards.next(it)) - it->cancelNotify(); - } - } - void setupFunction(QV4::ExecutionContext *qmlContext, QV4::Function *f); void setCompilationUnit(const QQmlRefPointer &compilationUnit); @@ -169,7 +160,6 @@ protected: // activeGuards:flag2 - useSharedContext QBiPointer m_scopeObject; QForwardFieldList activeGuards; - QForwardFieldList permanentGuards; void setTranslationsCaptured(bool captured) { m_error.setFlagValue(captured); } bool translationsCaptured() const { return m_error.flag(); } @@ -186,7 +176,6 @@ private: QQmlContextData *m_context; QQmlJavaScriptExpression **m_prevExpression; QQmlJavaScriptExpression *m_nextExpression; - bool m_permanentDependenciesRegistered = false; QV4::PersistentValue m_qmlScope; QQmlRefPointer m_compilationUnit; @@ -204,14 +193,8 @@ public: Q_ASSERT(errorString == nullptr); } - enum Duration { - OnlyOnce, - Permanently - }; - - static void registerQmlDependencies(QV4::Heap::QmlContext *context, const QV4::ExecutionEngine *engine, const QV4::CompiledData::Function *compiledFunction); - void captureProperty(QQmlNotifier *, Duration duration = OnlyOnce); - void captureProperty(QObject *, int, int, Duration duration = OnlyOnce, bool doNotify = true); + void captureProperty(QQmlNotifier *); + void captureProperty(QObject *, int, int, bool doNotify = true); void captureTranslation() { translationCaptured = true; } QQmlEngine *engine; diff --git a/src/qml/types/qqmllistmodel.cpp b/src/qml/types/qqmllistmodel.cpp index 006825cc93..69b7876cf6 100644 --- a/src/qml/types/qqmllistmodel.cpp +++ b/src/qml/types/qqmllistmodel.cpp @@ -1605,8 +1605,7 @@ ReturnedValue ModelObject::virtualGet(const Managed *m, PropertyKey id, const Va if (QQmlEngine *qmlEngine = that->engine()->qmlEngine()) { QQmlEnginePrivate *ep = QQmlEnginePrivate::get(qmlEngine); if (ep && ep->propertyCapture) - ep->propertyCapture->captureProperty(that->object(), -1, role->index, - QQmlPropertyCapture::OnlyOnce, false); + ep->propertyCapture->captureProperty(that->object(), -1, role->index, /*doNotify=*/ false); } const int elementIndex = that->d()->elementIndex(); -- cgit v1.2.3 From 69d76d59cec0dcff4c52eef24e779fbef14beeca Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 19 Mar 2019 13:39:10 +0100 Subject: V4: Don't mark InternalClass::parent when garbage collecting The parent pointer is only kept so that we can update the parent's transitions when removing a child. There is no need to keep the parents alive for the children. Fixes: QTBUG-58559 Change-Id: Ia28183966bde6d478ca030fe11195489925dfc13 Reviewed-by: Simon Hausmann --- src/qml/jsruntime/qv4internalclass.cpp | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4internalclass.cpp b/src/qml/jsruntime/qv4internalclass.cpp index ddb8542e07..9906e2b1a0 100644 --- a/src/qml/jsruntime/qv4internalclass.cpp +++ b/src/qml/jsruntime/qv4internalclass.cpp @@ -257,11 +257,15 @@ void InternalClass::init(Heap::InternalClass *other) void InternalClass::destroy() { -#ifndef QT_NO_DEBUG for (const auto &t : transitions) { - Q_ASSERT(!t.lookup || !t.lookup->isMarked()); - } + if (t.lookup) { +#ifndef QT_NO_DEBUG + Q_ASSERT(t.lookup->parent == this); #endif + t.lookup->parent = nullptr; + } + } + if (parent && parent->engine && parent->isMarked()) parent->removeChildEntry(this); @@ -659,8 +663,6 @@ void InternalClass::markObjects(Heap::Base *b, MarkStack *stack) Heap::InternalClass *ic = static_cast(b); if (ic->prototype) ic->prototype->mark(stack); - if (ic->parent) - ic->parent->mark(stack); ic->nameMap.mark(stack); } -- cgit v1.2.3 From 710580aa84c5c3273e883d9e650f3159ad8250f3 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Thu, 3 Jan 2019 17:00:36 +0100 Subject: Accelerate access to singletons and imported scripts Use a dedicated lookup type to provide super fast access to engine wide singleton objects as well as scripts Task-number: QTBUG-69898 Change-Id: Ie430f48f6576a9171018ef18742dcf6b2adb4310 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup_p.h | 9 ++++++ src/qml/jsruntime/qv4qmlcontext.cpp | 55 +++++++++++++++++++++++++++++++++++-- src/qml/jsruntime/qv4qmlcontext_p.h | 4 ++- 3 files changed, 65 insertions(+), 3 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index e4d9ad8328..2384a1194e 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -129,6 +129,15 @@ struct Lookup { QQmlPropertyCache *propertyCache; QQmlPropertyData *propertyData; } qgadgetLookup; + struct { + quintptr unused1; + quintptr unused2; + int scriptIndex; + } qmlContextScriptLookup; + struct { + Heap::Object *singleton; + quintptr unused; + } qmlContextSingletonLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index ae458c604a..ab80e6bc45 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -79,7 +79,7 @@ void Heap::QQmlContextWrapper::destroy() Object::destroy(); } -ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base) +ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, bool *hasProperty, Value *base, Lookup *lookup) { if (!id.isString()) return Object::virtualGet(resource, id, receiver, hasProperty); @@ -159,6 +159,8 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Note: The scope object is only a QADMO for example when somebody registers a QQmlPropertyMap // sub-class as QML type and then instantiates it in .qml. if (scopeObject && QQmlPropertyCache::isDynamicMetaObject(scopeObject->metaObject())) { + // all bets are off, so don't try to optimize any lookups + lookup = nullptr; if (performGobalLookUp()) return result->asReturnedValue(); } @@ -171,11 +173,35 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r if (hasProperty) *hasProperty = true; if (r.scriptIndex != -1) { + if (lookup) { + lookup->qmlContextScriptLookup.scriptIndex = r.scriptIndex; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScript; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); if (scripts) return scripts->get(r.scriptIndex); return QV4::Encode::null(); } else if (r.type.isValid()) { + if (lookup) { + if (r.type.isSingleton()) { + QQmlEngine *e = v4->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = r.type.singletonInstanceInfo(); + siinfo->init(e); + if (siinfo->qobjectApi(e)) { + lookup->qmlContextSingletonLookup.singleton = + static_cast( + Value::fromReturnedValue( + QQmlTypeWrapper::create(v4, nullptr, r.type) + ).heapObject()); + } else { + QV4::ScopedObject o(scope, QJSValuePrivate::convertedToValue(v4, siinfo->scriptApi(e))); + lookup->qmlContextSingletonLookup.singleton = o->d(); + } + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupSingleton; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } + } return QQmlTypeWrapper::create(v4, scopeObject, r.type); } else if (r.importNamespace) { return QQmlTypeWrapper::create(v4, scopeObject, context->imports, r.importNamespace); @@ -361,12 +387,37 @@ ReturnedValue QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(Lookup * Scoped qmlContext(scope, engine->qmlContext()->qml()); bool hasProperty = false; ScopedValue result(scope, QQmlContextWrapper::getPropertyAndBase(qmlContext, name, /*receiver*/nullptr, - &hasProperty, base)); + &hasProperty, base, l)); if (!hasProperty) return engine->throwReferenceError(name.toQString()); return result->asReturnedValue(); } +ReturnedValue QQmlContextWrapper::lookupScript(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::null(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::null(); + + QV4::ScopedObject scripts(scope, context->importedScripts.valueRef()); + if (!scripts) + return QV4::Encode::null(); + return scripts->get(l->qmlContextScriptLookup.scriptIndex); +} + +ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(engine) + Q_UNUSED(base) + return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue(); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index f704280bf4..5eacea5830 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -100,11 +100,13 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object inline QQmlContextData *getContext() const { return *d()->context; } static ReturnedValue getPropertyAndBase(const QQmlContextWrapper *resource, PropertyKey id, const Value *receiver, - bool *hasProperty, Value *base); + bool *hasProperty, Value *base, Lookup *lookup = nullptr); 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); static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext -- cgit v1.2.3 From 78a2887d8c5d866fc430f197abea6bb31293c4b1 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2019 10:41:02 +0100 Subject: Accelerate access to id objects in lookups Task-number: QTBUG-69898 Change-Id: Ifbd9b3bf8d4b0c82b4c3933912e61eea8e0bb987 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup_p.h | 5 +++++ src/qml/jsruntime/qv4qmlcontext.cpp | 35 +++++++++++++++++++++++++++++++++-- src/qml/jsruntime/qv4qmlcontext_p.h | 1 + 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 2384a1194e..68da224273 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -138,6 +138,11 @@ struct Lookup { Heap::Object *singleton; quintptr unused; } qmlContextSingletonLookup; + struct { + quintptr unused1; + quintptr unused2; + int objectId; + } qmlContextIdObjectLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index ab80e6bc45..3d9e1cc0f8 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -223,11 +223,17 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r if (propertyIdx != -1) { if (propertyIdx < context->idValueCount) { + if (hasProperty) + *hasProperty = true; + + if (lookup) { + lookup->qmlContextIdObjectLookup.objectId = propertyIdx; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupIdObject; + return lookup->qmlContextPropertyGetter(lookup, v4, base); + } if (ep->propertyCapture) ep->propertyCapture->captureProperty(&context->idValues[propertyIdx].bindings); - if (hasProperty) - *hasProperty = true; return QV4::QObjectWrapper::wrap(v4, context->idValues[propertyIdx]); } else { @@ -281,6 +287,10 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } context = context->parent; + + // As the hierarchy of contexts is not stable, we can't do accelerated lookups beyond + // the immediate QML context (of the .qml file). + lookup = nullptr; } // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming @@ -418,6 +428,27 @@ ReturnedValue QQmlContextWrapper::lookupSingleton(Lookup *l, ExecutionEngine *en return Value::fromHeapObject(l->qmlContextSingletonLookup.singleton).asReturnedValue(); } +ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::null(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::null(); + + QQmlEnginePrivate *qmlEngine = QQmlEnginePrivate::get(engine->qmlEngine()); + const int objectId = l->qmlContextIdObjectLookup.objectId; + + if (qmlEngine->propertyCapture) + qmlEngine->propertyCapture->captureProperty(&context->idValues[objectId].bindings); + + return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 5eacea5830..caf9281540 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -107,6 +107,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue resolveQmlContextPropertyLookupGetter(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext -- cgit v1.2.3 From bb34abcc05d11f23c329f4c52aab638d991c1b98 Mon Sep 17 00:00:00 2001 From: Simon Hausmann Date: Fri, 4 Jan 2019 15:32:33 +0100 Subject: Fix lookup fallback into the global object in QML bindings This change addresses in particular qmlbench's fib10.qml, where usage of properties of the global object needs to go be accelerated in order to avoid regressing in performance. Task-number: QTBUG-69898 Change-Id: Ic43c64f4dd5459c4e92f87f03235ea836f971515 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup_p.h | 7 +++++++ src/qml/jsruntime/qv4qmlcontext.cpp | 33 +++++++++++++++++++++++++++++++-- src/qml/jsruntime/qv4qmlcontext_p.h | 1 + 3 files changed, 39 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index 68da224273..ee6da21121 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -143,6 +143,13 @@ struct Lookup { quintptr unused2; int objectId; } qmlContextIdObjectLookup; + struct { + // Same as protoLookup, as used for global lookups + quintptr reserved1; + quintptr reserved2; + quintptr reserved3; + ReturnedValue (*getterTrampoline)(Lookup *l, ExecutionEngine *engine); + } qmlContextGlobalLookup; }; uint nameIndex; diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 3d9e1cc0f8..e728cfb457 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -213,6 +213,7 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r } QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine()); + Lookup * const originalLookup = lookup; while (context) { // Search context properties @@ -295,8 +296,24 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Do a lookup in the global object here to avoid expressionContext->unresolvedNames becoming // true if we access properties of the global object. - if (performGobalLookUp()) - return result->asReturnedValue(); + if (originalLookup) { + // Try a lookup in the global object. It's theoretically possible to first find a property + // in the global object and then later a context property with the same name is added, but that + // never really worked as we used to detect access to global properties at type compile time anyway. + lookup = originalLookup; + result = lookup->resolveGlobalGetter(v4); + if (lookup->globalGetter != Lookup::globalGetterGeneric) { + if (hasProperty) + *hasProperty = true; + lookup->qmlContextGlobalLookup.getterTrampoline = lookup->globalGetter; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject; + return result->asReturnedValue(); + } + lookup->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + } else { + if (performGobalLookUp()) + return result->asReturnedValue(); + } expressionContext->unresolvedNames = true; @@ -449,6 +466,18 @@ ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *eng return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); } +ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base); + ReturnedValue result = l->qmlContextGlobalLookup.getterTrampoline(l, engine); + // In the unlikely event of mutation of the global object, update the trampoline. + if (l->qmlContextPropertyGetter != lookupInGlobalObject) { + l->qmlContextGlobalLookup.getterTrampoline = l->globalGetter; + l->qmlContextPropertyGetter = QQmlContextWrapper::lookupInGlobalObject; + } + return result; +} + void Heap::QmlContext::init(QV4::ExecutionContext *outerContext, QV4::QQmlContextWrapper *qml) { Heap::ExecutionContext::init(Heap::ExecutionContext::Type_QmlContext); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index caf9281540..cfcc53c7c9 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -108,6 +108,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); }; struct Q_QML_EXPORT QmlContext : public ExecutionContext -- cgit v1.2.3 From f9dac6f900fde93014305854b1bf3a81d9e58b92 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Thu, 3 Jan 2019 16:28:16 -0600 Subject: Accelerate lookup of singleton properties Task-number: QTBUG-69898 Change-Id: Id03ba543fa293da2690099c3e6f94b2725de562f Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4lookup_p.h | 3 +- src/qml/jsruntime/qv4qobjectwrapper.cpp | 4 ++- src/qml/qml/qqmltypewrapper.cpp | 61 +++++++++++++++++++++++++++++++++ src/qml/qml/qqmltypewrapper_p.h | 3 ++ 4 files changed, 69 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4lookup_p.h b/src/qml/jsruntime/qv4lookup_p.h index ee6da21121..03dc5f6d3c 100644 --- a/src/qml/jsruntime/qv4lookup_p.h +++ b/src/qml/jsruntime/qv4lookup_p.h @@ -71,6 +71,7 @@ struct Lookup { ReturnedValue (*qmlContextPropertyGetter)(Lookup *l, ExecutionEngine *engine, Value *thisObject); bool (*setter)(Lookup *l, ExecutionEngine *engine, Value &object, const Value &v); }; + // NOTE: gc assumes the first two entries in the struct are pointers to heap objects or null union { struct { Heap::Base *h1; @@ -119,7 +120,7 @@ struct Lookup { } indexedLookup; struct { Heap::InternalClass *ic; - quintptr unused; + Heap::QObjectWrapper *staticQObject; QQmlPropertyCache *propertyCache; QQmlPropertyData *propertyData; } qobjectLookup; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 053581226f..351076ac28 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -867,6 +867,7 @@ ReturnedValue QObjectWrapper::virtualResolveLookupGetter(const Object *object, E } lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; lookup->qobjectLookup.propertyCache = ddata->propertyCache; lookup->qobjectLookup.propertyCache->addref(); lookup->qobjectLookup.propertyData = property; @@ -889,7 +890,8 @@ ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engi if (!o || o->internalClass != lookup->qobjectLookup.ic) return revertLookup(); - const Heap::QObjectWrapper *This = static_cast(o); + const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject : + static_cast(o); QObject *qobj = This->object(); if (QQmlData::wasDeleted(qobj)) return QV4::Encode::undefined(); diff --git a/src/qml/qml/qqmltypewrapper.cpp b/src/qml/qml/qqmltypewrapper.cpp index 4089a7f030..d30c225741 100644 --- a/src/qml/qml/qqmltypewrapper.cpp +++ b/src/qml/qml/qqmltypewrapper.cpp @@ -47,6 +47,8 @@ #include #include #include +#include +#include QT_BEGIN_NAMESPACE @@ -169,6 +171,7 @@ static ReturnedValue throwLowercaseEnumError(QV4::ExecutionEngine *v4, String *n ReturnedValue QQmlTypeWrapper::virtualGet(const Managed *m, PropertyKey id, const Value *receiver, bool *hasProperty) { + // Keep this code in sync with ::virtualResolveLookupGetter Q_ASSERT(m->as()); if (!id.isString()) @@ -425,6 +428,64 @@ ReturnedValue QQmlTypeWrapper::virtualInstanceOf(const Object *typeObject, const return QV4::Encode(QQmlMetaObject::canConvert(theirType, myQmlType)); } +ReturnedValue QQmlTypeWrapper::virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup) +{ + // Keep this code in sync with ::virtualGet + PropertyKey id = engine->identifierTable->asPropertyKey(engine->currentStackFrame->v4Function->compilationUnit->runtimeStrings[lookup->nameIndex]); + if (!id.isString()) + return Object::virtualResolveLookupGetter(object, engine, lookup); + Scope scope(engine); + + const QQmlTypeWrapper *This = static_cast(object); + ScopedString name(scope, id.asStringOrSymbol()); + QQmlContextData *qmlContext = engine->callingQmlContext(); + + Scoped w(scope, static_cast(This)); + QQmlType type = w->d()->type(); + + if (type.isValid()) { + + if (type.isSingleton()) { + QQmlEngine *e = engine->qmlEngine(); + QQmlType::SingletonInstanceInfo *siinfo = type.singletonInstanceInfo(); + siinfo->init(e); + + QObject *qobjectSingleton = siinfo->qobjectApi(e); + if (qobjectSingleton) { + + const bool includeEnums = w->d()->mode == Heap::QQmlTypeWrapper::IncludeEnums; + if (!includeEnums || !name->startsWithUpper()) { + QQmlData *ddata = QQmlData::get(qobjectSingleton, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, Value::fromReturnedValue(QV4::QObjectWrapper::wrap(engine, qobjectSingleton))); + QQmlPropertyData *property = ddata->propertyCache->property(name.getPointer(), qobjectSingleton, qmlContext); + if (property) { + lookup->qobjectLookup.ic = This->internalClass(); + lookup->qobjectLookup.staticQObject = static_cast(val->heapObject()); + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = property; + lookup->getter = QV4::QObjectWrapper::lookupGetter; + return lookup->getter(lookup, engine, *This); + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + // Fall through to base implementation + } + return QV4::Object::virtualResolveLookupGetter(object, engine, lookup); +} + +bool QQmlTypeWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value) +{ + return Object::virtualResolveLookupSetter(object, engine, lookup, value); +} + void Heap::QQmlScopedEnumWrapper::destroy() { QQmlType::derefHandle(typePrivate); diff --git a/src/qml/qml/qqmltypewrapper_p.h b/src/qml/qml/qqmltypewrapper_p.h index fa1ad56902..44e82dec2b 100644 --- a/src/qml/qml/qqmltypewrapper_p.h +++ b/src/qml/qml/qqmltypewrapper_p.h @@ -111,6 +111,9 @@ struct Q_QML_EXPORT QQmlTypeWrapper : Object static ReturnedValue create(ExecutionEngine *, QObject *, const QQmlRefPointer &, const QQmlImportRef *, Heap::QQmlTypeWrapper::TypeNameMode = Heap::QQmlTypeWrapper::IncludeEnums); + static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); + static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &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); -- cgit v1.2.3 From bcbce96fffd25a4f2810f03cec060ab13e34ac9e Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 4 Jan 2019 13:09:21 -0600 Subject: Accelerate access to scope object properties in lookups Task-number: QTBUG-69898 Change-Id: I94bf1aa85c9b2302894f3224e41de81a456211f9 Reviewed-by: Ulf Hermann --- src/qml/compiler/qv4compileddata.cpp | 5 ++++ src/qml/jsruntime/qv4qmlcontext.cpp | 45 ++++++++++++++++++++++++++++++++- src/qml/jsruntime/qv4qmlcontext_p.h | 1 + src/qml/jsruntime/qv4qobjectwrapper.cpp | 24 ++++-------------- src/qml/jsruntime/qv4qobjectwrapper_p.h | 45 ++++++++++++++++++++++++++++++++- 5 files changed, 99 insertions(+), 21 deletions(-) (limited to 'src/qml') diff --git a/src/qml/compiler/qv4compileddata.cpp b/src/qml/compiler/qv4compileddata.cpp index 6701443971..7906b3572c 100644 --- a/src/qml/compiler/qv4compileddata.cpp +++ b/src/qml/compiler/qv4compileddata.cpp @@ -284,6 +284,11 @@ void CompilationUnit::unlink() if (QQmlPropertyCache *pc = l.qgadgetLookup.propertyCache) pc->release(); } + + if (l.qmlContextPropertyGetter == QQmlContextWrapper::lookupScopeObjectProperty) { + if (QQmlPropertyCache *pc = l.qobjectLookup.propertyCache) + pc->release(); + } } } diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index e728cfb457..9bdfa10030 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -261,13 +261,30 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Search scope object if (scopeObject) { bool hasProp = false; + + QQmlPropertyData *propertyData = nullptr; QV4::ScopedValue result(scope, QV4::QObjectWrapper::getQmlProperty(v4, context, scopeObject, - name, QV4::QObjectWrapper::CheckRevision, &hasProp)); + name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData)); if (hasProp) { if (hasProperty) *hasProperty = true; if (base) *base = QV4::QObjectWrapper::wrap(v4, scopeObject); + + if (lookup && propertyData) { + QQmlData *ddata = QQmlData::get(scopeObject, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, scopeObject))); + const QObjectWrapper *That = static_cast(val->objectValue()); + lookup->qobjectLookup.ic = That->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; + lookup->qmlContextPropertyGetter = QQmlContextWrapper::lookupScopeObjectProperty; + } + } + return result->asReturnedValue(); } } @@ -466,6 +483,32 @@ ReturnedValue QQmlContextWrapper::lookupIdObject(Lookup *l, ExecutionEngine *eng return QV4::QObjectWrapper::wrap(engine, context->idValues[objectId]); } +ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QObject *scopeObject = qmlContext->qmlScope(); + if (!scopeObject) + return QV4::Encode::undefined(); + + if (QQmlData::wasDeleted(scopeObject)) + return QV4::Encode::undefined(); + + const auto revertLookup = [l, engine, base]() { + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + }; + + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, scopeObject)); + return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); +} + ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) { Q_UNUSED(base); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index cfcc53c7c9..83bf2a1d3e 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -108,6 +108,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupScript(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); }; diff --git a/src/qml/jsruntime/qv4qobjectwrapper.cpp b/src/qml/jsruntime/qv4qobjectwrapper.cpp index 351076ac28..377c30617a 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper.cpp +++ b/src/qml/jsruntime/qv4qobjectwrapper.cpp @@ -357,7 +357,7 @@ ReturnedValue QObjectWrapper::getQmlProperty(QQmlContextData *qmlContext, String return getProperty(v4, d()->object(), result); } -ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty) +ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, QObjectWrapper::RevisionMode revisionMode, bool *hasProperty, QQmlPropertyData **property) { if (QQmlData::wasDeleted(object)) { if (hasProperty) @@ -384,6 +384,9 @@ ReturnedValue QObjectWrapper::getQmlProperty(QV4::ExecutionEngine *engine, QQmlC if (hasProperty) *hasProperty = true; + if (property) + *property = result; + return getProperty(engine, object, result); } else { // Check if this object is already wrapped. @@ -884,24 +887,7 @@ ReturnedValue QObjectWrapper::lookupGetter(Lookup *lookup, ExecutionEngine *engi return Lookup::getterGeneric(lookup, engine, object); }; - // we can safely cast to a QV4::Object here. If object is something else, - // the internal class won't match - Heap::Object *o = static_cast(object.heapObject()); - if (!o || o->internalClass != lookup->qobjectLookup.ic) - return revertLookup(); - - const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject : - static_cast(o); - QObject *qobj = This->object(); - if (QQmlData::wasDeleted(qobj)) - return QV4::Encode::undefined(); - - QQmlData *ddata = QQmlData::get(qobj, /*create*/false); - if (!ddata || ddata->propertyCache != lookup->qobjectLookup.propertyCache) - return revertLookup(); - - QQmlPropertyData *property = lookup->qobjectLookup.propertyData; - return getProperty(engine, qobj, property); + return lookupGetterImpl(lookup, engine, object, /*useOriginalProperty*/ false, revertLookup); } bool QObjectWrapper::virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, diff --git a/src/qml/jsruntime/qv4qobjectwrapper_p.h b/src/qml/jsruntime/qv4qobjectwrapper_p.h index 471f352c2a..2558ede401 100644 --- a/src/qml/jsruntime/qv4qobjectwrapper_p.h +++ b/src/qml/jsruntime/qv4qobjectwrapper_p.h @@ -62,6 +62,7 @@ #include #include +#include QT_BEGIN_NAMESPACE @@ -167,7 +168,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object QObject *object() const { return d()->object(); } ReturnedValue getQmlProperty(QQmlContextData *qmlContext, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, bool includeImports = false) const; - static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr); + static ReturnedValue getQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, bool *hasProperty = nullptr, QQmlPropertyData **property = nullptr); static bool setQmlProperty(ExecutionEngine *engine, QQmlContextData *qmlContext, QObject *object, String *name, RevisionMode revisionMode, const Value &value); @@ -185,6 +186,7 @@ struct Q_QML_EXPORT QObjectWrapper : public Object static ReturnedValue virtualResolveLookupGetter(const Object *object, ExecutionEngine *engine, Lookup *lookup); static ReturnedValue lookupGetter(Lookup *l, ExecutionEngine *engine, const Value &object); + template static ReturnedValue lookupGetterImpl(Lookup *l, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revert); static bool virtualResolveLookupSetter(Object *object, ExecutionEngine *engine, Lookup *lookup, const Value &value); protected: @@ -222,6 +224,47 @@ inline ReturnedValue QObjectWrapper::wrap(ExecutionEngine *engine, QObject *obje return wrap_slowPath(engine, object); } +template +inline ReturnedValue QObjectWrapper::lookupGetterImpl(Lookup *lookup, ExecutionEngine *engine, const Value &object, bool useOriginalProperty, ReversalFunctor revertLookup) +{ + // we can safely cast to a QV4::Object here. If object is something else, + // the internal class won't match + Heap::Object *o = static_cast(object.heapObject()); + if (!o || o->internalClass != lookup->qobjectLookup.ic) + return revertLookup(); + + const Heap::QObjectWrapper *This = lookup->qobjectLookup.staticQObject ? lookup->qobjectLookup.staticQObject : + static_cast(o); + QObject *qobj = This->object(); + if (QQmlData::wasDeleted(qobj)) + return QV4::Encode::undefined(); + + QQmlData *ddata = QQmlData::get(qobj, /*create*/false); + if (!ddata) + return revertLookup(); + + QQmlPropertyData *property = lookup->qobjectLookup.propertyData; + if (ddata->propertyCache != lookup->qobjectLookup.propertyCache) { + if (property->isOverridden() && (!useOriginalProperty || property->isFunction() || property->isSignalHandler())) + return revertLookup(); + + QQmlPropertyCache *fromMo = ddata->propertyCache; + QQmlPropertyCache *toMo = lookup->qobjectLookup.propertyCache; + bool canConvert = false; + while (fromMo) { + if (fromMo == toMo) { + canConvert = true; + break; + } + fromMo = fromMo->parent(); + } + if (!canConvert) + return revertLookup(); + } + + return getProperty(engine, qobj, property); +} + struct QQmlValueTypeWrapper; struct Q_QML_EXPORT QObjectMethod : public QV4::FunctionObject -- cgit v1.2.3 From 937d8114e9ccf607462ab72a4b6e801756698473 Mon Sep 17 00:00:00 2001 From: Michael Brasser Date: Fri, 22 Feb 2019 16:12:50 -0600 Subject: Accelerate access to initial context object properties in lookups Task-number: QTBUG-69898 Change-Id: If92a0931bd4d64f6c176e93effb04df85ce27284 Reviewed-by: Ulf Hermann --- src/qml/jsruntime/qv4qmlcontext.cpp | 57 ++++++++++++++++++++++++++++++++++++- src/qml/jsruntime/qv4qmlcontext_p.h | 1 + 2 files changed, 57 insertions(+), 1 deletion(-) (limited to 'src/qml') diff --git a/src/qml/jsruntime/qv4qmlcontext.cpp b/src/qml/jsruntime/qv4qmlcontext.cpp index 9bdfa10030..97b955632d 100644 --- a/src/qml/jsruntime/qv4qmlcontext.cpp +++ b/src/qml/jsruntime/qv4qmlcontext.cpp @@ -215,6 +215,14 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r QQmlEnginePrivate *ep = QQmlEnginePrivate::get(v4->qmlEngine()); Lookup * const originalLookup = lookup; + decltype(lookup->qmlContextPropertyGetter) contextGetterFunction = QQmlContextWrapper::lookupContextObjectProperty; + + // minor optimization so we don't potentially try two property lookups on the same object + if (scopeObject == context->contextObject) { + scopeObject = nullptr; + contextGetterFunction = QQmlContextWrapper::lookupScopeObjectProperty; + } + while (context) { // Search context properties const QV4::IdentifierHash &properties = context->propertyNames(); @@ -294,12 +302,29 @@ ReturnedValue QQmlContextWrapper::getPropertyAndBase(const QQmlContextWrapper *r // Search context object if (context->contextObject) { bool hasProp = false; - result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, name, QV4::QObjectWrapper::CheckRevision, &hasProp); + QQmlPropertyData *propertyData = nullptr; + result = QV4::QObjectWrapper::getQmlProperty(v4, context, context->contextObject, + name, QV4::QObjectWrapper::CheckRevision, &hasProp, &propertyData); if (hasProp) { if (hasProperty) *hasProperty = true; if (base) *base = QV4::QObjectWrapper::wrap(v4, context->contextObject); + + if (lookup && propertyData) { + QQmlData *ddata = QQmlData::get(context->contextObject, false); + if (ddata && ddata->propertyCache) { + ScopedValue val(scope, base ? *base : Value::fromReturnedValue(QV4::QObjectWrapper::wrap(v4, context->contextObject))); + const QObjectWrapper *That = static_cast(val->objectValue()); + lookup->qobjectLookup.ic = That->internalClass(); + lookup->qobjectLookup.staticQObject = nullptr; + lookup->qobjectLookup.propertyCache = ddata->propertyCache; + lookup->qobjectLookup.propertyCache->addref(); + lookup->qobjectLookup.propertyData = propertyData; + lookup->qmlContextPropertyGetter = contextGetterFunction; + } + } + return result->asReturnedValue(); } } @@ -509,6 +534,36 @@ ReturnedValue QQmlContextWrapper::lookupScopeObjectProperty(Lookup *l, Execution return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); } +ReturnedValue QQmlContextWrapper::lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base) +{ + Q_UNUSED(base) + Scope scope(engine); + Scoped qmlContext(scope, engine->qmlContext()); + if (!qmlContext) + return QV4::Encode::undefined(); + + QQmlContextData *context = qmlContext->qmlContext(); + if (!context) + return QV4::Encode::undefined(); + + QObject *contextObject = context->contextObject; + if (!contextObject) + return QV4::Encode::undefined(); + + if (QQmlData::wasDeleted(contextObject)) + return QV4::Encode::undefined(); + + const auto revertLookup = [l, engine, base]() { + l->qobjectLookup.propertyCache->release(); + l->qobjectLookup.propertyCache = nullptr; + l->qmlContextPropertyGetter = QQmlContextWrapper::resolveQmlContextPropertyLookupGetter; + return QQmlContextWrapper::resolveQmlContextPropertyLookupGetter(l, engine, base); + }; + + ScopedValue obj(scope, QV4::QObjectWrapper::wrap(engine, contextObject)); + return QObjectWrapper::lookupGetterImpl(l, engine, obj, /*useOriginalProperty*/ true, revertLookup); +} + ReturnedValue QQmlContextWrapper::lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base) { Q_UNUSED(base); diff --git a/src/qml/jsruntime/qv4qmlcontext_p.h b/src/qml/jsruntime/qv4qmlcontext_p.h index 83bf2a1d3e..6375294375 100644 --- a/src/qml/jsruntime/qv4qmlcontext_p.h +++ b/src/qml/jsruntime/qv4qmlcontext_p.h @@ -109,6 +109,7 @@ struct Q_QML_EXPORT QQmlContextWrapper : Object static ReturnedValue lookupSingleton(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupIdObject(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupScopeObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); + static ReturnedValue lookupContextObjectProperty(Lookup *l, ExecutionEngine *engine, Value *base); static ReturnedValue lookupInGlobalObject(Lookup *l, ExecutionEngine *engine, Value *base); }; -- cgit v1.2.3 From 77847ca2c3bad62bab2219d37e1d3d51ce83ade2 Mon Sep 17 00:00:00 2001 From: Kai Koehne Date: Mon, 18 Mar 2019 16:55:49 +0100 Subject: Doc: Rename State Machine QML Types page Make name more in line with the import and with the other overview pages. This became apparent because the All QML Modules overview page now shows the name of the page, instead of the import: https://doc-snapshots.qt.io/qt5-5.13/modules-qml.html# Change-Id: Iee1362247ea6837dd9404885890b2adb7af2c544 Reviewed-by: Ulf Hermann Reviewed-by: Venugopal Shivashankar --- src/qml/doc/src/statemachine.qdoc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/qml') diff --git a/src/qml/doc/src/statemachine.qdoc b/src/qml/doc/src/statemachine.qdoc index 6986f1baa0..231b85af76 100644 --- a/src/qml/doc/src/statemachine.qdoc +++ b/src/qml/doc/src/statemachine.qdoc @@ -27,7 +27,7 @@ /*! \qmlmodule QtQml.StateMachine 1.\QtMinorVersion - \title Declarative State Machine QML Types + \title Qt QML State Machine QML Types \brief Provides QML types to create and execute state graphs. The following is a list of QML types provided by the module: @@ -322,7 +322,7 @@ \section1 Related Information \list - \li \l{Declarative State Machine QML Types} + \li \l{Qt QML State Machine QML Types} \li \l{The State Machine Framework} \endlist */ -- cgit v1.2.3