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