• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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