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 "ast_verifier_test.h"
17 #include "checker/ETSchecker.h"
18 #include "ir/expressions/literals/stringLiteral.h"
19 #include "ir/expressions/identifier.h"
20 #include "ir/expressions/literals/numberLiteral.h"
21 #include "ir/expressions/literals/booleanLiteral.h"
22 #include "macros.h"
23 #include "parser/ETSparser.h"
24 #include "varbinder/ETSBinder.h"
25 
26 #include <gtest/gtest.h>
27 
28 using ark::es2panda::CompilerOptions;
29 using ark::es2panda::ScriptExtension;
30 using ark::es2panda::checker::ETSChecker;
31 using ark::es2panda::compiler::ast_verifier::ASTVerifier;
32 using ark::es2panda::compiler::ast_verifier::InvariantNameSet;
33 using ark::es2panda::ir::BinaryExpression;
34 using ark::es2panda::ir::BooleanLiteral;
35 using ark::es2panda::ir::Expression;
36 using ark::es2panda::ir::Identifier;
37 using ark::es2panda::ir::NumberLiteral;
38 using ark::es2panda::ir::SequenceExpression;
39 using ark::es2panda::ir::StringLiteral;
40 using ark::es2panda::lexer::Number;
41 using ark::es2panda::lexer::TokenType;
42 using ark::es2panda::parser::ETSParser;
43 using ark::es2panda::parser::Program;
44 using ark::es2panda::util::StringView;
45 using ark::es2panda::varbinder::ETSBinder;
46 using ark::es2panda::varbinder::FunctionScope;
47 using ark::es2panda::varbinder::LetDecl;
48 using ark::es2panda::varbinder::LocalScope;
49 using ark::es2panda::varbinder::LocalVariable;
50 using ark::es2panda::varbinder::VariableFlags;
51 
TEST_F(ASTVerifierTest, NullParent)52 TEST_F(ASTVerifierTest, NullParent)
53 {
54     ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
55     StringLiteral emptyNode;
56 
57     const auto check = "NodeHasParent";
58     auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
59     checks.insert(check);
60     const auto &messages = verifier.Verify(&emptyNode, checks);
61     bool hasParent = messages.empty();
62     ASSERT_FALSE(hasParent);
63     ASSERT_EQ(messages.size(), 1);
64 
65     ASSERT_EQ(messages[0].Invariant(), check);
66 }
67 
TEST_F(ASTVerifierTest, NullRange)68 TEST_F(ASTVerifierTest, NullRange)
69 {
70     ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
71     StringLiteral emptyNode;
72 
73     const auto check = "NodeHasSourceRange";
74     auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
75     checks.insert(check);
76     const auto &messages = verifier.Verify(&emptyNode, checks);
77     bool hasSourceRange = messages.empty();
78     ASSERT_FALSE(hasSourceRange);
79     ASSERT_EQ(messages.size(), 1);
80 
81     ASSERT_EQ(messages[0].Invariant(), check);
82 }
83 
TEST_F(ASTVerifierTest, NullType)84 TEST_F(ASTVerifierTest, NullType)
85 {
86     ASTVerifier verifier {Allocator()};
87     StringLiteral emptyNode;
88 
89     auto check = "NodeHasType";
90     auto checks = InvariantNameSet {};
91     checks.insert(check);
92     const auto &messages = verifier.Verify(&emptyNode, checks);
93     bool hasType = messages.empty();
94     ASSERT_EQ(hasType, false);
95     ASSERT_NE(messages.size(), 0);
96 
97     ASSERT_EQ(messages[0].Invariant(), check);
98 }
99 
TEST_F(ASTVerifierTest, WithoutScope)100 TEST_F(ASTVerifierTest, WithoutScope)
101 {
102     ASTVerifier verifier {Allocator()};
103     StringLiteral emptyNode;
104 
105     auto checks = InvariantNameSet {};
106     checks.insert("VariableHasScope");
107     const auto &messages = verifier.Verify(&emptyNode, checks);
108 
109     ASSERT_EQ(messages.size(), 0);
110 }
111 
TEST_F(ASTVerifierTest, ScopeTest)112 TEST_F(ASTVerifierTest, ScopeTest)
113 {
114     ASTVerifier verifier {Allocator()};
115     Identifier ident(StringView("var_decl"), Allocator());
116     LetDecl decl("test", &ident);
117     LocalVariable local(&decl, VariableFlags::LOCAL);
118     ident.SetVariable(&local);
119 
120     LocalScope scope(Allocator(), nullptr);
121     FunctionScope parentScope(Allocator(), nullptr);
122     scope.SetParent(&parentScope);
123     scope.AddDecl(Allocator(), &decl, ScriptExtension::ETS);
124     scope.BindNode(&ident);
125 
126     local.SetScope(&scope);
127 
128     auto checks = InvariantNameSet {};
129     checks.insert("VariableHasScope");
130     const auto &messages = verifier.Verify(&ident, checks);
131 
132     ASSERT_EQ(messages.size(), 0);
133 }
134 
TEST_F(ASTVerifierTest, ScopeNodeTest)135 TEST_F(ASTVerifierTest, ScopeNodeTest)
136 {
137     ASTVerifier verifier {Allocator()};
138     Identifier ident(StringView("var_decl"), Allocator());
139     LetDecl decl("test", &ident);
140     LocalVariable local(&decl, VariableFlags::LOCAL);
141     ident.SetVariable(&local);
142 
143     LocalScope scope(Allocator(), nullptr);
144     FunctionScope parentScope(Allocator(), nullptr);
145     scope.SetParent(&parentScope);
146     scope.AddDecl(Allocator(), &decl, ScriptExtension::ETS);
147     scope.BindNode(&ident);
148     parentScope.BindNode(&ident);
149 
150     local.SetScope(&scope);
151 
152     auto checks = InvariantNameSet {};
153     checks.insert("VariableHasEnclosingScope");
154     const auto &messages = verifier.Verify(&ident, checks);
155 
156     ASSERT_EQ(messages.size(), 0);
157 }
158 
TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1)159 TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect1)
160 {
161     ETSChecker etschecker {};
162     ASTVerifier verifier {Allocator()};
163     auto program = Program::NewProgram<ETSBinder>(Allocator());
164     auto parser = ETSParser(&program, CompilerOptions {});
165 
166     auto left = NumberLiteral(Number {1});
167     auto right = NumberLiteral(Number {6});
168     auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_PLUS);
169 
170     left.SetTsType(etschecker.GlobalIntType());
171     right.SetTsType(etschecker.GlobalIntType());
172 
173     auto checks = InvariantNameSet {};
174     checks.insert("ArithmeticOperationValid");
175     const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
176     ASSERT_EQ(messages.size(), 0);
177 }
178 
TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2)179 TEST_F(ASTVerifierTest, ArithmeticExpressionCorrect2)
180 {
181     ETSChecker etschecker {};
182     ASTVerifier verifier {Allocator()};
183     auto program = Program::NewProgram<ETSBinder>(Allocator());
184     auto parser = ETSParser(&program, CompilerOptions {});
185 
186     constexpr uint32_t LEFT1_PARAM = 1;
187     constexpr uint32_t LEFT2_PARAM = 12;
188     constexpr uint32_t RIGHT2_PARAM = 6;
189     auto left1 = NumberLiteral(Number {LEFT1_PARAM});
190     auto left2 = NumberLiteral(Number {LEFT2_PARAM});
191     auto right2 = NumberLiteral(Number {RIGHT2_PARAM});
192     auto right1 = BinaryExpression(&left2, &right2, TokenType::PUNCTUATOR_MULTIPLY);
193     auto arithmeticExpression = BinaryExpression(&left1, &right1, TokenType::PUNCTUATOR_PLUS);
194 
195     left1.SetTsType(etschecker.GlobalIntType());
196     right1.SetTsType(etschecker.GlobalIntType());
197     left2.SetTsType(etschecker.GlobalIntType());
198     right2.SetTsType(etschecker.GlobalIntType());
199 
200     auto checks = InvariantNameSet {};
201     checks.insert("ArithmeticOperationValid");
202     const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
203     ASSERT_EQ(messages.size(), 0);
204 }
205 
TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1)206 TEST_F(ASTVerifierTest, ArithmeticExpressionNegative1)
207 {
208     ETSChecker etschecker {};
209     ASTVerifier verifier {Allocator()};
210     auto program = Program::NewProgram<ETSBinder>(Allocator());
211     auto parser = ETSParser(&program, CompilerOptions {});
212 
213     const StringView leftParam("1");
214     constexpr uint32_t RIGHT_PARAM = 1;
215     auto left = StringLiteral(leftParam);
216     auto right = NumberLiteral(Number {RIGHT_PARAM});
217     auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_DIVIDE);
218 
219     left.SetTsType(etschecker.GlobalETSStringLiteralType());
220     right.SetTsType(etschecker.GlobalIntType());
221 
222     auto checks = InvariantNameSet {};
223     checks.insert("ArithmeticOperationValid");
224     const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
225 
226     ASSERT_EQ(messages.size(), 1);
227 }
228 
TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2)229 TEST_F(ASTVerifierTest, ArithmeticExpressionNegative2)
230 {
231     ETSChecker etschecker {};
232     ASTVerifier verifier {Allocator()};
233     auto program = Program::NewProgram<ETSBinder>(Allocator());
234     auto parser = ETSParser(&program, CompilerOptions {});
235     auto left = BooleanLiteral(true);
236     auto right = NumberLiteral(Number {1});
237     auto arithmeticExpression = BinaryExpression(&left, &right, TokenType::PUNCTUATOR_DIVIDE);
238 
239     left.SetTsType(etschecker.GlobalETSStringLiteralType());
240     right.SetTsType(etschecker.GlobalIntType());
241 
242     auto checks = InvariantNameSet {};
243     checks.insert("ArithmeticOperationValid");
244     const auto &messages = verifier.Verify(arithmeticExpression.AsBinaryExpression(), checks);
245 
246     ASSERT_EQ(messages.size(), 1);
247 }
248 
TEST_F(ASTVerifierTest, SequenceExpressionType)249 TEST_F(ASTVerifierTest, SequenceExpressionType)
250 {
251     ASTVerifier verifier {Allocator()};
252     auto checker = ETSChecker();
253     auto *last = Tree(Node<NumberLiteral>(Number {3}));
254     auto *sequenceExpression = Tree(Node<SequenceExpression>(
255         Nodes<Expression>(Node<NumberLiteral>(Number {1}), Node<NumberLiteral>(Number {2}), last)));
256 
257     last->SetTsType(checker.GlobalIntType());
258     sequenceExpression->SetTsType(checker.GlobalIntType());
259 
260     auto checks = InvariantNameSet {};
261     checks.insert("SequenceExpressionHasLastType");
262     const auto &messages = verifier.Verify(sequenceExpression, checks);
263 
264     ASSERT_EQ(messages.size(), 0);
265 }
266 
TEST_F(ASTVerifierTest, VariableNameIdentifierNameSameNegative)267 TEST_F(ASTVerifierTest, VariableNameIdentifierNameSameNegative)
268 {
269     ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
270     char const *text = R"(
271         function main(): void {
272             let tmp = 1;
273             let lambda2: (value: int) => Int = (value: int): Int => {
274                 let a = 42;
275                 let num: Int = new Int(a + value);
276                 return num;
277             }
278             let n_tmp = tmp + 2;
279             return 1;
280         }
281     )";
282 
283     es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
284     impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
285 
286     auto *ast = reinterpret_cast<ark::es2panda::ir::ETSScript *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
287 
288     // Note(@kirillbychkov): Change Identifier name in variable lambda2
289     ast->AsETSScript()
290         ->Statements()[0]
291         ->AsClassDeclaration()
292         ->Definition()
293         ->AsClassDefinition()
294         ->Body()[1]
295         ->AsClassElement()
296         ->Value()
297         ->AsFunctionExpression()
298         ->Function()
299         ->AsScriptFunction()
300         ->Body()
301         ->AsBlockStatement()
302         ->Statements()[1]
303         ->AsVariableDeclaration()
304         ->Declarators()[0]
305         ->AsVariableDeclarator()
306         ->Id()
307         ->AsIdentifier()
308         ->SetName("not_name");
309 
310     const auto check = "VariableNameIdentifierNameSameForAll";
311     auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
312     checks.insert(check);
313     const auto &messages = verifier.Verify(ast, checks);
314     ASSERT_EQ(messages.size(), 1);
315 
316     ASSERT_EQ(messages[0].Invariant(), check);
317 
318     impl_->DestroyContext(ctx);
319 }
320 
TEST_F(ASTVerifierTest, VariableNameIdentifierNameSame)321 TEST_F(ASTVerifierTest, VariableNameIdentifierNameSame)
322 {
323     ark::es2panda::compiler::ast_verifier::ASTVerifier verifier {Allocator()};
324     char const *text = R"(
325         function main(): void {
326             let tmp = 1;
327             let lambda2: (value: int) => Int = (value: int): Int => {
328                 let a = 42;
329                 let num: Int = new Int(a + value);
330                 return num;
331             }
332             let n_tmp = tmp + 2;
333             return 1;
334         }
335     )";
336 
337     es2panda_Context *ctx = impl_->CreateContextFromString(cfg_, text, "dummy.sts");
338     impl_->ProceedToState(ctx, ES2PANDA_STATE_CHECKED);
339 
340     auto *ast = reinterpret_cast<ark::es2panda::ir::ETSScript *>(impl_->ProgramAst(impl_->ContextProgram(ctx)));
341 
342     auto checks = ark::es2panda::compiler::ast_verifier::InvariantNameSet {};
343     checks.insert("VariableNameIdentifierNameSameForAll");
344     const auto &messages = verifier.Verify(ast, checks);
345     ASSERT_EQ(messages.size(), 0);
346     impl_->DestroyContext(ctx);
347 }
348