1 /**
2 * Copyright (c) 2021-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 <gtest/gtest.h>
17 #include <algorithm>
18 #include <cstddef>
19 #include <cstdlib>
20 #include <fstream>
21 #include <ios>
22 #include <memory>
23 #include <optional>
24 #include <sstream>
25 #include <string>
26
27 #include "assembler/assembly-field.h"
28 #include "assembler/assembly-function.h"
29 #include "assembler/assembly-program.h"
30 #include "assembler/assembly-record.h"
31 #include "assembler/assembly-emitter.h"
32 #include "assembler/assembly-parser.h"
33 #include "assembler/ins_create_api.h"
34 #include "libpandafile/file_item_container.h"
35 #include "libpandafile/file_writer.h"
36 #include "include/runtime_options.h"
37 #include "linker.h"
38 #include "linker_context.h"
39 #include "linker_context_misc.cpp"
40 #include "runtime/include/runtime.h"
41 #include "source_lang_enum.h"
42
43 namespace {
44 using Config = ark::static_linker::Config;
45 using Result = ark::static_linker::Result;
46 using ark::static_linker::DefaultConfig;
47 using ark::static_linker::Link;
48
49 #ifdef TEST_STATIC_LINKER_WITH_STS
50 constexpr std::string_view ABC_FILE_EXTENSION = ".abc";
51 constexpr size_t ABC_FILE_EXTENSION_LENGTH = 4;
52 #endif
53 constexpr size_t TEST_REPEAT_COUNT = 10;
54
ExecPanda(const std::string & file,const std::string & loadRuntimes="core",const std::string & entryPoint="_GLOBAL::main")55 std::pair<int, std::string> ExecPanda(const std::string &file, const std::string &loadRuntimes = "core",
56 const std::string &entryPoint = "_GLOBAL::main")
57 {
58 auto opts = ark::RuntimeOptions {};
59 if (loadRuntimes == "ets") {
60 opts.SetBootPandaFiles({"../../plugins/ets/etsstdlib.abc"});
61 } else if (loadRuntimes == "core") {
62 auto boot = opts.GetBootPandaFiles();
63 for (auto &a : boot) {
64 a.insert(0, "../");
65 }
66 opts.SetBootPandaFiles(std::move(boot));
67 } else {
68 return {1, "unknown loadRuntimes " + loadRuntimes};
69 }
70
71 opts.SetLoadRuntimes({loadRuntimes});
72
73 opts.SetPandaFiles({file});
74 if (!ark::Runtime::Create(opts)) {
75 return {1, "can't create runtime"};
76 }
77
78 auto *runtime = ark::Runtime::GetCurrent();
79
80 std::stringstream strBuf;
81 auto old = std::cout.rdbuf(strBuf.rdbuf());
82 auto reset = [&old](auto *cout) { cout->rdbuf(old); };
83 auto guard = std::unique_ptr<std::ostream, decltype(reset)>(&std::cout, reset);
84
85 auto res = runtime->ExecutePandaFile(file, entryPoint, {});
86 auto ret = std::pair<int, std::string> {};
87 if (!res) {
88 ret = {1, "error " + std::to_string((int)res.Error())};
89 } else {
90 ret = {0, strBuf.str()};
91 }
92
93 if (!ark::Runtime::Destroy()) {
94 return {1, "can't destroy runtime"};
95 }
96
97 return ret;
98 }
99
100 template <bool IS_BINARY>
ReadFile(const std::string & path,std::conditional_t<IS_BINARY,std::vector<char>,std::string> & out)101 bool ReadFile(const std::string &path, std::conditional_t<IS_BINARY, std::vector<char>, std::string> &out)
102 {
103 auto f = std::ifstream(path, IS_BINARY ? std::ios_base::binary : std::ios_base::in);
104 if (!f.is_open() || f.bad()) {
105 return false;
106 }
107
108 out.clear();
109
110 out.assign((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
111 return true;
112 }
113
114 // removes comments
NormalizeGold(std::string & gold)115 void NormalizeGold(std::string &gold)
116 {
117 std::string_view in = gold;
118 std::string out;
119 out.reserve(gold.size());
120 while (!in.empty()) {
121 auto nxtNl = in.find('\n');
122 if (in[0] == '#') {
123 if (nxtNl == std::string::npos) {
124 break;
125 }
126 in = in.substr(nxtNl + 1);
127 continue;
128 }
129 if (nxtNl == std::string::npos) {
130 out += in;
131 break;
132 }
133 out += in.substr(0, nxtNl + 1);
134 in = in.substr(nxtNl + 1);
135 }
136 gold = std::move(out);
137 }
138
Build(const std::string & path)139 std::optional<std::string> Build(const std::string &path)
140 {
141 std::string prog;
142
143 if (!ReadFile<false>(path + ".pa", prog)) {
144 return "can't read file " + path + ".pa";
145 }
146 ark::pandasm::Parser p;
147 auto res = p.Parse(prog, path + ".pa");
148 if (p.ShowError().err != ark::pandasm::Error::ErrorType::ERR_NONE) {
149 return p.ShowError().message + "\n" + p.ShowError().wholeLine;
150 }
151
152 if (!res.HasValue()) {
153 return "no parsed value";
154 }
155
156 auto writer = ark::panda_file::FileWriter(path + ".abc");
157 if (!ark::pandasm::AsmEmitter::Emit(&writer, res.Value())) {
158 return "can't emit";
159 }
160
161 return std::nullopt;
162 }
163
TestSingle(const std::string & path,bool isGood=true,const Config & conf=ark::static_linker::DefaultConfig (),bool * succeded=nullptr,Result * saveResult=nullptr)164 void TestSingle(const std::string &path, bool isGood = true, const Config &conf = ark::static_linker::DefaultConfig(),
165 bool *succeded = nullptr, Result *saveResult = nullptr)
166 {
167 const auto pathPrefix = "data/single/";
168 ASSERT_EQ(Build(pathPrefix + path), std::nullopt);
169 auto gold = std::string {};
170 ASSERT_TRUE(ReadFile<false>(pathPrefix + path + ".gold", gold));
171
172 NormalizeGold(gold);
173
174 const auto out = pathPrefix + path + ".linked.abc";
175 auto linkRes = Link(conf, out, {pathPrefix + path + ".abc"});
176
177 ASSERT_EQ(linkRes.errors.empty(), isGood);
178
179 if (isGood) {
180 auto res = ExecPanda(out);
181 ASSERT_EQ(res.first, 0);
182 ASSERT_EQ(res.second, gold);
183 }
184
185 if (succeded != nullptr) {
186 *succeded = true;
187 }
188
189 if (saveResult != nullptr) {
190 *saveResult = std::move(linkRes);
191 }
192 }
193
194 struct TestData {
195 std::string pathPrefix;
196 bool isGood = false;
197 Result *expected = nullptr;
198 std::string gold;
199 };
200
PerformTest(TestData * data,const std::vector<std::string> & perms,const Config & conf,std::optional<std::vector<char>> expectedFile,size_t iteration)201 void PerformTest(TestData *data, const std::vector<std::string> &perms, const Config &conf,
202 std::optional<std::vector<char>> expectedFile, size_t iteration)
203 {
204 auto out = data->pathPrefix + "linked.";
205 auto files = std::vector<std::string> {};
206
207 for (const auto &f : perms) {
208 out += f;
209 out += ".";
210 files.emplace_back(data->pathPrefix + f + ".abc");
211 }
212 out += "it";
213 out += std::to_string(iteration);
214 out += ".abc";
215
216 SCOPED_TRACE(out);
217
218 auto linkRes = Link(conf, out, files);
219 if (linkRes.errors.empty() != data->isGood) {
220 auto errs = std::string();
221 for (auto &err : linkRes.errors) {
222 errs += err;
223 errs += "\n";
224 }
225 ASSERT_EQ(linkRes.errors.empty(), data->isGood) << errs;
226 }
227
228 if (data->expected != nullptr) {
229 ASSERT_EQ(linkRes.stats.deduplicatedForeigners, data->expected->stats.deduplicatedForeigners);
230 }
231
232 if (data->isGood) {
233 std::vector<char> gotFile;
234 ASSERT_TRUE(ReadFile<true>(out, gotFile));
235 if (!expectedFile.has_value()) {
236 expectedFile = std::move(gotFile);
237 } else {
238 (void)iteration;
239 ASSERT_EQ(expectedFile.value(), gotFile) << "on iteration: " << iteration;
240 }
241
242 auto res = ExecPanda(out);
243 ASSERT_EQ(res.first, 0);
244 ASSERT_EQ(res.second, data->gold);
245 }
246 }
247
TestMultiple(const std::string & path,std::vector<std::string> perms,bool isGood=true,const Config & conf=ark::static_linker::DefaultConfig (),Result * expected=nullptr)248 void TestMultiple(const std::string &path, std::vector<std::string> perms, bool isGood = true,
249 const Config &conf = ark::static_linker::DefaultConfig(), Result *expected = nullptr)
250 {
251 std::sort(perms.begin(), perms.end());
252
253 const auto pathPrefix = "data/multi/" + path + "/";
254
255 for (const auto &p : perms) {
256 ASSERT_EQ(Build(pathPrefix + p), std::nullopt);
257 }
258
259 auto gold = std::string {};
260
261 if (isGood) {
262 ASSERT_TRUE(ReadFile<false>(pathPrefix + "out.gold", gold));
263 NormalizeGold(gold);
264 }
265
266 std::optional<std::vector<char>> expectedFile;
267
268 do {
269 expectedFile = std::nullopt;
270 TestData data;
271 data.pathPrefix = pathPrefix;
272 data.isGood = isGood;
273 data.expected = expected;
274 data.gold = gold;
275 for (size_t iteration = 0; iteration < TEST_REPEAT_COUNT; iteration++) {
276 PerformTest(&data, perms, conf, expectedFile, iteration);
277 }
278 } while (std::next_permutation(perms.begin(), perms.end()));
279 }
280
281 #ifdef TEST_STATIC_LINKER_WITH_STS
282 // NOLINTBEGIN(misc-non-private-member-variables-in-classes)
283 struct StripOptions {
284 bool stripUnused = false;
285 std::optional<std::string> stripUnusedSkiplist;
IsEmpty__anonc583d2df0111::StripOptions286 bool IsEmpty() const
287 {
288 return !stripUnused && !stripUnusedSkiplist.has_value();
289 }
290 StripOptions() = default;
291 };
292
293 struct TestStsConfig {
294 std::string entryPoint = "1/ETSGLOBAL::main";
295 std::string outputFileName = "out.gold";
296 TestStsConfig() = default;
297 };
298 // NOLINTEND(misc-non-private-member-variables-in-classes)
299
BuildLinkerCommand(const std::string & path,const std::vector<std::string> & files,const StripOptions & stripOptions,const std::string & targetPathPrefix)300 std::string BuildLinkerCommand(const std::string &path, const std::vector<std::string> &files,
301 const StripOptions &stripOptions, const std::string &targetPathPrefix)
302 {
303 std::string linkerCommand = "../../bin/ark_link";
304
305 if (stripOptions.stripUnused) {
306 linkerCommand.append(" --strip-unused");
307 }
308
309 if (stripOptions.stripUnusedSkiplist.has_value()) {
310 linkerCommand.append(" --strip-unused-skiplist=");
311 linkerCommand.append(stripOptions.stripUnusedSkiplist.value());
312 }
313
314 linkerCommand.append(" --output " + targetPathPrefix + "linked.abc");
315 linkerCommand.append(" -- ");
316 for (const auto &f : files) {
317 if (f.length() < ABC_FILE_EXTENSION_LENGTH ||
318 f.substr(f.length() - ABC_FILE_EXTENSION_LENGTH) != ABC_FILE_EXTENSION) {
319 linkerCommand.push_back('@'); // test filesinfo
320 }
321 linkerCommand.append(path + f);
322 linkerCommand.push_back(' ');
323 }
324
325 return linkerCommand;
326 }
327
TestSts(const std::string & path,const std::vector<std::string> & files,bool isGood=true,const StripOptions & deleteOptions={},const TestStsConfig & config={})328 void TestSts(const std::string &path, const std::vector<std::string> &files, bool isGood = true,
329 const StripOptions &deleteOptions = {}, const TestStsConfig &config = {})
330 {
331 const auto sourcePathPrefix = "data/ets/" + path + "/";
332 const auto targetPathPrefix = "data/output/" + path + "/";
333
334 std::string linkerCommand = BuildLinkerCommand(sourcePathPrefix, files, deleteOptions, targetPathPrefix);
335 // NOLINTNEXTLINE(cert-env33-c)
336 auto linkRes = std::system(linkerCommand.c_str());
337
338 if (isGood) {
339 ASSERT_EQ(linkRes, 0);
340 auto gold = std::string {};
341 ASSERT_TRUE(ReadFile<false>(sourcePathPrefix + config.outputFileName, gold));
342 NormalizeGold(gold);
343 auto ret = ExecPanda(targetPathPrefix + "linked.abc", "ets", config.entryPoint);
344 ASSERT_EQ(ret.first, 0);
345 ASSERT_EQ(ret.second, gold);
346 } else {
347 ASSERT_NE(linkRes, 0);
348 }
349 }
350 #endif
351
TEST(linkertests,HelloWorld)352 TEST(linkertests, HelloWorld)
353 {
354 TestSingle("hello_world");
355 }
356
TEST(linkertests,LitArray)357 TEST(linkertests, LitArray)
358 {
359 TestSingle("lit_array");
360 }
361
TEST(linkertests,Exceptions)362 TEST(linkertests, Exceptions)
363 {
364 TestSingle("exceptions");
365 }
366
TEST(linkertests,ForeignMethod)367 TEST(linkertests, ForeignMethod)
368 {
369 TestMultiple("fmethod", {"1", "2"});
370 }
371
TEST(linkertests,ForeignField)372 TEST(linkertests, ForeignField)
373 {
374 TestMultiple("ffield", {"1", "2"});
375 }
376
TEST(linkertests,BadForeignField)377 TEST(linkertests, BadForeignField)
378 {
379 TestMultiple("bad_ffield", {"1", "2"}, false);
380 }
381
TEST(linkertests,BadClassRedefinition)382 TEST(linkertests, BadClassRedefinition)
383 {
384 TestMultiple("bad_class_redefinition", {"1", "2"}, false);
385 }
386
TEST(linkertests,BadFFieldType)387 TEST(linkertests, BadFFieldType)
388 {
389 TestMultiple("bad_ffield_type", {"1", "2"}, false);
390 }
391
TEST(linkertests,FMethodOverloaded)392 TEST(linkertests, FMethodOverloaded)
393 {
394 TestMultiple("fmethod_overloaded", {"1", "2"});
395 }
396
TEST(linkertests,FMethodOverloaded2)397 TEST(linkertests, FMethodOverloaded2)
398 {
399 TestMultiple("fmethod_overloaded_2", {"1", "2", "3", "4"});
400 }
401
TEST(linkertests,BadFMethodOverloaded)402 TEST(linkertests, BadFMethodOverloaded)
403 {
404 TestMultiple("bad_fmethod_overloaded", {"1", "2"}, false);
405 }
406
TEST(linkertests,DeduplicatedField)407 TEST(linkertests, DeduplicatedField)
408 {
409 auto res = Result {};
410 res.stats.deduplicatedForeigners = 1;
411 TestMultiple("dedup_field", {"1", "2"}, true, DefaultConfig(), &res);
412 }
413
TEST(linkertests,DeduplicatedMethod)414 TEST(linkertests, DeduplicatedMethod)
415 {
416 auto res = Result {};
417 res.stats.deduplicatedForeigners = 1;
418 TestMultiple("dedup_method", {"1", "2"}, true, DefaultConfig(), &res);
419 }
420
TEST(linkertests,UnresolvedInGlobal)421 TEST(linkertests, UnresolvedInGlobal)
422 {
423 TestSingle("unresolved_global", false);
424 auto conf = DefaultConfig();
425 conf.remainsPartial = {std::string(ark::panda_file::ItemContainer::GetGlobalClassName())};
426 TestSingle("unresolved_global", true, conf);
427 }
428
TEST(linkertests,DeduplicateLineNumberNrogram)429 TEST(linkertests, DeduplicateLineNumberNrogram)
430 {
431 auto succ = false;
432 auto res = Result {};
433 TestSingle("lnp_dedup", true, DefaultConfig(), &succ, &res);
434 ASSERT_TRUE(succ);
435 ASSERT_EQ(res.stats.debugCount, 1);
436 }
437
TEST(linkertests,StripDebugInfo)438 TEST(linkertests, StripDebugInfo)
439 {
440 auto succ = false;
441 auto res = Result {};
442 auto conf = DefaultConfig();
443 conf.stripDebugInfo = true;
444 TestSingle("hello_world", true, conf, &succ, &res);
445 ASSERT_TRUE(succ);
446 ASSERT_EQ(res.stats.debugCount, 0);
447 }
448
TEST(linkertests,FieldOverload)449 TEST(linkertests, FieldOverload)
450 {
451 auto conf = DefaultConfig();
452 conf.partial.emplace("LFor;");
453 TestMultiple("ffield_overloaded", {"1", "2"}, true, conf);
454 }
455
TEST(linkertests,ForeignBase)456 TEST(linkertests, ForeignBase)
457 {
458 #ifdef PANDA_WITH_ETS
459 constexpr auto LANG = ark::panda_file::SourceLang::ETS;
460 auto makeRecord = [](ark::pandasm::Program &prog, const std::string &name) {
461 return &prog.recordTable.emplace(name, ark::pandasm::Record(name, LANG)).first->second;
462 };
463
464 const std::string basePath = "data/multi/ForeignBase.1.abc";
465 const std::string dervPath = "data/multi/ForeignBase.2.abc";
466
467 {
468 ark::pandasm::Program progBase;
469 auto base = makeRecord(progBase, "Base");
470 auto fld = ark::pandasm::Field(LANG);
471 fld.name = "fld";
472 fld.type = ark::pandasm::Type("i32", 0);
473 base->fieldList.push_back(std::move(fld));
474
475 ASSERT_TRUE(ark::pandasm::AsmEmitter::Emit(basePath, progBase));
476 }
477
478 {
479 ark::pandasm::Program progDer;
480 auto base = makeRecord(progDer, "Base");
481 base->metadata->SetAttribute("external");
482
483 auto derv = makeRecord(progDer, "Derv");
484 ASSERT_EQ(derv->metadata->SetAttributeValue("ets.extends", "Base"), std::nullopt);
485 std::ignore = derv;
486 auto fld = ark::pandasm::Field(LANG);
487 fld.name = "fld";
488 fld.type = ark::pandasm::Type("i32", 0);
489 fld.metadata->SetAttribute("external");
490 derv->fieldList.push_back(std::move(fld));
491
492 auto func = ark::pandasm::Function("main", LANG);
493 func.regsNum = 1;
494 func.returnType = ark::pandasm::Type("void", 0);
495 func.AddInstruction(ark::pandasm::Create_NEWOBJ(0, "Derv"));
496 func.AddInstruction(ark::pandasm::Create_LDOBJ(0, "Derv.fld"));
497 func.AddInstruction(ark::pandasm::Create_RETURN_VOID());
498
499 progDer.functionInstanceTable.emplace(func.name, std::move(func));
500
501 ASSERT_TRUE(ark::pandasm::AsmEmitter::Emit(dervPath, progDer));
502 }
503
504 auto res = Link(DefaultConfig(), "data/multi/ForeignBase.linked.abc", {basePath, dervPath});
505 ASSERT_TRUE(res.errors.empty()) << res.errors.front();
506 #endif
507 }
508
TEST(linkertests,StsHelloWorld)509 TEST(linkertests, StsHelloWorld)
510 {
511 #ifdef TEST_STATIC_LINKER_WITH_STS
512 TestSts("hello_world", {"1.ets.abc"});
513 #endif
514 }
515
TEST(linkertests,StsForeignClass)516 TEST(linkertests, StsForeignClass)
517 {
518 #ifdef TEST_STATIC_LINKER_WITH_STS
519 TestSts("fclass", {"1.ets.abc", "2.ets.abc"});
520 #endif
521 }
522
TEST(linkertests,StsForeignMethod)523 TEST(linkertests, StsForeignMethod)
524 {
525 #ifdef TEST_STATIC_LINKER_WITH_STS
526 TestSts("fmethod", {"1.ets.abc", "2.ets.abc"});
527 #endif
528 }
529
TEST(linkertests,StsFMethodOverloaded)530 TEST(linkertests, StsFMethodOverloaded)
531 {
532 #ifdef TEST_STATIC_LINKER_WITH_STS
533 TestSts("fmethod_overloaded", {"1.ets.abc", "2.ets.abc"});
534 #endif
535 }
536
TEST(linkertests,StsFMethodOverloaded2)537 TEST(linkertests, StsFMethodOverloaded2)
538 {
539 #ifdef TEST_STATIC_LINKER_WITH_STS
540 TestSts("fmethod_overloaded_2", {"1.ets.abc", "2.ets.abc", "3.ets.abc", "4.ets.abc"});
541 #endif
542 }
543
TEST(linkertests,FilesInfo)544 TEST(linkertests, FilesInfo)
545 {
546 #ifdef TEST_STATIC_LINKER_WITH_STS
547 TestSts("filesinfo", {"filesinfo.txt"});
548 #endif
549 }
550
TEST(linkertests,Mix)551 TEST(linkertests, Mix)
552 {
553 #ifdef TEST_STATIC_LINKER_WITH_STS
554 TestSts("mix", {"1.ets.abc", "2.ets.abc"});
555 #endif
556 }
557
TEST(linkertests,ClassCallNoDeleteDependency)558 TEST(linkertests, ClassCallNoDeleteDependency)
559 {
560 #ifdef TEST_STATIC_LINKER_WITH_STS
561 TestStsConfig config;
562 config.entryPoint = "dependency/ETSGLOBAL::main";
563 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, true, {}, config);
564 #endif
565 }
566
TEST(linkertests,ClassCallDeleteDependency)567 TEST(linkertests, ClassCallDeleteDependency)
568 {
569 #ifdef TEST_STATIC_LINKER_WITH_STS
570 StripOptions opts;
571 opts.stripUnused = true;
572 TestStsConfig config;
573 config.entryPoint = "dependency/ETSGLOBAL::main";
574 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, true, opts, config);
575 #endif
576 }
577
TEST(linkertests,ClassCallDeleteDependencyFromEntryInputError)578 TEST(linkertests, ClassCallDeleteDependencyFromEntryInputError)
579 {
580 #ifdef TEST_STATIC_LINKER_WITH_STS
581 StripOptions opts;
582 opts.stripUnused = true;
583 opts.stripUnusedSkiplist = "\"1/ETSGLOBAL\"";
584 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, false, opts);
585 #endif
586 }
587
TEST(linkertests,MethodCallDeleteDependencyFromEntryInputError)588 TEST(linkertests, MethodCallDeleteDependencyFromEntryInputError)
589 {
590 #ifdef TEST_STATIC_LINKER_WITH_STS
591 StripOptions opts;
592 opts.stripUnused = true;
593 opts.stripUnusedSkiplist = "\"1/ETSGLOBAL/main\"";
594 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, false, opts);
595 #endif
596 }
597
TEST(linkertests,CallDeleteDependencyFromConfigFileNotExist)598 TEST(linkertests, CallDeleteDependencyFromConfigFileNotExist)
599 {
600 #ifdef TEST_STATIC_LINKER_WITH_STS
601 StripOptions opts;
602 opts.stripUnused = true;
603 opts.stripUnusedSkiplist = "@data/ets/classcall_doublefile/methodconfig1.txt";
604 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, false, opts);
605 #endif
606 }
607
TEST(linkertests,CallDeleteDependencyFromEntryFileNotExist)608 TEST(linkertests, CallDeleteDependencyFromEntryFileNotExist)
609 {
610 #ifdef TEST_STATIC_LINKER_WITH_STS
611 StripOptions opts;
612 opts.stripUnused = true;
613 opts.stripUnusedSkiplist = "\"1.ets\"";
614 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, false, opts);
615 #endif
616 }
617
TEST(linkertests,MultiClassCallNoDeleteDependency)618 TEST(linkertests, MultiClassCallNoDeleteDependency)
619 {
620 #ifdef TEST_STATIC_LINKER_WITH_STS
621 TestStsConfig config;
622 config.entryPoint = "main_dependencyUser/ETSGLOBAL::main";
623 TestSts("classcall_multifile",
624 {"main_dependencyUser.ets.abc", "dependency.ets.abc", "user_dependency.ets.abc", "product_user.ets.abc"},
625 true, {}, config);
626 #endif
627 }
628
TEST(linkertests,MultiClassCallDeleteDependency)629 TEST(linkertests, MultiClassCallDeleteDependency)
630 {
631 #ifdef TEST_STATIC_LINKER_WITH_STS
632 StripOptions opts;
633 opts.stripUnused = true;
634 TestStsConfig config;
635 config.entryPoint = "main_dependencyUser/ETSGLOBAL::main";
636 TestSts("classcall_multifile",
637 {"main_dependencyUser.ets.abc", "dependency.ets.abc", "user_dependency.ets.abc", "product_user.ets.abc"},
638 true, opts, config);
639 #endif
640 }
641
TEST(linkertests,ClassCallDeleteDependencyAll)642 TEST(linkertests, ClassCallDeleteDependencyAll)
643 {
644 #ifdef TEST_STATIC_LINKER_WITH_STS
645 StripOptions opts;
646 opts.stripUnused = true;
647 opts.stripUnusedSkiplist = "*";
648 TestStsConfig config;
649 config.entryPoint = "dependency/ETSGLOBAL::main";
650 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, true, opts, config);
651 #endif
652 }
653
TEST(linkertests,CallDeleteDependencyNoStripUnusedArg)654 TEST(linkertests, CallDeleteDependencyNoStripUnusedArg)
655 {
656 #ifdef TEST_STATIC_LINKER_WITH_STS
657 StripOptions opts;
658 opts.stripUnusedSkiplist = "\"dependency/ETSGLOBAL/main\"";
659 TestSts("classcall_doublefile", {"dependency.ets.abc", "bedependent.ets.abc"}, false, opts);
660 #endif
661 }
662
663 #ifdef TEST_STATIC_LINKER_WITH_STS
GenLinkCmd(const std::string & param)664 std::string GenLinkCmd(const std::string ¶m)
665 {
666 std::string prefix = "../../bin/ark_link";
667 std::string suffix = " > /dev/null 2>&1";
668 return prefix + param + suffix;
669 }
670 #endif
671
TEST(linkertests,TestForCoverage)672 TEST(linkertests, TestForCoverage)
673 {
674 #ifdef TEST_STATIC_LINKER_WITH_STS
675 std::string cmd = GenLinkCmd(" --error-option");
676 // NOLINTNEXTLINE(cert-env33-c)
677 auto linkRes = std::system(cmd.c_str());
678 ASSERT_NE(linkRes, 0);
679
680 cmd = GenLinkCmd(" --output data/ets/sys/target.abc");
681 // NOLINTNEXTLINE(cert-env33-c)
682 linkRes = std::system(cmd.c_str());
683 ASSERT_NE(linkRes, 0);
684
685 cmd = GenLinkCmd(" -- data/ets/sys/1.ets.abc data/ets/sys/2.ets.abc");
686 // NOLINTNEXTLINE(cert-env33-c)
687 linkRes = std::system(cmd.c_str());
688 ASSERT_EQ(linkRes, 0);
689
690 cmd = GenLinkCmd(" -- data/ets/sys/no_exist.abc");
691 // NOLINTNEXTLINE(cert-env33-c)
692 linkRes = std::system(cmd.c_str());
693 ASSERT_NE(linkRes, 0);
694
695 cmd = GenLinkCmd(" -- data/ets/sys/1.ets.abc data/ets/sys/2.ets.abc");
696 // NOLINTNEXTLINE(cert-env33-c)
697 linkRes = std::system(cmd.c_str());
698 ASSERT_EQ(linkRes, 0);
699
700 cmd = GenLinkCmd(" --output data/ets/sys/target.abc -- data/ets/sys/1.ets.abc data/ets/sys/2.ets.abc");
701 // NOLINTNEXTLINE(cert-env33-c)
702 linkRes = std::system(cmd.c_str());
703 ASSERT_EQ(linkRes, 0);
704
705 std::string opt = " --show-stats --version --log-level info";
706 std::string dst = " --output data/ets/sys/target.abc -- data/ets/sys/1.ets.abc data/ets/sys/2.ets.abc";
707 cmd = GenLinkCmd(opt + dst);
708 // NOLINTNEXTLINE(cert-env33-c)
709 linkRes = std::system(cmd.c_str());
710 ASSERT_EQ(linkRes, 0);
711
712 cmd = GenLinkCmd(" --output data/ets/sys/target.abc -- @data/ets/sys/no_exist.txt");
713 // NOLINTNEXTLINE(cert-env33-c)
714 linkRes = std::system(cmd.c_str());
715 ASSERT_NE(linkRes, 0);
716
717 cmd = GenLinkCmd(" --output data/ets/sys/target.abc -- @data/ets/sys/file_list_exist.txt");
718 // NOLINTNEXTLINE(cert-env33-c)
719 linkRes = std::system(cmd.c_str());
720 ASSERT_EQ(linkRes, 0);
721
722 cmd = GenLinkCmd(" --output data/ets/sys/target.abc -- @data/ets/sys/file_list_noexist.txt");
723 // NOLINTNEXTLINE(cert-env33-c)
724 linkRes = std::system(cmd.c_str());
725 ASSERT_NE(linkRes, 0);
726
727 cmd = GenLinkCmd(" --output data/ets/sys/target.abc -- @data/ets/sys/file_list_errorformat.txt");
728 // NOLINTNEXTLINE(cert-env33-c)
729 linkRes = std::system(cmd.c_str());
730 ASSERT_NE(linkRes, 0);
731 #endif
732 }
733
734 constexpr uint32_t I = 32;
735 constexpr uint64_t L = 64;
736 constexpr float F = 11.1;
737 constexpr double D = 22.2;
738
TEST(linkertests,TestForReprValueItem)739 TEST(linkertests, TestForReprValueItem)
740 {
741 std::string sv1Expect = "32 as int";
742 std::stringstream ss1;
743 auto *sv1 = new ark::panda_file::ScalarValueItem(I);
744 ark::static_linker::ReprValueItem(ss1, sv1);
745 std::string sv1Res = ss1.str();
746 delete sv1;
747 ASSERT_TRUE(sv1Res == sv1Expect);
748
749 std::string sv2Expect = "64 as long";
750 std::stringstream ss2;
751 auto *sv2 = new ark::panda_file::ScalarValueItem(L);
752 ark::static_linker::ReprValueItem(ss2, sv2);
753 std::string sv2Res = ss2.str();
754 delete sv2;
755 ASSERT_TRUE(sv2Res == sv2Expect);
756
757 std::string sv3Expect = "11.1 as float";
758 std::stringstream ss3;
759 auto *sv3 = new ark::panda_file::ScalarValueItem(F);
760 ark::static_linker::ReprValueItem(ss3, sv3);
761 std::string sv3Res = ss3.str();
762 delete sv3;
763 ASSERT_TRUE(sv3Res == sv3Expect);
764
765 std::string sv4Expect = "22.2 as double";
766 std::stringstream ss4;
767 auto *sv4 = new ark::panda_file::ScalarValueItem(D);
768 ark::static_linker::ReprValueItem(ss4, sv4);
769 std::string sv4Res = ss4.str();
770 delete sv4;
771 ASSERT_TRUE(sv4Res == sv4Expect);
772
773 auto *sv5p = new ark::panda_file::ScalarValueItem(D);
774 auto *sv5 = new ark::panda_file::ScalarValueItem(sv5p);
775 std::stringstream ss5;
776 ark::static_linker::ReprValueItem(ss5, sv5);
777 std::string sv5Res = ss5.str();
778 delete sv5p;
779 delete sv5;
780 ASSERT_EQ(sv5Res, sv4Expect);
781 }
782
TEST(linkertests,TestForReprArrayValueItem)783 TEST(linkertests, TestForReprArrayValueItem)
784 {
785 std::string av1Expect = "[1 as int, 2 as int, 3 as int]";
786 std::stringstream ss6;
787 std::vector<ark::panda_file::ScalarValueItem> items;
788 items.emplace_back(static_cast<uint32_t>(1));
789 items.emplace_back(static_cast<uint32_t>(2));
790 items.emplace_back(static_cast<uint32_t>(3));
791 auto *av1 = new ark::panda_file::ArrayValueItem(ark::panda_file::Type(ark::panda_file::Type::TypeId::I32), items);
792 ark::static_linker::ReprValueItem(ss6, av1);
793 std::string av1Res = ss6.str();
794 delete av1;
795 ASSERT_TRUE(av1Res == av1Expect);
796 }
797
798 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST(linkertests,TestLoopSuperClass)799 TEST(linkertests, TestLoopSuperClass)
800 {
801 #ifdef TEST_STATIC_LINKER_WITH_STS
802 // Write panda file to memory
803 ark::panda_file::ItemContainer container;
804
805 // set current class's superclass to self
806 ark::panda_file::ClassItem *classItem = container.GetOrCreateClassItem("Bar");
807 classItem->SetAccessFlags(ark::ACC_PUBLIC);
808 classItem->SetSuperClass(classItem);
809
810 // Add interface
811 ark::panda_file::ClassItem *ifaceItem = container.GetOrCreateClassItem("Iface");
812 ifaceItem->SetAccessFlags(ark::ACC_PUBLIC);
813 classItem->AddInterface(ifaceItem);
814
815 // Add method
816 ark::panda_file::StringItem *methodName = container.GetOrCreateStringItem("foo");
817 ark::panda_file::PrimitiveTypeItem *retType =
818 container.GetOrCreatePrimitiveTypeItem(ark::panda_file::Type::TypeId::VOID);
819 std::vector<ark::panda_file::MethodParamItem> params;
820 ark::panda_file::ProtoItem *protoItem = container.GetOrCreateProtoItem(retType, params);
821 classItem->AddMethod(methodName, protoItem, ark::ACC_PUBLIC | ark::ACC_STATIC, params);
822
823 // Add field
824 ark::panda_file::StringItem *fieldName = container.GetOrCreateStringItem("field");
825 ark::panda_file::PrimitiveTypeItem *fieldType =
826 container.GetOrCreatePrimitiveTypeItem(ark::panda_file::Type::TypeId::I32);
827 classItem->AddField(fieldName, fieldType, ark::ACC_PUBLIC);
828
829 // Add source file
830 ark::panda_file::StringItem *sourceFile = container.GetOrCreateStringItem("source_file");
831 classItem->SetSourceFile(sourceFile);
832 auto writer = ark::panda_file::FileWriter("loop_super_class.abc");
833 ASSERT_TRUE(container.Write(&writer));
834
835 std::string cmd = GenLinkCmd(" -- loop_super_class.abc");
836 // NOLINTNEXTLINE(cert-env33-c)
837 auto linkRes = std::system(cmd.c_str());
838 ASSERT_NE(linkRes, 0);
839 #endif
840 }
841
842 } // namespace
843