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
27namespace ark::es2panda::compiler::ast_verifier {
28
29bool IsImportLike(const ir::AstNode *ast)
30{
31    return (ast->IsETSImportDeclaration() || ast->IsETSReExportDeclaration() || ast->IsImportExpression() ||
32            ast->IsImportSpecifier() || ast->IsImportDefaultSpecifier() || ast->IsImportNamespaceSpecifier());
33}
34
35bool IsExportLike(const ir::AstNode *ast)
36{
37    return (ast->IsExportDefaultDeclaration() || ast->IsExportSpecifier() || ast->IsExportAllDeclaration() ||
38            ast->IsExportNamedDeclaration() || ast->IsETSReExportDeclaration());
39}
40
41bool 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
66bool 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
107bool 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
131bool 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
156const 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
169const 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
182bool 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
207bool 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
232bool 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
257bool 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
288bool 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