• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-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 <fstream>
17 #include <iomanip>
18 #include <iostream>
19 #include <map>
20 #include <sstream>
21 #include <string>
22 #include <vector>
23 
24 #include "ark_version.h"
25 
26 #include "assembly-emitter.h"
27 #include "assembly-parser.h"
28 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
29 #include "bytecode_optimizer/optimize_bytecode.h"
30 #endif
31 #include "file_format_version.h"
32 #include "error.h"
33 #include "lexer.h"
34 #include "pandasm.h"
35 #include "utils/expected.h"
36 #include "utils/logger.h"
37 #include "utils/pandargs.h"
38 
39 namespace ark::pandasm {
40 
PrintError(const ark::pandasm::Error & e,const std::string & msg)41 void PrintError(const ark::pandasm::Error &e, const std::string &msg)
42 {
43     std::stringstream sos;
44     std::cerr << msg << ": " << e.message << std::endl;
45     sos << "      Line " << e.lineNumber << ", Column " << (e.pos + 1) << ": ";
46     std::cerr << sos.str() << e.wholeLine << std::endl;
47     std::cerr << std::setw(static_cast<int>(e.pos + sos.str().size()) + 1) << "^" << std::endl;
48 }
49 
PrintErrors(const ark::pandasm::ErrorList & warnings,const std::string & msg)50 void PrintErrors(const ark::pandasm::ErrorList &warnings, const std::string &msg)
51 {
52     for (const auto &iter : warnings) {
53         PrintError(iter, msg);
54     }
55 }
56 
PrintHelp(const ark::PandArgParser & paParser)57 void PrintHelp(const ark::PandArgParser &paParser)
58 {
59     std::cerr << "Usage:" << std::endl;
60     std::cerr << "pandasm [OPTIONS] INPUT_FILE OUTPUT_FILE" << std::endl << std::endl;
61     std::cerr << "Supported options:" << std::endl << std::endl;
62     std::cerr << paParser.GetHelpString() << std::endl;
63 }
64 
65 // CC-OFFNXT(G.FUN.01-CPP) solid logic
PrepareArgs(ark::PandArgParser & paParser,const ark::PandArg<std::string> & inputFile,const ark::PandArg<std::string> & outputFile,const ark::PandArg<std::string> & logFile,const ark::PandArg<bool> & help,const ark::PandArg<bool> & verbose,const ark::PandArg<bool> & version,std::ifstream & inputfile,int argc,const char ** argv)66 bool PrepareArgs(ark::PandArgParser &paParser, const ark::PandArg<std::string> &inputFile,
67                  const ark::PandArg<std::string> &outputFile, const ark::PandArg<std::string> &logFile,
68                  const ark::PandArg<bool> &help, const ark::PandArg<bool> &verbose, const ark::PandArg<bool> &version,
69                  std::ifstream &inputfile, int argc, const char **argv)
70 {
71     if (!paParser.Parse(argc, argv)) {
72         PrintHelp(paParser);
73         return false;
74     }
75 
76     if (version.GetValue()) {
77         ark::PrintPandaVersion();
78         panda_file::PrintBytecodeVersion();
79         return false;
80     }
81 
82     if (inputFile.GetValue().empty() || outputFile.GetValue().empty() || help.GetValue()) {
83         PrintHelp(paParser);
84         return false;
85     }
86 
87     if (verbose.GetValue()) {
88         ark::Logger::ComponentMask componentMask;
89         componentMask.set(ark::Logger::Component::ASSEMBLER);
90         componentMask.set(ark::Logger::Component::BYTECODE_OPTIMIZER);
91         if (logFile.GetValue().empty()) {
92             ark::Logger::InitializeStdLogging(ark::Logger::Level::DEBUG, componentMask);
93         } else {
94             ark::Logger::InitializeFileLogging(logFile.GetValue(), ark::Logger::Level::DEBUG, componentMask);
95         }
96     }
97 
98     inputfile.open(inputFile.GetValue(), std::ifstream::in);
99 
100     if (!inputfile) {
101         std::cerr << "The input file does not exist." << std::endl;
102         return false;
103     }
104 
105     return true;
106 }
107 
Tokenize(ark::pandasm::Lexer & lexer,std::vector<std::vector<ark::pandasm::Token>> & tokens,std::ifstream & inputfile)108 bool Tokenize(ark::pandasm::Lexer &lexer, std::vector<std::vector<ark::pandasm::Token>> &tokens,
109               std::ifstream &inputfile)
110 {
111     std::string s;
112 
113     while (getline(inputfile, s)) {
114         ark::pandasm::Tokens q = lexer.TokenizeString(s);
115 
116         auto e = q.second;
117 
118         if (e.err != ark::pandasm::Error::ErrorType::ERR_NONE) {
119             e.lineNumber = tokens.size() + 1;
120             PrintError(e, "ERROR");
121             return false;
122         }
123 
124         tokens.push_back(q.first);
125     }
126 
127     return true;
128 }
129 
ParseProgram(ark::pandasm::Parser & parser,std::vector<std::vector<ark::pandasm::Token>> & tokens,const ark::PandArg<std::string> & inputFile,ark::Expected<ark::pandasm::Program,ark::pandasm::Error> & res)130 bool ParseProgram(ark::pandasm::Parser &parser, std::vector<std::vector<ark::pandasm::Token>> &tokens,
131                   const ark::PandArg<std::string> &inputFile,
132                   ark::Expected<ark::pandasm::Program, ark::pandasm::Error> &res)
133 {
134     res = parser.Parse(tokens, inputFile.GetValue());
135     if (!res) {
136         PrintError(res.Error(), "ERROR");
137         return false;
138     }
139 
140     return true;
141 }
142 
DumpProgramInJson(ark::pandasm::Program & program,const ark::PandArg<std::string> & scopesFile)143 bool DumpProgramInJson(ark::pandasm::Program &program, const ark::PandArg<std::string> &scopesFile)
144 {
145     if (!scopesFile.GetValue().empty()) {
146         std::ofstream dumpFile;
147         dumpFile.open(scopesFile.GetValue());
148 
149         if (!dumpFile) {
150             std::cerr << "Cannot write scopes into the given file." << std::endl;
151             return false;
152         }
153         dumpFile << program.JsonDump();
154     }
155 
156     return true;
157 }
158 
EmitProgramInBinary(ark::pandasm::Program & program,ark::PandArgParser & paParser,const ark::PandArg<std::string> & outputFile,ark::PandArg<bool> & optimize,ark::PandArg<bool> & sizeStat)159 bool EmitProgramInBinary(ark::pandasm::Program &program, ark::PandArgParser &paParser,
160                          const ark::PandArg<std::string> &outputFile, ark::PandArg<bool> &optimize,
161                          ark::PandArg<bool> &sizeStat)
162 {
163     auto emitDebugInfo = !optimize.GetValue();
164     std::map<std::string, size_t> stat;
165     std::map<std::string, size_t> *statp = sizeStat.GetValue() ? &stat : nullptr;
166     ark::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
167     ark::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = optimize.GetValue() ? &maps : nullptr;
168 
169     if (!ark::pandasm::AsmEmitter::Emit(outputFile.GetValue(), program, statp, mapsp, emitDebugInfo)) {
170         std::cerr << "Failed to emit binary data: " << ark::pandasm::AsmEmitter::GetLastError() << std::endl;
171         return false;
172     }
173 
174 #ifdef PANDA_WITH_BYTECODE_OPTIMIZER
175     if (optimize.GetValue()) {
176         bool isOptimized = ark::bytecodeopt::OptimizeBytecode(&program, mapsp, outputFile.GetValue());
177         if (!ark::pandasm::AsmEmitter::Emit(outputFile.GetValue(), program, statp, mapsp, emitDebugInfo)) {
178             std::cerr << "Failed to emit binary data: " << ark::pandasm::AsmEmitter::GetLastError() << std::endl;
179             return false;
180         }
181         if (!isOptimized) {
182             std::cerr << "Bytecode optimizer reported internal errors" << std::endl;
183             return false;
184         }
185     }
186 #endif
187 
188     if (sizeStat.GetValue()) {
189         size_t totalSize = 0;
190         std::cout << "Panda file size statistic:" << std::endl;
191 
192         for (auto [name, size] : stat) {
193             std::cout << name << " section: " << size << std::endl;
194             totalSize += size;
195         }
196 
197         std::cout << "total: " << totalSize << std::endl;
198     }
199 
200     paParser.DisableTail();
201 
202     return true;
203 }
204 
205 // CC-OFFNXT(G.FUN.01-CPP) solid logic
BuildFiles(ark::pandasm::Program & program,ark::PandArgParser & paParser,const ark::PandArg<std::string> & outputFile,ark::PandArg<bool> & optimize,ark::PandArg<bool> & sizeStat,ark::PandArg<std::string> & scopesFile)206 bool BuildFiles(ark::pandasm::Program &program, ark::PandArgParser &paParser,
207                 const ark::PandArg<std::string> &outputFile, ark::PandArg<bool> &optimize, ark::PandArg<bool> &sizeStat,
208                 ark::PandArg<std::string> &scopesFile)
209 {
210     if (!DumpProgramInJson(program, scopesFile)) {
211         return false;
212     }
213 
214     if (!EmitProgramInBinary(program, paParser, outputFile, optimize, sizeStat)) {
215         return false;
216     }
217 
218     return true;
219 }
220 
221 }  // namespace ark::pandasm
222 
main(int argc,const char * argv[])223 int main(int argc, const char *argv[])
224 {
225     ark::PandArg<bool> verbose("verbose", false, "Enable verbose output (will be printed to standard output)");
226     ark::PandArg<std::string> logFile("log-file", "", "(--log-file FILENAME) Set log file name");
227     ark::PandArg<std::string> scopesFile("dump-scopes", "", "(--dump-scopes FILENAME) Enable dump of scopes to file");
228     ark::PandArg<bool> help("help", false, "Print this message and exit");
229     ark::PandArg<bool> sizeStat("size-stat", false, "Print panda file size statistic");
230     ark::PandArg<bool> optimize("optimize", false, "Run the bytecode optimization");
231     ark::PandArg<bool> version {"version", false,
232                                 "Ark version, file format version and minimum supported file format version"};
233     // tail arguments
234     ark::PandArg<std::string> inputFile("INPUT_FILE", "", "Path to the source assembly code");
235     ark::PandArg<std::string> outputFile("OUTPUT_FILE", "", "Path to the generated binary code");
236     ark::PandArgParser paParser;
237     paParser.Add(&verbose);
238     paParser.Add(&help);
239     paParser.Add(&logFile);
240     paParser.Add(&scopesFile);
241     paParser.Add(&sizeStat);
242     paParser.Add(&optimize);
243     paParser.Add(&version);
244     paParser.PushBackTail(&inputFile);
245     paParser.PushBackTail(&outputFile);
246     paParser.EnableTail();
247 
248     std::ifstream inputfile;
249 
250     if (!ark::pandasm::PrepareArgs(paParser, inputFile, outputFile, logFile, help, verbose, version, inputfile, argc,
251                                    argv)) {
252         return 1;
253     }
254 
255     LOG(DEBUG, ASSEMBLER) << "Lexical analysis:";
256 
257     ark::pandasm::Lexer lexer;
258 
259     std::vector<std::vector<ark::pandasm::Token>> tokens;
260 
261     if (!Tokenize(lexer, tokens, inputfile)) {
262         return 1;
263     }
264 
265     LOG(DEBUG, ASSEMBLER) << "parsing:";
266 
267     ark::pandasm::Parser parser;
268 
269     ark::Expected<ark::pandasm::Program, ark::pandasm::Error> res;
270     if (!ark::pandasm::ParseProgram(parser, tokens, inputFile, res)) {
271         return 1;
272     }
273 
274     auto &program = res.Value();
275 
276     auto w = parser.ShowWarnings();
277     if (!w.empty()) {
278         ark::pandasm::PrintErrors(w, "WARNING");
279     }
280 
281     if (!ark::pandasm::BuildFiles(program, paParser, outputFile, optimize, sizeStat, scopesFile)) {
282         return 1;
283     }
284 
285     return 0;
286 }
287