blob: c5efb222b1cf3313fa8fdb2ad11cac4475529d6d [file] [log] [blame]
Austin Schuhe89fa2d2019-08-14 20:24:23 -07001/*
2 * Copyright 2016 Google Inc. All rights reserved.
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 "flatbuffers/code_generators.h"
Austin Schuh272c6132020-11-14 16:37:52 -080018
Austin Schuhe89fa2d2019-08-14 20:24:23 -070019#include <assert.h>
Austin Schuhe89fa2d2019-08-14 20:24:23 -070020
21#include <cmath>
22
Austin Schuh272c6132020-11-14 16:37:52 -080023#include "flatbuffers/base.h"
24#include "flatbuffers/util.h"
25
Austin Schuhe89fa2d2019-08-14 20:24:23 -070026#if defined(_MSC_VER)
27# pragma warning(push)
28# pragma warning(disable : 4127) // C4127: conditional expression is constant
29#endif
30
31namespace flatbuffers {
32
Austin Schuh2dd86a92022-09-14 21:19:23 -070033namespace {
34
35static std::string JavaCSharpMakeRule(const bool java, const Parser &parser,
36 const std::string &path,
37 const std::string &file_name) {
38 const std::string file_extension = java ? ".java" : ".cs";
39 std::string make_rule;
40
41 for (auto it = parser.enums_.vec.begin(); it != parser.enums_.vec.end();
42 ++it) {
43 auto &enum_def = **it;
44 if (!make_rule.empty()) make_rule += " ";
45 std::string directory =
46 BaseGenerator::NamespaceDir(parser, path, *enum_def.defined_namespace);
47 make_rule += directory + enum_def.name + file_extension;
48 }
49
50 for (auto it = parser.structs_.vec.begin(); it != parser.structs_.vec.end();
51 ++it) {
52 auto &struct_def = **it;
53 if (!make_rule.empty()) make_rule += " ";
54 std::string directory = BaseGenerator::NamespaceDir(
55 parser, path, *struct_def.defined_namespace);
56 make_rule += directory + struct_def.name + file_extension;
57 }
58
59 make_rule += ": ";
60 auto included_files = parser.GetIncludedFilesRecursive(file_name);
61 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
62 make_rule += " " + *it;
63 }
64 return make_rule;
65}
66
67
68static std::string BinaryFileName(const Parser &parser, const std::string &path,
69 const std::string &file_name) {
70 auto ext = parser.file_extension_.length() ? parser.file_extension_ : "bin";
71 return path + file_name + "." + ext;
72}
73
74} // namespace
75
76
77
78
Austin Schuhe89fa2d2019-08-14 20:24:23 -070079void CodeWriter::operator+=(std::string text) {
80 if (!ignore_ident_ && !text.empty()) AppendIdent(stream_);
81
82 while (true) {
83 auto begin = text.find("{{");
84 if (begin == std::string::npos) { break; }
85
86 auto end = text.find("}}");
87 if (end == std::string::npos || end < begin) { break; }
88
89 // Write all the text before the first {{ into the stream.
90 stream_.write(text.c_str(), begin);
91
92 // The key is between the {{ and }}.
93 const std::string key = text.substr(begin + 2, end - begin - 2);
94
95 // Find the value associated with the key. If it exists, write the
96 // value into the stream, otherwise write the key itself into the stream.
97 auto iter = value_map_.find(key);
98 if (iter != value_map_.end()) {
99 const std::string &value = iter->second;
100 stream_ << value;
101 } else {
102 FLATBUFFERS_ASSERT(false && "could not find key");
103 stream_ << key;
104 }
105
106 // Update the text to everything after the }}.
107 text = text.substr(end + 2);
108 }
James Kuszmaul8e62b022022-03-22 09:33:25 -0700109 if (!text.empty() && text.back() == '\\') {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700110 text.pop_back();
111 ignore_ident_ = true;
112 stream_ << text;
113 } else {
114 ignore_ident_ = false;
115 stream_ << text << std::endl;
116 }
117}
118
119void CodeWriter::AppendIdent(std::stringstream &stream) {
120 int lvl = cur_ident_lvl_;
121 while (lvl--) {
122 stream.write(pad_.c_str(), static_cast<std::streamsize>(pad_.size()));
123 }
124}
125
126const char *BaseGenerator::FlatBuffersGeneratedWarning() {
127 return "automatically generated by the FlatBuffers compiler,"
128 " do not modify";
129}
130
131std::string BaseGenerator::NamespaceDir(const Parser &parser,
132 const std::string &path,
James Kuszmaul8e62b022022-03-22 09:33:25 -0700133 const Namespace &ns,
134 const bool dasherize) {
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700135 EnsureDirExists(path);
136 if (parser.opts.one_file) return path;
137 std::string namespace_dir = path; // Either empty or ends in separator.
138 auto &namespaces = ns.components;
139 for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
James Kuszmaul8e62b022022-03-22 09:33:25 -0700140 namespace_dir +=
141 !dasherize ? *it : ConvertCase(*it, Case::kDasher, Case::kUpperCamel);
142 namespace_dir += kPathSeparator;
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700143 EnsureDirExists(namespace_dir);
144 }
145 return namespace_dir;
146}
147
James Kuszmaul8e62b022022-03-22 09:33:25 -0700148std::string BaseGenerator::NamespaceDir(const Namespace &ns,
149 const bool dasherize) const {
150 return BaseGenerator::NamespaceDir(parser_, path_, ns, dasherize);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700151}
152
153std::string BaseGenerator::FullNamespace(const char *separator,
154 const Namespace &ns) {
155 std::string namespace_name;
156 auto &namespaces = ns.components;
157 for (auto it = namespaces.begin(); it != namespaces.end(); ++it) {
158 if (namespace_name.length()) namespace_name += separator;
159 namespace_name += *it;
160 }
161 return namespace_name;
162}
163
164std::string BaseGenerator::LastNamespacePart(const Namespace &ns) {
165 if (!ns.components.empty())
166 return ns.components.back();
167 else
168 return std::string("");
169}
170
171// Ensure that a type is prefixed with its namespace.
172std::string BaseGenerator::WrapInNameSpace(const Namespace *ns,
173 const std::string &name) const {
174 std::string qualified_name = qualifying_start_;
175 for (auto it = ns->components.begin(); it != ns->components.end(); ++it)
176 qualified_name += *it + qualifying_separator_;
177 return qualified_name + name;
178}
179
Austin Schuh2dd86a92022-09-14 21:19:23 -0700180std::string BaseGenerator::WrapInNameSpace(const Definition &def,
181 const std::string &suffix) const {
182 return WrapInNameSpace(def.defined_namespace, def.name + suffix);
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700183}
184
185std::string BaseGenerator::GetNameSpace(const Definition &def) const {
186 const Namespace *ns = def.defined_namespace;
187 if (CurrentNameSpace() == ns) return "";
188 std::string qualified_name = qualifying_start_;
189 for (auto it = ns->components.begin(); it != ns->components.end(); ++it) {
190 qualified_name += *it;
191 if ((it + 1) != ns->components.end()) {
192 qualified_name += qualifying_separator_;
193 }
194 }
195
196 return qualified_name;
197}
198
Austin Schuh272c6132020-11-14 16:37:52 -0800199std::string BaseGenerator::GeneratedFileName(const std::string &path,
200 const std::string &file_name,
201 const IDLOptions &options) const {
202 return path + file_name + options.filename_suffix + "." +
203 (options.filename_extension.empty() ? default_extension_
204 : options.filename_extension);
205}
206
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700207// Generate a documentation comment, if available.
208void GenComment(const std::vector<std::string> &dc, std::string *code_ptr,
209 const CommentConfig *config, const char *prefix) {
210 if (dc.begin() == dc.end()) {
211 // Don't output empty comment blocks with 0 lines of comment content.
212 return;
213 }
214
215 std::string &code = *code_ptr;
216 if (config != nullptr && config->first_line != nullptr) {
217 code += std::string(prefix) + std::string(config->first_line) + "\n";
218 }
219 std::string line_prefix =
220 std::string(prefix) +
221 ((config != nullptr && config->content_line_prefix != nullptr)
222 ? config->content_line_prefix
223 : "///");
224 for (auto it = dc.begin(); it != dc.end(); ++it) {
225 code += line_prefix + *it + "\n";
226 }
227 if (config != nullptr && config->last_line != nullptr) {
228 code += std::string(prefix) + std::string(config->last_line) + "\n";
229 }
230}
231
232template<typename T>
233std::string FloatConstantGenerator::GenFloatConstantImpl(
234 const FieldDef &field) const {
235 const auto &constant = field.value.constant;
236 T v;
237 auto done = StringToNumber(constant.c_str(), &v);
238 FLATBUFFERS_ASSERT(done);
239 if (done) {
240#if (!defined(_MSC_VER) || (_MSC_VER >= 1800))
241 if (std::isnan(v)) return NaN(v);
242 if (std::isinf(v)) return Inf(v);
243#endif
244 return Value(v, constant);
245 }
246 return "#"; // compile time error
247}
248
249std::string FloatConstantGenerator::GenFloatConstant(
250 const FieldDef &field) const {
251 switch (field.value.type.base_type) {
252 case BASE_TYPE_FLOAT: return GenFloatConstantImpl<float>(field);
253 case BASE_TYPE_DOUBLE: return GenFloatConstantImpl<double>(field);
254 default: {
255 FLATBUFFERS_ASSERT(false);
256 return "INVALID_BASE_TYPE";
257 }
258 };
259}
260
261TypedFloatConstantGenerator::TypedFloatConstantGenerator(
262 const char *double_prefix, const char *single_prefix,
263 const char *nan_number, const char *pos_inf_number,
264 const char *neg_inf_number)
265 : double_prefix_(double_prefix),
266 single_prefix_(single_prefix),
267 nan_number_(nan_number),
268 pos_inf_number_(pos_inf_number),
269 neg_inf_number_(neg_inf_number) {}
270
271std::string TypedFloatConstantGenerator::MakeNaN(
272 const std::string &prefix) const {
273 return prefix + nan_number_;
274}
275std::string TypedFloatConstantGenerator::MakeInf(
276 bool neg, const std::string &prefix) const {
277 if (neg)
278 return !neg_inf_number_.empty() ? (prefix + neg_inf_number_)
279 : ("-" + prefix + pos_inf_number_);
280 else
281 return prefix + pos_inf_number_;
282}
283
284std::string TypedFloatConstantGenerator::Value(double v,
285 const std::string &src) const {
286 (void)v;
287 return src;
288}
289
290std::string TypedFloatConstantGenerator::Inf(double v) const {
291 return MakeInf(v < 0, double_prefix_);
292}
293
294std::string TypedFloatConstantGenerator::NaN(double v) const {
295 (void)v;
296 return MakeNaN(double_prefix_);
297}
298
299std::string TypedFloatConstantGenerator::Value(float v,
300 const std::string &src) const {
301 (void)v;
302 return src + "f";
303}
304
305std::string TypedFloatConstantGenerator::Inf(float v) const {
306 return MakeInf(v < 0, single_prefix_);
307}
308
309std::string TypedFloatConstantGenerator::NaN(float v) const {
310 (void)v;
311 return MakeNaN(single_prefix_);
312}
313
314SimpleFloatConstantGenerator::SimpleFloatConstantGenerator(
315 const char *nan_number, const char *pos_inf_number,
316 const char *neg_inf_number)
317 : nan_number_(nan_number),
318 pos_inf_number_(pos_inf_number),
319 neg_inf_number_(neg_inf_number) {}
320
321std::string SimpleFloatConstantGenerator::Value(double v,
322 const std::string &src) const {
323 (void)v;
324 return src;
325}
326
327std::string SimpleFloatConstantGenerator::Inf(double v) const {
328 return (v < 0) ? neg_inf_number_ : pos_inf_number_;
329}
330
331std::string SimpleFloatConstantGenerator::NaN(double v) const {
332 (void)v;
333 return nan_number_;
334}
335
336std::string SimpleFloatConstantGenerator::Value(float v,
337 const std::string &src) const {
338 return this->Value(static_cast<double>(v), src);
339}
340
341std::string SimpleFloatConstantGenerator::Inf(float v) const {
342 return this->Inf(static_cast<double>(v));
343}
344
345std::string SimpleFloatConstantGenerator::NaN(float v) const {
346 return this->NaN(static_cast<double>(v));
347}
348
Austin Schuh272c6132020-11-14 16:37:52 -0800349
James Kuszmaul8e62b022022-03-22 09:33:25 -0700350std::string JavaMakeRule(const Parser &parser, const std::string &path,
351 const std::string &file_name) {
352 return JavaCSharpMakeRule(true, parser, path, file_name);
353}
354std::string CSharpMakeRule(const Parser &parser, const std::string &path,
355 const std::string &file_name) {
356 return JavaCSharpMakeRule(false, parser, path, file_name);
357}
358
Austin Schuh272c6132020-11-14 16:37:52 -0800359bool GenerateBinary(const Parser &parser, const std::string &path,
360 const std::string &file_name) {
361 if (parser.opts.use_flexbuffers) {
362 auto data_vec = parser.flex_builder_.GetBuffer();
363 auto data_ptr = reinterpret_cast<char *>(data(data_vec));
364 return !parser.flex_builder_.GetSize() ||
365 flatbuffers::SaveFile(
366 BinaryFileName(parser, path, file_name).c_str(), data_ptr,
367 parser.flex_builder_.GetSize(), true);
368 }
369 return !parser.builder_.GetSize() ||
370 flatbuffers::SaveFile(
371 BinaryFileName(parser, path, file_name).c_str(),
372 reinterpret_cast<char *>(parser.builder_.GetBufferPointer()),
373 parser.builder_.GetSize(), true);
374}
375
376std::string BinaryMakeRule(const Parser &parser, const std::string &path,
377 const std::string &file_name) {
378 if (!parser.builder_.GetSize()) return "";
379 std::string filebase =
380 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
381 std::string make_rule =
382 BinaryFileName(parser, path, filebase) + ": " + file_name;
383 auto included_files =
384 parser.GetIncludedFilesRecursive(parser.root_struct_def_->file);
385 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
386 make_rule += " " + *it;
387 }
388 return make_rule;
389}
390
Austin Schuhe89fa2d2019-08-14 20:24:23 -0700391} // namespace flatbuffers
392
393#if defined(_MSC_VER)
394# pragma warning(pop)
395#endif