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