diff --git a/es2panda/BUILD.gn b/es2panda/BUILD.gn index 847457188e5dd7c19018a2b5d32d44417a9f41ca..cf0198dc70566303755f7a15ea2d9181c7aff8ff 100644 --- a/es2panda/BUILD.gn +++ b/es2panda/BUILD.gn @@ -29,6 +29,7 @@ es2panda_src = [ "compiler/base/lexenv.cpp", "compiler/base/literals.cpp", "compiler/base/lreference.cpp", + "compiler/base/optionalChain.cpp", "compiler/core/compileQueue.cpp", "compiler/core/compilerContext.cpp", "compiler/core/compilerImpl.cpp", diff --git a/es2panda/compiler/base/optionalChain.cpp b/es2panda/compiler/base/optionalChain.cpp new file mode 100644 index 0000000000000000000000000000000000000000..af6ec45f5f9be9bdf34f62146e32992b0bacc9fe --- /dev/null +++ b/es2panda/compiler/base/optionalChain.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2022 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. + */ + +#include "optionalChain.h" + +#include + +namespace panda::es2panda::compiler { +OptionalChain::OptionalChain(PandaGen *pg, const ir::AstNode *node) : pg_(pg), node_(node), prev_(pg->optionalChain_) +{ + pg_->optionalChain_ = this; +} + +OptionalChain::~OptionalChain() +{ + if (label_) { + pg_->SetLabel(node_, label_); + } + pg_->optionalChain_ = prev_; +} + +void OptionalChain::CheckNullish(bool optional, compiler::VReg obj) +{ + if (!optional) { + return; + } + + if (!label_) { + label_ = pg_->AllocLabel(); + } + + RegScope rs(pg_); + + auto *notNullish = pg_->AllocLabel(); + auto *nullish = pg_->AllocLabel(); + + pg_->LoadConst(node_, Constant::JS_NULL); + pg_->Condition(node_, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, obj, nullish); + pg_->LoadConst(node_, Constant::JS_UNDEFINED); + pg_->Condition(node_, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, obj, nullish); + pg_->Branch(node_, notNullish); + pg_->SetLabel(node_, nullish); + + pg_->LoadConst(node_, compiler::Constant::JS_UNDEFINED); + pg_->Branch(node_, label_); + pg_->SetLabel(node_, notNullish); + pg_->LoadAccumulator(node_, obj); +} + +} // namespace panda::es2panda::compiler diff --git a/es2panda/compiler/base/optionalChain.h b/es2panda/compiler/base/optionalChain.h new file mode 100644 index 0000000000000000000000000000000000000000..c6005d03b2c892dc728fa025f161f6604687c510 --- /dev/null +++ b/es2panda/compiler/base/optionalChain.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2022 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. + */ + +#ifndef ES2PANDA_COMPILER_BASE_OPTIONAL_CHAIN_H +#define ES2PANDA_COMPILER_BASE_OPTIONAL_CHAIN_H + +#include +#include + +namespace panda::es2panda::compiler { +class PandaGen; +class Label; + +class OptionalChain { +public: + explicit OptionalChain(PandaGen *pg, const ir::AstNode *node); + ~OptionalChain(); + + void CheckNullish(bool optional, compiler::VReg obj); + + NO_COPY_SEMANTIC(OptionalChain); + NO_MOVE_SEMANTIC(OptionalChain); + +private: + PandaGen *pg_ {}; + const ir::AstNode *node_ {}; + Label *label_ {}; + OptionalChain *prev_ {}; +}; +} // namespace panda::es2panda::compiler + +#endif diff --git a/es2panda/compiler/core/pandagen.h b/es2panda/compiler/core/pandagen.h index 64fe2d6132bd853e72f05f55102588d262f135a5..79b68804e49f5f44ea067eac988f054c00a4bdeb 100644 --- a/es2panda/compiler/core/pandagen.h +++ b/es2panda/compiler/core/pandagen.h @@ -16,6 +16,7 @@ #ifndef ES2PANDA_COMPILER_CORE_PANDAGEN_H #define ES2PANDA_COMPILER_CORE_PANDAGEN_H +#include #include #include #include @@ -174,6 +175,11 @@ public: return buffStorage_; } + OptionalChain *GetOptionalChain() const + { + return optionalChain_; + } + uint32_t IcSize() const { return ic_.Size(); @@ -430,6 +436,7 @@ private: ArenaVector buffStorage_; EnvScope *envScope_ {}; DynamicContext *dynamicContext_ {}; + OptionalChain *optionalChain_ {}; InlineCache ic_; SimpleAllocator sa_; RegAllocator ra_; @@ -446,6 +453,7 @@ private: friend class EnvScope; friend class LoopEnvScope; friend class DynamicContext; + friend class OptionalChain; size_t labelId_ {0}; }; } // namespace panda::es2panda::compiler diff --git a/es2panda/ir/expressions/callExpression.cpp b/es2panda/ir/expressions/callExpression.cpp index dc09961b5d94c2e2c318738f2fccf1956035c239..c829b559a4f1a18ceb710cdb21645eaeb0aebf78 100644 --- a/es2panda/ir/expressions/callExpression.cpp +++ b/es2panda/ir/expressions/callExpression.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -119,11 +120,15 @@ void CallExpression::Compile(compiler::PandaGen *pg) const compiler::RegScope mrs(pg); callee_->AsMemberExpression()->Compile(pg, thisReg); + } else if (callee_->IsChainExpression()) { + hasThis = true; + callee_->AsChainExpression()->Compile(pg); } else { callee_->Compile(pg); } pg->StoreAccumulator(this, callee); + pg->GetOptionalChain()->CheckNullish(optional_, callee); if (containsSpread) { if (!hasThis) { diff --git a/es2panda/ir/expressions/chainExpression.cpp b/es2panda/ir/expressions/chainExpression.cpp index 3bf58ae577860f249ab15dbc6165e49193063434..495cb42bea1b760001b180d33fc86a21269b13e4 100644 --- a/es2panda/ir/expressions/chainExpression.cpp +++ b/es2panda/ir/expressions/chainExpression.cpp @@ -14,11 +14,12 @@ */ #include "chainExpression.h" -#include +#include +#include #include -#include #include +#include namespace panda::es2panda::ir { @@ -34,35 +35,14 @@ void ChainExpression::Dump(ir::AstDumper *dumper) const void ChainExpression::Compile(compiler::PandaGen *pg) const { - // TODO: support continuous optional chain expression - compiler::RegScope rs(pg); - const MemberExpression *memberExpr = nullptr; - if (this->GetExpression()->IsMemberExpression()) { - memberExpr = this->GetExpression()->AsMemberExpression(); + compiler::OptionalChain chain(pg, this); + + if (expression_->IsMemberExpression()) { + expression_->AsMemberExpression()->Compile(pg); } else { - auto callExpr = this->GetExpression()->AsCallExpression(); - memberExpr = callExpr->Callee()->AsMemberExpression(); + assert(expression_->IsCallExpression()); + expression_->AsCallExpression()->Compile(pg); } - - compiler::VReg objReg = pg->AllocReg(); - auto *isNullOrUndefinedLabel = pg->AllocLabel(); - auto *endLabel = pg->AllocLabel(); - - memberExpr->CompileObject(pg, objReg); - pg->LoadConst(this, compiler::Constant::JS_NULL); - pg->Condition(this, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, objReg, isNullOrUndefinedLabel); - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - pg->Condition(this, lexer::TokenType::PUNCTUATOR_NOT_STRICT_EQUAL, objReg, isNullOrUndefinedLabel); - - // obj (ahead ?.) is not null/undefined, continue to compile sub-expression) - this->GetExpression()->Compile(pg); - pg->Branch(this, endLabel); - - // obj (ahead ?.) is null/undefined, return undefined) - pg->SetLabel(this, isNullOrUndefinedLabel); - pg->LoadConst(this, compiler::Constant::JS_UNDEFINED); - - pg->SetLabel(this, endLabel); } checker::Type *ChainExpression::Check(checker::Checker *checker) const diff --git a/es2panda/ir/expressions/memberExpression.cpp b/es2panda/ir/expressions/memberExpression.cpp index db2fcb5136bd6d0c455f5e91ce265ca27af77b7e..6754ecf67251dc7ccbc6e41988889c9755d7075b 100644 --- a/es2panda/ir/expressions/memberExpression.cpp +++ b/es2panda/ir/expressions/memberExpression.cpp @@ -43,6 +43,7 @@ void MemberExpression::CompileObject(compiler::PandaGen *pg, compiler::VReg dest { object_->Compile(pg); pg->StoreAccumulator(this, dest); + pg->GetOptionalChain()->CheckNullish(optional_, dest); } compiler::Operand MemberExpression::CompileKey(compiler::PandaGen *pg) const @@ -54,14 +55,7 @@ void MemberExpression::Compile(compiler::PandaGen *pg) const { compiler::RegScope rs(pg); compiler::VReg objReg = pg->AllocReg(); - CompileObject(pg, objReg); - compiler::Operand prop = CompileKey(pg); - - if (object_->IsSuperExpression()) { - pg->LoadSuperProperty(this, objReg, prop); - } else { - pg->LoadObjProperty(this, objReg, prop); - } + Compile(pg, objReg); } void MemberExpression::Compile(compiler::PandaGen *pg, compiler::VReg objReg) const diff --git a/test262/es2021_tests.txt b/test262/es2021_tests.txt index a0ea2db1eccd48823be3f8db676231e2ee1b2f70..f4e31babb9809aa3221801c566bcd06772cfc2bc 100644 --- a/test262/es2021_tests.txt +++ b/test262/es2021_tests.txt @@ -1,3 +1,43 @@ +built-ins/AsyncGeneratorFunction/name.js +built-ins/AsyncGeneratorFunction/length.js +built-ins/AsyncGeneratorFunction/prototype/constructor.js +built-ins/AsyncGeneratorFunction/prototype/prop-desc.js +built-ins/AsyncGeneratorFunction/prototype/Symbol.toStringTag.js +built-ins/AsyncGeneratorFunction/prototype/not-callable.js +built-ins/AsyncGeneratorFunction/prototype/prototype.js +built-ins/AsyncGeneratorFunction/prototype/extensibility.js +built-ins/AsyncGeneratorFunction/extensibility.js +built-ins/AsyncGeneratorPrototype/Symbol.toStringTag.js +built-ins/AsyncGeneratorPrototype/throw/return-rejected-promise.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart-promise.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-return.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-throw.js +built-ins/AsyncGeneratorPrototype/throw/prop-desc.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-promise.js +built-ins/AsyncGeneratorPrototype/throw/throw-state-completed.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-catch.js +built-ins/AsyncGeneratorPrototype/throw/name.js +built-ins/AsyncGeneratorPrototype/throw/length.js +built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield.js +built-ins/AsyncGeneratorPrototype/next/request-queue-promise-resolve-order.js +built-ins/AsyncGeneratorPrototype/next/request-queue-order.js +built-ins/AsyncGeneratorPrototype/next/prop-desc.js +built-ins/AsyncGeneratorPrototype/next/iterator-result-prototype.js +built-ins/AsyncGeneratorPrototype/next/name.js +built-ins/AsyncGeneratorPrototype/next/length.js +built-ins/AsyncGeneratorPrototype/next/request-queue-await-order.js +built-ins/AsyncGeneratorPrototype/next/return-promise.js +built-ins/AsyncGeneratorPrototype/return/return-suspendedStart.js +built-ins/AsyncGeneratorPrototype/return/prop-desc.js +built-ins/AsyncGeneratorPrototype/return/return-suspendedYield.js +built-ins/AsyncGeneratorPrototype/return/iterator-result-prototype.js +built-ins/AsyncGeneratorPrototype/return/name.js +built-ins/AsyncGeneratorPrototype/return/return-state-completed.js +built-ins/AsyncGeneratorPrototype/return/length.js +built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-promise.js +built-ins/AsyncGeneratorPrototype/return/return-promise.js +built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-promise.js built-ins/String/prototype/matchAll/regexp-get-matchAll-throws.js built-ins/String/prototype/matchAll/not-a-constructor.js built-ins/String/prototype/matchAll/flags-undefined-throws.js @@ -656,43 +696,41 @@ language/expressions/dynamic-import/for-await-resolution-and-error-a_FIXTURE.js language/expressions/dynamic-import/module-code_FIXTURE.js language/expressions/dynamic-import/update-to-dynamic-import-other_FIXTURE.js language/expressions/dynamic-import/syntax/valid/empty_FIXTURE.js -built-ins/AsyncGeneratorFunction/name.js -built-ins/AsyncGeneratorFunction/length.js -built-ins/AsyncGeneratorFunction/prototype/constructor.js -built-ins/AsyncGeneratorFunction/prototype/prop-desc.js -built-ins/AsyncGeneratorFunction/prototype/Symbol.toStringTag.js -built-ins/AsyncGeneratorFunction/prototype/not-callable.js -built-ins/AsyncGeneratorFunction/prototype/prototype.js -built-ins/AsyncGeneratorFunction/prototype/extensibility.js -built-ins/AsyncGeneratorFunction/extensibility.js -built-ins/AsyncGeneratorPrototype/Symbol.toStringTag.js -built-ins/AsyncGeneratorPrototype/throw/return-rejected-promise.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart-promise.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-return.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-finally-throw.js -built-ins/AsyncGeneratorPrototype/throw/prop-desc.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-promise.js -built-ins/AsyncGeneratorPrototype/throw/throw-state-completed.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedStart.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield-try-catch.js -built-ins/AsyncGeneratorPrototype/throw/name.js -built-ins/AsyncGeneratorPrototype/throw/length.js -built-ins/AsyncGeneratorPrototype/throw/throw-suspendedYield.js -built-ins/AsyncGeneratorPrototype/next/request-queue-promise-resolve-order.js -built-ins/AsyncGeneratorPrototype/next/request-queue-order.js -built-ins/AsyncGeneratorPrototype/next/prop-desc.js -built-ins/AsyncGeneratorPrototype/next/iterator-result-prototype.js -built-ins/AsyncGeneratorPrototype/next/name.js -built-ins/AsyncGeneratorPrototype/next/length.js -built-ins/AsyncGeneratorPrototype/next/request-queue-await-order.js -built-ins/AsyncGeneratorPrototype/next/return-promise.js -built-ins/AsyncGeneratorPrototype/return/return-suspendedStart.js -built-ins/AsyncGeneratorPrototype/return/prop-desc.js -built-ins/AsyncGeneratorPrototype/return/return-suspendedYield.js -built-ins/AsyncGeneratorPrototype/return/iterator-result-prototype.js -built-ins/AsyncGeneratorPrototype/return/name.js -built-ins/AsyncGeneratorPrototype/return/return-state-completed.js -built-ins/AsyncGeneratorPrototype/return/length.js -built-ins/AsyncGeneratorPrototype/return/return-suspendedStart-promise.js -built-ins/AsyncGeneratorPrototype/return/return-promise.js -built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-promise.js +language/expressions/optional-chaining/call-expression.js +language/expressions/optional-chaining/call-expression-super-no-base.js +language/expressions/optional-chaining/early-errors-tail-position-null-optchain-template-string-esi.js +language/expressions/optional-chaining/early-errors-tail-position-null-optchain-template-string.js +language/expressions/optional-chaining/early-errors-tail-position-null-op-template-string-esi.js +language/expressions/optional-chaining/early-errors-tail-position-null-op-template-string.js +language/expressions/optional-chaining/early-errors-tail-position-optchain-template-string-esi.js +language/expressions/optional-chaining/early-errors-tail-position-optchain-template-string.js +language/expressions/optional-chaining/early-errors-tail-position-op-template-string-esi.js +language/expressions/optional-chaining/early-errors-tail-position-op-template-string.js +language/expressions/optional-chaining/eval-optional-call.js +language/expressions/optional-chaining/iteration-statement-do.js +language/expressions/optional-chaining/iteration-statement-for-await-of.js +language/expressions/optional-chaining/iteration-statement-for-in.js +language/expressions/optional-chaining/iteration-statement-for.js +language/expressions/optional-chaining/iteration-statement-for-of-type-error.js +language/expressions/optional-chaining/iteration-statement-while.js +language/expressions/optional-chaining/member-expression-async-identifier.js +language/expressions/optional-chaining/member-expression-async-literal.js +language/expressions/optional-chaining/member-expression-async-this.js +language/expressions/optional-chaining/member-expression.js +language/expressions/optional-chaining/new-target-optional-call.js +language/expressions/optional-chaining/optional-call-preserves-this.js +language/expressions/optional-chaining/optional-chain-async-optional-chain-square-brackets.js +language/expressions/optional-chaining/optional-chain-async-square-brackets.js +language/expressions/optional-chaining/optional-chain-expression-optional-expression.js +language/expressions/optional-chaining/optional-chain.js +language/expressions/optional-chaining/optional-chain-prod-arguments.js +language/expressions/optional-chaining/optional-chain-prod-expression.js +language/expressions/optional-chaining/optional-chain-prod-identifiername.js +language/expressions/optional-chaining/optional-expression.js +language/expressions/optional-chaining/punctuator-decimal-lookahead.js +language/expressions/optional-chaining/runtime-semantics-evaluation.js +language/expressions/optional-chaining/short-circuiting.js +language/expressions/optional-chaining/static-semantics-simple-assignment.js +language/expressions/optional-chaining/super-property-optional-call.js +language/expressions/optional-chaining/update-expression-postfix.js +language/expressions/optional-chaining/update-expression-prefix.js diff --git a/test262/skip_tests.json b/test262/skip_tests.json index 62270aa0aa3e97c0f23262c76f2787f2f56e3425..eb57ff0fb8c1810ee59925870cb1bdd5ff8a1412 100644 --- a/test262/skip_tests.json +++ b/test262/skip_tests.json @@ -676,7 +676,8 @@ "built-ins/Function/internals/Construct/derived-return-val-realm.js", "built-ins/Function/internals/Construct/base-ctor-revoked-proxy-realm.js", "built-ins/Function/internals/Call/class-ctor-realm.js", - "built-ins/Proxy/get-fn-realm-recursive.js" + "built-ins/Proxy/get-fn-realm-recursive.js", + "language/expressions/optional-chaining/eval-optional-call.js" ] }, { @@ -1625,7 +1626,9 @@ "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally-return.js", "built-ins/AsyncGeneratorPrototype/return/request-queue-order-state-executing.js", "built-ins/AsyncGeneratorPrototype/return/this-val-not-object.js", - "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally.js" + "built-ins/AsyncGeneratorPrototype/return/return-suspendedYield-try-finally.js", + "language/expressions/optional-chaining/member-expression.js", + "language/expressions/optional-chaining/iteration-statement-for-await-of.js" ] } -] \ No newline at end of file +]