• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2023-2025 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/scopesInit/scopesInitPhase.h"
21 #include "compiler/lowering/util.h"
22 #include "ir/astNode.h"
23 #include "ir/expression.h"
24 #include "ir/expressions/identifier.h"
25 #include "ir/opaqueTypeNode.h"
26 #include "ir/statements/blockStatement.h"
27 #include "ir/ts/tsInterfaceBody.h"
28 #include "ir/base/classProperty.h"
29 #include "ir/ets/etsUnionType.h"
30 #include "ir/ets/etsNullishTypes.h"
31 
32 namespace ark::es2panda::compiler {
33 
TransformOptionalFieldTypeAnnotation(public_lib::Context * ctx,ir::ClassProperty * const field,bool isInterface)34 void InterfacePropertyDeclarationsPhase::TransformOptionalFieldTypeAnnotation(public_lib::Context *ctx,
35                                                                               ir::ClassProperty *const field,
36                                                                               bool isInterface)
37 {
38     if (!field->IsOptionalDeclaration()) {
39         return;
40     }
41 
42     if (field->IsETSUnionType()) {
43         bool alreadyHasUndefined = false;
44         auto unionTypes = field->AsETSUnionType()->Types();
45         for (const auto &type : unionTypes) {
46             if (type->IsETSUndefinedType()) {
47                 alreadyHasUndefined = true;
48                 break;
49             }
50         }
51         if (!alreadyHasUndefined) {
52             ArenaVector<ir::TypeNode *> types(field->AsETSUnionType()->Types(), ctx->Allocator()->Adapter());
53             types.push_back(ctx->AllocNode<ir::ETSUndefinedType>(ctx->Allocator()));
54             auto *const unionType = ctx->AllocNode<ir::ETSUnionType>(std::move(types), ctx->Allocator());
55             field->SetTypeAnnotation(unionType);
56         }
57     } else {
58         ArenaVector<ir::TypeNode *> types(ctx->Allocator()->Adapter());
59         types.push_back(field->TypeAnnotation());
60         types.push_back(ctx->AllocNode<ir::ETSUndefinedType>(ctx->Allocator()));
61         auto *const unionType = ctx->AllocNode<ir::ETSUnionType>(std::move(types), ctx->Allocator());
62         field->SetTypeAnnotation(unionType);
63     }
64     field->ClearModifier(ir::ModifierFlags::OPTIONAL);
65 
66     if (isInterface) {
67         GetPropCollector().InsertInterfaceProperty(field->Key()->ToString());
68     }
69 }
70 
GenerateGetterOrSetterSignature(public_lib::Context * ctx,varbinder::ETSBinder * varbinder,ir::ClassProperty * const field,bool isSetter,varbinder::FunctionParamScope * paramScope)71 ir::FunctionSignature InterfacePropertyDeclarationsPhase::GenerateGetterOrSetterSignature(
72     public_lib::Context *ctx, varbinder::ETSBinder *varbinder, ir::ClassProperty *const field, bool isSetter,
73     varbinder::FunctionParamScope *paramScope)
74 {
75     TransformOptionalFieldTypeAnnotation(ctx, field, true);
76     ArenaVector<ir::Expression *> params(ctx->Allocator()->Adapter());
77 
78     if (isSetter) {
79         auto paramIdent = field->Key()->AsIdentifier()->Clone(ctx->Allocator(), nullptr);
80         paramIdent->SetTsTypeAnnotation(field->TypeAnnotation()->Clone(ctx->Allocator(), nullptr));
81         paramIdent->TypeAnnotation()->SetParent(paramIdent);
82 
83         ClearTypesVariablesAndScopes(paramIdent);
84         auto classCtx = varbinder::LexicalScope<varbinder::Scope>::Enter(varbinder, paramScope);
85         InitScopesPhaseETS::RunExternalNode(paramIdent, varbinder);
86 
87         auto *const paramExpression = ctx->AllocNode<ir::ETSParameterExpression>(paramIdent, false, ctx->Allocator());
88         ES2PANDA_ASSERT(paramExpression != nullptr);
89         paramExpression->SetRange(paramIdent->Range());
90         auto [paramVar, node] = paramScope->AddParamDecl(ctx->Allocator(), varbinder, paramExpression);
91         if (node != nullptr) {
92             varbinder->ThrowRedeclaration(node->Start(), paramVar->Name(), paramVar->Declaration()->Type());
93         }
94 
95         paramIdent->SetVariable(paramVar);
96         paramExpression->SetVariable(paramVar);
97 
98         params.push_back(paramExpression);
99     }
100 
101     return ir::FunctionSignature(nullptr, std::move(params), isSetter ? nullptr : field->TypeAnnotation());
102 }
103 
GenerateGetterOrSetter(public_lib::Context * ctx,varbinder::ETSBinder * varbinder,ir::ClassProperty * const field,bool isSetter)104 ir::MethodDefinition *InterfacePropertyDeclarationsPhase::GenerateGetterOrSetter(public_lib::Context *ctx,
105                                                                                  varbinder::ETSBinder *varbinder,
106                                                                                  ir::ClassProperty *const field,
107                                                                                  bool isSetter)
108 {
109     auto classScope = NearestScope(field);
110     auto *paramScope = ctx->Allocator()->New<varbinder::FunctionParamScope>(ctx->Allocator(), classScope);
111     auto *functionScope = ctx->Allocator()->New<varbinder::FunctionScope>(ctx->Allocator(), paramScope);
112     ES2PANDA_ASSERT(functionScope != nullptr);
113 
114     functionScope->BindParamScope(paramScope);
115     paramScope->BindFunctionScope(functionScope);
116 
117     auto flags = ir::ModifierFlags::PUBLIC;
118     flags |= ir::ModifierFlags::ABSTRACT;
119 
120     ir::FunctionSignature signature = GenerateGetterOrSetterSignature(ctx, varbinder, field, isSetter, paramScope);
121 
122     auto *func = ctx->AllocNode<ir::ScriptFunction>(
123         ctx->Allocator(), ir::ScriptFunction::ScriptFunctionData {
124                               nullptr, std::move(signature),  // CC-OFF(G.FMT.02) project code style
125                               // CC-OFFNXT(G.FMT.02) project code style
126                               isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER, flags});
127 
128     func->SetRange(field->Range());
129     func->SetScope(functionScope);
130 
131     auto const &name = field->Key()->AsIdentifier()->Name();
132     auto methodIdent = ctx->AllocNode<ir::Identifier>(name, ctx->Allocator());
133 
134     auto *decl = ctx->Allocator()->New<varbinder::VarDecl>(name);
135     auto var = functionScope->AddDecl(ctx->Allocator(), decl, ScriptExtension::ETS);
136     ES2PANDA_ASSERT(var != nullptr);
137     methodIdent->SetVariable(var);
138 
139     auto *funcExpr = ctx->AllocNode<ir::FunctionExpression>(func);
140     funcExpr->SetRange(func->Range());
141     func->AddFlag(ir::ScriptFunctionFlags::METHOD);
142 
143     auto *method =
144         ctx->AllocNode<ir::MethodDefinition>(isSetter ? ir::MethodDefinitionKind::SET : ir::MethodDefinitionKind::GET,
145                                              methodIdent, funcExpr, flags, ctx->Allocator(), false);
146 
147     method->Id()->SetMutator();
148     method->SetRange(field->Range());
149     method->Function()->SetIdent(method->Id()->Clone(ctx->Allocator(), nullptr));
150     method->Function()->AddModifier(method->Modifiers());
151     paramScope->BindNode(func);
152     functionScope->BindNode(func);
153 
154     if (!field->Annotations().empty()) {
155         ArenaVector<ir::AnnotationUsage *> functionAnnotations(ctx->Allocator()->Adapter());
156         for (auto *annotationUsage : field->Annotations()) {
157             functionAnnotations.push_back(annotationUsage->Clone(ctx->Allocator(), method)->AsAnnotationUsage());
158         }
159         method->Function()->SetAnnotations(std::move(functionAnnotations));
160     }
161 
162     return method;
163 }
164 
CollectPropertiesAndSuperInterfaces(ir::TSInterfaceBody * const interface)165 void InterfacePropertyDeclarationsPhase::CollectPropertiesAndSuperInterfaces(ir::TSInterfaceBody *const interface)
166 {
167     ES2PANDA_ASSERT(interface->Parent()->IsTSInterfaceDeclaration());
168     auto *interfaceDecl = interface->Parent()->AsTSInterfaceDeclaration();
169     GetPropCollector().SetInterfaceId(interfaceDecl->Id()->ToString());
170     GetPropCollector().InitInterfacePropertyMap();
171     for (const auto &superInterface : interfaceDecl->Extends()) {
172         std::string superId = superInterface->Expr()->AsETSTypeReference()->Part()->Name()->ToString();
173         if (!GetPropCollector().IsParentExists(GetPropCollector().GetInterfaceId())) {
174             GetPropCollector().InitInterfaceParentMap();
175         }
176         GetPropCollector().InsertInterfaceParent(superId);
177     }
178 }
179 
HandleInternalGetterOrSetterMethod(ir::AstNode * const ast)180 void InterfacePropertyDeclarationsPhase::HandleInternalGetterOrSetterMethod(ir::AstNode *const ast)
181 {
182     if (!ast->IsMethodDefinition()) {
183         return;
184     }
185     auto *method = ast->AsMethodDefinition();
186     if (method->Kind() == ir::MethodDefinitionKind::GET || method->Kind() == ir::MethodDefinitionKind::SET) {
187         GetPropCollector().InsertInterfaceProperty(method->Key()->ToString());
188     }
189 }
190 
191 //  Extracted form 'UpdateInterfaceProperties(...)' to reduce its size.
AddOverload(ir::MethodDefinition * method,ir::MethodDefinition * overload,varbinder::Variable * variable)192 static void AddOverload(ir::MethodDefinition *method, ir::MethodDefinition *overload, varbinder::Variable *variable)
193 {
194     method->AddOverload(overload);
195     overload->SetParent(method);
196     ES2PANDA_ASSERT(overload->Function());
197     overload->Function()->AddFlag(ir::ScriptFunctionFlags::OVERLOAD);
198     overload->Function()->Id()->SetVariable(variable);
199 }
200 
UpdateInterfaceProperties(public_lib::Context * ctx,varbinder::ETSBinder * varbinder,ir::TSInterfaceBody * const interface)201 ir::Expression *InterfacePropertyDeclarationsPhase::UpdateInterfaceProperties(public_lib::Context *ctx,
202                                                                               varbinder::ETSBinder *varbinder,
203                                                                               ir::TSInterfaceBody *const interface)
204 {
205     if (interface->Body().empty()) {
206         return interface;
207     }
208 
209     CollectPropertiesAndSuperInterfaces(interface);
210 
211     auto propertyList = interface->Body();
212     ArenaVector<ir::AstNode *> newPropertyList(ctx->Allocator()->Adapter());
213 
214     auto scope = NearestScope(interface);
215     ES2PANDA_ASSERT(scope->IsClassScope());
216 
217     for (const auto &prop : propertyList) {
218         if (!prop->IsClassProperty()) {
219             newPropertyList.emplace_back(prop);
220             HandleInternalGetterOrSetterMethod(prop);
221             continue;
222         }
223         auto *originProp = prop->Clone(ctx->allocator, nullptr);
224         auto getter = GenerateGetterOrSetter(ctx, varbinder, prop->AsClassProperty(), false);
225         getter->SetOriginalNode(originProp);
226 
227         auto methodScope = scope->AsClassScope()->InstanceMethodScope();
228         auto name = getter->Key()->AsIdentifier()->Name();
229 
230         auto *decl = ctx->Allocator()->New<varbinder::FunctionDecl>(ctx->Allocator(), name, getter);
231         auto *variable = methodScope->AddDecl(ctx->Allocator(), decl, ScriptExtension::ETS);
232 
233         if (variable == nullptr) {
234             auto prevDecl = methodScope->FindDecl(name);
235             ES2PANDA_ASSERT(prevDecl->IsFunctionDecl());
236 
237             auto *const method = prevDecl->Node()->AsMethodDefinition();
238             auto *const var = methodScope->FindLocal(name, varbinder::ResolveBindingOptions::BINDINGS);
239 
240             AddOverload(method, getter, var);
241 
242             if (!prop->AsClassProperty()->IsReadonly()) {
243                 auto setter = GenerateGetterOrSetter(ctx, varbinder, prop->AsClassProperty(), true);
244                 AddOverload(method, setter, var);
245             }
246             continue;
247         }
248 
249         getter->Function()->Id()->SetVariable(variable);
250         newPropertyList.emplace_back(getter);
251 
252         if (!prop->AsClassProperty()->IsReadonly()) {
253             auto setter = GenerateGetterOrSetter(ctx, varbinder, prop->AsClassProperty(), true);
254             AddOverload(getter, setter, variable);
255         }
256         scope->AsClassScope()->InstanceFieldScope()->EraseBinding(name);
257     }
258 
259     auto newInterface = ctx->AllocNode<ir::TSInterfaceBody>(std::move(newPropertyList));
260     ES2PANDA_ASSERT(newInterface != nullptr);
261     newInterface->SetRange(interface->Range());
262     newInterface->SetParent(interface->Parent());
263 
264     return newInterface;
265 }
266 
CollectSuperInterfaceProperties(InterfacePropertyType & implInterfaceProperties,const std::string & interId)267 void InterfacePropertyDeclarationsPhase::CollectSuperInterfaceProperties(InterfacePropertyType &implInterfaceProperties,
268                                                                          const std::string &interId)
269 {
270     if (GetPropCollector().IsVisitedInterface(interId)) {
271         return;
272     }
273 
274     if (GetPropCollector().IsInterfaceHasProperty(interId)) {
275         InterfacePropertyType &properties = GetPropCollector().GetInterfaceProperty(interId);
276         implInterfaceProperties.insert(properties.begin(), properties.end());
277     }
278     if (GetPropCollector().IsParentExists(interId)) {
279         for (auto &superId : GetPropCollector().GetInterfaceParent(interId)) {
280             CollectSuperInterfaceProperties(implInterfaceProperties, superId);
281         }
282     }
283 }
284 
UpdateClassProperties(public_lib::Context * ctx,ir::ClassDefinition * const klass)285 void InterfacePropertyDeclarationsPhase::UpdateClassProperties(public_lib::Context *ctx,
286                                                                ir::ClassDefinition *const klass)
287 {
288     if (klass->Body().empty()) {
289         return;
290     }
291 
292     InterfacePropertyType implInterfaceProperties = {};
293 
294     GetPropCollector().InitVisitedInterfaces();
295     for (const auto &implement : klass->Implements()) {
296         std::string interId = implement->Expr()->IsOpaqueTypeNode()
297                                   ? implement->Expr()->TsType()->AsETSObjectType()->Name().Mutf8()
298                                   : implement->Expr()->AsETSTypeReference()->Part()->Name()->ToString();
299         CollectSuperInterfaceProperties(implInterfaceProperties, interId);
300     }
301 
302     for (auto *elem : klass->Body()) {
303         if (elem->IsClassProperty() &&
304             (implInterfaceProperties.count(elem->AsClassProperty()->Key()->ToString()) != 0U)) {
305             TransformOptionalFieldTypeAnnotation(ctx, elem->AsClassProperty());
306         }
307     }
308 }
309 
PerformForModule(public_lib::Context * ctx,parser::Program * program)310 bool InterfacePropertyDeclarationsPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
311 {
312     varbinder::ETSBinder *const varbinder = ctx->parserProgram->VarBinder()->AsETSBinder();
313 
314     ir::NodeTransformer handleInterfacePropertyDecl = [this, ctx, varbinder](ir::AstNode *const ast) {
315         return ast->IsTSInterfaceBody() ? UpdateInterfaceProperties(ctx, varbinder, ast->AsTSInterfaceBody()) : ast;
316     };
317 
318     ir::NodeTransformer handleClassPropertyDecl = [this, ctx](ir::AstNode *const ast) {
319         if (ast->IsClassDefinition() && !ast->AsClassDefinition()->Implements().empty()) {
320             UpdateClassProperties(ctx, ast->AsClassDefinition());
321         }
322         return ast;
323     };
324 
325     program->Ast()->TransformChildrenRecursively(handleInterfacePropertyDecl, Name());
326     program->Ast()->TransformChildrenRecursively(handleClassPropertyDecl, Name());
327 
328     return true;
329 }
330 
331 }  // namespace ark::es2panda::compiler
332