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 auto *func = checker->AllocNode<ir::ScriptFunction>(
101 checker->Allocator(), ir::ScriptFunction::ScriptFunctionData {
102 nullptr, std::move(signature), // CC-OFF(G.FMT.02) project code style
103 // CC-OFFNXT(G.FMT.02) project code style
104 isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER, flags});
105
106 func->SetRange(field->Range());
107
108 func->SetScope(functionScope);
109
110 auto const &name = field->Key()->AsIdentifier()->Name();
111 auto methodIdent = checker->AllocNode<ir::Identifier>(name, checker->Allocator());
112 auto *decl = checker->Allocator()->New<varbinder::VarDecl>(name);
113 auto var = functionScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS);
114
115 methodIdent->SetVariable(var);
116
117 auto *funcExpr = checker->AllocNode<ir::FunctionExpression>(func);
118 funcExpr->SetRange(func->Range());
119 func->AddFlag(ir::ScriptFunctionFlags::METHOD);
120
121 auto *method = checker->AllocNode<ir::MethodDefinition>(ir::MethodDefinitionKind::METHOD, methodIdent, funcExpr,
122 flags, checker->Allocator(), false);
123
124 method->Id()->SetMutator();
125 method->SetRange(field->Range());
126 method->Function()->SetIdent(method->Id()->Clone(checker->Allocator(), nullptr));
127 method->Function()->AddModifier(method->Modifiers());
128 paramScope->BindNode(func);
129 functionScope->BindNode(func);
130
131 return method;
132 }
133
UpdateInterfacePropertys(checker::ETSChecker * const checker,ir::TSInterfaceBody * const interface)134 static ir::Expression *UpdateInterfacePropertys(checker::ETSChecker *const checker,
135 ir::TSInterfaceBody *const interface)
136 {
137 if (interface->Body().empty()) {
138 return interface;
139 }
140
141 auto propertyList = interface->Body();
142 ArenaVector<ir::AstNode *> newPropertyList(checker->Allocator()->Adapter());
143
144 auto scope = NearestScope(interface);
145 ASSERT(scope->IsClassScope());
146
147 for (const auto &prop : propertyList) {
148 if (!prop->IsClassProperty()) {
149 newPropertyList.emplace_back(prop);
150 continue;
151 }
152 auto getter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), false);
153 newPropertyList.emplace_back(getter);
154
155 auto methodScope = scope->AsClassScope()->InstanceMethodScope();
156 auto name = getter->Key()->AsIdentifier()->Name();
157
158 auto *decl = checker->Allocator()->New<varbinder::FunctionDecl>(checker->Allocator(), name, getter);
159
160 if (methodScope->AddDecl(checker->Allocator(), decl, ScriptExtension::ETS) == nullptr) {
161 auto prevDecl = methodScope->FindDecl(name);
162 ASSERT(prevDecl->IsFunctionDecl());
163 prevDecl->Node()->AsMethodDefinition()->AddOverload(getter);
164
165 if (!prop->AsClassProperty()->IsReadonly()) {
166 auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
167 newPropertyList.emplace_back(setter);
168 prevDecl->Node()->AsMethodDefinition()->AddOverload(setter);
169 }
170
171 getter->Function()->Id()->SetVariable(
172 methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS));
173 continue;
174 }
175
176 if (!prop->AsClassProperty()->IsReadonly()) {
177 auto setter = GenerateGetterOrSetter(checker, prop->AsClassProperty(), true);
178 newPropertyList.emplace_back(setter);
179 getter->AddOverload(setter);
180 }
181 scope->AsClassScope()->InstanceFieldScope()->EraseBinding(name);
182 }
183
184 auto newInterface = checker->AllocNode<ir::TSInterfaceBody>(std::move(newPropertyList));
185 newInterface->SetRange(interface->Range());
186 newInterface->SetParent(interface->Parent());
187
188 return newInterface;
189 }
190
Perform(public_lib::Context * ctx,parser::Program * program)191 bool InterfacePropertyDeclarationsPhase::Perform(public_lib::Context *ctx, parser::Program *program)
192 {
193 for (const auto &[_, ext_programs] : program->ExternalSources()) {
194 (void)_;
195 for (auto *const extProg : ext_programs) {
196 Perform(ctx, extProg);
197 }
198 }
199
200 checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
201
202 program->Ast()->TransformChildrenRecursively(
203 // CC-OFFNXT(G.FMT.14-CPP) project code style
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