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 "util.h"
17 #include <algorithm>
18 #include <cstddef>
19 #include <functional>
20 #include <ostream>
21 #include <string>
22 #include "macros.h"
23
24 static es2panda_Impl *g_implPtr = nullptr;
25
GetImpl()26 es2panda_Impl *GetImpl()
27 {
28 if (g_implPtr != nullptr) {
29 return g_implPtr;
30 }
31
32 std::string soName = ark::os::library_loader::DYNAMIC_LIBRARY_PREFIX + std::string("es2panda-public") +
33 ark::os::library_loader::DYNAMIC_LIBRARY_SUFFIX;
34 auto libraryRes = ark::os::library_loader::Load(soName);
35 if (!libraryRes.HasValue()) {
36 std::cout << "Error in load lib" << std::endl;
37 return nullptr;
38 }
39
40 auto library = std::move(libraryRes.Value());
41 auto getImpl = ark::os::library_loader::ResolveSymbol(library, "es2panda_GetImpl");
42 if (!getImpl.HasValue()) {
43 std::cout << "Error in load func get g_implPtr" << std::endl;
44 return nullptr;
45 }
46
47 auto getImplFunc = reinterpret_cast<const es2panda_Impl *(*)(int)>(getImpl.Value());
48 if (getImplFunc != nullptr) {
49 g_implPtr = const_cast<es2panda_Impl *>(getImplFunc(ES2PANDA_LIB_VERSION));
50 return g_implPtr;
51 }
52 return nullptr;
53 }
54
CheckForErrors(const std::string & stateName,es2panda_Context * context)55 void CheckForErrors(const std::string &stateName, es2panda_Context *context)
56 {
57 if (g_implPtr->ContextState(context) == ES2PANDA_STATE_ERROR) {
58 std::cout << "PROCEED TO " << stateName << " ERROR" << std::endl;
59 std::cout << g_implPtr->ContextErrorMessage(context) << std::endl;
60 } else {
61 std::cout << "PROCEED TO " << stateName << " SUCCESS" << std::endl;
62 }
63 }
64
CreateIdentifierFromString(es2panda_Context * context,const std::string_view & name)65 es2panda_AstNode *CreateIdentifierFromString(es2panda_Context *context, const std::string_view &name)
66 {
67 auto impl = GetImpl();
68 auto *memForName = static_cast<char *>(impl->AllocMemory(context, name.size() + 1, 1));
69 std::copy_n(name.data(), name.size() + 1, memForName);
70 auto *identifier = impl->CreateIdentifier1(context, memForName);
71 return identifier;
72 }
73
AppendStatementToProgram(es2panda_Context * context,es2panda_AstNode * program,es2panda_AstNode * newStatement)74 void AppendStatementToProgram(es2panda_Context *context, es2panda_AstNode *program, es2panda_AstNode *newStatement)
75 {
76 auto impl = GetImpl();
77 size_t sizeOfStatements = 0;
78 auto *statements = impl->BlockStatementStatements(context, program, &sizeOfStatements);
79 auto **newStatements =
80 static_cast<es2panda_AstNode **>(impl->AllocMemory(context, sizeOfStatements + 1, sizeof(es2panda_AstNode *)));
81 for (size_t i = 0; i < sizeOfStatements; i++) {
82 newStatements[i] = statements[i];
83 }
84 newStatements[sizeOfStatements] = newStatement;
85 impl->BlockStatementSetStatements(context, program, newStatements, sizeOfStatements + 1);
86 impl->AstNodeSetParent(context, newStatement, program);
87 }
88
PrependStatementToProgram(es2panda_Context * context,es2panda_AstNode * program,es2panda_AstNode * newStatement)89 void PrependStatementToProgram(es2panda_Context *context, es2panda_AstNode *program, es2panda_AstNode *newStatement)
90 {
91 auto impl = GetImpl();
92 size_t sizeOfStatements = 0;
93 auto *statements = impl->BlockStatementStatements(context, program, &sizeOfStatements);
94 auto **newStatements =
95 static_cast<es2panda_AstNode **>(impl->AllocMemory(context, sizeOfStatements + 1, sizeof(es2panda_AstNode *)));
96 for (size_t i = 0; i < sizeOfStatements; i++) {
97 newStatements[i + 1] = statements[i];
98 }
99 newStatements[0] = newStatement;
100 impl->BlockStatementSetStatements(context, program, newStatements, sizeOfStatements + 1);
101 impl->AstNodeSetParent(context, newStatement, program);
102 }
103
GetPhaseName(es2panda_ContextState state)104 static const char *GetPhaseName(es2panda_ContextState state)
105 {
106 switch (state) {
107 case ES2PANDA_STATE_NEW:
108 return "NEW";
109 case ES2PANDA_STATE_PARSED:
110 return "PARSE";
111 case ES2PANDA_STATE_BOUND:
112 return "BOUND";
113 case ES2PANDA_STATE_CHECKED:
114 return "CHECKED";
115 case ES2PANDA_STATE_LOWERED:
116 return "LOWERED";
117 case ES2PANDA_STATE_ASM_GENERATED:
118 return "ASM";
119 case ES2PANDA_STATE_BIN_GENERATED:
120 return "BIN";
121 case ES2PANDA_STATE_ERROR:
122 return "ERROR";
123 default:
124 return "NON_VALID_STATE";
125 }
126 }
127
IsAllowedStage(es2panda_ContextState state)128 static bool IsAllowedStage(es2panda_ContextState state)
129 {
130 switch (state) {
131 case ES2PANDA_STATE_NEW:
132 return false;
133 case ES2PANDA_STATE_ERROR:
134 return false;
135 default:
136 return true;
137 }
138 }
139
DestroyTest(es2panda_Context * context,es2panda_Config * config,const int exitCode)140 static int DestroyTest(es2panda_Context *context, es2panda_Config *config, const int exitCode)
141 {
142 g_implPtr->DestroyContext(context);
143 g_implPtr->DestroyConfig(config);
144 return exitCode;
145 }
146
RunAllStagesWithTestFunction(ProccedToStatePluginTestData & data)147 int RunAllStagesWithTestFunction(ProccedToStatePluginTestData &data)
148 {
149 if (data.argc < MIN_ARGC) {
150 return INVALID_ARGC_ERROR_CODE;
151 }
152
153 if (GetImpl() == nullptr) {
154 return NULLPTR_IMPL_ERROR_CODE;
155 }
156 *data.impl = GetImpl();
157 std::cout << "LOAD SUCCESS" << std::endl;
158 const char **args = const_cast<const char **>(&(data.argv[1]));
159 auto config = g_implPtr->CreateConfig(data.argc - 1, args);
160 es2panda_Context *context = nullptr;
161 if (data.fromSource) {
162 context = g_implPtr->CreateContextFromString(config, data.source.data(), data.argv[data.argc - 1]);
163 } else {
164 context = g_implPtr->CreateContextFromFile(config, data.argv[data.argc - 1]);
165 }
166 if (context == nullptr) {
167 std::cerr << "FAILED TO CREATE CONTEXT" << std::endl;
168 return NULLPTR_CONTEXT_ERROR_CODE;
169 }
170
171 for (auto [testStage, _] : data.testFunctions) {
172 if (!IsAllowedStage(testStage)) {
173 return DestroyTest(context, config, TEST_ERROR_CODE);
174 }
175 }
176
177 for (auto state = ES2PANDA_STATE_PARSED; state <= ES2PANDA_STATE_BIN_GENERATED;
178 state = static_cast<es2panda_ContextState>(state + 1)) {
179 if (!IsAllowedStage(state)) {
180 continue;
181 }
182 g_implPtr->ProceedToState(context, state);
183 CheckForErrors(GetPhaseName(state), context);
184 for (const auto &testFunc : data.testFunctions[state]) {
185 if (!testFunc(context)) {
186 return DestroyTest(context, config, TEST_ERROR_CODE);
187 }
188 }
189 if (state == data.exitAfterState) {
190 break;
191 }
192 }
193
194 int result = g_implPtr->ContextState(context) == ES2PANDA_STATE_ERROR ? PROCEED_ERROR_CODE : 0;
195 return DestroyTest(context, config, result);
196 }
197
Test(es2panda_Context * context,es2panda_Impl * impl,int stage,const std::function<bool (es2panda_Context *,es2panda_AstNode *)> & handle)198 int Test(es2panda_Context *context, es2panda_Impl *impl, int stage,
199 const std::function<bool(es2panda_Context *, es2panda_AstNode *)> &handle)
200 {
201 impl->ProceedToState(context, ES2PANDA_STATE_PARSED);
202 CheckForErrors("PARSE", context);
203 es2panda_AstNode *ast = impl->ProgramAst(context, impl->ContextProgram(context));
204 if (ast == nullptr) {
205 return TEST_ERROR_CODE;
206 }
207 switch (stage) {
208 case ES2PANDA_STATE_PARSED: {
209 break;
210 }
211 case ES2PANDA_STATE_BOUND: {
212 impl->ProceedToState(context, ES2PANDA_STATE_BOUND);
213 CheckForErrors("BOUND", context);
214 break;
215 }
216 case ES2PANDA_STATE_CHECKED: {
217 impl->ProceedToState(context, ES2PANDA_STATE_CHECKED);
218 CheckForErrors("CHECKED", context);
219 break;
220 }
221 case ES2PANDA_STATE_LOWERED: {
222 impl->ProceedToState(context, ES2PANDA_STATE_LOWERED);
223 CheckForErrors("LOWERED", context);
224 break;
225 }
226 default: {
227 UNREACHABLE();
228 }
229 }
230 if (impl->ContextState(context) == ES2PANDA_STATE_ERROR) {
231 return PROCEED_ERROR_CODE;
232 }
233 if (!handle(context, ast)) {
234 return TEST_ERROR_CODE;
235 }
236 if (stage == ES2PANDA_STATE_BOUND) {
237 impl->AstNodeRebind(context, ast);
238 } else if (stage == ES2PANDA_STATE_CHECKED || stage == ES2PANDA_STATE_LOWERED) {
239 impl->AstNodeRecheck(context, ast);
240 }
241 impl->ProceedToState(context, ES2PANDA_STATE_BIN_GENERATED);
242 CheckForErrors("BIN", context);
243 if (impl->ContextState(context) == ES2PANDA_STATE_ERROR) {
244 return PROCEED_ERROR_CODE;
245 }
246 impl->DestroyContext(context);
247 return 0;
248 }
249