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 
19 #include <cerrno>
20 #include <cstdio>
21 #include <filesystem>
22 #include <fstream>
23 #include <iostream>
24 #include <queue>
25 #include <regex>
26 #include <sstream>
27 #include <vector>
28 
29 #include "declarations.h"
30 #include "struct_parser_generator.h"
31 
parse_namespace(const std::string & root_namespace,const std::filesystem::path & input_file_relative_path,std::vector<std::string> * token)32 void parse_namespace(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   for (auto it = gen_namespace.begin(); it != gen_namespace.end(); ++it) {
37     token->push_back(it->string());
38   }
39 }
40 
generate_namespace_open(const std::vector<std::string> & token,std::ostream & output)41 void generate_namespace_open(const std::vector<std::string>& token, std::ostream& output) {
42   for (const auto& ns : token) {
43     output << "namespace " << ns << " {" << std::endl;
44   }
45 }
46 
generate_namespace_close(const std::vector<std::string> & token,std::ostream & output)47 void generate_namespace_close(const std::vector<std::string>& token, std::ostream& output) {
48   for (auto it = token.rbegin(); it != token.rend(); ++it) {
49     output << "}  //namespace " << *it << std::endl;
50   }
51 }
52 
generate_cpp_headers_one_file(const Declarations & decls,bool generate_fuzzing,bool generate_tests,const std::filesystem::path & input_file,const std::filesystem::path & include_dir,const std::filesystem::path & out_dir,const std::string & root_namespace)53 bool generate_cpp_headers_one_file(const Declarations& decls, bool generate_fuzzing,
54                                    bool generate_tests, const std::filesystem::path& input_file,
55                                    const std::filesystem::path& include_dir,
56                                    const std::filesystem::path& out_dir,
57                                    const std::string& root_namespace) {
58   auto gen_relative_path = input_file.lexically_relative(include_dir).parent_path();
59 
60   auto input_filename =
61           input_file.filename().string().substr(0, input_file.filename().string().find(".pdl"));
62   auto gen_path = out_dir / gen_relative_path;
63 
64   std::filesystem::create_directories(gen_path);
65 
66   auto gen_file = gen_path / (input_filename + ".h");
67 
68   std::cout << "generating " << gen_file << std::endl;
69 
70   std::ofstream out_file;
71   out_file.open(gen_file);
72   if (!out_file.is_open()) {
73     std::cerr << "can't open " << gen_file << std::endl;
74     return false;
75   }
76 
77   out_file <<
78           R"(
79 #pragma once
80 
81 #include <cstdint>
82 #include <functional>
83 #include <iomanip>
84 #include <optional>
85 #include <sstream>
86 #include <string>
87 #include <type_traits>
88 
89 #include "packet/base_packet_builder.h"
90 #include "packet/bit_inserter.h"
91 #include "packet/custom_field_fixed_size_interface.h"
92 #include "packet/iterator.h"
93 #include "packet/packet_builder.h"
94 #include "packet/packet_struct.h"
95 #include "packet/packet_view.h"
96 #include "packet/checksum_type_checker.h"
97 #include "packet/custom_type_checker.h"
98 
99 #if __has_include(<bluetooth/log.h>)
100 
101 #include <bluetooth/log.h>
102 
103 #ifndef ASSERT
104 #define ASSERT(cond) bluetooth::log::assert_that(cond, #cond)
105 #endif // !defined(ASSERT)
106 
107 #else
108 
109 #ifndef ASSERT
110 #define ASSERT(cond) assert(cond)
111 #endif // !defined(ASSERT)
112 
113 #endif // __has_include(<bluetooth/log.h>)
114 )";
115 
116   if (generate_fuzzing || generate_tests) {
117     out_file <<
118             R"(
119 
120 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
121 #include "packet/raw_builder.h"
122 #endif
123 )";
124   }
125 
126   for (const auto& c : decls.type_defs_queue_) {
127     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
128         c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
129       ((CustomFieldDef*)c.second)->GenInclude(out_file);
130     }
131   }
132   out_file << "\n\n";
133 
134   std::vector<std::string> namespace_list;
135   parse_namespace(root_namespace, gen_relative_path, &namespace_list);
136   generate_namespace_open(namespace_list, out_file);
137   out_file << "\n\n";
138 
139   for (const auto& c : decls.type_defs_queue_) {
140     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM ||
141         c.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
142       ((CustomFieldDef*)c.second)->GenUsing(out_file);
143     }
144   }
145 
146   out_file <<
147           R"(
148 
149 using ::bluetooth::packet::BasePacketBuilder;
150 using ::bluetooth::packet::BitInserter;
151 using ::bluetooth::packet::CustomFieldFixedSizeInterface;
152 using ::bluetooth::packet::CustomTypeChecker;
153 using ::bluetooth::packet::Iterator;
154 using ::bluetooth::packet::kLittleEndian;
155 using ::bluetooth::packet::PacketBuilder;
156 using ::bluetooth::packet::PacketStruct;
157 using ::bluetooth::packet::PacketView;
158 using ::bluetooth::packet::parser::ChecksumTypeChecker;
159 )";
160 
161   if (generate_fuzzing || generate_tests) {
162     out_file <<
163             R"(
164 #if defined(PACKET_FUZZ_TESTING) || defined(PACKET_TESTING) || defined(FUZZ_TARGET)
165 using ::bluetooth::packet::RawBuilder;
166 #endif
167 )";
168   }
169 
170   for (const auto& e : decls.type_defs_queue_) {
171     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
172       const auto* enum_def = static_cast<const EnumDef*>(e.second);
173       EnumGen gen(*enum_def);
174       gen.GenDefinition(out_file);
175       out_file << "\n\n";
176     }
177   }
178   for (const auto& e : decls.type_defs_queue_) {
179     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
180       const auto* enum_def = static_cast<const EnumDef*>(e.second);
181       EnumGen gen(*enum_def);
182       gen.GenLogging(out_file);
183       out_file << "\n\n";
184     }
185   }
186   for (const auto& ch : decls.type_defs_queue_) {
187     if (ch.second->GetDefinitionType() == TypeDef::Type::CHECKSUM) {
188       const auto* checksum_def = static_cast<const ChecksumDef*>(ch.second);
189       checksum_def->GenChecksumCheck(out_file);
190     }
191   }
192   out_file << "\n/* Done ChecksumChecks */\n";
193 
194   for (const auto& c : decls.type_defs_queue_) {
195     if (c.second->GetDefinitionType() == TypeDef::Type::CUSTOM) {
196       if (c.second->size_ == -1 /* Variable Size */) {
197         const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
198         custom_field_def->GenCustomFieldCheck(out_file, decls.is_little_endian);
199       } else {  // fixed size
200         const auto* custom_field_def = static_cast<const CustomFieldDef*>(c.second);
201         custom_field_def->GenFixedSizeCustomFieldCheck(out_file);
202       }
203     }
204   }
205   out_file << "\n";
206 
207   for (auto& s : decls.type_defs_queue_) {
208     if (s.second->GetDefinitionType() == TypeDef::Type::STRUCT) {
209       const auto* struct_def = static_cast<const StructDef*>(s.second);
210       struct_def->GenDefinition(out_file);
211       out_file << "\n";
212     }
213   }
214 
215   {
216     StructParserGenerator spg(decls);
217     spg.Generate(out_file);
218     out_file << "\n\n";
219   }
220 
221   for (const auto& packet_def : decls.packet_defs_queue_) {
222     packet_def.second->GenParserDefinition(out_file, generate_fuzzing, generate_tests);
223     out_file << "\n\n";
224   }
225 
226   for (const auto& packet_def : decls.packet_defs_queue_) {
227     packet_def.second->GenBuilderDefinition(out_file, generate_fuzzing, generate_tests);
228     out_file << "\n\n";
229   }
230 
231   if (input_filename == "hci_packets") {
232     out_file << "class Checker { public: static bool IsCommandStatusOpcode(OpCode op_code) {";
233     out_file << "switch (op_code) {";
234     std::set<std::string> op_codes;
235     for (const auto& packet_def : decls.packet_defs_queue_) {
236       auto packet = packet_def.second;
237       auto op_constraint = packet->parent_constraints_.find("op_code");
238       if (op_constraint == packet->parent_constraints_.end()) {
239         auto constraint = packet->parent_constraints_.find("command_op_code");
240         if (constraint == packet->parent_constraints_.end()) {
241           continue;
242         }
243         if (packet->HasAncestorNamed("CommandStatus")) {
244           out_file << "case " << std::get<std::string>(constraint->second) << ":";
245           op_codes.erase(std::get<std::string>(constraint->second));
246         }
247         if (packet->HasAncestorNamed("CommandComplete")) {
248           op_codes.erase(std::get<std::string>(constraint->second));
249         }
250       } else {
251         op_codes.insert(std::get<std::string>(op_constraint->second));
252       }
253     }
254     bool unhandled_opcode = false;
255     for (const auto& opcode : op_codes) {
256       unhandled_opcode = true;
257       std::cerr << "Opcode with no Status or Complete " << opcode << std::endl;
258     }
259     if (unhandled_opcode) {
260       ERROR() << "At least one unhandled opcode";
261     }
262     out_file << "return true; default: return false; }}};";
263   }
264 
265   generate_namespace_close(namespace_list, out_file);
266 
267   // Generate formatters for all enum declarations.
268   std::string namespace_prefix;
269   for (auto const& fragment : namespace_list) {
270     namespace_prefix += fragment;
271     namespace_prefix += "::";
272   }
273 
274   out_file << "#if __has_include(<bluetooth/log.h>)" << std::endl << "namespace std {" << std::endl;
275   for (const auto& e : decls.type_defs_queue_) {
276     if (e.second->GetDefinitionType() == TypeDef::Type::ENUM) {
277       const auto* enum_def = static_cast<const EnumDef*>(e.second);
278       out_file << "template <>" << std::endl
279                << "struct formatter<" << namespace_prefix << enum_def->name_ << ">"
280                << " : enum_formatter<" << namespace_prefix << enum_def->name_ << "> {};"
281                << std::endl;
282     }
283   }
284   out_file << "} // namespace std" << std::endl
285            << "#endif // __has_include(<bluetooth/log.h>)" << std::endl;
286 
287   out_file.close();
288 
289   return true;
290 }
291 
292 // 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)293 std::ofstream& get_out_file(size_t symbol_count, size_t symbol_total,
294                             std::vector<std::ofstream>* out_files) {
295   auto symbols_per_shard = symbol_total / out_files->size();
296   auto file_index = std::min(symbol_count / symbols_per_shard, out_files->size() - 1);
297   return out_files->at(file_index);
298 }
299