1 /**
2  * Copyright (c) 2023-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "interfacePropertyDeclarations.h"
17 
18 #include "checker/ETSchecker.h"
19 #include "checker/types/type.h"
20 #include "compiler/lowering/util.h"
21 #include "ir/astNode.h"
22 #include "ir/expression.h"
23 #include "ir/expressions/identifier.h"
24 #include "ir/opaqueTypeNode.h"
25 #include "ir/statements/blockStatement.h"
26 #include "ir/ts/tsInterfaceBody.h"
27 #include "ir/base/classProperty.h"
28 #include "ir/ets/etsUnionType.h"
29 #include "ir/ets/etsNullishTypes.h"
30 
31 namespace ark::es2panda::compiler {
32 
33 namespace {
34 
TransformOptionalFieldTypeAnnotation(checker::ETSChecker *const checker, ir::ClassProperty *const field)35 void TransformOptionalFieldTypeAnnotation(checker::ETSChecker *const checker, ir::ClassProperty *const field)
36 {
37     if (!field->IsOptionalDeclaration()) {
38         return;
39     }
40 
41     if (field->IsETSUnionType()) {
42         bool alreadyHasUndefined = false;
43         auto unionTypes = field->AsETSUnionType()->Types();
44         for (const auto &type : unionTypes) {
45             if (type->IsETSUndefinedType()) {
46                 alreadyHasUndefined = true;
47                 break;
48             }
49         }
50         if (!alreadyHasUndefined) {
51             ArenaVector<ir::TypeNode *> types(field->AsETSUnionType()->Types(), checker->Allocator()->Adapter());
52             types.push_back(checker->AllocNode<ir::ETSUndefinedType>());
53             auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types));
54             field->SetTypeAnnotation(unionType);
55         }
56     } else {
57         ArenaVector<ir::TypeNode *> types(checker->Allocator()->Adapter());
58         types.push_back(field->TypeAnnotation());
59         types.push_back(checker->AllocNode<ir::ETSUndefinedType>());
60         auto *const unionType = checker->AllocNode<ir::ETSUnionType>(std::move(types));
61         field->SetTypeAnnotation(unionType);
62     }
63     field->ClearModifier(ir::ModifierFlags::OPTIONAL);
64 }
65 
66 }  // namespace
67 
GenerateGetterOrSetter(checker::ETSChecker *const checker, ir::ClassProperty *const field, bool isSetter)68 static ir::MethodDefinition *GenerateGetterOrSetter(checker::ETSChecker *const checker, ir::ClassProperty *const field,
69                                                     bool isSetter)
70 {
71     auto classScope = NearestScope(field);
72     auto *paramScope = checker->Allocator()->New<varbinder::FunctionParamScope>(checker->Allocator(), classScope);
73     auto *functionScope = checker->Allocator()->New<varbinder::FunctionScope>(checker->Allocator(), paramScope);
74 
75     functionScope->BindParamScope(paramScope);
76     paramScope->BindFunctionScope(functionScope);
77 
78     auto flags = ir::ModifierFlags::PUBLIC;
79     flags |= ir::ModifierFlags::ABSTRACT;
80 
81     TransformOptionalFieldTypeAnnotation(checker, field);
82     ArenaVector<ir::Expression *> params(checker->Allocator()->Adapter());
83 
84     if (isSetter) {
85         auto paramIdent = field->Key()->AsIdentifier()->Clone(checker->Allocator(), nullptr);
86         paramIdent->SetTsTypeAnnotation(field->TypeAnnotation()->Clone(checker->Allocator(), nullptr));
87         paramIdent->TypeAnnotation()->SetParent(paramIdent);
88 
89         auto *const paramExpression = checker->AllocNode<ir::ETSParameterExpression>(paramIdent, nullptr);
90         paramExpression->SetRange(paramIdent->Range());
91         auto *const paramVar = std::get<2>(paramScope->AddParamDecl(checker->Allocator(), paramExpression));
92 
93         paramIdent->SetVariable(paramVar);
94         paramExpression->SetVariable(paramVar);
95 
96         params.push_back(paramExpression);
97     }
98 
99     auto signature = ir::FunctionSignature(nullptr, std::move(params), isSetter ? nullptr : field->TypeAnnotation());
100 
101     auto *func = checker->AllocNode<ir::ScriptFunction>(
102         checker->Allocator(), ir::ScriptFunction::ScriptFunctionData {nullptr, std::move(signature),
103                                                                       isSetter ? ir::ScriptFunctionFlags::SETTER
104                                                                                : ir::ScriptFunctionFlags::GETTER,
105                                                                       flags, true});
106 
107     func->SetRange(field->Range());
108 
109     func->SetScope(functionScope);
110 
111     auto const &name = field->Key()->AsIdentifier()->Name();
112     auto methodIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
113     auto *decl = checker->Allocator()->New<varbinder::VarDecl>(name);
114     auto var = functionScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS);
115 
116     methodIdent->SetVariable(var);
117 
118     auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func);
119     funcExpr->SetRange(func->Range());
120     func->AddFlag(ir::ScriptFunctionFlags::METHOD);
121 
122     auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr,
123                                                             flags, checker->Allocator(), false);
124 
125     method->Id()->SetMutator();
126     method->SetRange(field->Range());
127     method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr));
128     method->Function()->AddModifier(method->Modifiers());
129     paramScope->BindNode(func);
130     functionScope->BindNode(func);
131 
132     return method;
133 }
134 
UpdateInterfacePropertys(checker::ETSChecker *const checker, ir::TSInterfaceBody *const interface)135 static ir::Expression *UpdateInterfacePropertys(checker::ETSChecker *const checker,
136                                                 ir::TSInterfaceBody *const interface)
137 {
138     if (interface->Body().empty()) {
139         return interface;
140     }
141 
142     auto propertyList = interface->Body();
143     ArenaVector<ir::AstNode *> newPropertyList(checker->Allocator()->Adapter());
144 
145     auto scope = NearestScope(interface);
146     ASSERT(scope->IsClassScope());
147 
148     for (const auto &prop : propertyList) {
149         if (!prop->IsClassProperty()) {
150             newPropertyList.emplace_back(prop);
151             continue;
152         }
153         auto getter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), false);
154         newPropertyList.emplace_back(getter);
155 
156         auto methodScope = scope->AsClassScope()->InstanceMethodScope();
157         auto name = getter->Key()->AsIdentifier()->Name();
158 
159         auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(), name, getter);
160 
161         if (methodScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS) == nullptr) {
162             auto prevDecl = methodScope->FindDecl(name);
163             ASSERT(prevDecl->IsFunctionDecl());
164             prevDecl->Node()->AsMethodDefinition()->AddOverload(getter);
165 
166             if (!prop->AsClassProperty()->IsReadonly()) {
167                 auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
168                 newPropertyList.emplace_back(setter);
169                 prevDecl->Node()->AsMethodDefinition()->AddOverload(setter);
170             }
171 
172             getter->Function()->Id()->SetVariable(
173                 methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS));
174             continue;
175         }
176 
177         if (!prop->AsClassProperty()->IsReadonly()) {
178             auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
179             newPropertyList.emplace_back(setter);
180             getter->AddOverload(setter);
181         }
182         scope->AsClassScope()->InstanceFieldScope()->EraseBinding(name);
183     }
184 
185     auto newInterface = checker->AllocNode<ir::TSInterfaceBody>(std::move(newPropertyList));
186     newInterface->SetRange(interface->Range());
187     newInterface->SetParent(interface->Parent());
188 
189     return newInterface;
190 }
191 
Perform(public_lib::Context *ctx, parser::Program *program)192 bool InterfacePropertyDeclarationsPhase::Perform(public_lib::Context *ctx, parser::Program *program)
193 {
194     for (const auto &[_, ext_programs] : program->ExternalSources()) {
195         (void)_;
196         for (auto *const extProg : ext_programs) {
197             Perform(ctx, extProg);
198         }
199     }
200 
201     checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
202 
203     program->Ast()->TransformChildrenRecursively(
204         [checker](ir::AstNode *const ast) -> ir::AstNode * {
205             return ast->IsTSInterfaceBody() ? UpdateInterfacePropertys(checker, ast->AsTSInterfaceBody()) : ast;
206         },
207         Name());
208 
209     return true;
210 }
211 
212 }  // namespace ark::es2panda::compiler
213