1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <unistd.h>
18 #include <cerrno>
19 #include <cstdio>
20 #include <filesystem>
21 #include <fstream>
22 #include <iostream>
23 #include <queue>
24 #include <regex>
25 #include <sstream>
26 #include <vector>
27 
28 #include "declarations.h"
29 #include "struct_parser_generator.h"
30 
parse_namespace(const std::string & root_namespace,const std::filesystem::path & input_file_relative_path,std::vector<std::string> * token)31 void parse_namespace(
32     const std::string& root_namespace,
33     const std::filesystem::path& input_file_relative_path,
34     std::vector<std::string>* token) {
35   std::filesystem::path gen_namespace = root_namespace / input_file_relative_path;
36   std::string gen_namespace_str = gen_namespace.u8string();
37   std::regex path_tokenizer("/");
38   auto it = std::sregex_token_iterator(gen_namespace_str.cbegin(), gen_namespace_str.cend(), path_tokenizer, -1);
39   std::sregex_token_iterator it_end = {};
40   for (; it != it_end; ++it) {
41     token->push_back(it->str());
42   }
43 }
44 
generate_namespace_open(const std::vector<std::string> & token,std::ostream & output)45 void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) {
46   for (const auto& ns : token) {
47     output << "namespace " << ns << " {" << std::endl;
48   }
49 }
50 
generate_namespace_close(const std::vector<std::string> & token,std::ostream & output)51 void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) {
52   for (auto it = token.rbegin(); it != token.rend(); ++it) {
53     output << "}  //namespace " << *it << std::endl;
54   }
55 }
56 
generate_cpp_headers_one_file(const Declarations & decls,const std::filesystem::path & input_file,const std::filesystem::path & include_dir,const std::filesystem::path & out_dir,const std::string & root_namespace)57 bool generate_cpp_headers_one_file(
58     const Declarations& decls,
59     const std::filesystem::path& input_file,
60     const std::filesystem::path& include_dir,
61     const std::filesystem::path& out_dir,
62     const std::string& root_namespace) {
63   auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
64 
65   auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
66   auto gen_path = out_dir / gen_relative_path;
67 
68   std::filesystem::create_directories(gen_path);
69 
70   auto gen_file = gen_path / (input_filename + ".h");
71 
72   std::cout << "generating " << gen_file << std::endl;
73 
74   std::ofstream out_file;
75   out_file.open(gen_file);
76   if (!out_file.is_open()) {
77     std::cerr << "can't open " << gen_file << std::endl;
78     return false;
79   }
80 
81   out_file <<
82       R"(
83 #pragma once
84 
85 #include <cstdint>
86 #include <functional>
87 #include <sstream>
88 #include <string>
89 #include <type_traits>
90 
91 #include "os/log.h"
92 #include "packet/base_packet_builder.h"
93 #include "packet/bit_inserter.h"
94 #include "packet/custom_field_fixed_size_interface.h"
95 #include "packet/iterator.h"
96 #include "packet/packet_builder.h"
97 #include "packet/packet_struct.h"
98 #include "packet/packet_view.h"
99 
100 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
101 #include "packet/raw_builder.h"
102 #endif
103 #include "packet/parser/checksum_type_checker.h"
104 #include "packet/parser/custom_type_checker.h"
105 
106 )";
107 
108   for (const auto& c : decls.type_defs_queue_) {
109     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
110         c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
111       ((CustomFieldDef*)c.second)->GenInclude(out_file);
112     }
113   }
114   out_file << "\n\n";
115 
116   std::vector<std::string> namespace_list;
117   parse_namespace(root_namespace, gen_relative_path, &namespace_list);
118   generate_namespace_open(namespace_list, out_file);
119   out_file << "\n\n";
120 
121   for (const auto& c : decls.type_defs_queue_) {
122     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
123         c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
124       ((CustomFieldDef*)c.second)->GenUsing(out_file);
125     }
126   }
127   out_file <<
128       R"(
129 
130 using ::bluetooth::packet::BasePacketBuilder;
131 using ::bluetooth::packet::BitInserter;
132 using ::bluetooth::packet::CustomFieldFixedSizeInterface;
133 using ::bluetooth::packet::CustomTypeChecker;
134 using ::bluetooth::packet::Iterator;
135 using ::bluetooth::packet::kLittleEndian;
136 using ::bluetooth::packet::PacketBuilder;
137 using ::bluetooth::packet::PacketStruct;
138 using ::bluetooth::packet::PacketView;
139 
140 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
141 using ::bluetooth::packet::RawBuilder;
142 #endif
143 
144 using ::bluetooth::packet::parser::ChecksumTypeChecker;
145 
146 )";
147 
148   for (const auto& e : decls.type_defs_queue_) {
149     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
150       const auto* enum_def = static_cast<const EnumDef*>(e.second);
151       EnumGen gen(*enum_def);
152       gen.GenDefinition(out_file);
153       out_file << "\n\n";
154     }
155   }
156   for (const auto& e : decls.type_defs_queue_) {
157     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
158       const auto* enum_def = static_cast<const EnumDef*>(e.second);
159       EnumGen gen(*enum_def);
160       gen.GenLogging(out_file);
161       out_file << "\n\n";
162     }
163   }
164   for (const auto& ch : decls.type_defs_queue_) {
165     if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
166       const auto* checksum_def = static_cast<const ChecksumDef*>(ch.second);
167       checksum_def->GenChecksumCheck(out_file);
168     }
169   }
170   out_file << "\n/* Done ChecksumChecks */\n";
171 
172   for (const auto& c : decls.type_defs_queue_) {
173     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
174       if (c.second->size_ == -1 /* Variable Size */) {
175         const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
176         custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian);
177       } else {  // fixed size
178         const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
179         custom_field_def->GenFixedSizeCustomFieldCheck(out_file);
180       }
181     }
182   }
183   out_file << "\n";
184 
185   for (auto& s : decls.type_defs_queue_) {
186     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
187       const auto* struct_def = static_cast<const StructDef*>(s.second);
188       struct_def->GenDefinition(out_file);
189       out_file << "\n";
190     }
191   }
192 
193   {
194     StructParserGenerator spg(decls);
195     spg.Generate(out_file);
196     out_file << "\n\n";
197   }
198 
199   for (const auto& packet_def : decls.packet_defs_queue_) {
200     packet_def.second->GenParserDefinition(out_file);
201     out_file << "\n\n";
202   }
203 
204   for (const auto& packet_def : decls.packet_defs_queue_) {
205     packet_def.second->GenBuilderDefinition(out_file);
206     out_file << "\n\n";
207   }
208 
209   if (input_filename == "hci_packets") {
210     out_file << "class Checker { public: static bool IsCommandStatusOpcode(OpCode op_code) {";
211     out_file << "switch (op_code) {";
212     std::set<std::string> op_codes;
213     for (const auto& packet_def : decls.packet_defs_queue_) {
214       auto packet = packet_def.second;
215       auto op_constraint = packet->parent_constraints_.find("op_code");
216       if (op_constraint == packet->parent_constraints_.end()) {
217         auto constraint = packet->parent_constraints_.find("command_op_code");
218         if (constraint == packet->parent_constraints_.end()) {
219           continue;
220         }
221         if (packet->HasAncestorNamed("CommandStatus")) {
222           out_file << "case " << std::get<std::string>(constraint->second) << ":";
223           op_codes.erase(std::get<std::string>(constraint->second));
224         }
225         if (packet->HasAncestorNamed("CommandComplete")) {
226           op_codes.erase(std::get<std::string>(constraint->second));
227         }
228       } else {
229         op_codes.insert(std::get<std::string>(op_constraint->second));
230       }
231     }
232     bool unhandled_opcode = false;
233     for (const auto& opcode : op_codes) {
234       unhandled_opcode = true;
235       std::cerr << "Opcode with no Status or Complete " << opcode << std::endl;
236     }
237     if (unhandled_opcode) {
238       ERROR() << "At least one unhandled opcode";
239     }
240     out_file << "return true; default: return false; }}};";
241   }
242 
243   generate_namespace_close(namespace_list, out_file);
244 
245   out_file.close();
246 
247   return true;
248 }
249 
250 // Get the out_file shard at a symbol_count
get_out_file(size_t symbol_count,size_t symbol_total,std::vector<std::ofstream> * out_files)251 std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total, std::vector<std::ofstream>* out_files) {
252   auto symbols_per_shard = symbol_total / out_files->size();
253   auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
254   return out_files->at(file_index);
255 }
256 
generate_pybind11_sources_one_file(const Declarations & decls,const std::filesystem::path & input_file,const std::filesystem::path & include_dir,const std::filesystem::path & out_dir,const std::string & root_namespace,size_t num_shards)257 bool generate_pybind11_sources_one_file(
258     const Declarations& decls,
259     const std::filesystem::path& input_file,
260     const std::filesystem::path& include_dir,
261     const std::filesystem::path& out_dir,
262     const std::string& root_namespace,
263     size_t num_shards) {
264   auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
265 
266   auto input_filename = input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
267   auto gen_path = out_dir / gen_relative_path;
268 
269   std::filesystem::create_directories(gen_path);
270 
271   auto gen_relative_header = gen_relative_path / (input_filename + ".h");
272 
273   std::vector<std::string> namespace_list;
274   parse_namespace(root_namespace, gen_relative_path, &namespace_list);
275 
276   std::vector<std::ofstream> out_file_shards(num_shards);
277   for (size_t i = 0; i < out_file_shards.size(); i++) {
278     auto filename = gen_path / (input_filename + "_python3_shard_" + std::to_string(i) + ".cc");
279     std::cout << "generating " << filename << std::endl;
280     auto& out_file = out_file_shards[i];
281     out_file.open(filename);
282     if (!out_file.is_open()) {
283       std::cerr << "can't open " << filename << std::endl;
284       return false;
285     }
286     out_file << "#include <pybind11/pybind11.h>\n";
287     out_file << "#include <pybind11/stl.h>\n";
288     out_file << "\n\n";
289     out_file << "#include " << gen_relative_header << "\n";
290     out_file << "\n\n";
291     out_file << "#include \"packet/raw_builder.h\"\n";
292     out_file << "\n\n";
293 
294     for (const auto& c : decls.type_defs_queue_) {
295       if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
296         const auto* custom_def = static_cast<const CustomFieldDef*>(c.second);
297         custom_def->GenPyBind11Include(out_file);
298       }
299     }
300 
301     out_file << "\n\n";
302 
303     generate_namespace_open(namespace_list, out_file);
304     out_file << "\n\n";
305 
306     for (const auto& c : decls.type_defs_queue_) {
307       if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
308           c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
309         const auto* custom_def = static_cast<const CustomFieldDef*>(c.second);
310         custom_def->GenUsing(out_file);
311       }
312     }
313     out_file << "\n\n";
314 
315     out_file << "using ::bluetooth::packet::BasePacketBuilder;";
316     out_file << "using ::bluetooth::packet::BitInserter;";
317     out_file << "using ::bluetooth::packet::CustomTypeChecker;";
318     out_file << "using ::bluetooth::packet::Iterator;";
319     out_file << "using ::bluetooth::packet::kLittleEndian;";
320     out_file << "using ::bluetooth::packet::PacketBuilder;";
321     out_file << "using ::bluetooth::packet::BaseStruct;";
322     out_file << "using ::bluetooth::packet::PacketStruct;";
323     out_file << "using ::bluetooth::packet::PacketView;";
324     out_file << "using ::bluetooth::packet::RawBuilder;";
325     out_file << "using ::bluetooth::packet::parser::ChecksumTypeChecker;";
326     out_file << "\n\n";
327 
328     out_file << "namespace py = pybind11;\n\n";
329 
330     out_file << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(py::module& m) {\n\n";
331   }
332   size_t symbol_total = 0;
333   // Only count types that will be generated
334   for (const auto& e : decls.type_defs_queue_) {
335     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
336       symbol_total++;
337     } else if (e.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
338       symbol_total++;
339     }
340   }
341   // View and builder are counted separately
342   symbol_total += decls.packet_defs_queue_.size() * 2;
343   size_t symbol_count = 0;
344 
345   for (const auto& e : decls.type_defs_queue_) {
346     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
347       const auto* enum_def = static_cast<const EnumDef*>(e.second);
348       EnumGen gen(*enum_def);
349       auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
350       gen.GenDefinitionPybind11(out_file);
351       out_file << "\n\n";
352       symbol_count++;
353     }
354   }
355 
356   for (const auto& s : decls.type_defs_queue_) {
357     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
358       const auto* struct_def = static_cast<const StructDef*>(s.second);
359       auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
360       struct_def->GenDefinitionPybind11(out_file);
361       out_file << "\n";
362       symbol_count++;
363     }
364   }
365 
366   for (const auto& packet_def : decls.packet_defs_queue_) {
367     auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
368     packet_def.second->GenParserDefinitionPybind11(out_file);
369     out_file << "\n\n";
370     symbol_count++;
371   }
372 
373   for (const auto& p : decls.packet_defs_queue_) {
374     auto& out_file = get_out_file(symbol_count, symbol_total, &out_file_shards);
375     p.second->GenBuilderDefinitionPybind11(out_file);
376     out_file << "\n\n";
377     symbol_count++;
378   }
379 
380   for (auto& out_file : out_file_shards) {
381     out_file << "}\n\n";
382     generate_namespace_close(namespace_list, out_file);
383   }
384 
385   auto gen_file_main = gen_path / (input_filename + "_python3.cc");
386   std::ofstream out_file_main;
387   out_file_main.open(gen_file_main);
388   if (!out_file_main.is_open()) {
389     std::cerr << "can't open " << gen_file_main << std::endl;
390     return false;
391   }
392   out_file_main << "#include <pybind11/pybind11.h>\n";
393   generate_namespace_open(namespace_list, out_file_main);
394 
395   out_file_main << "namespace py = pybind11;\n\n";
396 
397   for (size_t i = 0; i < out_file_shards.size(); i++) {
398     out_file_main << "void define_" << input_filename << "_submodule_shard_" << std::to_string(i)
399                   << "(py::module& m);\n";
400   }
401 
402   out_file_main << "void define_" << input_filename << "_submodule(py::module& m) {\n\n";
403   for (size_t i = 0; i < out_file_shards.size(); i++) {
404     out_file_main << "define_" << input_filename << "_submodule_shard_" << std::to_string(i) << "(m);\n";
405   }
406   out_file_main << "}\n\n";
407 
408   generate_namespace_close(namespace_list, out_file_main);
409 
410   return true;
411 }
412