* Copyright (c) 2021-2025 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 "arrowFunctionExpression.h"
#include "compiler/core/pandagen.h"
#include "compiler/core/ETSGen.h"
#include "checker/ETSchecker.h"
#include "checker/TSchecker.h"
namespace ark::es2panda::ir {
void ArrowFunctionExpression::SetFunction(ScriptFunction *func)
{
this->func_ = func;
if (func != nullptr) {
func->SetParent(this);
}
}
void ArrowFunctionExpression::TransformChildren(const NodeTransformer &cb, std::string_view const transformationName)
{
if (auto *transformedNode = cb(func_); func_ != transformedNode) {
func_->SetTransformedNode(transformationName, transformedNode);
func_ = transformedNode->AsScriptFunction();
}
TransformAnnotations(cb, transformationName);
}
void ArrowFunctionExpression::Iterate(const NodeTraverser &cb) const
{
cb(func_);
IterateAnnotations(cb);
}
void ArrowFunctionExpression::Dump(ir::AstDumper *dumper) const
{
dumper->Add({{"type", "ArrowFunctionExpression"},
{"function", func_},
{"annotations", AstDumper::Optional(Annotations())}});
}
void ArrowFunctionExpression::Dump(ir::SrcDumper *dumper) const
{
DumpAnnotations(dumper);
dumper->Add("(");
if (func_->IsScriptFunction() && func_->AsScriptFunction()->IsAsyncFunc()) {
dumper->Add("async ");
}
func_->Dump(dumper);
dumper->Add(")");
}
void ArrowFunctionExpression::Compile(compiler::PandaGen *pg) const
{
pg->GetAstCompiler()->Compile(this);
}
void ArrowFunctionExpression::Compile(compiler::ETSGen *etsg) const
{
etsg->GetAstCompiler()->Compile(this);
}
checker::Type *ArrowFunctionExpression::Check(checker::TSChecker *checker)
{
return checker->GetAnalyzer()->Check(this);
}
checker::VerifiedType ArrowFunctionExpression::Check(checker::ETSChecker *checker)
{
return {this, checker->GetAnalyzer()->Check(this)};
}
ArrowFunctionExpression::ArrowFunctionExpression(ArrowFunctionExpression const &other, ArenaAllocator *const allocator)
: AnnotationAllowed<Expression>(static_cast<Expression const &>(other), allocator)
{
ES2PANDA_ASSERT(other.func_->Clone(allocator, this));
func_ = other.func_->Clone(allocator, this)->AsScriptFunction();
}
ArrowFunctionExpression *ArrowFunctionExpression::Clone(ArenaAllocator *const allocator, AstNode *const parent)
{
auto *const clone = allocator->New<ArrowFunctionExpression>(*this, allocator);
ES2PANDA_ASSERT(clone);
if (parent != nullptr) {
clone->SetParent(parent);
}
if (HasAnnotations()) {
clone->SetAnnotations(Annotations());
}
clone->SetRange(Range());
return clone;
}
ir::TypeNode *ArrowFunctionExpression::CreateReturnNodeFromType(checker::ETSChecker *checker, checker::Type *returnType)
{
Construct a synthetic Node with the correct ts_type_.
*/
ES2PANDA_ASSERT(returnType != nullptr);
auto *ident = checker->AllocNode<ir::Identifier>(util::StringView(""), checker->Allocator());
auto *const part = checker->AllocNode<ir::ETSTypeReferencePart>(ident, checker->Allocator());
auto *returnNode = checker->AllocNode<ir::ETSTypeReference>(part, checker->Allocator());
ES2PANDA_ASSERT(returnNode);
returnNode->SetTsType(returnType);
return returnNode;
}
void ArrowFunctionExpression::CleanCheckInformation()
{
SetTsType(nullptr);
ir::ScriptFunction *const lambda = Function();
for (auto *p : lambda->Params()) {
if (!p->IsETSParameterExpression()) {
continue;
}
auto *const lambdaParam = p->AsETSParameterExpression()->Ident();
if (lambdaParam->TypeAnnotation() == nullptr) {
lambdaParam->Variable()->SetTsType(nullptr);
lambdaParam->SetTsType(nullptr);
}
}
if (lambda->ReturnTypeAnnotation() == nullptr) {
lambda->SetPreferredReturnType(nullptr);
}
lambda->SetSignature(nullptr);
if (!lambda->HasBody()) {
return;
}
lambda->Body()->IterateRecursivelyPostorder([](ir::AstNode *node) {
if (node->IsOpaqueTypeNode() || !node->IsTyped()) {
return;
}
node->RemoveAstNodeFlags(ir::AstNodeFlags::GENERATE_VALUE_OF);
node->AsTyped()->SetPreferredType(nullptr);
node->AsTyped()->SetTsType(nullptr);
if (node->IsIdentifier() && node->Parent()->IsCallExpression() &&
node->Parent()->AsCallExpression()->Callee() == node) {
node->AsTyped()->SetVariable(nullptr);
}
});
}
ir::TypeNode *ArrowFunctionExpression::CreateTypeAnnotation(checker::ETSChecker *checker)
{
ir::TypeNode *returnNode = nullptr;
There are two scenarios for lambda type inference: defined or undefined return type.
example code:
```
enum Color { Red, Blue}
// has Return Type Color
let x = () : Color => {return Color.Red}
// No Return Type Color
let y = () => {return Color.Red}
```
*/
if (Function()->ReturnTypeAnnotation() == nullptr) {
When lambda expression does not declare a return type, we need to construct the
declaration node of lambda according to the Function()->Signature()->ReturnType().
*/
returnNode = CreateReturnNodeFromType(checker, Function()->Signature()->ReturnType());
} else {
returnNode = Function()->ReturnTypeAnnotation()->Clone(checker->Allocator(), nullptr);
returnNode->SetTsType(Function()->ReturnTypeAnnotation()->TsType());
}
ArenaVector<ir::Expression *> params {checker->Allocator()->Adapter()};
checker->CopyParams(Function()->Params(), params, nullptr);
auto signature = ir::FunctionSignature(nullptr, std::move(params), returnNode);
auto *funcType = checker->AllocNode<ir::ETSFunctionType>(std::move(signature), ir::ScriptFunctionFlags::NONE,
checker->Allocator());
return funcType;
}
bool ArrowFunctionExpression::IsVarFromSubscope(const varbinder::Variable *var) const
{
return Function()->Scope()->IsSuperscopeOf(var->GetScope()) ||
Function()->Scope()->ParamScope()->IsSuperscopeOf(var->GetScope());
}
}