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 "helpers.h"
17
18 #include "checker/types/typeFlag.h"
19 #include "checker/types/type.h"
20 #include "checker/types/ets/etsObjectType.h"
21 #include "checker/types/ets/etsUnionType.h"
22 #include "ir/statements/blockStatement.h"
23 #include "ir/ets/etsModule.h"
24 #include "parser/program/program.h"
25 #include "ir/expressions/memberExpression.h"
26 #include "ir/expressions/callExpression.h"
27
28 namespace ark::es2panda::compiler::ast_verifier {
29
IsImportLike(const ir::AstNode * ast)30 bool IsImportLike(const ir::AstNode *ast)
31 {
32 return (ast->IsETSImportDeclaration() || ast->IsETSReExportDeclaration() || ast->IsImportExpression() ||
33 ast->IsImportSpecifier() || ast->IsImportDefaultSpecifier() || ast->IsImportNamespaceSpecifier());
34 }
35
IsExportLike(const ir::AstNode * ast)36 bool IsExportLike(const ir::AstNode *ast)
37 {
38 return (ast->IsExportDefaultDeclaration() || ast->IsExportSpecifier() || ast->IsExportAllDeclaration() ||
39 ast->IsExportNamedDeclaration() || ast->IsETSReExportDeclaration());
40 }
41
IsBooleanType(const ir::AstNode * ast)42 bool IsBooleanType(const ir::AstNode *ast)
43 {
44 if (ast == nullptr) {
45 return false;
46 }
47
48 if (!ast->IsTyped()) {
49 return false;
50 }
51
52 auto typedAst = static_cast<const ir::TypedAstNode *>(ast);
53
54 if (typedAst->TsType() == nullptr) {
55 return false;
56 }
57
58 if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) &&
59 ast->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG)) {
60 return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN);
61 }
62
63 return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_BOOLEAN) ||
64 typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BOOLEAN_LIKE);
65 }
66
IsValidTypeForBinaryOp(const ir::AstNode * ast,bool isBitwise)67 bool IsValidTypeForBinaryOp(const ir::AstNode *ast, bool isBitwise)
68 {
69 if (ast == nullptr) {
70 return false;
71 }
72
73 if (!ast->IsTyped()) {
74 return false;
75 }
76
77 auto typedAst = static_cast<const ir::TypedAstNode *>(ast);
78
79 if (typedAst->TsType() == nullptr) {
80 return false;
81 }
82
83 if (IsBooleanType(ast)) {
84 return isBitwise;
85 }
86
87 if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) &&
88 typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BIGINT)) {
89 return true;
90 }
91
92 if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT) &&
93 ast->HasBoxingUnboxingFlags(ir::BoxingUnboxingFlags::UNBOXING_FLAG)) {
94 return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE) &&
95 !typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_BOOLEAN);
96 }
97
98 return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_CONVERTIBLE_TO_NUMERIC) ||
99 typedAst->TsType()->HasTypeFlag(checker::TypeFlag::NUMBER_LITERAL) ||
100 typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT) ||
101 typedAst->TsType()->HasTypeFlag(checker::TypeFlag::BIGINT_LITERAL);
102 }
103
IsStringType(const ir::AstNode * ast)104 bool IsStringType(const ir::AstNode *ast)
105 {
106 if (ast == nullptr) {
107 return false;
108 }
109
110 if (!ast->IsTyped()) {
111 return false;
112 }
113
114 auto typedAst = static_cast<const ir::TypedAstNode *>(ast);
115
116 if (typedAst->TsType() == nullptr) {
117 return false;
118 }
119
120 if (typedAst->TsType()->HasTypeFlag(checker::TypeFlag::ETS_OBJECT)) {
121 return typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::STRING) ||
122 typedAst->TsType()->AsETSObjectType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_STRING);
123 }
124
125 return typedAst->TsType()->HasTypeFlag(checker::TypeFlag::STRING_LIKE);
126 }
127
IsVisibleInternalNode(const ir::AstNode * ast,const ir::AstNode * objTypeDeclNode)128 bool IsVisibleInternalNode(const ir::AstNode *ast, const ir::AstNode *objTypeDeclNode)
129 {
130 if (!ast->GetTopStatement()->IsETSModule()) {
131 return false;
132 }
133 auto *currentTopStatement = ast->GetTopStatement()->AsETSModule();
134 auto *currentProgram = currentTopStatement->Program();
135 if (currentProgram == nullptr) {
136 return false;
137 }
138 if (!objTypeDeclNode->GetTopStatement()->IsETSModule()) {
139 return false;
140 }
141 auto *objectTopStatement = objTypeDeclNode->GetTopStatement()->AsETSModule();
142 auto *objectProgram = objectTopStatement->Program();
143 if (objectProgram == nullptr) {
144 return false;
145 }
146 return currentTopStatement == objectTopStatement || currentProgram->ModuleName() == objectProgram->ModuleName();
147 }
148
GetClassDefinitionType(const ir::AstNode * ast)149 const checker::Type *GetClassDefinitionType(const ir::AstNode *ast)
150 {
151 const ir::AstNode *tmpNode = ast;
152 while (tmpNode->Parent() != nullptr && !tmpNode->IsClassDefinition()) {
153 tmpNode = tmpNode->Parent();
154 }
155 if (!tmpNode->IsClassDefinition()) {
156 return nullptr;
157 }
158 auto *classDefinition = tmpNode->AsClassDefinition();
159 return classDefinition->TsType();
160 }
161
GetTSInterfaceDeclarationType(const ir::AstNode * ast)162 const checker::Type *GetTSInterfaceDeclarationType(const ir::AstNode *ast)
163 {
164 const ir::AstNode *tmpNode = ast;
165 while (tmpNode->Parent() != nullptr && !tmpNode->IsTSInterfaceDeclaration()) {
166 tmpNode = tmpNode->Parent();
167 }
168 if (!tmpNode->IsTSInterfaceDeclaration()) {
169 return nullptr;
170 }
171 auto *tsInterfaceDeclaration = tmpNode->AsTSInterfaceDeclaration();
172 return tsInterfaceDeclaration->TsType();
173 }
174
ValidateMethodAccessForClass(const ir::AstNode * ast,const ir::AstNode * ownerSignDeclNode,checker::Signature * signature,const ir::AstNode * memberObjTypeDeclNode)175 bool ValidateMethodAccessForClass(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode,
176 checker::Signature *signature, const ir::AstNode *memberObjTypeDeclNode)
177 {
178 // Check if the method is used where it is declared
179 if (IsContainedIn<const ir::AstNode>(ast, ownerSignDeclNode)) {
180 return true;
181 }
182 if (signature->HasSignatureFlag(checker::SignatureFlags::PRIVATE)) {
183 return false;
184 }
185 if (signature->HasSignatureFlag(checker::SignatureFlags::PROTECTED)) {
186 // Check if the method is inherited and is used in class in which it is inherited
187 auto *classDefinitionType = GetClassDefinitionType(ast);
188 if (classDefinitionType == nullptr || !classDefinitionType->IsETSObjectType()) {
189 return false;
190 }
191 auto *classObjectType = classDefinitionType->AsETSObjectType();
192 return classObjectType->IsDescendantOf(signature->Owner());
193 }
194 if (signature->HasSignatureFlag(checker::SignatureFlags::INTERNAL)) {
195 return IsVisibleInternalNode(ast, memberObjTypeDeclNode);
196 }
197 return true;
198 }
199
ValidateMethodAccessForTSInterface(const ir::AstNode * ast,const ir::AstNode * ownerSignDeclNode,checker::Signature * signature,const ir::AstNode * memberObjTypeDeclNode)200 bool ValidateMethodAccessForTSInterface(const ir::AstNode *ast, const ir::AstNode *ownerSignDeclNode,
201 checker::Signature *signature, const ir::AstNode *memberObjTypeDeclNode)
202 {
203 // Check if the method is used where it is declared
204 if (IsContainedIn<const ir::AstNode>(ast, ownerSignDeclNode)) {
205 return true;
206 }
207 if (signature->HasSignatureFlag(checker::SignatureFlags::PRIVATE)) {
208 return false;
209 }
210 if (signature->HasSignatureFlag(checker::SignatureFlags::PROTECTED)) {
211 // Check if the method is inherited and is used in class in which it is inherited
212 auto *tsInterfaceDeclarationType = GetTSInterfaceDeclarationType(ast);
213 if (tsInterfaceDeclarationType == nullptr || !tsInterfaceDeclarationType->IsETSObjectType()) {
214 return false;
215 }
216 auto *tsInterfaceObjectType = tsInterfaceDeclarationType->AsETSObjectType();
217 return tsInterfaceObjectType->IsDescendantOf(signature->Owner());
218 }
219 if (signature->HasSignatureFlag(checker::SignatureFlags::INTERNAL)) {
220 return IsVisibleInternalNode(ast, memberObjTypeDeclNode);
221 }
222 return true;
223 }
224
ValidatePropertyAccessForClass(const ir::AstNode * ast,const ir::AstNode * propVarDeclNode,const ir::AstNode * propVarDeclNodeParent,const varbinder::LocalVariable * propVar,const ir::AstNode * objTypeDeclNode)225 bool ValidatePropertyAccessForClass(const ir::AstNode *ast, const ir::AstNode *propVarDeclNode,
226 const ir::AstNode *propVarDeclNodeParent, const varbinder::LocalVariable *propVar,
227 const ir::AstNode *objTypeDeclNode)
228 {
229 // Check if the variable is used where it is declared
230 if (IsContainedIn<const ir::AstNode>(ast, propVarDeclNodeParent)) {
231 return true;
232 }
233 if (propVarDeclNode->IsPrivate()) {
234 return false;
235 }
236 if (propVarDeclNode->IsProtected()) {
237 auto *classDefinitionType = GetClassDefinitionType(ast);
238 if (classDefinitionType != nullptr && classDefinitionType->IsETSObjectType()) {
239 auto *classObjectType = classDefinitionType->AsETSObjectType();
240 return classObjectType->IsPropertyOfAscendant(propVar);
241 }
242 auto *interfaceDefType = GetTSInterfaceDeclarationType(ast);
243 if (interfaceDefType != nullptr && interfaceDefType->IsETSObjectType()) {
244 auto *interfaceObjectType = interfaceDefType->AsETSObjectType();
245 return interfaceObjectType->IsPropertyOfAscendant(propVar);
246 }
247 return false;
248 }
249 if (propVarDeclNode->IsInternal()) {
250 return IsVisibleInternalNode(ast, objTypeDeclNode);
251 }
252 return true;
253 }
254
ValidateVariableAccess(const varbinder::LocalVariable * propVar,const ir::MemberExpression * ast)255 bool ValidateVariableAccess(const varbinder::LocalVariable *propVar, const ir::MemberExpression *ast)
256 {
257 const auto *propVarDecl = propVar->Declaration();
258 if (propVarDecl == nullptr) {
259 return false;
260 }
261 const auto *propVarDeclNode = propVarDecl->Node();
262 if (propVarDeclNode == nullptr) {
263 return false;
264 }
265
266 // NOTE: need to refactor: type of member expression object can be obtained via
267 // me->ObjType() or me->Object()->TsType() and they may differ!!!!
268 if (auto objType = const_cast<ir::MemberExpression *>(ast)->Object()->TsType(); objType->IsETSUnionType()) {
269 bool res = true;
270 for (auto type : objType->AsETSUnionType()->ConstituentTypes()) {
271 const_cast<ir::MemberExpression *>(ast)->SetObjectType(type->AsETSObjectType());
272 // Just to skip enclosing if clause checking whether object tsType is ETSUnionType in subsequent recursive
273 // call
274 const_cast<ir::MemberExpression *>(ast)->Object()->SetTsType(type->AsETSObjectType());
275 }
276 const_cast<ir::MemberExpression *>(ast)->SetObjectType(ast->ObjType());
277 const_cast<ir::MemberExpression *>(ast)->Object()->SetTsType(objType);
278 return res;
279 }
280
281 auto *objType = ast->ObjType();
282 if (objType == nullptr) {
283 return false;
284 }
285 const auto *objTypeDeclNode = objType->GetDeclNode();
286 if (objTypeDeclNode == nullptr) {
287 return false;
288 }
289 if (objTypeDeclNode->Parent() != nullptr && objTypeDeclNode->Parent()->IsImportNamespaceSpecifier()) {
290 return true;
291 }
292 const auto *propVarDeclNodeParent = propVarDeclNode->Parent();
293 if (propVarDeclNodeParent == nullptr) {
294 return false;
295 }
296 if ((propVarDeclNodeParent->IsClassDefinition() && objTypeDeclNode->IsClassDefinition()) ||
297 (propVarDeclNodeParent->IsTSInterfaceDeclaration() && objTypeDeclNode->IsTSInterfaceDeclaration())) {
298 return ValidatePropertyAccessForClass(ast, propVarDeclNode, propVarDeclNodeParent, propVar, objTypeDeclNode);
299 }
300 return false;
301 }
302
ValidateMethodAccess(const ir::MemberExpression * memberExpression,const ir::CallExpression * ast)303 bool ValidateMethodAccess(const ir::MemberExpression *memberExpression, const ir::CallExpression *ast)
304 {
305 // NOTE: need to refactor: type of member expression object can be obtained via
306 // me->ObjType() or me->Object()->TsType() and they may differ!!!!
307 if (memberExpression->Object()->TsType() != nullptr) {
308 // When calling enum methods member expression
309 // object has ETSEnumType instead of ETSObjectType.
310 const auto *const type = memberExpression->Object()->TsType();
311 if (type->IsETSEnumType()) {
312 return true;
313 }
314
315 // When calling enum methods member expression
316 // object has ETSUnionType instead of ETSObjectType.
317 if (type->IsETSUnionType()) {
318 return true;
319 }
320 }
321
322 auto *memberObjType = memberExpression->ObjType();
323 if (memberObjType == nullptr) {
324 return false;
325 }
326 if (memberObjType->HasObjectFlag(checker::ETSObjectFlags::RESOLVED_SUPER) &&
327 memberObjType->SuperType() != nullptr &&
328 memberObjType->SuperType()->HasObjectFlag(checker::ETSObjectFlags::BUILTIN_TYPE |
329 checker::ETSObjectFlags::GLOBAL)) {
330 return true;
331 }
332 const auto *memberObjTypeDeclNode = memberObjType->GetDeclNode();
333 if (memberObjTypeDeclNode == nullptr) {
334 return false;
335 }
336 if (memberObjTypeDeclNode->Parent() != nullptr && memberObjTypeDeclNode->Parent()->IsImportNamespaceSpecifier()) {
337 return true;
338 }
339 auto *signature = ast->Signature();
340 if (signature == nullptr) {
341 return false;
342 }
343 auto *ownerSign = signature->Owner();
344 if (ownerSign == nullptr) {
345 return false;
346 }
347 auto *ownerSignDeclNode = ownerSign->GetDeclNode();
348 if (ownerSignDeclNode == nullptr) {
349 return false;
350 }
351 if (!ownerSignDeclNode->IsClassDefinition() && !ownerSignDeclNode->IsTSInterfaceDeclaration()) {
352 return false;
353 }
354 bool ret = false;
355 if (memberObjTypeDeclNode->IsClassDefinition()) {
356 ret = ValidateMethodAccessForClass(ast, ownerSignDeclNode, signature, memberObjTypeDeclNode);
357 } else if (memberObjTypeDeclNode->IsTSInterfaceDeclaration()) {
358 ret = ValidateMethodAccessForTSInterface(ast, ownerSignDeclNode, signature, memberObjTypeDeclNode);
359 }
360 return ret;
361 }
362
363 } // namespace ark::es2panda::compiler::ast_verifier
364