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