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