diff --git a/es2panda/compiler/base/literals.cpp b/es2panda/compiler/base/literals.cpp index 0690bb6602f2737e4568ab5dc1f3b9fad29f758f..9775c55e5ea67f9d9fa093b137a00c935d7e99ca 100644 --- a/es2panda/compiler/base/literals.cpp +++ b/es2panda/compiler/base/literals.cpp @@ -48,8 +48,12 @@ void Literals::GetTemplateObject(PandaGen *pg, const ir::TaggedTemplateExpressio pg->LoadAccumulatorString(element, element->Raw()); pg->StoreObjByValue(element, rawArr, indexReg); - - pg->LoadAccumulatorString(element, element->Cooked()); + // Generate ldundefined when element is escape error + if (element->EscapeError()) { + pg->LoadConst(element, compiler::Constant::JS_UNDEFINED); + } else { + pg->LoadAccumulatorString(element, element->Cooked()); + } pg->StoreObjByValue(element, cookedArr, indexReg); elemIndex++; diff --git a/es2panda/ir/base/templateElement.h b/es2panda/ir/base/templateElement.h index f717c4f4e425f1812a369cf4185400095afd6770..1e52a65b8ee0c777f6ce27c034f7084ba66d3376 100644 --- a/es2panda/ir/base/templateElement.h +++ b/es2panda/ir/base/templateElement.h @@ -49,6 +49,16 @@ public: return cooked_; } + bool EscapeError() const + { + return escapeError_; + } + + void SetEscapeError(bool escapeError) + { + escapeError_ = escapeError; + } + void Iterate(const NodeTraverser &cb) const override; void Dump(ir::AstDumper *dumper) const override; void Compile([[maybe_unused]] compiler::PandaGen *pg) const override; @@ -58,6 +68,7 @@ public: protected: util::StringView raw_ {}; util::StringView cooked_ {}; + bool escapeError_ {false}; }; } // namespace panda::es2panda::ir diff --git a/es2panda/lexer/lexer.cpp b/es2panda/lexer/lexer.cpp index 224b5c9bc82cf4bddac3493ffccfdbb4205e34db..cb8668343086a8b23573ffd23edf571c846c6bd1 100644 --- a/es2panda/lexer/lexer.cpp +++ b/es2panda/lexer/lexer.cpp @@ -66,12 +66,20 @@ char32_t Lexer::ScanUnicodeCodePointEscape() constexpr auto multiplier = 16; code = code * multiplier + HexValue(cp); if (code > UNICODE_CODE_POINT_MAX) { + if (CheckTokenIsTaggedTemplate()) { + AssignTokenEscapeError(); + break; + } ThrowError("Invalid unicode escape sequence"); } } - if (cp != LEX_CHAR_RIGHT_BRACE) { - ThrowError("Invalid unicode escape sequence"); + if (CheckTokenIsTaggedTemplate()) { + AssignTokenEscapeError(); + return static_cast(code); + } else { + ThrowError("Invalid unicode escape sequence"); + } } Iterator().Forward(1); @@ -602,6 +610,10 @@ void Lexer::ScanStringUnicodePart(util::UString *str) } if (isOctal) { + if (CheckTokenIsTaggedTemplate()) { + AssignTokenEscapeError(); + break; + } ThrowError("Octal escape sequences are not allowed in strict mode"); } @@ -609,6 +621,10 @@ void Lexer::ScanStringUnicodePart(util::UString *str) } default: { if (IsDecimalDigit(Iterator().Peek())) { + if (CheckTokenIsTaggedTemplate()) { + AssignTokenEscapeError(); + break; + } ThrowError("Invalid character escape sequence in strict mode"); } @@ -1661,4 +1677,19 @@ void Lexer::NextToken(LexerNextTokenFlags flags) SkipWhiteSpaces(); } +void Lexer::AssignTokenEscapeError() +{ + GetToken().flags_ |= TokenFlags::ESCAPE_ERROR; +} + +void Lexer::AssignTokenTaggedTemplate() +{ + GetToken().flags_ |= TokenFlags::TAGGED_TEMPLATE; +} + +bool Lexer::CheckTokenIsTaggedTemplate() const +{ + return GetToken().IsTaggedTemplate(); +} + } // namespace panda::es2panda::lexer diff --git a/es2panda/lexer/lexer.h b/es2panda/lexer/lexer.h index 7ce0e506dc6650eb634fb2e99de31409aaaa0465..f8d531f931356468fd7f63aa0282e968b1e30118 100644 --- a/es2panda/lexer/lexer.h +++ b/es2panda/lexer/lexer.h @@ -91,6 +91,7 @@ public: LexerTemplateString ScanTemplateString(); void ScanTemplateStringEnd(); void PushTemplateContext(TemplateLiteralParserContext *ctx); + void AssignTokenTaggedTemplate(); private: ArenaAllocator *Allocator(); @@ -102,6 +103,7 @@ private: void SetTokenStart(); void SetTokenEnd(); + bool CheckTokenIsTaggedTemplate() const; inline util::StringView::Iterator &Iterator() { @@ -161,6 +163,7 @@ private: void ScanDecimalLiteral(); void ScanDecimalDigits(bool allowNumericSeparator); void CheckNumberLiteralEnd(); + void AssignTokenEscapeError(); inline static uint32_t HexValue(char32_t ch); inline static bool IsDecimalDigit(uint32_t cp); @@ -312,7 +315,12 @@ char32_t Lexer::ScanHexEscape() Iterator().Forward(1); if (!IsHexDigit(cp)) { - ThrowError("Invalid unicode escape sequence"); + // Should not throw error in tagged template in ES2021 + if (CheckTokenIsTaggedTemplate()) { + AssignTokenEscapeError(); + } else { + ThrowError("Invalid unicode escape sequence"); + } } constexpr auto MULTIPLIER = 16; diff --git a/es2panda/lexer/token/token.h b/es2panda/lexer/token/token.h index c31c3efe81b0d8ae8e66511378fde93d1cb6767a..7f7876414eec024fec82f9454dcb707eec36272f 100644 --- a/es2panda/lexer/token/token.h +++ b/es2panda/lexer/token/token.h @@ -30,6 +30,8 @@ enum class TokenFlags { HAS_ESCAPE = (1 << 2), NUMBER_BIGINT = (1 << 3), NUMBER_HAS_UNDERSCORE = (1 << 4), + ESCAPE_ERROR = (1 << 5), + TAGGED_TEMPLATE = (1 << 6), }; DEFINE_BITOPS(TokenFlags) @@ -106,6 +108,16 @@ public: return flags_ & TokenFlags::NEW_LINE; } + bool EscapeError() const + { + return flags_ & TokenFlags::ESCAPE_ERROR; + } + + bool IsTaggedTemplate() const + { + return flags_ & TokenFlags::TAGGED_TEMPLATE; + } + bool IsAccessability() const; bool IsAsyncModifier() const; bool IsStaticModifier() const; diff --git a/es2panda/parser/expressionParser.cpp b/es2panda/parser/expressionParser.cpp index 3c34c1b3f9e1698609bbbf443071b27e3e4d4931..b400ddfb1d037b44b366cfefbfc27445f5a5ab76 100644 --- a/es2panda/parser/expressionParser.cpp +++ b/es2panda/parser/expressionParser.cpp @@ -863,7 +863,7 @@ ir::Expression *ParserImpl::ParseAssignmentExpression(ir::Expression *lhsExpress return lhsExpression; } -ir::TemplateLiteral *ParserImpl::ParseTemplateLiteral() +ir::TemplateLiteral *ParserImpl::ParseTemplateLiteral(bool isTaggedTemplate) { lexer::SourcePosition startLoc = lexer_->GetToken().Start(); @@ -873,14 +873,18 @@ ir::TemplateLiteral *ParserImpl::ParseTemplateLiteral() while (true) { lexer_->ResetTokenEnd(); const auto startPos = lexer_->Save(); - + if (isTaggedTemplate) { + lexer_->AssignTokenTaggedTemplate(); + } lexer_->ScanString(); util::StringView cooked = lexer_->GetToken().String(); + bool escapeError = lexer_->GetToken().EscapeError(); lexer_->Rewind(startPos); auto [raw, end, scanExpression] = lexer_->ScanTemplateString(); auto *element = AllocNode(raw.View(), cooked); + element->SetEscapeError(escapeError); element->SetRange({lexer::SourcePosition {startPos.iterator.Index(), startPos.line}, lexer::SourcePosition {end, lexer_->Line()}}); quasis.push_back(element); @@ -1620,7 +1624,7 @@ bool ParserImpl::ParsePotentialTsGenericFunctionCall(ir::Expression **returnExpr } if (lexer_->GetToken().Type() == lexer::TokenType::PUNCTUATOR_BACK_TICK) { - ir::TemplateLiteral *propertyNode = ParseTemplateLiteral(); + ir::TemplateLiteral *propertyNode = ParseTemplateLiteral(true); lexer::SourcePosition endLoc = propertyNode->End(); *returnExpression = AllocNode(*returnExpression, propertyNode, typeParams); @@ -1718,7 +1722,7 @@ ir::Expression *ParserImpl::ParsePostPrimaryExpression(ir::Expression *primaryEx continue; } case lexer::TokenType::PUNCTUATOR_BACK_TICK: { - ir::TemplateLiteral *propertyNode = ParseTemplateLiteral(); + ir::TemplateLiteral *propertyNode = ParseTemplateLiteral(true); lexer::SourcePosition endLoc = propertyNode->End(); returnExpression = AllocNode(returnExpression, propertyNode, nullptr); diff --git a/es2panda/parser/parserImpl.h b/es2panda/parser/parserImpl.h index 2889a846b07e2706b461814d59f99de70be4c3e7..e5394f967d341a792ad8162ec9074fd1fa244b3f 100644 --- a/es2panda/parser/parserImpl.h +++ b/es2panda/parser/parserImpl.h @@ -431,7 +431,7 @@ private: bool isDeclare = false); ir::Expression *ParsePatternElement(ExpressionParseFlags flags = ExpressionParseFlags::NO_OPTS, bool allowDefault = true, bool isDeclare = false); - ir::TemplateLiteral *ParseTemplateLiteral(); + ir::TemplateLiteral *ParseTemplateLiteral(bool isTaggedTemplate = false); ir::Expression *ParseImportExpression(); ir::ObjectExpression *ParseImportAssertionForDynamicImport(); void ValidateImportAssertionForDynamicImport(ir::ObjectExpression *importAssertion); diff --git a/es2panda/test/compiler/js/test-tag-template-string-escape-error-expected.txt b/es2panda/test/compiler/js/test-tag-template-string-escape-error-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..b784bb76e32955d1aed071afea30aceafba82632 --- /dev/null +++ b/es2panda/test/compiler/js/test-tag-template-string-escape-error-expected.txt @@ -0,0 +1,9 @@ +ES2021undefined +ES2021undefined +ES2021undefined +ES2021undefined +ES2021 ჿ +ES2021undefined +ES2021 ዝ +ES2021undefined +ES2021 ª diff --git a/es2panda/test/compiler/js/test-tag-template-string-escape-error.js b/es2panda/test/compiler/js/test-tag-template-string-escape-error.js new file mode 100644 index 0000000000000000000000000000000000000000..e8d10e2a7f235a39389c40f6361edeeaa5fed852 --- /dev/null +++ b/es2panda/test/compiler/js/test-tag-template-string-escape-error.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function fn(str, substitute) { + return substitute + str[1]; +} +const str = 'ES2021'; +const result = fn`${str} \ubuntu`; +const result1 = fn`${str} \123`; +const result2 = fn`${str} \0123`; +const result3 = fn`${str} \u{11ffff}`; +const result31 = fn`${str} \u{10ff}`; +const result4 = fn`${str} \u{12dd`; +const result41 = fn`${str} \u12dd`; +const result5 = fn`${str} \xgg`; +const result6 = fn`${str} \xaa`; + +print(result); +print(result1); +print(result2); +print(result3); +print(result31); +print(result4); +print(result41); +print(result5); +print(result6); diff --git a/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error-expected.txt b/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..b784bb76e32955d1aed071afea30aceafba82632 --- /dev/null +++ b/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error-expected.txt @@ -0,0 +1,9 @@ +ES2021undefined +ES2021undefined +ES2021undefined +ES2021undefined +ES2021 ჿ +ES2021undefined +ES2021 ዝ +ES2021undefined +ES2021 ª diff --git a/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error.ts b/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error.ts new file mode 100644 index 0000000000000000000000000000000000000000..ed07b2e64ab8e8746177cbb64bd0692a1952f88d --- /dev/null +++ b/es2panda/test/compiler/ts/cases/compiler/test-ts-generic-tag-template-string-escape-error.ts @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +function fn(strs: TemplateStringsArray, ...args: T[]) : T { + return args[0] + args[1] + strs[2]; +} +const str = 'ES'; +const num = 2021; +const result = fn`${str}${num} \ubuntu`; +const result1 = fn`${str}${num} \123`; +const result2 = fn`${str}${num} \0123`; +const result3 = fn`${str}${num} \u{11ffff}`; +const result31 = fn`${str}${num} \u{10ff}`; +const result4 = fn`${str}${num} \u{12dd`; +const result41 = fn`${str}${num} \u12dd`; +const result5 = fn`${str}${num} \xgg`; +const result6 = fn`${str}${num} \xaa`; + +print(result); +print(result1); +print(result2); +print(result3); +print(result31); +print(result4); +print(result41); +print(result5); +print(result6); diff --git a/es2panda/test/parser/js/test_template_string-expected.txt b/es2panda/test/parser/js/test_template_string-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..5df45b6c5dc5f490e4f4ad4cf38b33557dd694dd --- /dev/null +++ b/es2panda/test/parser/js/test_template_string-expected.txt @@ -0,0 +1 @@ +SyntaxError: Invalid unicode escape sequence [test_template_string.js:16:18] diff --git a/es2panda/test/parser/js/test_template_string.js b/es2panda/test/parser/js/test_template_string.js new file mode 100644 index 0000000000000000000000000000000000000000..2dba6954e64c217e1905ccd2cdebff79b823bc21 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\ubuntu`; diff --git a/es2panda/test/parser/js/test_template_string1-expected.txt b/es2panda/test/parser/js/test_template_string1-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..c4eb2bdceb96207bec16f0b867974d3eddca02f4 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string1-expected.txt @@ -0,0 +1 @@ +SyntaxError: Invalid character escape sequence in strict mode [test_template_string1.js:16:15] diff --git a/es2panda/test/parser/js/test_template_string1.js b/es2panda/test/parser/js/test_template_string1.js new file mode 100644 index 0000000000000000000000000000000000000000..a07b0d0d2f3fdbd20f15121e5fd1bcc812d2fef0 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string1.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\123`; diff --git a/es2panda/test/parser/js/test_template_string2-expected.txt b/es2panda/test/parser/js/test_template_string2-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..129400dd1fb6b56876933d70b30c9e704baae6c3 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string2-expected.txt @@ -0,0 +1 @@ +SyntaxError: Octal escape sequences are not allowed in strict mode [test_template_string2.js:16:15] diff --git a/es2panda/test/parser/js/test_template_string2.js b/es2panda/test/parser/js/test_template_string2.js new file mode 100644 index 0000000000000000000000000000000000000000..152f94504b3d1f1eacc3d2d8ea05e8e73a3817c6 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string2.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\0123`; diff --git a/es2panda/test/parser/js/test_template_string3-expected.txt b/es2panda/test/parser/js/test_template_string3-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..1f57dcecef863a9fad4d19aec579dcbad59406a6 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string3-expected.txt @@ -0,0 +1 @@ +SyntaxError: Invalid unicode escape sequence [test_template_string3.js:16:23] diff --git a/es2panda/test/parser/js/test_template_string3.js b/es2panda/test/parser/js/test_template_string3.js new file mode 100644 index 0000000000000000000000000000000000000000..4f42f1443c0be268bac180b54ee9a669a6baca08 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string3.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\u{11ffff}`; diff --git a/es2panda/test/parser/js/test_template_string4-expected.txt b/es2panda/test/parser/js/test_template_string4-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..7db38dd569eaafc07364119d6110d73535247872 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string4-expected.txt @@ -0,0 +1 @@ +SyntaxError: Invalid unicode escape sequence [test_template_string4.js:16:21] diff --git a/es2panda/test/parser/js/test_template_string4.js b/es2panda/test/parser/js/test_template_string4.js new file mode 100644 index 0000000000000000000000000000000000000000..9d640c3d5672fed9f0f7bad82e5e739fd95c5702 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string4.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\u{12dd`; diff --git a/es2panda/test/parser/js/test_template_string5-expected.txt b/es2panda/test/parser/js/test_template_string5-expected.txt new file mode 100644 index 0000000000000000000000000000000000000000..53c9ba7442833fa6d547771df96dff8718518f67 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string5-expected.txt @@ -0,0 +1 @@ +SyntaxError: Invalid unicode escape sequence [test_template_string5.js:16:17] diff --git a/es2panda/test/parser/js/test_template_string5.js b/es2panda/test/parser/js/test_template_string5.js new file mode 100644 index 0000000000000000000000000000000000000000..899c9809a0c74678c620a7c4e2b34e4ee5114b57 --- /dev/null +++ b/es2panda/test/parser/js/test_template_string5.js @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +const str = `\xgg`;