• 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 "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