• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2018 Dan Field
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 // independent from idl_parser, since this code is not needed for most clients
18 #include <cassert>
19 
20 #include "flatbuffers/code_generators.h"
21 #include "flatbuffers/flatbuffers.h"
22 #include "flatbuffers/idl.h"
23 #include "flatbuffers/util.h"
24 
25 namespace flatbuffers {
26 
27 namespace dart {
28 
29 const std::string _kFb = "fb";
30 // see https://www.dartlang.org/guides/language/language-tour#keywords
31 // yeild*, async*, and sync* shouldn't be problems anyway but keeping them in
32 static const char *keywords[] = {
33   "abstract",   "deferred", "if",       "super",   "as",       "do",
34   "implements", "switch",   "assert",   "dynamic", "import",   "sync*",
35   "async",      "else",     "in",       "this",    "async*",   "enum",
36   "is",         "throw",    "await",    "export",  "library",  "true",
37   "break",      "external", "new",      "try",     "case",     "extends",
38   "null",       "typedef",  "catch",    "factory", "operator", "var",
39   "class",      "false",    "part",     "void",    "const",    "final",
40   "rethrow",    "while",    "continue", "finally", "return",   "with",
41   "covariant",  "for",      "set",      "yield",   "default",  "get",
42   "static",     "yield*"
43 };
44 
45 // Iterate through all definitions we haven't generate code for (enums, structs,
46 // and tables) and output them to a single file.
47 class DartGenerator : public BaseGenerator {
48  public:
49   typedef std::map<std::string, std::string> namespace_code_map;
50 
DartGenerator(const Parser & parser,const std::string & path,const std::string & file_name)51   DartGenerator(const Parser &parser, const std::string &path,
52                 const std::string &file_name)
53       : BaseGenerator(parser, path, file_name, "", ".", "dart") {}
54   // Iterate through all definitions we haven't generate code for (enums,
55   // structs, and tables) and output them to a single file.
generate()56   bool generate() {
57     std::string code;
58     namespace_code_map namespace_code;
59     GenerateEnums(&namespace_code);
60     GenerateStructs(&namespace_code);
61 
62     for (auto kv = namespace_code.begin(); kv != namespace_code.end(); ++kv) {
63       code.clear();
64       code = code + "// " + FlatBuffersGeneratedWarning() + "\n";
65       code = code +
66              "// ignore_for_file: unused_import, unused_field, "
67              "unused_local_variable\n\n";
68 
69       if (!kv->first.empty()) { code += "library " + kv->first + ";\n\n"; }
70 
71       code += "import 'dart:typed_data' show Uint8List;\n";
72       code += "import 'package:flat_buffers/flat_buffers.dart' as " + _kFb +
73               ";\n\n";
74 
75       for (auto kv2 = namespace_code.begin(); kv2 != namespace_code.end();
76            ++kv2) {
77         if (kv2->first != kv->first) {
78           code +=
79               "import '" +
80               GeneratedFileName(
81                   "./",
82                   file_name_ + (!kv2->first.empty() ? "_" + kv2->first : ""),
83                   parser_.opts) +
84               "' as " + ImportAliasName(kv2->first) + ";\n";
85         }
86       }
87       code += "\n";
88       code += kv->second;
89 
90       if (!SaveFile(
91               GeneratedFileName(
92                   path_,
93                   file_name_ + (!kv->first.empty() ? "_" + kv->first : ""),
94                   parser_.opts)
95                   .c_str(),
96               code, false)) {
97         return false;
98       }
99     }
100     return true;
101   }
102 
103  private:
ImportAliasName(const std::string & ns)104   static std::string ImportAliasName(const std::string &ns) {
105     std::string ret;
106     ret.assign(ns);
107     size_t pos = ret.find('.');
108     while (pos != std::string::npos) {
109       ret.replace(pos, 1, "_");
110       pos = ret.find('.', pos + 1);
111     }
112 
113     return ret;
114   }
115 
BuildNamespaceName(const Namespace & ns)116   static std::string BuildNamespaceName(const Namespace &ns) {
117     if (ns.components.empty()) { return ""; }
118     std::stringstream sstream;
119     std::copy(ns.components.begin(), ns.components.end() - 1,
120               std::ostream_iterator<std::string>(sstream, "."));
121 
122     auto ret = sstream.str() + ns.components.back();
123     for (size_t i = 0; i < ret.size(); i++) {
124       auto lower = CharToLower(ret[i]);
125       if (lower != ret[i]) {
126         ret[i] = lower;
127         if (i != 0 && ret[i - 1] != '.') {
128           ret.insert(i, "_");
129           i++;
130         }
131       }
132     }
133     // std::transform(ret.begin(), ret.end(), ret.begin(), CharToLower);
134     return ret;
135   }
136 
GenIncludeDependencies(std::string * code,const std::string & the_namespace)137   void GenIncludeDependencies(std::string *code,
138                               const std::string &the_namespace) {
139     for (auto it = parser_.included_files_.begin();
140          it != parser_.included_files_.end(); ++it) {
141       if (it->second.empty()) continue;
142 
143       auto noext = flatbuffers::StripExtension(it->second);
144       auto basename = flatbuffers::StripPath(noext);
145 
146       *code +=
147           "import '" +
148           GeneratedFileName(
149               "", basename + (the_namespace == "" ? "" : "_" + the_namespace),
150               parser_.opts) +
151           "';\n";
152     }
153   }
154 
EscapeKeyword(const std::string & name)155   static std::string EscapeKeyword(const std::string &name) {
156     for (size_t i = 0; i < sizeof(keywords) / sizeof(keywords[0]); i++) {
157       if (name == keywords[i]) { return MakeCamel(name + "_", false); }
158     }
159 
160     return MakeCamel(name, false);
161   }
162 
GenerateEnums(namespace_code_map * namespace_code)163   void GenerateEnums(namespace_code_map *namespace_code) {
164     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
165          ++it) {
166       auto &enum_def = **it;
167       GenEnum(enum_def, namespace_code);  // enum_code_ptr);
168     }
169   }
170 
GenerateStructs(namespace_code_map * namespace_code)171   void GenerateStructs(namespace_code_map *namespace_code) {
172     for (auto it = parser_.structs_.vec.begin();
173          it != parser_.structs_.vec.end(); ++it) {
174       auto &struct_def = **it;
175       GenStruct(struct_def, namespace_code);
176     }
177   }
178 
179   // Generate a documentation comment, if available.
GenDocComment(const std::vector<std::string> & dc,std::string * code_ptr,const std::string & extra_lines,const char * indent=nullptr)180   static void GenDocComment(const std::vector<std::string> &dc,
181                             std::string *code_ptr,
182                             const std::string &extra_lines,
183                             const char *indent = nullptr) {
184     if (dc.empty() && extra_lines.empty()) {
185       // Don't output empty comment blocks with 0 lines of comment content.
186       return;
187     }
188 
189     auto &code = *code_ptr;
190 
191     for (auto it = dc.begin(); it != dc.end(); ++it) {
192       if (indent) code += indent;
193       code += "/// " + *it + "\n";
194     }
195     if (!extra_lines.empty()) {
196       if (!dc.empty()) {
197         if (indent) code += indent;
198         code += "///\n";
199       }
200       if (indent) code += indent;
201       std::string::size_type start = 0;
202       for (;;) {
203         auto end = extra_lines.find('\n', start);
204         if (end != std::string::npos) {
205           code += "/// " + extra_lines.substr(start, end - start) + "\n";
206           start = end + 1;
207         } else {
208           code += "/// " + extra_lines.substr(start) + "\n";
209           break;
210         }
211       }
212     }
213   }
214 
GenDocComment(std::string * code_ptr,const std::string & extra_lines)215   static void GenDocComment(std::string *code_ptr,
216                             const std::string &extra_lines) {
217     GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
218   }
219 
220   // Generate an enum declaration and an enum string lookup table.
GenEnum(EnumDef & enum_def,namespace_code_map * namespace_code)221   void GenEnum(EnumDef &enum_def, namespace_code_map *namespace_code) {
222     if (enum_def.generated) return;
223     auto ns = BuildNamespaceName(*enum_def.defined_namespace);
224     std::string code;
225     GenDocComment(enum_def.doc_comment, &code, "");
226 
227     auto name = enum_def.is_union ? enum_def.name + "TypeId" : enum_def.name;
228     auto is_bit_flags = enum_def.attributes.Lookup("bit_flags");
229 
230     code += "class " + name + " {\n";
231     code += "  final int value;\n";
232     code += "  const " + name + "._(this.value);\n\n";
233     code += "  factory " + name + ".fromValue(int value) {\n";
234     code += "    if (value == null) value = 0;\n";
235 
236     code += "    if (!values.containsKey(value)) {\n";
237     code +=
238         "      throw new StateError('Invalid value $value for bit flag enum ";
239     code += name + "');\n";
240     code += "    }\n";
241 
242     code += "    return values[value];\n";
243     code += "  }\n\n";
244 
245     // this is meaningless for bit_flags
246     // however, note that unlike "regular" dart enums this enum can still have
247     // holes.
248     if (!is_bit_flags) {
249       code += "  static const int minValue = " +
250               enum_def.ToString(*enum_def.MinValue()) + ";\n";
251       code += "  static const int maxValue = " +
252               enum_def.ToString(*enum_def.MaxValue()) + ";\n";
253     }
254 
255     code +=
256         "  static bool containsValue(int value) =>"
257         " values.containsKey(value);\n\n";
258 
259     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
260       auto &ev = **it;
261 
262       if (!ev.doc_comment.empty()) {
263         if (it != enum_def.Vals().begin()) { code += '\n'; }
264         GenDocComment(ev.doc_comment, &code, "", "  ");
265       }
266       code += "  static const " + name + " " + ev.name + " = ";
267       code += "const " + name + "._(" + enum_def.ToString(ev) + ");\n";
268     }
269 
270     code += "  static const Map<int," + name + "> values = {";
271     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
272       auto &ev = **it;
273       code += enum_def.ToString(ev) + ": " + ev.name + ",";
274     }
275     code += "};\n\n";
276 
277     code += "  static const " + _kFb + ".Reader<" + name +
278             "> reader = const _" + name + "Reader();\n\n";
279     code += "  @override\n";
280     code += "  String toString() {\n";
281     code += "    return '" + name + "{value: $value}';\n";
282     code += "  }\n";
283     code += "}\n\n";
284 
285     GenEnumReader(enum_def, name, &code);
286     (*namespace_code)[ns] += code;
287   }
288 
GenEnumReader(EnumDef & enum_def,const std::string & name,std::string * code_ptr)289   void GenEnumReader(EnumDef &enum_def, const std::string &name,
290                      std::string *code_ptr) {
291     auto &code = *code_ptr;
292 
293     code += "class _" + name + "Reader extends " + _kFb + ".Reader<" + name +
294             "> {\n";
295     code += "  const _" + name + "Reader();\n\n";
296     code += "  @override\n";
297     code += "  int get size => 1;\n\n";
298     code += "  @override\n";
299     code +=
300         "  " + name + " read(" + _kFb + ".BufferContext bc, int offset) =>\n";
301     code += "      new " + name + ".fromValue(const " + _kFb + "." +
302             GenType(enum_def.underlying_type) + "Reader().read(bc, offset));\n";
303     code += "}\n\n";
304   }
305 
GenType(const Type & type)306   static std::string GenType(const Type &type) {
307     switch (type.base_type) {
308       case BASE_TYPE_BOOL: return "Bool";
309       case BASE_TYPE_CHAR: return "Int8";
310       case BASE_TYPE_UTYPE:
311       case BASE_TYPE_UCHAR: return "Uint8";
312       case BASE_TYPE_SHORT: return "Int16";
313       case BASE_TYPE_USHORT: return "Uint16";
314       case BASE_TYPE_INT: return "Int32";
315       case BASE_TYPE_UINT: return "Uint32";
316       case BASE_TYPE_LONG: return "Int64";
317       case BASE_TYPE_ULONG: return "Uint64";
318       case BASE_TYPE_FLOAT: return "Float32";
319       case BASE_TYPE_DOUBLE: return "Float64";
320       case BASE_TYPE_STRING: return "String";
321       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
322       case BASE_TYPE_STRUCT: return type.struct_def->name;
323       case BASE_TYPE_UNION: return type.enum_def->name + "TypeId";
324       default: return "Table";
325     }
326   }
327 
GenReaderTypeName(const Type & type,Namespace * current_namespace,const FieldDef & def,bool parent_is_vector=false)328   std::string GenReaderTypeName(const Type &type, Namespace *current_namespace,
329                                 const FieldDef &def,
330                                 bool parent_is_vector = false) {
331     if (type.base_type == BASE_TYPE_BOOL) {
332       return "const " + _kFb + ".BoolReader()";
333     } else if (IsVector(type)) {
334       return "const " + _kFb + ".ListReader<" +
335              GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" +
336              GenReaderTypeName(type.VectorType(), current_namespace, def,
337                                true) +
338              ")";
339     } else if (IsString(type)) {
340       return "const " + _kFb + ".StringReader()";
341     }
342     if (IsScalar(type.base_type)) {
343       if (type.enum_def && parent_is_vector) {
344         return GenDartTypeName(type, current_namespace, def) + ".reader";
345       }
346       return "const " + _kFb + "." + GenType(type) + "Reader()";
347     } else {
348       return GenDartTypeName(type, current_namespace, def) + ".reader";
349     }
350   }
351 
GenDartTypeName(const Type & type,Namespace * current_namespace,const FieldDef & def,bool addBuilder=false)352   std::string GenDartTypeName(const Type &type, Namespace *current_namespace,
353                               const FieldDef &def, bool addBuilder = false) {
354     if (type.enum_def) {
355       if (type.enum_def->is_union && type.base_type != BASE_TYPE_UNION) {
356         return type.enum_def->name + "TypeId";
357       } else if (type.enum_def->is_union) {
358         return "dynamic";
359       } else if (type.base_type != BASE_TYPE_VECTOR) {
360         return type.enum_def->name;
361       }
362     }
363 
364     switch (type.base_type) {
365       case BASE_TYPE_BOOL: return "bool";
366       case BASE_TYPE_LONG:
367       case BASE_TYPE_ULONG:
368       case BASE_TYPE_INT:
369       case BASE_TYPE_UINT:
370       case BASE_TYPE_SHORT:
371       case BASE_TYPE_USHORT:
372       case BASE_TYPE_CHAR:
373       case BASE_TYPE_UCHAR: return "int";
374       case BASE_TYPE_FLOAT:
375       case BASE_TYPE_DOUBLE: return "double";
376       case BASE_TYPE_STRING: return "String";
377       case BASE_TYPE_STRUCT:
378         return MaybeWrapNamespace(
379             type.struct_def->name + (addBuilder ? "ObjectBuilder" : ""),
380             current_namespace, def);
381       case BASE_TYPE_VECTOR:
382         return "List<" +
383                GenDartTypeName(type.VectorType(), current_namespace, def,
384                                addBuilder) +
385                ">";
386       default: assert(0); return "dynamic";
387     }
388   }
389 
MaybeWrapNamespace(const std::string & type_name,Namespace * current_ns,const FieldDef & field)390   static const std::string MaybeWrapNamespace(const std::string &type_name,
391                                               Namespace *current_ns,
392                                               const FieldDef &field) {
393     auto curr_ns_str = BuildNamespaceName(*current_ns);
394     std::string field_ns_str = "";
395     if (field.value.type.struct_def) {
396       field_ns_str +=
397           BuildNamespaceName(*field.value.type.struct_def->defined_namespace);
398     } else if (field.value.type.enum_def) {
399       field_ns_str +=
400           BuildNamespaceName(*field.value.type.enum_def->defined_namespace);
401     }
402 
403     if (field_ns_str != "" && field_ns_str != curr_ns_str) {
404       return ImportAliasName(field_ns_str) + "." + type_name;
405     } else {
406       return type_name;
407     }
408   }
409 
410   // Generate an accessor struct with constructor for a flatbuffers struct.
GenStruct(const StructDef & struct_def,namespace_code_map * namespace_code)411   void GenStruct(const StructDef &struct_def,
412                  namespace_code_map *namespace_code) {
413     if (struct_def.generated) return;
414 
415     auto object_namespace = BuildNamespaceName(*struct_def.defined_namespace);
416     std::string code;
417 
418     const auto &object_name = struct_def.name;
419 
420     // Emit constructor
421 
422     GenDocComment(struct_def.doc_comment, &code, "");
423 
424     auto reader_name = "_" + object_name + "Reader";
425     auto builder_name = object_name + "Builder";
426     auto object_builder_name = object_name + "ObjectBuilder";
427 
428     std::string reader_code, builder_code;
429 
430     code += "class " + object_name + " {\n";
431 
432     code += "  " + object_name + "._(this._bc, this._bcOffset);\n";
433     if (!struct_def.fixed) {
434       code += "  factory " + object_name + "(List<int> bytes) {\n";
435       code += "    " + _kFb + ".BufferContext rootRef = new " + _kFb +
436               ".BufferContext.fromBytes(bytes);\n";
437       code += "    return reader.read(rootRef, 0);\n";
438       code += "  }\n";
439     }
440 
441     code += "\n";
442     code += "  static const " + _kFb + ".Reader<" + object_name +
443             "> reader = const " + reader_name + "();\n\n";
444 
445     code += "  final " + _kFb + ".BufferContext _bc;\n";
446     code += "  final int _bcOffset;\n\n";
447 
448     std::vector<std::pair<int, FieldDef *>> non_deprecated_fields;
449     for (auto it = struct_def.fields.vec.begin();
450          it != struct_def.fields.vec.end(); ++it) {
451       auto &field = **it;
452       if (field.deprecated) continue;
453       auto offset = static_cast<int>(it - struct_def.fields.vec.begin());
454       non_deprecated_fields.push_back(std::make_pair(offset, &field));
455     }
456 
457     GenImplementationGetters(struct_def, non_deprecated_fields, &code);
458 
459     code += "}\n\n";
460 
461     GenReader(struct_def, &reader_name, &reader_code);
462     GenBuilder(struct_def, non_deprecated_fields, &builder_name, &builder_code);
463     GenObjectBuilder(struct_def, non_deprecated_fields, &object_builder_name,
464                      &builder_code);
465 
466     code += reader_code;
467     code += builder_code;
468 
469     (*namespace_code)[object_namespace] += code;
470   }
471 
NamespaceAliasFromUnionType(Namespace * root_namespace,const Type & type)472   std::string NamespaceAliasFromUnionType(Namespace *root_namespace,
473                                           const Type &type) {
474     const std::vector<std::string> qualified_name_parts =
475         type.struct_def->defined_namespace->components;
476     if (std::equal(root_namespace->components.begin(),
477                    root_namespace->components.end(),
478                    qualified_name_parts.begin())) {
479       return type.struct_def->name;
480     }
481 
482     std::string ns;
483 
484     for (auto it = qualified_name_parts.begin();
485          it != qualified_name_parts.end(); ++it) {
486       auto &part = *it;
487 
488       for (size_t i = 0; i < part.length(); i++) {
489         if (i && !isdigit(part[i]) && part[i] == CharToUpper(part[i])) {
490           ns += "_";
491           ns += CharToLower(part[i]);
492         } else {
493           ns += CharToLower(part[i]);
494         }
495       }
496       if (it != qualified_name_parts.end() - 1) { ns += "_"; }
497     }
498 
499     return ns + "." + type.struct_def->name;
500   }
501 
GenImplementationGetters(const StructDef & struct_def,std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * code_ptr)502   void GenImplementationGetters(
503       const StructDef &struct_def,
504       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
505       std::string *code_ptr) {
506     auto &code = *code_ptr;
507 
508     for (auto it = non_deprecated_fields.begin();
509          it != non_deprecated_fields.end(); ++it) {
510       auto pair = *it;
511       auto &field = *pair.second;
512 
513       std::string field_name = MakeCamel(field.name, false);
514       std::string type_name = GenDartTypeName(
515           field.value.type, struct_def.defined_namespace, field, false);
516 
517       GenDocComment(field.doc_comment, &code, "", "  ");
518 
519       code += "  " + type_name + " get " + field_name;
520       if (field.value.type.base_type == BASE_TYPE_UNION) {
521         code += " {\n";
522         code += "    switch (" + field_name + "Type?.value) {\n";
523         auto &enum_def = *field.value.type.enum_def;
524         for (auto en_it = enum_def.Vals().begin() + 1;
525              en_it != enum_def.Vals().end(); ++en_it) {
526           auto &ev = **en_it;
527 
528           auto enum_name = NamespaceAliasFromUnionType(
529               enum_def.defined_namespace, ev.union_type);
530           code += "      case " + enum_def.ToString(ev) + ": return " +
531                   enum_name + ".reader.vTableGet(_bc, _bcOffset, " +
532                   NumToString(field.value.offset) + ", null);\n";
533         }
534         code += "      default: return null;\n";
535         code += "    }\n";
536         code += "  }\n";
537       } else {
538         code += " => ";
539         if (field.value.type.enum_def &&
540             field.value.type.base_type != BASE_TYPE_VECTOR) {
541           code += "new " +
542                   GenDartTypeName(field.value.type,
543                                   struct_def.defined_namespace, field) +
544                   ".fromValue(";
545         }
546 
547         code += GenReaderTypeName(field.value.type,
548                                   struct_def.defined_namespace, field);
549         if (struct_def.fixed) {
550           code +=
551               ".read(_bc, _bcOffset + " + NumToString(field.value.offset) + ")";
552         } else {
553           code += ".vTableGet(_bc, _bcOffset, " +
554                   NumToString(field.value.offset) + ", ";
555           if (!field.value.constant.empty() && field.value.constant != "0") {
556             if (IsBool(field.value.type.base_type)) {
557               code += "true";
558             } else if (field.value.constant == "nan" ||
559                        field.value.constant == "+nan" ||
560                        field.value.constant == "-nan") {
561               code += "double.nan";
562             } else if (field.value.constant == "inf" ||
563                        field.value.constant == "+inf") {
564               code += "double.infinity";
565             } else if (field.value.constant == "-inf") {
566               code += "double.negativeInfinity";
567             } else {
568               code += field.value.constant;
569             }
570           } else {
571             if (IsBool(field.value.type.base_type)) {
572               code += "false";
573             } else if (IsScalar(field.value.type.base_type)) {
574               code += "0";
575             } else {
576               code += "null";
577             }
578           }
579           code += ")";
580         }
581         if (field.value.type.enum_def &&
582             field.value.type.base_type != BASE_TYPE_VECTOR) {
583           code += ")";
584         }
585         code += ";\n";
586       }
587     }
588 
589     code += "\n";
590 
591     code += "  @override\n";
592     code += "  String toString() {\n";
593     code += "    return '" + struct_def.name + "{";
594     for (auto it = non_deprecated_fields.begin();
595          it != non_deprecated_fields.end(); ++it) {
596       auto pair = *it;
597       auto &field = *pair.second;
598       code +=
599           MakeCamel(field.name, false) + ": $" + MakeCamel(field.name, false);
600       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
601     }
602     code += "}';\n";
603     code += "  }\n";
604   }
605 
GenReader(const StructDef & struct_def,std::string * reader_name_ptr,std::string * code_ptr)606   void GenReader(const StructDef &struct_def, std::string *reader_name_ptr,
607                  std::string *code_ptr) {
608     auto &code = *code_ptr;
609     auto &reader_name = *reader_name_ptr;
610     auto &impl_name = struct_def.name;
611 
612     code += "class " + reader_name + " extends " + _kFb;
613     if (struct_def.fixed) {
614       code += ".StructReader<";
615     } else {
616       code += ".TableReader<";
617     }
618     code += impl_name + "> {\n";
619     code += "  const " + reader_name + "();\n\n";
620 
621     if (struct_def.fixed) {
622       code += "  @override\n";
623       code += "  int get size => " + NumToString(struct_def.bytesize) + ";\n\n";
624     }
625     code += "  @override\n";
626     code += "  " + impl_name +
627             " createObject(fb.BufferContext bc, int offset) => \n    new " +
628             impl_name + "._(bc, offset);\n";
629     code += "}\n\n";
630   }
631 
GenBuilder(const StructDef & struct_def,std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * builder_name_ptr,std::string * code_ptr)632   void GenBuilder(const StructDef &struct_def,
633                   std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
634                   std::string *builder_name_ptr, std::string *code_ptr) {
635     if (non_deprecated_fields.size() == 0) { return; }
636     auto &code = *code_ptr;
637     auto &builder_name = *builder_name_ptr;
638 
639     code += "class " + builder_name + " {\n";
640     code += "  " + builder_name + "(this.fbBuilder) {\n";
641     code += "    assert(fbBuilder != null);\n";
642     code += "  }\n\n";
643     code += "  final " + _kFb + ".Builder fbBuilder;\n\n";
644 
645     if (struct_def.fixed) {
646       StructBuilderBody(struct_def, non_deprecated_fields, code_ptr);
647     } else {
648       TableBuilderBody(struct_def, non_deprecated_fields, code_ptr);
649     }
650 
651     code += "}\n\n";
652   }
653 
StructBuilderBody(const StructDef & struct_def,std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * code_ptr)654   void StructBuilderBody(
655       const StructDef &struct_def,
656       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
657       std::string *code_ptr) {
658     auto &code = *code_ptr;
659 
660     code += "  int finish(";
661     for (auto it = non_deprecated_fields.begin();
662          it != non_deprecated_fields.end(); ++it) {
663       auto pair = *it;
664       auto &field = *pair.second;
665 
666       if (IsStruct(field.value.type)) {
667         code += "fb.StructBuilder";
668       } else {
669         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
670                                 field);
671       }
672       code += " " + field.name;
673       if (it != non_deprecated_fields.end() - 1) { code += ", "; }
674     }
675     code += ") {\n";
676 
677     for (auto it = non_deprecated_fields.rbegin();
678          it != non_deprecated_fields.rend(); ++it) {
679       auto pair = *it;
680       auto &field = *pair.second;
681 
682       if (field.padding) {
683         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
684       }
685 
686       if (IsStruct(field.value.type)) {
687         code += "    " + field.name + "();\n";
688       } else {
689         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
690         code += field.name;
691         if (field.value.type.enum_def) { code += "?.value"; }
692         code += ");\n";
693       }
694     }
695     code += "    return fbBuilder.offset;\n";
696     code += "  }\n\n";
697   }
698 
TableBuilderBody(const StructDef & struct_def,std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * code_ptr)699   void TableBuilderBody(
700       const StructDef &struct_def,
701       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
702       std::string *code_ptr) {
703     auto &code = *code_ptr;
704 
705     code += "  void begin() {\n";
706     code += "    fbBuilder.startTable();\n";
707     code += "  }\n\n";
708 
709     for (auto it = non_deprecated_fields.begin();
710          it != non_deprecated_fields.end(); ++it) {
711       auto pair = *it;
712       auto &field = *pair.second;
713       auto offset = pair.first;
714 
715       if (IsScalar(field.value.type.base_type)) {
716         code += "  int add" + MakeCamel(field.name) + "(";
717         code += GenDartTypeName(field.value.type, struct_def.defined_namespace,
718                                 field);
719         code += " " + MakeCamel(field.name, false) + ") {\n";
720         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
721                 NumToString(offset) + ", ";
722         code += MakeCamel(field.name, false);
723         if (field.value.type.enum_def) { code += "?.value"; }
724         code += ");\n";
725       } else if (IsStruct(field.value.type)) {
726         code += "  int add" + MakeCamel(field.name) + "(int offset) {\n";
727         code +=
728             "    fbBuilder.addStruct(" + NumToString(offset) + ", offset);\n";
729       } else {
730         code += "  int add" + MakeCamel(field.name) + "Offset(int offset) {\n";
731         code +=
732             "    fbBuilder.addOffset(" + NumToString(offset) + ", offset);\n";
733       }
734       code += "    return fbBuilder.offset;\n";
735       code += "  }\n";
736     }
737 
738     code += "\n";
739     code += "  int finish() {\n";
740     code += "    return fbBuilder.endTable();\n";
741     code += "  }\n";
742   }
743 
GenObjectBuilder(const StructDef & struct_def,std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * builder_name_ptr,std::string * code_ptr)744   void GenObjectBuilder(
745       const StructDef &struct_def,
746       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
747       std::string *builder_name_ptr, std::string *code_ptr) {
748     auto &code = *code_ptr;
749     auto &builder_name = *builder_name_ptr;
750 
751     code += "class " + builder_name + " extends " + _kFb + ".ObjectBuilder {\n";
752     for (auto it = non_deprecated_fields.begin();
753          it != non_deprecated_fields.end(); ++it) {
754       auto pair = *it;
755       auto &field = *pair.second;
756 
757       code += "  final " +
758               GenDartTypeName(field.value.type, struct_def.defined_namespace,
759                               field, true) +
760               " _" + MakeCamel(field.name, false) + ";\n";
761     }
762     code += "\n";
763     code += "  " + builder_name + "(";
764 
765     if (non_deprecated_fields.size() != 0) {
766       code += "{\n";
767       for (auto it = non_deprecated_fields.begin();
768            it != non_deprecated_fields.end(); ++it) {
769         auto pair = *it;
770         auto &field = *pair.second;
771 
772         code += "    " +
773                 GenDartTypeName(field.value.type, struct_def.defined_namespace,
774                                 field, true) +
775                 " " + MakeCamel(field.name, false) + ",\n";
776       }
777       code += "  })\n";
778       code += "      : ";
779       for (auto it = non_deprecated_fields.begin();
780            it != non_deprecated_fields.end(); ++it) {
781         auto pair = *it;
782         auto &field = *pair.second;
783 
784         code += "_" + MakeCamel(field.name, false) + " = " +
785                 MakeCamel(field.name, false);
786         if (it == non_deprecated_fields.end() - 1) {
787           code += ";\n\n";
788         } else {
789           code += ",\n        ";
790         }
791       }
792     } else {
793       code += ");\n\n";
794     }
795 
796     code += "  /// Finish building, and store into the [fbBuilder].\n";
797     code += "  @override\n";
798     code += "  int finish(\n";
799     code += "    " + _kFb + ".Builder fbBuilder) {\n";
800     code += "    assert(fbBuilder != null);\n";
801 
802     for (auto it = non_deprecated_fields.begin();
803          it != non_deprecated_fields.end(); ++it) {
804       auto pair = *it;
805       auto &field = *pair.second;
806 
807       if (IsScalar(field.value.type.base_type) || IsStruct(field.value.type))
808         continue;
809 
810       code += "    final int " + MakeCamel(field.name, false) + "Offset";
811       if (IsVector(field.value.type)) {
812         code +=
813             " = _" + MakeCamel(field.name, false) + "?.isNotEmpty == true\n";
814         code += "        ? fbBuilder.writeList";
815         switch (field.value.type.VectorType().base_type) {
816           case BASE_TYPE_STRING:
817             code += "(_" + MakeCamel(field.name, false) +
818                     ".map((b) => fbBuilder.writeString(b)).toList())";
819             break;
820           case BASE_TYPE_STRUCT:
821             if (field.value.type.struct_def->fixed) {
822               code += "OfStructs(_" + MakeCamel(field.name, false) + ")";
823             } else {
824               code += "(_" + MakeCamel(field.name, false) +
825                       ".map((b) => b.getOrCreateOffset(fbBuilder)).toList())";
826             }
827             break;
828           default:
829             code += GenType(field.value.type.VectorType()) + "(_" +
830                     MakeCamel(field.name, false);
831             if (field.value.type.enum_def) { code += ".map((f) => f.value)"; }
832             code += ")";
833         }
834         code += "\n        : null;\n";
835       } else if (IsString(field.value.type)) {
836         code += " = fbBuilder.writeString(_" + MakeCamel(field.name, false) +
837                 ");\n";
838       } else {
839         code += " = _" + MakeCamel(field.name, false) +
840                 "?.getOrCreateOffset(fbBuilder);\n";
841       }
842     }
843 
844     code += "\n";
845     if (struct_def.fixed) {
846       StructObjectBuilderBody(non_deprecated_fields, code_ptr);
847     } else {
848       TableObjectBuilderBody(non_deprecated_fields, code_ptr);
849     }
850     code += "  }\n\n";
851 
852     code += "  /// Convenience method to serialize to byte list.\n";
853     code += "  @override\n";
854     code += "  Uint8List toBytes([String fileIdentifier]) {\n";
855     code += "    " + _kFb + ".Builder fbBuilder = new ";
856     code += _kFb + ".Builder();\n";
857     code += "    int offset = finish(fbBuilder);\n";
858     code += "    return fbBuilder.finish(offset, fileIdentifier);\n";
859     code += "  }\n";
860     code += "}\n";
861   }
862 
StructObjectBuilderBody(std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * code_ptr,bool prependUnderscore=true)863   void StructObjectBuilderBody(
864       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
865       std::string *code_ptr, bool prependUnderscore = true) {
866     auto &code = *code_ptr;
867 
868     for (auto it = non_deprecated_fields.rbegin();
869          it != non_deprecated_fields.rend(); ++it) {
870       auto pair = *it;
871       auto &field = *pair.second;
872 
873       if (field.padding) {
874         code += "    fbBuilder.pad(" + NumToString(field.padding) + ");\n";
875       }
876 
877       if (IsStruct(field.value.type)) {
878         code += "    ";
879         if (prependUnderscore) { code += "_"; }
880         code += field.name + ".finish(fbBuilder);\n";
881       } else {
882         code += "    fbBuilder.put" + GenType(field.value.type) + "(";
883         if (prependUnderscore) { code += "_"; }
884         code += field.name;
885         if (field.value.type.enum_def) { code += "?.value"; }
886         code += ");\n";
887       }
888     }
889 
890     code += "    return fbBuilder.offset;\n";
891   }
892 
TableObjectBuilderBody(std::vector<std::pair<int,FieldDef * >> non_deprecated_fields,std::string * code_ptr,bool prependUnderscore=true)893   void TableObjectBuilderBody(
894       std::vector<std::pair<int, FieldDef *>> non_deprecated_fields,
895       std::string *code_ptr, bool prependUnderscore = true) {
896     std::string &code = *code_ptr;
897     code += "    fbBuilder.startTable();\n";
898 
899     for (auto it = non_deprecated_fields.begin();
900          it != non_deprecated_fields.end(); ++it) {
901       auto pair = *it;
902       auto &field = *pair.second;
903       auto offset = pair.first;
904 
905       if (IsScalar(field.value.type.base_type)) {
906         code += "    fbBuilder.add" + GenType(field.value.type) + "(" +
907                 NumToString(offset) + ", ";
908         if (prependUnderscore) { code += "_"; }
909         code += MakeCamel(field.name, false);
910         if (field.value.type.enum_def) { code += "?.value"; }
911         code += ");\n";
912       } else if (IsStruct(field.value.type)) {
913         code += "    if (";
914         if (prependUnderscore) { code += "_"; }
915         code += MakeCamel(field.name, false) + " != null) {\n";
916         code += "      fbBuilder.addStruct(" + NumToString(offset) + ", ";
917         code += "_" + MakeCamel(field.name, false) + ".finish(fbBuilder));\n";
918         code += "    }\n";
919       } else {
920         code +=
921             "    if (" + MakeCamel(field.name, false) + "Offset != null) {\n";
922         code += "      fbBuilder.addOffset(" + NumToString(offset) + ", " +
923                 MakeCamel(field.name, false) + "Offset);\n";
924         code += "    }\n";
925       }
926     }
927     code += "    return fbBuilder.endTable();\n";
928   }
929 };
930 }  // namespace dart
931 
GenerateDart(const Parser & parser,const std::string & path,const std::string & file_name)932 bool GenerateDart(const Parser &parser, const std::string &path,
933                   const std::string &file_name) {
934   dart::DartGenerator generator(parser, path, file_name);
935   return generator.generate();
936 }
937 
DartMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)938 std::string DartMakeRule(const Parser &parser, const std::string &path,
939                          const std::string &file_name) {
940   assert(parser.opts.lang <= IDLOptions::kMAX);
941 
942   auto filebase =
943       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
944   dart::DartGenerator generator(parser, path, file_name);
945   auto make_rule =
946       generator.GeneratedFileName(path, file_name, parser.opts) + ": ";
947 
948   auto included_files = parser.GetIncludedFilesRecursive(file_name);
949   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
950     make_rule += " " + *it;
951   }
952   return make_rule;
953 }
954 
955 }  // namespace flatbuffers
956