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