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