• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "extensionAccessorLowering.h"
17 #include "checker/ETSchecker.h"
18 #include "compiler/lowering/scopesInit/scopesInitPhase.h"
19 #include "compiler/lowering/util.h"
20 
21 namespace ark::es2panda::compiler {
22 
23 static ir::AstNode *CheckAndReturnNode(checker::ETSChecker *checker, ir::AstNode *node);
24 
ResolveAssignmentExpressionToExtensionAccessorCall(ir::AssignmentExpression * assignExpr,ir::MemberExpression * expr)25 static bool ResolveAssignmentExpressionToExtensionAccessorCall(ir::AssignmentExpression *assignExpr,
26                                                                ir::MemberExpression *expr)
27 {
28     auto *curParent = assignExpr->Parent()->AsExpressionStatement();
29     ir::AstNode *callExpr = nullptr;
30     if (expr->Object()->IsETSNewClassInstanceExpression()) {
31         callExpr = assignExpr->Left()->AsMemberExpression()->Property()->Parent();
32     } else {
33         callExpr = assignExpr->Left()->AsMemberExpression()->Parent();
34     }
35     curParent->SetExpression(callExpr->AsExpression());
36     callExpr->SetParent(curParent);
37     callExpr->SetRange(curParent->Range());
38     return true;
39 }
40 
IsMemberExprExtensionAccessor(ir::AstNode * node)41 static bool IsMemberExprExtensionAccessor(ir::AstNode *node)
42 {
43     return node->IsMemberExpression() &&
44            node->AsMemberExpression()->HasMemberKind(ir::MemberExpressionKind::EXTENSION_ACCESSOR);
45 }
46 
IsAssignExprExtensionSetter(ir::AstNode * node)47 static bool IsAssignExprExtensionSetter(ir::AstNode *node)
48 {
49     return node->IsAssignmentExpression() &&
50            node->AsAssignmentExpression()->OperatorType() == lexer::TokenType::PUNCTUATOR_SUBSTITUTION &&
51            IsMemberExprExtensionAccessor(node->AsAssignmentExpression()->Left());
52 }
53 
54 // Note: In check phase, extension accessor 'a.m' get its type from returnType of Signature. In this lowering, `a.m` is
55 // lowered to 'a.m()', so its Type should be switched to the functionType of extension accessor, and in the reCheck,
56 // `a.m` will not be checked again
SwitchType(ir::MemberExpression * expr)57 static void SwitchType(ir::MemberExpression *expr)
58 {
59     expr->SetTsType(expr->ExtensionAccessorType());
60 }
61 
TryHandleExtensionAccessor(checker::ETSChecker * checker,ir::MemberExpression * expr)62 static void TryHandleExtensionAccessor(checker::ETSChecker *checker, ir::MemberExpression *expr)
63 {
64     checker::SavedCheckerContextStatus ccStatusHelper(&checker->Context(),
65                                                       checker::CheckerStatus::IN_EXTENSION_ACCESSOR_CHECK);
66     SwitchType(expr);
67     auto oldParent = expr->Parent();
68     if (IsAssignExprExtensionSetter(oldParent) && expr == oldParent->AsAssignmentExpression()->Left()) {
69         auto *assignExpr = oldParent->AsAssignmentExpression();
70         auto *callExpr = checker->CreateExtensionAccessorCall(
71             checker, expr, ArenaVector<ir::Expression *>(checker->ProgramAllocator()->Adapter()));
72 
73         auto *rightExpr = assignExpr->AsAssignmentExpression()->Right();
74         rightExpr->SetBoxingUnboxingFlags(ir::BoxingUnboxingFlags::NONE);
75         if (IsMemberExprExtensionAccessor(rightExpr)) {
76             SwitchType(rightExpr->AsMemberExpression());
77             checker::Type *tsType = rightExpr->AsMemberExpression()->TsType();
78             checker::ETSFunctionType *eAccType = rightExpr->AsMemberExpression()->ExtensionAccessorType();
79             auto *copyedRight = rightExpr->Clone(checker->ProgramAllocator(), nullptr);
80             copyedRight->AsMemberExpression()->SetTsType(tsType);
81             copyedRight->AsMemberExpression()->SetExtensionAccessorType(eAccType);
82             rightExpr = checker->CreateExtensionAccessorCall(
83                 checker, copyedRight->AsMemberExpression(),
84                 ArenaVector<ir::Expression *>(checker->ProgramAllocator()->Adapter()));
85         }
86         rightExpr->SetParent(callExpr);
87         callExpr->AsCallExpression()->Arguments().emplace_back(rightExpr);
88         if (!ResolveAssignmentExpressionToExtensionAccessorCall(assignExpr, expr)) {
89             return;
90         };
91         CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, callExpr);
92         return;
93     }
94 
95     auto *callExpr = checker->CreateExtensionAccessorCall(
96         checker, expr, ArenaVector<ir::Expression *>(checker->ProgramAllocator()->Adapter()));
97     callExpr->SetParent(oldParent);
98     CheckLoweredNode(checker->VarBinder()->AsETSBinder(), checker, callExpr);
99     callExpr->AddBoxingUnboxingFlags(expr->GetBoxingUnboxingFlags());
100 }
101 
CheckAndReturnNode(checker::ETSChecker * checker,ir::AstNode * node)102 static ir::AstNode *CheckAndReturnNode(checker::ETSChecker *checker, ir::AstNode *node)
103 {
104     if (!IsAssignExprExtensionSetter(node) && !IsMemberExprExtensionAccessor(node)) {
105         return node;
106     }
107 
108     if (IsAssignExprExtensionSetter(node)) {
109         node->SetParent(nullptr);
110         if (node->AsAssignmentExpression()->Left()->AsMemberExpression()->Object()->IsETSNewClassInstanceExpression()) {
111             return node->AsAssignmentExpression()->Left()->AsMemberExpression()->Property()->Parent();
112         }
113         return node->AsAssignmentExpression()->Left()->Parent();
114     }
115 
116     auto *oldParent = node->Parent();
117     TryHandleExtensionAccessor(checker, node->AsMemberExpression());
118     if (IsAssignExprExtensionSetter(oldParent) && (node == oldParent->AsAssignmentExpression()->Left())) {
119         return node;
120     }
121 
122     if (node->AsMemberExpression()->Object()->IsETSNewClassInstanceExpression()) {
123         return node->AsMemberExpression()->Property()->Parent();
124     }
125     return node->Parent();
126 }
127 
128 using AstNodePtr = ir::AstNode *;
129 
PerformForModule(public_lib::Context * ctx,parser::Program * program)130 bool ExtensionAccessorPhase::PerformForModule(public_lib::Context *ctx, parser::Program *program)
131 {
132     if (program->Extension() != ScriptExtension::ETS) {
133         return true;
134     }
135 
136     checker::ETSChecker *const checker = ctx->checker->AsETSChecker();
137     program->Ast()->TransformChildrenRecursively(
138         [&checker](ir::AstNode *const node) -> AstNodePtr { return CheckAndReturnNode(checker, node); }, Name());
139     return true;
140 }
141 
142 }  // namespace ark::es2panda::compiler
143