• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &param)
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