84dc73747ce543a4c230577afbf2272922414bf6
[platform/upstream/flatbuffers.git] / src / idl_gen_js_ts.cpp
1 /*
2  * Copyright 2014 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 // independent from idl_parser, since this code is not needed for most clients
18 #include <cassert>
19 #include <unordered_map>
20 #include <unordered_set>
21
22 #include "flatbuffers/code_generators.h"
23 #include "flatbuffers/flatbuffers.h"
24 #include "flatbuffers/idl.h"
25 #include "flatbuffers/util.h"
26
27 namespace flatbuffers {
28
29 const std::string kGeneratedFileNamePostfix = "_generated";
30
31 struct JsTsLanguageParameters {
32   IDLOptions::Language language;
33   std::string file_extension;
34 };
35
36 struct ReexportDescription {
37   std::string symbol;
38   std::string source_namespace;
39   std::string target_namespace;
40 };
41
42 enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 };
43
44 const JsTsLanguageParameters &GetJsLangParams(IDLOptions::Language lang) {
45   static JsTsLanguageParameters js_language_parameters[] = {
46     {
47         IDLOptions::kJs,
48         ".js",
49     },
50     {
51         IDLOptions::kTs,
52         ".ts",
53     },
54   };
55
56   if (lang == IDLOptions::kJs) {
57     return js_language_parameters[0];
58   } else {
59     FLATBUFFERS_ASSERT(lang == IDLOptions::kTs);
60     return js_language_parameters[1];
61   }
62 }
63
64 static std::string GeneratedFileName(const std::string &path,
65                                      const std::string &file_name,
66                                      const JsTsLanguageParameters &lang) {
67   return path + file_name + kGeneratedFileNamePostfix + lang.file_extension;
68 }
69
70 namespace jsts {
71 // Iterate through all definitions we haven't generate code for (enums, structs,
72 // and tables) and output them to a single file.
73 class JsTsGenerator : public BaseGenerator {
74  public:
75   typedef std::unordered_set<std::string> imported_fileset;
76   typedef std::unordered_multimap<std::string, ReexportDescription>
77       reexport_map;
78
79   JsTsGenerator(const Parser &parser, const std::string &path,
80                 const std::string &file_name)
81       : BaseGenerator(parser, path, file_name, "", "."),
82         lang_(GetJsLangParams(parser_.opts.lang)) {}
83   // Iterate through all definitions we haven't generate code for (enums,
84   // structs, and tables) and output them to a single file.
85   bool generate() {
86     imported_fileset imported_files;
87     reexport_map reexports;
88
89     std::string enum_code, struct_code, import_code, exports_code, code;
90     generateEnums(&enum_code, &exports_code, reexports);
91     generateStructs(&struct_code, &exports_code, imported_files);
92     generateImportDependencies(&import_code, imported_files);
93     generateReexports(&import_code, reexports, imported_files);
94
95     code = code + "// " + FlatBuffersGeneratedWarning() + "\n\n";
96
97     // Generate code for all the namespace declarations.
98     GenNamespaces(&code, &exports_code);
99
100     // Output the main declaration code from above.
101     code += import_code;
102
103     code += enum_code;
104     code += struct_code;
105
106     if (lang_.language == IDLOptions::kJs && !exports_code.empty() &&
107         !parser_.opts.skip_js_exports) {
108       if (parser_.opts.use_ES6_js_export_format)
109         code += "// Exports for ECMAScript6 Modules\n";
110       else
111         code += "// Exports for Node.js and RequireJS\n";
112       code += exports_code;
113     }
114
115     return SaveFile(GeneratedFileName(path_, file_name_, lang_).c_str(), code,
116                     false);
117   }
118
119  private:
120   JsTsLanguageParameters lang_;
121
122   // Generate code for imports
123   void generateImportDependencies(std::string *code_ptr,
124                                   const imported_fileset &imported_files) {
125     std::string &code = *code_ptr;
126     for (auto it = imported_files.begin(); it != imported_files.end(); ++it) {
127       const auto &file = *it;
128       const auto basename =
129           flatbuffers::StripPath(flatbuffers::StripExtension(file));
130       if (basename != file_name_) {
131         code += GenPrefixedImport(file, basename);
132       }
133     }
134   }
135
136   // Generate reexports, which might not have been explicitly imported using the
137   // "export import" trick
138   void generateReexports(std::string *code_ptr, const reexport_map &reexports,
139                          imported_fileset imported_files) {
140     if (!parser_.opts.reexport_ts_modules ||
141         lang_.language != IDLOptions::kTs) {
142       return;
143     }
144
145     std::string &code = *code_ptr;
146     for (auto it = reexports.begin(); it != reexports.end(); ++it) {
147       const auto &file = *it;
148       const auto basename =
149           flatbuffers::StripPath(flatbuffers::StripExtension(file.first));
150       if (basename != file_name_) {
151         if (imported_files.find(file.first) == imported_files.end()) {
152           code += GenPrefixedImport(file.first, basename);
153           imported_files.emplace(file.first);
154         }
155
156         code += "export namespace " + file.second.target_namespace + " { \n";
157         code += "export import " + file.second.symbol + " = ";
158         code += GenFileNamespacePrefix(file.first) + "." +
159                 file.second.source_namespace + "." + file.second.symbol +
160                 "; }\n";
161       }
162     }
163   }
164
165   // Generate code for all enums.
166   void generateEnums(std::string *enum_code_ptr, std::string *exports_code_ptr,
167                      reexport_map &reexports) {
168     for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
169          ++it) {
170       auto &enum_def = **it;
171       GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, false);
172       GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true);
173     }
174   }
175
176   // Generate code for all structs.
177   void generateStructs(std::string *decl_code_ptr,
178                        std::string *exports_code_ptr,
179                        imported_fileset &imported_files) {
180     for (auto it = parser_.structs_.vec.begin();
181          it != parser_.structs_.vec.end(); ++it) {
182       auto &struct_def = **it;
183       GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr,
184                 imported_files);
185     }
186   }
187   void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
188     if (lang_.language == IDLOptions::kTs &&
189         parser_.opts.skip_flatbuffers_import) {
190       return;
191     }
192
193     std::set<std::string> namespaces;
194
195     for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end();
196          ++it) {
197       std::string namespace_so_far;
198
199       // Gather all parent namespaces for this namespace
200       for (auto component = (*it)->components.begin();
201            component != (*it)->components.end(); ++component) {
202         if (!namespace_so_far.empty()) { namespace_so_far += '.'; }
203         namespace_so_far += *component;
204         namespaces.insert(namespace_so_far);
205       }
206     }
207
208     // Make sure parent namespaces come before child namespaces
209     std::vector<std::string> sorted_namespaces(namespaces.begin(),
210                                                namespaces.end());
211     std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
212
213     // Emit namespaces in a form that Closure Compiler can optimize
214     std::string &code = *code_ptr;
215     std::string &exports = *exports_ptr;
216     for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end();
217          ++it) {
218       if (lang_.language == IDLOptions::kTs) {
219         if (it->find('.') == std::string::npos) {
220           code += "import { flatbuffers } from \"./flatbuffers\"\n";
221           break;
222         }
223       } else {
224         code += "/**\n * @const\n * @namespace\n */\n";
225         if (it->find('.') == std::string::npos) {
226           code += "var ";
227           if (parser_.opts.use_goog_js_export_format) {
228             exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
229           } else if (parser_.opts.use_ES6_js_export_format) {
230             exports += "export {" + *it + "};\n";
231           } else {
232             exports += "this." + *it + " = " + *it + ";\n";
233           }
234         }
235         code += *it + " = " + *it + " || {};\n\n";
236       }
237     }
238   }
239
240   // Generate a documentation comment, if available.
241   static void GenDocComment(const std::vector<std::string> &dc,
242                             std::string *code_ptr,
243                             const std::string &extra_lines,
244                             const char *indent = nullptr) {
245     if (dc.empty() && extra_lines.empty()) {
246       // Don't output empty comment blocks with 0 lines of comment content.
247       return;
248     }
249
250     std::string &code = *code_ptr;
251     if (indent) code += indent;
252     code += "/**\n";
253     for (auto it = dc.begin(); it != dc.end(); ++it) {
254       if (indent) code += indent;
255       code += " *" + *it + "\n";
256     }
257     if (!extra_lines.empty()) {
258       if (!dc.empty()) {
259         if (indent) code += indent;
260         code += " *\n";
261       }
262       if (indent) code += indent;
263       std::string::size_type start = 0;
264       for (;;) {
265         auto end = extra_lines.find('\n', start);
266         if (end != std::string::npos) {
267           code += " * " + extra_lines.substr(start, end - start) + "\n";
268           start = end + 1;
269         } else {
270           code += " * " + extra_lines.substr(start) + "\n";
271           break;
272         }
273       }
274     }
275     if (indent) code += indent;
276     code += " */\n";
277   }
278
279   static void GenDocComment(std::string *code_ptr,
280                             const std::string &extra_lines) {
281     GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
282   }
283
284   std::string GenTypeAnnotation(AnnotationType annotation_type,
285                                 const std::string &type_name,
286                                 const std::string &arg_name,
287                                 bool include_newline = true) {
288     std::string result = "";
289     switch (annotation_type) {
290       case kParam: {
291         result += "@param";
292         break;
293       }
294       case kType: {
295         if (lang_.language != IDLOptions::kTs) {
296           result += "@type";
297         } else {
298           return "";
299         }
300         break;
301       }
302       case kReturns: {
303         result += "@returns";
304         break;
305       }
306     }
307     switch (lang_.language) {
308       case IDLOptions::kTs: {
309         result += " " + type_name;
310         break;
311       }
312       default: { result += " {" + type_name + "}"; }
313     }
314     if (!arg_name.empty()) {
315       result += " " + arg_name;
316     }
317     if (include_newline) {
318       result += "\n";
319     }
320
321     return result;
322   }
323
324   // Generate an enum declaration and an enum string lookup table.
325   void GenEnum(EnumDef &enum_def, std::string *code_ptr,
326                std::string *exports_ptr, reexport_map &reexports,
327                bool reverse) {
328     if (enum_def.generated) return;
329     if (reverse && lang_.language == IDLOptions::kTs) return;  // FIXME.
330     std::string &code = *code_ptr;
331     std::string &exports = *exports_ptr;
332     GenDocComment(enum_def.doc_comment, code_ptr,
333                   reverse ? "@enum {string}" : "@enum {number}");
334     std::string ns = GetNameSpace(enum_def);
335     std::string enum_def_name = enum_def.name + (reverse ? "Name" : "");
336     if (lang_.language == IDLOptions::kTs) {
337       if (!ns.empty()) { code += "export namespace " + ns + "{\n"; }
338       code += "export enum " + enum_def.name + "{\n";
339     } else {
340       if (enum_def.defined_namespace->components.empty()) {
341         code += "var ";
342         if (parser_.opts.use_goog_js_export_format) {
343           exports += "goog.exportSymbol('" + enum_def_name + "', " +
344                      enum_def.name + ");\n";
345         } else if (parser_.opts.use_ES6_js_export_format) {
346           exports += "export {" + enum_def_name + "};\n";
347         } else {
348           exports += "this." + enum_def_name + " = " + enum_def_name + ";\n";
349         }
350       }
351       code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "") + " = {\n";
352     }
353     for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) {
354       auto &ev = **it;
355       if (!ev.doc_comment.empty()) {
356         if (it != enum_def.Vals().begin()) { code += '\n'; }
357         GenDocComment(ev.doc_comment, code_ptr, "", "  ");
358       }
359
360       // Generate mapping between EnumName: EnumValue(int)
361       if (reverse) {
362         code += "  '" + enum_def.ToString(ev) + "'";
363         code += lang_.language == IDLOptions::kTs ? "= " : ": ";
364         code += "'" + ev.name + "'";
365       } else {
366         code += "  " + ev.name;
367         code += lang_.language == IDLOptions::kTs ? "= " : ": ";
368         code += enum_def.ToString(ev);
369       }
370
371       code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n";
372
373       if (ev.union_type.struct_def) {
374         ReexportDescription desc = { ev.name,
375                                      GetNameSpace(*ev.union_type.struct_def),
376                                      GetNameSpace(enum_def) };
377         reexports.insert(
378             std::make_pair(ev.union_type.struct_def->file, std::move(desc)));
379       }
380     }
381
382     if (lang_.language == IDLOptions::kTs && !ns.empty()) { code += "}"; }
383     code += "};\n\n";
384   }
385
386   static std::string GenType(const Type &type) {
387     switch (type.base_type) {
388       case BASE_TYPE_BOOL:
389       case BASE_TYPE_CHAR: return "Int8";
390       case BASE_TYPE_UTYPE:
391       case BASE_TYPE_UCHAR: return "Uint8";
392       case BASE_TYPE_SHORT: return "Int16";
393       case BASE_TYPE_USHORT: return "Uint16";
394       case BASE_TYPE_INT: return "Int32";
395       case BASE_TYPE_UINT: return "Uint32";
396       case BASE_TYPE_LONG: return "Int64";
397       case BASE_TYPE_ULONG: return "Uint64";
398       case BASE_TYPE_FLOAT: return "Float32";
399       case BASE_TYPE_DOUBLE: return "Float64";
400       case BASE_TYPE_STRING: return "String";
401       case BASE_TYPE_VECTOR: return GenType(type.VectorType());
402       case BASE_TYPE_STRUCT: return type.struct_def->name;
403       default: return "Table";
404     }
405   }
406
407   std::string GenGetter(const Type &type, const std::string &arguments) {
408     switch (type.base_type) {
409       case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments;
410       case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments;
411       case BASE_TYPE_UNION: return GenBBAccess() + ".__union" + arguments;
412       case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
413       default: {
414         auto getter =
415             GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments;
416         if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; }
417         if (type.enum_def) {
418           getter = "/** " +
419                    GenTypeAnnotation(kType, WrapInNameSpace(*type.enum_def), "",
420                                      false) +
421                    " */ (" + getter + ")";
422         }
423         return getter;
424       }
425     }
426   }
427
428   std::string GenBBAccess() {
429     return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb";
430   }
431
432   std::string GenDefaultValue(const Value &value, const std::string &context) {
433     if (value.type.enum_def) {
434       if (auto val = value.type.enum_def->FindByValue(value.constant)) {
435         if (lang_.language == IDLOptions::kTs) {
436           return GenPrefixedTypeName(WrapInNameSpace(*value.type.enum_def),
437                                      value.type.enum_def->file) +
438                  "." + val->name;
439         } else {
440           return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
441         }
442       } else {
443         return "/** " +
444                GenTypeAnnotation(kType, WrapInNameSpace(*value.type.enum_def),
445                                  "", false) +
446                "} */ (" + value.constant + ")";
447       }
448     }
449
450     switch (value.type.base_type) {
451       case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true";
452
453       case BASE_TYPE_STRING: return "null";
454
455       case BASE_TYPE_LONG:
456       case BASE_TYPE_ULONG: {
457         int64_t constant = StringToInt(value.constant.c_str());
458         return context + ".createLong(" +
459                NumToString(static_cast<int32_t>(constant)) + ", " +
460                NumToString(static_cast<int32_t>(constant >> 32)) + ")";
461       }
462
463       default: return value.constant;
464     }
465   }
466
467   std::string GenTypeName(const Type &type, bool input,
468                           bool allowNull = false) {
469     if (!input) {
470       if (type.base_type == BASE_TYPE_STRING ||
471           type.base_type == BASE_TYPE_STRUCT) {
472         std::string name;
473         if (type.base_type == BASE_TYPE_STRING) {
474           name = "string|Uint8Array";
475         } else {
476           name = WrapInNameSpace(*type.struct_def);
477         }
478         return (allowNull) ? (name + "|null") : (name);
479       }
480     }
481
482     switch (type.base_type) {
483       case BASE_TYPE_BOOL: return "boolean";
484       case BASE_TYPE_LONG:
485       case BASE_TYPE_ULONG: return "flatbuffers.Long";
486       default:
487         if (IsScalar(type.base_type)) {
488           if (type.enum_def) { return WrapInNameSpace(*type.enum_def); }
489           return "number";
490         }
491         return "flatbuffers.Offset";
492     }
493   }
494
495   // Returns the method name for use with add/put calls.
496   static std::string GenWriteMethod(const Type &type) {
497     // Forward to signed versions since unsigned versions don't exist
498     switch (type.base_type) {
499       case BASE_TYPE_UTYPE:
500       case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
501       case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
502       case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
503       case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
504       default: break;
505     }
506
507     return IsScalar(type.base_type) ? MakeCamel(GenType(type))
508                                     : (IsStruct(type) ? "Struct" : "Offset");
509   }
510
511   template<typename T> static std::string MaybeAdd(T value) {
512     return value != 0 ? " + " + NumToString(value) : "";
513   }
514
515   template<typename T> static std::string MaybeScale(T value) {
516     return value != 1 ? " * " + NumToString(value) : "";
517   }
518
519   static std::string GenFileNamespacePrefix(const std::string &file) {
520     return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str()));
521   }
522
523   std::string GenPrefixedImport(const std::string &full_file_name,
524                                 const std::string &base_name) {
525     // Either keep the include path as it was
526     // or use only the base_name + kGeneratedFileNamePostfix
527     std::string path;
528     if (parser_.opts.keep_include_path) {
529       auto it = parser_.included_files_.find(full_file_name);
530       FLATBUFFERS_ASSERT(it != parser_.included_files_.end());
531       path =
532           flatbuffers::StripExtension(it->second) + kGeneratedFileNamePostfix;
533     } else {
534       path = base_name + kGeneratedFileNamePostfix;
535     }
536
537     // Add the include prefix and make the path always relative
538     path = flatbuffers::ConCatPathFileName(parser_.opts.include_prefix, path);
539     path = std::string(".") + kPathSeparator + path;
540
541     return "import * as " + GenFileNamespacePrefix(full_file_name) +
542            " from \"" + path + "\";\n";
543   }
544
545   // Adds a source-dependent prefix, for of import * statements.
546   std::string GenPrefixedTypeName(const std::string &typeName,
547                                   const std::string &file) {
548     const auto basename =
549         flatbuffers::StripPath(flatbuffers::StripExtension(file));
550     if (basename == file_name_ || parser_.opts.generate_all) {
551       return typeName;
552     }
553     return GenFileNamespacePrefix(file) + "." + typeName;
554   }
555
556   void GenStructArgs(const StructDef &struct_def, std::string *annotations,
557                      std::string *arguments, const std::string &nameprefix) {
558     for (auto it = struct_def.fields.vec.begin();
559          it != struct_def.fields.vec.end(); ++it) {
560       auto &field = **it;
561       if (IsStruct(field.value.type)) {
562         // Generate arguments for a struct inside a struct. To ensure names
563         // don't clash, and to make it obvious these arguments are constructing
564         // a nested struct, prefix the name with the field name.
565         GenStructArgs(*field.value.type.struct_def, annotations, arguments,
566                       nameprefix + field.name + "_");
567       } else {
568         *annotations +=
569             GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
570                               nameprefix + field.name);
571         if (lang_.language == IDLOptions::kTs) {
572           *arguments += ", " + nameprefix + field.name + ": " +
573                         GenTypeName(field.value.type, true);
574         } else {
575           *arguments += ", " + nameprefix + field.name;
576         }
577       }
578     }
579   }
580
581   static void GenStructBody(const StructDef &struct_def, std::string *body,
582                             const std::string &nameprefix) {
583     *body += "  builder.prep(";
584     *body += NumToString(struct_def.minalign) + ", ";
585     *body += NumToString(struct_def.bytesize) + ");\n";
586
587     for (auto it = struct_def.fields.vec.rbegin();
588          it != struct_def.fields.vec.rend(); ++it) {
589       auto &field = **it;
590       if (field.padding) {
591         *body += "  builder.pad(" + NumToString(field.padding) + ");\n";
592       }
593       if (IsStruct(field.value.type)) {
594         // Generate arguments for a struct inside a struct. To ensure names
595         // don't clash, and to make it obvious these arguments are constructing
596         // a nested struct, prefix the name with the field name.
597         GenStructBody(*field.value.type.struct_def, body,
598                       nameprefix + field.name + "_");
599       } else {
600         *body += "  builder.write" + GenWriteMethod(field.value.type) + "(";
601         if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
602         *body += nameprefix + field.name + ");\n";
603       }
604     }
605   }
606
607   void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr,
608                  std::string &code, std::string &object_name, bool size_prefixed) {
609     if (!struct_def.fixed) {
610       GenDocComment(code_ptr,
611                     GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
612                         GenTypeAnnotation(kParam, object_name + "=", "obj") +
613                         GenTypeAnnotation(kReturns, object_name, "", false));
614       std::string sizePrefixed("SizePrefixed");
615       if (lang_.language == IDLOptions::kTs) {
616         code += "static get" + (size_prefixed ? sizePrefixed : "") + "Root" + Verbose(struct_def, "As");
617         code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
618                 "):" + object_name + " {\n";
619       } else {
620         code += object_name + ".get" + (size_prefixed ? sizePrefixed : "") + "Root" + Verbose(struct_def, "As");
621         code += " = function(bb, obj) {\n";
622       }
623       code += "  return (obj || new " + object_name;
624       code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
625       code += "};\n\n";
626     }
627   }
628
629   void GenerateFinisher(StructDef &struct_def, std::string *code_ptr,
630                  std::string &code, std::string &object_name, bool size_prefixed) {
631     if (parser_.root_struct_def_ == &struct_def) {
632       std::string sizePrefixed("SizePrefixed");
633       GenDocComment(
634           code_ptr,
635           GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
636               GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset",
637                                 false));
638
639       if (lang_.language == IDLOptions::kTs) {
640         code += "static finish" + (size_prefixed ? sizePrefixed : "") + Verbose(struct_def) + "Buffer";
641         code +=
642             "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
643       } else {
644         code += object_name + ".finish" + (size_prefixed ? sizePrefixed : "") + Verbose(struct_def) + "Buffer";
645         code += " = function(builder, offset) {\n";
646       }
647
648       code += "  builder.finish(offset";
649       if (!parser_.file_identifier_.empty()) {
650         code += ", '" + parser_.file_identifier_ + "'";
651       }
652       if (size_prefixed) {
653         if (parser_.file_identifier_.empty()) {
654           code += ", undefined";
655         }
656         code += ", true";
657       }
658       code += ");\n";
659       code += "};\n\n";
660     }
661   }
662
663   // Generate an accessor struct with constructor for a flatbuffers struct.
664   void GenStruct(const Parser &parser, StructDef &struct_def,
665                  std::string *code_ptr, std::string *exports_ptr,
666                  imported_fileset &imported_files) {
667     if (struct_def.generated) return;
668     std::string &code = *code_ptr;
669     std::string &exports = *exports_ptr;
670
671     std::string object_name;
672     std::string object_namespace = GetNameSpace(struct_def);
673
674     // Emit constructor
675     if (lang_.language == IDLOptions::kTs) {
676       object_name = struct_def.name;
677       GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
678       if (!object_namespace.empty()) {
679         code += "export namespace " + object_namespace + "{\n";
680       }
681       code += "export class " + struct_def.name;
682       code += " {\n";
683       if (lang_.language != IDLOptions::kTs) {
684         code += "  /**\n";
685         code += "   * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
686         code += "   */\n";
687       }
688       code += "  bb: flatbuffers.ByteBuffer|null = null;\n";
689       code += "\n";
690       if (lang_.language != IDLOptions::kTs) {
691         code += "  /**\n";
692         code += "   * " + GenTypeAnnotation(kType, "number", "");
693         code += "   */\n";
694       }
695       code += "  bb_pos:number = 0;\n";
696     } else {
697       bool isStatement = struct_def.defined_namespace->components.empty();
698       object_name = WrapInNameSpace(struct_def);
699       GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
700       if (isStatement) {
701         if (parser_.opts.use_goog_js_export_format) {
702           exports += "goog.exportSymbol('" + struct_def.name + "', " +
703                      struct_def.name + ");\n";
704         } else if (parser_.opts.use_ES6_js_export_format) {
705           exports += "export {" + struct_def.name + "};\n";
706         } else {
707           exports +=
708               "this." + struct_def.name + " = " + struct_def.name + ";\n";
709         }
710         code += "function " + object_name;
711       } else {
712         code += object_name + " = function";
713       }
714       code += "() {\n";
715       code += "  /**\n";
716       code += "   * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
717       code += "   */\n";
718       code += "  this.bb = null;\n";
719       code += "\n";
720       code += "  /**\n";
721       code += "   * " + GenTypeAnnotation(kType, "number", "");
722       code += "   */\n";
723       code += "  this.bb_pos = 0;\n";
724       code += isStatement ? "}\n\n" : "};\n\n";
725     }
726
727     // Generate the __init method that sets the field in a pre-existing
728     // accessor object. This is to allow object reuse.
729     code += "/**\n";
730     code += " * " + GenTypeAnnotation(kParam, "number", "i");
731     code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
732     code += " * " + GenTypeAnnotation(kReturns, object_name, "");
733     code += " */\n";
734
735     if (lang_.language == IDLOptions::kTs) {
736       code +=
737           "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
738     } else {
739       code += object_name + ".prototype.__init = function(i, bb) {\n";
740     }
741
742     code += "  this.bb_pos = i;\n";
743     code += "  this.bb = bb;\n";
744     code += "  return this;\n";
745     code += "};\n\n";
746
747     // Generate special accessors for the table that when used as the root of a
748     // FlatBuffer
749     GenerateRootAccessor(struct_def, code_ptr, code, object_name, false);
750     GenerateRootAccessor(struct_def, code_ptr, code, object_name, true);
751
752     // Generate the identifier check method
753     if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def &&
754         !parser_.file_identifier_.empty()) {
755       GenDocComment(
756           code_ptr,
757           GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
758               GenTypeAnnotation(kReturns, "boolean", "", false));
759       if (lang_.language == IDLOptions::kTs) {
760         code +=
761             "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
762             "{\n";
763       } else {
764         code += object_name + ".bufferHasIdentifier = function(bb) {\n";
765       }
766
767       code += "  return bb.__has_identifier('" + parser_.file_identifier_;
768       code += "');\n};\n\n";
769     }
770
771     // Emit field accessors
772     for (auto it = struct_def.fields.vec.begin();
773          it != struct_def.fields.vec.end(); ++it) {
774       auto &field = **it;
775       if (field.deprecated) continue;
776       auto offset_prefix =
777           "  var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
778           NumToString(field.value.offset) + ");\n  return offset ? ";
779
780       // Emit a scalar field
781       if (IsScalar(field.value.type.base_type) ||
782           field.value.type.base_type == BASE_TYPE_STRING) {
783         GenDocComment(
784             field.doc_comment, code_ptr,
785             std::string(field.value.type.base_type == BASE_TYPE_STRING
786                             ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
787                                                 "optionalEncoding")
788                             : "") +
789                 GenTypeAnnotation(kReturns,
790                                   GenTypeName(field.value.type, false, true),
791                                   "", false));
792         if (lang_.language == IDLOptions::kTs) {
793           std::string prefix = MakeCamel(field.name, false) + "(";
794           if (field.value.type.base_type == BASE_TYPE_STRING) {
795             code += prefix + "):string|null\n";
796             code += prefix + "optionalEncoding:flatbuffers.Encoding" +
797                     "):" + GenTypeName(field.value.type, false, true) + "\n";
798             code += prefix + "optionalEncoding?:any";
799           } else {
800             code += prefix;
801           }
802           if (field.value.type.enum_def) {
803             code +=
804                 "):" +
805                 GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
806                                     field.value.type.enum_def->file) +
807                 " {\n";
808
809             if (!parser_.opts.generate_all) {
810               imported_files.insert(field.value.type.enum_def->file);
811             }
812           } else {
813             code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
814           }
815         } else {
816           code += object_name + ".prototype." + MakeCamel(field.name, false);
817           code += " = function(";
818           if (field.value.type.base_type == BASE_TYPE_STRING) {
819             code += "optionalEncoding";
820           }
821           code += ") {\n";
822         }
823
824         if (struct_def.fixed) {
825           code +=
826               "  return " +
827               GenGetter(field.value.type,
828                         "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
829               ";\n";
830         } else {
831           std::string index = "this.bb_pos + offset";
832           if (field.value.type.base_type == BASE_TYPE_STRING) {
833             index += ", optionalEncoding";
834           }
835           code += offset_prefix +
836                   GenGetter(field.value.type, "(" + index + ")") + " : " +
837                   GenDefaultValue(field.value, GenBBAccess());
838           code += ";\n";
839         }
840       }
841
842       // Emit an object field
843       else {
844         switch (field.value.type.base_type) {
845           case BASE_TYPE_STRUCT: {
846             auto type = WrapInNameSpace(*field.value.type.struct_def);
847             GenDocComment(
848                 field.doc_comment, code_ptr,
849                 GenTypeAnnotation(kParam, type + "=", "obj") +
850                     GenTypeAnnotation(kReturns, type + "|null", "", false));
851             if (lang_.language == IDLOptions::kTs) {
852               type =
853                   GenPrefixedTypeName(type, field.value.type.struct_def->file);
854               code += MakeCamel(field.name, false);
855               code += "(obj?:" + type + "):" + type + "|null {\n";
856             } else {
857               code +=
858                   object_name + ".prototype." + MakeCamel(field.name, false);
859               code += " = function(obj) {\n";
860             }
861
862             if (struct_def.fixed) {
863               code += "  return (obj || new " + type;
864               code += ").__init(this.bb_pos";
865               code +=
866                   MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
867             } else {
868               code += offset_prefix + "(obj || new " + type + ").__init(";
869               code += field.value.type.struct_def->fixed
870                           ? "this.bb_pos + offset"
871                           : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
872               code += ", " + GenBBAccess() + ") : null;\n";
873             }
874
875             if (lang_.language == IDLOptions::kTs && !parser_.opts.generate_all) {
876               imported_files.insert(field.value.type.struct_def->file);
877             }
878
879             break;
880           }
881
882           case BASE_TYPE_VECTOR: {
883             auto vectortype = field.value.type.VectorType();
884             auto vectortypename = GenTypeName(vectortype, false);
885             auto inline_size = InlineSize(vectortype);
886             auto index = GenBBAccess() +
887                          ".__vector(this.bb_pos + offset) + index" +
888                          MaybeScale(inline_size);
889             std::string args = GenTypeAnnotation(kParam, "number", "index");
890             std::string ret_type;
891             bool is_union = false;
892             switch (vectortype.base_type) {
893               case BASE_TYPE_STRUCT:
894                 args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
895                 ret_type = vectortypename;
896                 break;
897               case BASE_TYPE_STRING:
898                 args += GenTypeAnnotation(
899                     kParam, "flatbuffers.Encoding=", "optionalEncoding");
900                 ret_type = vectortypename;
901                 break;
902               case BASE_TYPE_UNION:
903                 args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
904                 ret_type = "?flatbuffers.Table";
905                 is_union = true;
906                 break;
907               default: ret_type = vectortypename;
908             }
909             GenDocComment(
910                 field.doc_comment, code_ptr,
911                 args + GenTypeAnnotation(kReturns, ret_type, "", false));
912             if (lang_.language == IDLOptions::kTs) {
913               std::string prefix = MakeCamel(field.name, false);
914               if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
915               prefix += "(index: number";
916               if (is_union) {
917                 vectortypename = "T";
918                 code += prefix + ", obj:T";
919               } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
920                 vectortypename = GenPrefixedTypeName(
921                     vectortypename, vectortype.struct_def->file);
922                 code += prefix + ", obj?:" + vectortypename;
923
924                 if (!parser_.opts.generate_all) {
925                   imported_files.insert(vectortype.struct_def->file);
926                 }
927               } else if (vectortype.base_type == BASE_TYPE_STRING) {
928                 code += prefix + "):string\n";
929                 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
930                         "):" + vectortypename + "\n";
931                 code += prefix + ",optionalEncoding?:any";
932               } else {
933                 code += prefix;
934               }
935               code += "):" + vectortypename + "|null {\n";
936             } else {
937               code +=
938                   object_name + ".prototype." + MakeCamel(field.name, false);
939               code += " = function(index";
940               if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
941                 code += ", obj";
942               } else if (vectortype.base_type == BASE_TYPE_STRING) {
943                 code += ", optionalEncoding";
944               }
945               code += ") {\n";
946             }
947
948             if (vectortype.base_type == BASE_TYPE_STRUCT) {
949               code += offset_prefix + "(obj || new " + vectortypename;
950               code += ").__init(";
951               code += vectortype.struct_def->fixed
952                           ? index
953                           : GenBBAccess() + ".__indirect(" + index + ")";
954               code += ", " + GenBBAccess() + ")";
955             } else {
956               if (is_union) {
957                 index = "obj, " + index;
958               } else if (vectortype.base_type == BASE_TYPE_STRING) {
959                 index += ", optionalEncoding";
960               }
961               code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
962             }
963             code += " : ";
964             if (field.value.type.element == BASE_TYPE_BOOL) {
965               code += "false";
966             } else if (field.value.type.element == BASE_TYPE_LONG ||
967                        field.value.type.element == BASE_TYPE_ULONG) {
968               code += GenBBAccess() + ".createLong(0, 0)";
969             } else if (IsScalar(field.value.type.element)) {
970               if (field.value.type.enum_def) {
971                 code += "/** " +
972                         GenTypeAnnotation(
973                             kType, WrapInNameSpace(*field.value.type.enum_def),
974                             "", false) +
975                         " */ (" + field.value.constant + ")";
976               } else {
977                 code += "0";
978               }
979             } else {
980               code += "null";
981             }
982             code += ";\n";
983             break;
984           }
985
986           case BASE_TYPE_UNION:
987             GenDocComment(
988                 field.doc_comment, code_ptr,
989                 GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
990                     GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
991                                       false));
992             if (lang_.language == IDLOptions::kTs) {
993               code += MakeCamel(field.name, false);
994               code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
995             } else {
996               code +=
997                   object_name + ".prototype." + MakeCamel(field.name, false);
998               code += " = function(obj) {\n";
999             }
1000
1001             code += offset_prefix +
1002                     GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
1003                     " : null;\n";
1004             break;
1005
1006           default: FLATBUFFERS_ASSERT(0);
1007         }
1008       }
1009       code += "};\n\n";
1010
1011       if (parser_.opts.use_goog_js_export_format) {
1012         exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1013                    MakeCamel(field.name, false) + "', " + object_name +
1014                    ".prototype." + MakeCamel(field.name, false) + ");\n";
1015       }
1016
1017       // Adds the mutable scalar value to the output
1018       if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer && !IsUnion(field.value.type)) {
1019         std::string annotations = GenTypeAnnotation(
1020             kParam, GenTypeName(field.value.type, true), "value");
1021         GenDocComment(
1022             code_ptr,
1023             annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
1024
1025         if (lang_.language == IDLOptions::kTs) {
1026           std::string type;
1027           if (field.value.type.enum_def) {
1028             type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
1029                                        field.value.type.enum_def->file);
1030           } else {
1031             type = GenTypeName(field.value.type, true);
1032           }
1033
1034           code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
1035         } else {
1036           code += object_name + ".prototype.mutate_" + field.name +
1037                   " = function(value) {\n";
1038         }
1039
1040         code += "  var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
1041                 NumToString(field.value.offset) + ");\n\n";
1042         code += "  if (offset === 0) {\n";
1043         code += "    return false;\n";
1044         code += "  }\n\n";
1045
1046         // special case for bools, which are treated as uint8
1047         code += "  " + GenBBAccess() + ".write" +
1048                 MakeCamel(GenType(field.value.type)) +
1049                 "(this.bb_pos + offset, ";
1050         if (field.value.type.base_type == BASE_TYPE_BOOL &&
1051             lang_.language == IDLOptions::kTs) {
1052           code += "+";
1053         }
1054
1055         code += "value);\n";
1056         code += "  return true;\n";
1057         code += "};\n\n";
1058
1059         if (parser_.opts.use_goog_js_export_format) {
1060           exports += "goog.exportProperty(" + object_name +
1061                      ".prototype, 'mutate_" + field.name + "', " + object_name +
1062                      ".prototype.mutate_" + field.name + ");\n";
1063         }
1064       }
1065
1066       // Emit vector helpers
1067       if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1068         // Emit a length helper
1069         GenDocComment(code_ptr,
1070                       GenTypeAnnotation(kReturns, "number", "", false));
1071         if (lang_.language == IDLOptions::kTs) {
1072           code += MakeCamel(field.name, false);
1073           code += "Length():number {\n" + offset_prefix;
1074         } else {
1075           code += object_name + ".prototype." + MakeCamel(field.name, false);
1076           code += "Length = function() {\n" + offset_prefix;
1077         }
1078
1079         code +=
1080             GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
1081
1082         if (parser_.opts.use_goog_js_export_format) {
1083           exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1084                      MakeCamel(field.name, false) + "Length', " + object_name +
1085                      ".prototype." + MakeCamel(field.name, false) +
1086                      "Length);\n";
1087         }
1088
1089         // For scalar types, emit a typed array helper
1090         auto vectorType = field.value.type.VectorType();
1091         if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1092           GenDocComment(code_ptr, GenTypeAnnotation(
1093                                       kReturns, GenType(vectorType) + "Array",
1094                                       "", false));
1095
1096           if (lang_.language == IDLOptions::kTs) {
1097             code += MakeCamel(field.name, false);
1098             code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1099                     offset_prefix;
1100           } else {
1101             code += object_name + ".prototype." + MakeCamel(field.name, false);
1102             code += "Array = function() {\n" + offset_prefix;
1103           }
1104
1105           code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1106                   ".bytes().buffer, " + GenBBAccess() +
1107                   ".bytes().byteOffset + " + GenBBAccess() +
1108                   ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1109                   ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
1110
1111           if (parser_.opts.use_goog_js_export_format) {
1112             exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1113                        MakeCamel(field.name, false) + "Array', " + object_name +
1114                        ".prototype." + MakeCamel(field.name, false) +
1115                        "Array);\n";
1116           }
1117         }
1118       }
1119     }
1120
1121     // Emit a factory constructor
1122     if (struct_def.fixed) {
1123       std::string annotations =
1124           GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1125       std::string arguments;
1126       GenStructArgs(struct_def, &annotations, &arguments, "");
1127       GenDocComment(code_ptr, annotations + GenTypeAnnotation(
1128                                                 kReturns, "flatbuffers.Offset",
1129                                                 "", false));
1130
1131       if (lang_.language == IDLOptions::kTs) {
1132         code += "static create" + Verbose(struct_def) +
1133                 "(builder:flatbuffers.Builder";
1134         code += arguments + "):flatbuffers.Offset {\n";
1135       } else {
1136         code += object_name + ".create" + Verbose(struct_def);
1137         code += " = function(builder";
1138         code += arguments + ") {\n";
1139       }
1140
1141       GenStructBody(struct_def, &code, "");
1142       code += "  return builder.offset();\n};\n\n";
1143     } else {
1144       // Generate a method to start building a new object
1145       GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
1146                                                 "builder", false));
1147
1148       if (lang_.language == IDLOptions::kTs) {
1149         code += "static start" + Verbose(struct_def) +
1150                 "(builder:flatbuffers.Builder) {\n";
1151       } else {
1152         code += object_name + ".start" + Verbose(struct_def);
1153         code += " = function(builder) {\n";
1154       }
1155
1156       code += "  builder.startObject(" +
1157               NumToString(struct_def.fields.vec.size()) + ");\n";
1158       code += "};\n\n";
1159
1160       // Generate a set of static methods that allow table construction
1161       for (auto it = struct_def.fields.vec.begin();
1162            it != struct_def.fields.vec.end(); ++it) {
1163         auto &field = **it;
1164         if (field.deprecated) continue;
1165         const auto argname = GetArgName(field);
1166
1167         // Generate the field insertion method
1168         GenDocComment(
1169             code_ptr,
1170             GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1171                 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
1172                                   argname, false));
1173
1174         if (lang_.language == IDLOptions::kTs) {
1175           code += "static add" + MakeCamel(field.name);
1176           code += "(builder:flatbuffers.Builder, " + argname + ":" +
1177                   GetArgType(field) + ") {\n";
1178         } else {
1179           code += object_name + ".add" + MakeCamel(field.name);
1180           code += " = function(builder, " + argname + ") {\n";
1181         }
1182
1183         code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
1184         code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1185         if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1186         code += argname + ", ";
1187         if (!IsScalar(field.value.type.base_type)) {
1188           code += "0";
1189         } else {
1190           if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1191           code += GenDefaultValue(field.value, "builder");
1192         }
1193         code += ");\n};\n\n";
1194
1195         if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1196           auto vector_type = field.value.type.VectorType();
1197           auto alignment = InlineAlignment(vector_type);
1198           auto elem_size = InlineSize(vector_type);
1199
1200           // Generate a method to create a vector from a JavaScript array
1201           if (!IsStruct(vector_type)) {
1202             GenDocComment(
1203                 code_ptr,
1204                 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1205                     GenTypeAnnotation(
1206                         kParam,
1207                         "Array.<" + GenTypeName(vector_type, true) + ">",
1208                         "data") +
1209                     GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
1210                                       false));
1211
1212             if (lang_.language == IDLOptions::kTs) {
1213               code += "static create" + MakeCamel(field.name);
1214               std::string type = GenTypeName(vector_type, true) + "[]";
1215               if (type == "number[]") { type += " | Uint8Array"; }
1216               code += "Vector(builder:flatbuffers.Builder, data:" + type +
1217                       "):flatbuffers.Offset {\n";
1218             } else {
1219               code += object_name + ".create" + MakeCamel(field.name);
1220               code += "Vector = function(builder, data) {\n";
1221             }
1222
1223             code += "  builder.startVector(" + NumToString(elem_size);
1224             code += ", data.length, " + NumToString(alignment) + ");\n";
1225             code += "  for (var i = data.length - 1; i >= 0; i--) {\n";
1226             code += "    builder.add" + GenWriteMethod(vector_type) + "(";
1227             if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1228             code += "data[i]);\n";
1229             code += "  }\n";
1230             code += "  return builder.endVector();\n";
1231             code += "};\n\n";
1232           }
1233
1234           // Generate a method to start a vector, data to be added manually
1235           // after
1236           GenDocComment(
1237               code_ptr,
1238               GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1239                   GenTypeAnnotation(kParam, "number", "numElems", false));
1240
1241           if (lang_.language == IDLOptions::kTs) {
1242             code += "static start" + MakeCamel(field.name);
1243             code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1244           } else {
1245             code += object_name + ".start" + MakeCamel(field.name);
1246             code += "Vector = function(builder, numElems) {\n";
1247           }
1248
1249           code += "  builder.startVector(" + NumToString(elem_size);
1250           code += ", numElems, " + NumToString(alignment) + ");\n";
1251           code += "};\n\n";
1252         }
1253       }
1254
1255       // Generate a method to stop building a new object
1256       GenDocComment(
1257           code_ptr,
1258           GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1259               GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
1260
1261       if (lang_.language == IDLOptions::kTs) {
1262         code += "static end" + Verbose(struct_def);
1263         code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1264       } else {
1265         code += object_name + ".end" + Verbose(struct_def);
1266         code += " = function(builder) {\n";
1267       }
1268
1269       code += "  var offset = builder.endObject();\n";
1270       for (auto it = struct_def.fields.vec.begin();
1271            it != struct_def.fields.vec.end(); ++it) {
1272         auto &field = **it;
1273         if (!field.deprecated && field.required) {
1274           code += "  builder.requiredField(offset, ";
1275           code += NumToString(field.value.offset);
1276           code += "); // " + field.name + "\n";
1277         }
1278       }
1279       code += "  return offset;\n";
1280       code += "};\n\n";
1281
1282       // Generate the methods to complete buffer construction
1283       GenerateFinisher(struct_def, code_ptr, code, object_name, false);
1284       GenerateFinisher(struct_def, code_ptr, code, object_name, true);
1285
1286       // Generate a convenient CreateX function
1287       if (lang_.language == IDLOptions::kJs) {
1288         std::string paramDoc =
1289             GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1290         for (auto it = struct_def.fields.vec.begin();
1291              it != struct_def.fields.vec.end(); ++it) {
1292           const auto &field = **it;
1293           if (field.deprecated)
1294             continue;
1295           paramDoc +=
1296               GenTypeAnnotation(kParam, GetArgType(field), GetArgName(field));
1297         }
1298         paramDoc +=
1299             GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false);
1300
1301         GenDocComment(code_ptr, paramDoc);
1302       }
1303
1304       if (lang_.language == IDLOptions::kTs) {
1305         code += "static create" + Verbose(struct_def);
1306         code += "(builder:flatbuffers.Builder";
1307       } else {
1308         code += object_name + ".create" + Verbose(struct_def);
1309         code += " = function(builder";
1310       }
1311       for (auto it = struct_def.fields.vec.begin();
1312            it != struct_def.fields.vec.end(); ++it) {
1313         const auto &field = **it;
1314         if (field.deprecated)
1315           continue;
1316
1317         if (lang_.language == IDLOptions::kTs) {
1318           code += ", " + GetArgName(field) + ":" + GetArgType(field);
1319         } else {
1320           code += ", " + GetArgName(field);
1321         }
1322       }
1323
1324       if (lang_.language == IDLOptions::kTs) {
1325         code += "):flatbuffers.Offset {\n";
1326         code += "  " + struct_def.name + ".start" + Verbose(struct_def) +
1327                 "(builder);\n";
1328       } else {
1329         code += ") {\n";
1330         code += "  " + object_name + ".start" + Verbose(struct_def) +
1331                 "(builder);\n";
1332       }
1333
1334       std::string methodPrefix =
1335           lang_.language == IDLOptions::kTs ? struct_def.name : object_name;
1336       for (auto it = struct_def.fields.vec.begin();
1337            it != struct_def.fields.vec.end(); ++it) {
1338         const auto &field = **it;
1339         if (field.deprecated)
1340           continue;
1341
1342         code += "  " + methodPrefix + ".add" + MakeCamel(field.name) + "(";
1343         code += "builder, " + GetArgName(field) + ");\n";
1344       }
1345
1346       code += "  return " + methodPrefix + ".end" + Verbose(struct_def) +
1347               "(builder);\n";
1348       code += "}\n";
1349       if (lang_.language == IDLOptions::kJs)
1350         code += "\n";
1351     }
1352
1353     if (lang_.language == IDLOptions::kTs) {
1354       if (!object_namespace.empty()) {
1355         code += "}\n";
1356       }
1357       code += "}\n";
1358     }
1359   }
1360
1361   std::string GetArgType(const FieldDef &field) {
1362     if (field.value.type.enum_def)
1363       return GenPrefixedTypeName(GenTypeName(field.value.type, true),
1364                                  field.value.type.enum_def->file);
1365     return GenTypeName(field.value.type, true);
1366   }
1367
1368   static std::string GetArgName(const FieldDef &field) {
1369     auto argname = MakeCamel(field.name, false);
1370     if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
1371
1372     return argname;
1373   }
1374
1375   std::string Verbose(const StructDef &struct_def,
1376                       const char* prefix = "")
1377   {
1378     return parser_.opts.js_ts_short_names ? "" : prefix + struct_def.name;
1379   }
1380 };
1381 }  // namespace jsts
1382
1383 bool GenerateJSTS(const Parser &parser, const std::string &path,
1384                   const std::string &file_name) {
1385   jsts::JsTsGenerator generator(parser, path, file_name);
1386   return generator.generate();
1387 }
1388
1389 std::string JSTSMakeRule(const Parser &parser, const std::string &path,
1390                          const std::string &file_name) {
1391   FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
1392   const auto &lang = GetJsLangParams(parser.opts.lang);
1393
1394   std::string filebase =
1395       flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1396   std::string make_rule = GeneratedFileName(path, filebase, lang) + ": ";
1397
1398   auto included_files = parser.GetIncludedFilesRecursive(file_name);
1399   for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1400     make_rule += " " + *it;
1401   }
1402   return make_rule;
1403 }
1404
1405 }  // namespace flatbuffers