• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "interfaceObjectLiteralLowering.h"
17 #include "checker/ETSchecker.h"
18 #include "checker/ets/typeRelationContext.h"
19 #include "ir/expressions/assignmentExpression.h"
20 #include "util/helpers.h"
21 
22 namespace ark::es2panda::compiler {
23 
Name() const24 std::string_view InterfaceObjectLiteralLowering::Name() const
25 {
26     return "InterfaceObjectLiteralLowering";
27 }
28 
IsInterfaceType(const checker::Type * type)29 static inline bool IsInterfaceType(const checker::Type *type)
30 {
31     return type != nullptr && type->IsETSObjectType() &&
32            type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::INTERFACE) &&
33            !type->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::DYNAMIC);
34 }
35 
CreateAnonClassImplCtor(checker::ETSChecker * checker)36 static ir::AstNode *CreateAnonClassImplCtor(checker::ETSChecker *checker)
37 {
38     checker::ETSChecker::ClassInitializerBuilder initBuilder =
39         [checker]([[maybe_unused]] ArenaVector<ir::Statement *> *statements,
40                   [[maybe_unused]] ArenaVector<ir::Expression *> *params) {
41             checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
42         };
43 
44     return checker->CreateClassInstanceInitializer(initBuilder);
45 }
46 
CreateAnonClassField(ir::MethodDefinition * ifaceMethod,checker::ETSChecker * checker)47 static ir::ClassProperty *CreateAnonClassField(ir::MethodDefinition *ifaceMethod, checker::ETSChecker *checker)
48 {
49     auto *const allocator = checker->Allocator();
50 
51     // Field type annotation
52     auto *fieldType = ifaceMethod->Function()->Signature()->ReturnType();
53     ASSERT(fieldType != nullptr);
54     auto *fieldTypeNode = checker->AllocNode<ir::OpaqueTypeNode>(fieldType);
55 
56     // Field identifier
57     util::UString fieldName(std::string("_"), allocator);
58     fieldName.Append(ifaceMethod->Id()->Name());
59     auto *fieldId = checker->AllocNode<ir::Identifier>(fieldName.View(), nullptr, allocator);
60 
61     // Field modifiers flags
62     ir::ModifierFlags fieldMF = ir::ModifierFlags::PRIVATE;
63 
64     // No overloads means no setter function with the same name, so the field is readonly
65     if (ifaceMethod->Overloads().empty()) {
66         fieldMF |= ir::ModifierFlags::READONLY;
67     }
68 
69     // Create synthetic class property node
70     auto *field = checker->AllocNode<ir::ClassProperty>(fieldId, nullptr, fieldTypeNode->Clone(allocator, nullptr),
71                                                         fieldMF, allocator, false);
72     field->SetRange(ifaceMethod->Range());
73 
74     return field;
75 }
76 
CreateAnonClassFieldGetterSetter(checker::ETSChecker * checker,ir::MethodDefinition * ifaceMethod,bool isSetter)77 static ir::MethodDefinition *CreateAnonClassFieldGetterSetter(checker::ETSChecker *checker,
78                                                               ir::MethodDefinition *ifaceMethod, bool isSetter)
79 {
80     checker::ETSChecker::MethodBuilder methodBuilder = [checker, ifaceMethod,
81                                                         isSetter](ArenaVector<ir::Statement *> *statements,
82                                                                   ArenaVector<ir::Expression *> *params,
83                                                                   checker::Type **returnType) {
84         auto *const allocator = checker->Allocator();
85 
86         // Adding mandatory 'this' parameter
87         checker->AddParam(varbinder::VarBinder::MANDATORY_PARAM_THIS, nullptr);
88 
89         // ifaceMethod is getter, so it should have return type
90         auto *retType = ifaceMethod->Function()->Signature()->ReturnType();
91         ASSERT(retType != nullptr);
92 
93         // Field identifier
94         util::UString fieldName(std::string("_"), allocator);
95         fieldName.Append(ifaceMethod->Id()->Name());
96         auto *fieldId = checker->AllocNode<ir::Identifier>(fieldName.View(), nullptr, allocator);
97 
98         if (isSetter) {
99             // Setter call params
100             ir::ETSParameterExpression *param =
101                 checker->AddParam(ifaceMethod->Id()->Name(), checker->AllocNode<ir::OpaqueTypeNode>(retType));
102             params->push_back(param);
103 
104             // Setter body:
105             // this.<fieldName> = <callParam>;
106             auto *thisExpr = checker->AllocNode<ir::ThisExpression>();
107             auto *lhs = checker->AllocNode<ir::MemberExpression>(
108                 thisExpr, fieldId->Clone(allocator, nullptr), ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
109             auto *rhs = param->Ident()->CloneReference(allocator, nullptr);
110 
111             auto *assignment =
112                 checker->AllocNode<ir::AssignmentExpression>(lhs, rhs, lexer::TokenType::PUNCTUATOR_SUBSTITUTION);
113             auto *statement = checker->AllocNode<ir::ExpressionStatement>(assignment);
114             statements->push_back(statement);
115 
116             // Setter return type
117             *returnType = checker->GlobalVoidType();
118         } else {
119             // Getter call params are empty
120 
121             // Getter body:
122             // Just return this.<fieldName>;
123             auto *thisExpr = checker->AllocNode<ir::ThisExpression>();
124             auto *argument = checker->AllocNode<ir::MemberExpression>(
125                 thisExpr, fieldId->Clone(allocator, nullptr), ir::MemberExpressionKind::PROPERTY_ACCESS, false, false);
126 
127             auto *statement = checker->AllocNode<ir::ReturnStatement>(argument);
128             statements->push_back(statement);
129 
130             // Getter return type
131             *returnType = retType;
132         }
133     };
134 
135     ir::ModifierFlags modifierFlags = ir::ModifierFlags::PUBLIC;
136     modifierFlags |= isSetter ? ir::ModifierFlags::SETTER : ir::ModifierFlags::GETTER;
137     ir::ScriptFunctionFlags funcFlags = ir::ScriptFunctionFlags::METHOD;
138     funcFlags |= isSetter ? ir::ScriptFunctionFlags::SETTER : ir::ScriptFunctionFlags::GETTER;
139 
140     return checker->CreateClassMethod(ifaceMethod->Id()->Name().Utf8(), funcFlags, modifierFlags, methodBuilder);
141 }
142 
FillClassBody(checker::ETSChecker * checker,ArenaVector<ir::AstNode * > * classBody,const ArenaVector<ir::AstNode * > & ifaceBody,ir::ObjectExpression * objExpr)143 static void FillClassBody(checker::ETSChecker *checker, ArenaVector<ir::AstNode *> *classBody,
144                           const ArenaVector<ir::AstNode *> &ifaceBody, ir::ObjectExpression *objExpr)
145 {
146     for (auto *it : ifaceBody) {
147         ASSERT(it->IsMethodDefinition());
148         auto *ifaceMethod = it->AsMethodDefinition();
149 
150         if (!ifaceMethod->Function()->IsGetter() && !ifaceMethod->Function()->IsSetter()) {
151             checker->LogTypeError("Interface has methods", objExpr->Start());
152             objExpr->SetTsType(checker->GlobalTypeError());
153             return;
154         }
155 
156         if (!ifaceMethod->Function()->IsGetter()) {
157             continue;
158         }
159 
160         auto *field = CreateAnonClassField(ifaceMethod, checker);
161         classBody->push_back(field);
162 
163         auto *getter = CreateAnonClassFieldGetterSetter(checker, ifaceMethod, false);
164         classBody->push_back(getter);
165 
166         if (ifaceMethod->Overloads().size() == 1 && ifaceMethod->Overloads()[0]->Function()->IsSetter()) {
167             auto *setter = CreateAnonClassFieldGetterSetter(checker, ifaceMethod, true);
168             classBody->push_back(setter);
169         }
170     }
171 }
172 
FillAnonClassBody(checker::ETSChecker * checker,ArenaVector<ir::AstNode * > * classBody,ir::TSInterfaceDeclaration * ifaceNode,ir::ObjectExpression * objExpr)173 static void FillAnonClassBody(checker::ETSChecker *checker, ArenaVector<ir::AstNode *> *classBody,
174                               ir::TSInterfaceDeclaration *ifaceNode, ir::ObjectExpression *objExpr)
175 {
176     for (auto *extendedIface : ifaceNode->TsType()->AsETSObjectType()->Interfaces()) {
177         auto extendedIfaceBody = extendedIface->GetDeclNode()->AsTSInterfaceDeclaration()->Body()->Body();
178         FillClassBody(checker, classBody, extendedIfaceBody, objExpr);
179     }
180 
181     FillClassBody(checker, classBody, ifaceNode->Body()->Body(), objExpr);
182 }
183 
GenerateAnonClassTypeFromInterface(checker::ETSChecker * checker,ir::TSInterfaceDeclaration * ifaceNode,ir::ObjectExpression * objExpr)184 static checker::Type *GenerateAnonClassTypeFromInterface(checker::ETSChecker *checker,
185                                                          ir::TSInterfaceDeclaration *ifaceNode,
186                                                          ir::ObjectExpression *objExpr)
187 {
188     if (ifaceNode->GetAnonClass() != nullptr) {
189         return ifaceNode->GetAnonClass()->Definition()->TsType()->AsETSObjectType();
190     }
191 
192     auto classBodyBuilder = [checker, ifaceNode, objExpr](ArenaVector<ir::AstNode *> *classBody) {
193         if (ifaceNode->TsType() == nullptr) {
194             ifaceNode->Check(checker);
195         }
196 
197         FillAnonClassBody(checker, classBody, ifaceNode, objExpr);
198         classBody->push_back(CreateAnonClassImplCtor(checker));
199     };
200 
201     util::UString className(util::StringView("$anonymous_class$"), checker->Allocator());
202     className.Append(ifaceNode->Id()->Name());
203     auto *classDecl = checker->BuildClass(className.View(), classBodyBuilder);
204     auto *classDef = classDecl->Definition();
205     auto *classType = classDef->TsType()->AsETSObjectType();
206 
207     // Class type params
208     if (ifaceNode->TypeParams() != nullptr) {
209         // NOTE: to be done
210         checker->LogTypeError("Object literal cannot be of typed interface type", objExpr->Start());
211         return checker->GlobalTypeError();
212     }
213 
214     // Class implements
215     auto *classImplements =
216         checker->AllocNode<ir::TSClassImplements>(checker->AllocNode<ir::OpaqueTypeNode>(ifaceNode->TsType()));
217     classImplements->SetParent(classDef);
218     classDef->Implements().emplace_back(classImplements);
219     classType->RemoveObjectFlag(checker::ETSObjectFlags::RESOLVED_INTERFACES);
220     checker->GetInterfacesOfClass(classType);
221 
222     ifaceNode->SetAnonClass(classDecl);
223     return classType;
224 }
225 
HandleInterfaceLowering(checker::ETSChecker * checker,ir::ObjectExpression * objExpr)226 static void HandleInterfaceLowering(checker::ETSChecker *checker, ir::ObjectExpression *objExpr)
227 {
228     const auto *const targetType = objExpr->TsType();
229     ASSERT(targetType->AsETSObjectType()->GetDeclNode()->IsTSInterfaceDeclaration());
230     auto *ifaceNode = targetType->AsETSObjectType()->GetDeclNode()->AsTSInterfaceDeclaration();
231     checker::Type *resultType = GenerateAnonClassTypeFromInterface(checker, ifaceNode, objExpr);
232 
233     if (const auto *const parent = objExpr->Parent(); parent->IsArrayExpression()) {
234         for (auto *elem : parent->AsArrayExpression()->Elements()) {
235             if (!elem->IsObjectExpression()) {
236                 continue;
237             }
238             // Adjusting ts types of other object literals in array
239             elem->AsObjectExpression()->SetTsType(resultType);
240         }
241     }
242     objExpr->SetTsType(resultType);
243 }
244 
Perform(public_lib::Context * ctx,parser::Program * program)245 bool InterfaceObjectLiteralLowering::Perform(public_lib::Context *ctx, parser::Program *program)
246 {
247     for (auto &[_, extPrograms] : program->ExternalSources()) {
248         (void)_;
249         for (auto *extProg : extPrograms) {
250             Perform(ctx, extProg);
251         }
252     }
253 
254     auto *checker = ctx->checker->AsETSChecker();
255 
256     program->Ast()->IterateRecursivelyPostorder([checker](ir::AstNode *ast) -> void {
257         if (ast->IsObjectExpression() && IsInterfaceType(ast->AsObjectExpression()->TsType())) {
258             HandleInterfaceLowering(checker, ast->AsObjectExpression());
259         }
260     });
261 
262     return true;
263 }
264 
Postcondition(public_lib::Context * ctx,const parser::Program * program)265 bool InterfaceObjectLiteralLowering::Postcondition(public_lib::Context *ctx, const parser::Program *program)
266 {
267     for (auto &[_, extPrograms] : program->ExternalSources()) {
268         (void)_;
269         for (auto *extProg : extPrograms) {
270             if (!Postcondition(ctx, extProg)) {
271                 return false;
272             }
273         }
274     }
275 
276     return !program->Ast()->IsAnyChild([](const ir::AstNode *ast) -> bool {
277         return ast->IsObjectExpression() && IsInterfaceType(ast->AsObjectExpression()->TsType());
278     });
279 }
280 }  // namespace ark::es2panda::compiler
281