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