diff options
-rw-r--r-- | src/qml/compiler/qv4compileddata_p.h | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4compiler.cpp | 2 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilercontext_p.h | 1 | ||||
-rw-r--r-- | src/qml/compiler/qv4compilerscanfunctions.cpp | 2 | ||||
-rw-r--r-- | src/qml/jsruntime/qv4function_p.h | 1 | ||||
-rw-r--r-- | src/qml/parser/qqmljs.g | 148 | ||||
-rw-r--r-- | src/qml/parser/qqmljsast_p.h | 3 | ||||
-rw-r--r-- | tests/auto/qml/ecmascripttests/TestExpectations | 49 | ||||
-rw-r--r-- | tests/auto/qml/qqmlparser/tst_qqmlparser.cpp | 7 |
9 files changed, 147 insertions, 68 deletions
diff --git a/src/qml/compiler/qv4compileddata_p.h b/src/qml/compiler/qv4compileddata_p.h index 7f0b66c569..41c27ffcd4 100644 --- a/src/qml/compiler/qv4compileddata_p.h +++ b/src/qml/compiler/qv4compileddata_p.h @@ -210,7 +210,7 @@ struct Function IsStrict = 0x1, HasDirectEval = 0x2, UsesArgumentsObject = 0x4, -// Unused = 0x8, + IsArrowFunction = 0x8, HasCatchOrWith = 0x10 }; diff --git a/src/qml/compiler/qv4compiler.cpp b/src/qml/compiler/qv4compiler.cpp index c9e535c93f..bfb450d408 100644 --- a/src/qml/compiler/qv4compiler.cpp +++ b/src/qml/compiler/qv4compiler.cpp @@ -306,6 +306,8 @@ void QV4::Compiler::JSUnitGenerator::writeFunction(char *f, QV4::Compiler::Conte function->flags |= CompiledData::Function::UsesArgumentsObject; if (irFunction->isStrict) function->flags |= CompiledData::Function::IsStrict; + if (irFunction->isArrowFunction) + function->flags |= CompiledData::Function::IsArrowFunction; if (irFunction->hasTry || irFunction->hasWith) function->flags |= CompiledData::Function::HasCatchOrWith; function->nestedFunctionIndex = diff --git a/src/qml/compiler/qv4compilercontext_p.h b/src/qml/compiler/qv4compilercontext_p.h index fef2b56055..89bdfc9d8e 100644 --- a/src/qml/compiler/qv4compilercontext_p.h +++ b/src/qml/compiler/qv4compilercontext_p.h @@ -141,6 +141,7 @@ struct Context { bool hasDirectEval = false; bool hasNestedFunctions = false; bool isStrict = false; + bool isArrowFunction = false; bool usesThis = false; bool hasTry = false; bool hasWith = false; diff --git a/src/qml/compiler/qv4compilerscanfunctions.cpp b/src/qml/compiler/qv4compilerscanfunctions.cpp index 3f7211b95e..5c606e7f7f 100644 --- a/src/qml/compiler/qv4compilerscanfunctions.cpp +++ b/src/qml/compiler/qv4compilerscanfunctions.cpp @@ -408,6 +408,8 @@ bool ScanFunctions::enterFunction(Node *ast, const QString &name, FormalParamete if (formals->containsName(QStringLiteral("arguments"))) _context->usesArgumentsObject = Context::ArgumentsObjectNotUsed; + if (expr && expr->isArrowFunction) + _context->isArrowFunction = true; if (!name.isEmpty() && !formals->containsName(name)) diff --git a/src/qml/jsruntime/qv4function_p.h b/src/qml/jsruntime/qv4function_p.h index c8dbcec51d..d3206cc230 100644 --- a/src/qml/jsruntime/qv4function_p.h +++ b/src/qml/jsruntime/qv4function_p.h @@ -100,6 +100,7 @@ struct Q_QML_EXPORT Function { inline bool usesArgumentsObject() const { return compiledFunction->flags & CompiledData::Function::UsesArgumentsObject; } inline bool isStrict() const { return compiledFunction->flags & CompiledData::Function::IsStrict; } + inline bool isArrowFunction() const { return compiledFunction->flags & CompiledData::Function::IsArrowFunction; } QQmlSourceLocation sourceLocation() const { diff --git a/src/qml/parser/qqmljs.g b/src/qml/parser/qqmljs.g index fe8e744264..ecab053b4a 100644 --- a/src/qml/parser/qqmljs.g +++ b/src/qml/parser/qqmljs.g @@ -387,6 +387,7 @@ protected: AST::UiQualifiedId *reparseAsQualifiedId(AST::ExpressionNode *expr); AST::UiQualifiedPragmaId *reparseAsQualifiedPragmaId(AST::ExpressionNode *expr); + AST::FormalParameterList *reparseAsFormalParameterList(AST::ExpressionNode *expr); void pushToken(int token); int lookaheadToken(Lexer *lexer); @@ -429,6 +430,14 @@ protected: int functionNestingLevel = 0; + enum CoverExpressionType { + CE_Invalid, + CE_ParenthesizedExpression, + CE_FormalParameterList + }; + AST::SourceLocation coverExpressionErrorLocation; + CoverExpressionType coverExpressionType = CE_Invalid; + QList<DiagnosticMessage> diagnostic_messages; }; @@ -541,6 +550,34 @@ AST::UiQualifiedPragmaId *Parser::reparseAsQualifiedPragmaId(AST::ExpressionNode return 0; } +AST::FormalParameterList *Parser::reparseAsFormalParameterList(AST::ExpressionNode *expr) +{ + AST::FormalParameterList *f = nullptr; + if (AST::Expression *commaExpr = AST::cast<AST::Expression *>(expr)) { + f = reparseAsFormalParameterList(commaExpr->left); + if (!f) + return nullptr; + + expr = commaExpr->right; + } + + AST::ExpressionNode *rhs = nullptr; + if (AST::BinaryExpression *assign = AST::cast<AST::BinaryExpression *>(expr)) { + if (assign->op != QSOperator::Assign) + return nullptr; + expr = assign->left; + rhs = assign->right; + } + AST::BindingElement *binding = nullptr; + if (AST::IdentifierExpression *idExpr = AST::cast<AST::IdentifierExpression *>(expr)) { + binding = new (pool) AST::BindingElement(idExpr->name, rhs); + binding->identifierToken = idExpr->identifierToken; + } + if (!binding) + return nullptr; + return new (pool) AST::FormalParameterList(f, binding); +} + void Parser::pushToken(int token) { last_token->token = yytoken; @@ -1423,8 +1460,16 @@ PrimaryExpression: RegularExpressionLiteral; PrimaryExpression: TemplateLiteral; PrimaryExpression: CoverParenthesizedExpressionAndArrowParameterList; +/. + case $rule_number: { + if (coverExpressionType != CE_ParenthesizedExpression) { + syntaxError(coverExpressionErrorLocation, "Expected token ')'."); + return false; + } + } break; +./ --- ### Further restricted parsing of the CoverParenthesizedExpressionAndArrowParameterList to the one rule below when this is parsed as a primary expression! +-- Parsing of the CoverParenthesizedExpressionAndArrowParameterList is restricted to the one rule below when this is parsed as a primary expression CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_RPAREN; /. case $rule_number: { @@ -1432,14 +1477,42 @@ CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_RPAR node->lparenToken = loc(1); node->rparenToken = loc(3); sym(1).Node = node; + coverExpressionType = CE_ParenthesizedExpression; } break; ./ CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_RPAREN; -CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_ELLIPSIS BindingIdentifier T_RPAREN; -CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN T_ELLIPSIS BindingPattern T_RPAREN; -CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA T_ELLIPSIS BindingIdentifier T_RPAREN; -CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA T_ELLIPSIS BindingPattern T_RPAREN; +/. + case $rule_number: { + sym(1).Node = nullptr; + coverExpressionErrorLocation = loc(2); + coverExpressionType = CE_FormalParameterList; + } break; +./ + +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN BindingRestElement T_RPAREN; +/. + case $rule_number: { + AST::FormalParameterList *node = (new (pool) AST::FormalParameterList(nullptr, sym(2).Node))->finish(); + sym(1).Node = node; + coverExpressionErrorLocation = loc(2); + coverExpressionType = CE_FormalParameterList; + } break; +./ + +CoverParenthesizedExpressionAndArrowParameterList: T_LPAREN Expression_In T_COMMA BindingRestElementOpt T_RPAREN; +/. + case $rule_number: { + AST::FormalParameterList *list = reparseAsFormalParameterList(sym(2).Expression); + if (!list) + syntaxError(loc(1), "Invalid Arrow parameter list."); + if (sym(4).Node) + list = new (pool) AST::FormalParameterList(list, sym(4).Node); + coverExpressionErrorLocation = loc(4); + coverExpressionType = CE_FormalParameterList; + sym(1).Node = list->finish(); + } break; +./ Literal: T_NULL; /. @@ -3361,17 +3434,63 @@ FunctionRBrace: T_RBRACE; FunctionBody: FunctionStatementList; FunctionStatementList: StatementListOpt; -ArrowFunction: ArrowParameters T_ARROW ConciseBody; -/. case $rule_number: { UNIMPLEMENTED; } ./ -ArrowFunction_In: ArrowParameters T_ARROW ConciseBody_In; -/. case $rule_number: { UNIMPLEMENTED; } ./ +ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression; -- [lookahead ≠ {] +/. case $rule_number: Q_FALLTHROUGH(); ./ +ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead AssignmentExpression_In; -- [lookahead ≠ {] +/. + case $rule_number: { + AST::ReturnStatement *ret = new (pool) AST::ReturnStatement(sym(4).Expression); + ret->returnToken = sym(4).Node->firstSourceLocation(); + ret->semicolonToken = sym(4).Node->lastSourceLocation(); + AST::StatementList *statements = (new (pool) AST::StatementList(ret))->finish(); + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, statements); + f->isArrowFunction = true; + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); + f->lbraceToken = sym(4).Node->firstSourceLocation(); + f->rbraceToken = sym(4).Node->lastSourceLocation(); + sym(1).Node = f; + } break; +./ + +ArrowFunction: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; +/. case $rule_number: Q_FALLTHROUGH(); ./ +ArrowFunction_In: ArrowParameters T_ARROW ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; +/. + case $rule_number: { + AST::FunctionExpression *f = new (pool) AST::FunctionExpression(QStringRef(), sym(1).FormalParameterList, sym(6).StatementList); + f->isArrowFunction = true; + f->functionToken = sym(1).Node ? sym(1).Node->firstSourceLocation() : loc(1); + f->lbraceToken = loc(6); + f->rbraceToken = loc(7); + sym(1).Node = f; + } break; +./ ArrowParameters: BindingIdentifier; +/. + case $rule_number: { + AST::BindingElement *e = new (pool) AST::BindingElement(stringRef(1)); + e->identifierToken = loc(1); + sym(1).FormalParameterList = (new (pool) AST::FormalParameterList(nullptr, e))->finish(); + } break; +./ +-- CoverParenthesizedExpressionAndArrowParameterList for ArrowParameters i being refined to: +-- ArrowFormalParameters: T_LPAREN StrictFormalParameters T_RPAREN ArrowParameters: CoverParenthesizedExpressionAndArrowParameterList; - --- ### CoverParenthesizedExpressionAndArrowParameterList for ArrowParameters refined to: --- ArrowFormalParameters[Yield]: (StrictFormalParameters[?Yield]) +/. + case $rule_number: { + if (coverExpressionType != CE_FormalParameterList) { + AST::NestedExpression *ne = static_cast<AST::NestedExpression *>(sym(1).Node); + AST::FormalParameterList *list = reparseAsFormalParameterList(ne->expression); + if (!list) { + syntaxError(loc(1), "Invalid Arrow parameter list."); + return false; + } + sym(1).Node = list->finish(); + } + } break; +./ ConciseBodyLookahead: ; /: @@ -3384,11 +3503,6 @@ ConciseBodyLookahead: ; } break; ./ -ConciseBody: ConciseBodyLookahead AssignmentExpression; -- [lookahead ≠ {] -ConciseBody_In: ConciseBodyLookahead AssignmentExpression_In; -- [lookahead ≠ {] -ConciseBody: ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; -ConciseBody_In: ConciseBodyLookahead T_FORCE_BLOCK FunctionLBrace FunctionBody FunctionRBrace; - MethodDefinition: PropertyName T_LPAREN StrictFormalParameters T_RPAREN FunctionLBrace FunctionBody FunctionRBrace; /. case $rule_number: { UNIMPLEMENTED; } ./ diff --git a/src/qml/parser/qqmljsast_p.h b/src/qml/parser/qqmljsast_p.h index 6c8a5c7af6..db41528a02 100644 --- a/src/qml/parser/qqmljsast_p.h +++ b/src/qml/parser/qqmljsast_p.h @@ -1327,7 +1327,7 @@ public: void accept0(Visitor *visitor) override; SourceLocation firstSourceLocation() const override - { return statement ? statement->firstSourceLocation() : statement->firstSourceLocation(); } + { return statement->firstSourceLocation(); } SourceLocation lastSourceLocation() const override { return next ? next->lastSourceLocation() : statement->lastSourceLocation(); } @@ -2061,6 +2061,7 @@ public: // attributes QStringRef name; + bool isArrowFunction = false; FormalParameterList *formals; StatementList *body; SourceLocation functionToken; diff --git a/tests/auto/qml/ecmascripttests/TestExpectations b/tests/auto/qml/ecmascripttests/TestExpectations index f9c54d099c..e862d36dc8 100644 --- a/tests/auto/qml/ecmascripttests/TestExpectations +++ b/tests/auto/qml/ecmascripttests/TestExpectations @@ -783,7 +783,6 @@ built-ins/Date/prototype/toDateString/format built-ins/Date/prototype/toDateString/invalid-date built-ins/Date/prototype/toISOString/15.9.5.43-0-8 built-ins/Date/prototype/toString/format -built-ins/Date/prototype/toString/non-date-receiver built-ins/Date/prototype/toTimeString/format built-ins/Date/prototype/toTimeString/invalid-date built-ins/Date/prototype/toUTCString/day-names @@ -4067,16 +4066,13 @@ language/eval-code/direct/lex-env-heritage language/eval-code/direct/lex-env-no-init-cls language/eval-code/direct/lex-env-no-init-const language/eval-code/direct/lex-env-no-init-let -language/eval-code/direct/new.target-arrow language/eval-code/direct/new.target-fn language/eval-code/direct/non-definable-function-with-function language/eval-code/direct/non-definable-function-with-variable language/eval-code/direct/non-definable-global-function language/eval-code/direct/non-definable-global-generator language/eval-code/direct/non-definable-global-var -language/eval-code/direct/super-call-arrow language/eval-code/direct/super-call-method -language/eval-code/direct/super-prop-arrow language/eval-code/direct/super-prop-method language/eval-code/direct/this-value-func-strict-source language/eval-code/direct/var-env-func-init-global-new @@ -4133,20 +4129,10 @@ language/expressions/array/spread-sngl-empty language/expressions/array/spread-sngl-expr language/expressions/array/spread-sngl-iter language/expressions/array/spread-sngl-literal -language/expressions/arrow-function/arrow/binding-tests-1 -language/expressions/arrow-function/arrow/binding-tests-2 -language/expressions/arrow-function/arrow/binding-tests-3 -language/expressions/arrow-function/arrow/capturing-closure-variables-1 -language/expressions/arrow-function/arrow/capturing-closure-variables-2 -language/expressions/arrow-function/arrow/concisebody-lookahead-assignmentexpression-1 -language/expressions/arrow-function/arrow/concisebody-lookahead-assignmentexpression-2 language/expressions/arrow-function/ArrowFunction_restricted-properties language/expressions/arrow-function/cannot-override-this-with-thisArg language/expressions/arrow-function/dflt-params-abrupt -language/expressions/arrow-function/dflt-params-arg-val-not-undefined -language/expressions/arrow-function/dflt-params-arg-val-undefined language/expressions/arrow-function/dflt-params-ref-later -language/expressions/arrow-function/dflt-params-ref-prior language/expressions/arrow-function/dflt-params-ref-self language/expressions/arrow-function/dflt-params-trailing-comma language/expressions/arrow-function/dstr-ary-init-iter-close @@ -4311,11 +4297,8 @@ language/expressions/arrow-function/dstr-obj-ptrn-prop-obj language/expressions/arrow-function/dstr-obj-ptrn-prop-obj-init language/expressions/arrow-function/dstr-obj-ptrn-prop-obj-value-null language/expressions/arrow-function/dstr-obj-ptrn-prop-obj-value-undef -language/expressions/arrow-function/empty-function-body-returns-undefined -language/expressions/arrow-function/expression-body-implicit-return language/expressions/arrow-function/length-dflt language/expressions/arrow-function/lexical-arguments -language/expressions/arrow-function/lexical-bindings-overriden-by-formal-parameters-non-strict language/expressions/arrow-function/lexical-new.target language/expressions/arrow-function/lexical-new.target-closure-returned language/expressions/arrow-function/lexical-supercall-from-immediately-invoked-arrow @@ -4323,10 +4306,6 @@ language/expressions/arrow-function/lexical-super-call-from-within-constructor language/expressions/arrow-function/lexical-super-property language/expressions/arrow-function/lexical-super-property-from-within-constructor language/expressions/arrow-function/lexical-this -language/expressions/arrow-function/low-precedence-expression-body-no-parens -language/expressions/arrow-function/non-strict -language/expressions/arrow-function/object-literal-return-requires-body-parens -language/expressions/arrow-function/param-dflt-yield-id-non-strict language/expressions/arrow-function/params-trailing-comma-multiple language/expressions/arrow-function/params-trailing-comma-single language/expressions/arrow-function/prototype-rules @@ -4335,31 +4314,8 @@ language/expressions/arrow-function/scope-param-elem-var-close language/expressions/arrow-function/scope-param-elem-var-open language/expressions/arrow-function/scope-param-rest-elem-var-close language/expressions/arrow-function/scope-param-rest-elem-var-open -language/expressions/arrow-function/scope-paramsbody-var-close language/expressions/arrow-function/scope-paramsbody-var-open -language/expressions/arrow-function/statement-body-requires-braces-must-return-explicitly -language/expressions/arrow-function/statement-body-requires-braces-must-return-explicitly-missing -language/expressions/arrow-function/strict -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-arguments -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-concisebody-assignmentexpression -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-eval -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-lineterminator-concisebody-assignmentexpression -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-lineterminator-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-bindingidentifier-yield -language/expressions/arrow-function/syntax/arrowparameters-cover-concisebody-assignmentexpression -language/expressions/arrow-function/syntax/arrowparameters-cover-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-cover-formalparameters-arguments -language/expressions/arrow-function/syntax/arrowparameters-cover-formalparameters-eval -language/expressions/arrow-function/syntax/arrowparameters-cover-formalparameters-yield -language/expressions/arrow-function/syntax/arrowparameters-cover-includes-rest-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-cover-initialize-1 language/expressions/arrow-function/syntax/arrowparameters-cover-initialize-2 -language/expressions/arrow-function/syntax/arrowparameters-cover-lineterminator-concisebody-assignmentexpression -language/expressions/arrow-function/syntax/arrowparameters-cover-lineterminator-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-cover-rest-concisebody-functionbody -language/expressions/arrow-function/syntax/arrowparameters-cover-rest-lineterminator-concisebody-functionbody -language/expressions/arrow-function/syntax/variations language/expressions/arrow-function/throw-new language/expressions/assignment/destructuring/iterator-destructuring-property-reference-target-evaluation-order language/expressions/assignment/destructuring/keyed-destructuring-property-reference-target-evaluation-order @@ -4597,7 +4553,6 @@ language/expressions/assignment/S11.13.1_A7_T1 language/expressions/assignment/S11.13.1_A7_T2 language/expressions/assignment/S11.13.1_A7_T3 language/expressions/async-arrow-function/arrow-returns-promise -language/expressions/async-arrow-function/async-lineterminator-identifier-throws language/expressions/async-arrow-function/dflt-params-abrupt language/expressions/async-arrow-function/dflt-params-arg-val-not-undefined language/expressions/async-arrow-function/dflt-params-arg-val-undefined @@ -6548,8 +6503,6 @@ language/expressions/tagged-template/cache-realm language/expressions/tagged-template/cache-same-site language/expressions/tagged-template/cache-same-site-top-level language/expressions/tagged-template/call-expression-argument-list-evaluation -language/expressions/tagged-template/call-expression-context-no-strict -language/expressions/tagged-template/call-expression-context-strict language/expressions/tagged-template/invalid-escape-sequences language/expressions/tagged-template/member-expression-argument-list-evaluation language/expressions/tagged-template/tco-call @@ -6857,7 +6810,6 @@ language/module-code/namespace/Symbol.iterator language/module-code/namespace/Symbol.toStringTag language/module-code/parse-export-empty language/rest-parameters/array-pattern -language/rest-parameters/arrow-function language/rest-parameters/expected-argument-count language/rest-parameters/object-pattern language/rest-parameters/with-new-target @@ -9463,7 +9415,6 @@ built-ins/NativeErrors/ReferenceError/is-error-object built-ins/NativeErrors/SyntaxError/is-error-object built-ins/NativeErrors/TypeError/is-error-object built-ins/NativeErrors/URIError/is-error-object -language/expressions/object/11.1.5_7-3-2 # Expected to fail but passed --- language/eval-code/direct/var-env-global-lex-non-strict diff --git a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp index a36409a35e..ce9d0ac894 100644 --- a/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp +++ b/tests/auto/qml/qqmlparser/tst_qqmlparser.cpp @@ -81,6 +81,13 @@ public: const quint32 parentBegin = parent->firstSourceLocation().begin(); const quint32 parentEnd = parent->lastSourceLocation().end(); + if (node->firstSourceLocation().begin() < parentBegin) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->firstSourceLocation().startLine << "/" << node->firstSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->firstSourceLocation().startLine << "/" << parent->firstSourceLocation().startColumn; + if (node->lastSourceLocation().end() > parentEnd) + qDebug() << "first source loc failed: node:" << node->kind << "at" << node->lastSourceLocation().startLine << "/" << node->lastSourceLocation().startColumn + << "parent" << parent->kind << "at" << parent->lastSourceLocation().startLine << "/" << parent->lastSourceLocation().startColumn; + QVERIFY(node->firstSourceLocation().begin() >= parentBegin); QVERIFY(node->lastSourceLocation().end() <= parentEnd); } |