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