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
19#include "flatbuffers/flatbuffers.h"
20#include "flatbuffers/idl.h"
21#include "flatbuffers/util.h"
22#include "flatbuffers/code_generators.h"
23
24namespace flatbuffers {
25
26static std::string GeneratedFileName(const std::string &path,
27                                     const std::string &file_name) {
28  return path + file_name + "_generated.js";
29}
30
31namespace js {
32// Iterate through all definitions we haven't generate code for (enums, structs,
33// and tables) and output them to a single file.
34class JsGenerator : public BaseGenerator {
35 public:
36  JsGenerator(const Parser &parser, const std::string &path,
37              const std::string &file_name)
38      : BaseGenerator(parser, path, file_name, "", "."){};
39  // Iterate through all definitions we haven't generate code for (enums,
40  // structs, and tables) and output them to a single file.
41  bool generate() {
42    if (IsEverythingGenerated()) return true;
43
44    std::string enum_code, struct_code, exports_code, code;
45    generateEnums(&enum_code, &exports_code);
46    generateStructs(&struct_code, &exports_code);
47
48    code = code + "// " + FlatBuffersGeneratedWarning();
49
50    // Generate code for all the namespace declarations.
51    GenNamespaces(&code, &exports_code);
52
53    // Output the main declaration code from above.
54    code += enum_code;
55    code += struct_code;
56
57    if (!exports_code.empty() && !parser_.opts.skip_js_exports) {
58      code += "// Exports for Node.js and RequireJS\n";
59      code += exports_code;
60    }
61
62    return SaveFile(GeneratedFileName(path_, file_name_).c_str(), code, false);
63  }
64
65 private:
66  // Generate code for all enums.
67  void generateEnums(std::string *enum_code_ptr,
68                     std::string *exports_code_ptr) {
69    for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end();
70         ++it) {
71      auto &enum_def = **it;
72      GenEnum(enum_def, enum_code_ptr, exports_code_ptr);
73    }
74  }
75
76  // Generate code for all structs.
77  void generateStructs(std::string *decl_code_ptr,
78                       std::string *exports_code_ptr) {
79    for (auto it = parser_.structs_.vec.begin();
80         it != parser_.structs_.vec.end(); ++it) {
81      auto &struct_def = **it;
82      GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr);
83    }
84  }
85  void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) {
86  std::set<std::string> namespaces;
87
88  for (auto it = parser_.namespaces_.begin();
89       it != parser_.namespaces_.end(); ++it) {
90    std::string namespace_so_far;
91
92    // Gather all parent namespaces for this namespace
93    for (auto component = (*it)->components.begin();
94         component != (*it)->components.end(); ++component) {
95      if (!namespace_so_far.empty()) {
96        namespace_so_far += '.';
97      }
98      namespace_so_far += *component;
99      namespaces.insert(namespace_so_far);
100    }
101  }
102
103  // Make sure parent namespaces come before child namespaces
104  std::vector<std::string> sorted_namespaces(
105    namespaces.begin(), namespaces.end());
106  std::sort(sorted_namespaces.begin(), sorted_namespaces.end());
107
108  // Emit namespaces in a form that Closure Compiler can optimize
109  std::string &code = *code_ptr;
110  std::string &exports = *exports_ptr;
111  for (auto it = sorted_namespaces.begin();
112       it != sorted_namespaces.end(); it++) {
113    code += "/**\n * @const\n * @namespace\n */\n";
114    if (it->find('.') == std::string::npos) {
115      code += "var ";
116      if(parser_.opts.use_goog_js_export_format) {
117        exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n";
118      } else {
119        exports += "this." + *it + " = " + *it + ";\n";
120      }
121    }
122    code += *it + " = " + *it + " || {};\n\n";
123  }
124}
125
126// Generate a documentation comment, if available.
127static void GenDocComment(const std::vector<std::string> &dc,
128                          std::string *code_ptr,
129                          const std::string &extra_lines,
130                          const char *indent = nullptr) {
131  if (dc.empty() && extra_lines.empty()) {
132    // Don't output empty comment blocks with 0 lines of comment content.
133    return;
134  }
135
136  std::string &code = *code_ptr;
137  if (indent) code += indent;
138  code += "/**\n";
139  for (auto it = dc.begin(); it != dc.end(); ++it) {
140    if (indent) code += indent;
141    code += " *" + *it + "\n";
142  }
143  if (!extra_lines.empty()) {
144    if (!dc.empty()) {
145      if (indent) code += indent;
146      code += " *\n";
147    }
148    if (indent) code += indent;
149    std::string::size_type start = 0;
150    for (;;) {
151      auto end = extra_lines.find('\n', start);
152      if (end != std::string::npos) {
153        code += " * " + extra_lines.substr(start, end - start) + "\n";
154        start = end + 1;
155      } else {
156        code += " * " + extra_lines.substr(start) + "\n";
157        break;
158      }
159    }
160  }
161  if (indent) code += indent;
162  code += " */\n";
163}
164
165static void GenDocComment(std::string *code_ptr,
166                          const std::string &extra_lines) {
167  GenDocComment(std::vector<std::string>(), code_ptr, extra_lines);
168}
169
170// Generate an enum declaration and an enum string lookup table.
171void GenEnum(EnumDef &enum_def, std::string *code_ptr,
172                    std::string *exports_ptr) {
173  if (enum_def.generated) return;
174  std::string &code = *code_ptr;
175  std::string &exports = *exports_ptr;
176  GenDocComment(enum_def.doc_comment, code_ptr, "@enum");
177  if (enum_def.defined_namespace->components.empty()) {
178    code += "var ";
179    if(parser_.opts.use_goog_js_export_format) {
180      exports += "goog.exportSymbol('" + enum_def.name + "', " + enum_def.name +
181        ");\n";
182    } else {
183      exports += "this." + enum_def.name + " = " + enum_def.name + ";\n";
184    }
185  }
186  code += WrapInNameSpace(enum_def) + " = {\n";
187  for (auto it = enum_def.vals.vec.begin();
188       it != enum_def.vals.vec.end(); ++it) {
189    auto &ev = **it;
190    if (!ev.doc_comment.empty()) {
191      if (it != enum_def.vals.vec.begin()) {
192        code += '\n';
193      }
194      GenDocComment(ev.doc_comment, code_ptr, "", "  ");
195    }
196    code += "  " + ev.name + ": " + NumToString(ev.value);
197    code += (it + 1) != enum_def.vals.vec.end() ? ",\n" : "\n";
198  }
199  code += "};\n\n";
200}
201
202static std::string GenType(const Type &type) {
203  switch (type.base_type) {
204    case BASE_TYPE_BOOL:
205    case BASE_TYPE_CHAR: return "Int8";
206    case BASE_TYPE_UTYPE:
207    case BASE_TYPE_UCHAR: return "Uint8";
208    case BASE_TYPE_SHORT: return "Int16";
209    case BASE_TYPE_USHORT: return "Uint16";
210    case BASE_TYPE_INT: return "Int32";
211    case BASE_TYPE_UINT: return "Uint32";
212    case BASE_TYPE_LONG: return "Int64";
213    case BASE_TYPE_ULONG: return "Uint64";
214    case BASE_TYPE_FLOAT: return "Float32";
215    case BASE_TYPE_DOUBLE: return "Float64";
216    case BASE_TYPE_STRING: return "String";
217    case BASE_TYPE_VECTOR: return GenType(type.VectorType());
218    case BASE_TYPE_STRUCT: return type.struct_def->name;
219    default: return "Table";
220  }
221}
222
223std::string GenGetter(const Type &type, const std::string &arguments) {
224  switch (type.base_type) {
225    case BASE_TYPE_STRING: return "this.bb.__string" + arguments;
226    case BASE_TYPE_STRUCT: return "this.bb.__struct" + arguments;
227    case BASE_TYPE_UNION:  return "this.bb.__union" + arguments;
228    case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments);
229    default: {
230      auto getter = "this.bb.read" + MakeCamel(GenType(type)) + arguments;
231      if (type.base_type == BASE_TYPE_BOOL) {
232        getter = "!!" + getter;
233      }
234      if (type.enum_def) {
235        getter = "/** @type {" + WrapInNameSpace(*type.enum_def) + "} */ (" +
236          getter + ")";
237      }
238      return getter;
239    }
240  }
241}
242
243std::string GenDefaultValue(const Value &value, const std::string &context) {
244  if (value.type.enum_def) {
245    if (auto val = value.type.enum_def->ReverseLookup(
246        atoi(value.constant.c_str()), false)) {
247      return WrapInNameSpace(*value.type.enum_def) + "." + val->name;
248    } else {
249      return "/** @type {" + WrapInNameSpace(*value.type.enum_def) + "} */ ("
250        + value.constant + ")";
251    }
252  }
253
254  switch (value.type.base_type) {
255    case BASE_TYPE_BOOL:
256      return value.constant == "0" ? "false" : "true";
257
258    case BASE_TYPE_STRING:
259      return "null";
260
261    case BASE_TYPE_LONG:
262    case BASE_TYPE_ULONG: {
263      int64_t constant = StringToInt(value.constant.c_str());
264      return context + ".createLong(" + NumToString((int32_t)constant) +
265        ", " + NumToString((int32_t)(constant >> 32)) + ")";
266    }
267
268    default:
269      return value.constant;
270  }
271}
272
273std::string GenTypeName(const Type &type, bool input) {
274  if (!input) {
275    if (type.base_type == BASE_TYPE_STRING) {
276      return "string|Uint8Array";
277    }
278    if (type.base_type == BASE_TYPE_STRUCT) {
279      return WrapInNameSpace(*type.struct_def);
280    }
281  }
282
283  switch (type.base_type) {
284    case BASE_TYPE_BOOL: return "boolean";
285    case BASE_TYPE_LONG:
286    case BASE_TYPE_ULONG: return "flatbuffers.Long";
287    default:
288      if (IsScalar(type.base_type)) {
289        if (type.enum_def) {
290          return WrapInNameSpace(*type.enum_def);
291        }
292        return "number";
293      }
294      return "flatbuffers.Offset";
295  }
296}
297
298// Returns the method name for use with add/put calls.
299static std::string GenWriteMethod(const Type &type) {
300  // Forward to signed versions since unsigned versions don't exist
301  switch (type.base_type) {
302    case BASE_TYPE_UTYPE:
303    case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR));
304    case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT));
305    case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT));
306    case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG));
307    default: break;
308  }
309
310  return IsScalar(type.base_type)
311    ? MakeCamel(GenType(type))
312    : (IsStruct(type) ? "Struct" : "Offset");
313}
314
315template <typename T>
316static std::string MaybeAdd(T value) {
317  return value != 0 ? " + " + NumToString(value) : "";
318}
319
320template <typename T>
321static std::string MaybeScale(T value) {
322  return value != 1 ? " * " + NumToString(value) : "";
323}
324
325void GenStructArgs(const StructDef &struct_def,
326                          std::string *annotations,
327                          std::string *arguments,
328                          const std::string &nameprefix) {
329  for (auto it = struct_def.fields.vec.begin();
330       it != struct_def.fields.vec.end(); ++it) {
331    auto &field = **it;
332    if (IsStruct(field.value.type)) {
333      // Generate arguments for a struct inside a struct. To ensure names
334      // don't clash, and to make it obvious these arguments are constructing
335      // a nested struct, prefix the name with the field name.
336      GenStructArgs(*field.value.type.struct_def, annotations, arguments,
337                    nameprefix + field.name + "_");
338    } else {
339      *annotations += "@param {" + GenTypeName(field.value.type, true);
340      *annotations += "} " + nameprefix + field.name + "\n";
341      *arguments += ", " + nameprefix + field.name;
342    }
343  }
344}
345
346static void GenStructBody(const StructDef &struct_def,
347                          std::string *body,
348                          const std::string &nameprefix) {
349  *body += "  builder.prep(";
350  *body += NumToString(struct_def.minalign) + ", ";
351  *body += NumToString(struct_def.bytesize) + ");\n";
352
353  for (auto it = struct_def.fields.vec.rbegin();
354       it != struct_def.fields.vec.rend(); ++it) {
355    auto &field = **it;
356    if (field.padding) {
357      *body += "  builder.pad(" + NumToString(field.padding) + ");\n";
358    }
359    if (IsStruct(field.value.type)) {
360      // Generate arguments for a struct inside a struct. To ensure names
361      // don't clash, and to make it obvious these arguments are constructing
362      // a nested struct, prefix the name with the field name.
363      GenStructBody(*field.value.type.struct_def, body,
364                    nameprefix + field.name + "_");
365    } else {
366      *body += "  builder.write" + GenWriteMethod(field.value.type) + "(";
367      if (field.value.type.base_type == BASE_TYPE_BOOL) {
368        *body += "+";
369      }
370      *body += nameprefix + field.name + ");\n";
371    }
372  }
373}
374
375// Generate an accessor struct with constructor for a flatbuffers struct.
376void GenStruct(const Parser &parser, StructDef &struct_def, std::string *code_ptr, std::string *exports_ptr) {
377  if (struct_def.generated) return;
378  std::string &code = *code_ptr;
379  std::string &exports = *exports_ptr;
380
381  // Emit constructor
382  bool isStatement = struct_def.defined_namespace->components.empty();
383  std::string object_name = WrapInNameSpace(struct_def);
384  GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
385  if (isStatement) {
386    if(parser_.opts.use_goog_js_export_format) {
387      exports += "goog.exportSymbol('" + struct_def.name + "', " +
388        struct_def.name + ");\n";
389    } else {
390      exports += "this." + struct_def.name + " = " + struct_def.name + ";\n";
391    }
392    code += "function " + object_name;
393  } else {
394    code += object_name + " = function";
395  }
396  code += "() {\n";
397  code += "  /**\n";
398  code += "   * @type {flatbuffers.ByteBuffer}\n";
399  code += "   */\n";
400  code += "  this.bb = null;\n";
401  code += "\n";
402  code += "  /**\n";
403  code += "   * @type {number}\n";
404  code += "   */\n";
405  code += "  this.bb_pos = 0;\n";
406  code += isStatement ? "}\n\n" : "};\n\n";
407
408  // Generate the __init method that sets the field in a pre-existing
409  // accessor object. This is to allow object reuse.
410  code += "/**\n";
411  code += " * @param {number} i\n";
412  code += " * @param {flatbuffers.ByteBuffer} bb\n";
413  code += " * @returns {" + object_name + "}\n";
414  code += " */\n";
415  code += object_name + ".prototype.__init = function(i, bb) {\n";
416  code += "  this.bb_pos = i;\n";
417  code += "  this.bb = bb;\n";
418  code += "  return this;\n";
419  code += "};\n\n";
420
421  // Generate a special accessor for the table that when used as the root of a
422  // FlatBuffer
423  if (!struct_def.fixed) {
424    GenDocComment(code_ptr,
425      "@param {flatbuffers.ByteBuffer} bb\n"
426      "@param {" + object_name + "=} obj\n"
427      "@returns {" + object_name + "}");
428    code += object_name + ".getRootAs" + struct_def.name;
429    code += " = function(bb, obj) {\n";
430    code += "  return (obj || new " + object_name;
431    code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
432    code += "};\n\n";
433
434    // Generate the identifier check method
435    if (parser_.root_struct_def_ == &struct_def &&
436        !parser_.file_identifier_.empty()) {
437      GenDocComment(code_ptr,
438        "@param {flatbuffers.ByteBuffer} bb\n"
439        "@returns {boolean}");
440      code += object_name + ".bufferHasIdentifier = function(bb) {\n";
441      code += "  return bb.__has_identifier('" + parser_.file_identifier_;
442      code += "');\n};\n\n";
443    }
444  }
445
446  // Emit field accessors
447  for (auto it = struct_def.fields.vec.begin();
448       it != struct_def.fields.vec.end(); ++it) {
449    auto &field = **it;
450    if (field.deprecated) continue;
451    auto offset_prefix = "  var offset = this.bb.__offset(this.bb_pos, " +
452      NumToString(field.value.offset) + ");\n  return offset ? ";
453
454    // Emit a scalar field
455    if (IsScalar(field.value.type.base_type) ||
456        field.value.type.base_type == BASE_TYPE_STRING) {
457      GenDocComment(field.doc_comment, code_ptr,
458        std::string(field.value.type.base_type == BASE_TYPE_STRING ?
459          "@param {flatbuffers.Encoding=} optionalEncoding\n" : "") +
460        "@returns {" + GenTypeName(field.value.type, false) + "}");
461      code += object_name + ".prototype." + MakeCamel(field.name, false);
462      code += " = function(";
463      if (field.value.type.base_type == BASE_TYPE_STRING) {
464        code += "optionalEncoding";
465      }
466      code += ") {\n";
467      if (struct_def.fixed) {
468        code += "  return " + GenGetter(field.value.type, "(this.bb_pos" +
469          MaybeAdd(field.value.offset) + ")") + ";\n";
470      } else {
471        std::string index = "this.bb_pos + offset";
472        if (field.value.type.base_type == BASE_TYPE_STRING) {
473          index += ", optionalEncoding";
474        }
475        code += offset_prefix + GenGetter(field.value.type,
476          "(" + index + ")") + " : " + GenDefaultValue(field.value, "this.bb");
477        code += ";\n";
478      }
479    }
480
481    // Emit an object field
482    else {
483      switch (field.value.type.base_type) {
484        case BASE_TYPE_STRUCT: {
485          auto type = WrapInNameSpace(*field.value.type.struct_def);
486          GenDocComment(field.doc_comment, code_ptr,
487            "@param {" + type + "=} obj\n@returns {" + type + "}");
488          code += object_name + ".prototype." + MakeCamel(field.name, false);
489          code += " = function(obj) {\n";
490          if (struct_def.fixed) {
491            code += "  return (obj || new " + type;
492            code += ").__init(this.bb_pos";
493            code += MaybeAdd(field.value.offset) + ", this.bb);\n";
494          } else {
495            code += offset_prefix + "(obj || new " + type + ").__init(";
496            code += field.value.type.struct_def->fixed
497              ? "this.bb_pos + offset"
498              : "this.bb.__indirect(this.bb_pos + offset)";
499            code += ", this.bb) : null;\n";
500          }
501          break;
502        }
503
504        case BASE_TYPE_VECTOR: {
505          auto vectortype = field.value.type.VectorType();
506          auto vectortypename = GenTypeName(vectortype, false);
507          auto inline_size = InlineSize(vectortype);
508          auto index = "this.bb.__vector(this.bb_pos + offset) + index" +
509                       MaybeScale(inline_size);
510          std::string args = "@param {number} index\n";
511          if (vectortype.base_type == BASE_TYPE_STRUCT) {
512            args += "@param {" + vectortypename + "=} obj\n";
513          } else if (vectortype.base_type == BASE_TYPE_STRING) {
514            args += "@param {flatbuffers.Encoding=} optionalEncoding\n";
515          }
516          GenDocComment(field.doc_comment, code_ptr, args +
517            "@returns {" + vectortypename + "}");
518          code += object_name + ".prototype." + MakeCamel(field.name, false);
519          code += " = function(index";
520          if (vectortype.base_type == BASE_TYPE_STRUCT) {
521            code += ", obj";
522          } else if (vectortype.base_type == BASE_TYPE_STRING) {
523            code += ", optionalEncoding";
524          }
525          code += ") {\n";
526          if (vectortype.base_type == BASE_TYPE_STRUCT) {
527            code += offset_prefix + "(obj || new " + vectortypename;
528            code += ").__init(";
529            code += vectortype.struct_def->fixed
530              ? index
531              : "this.bb.__indirect(" + index + ")";
532            code += ", this.bb)";
533          } else {
534            if (vectortype.base_type == BASE_TYPE_STRING) {
535              index += ", optionalEncoding";
536            }
537            code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
538          }
539          code += " : ";
540          if (field.value.type.element == BASE_TYPE_BOOL) {
541            code += "false";
542          } else if (field.value.type.element == BASE_TYPE_LONG ||
543              field.value.type.element == BASE_TYPE_ULONG) {
544            code += "this.bb.createLong(0, 0)";
545          } else if (IsScalar(field.value.type.element)) {
546            if (field.value.type.enum_def) {
547              code += "/** @type {" +
548                WrapInNameSpace(*field.value.type.enum_def) + "} */ (" +
549                field.value.constant + ")";
550            } else {
551              code += "0";
552            }
553          } else {
554            code += "null";
555          }
556          code += ";\n";
557          break;
558        }
559
560        case BASE_TYPE_UNION:
561          GenDocComment(field.doc_comment, code_ptr,
562            "@param {flatbuffers.Table} obj\n"
563            "@returns {?flatbuffers.Table}");
564          code += object_name + ".prototype." + MakeCamel(field.name, false);
565          code += " = function(obj) {\n";
566          code += offset_prefix + GenGetter(field.value.type,
567            "(obj, this.bb_pos + offset)") + " : null;\n";
568          break;
569
570        default:
571          assert(0);
572      }
573    }
574    code += "};\n\n";
575
576    if(parser_.opts.use_goog_js_export_format) {
577      exports += "goog.exportProperty(" + object_name + ".prototype, '" +
578        MakeCamel(field.name, false) + "', " + object_name + ".prototype." +
579        MakeCamel(field.name, false) + ");\n";
580    }
581
582    // Adds the mutable scalar value to the output
583    if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) {
584      std::string annotations = "@param {" + GenTypeName(field.value.type, true) + "} value\n";
585      GenDocComment(code_ptr, annotations +
586        "@returns {boolean}");
587
588      code += object_name + ".prototype.mutate_" + field.name + " = function(value) {\n";
589      code += "  var offset = this.bb.__offset(this.bb_pos, " + NumToString(field.value.offset) + ");\n\n";
590      code += "  if (offset === 0) {\n";
591      code += "    return false;\n";
592      code += "  }\n\n";
593      code += "  this.bb.write" + MakeCamel(GenType(field.value.type)) + "(this.bb_pos + offset, value);\n";
594      code += "  return true;\n";
595      code += "};\n\n";
596
597      if(parser_.opts.use_goog_js_export_format) {
598        exports += "goog.exportProperty(" + object_name +
599          ".prototype, 'mutate_" + field.name + "', " + object_name +
600          ".prototype.mutate_" + field.name + ");\n";
601      }
602    }
603
604    // Emit vector helpers
605    if (field.value.type.base_type == BASE_TYPE_VECTOR) {
606      // Emit a length helper
607      GenDocComment(code_ptr, "@returns {number}");
608      code += object_name + ".prototype." + MakeCamel(field.name, false);
609      code += "Length = function() {\n" + offset_prefix;
610      code += "this.bb.__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
611
612      if(parser_.opts.use_goog_js_export_format) {
613        exports += "goog.exportProperty(" + object_name + ".prototype, '" +
614          MakeCamel(field.name, false) + "Length', " + object_name +
615          ".prototype." + MakeCamel(field.name, false) + "Length);\n";
616      }
617
618      // For scalar types, emit a typed array helper
619      auto vectorType = field.value.type.VectorType();
620      if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
621        GenDocComment(code_ptr, "@returns {" + GenType(vectorType) + "Array}");
622        code += object_name + ".prototype." + MakeCamel(field.name, false);
623        code += "Array = function() {\n" + offset_prefix;
624        code += "new " + GenType(vectorType) + "Array(this.bb.bytes().buffer, "
625          "this.bb.bytes().byteOffset + this.bb.__vector(this.bb_pos + offset), "
626          "this.bb.__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
627
628        if(parser_.opts.use_goog_js_export_format) {
629          exports += "goog.exportProperty(" + object_name + ".prototype, '" +
630            MakeCamel(field.name, false) + "Array', " + object_name +
631            ".prototype." + MakeCamel(field.name, false) + "Array);\n";
632        }
633      }
634    }
635  }
636
637  // Emit a factory constructor
638  if (struct_def.fixed) {
639    std::string annotations = "@param {flatbuffers.Builder} builder\n";
640    std::string arguments;
641    GenStructArgs(struct_def, &annotations, &arguments, "");
642    GenDocComment(code_ptr, annotations +
643      "@returns {flatbuffers.Offset}");
644    code += object_name + ".create" + struct_def.name + " = function(builder";
645    code += arguments + ") {\n";
646    GenStructBody(struct_def, &code, "");
647    code += "  return builder.offset();\n};\n\n";
648  } else {
649    // Generate a method to start building a new object
650    GenDocComment(code_ptr,
651      "@param {flatbuffers.Builder} builder");
652    code += object_name + ".start" + struct_def.name;
653    code += " = function(builder) {\n";
654    code += "  builder.startObject(" + NumToString(
655      struct_def.fields.vec.size()) + ");\n";
656    code += "};\n\n";
657
658    // Generate a set of static methods that allow table construction
659    for (auto it = struct_def.fields.vec.begin();
660         it != struct_def.fields.vec.end(); ++it) {
661      auto &field = **it;
662      if (field.deprecated) continue;
663      auto argname = MakeCamel(field.name, false);
664      if (!IsScalar(field.value.type.base_type)) {
665        argname += "Offset";
666      }
667
668      // Generate the field insertion method
669      GenDocComment(code_ptr,
670        "@param {flatbuffers.Builder} builder\n"
671        "@param {" + GenTypeName(field.value.type, true) + "} " +
672        argname);
673      code += object_name + ".add" + MakeCamel(field.name);
674      code += " = function(builder, " + argname + ") {\n";
675      code += "  builder.addField" + GenWriteMethod(field.value.type) + "(";
676      code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
677      if (field.value.type.base_type == BASE_TYPE_BOOL) {
678        code += "+";
679      }
680      code += argname + ", ";
681      if (!IsScalar(field.value.type.base_type)) {
682        code += "0";
683      } else {
684        if (field.value.type.base_type == BASE_TYPE_BOOL) {
685          code += "+";
686        }
687        code += GenDefaultValue(field.value, "builder");
688      }
689      code += ");\n};\n\n";
690
691      if (field.value.type.base_type == BASE_TYPE_VECTOR) {
692        auto vector_type = field.value.type.VectorType();
693        auto alignment = InlineAlignment(vector_type);
694        auto elem_size = InlineSize(vector_type);
695
696        // Generate a method to create a vector from a JavaScript array
697        if (!IsStruct(vector_type)) {
698          GenDocComment(code_ptr,
699            "@param {flatbuffers.Builder} builder\n"
700            "@param {Array.<" + GenTypeName(vector_type, true) +
701            ">} data\n"
702            "@returns {flatbuffers.Offset}");
703          code += object_name + ".create" + MakeCamel(field.name);
704          code += "Vector = function(builder, data) {\n";
705          code += "  builder.startVector(" + NumToString(elem_size);
706          code += ", data.length, " + NumToString(alignment) + ");\n";
707          code += "  for (var i = data.length - 1; i >= 0; i--) {\n";
708          code += "    builder.add" + GenWriteMethod(vector_type) + "(";
709          if (vector_type.base_type == BASE_TYPE_BOOL) {
710            code += "+";
711          }
712          code += "data[i]);\n";
713          code += "  }\n";
714          code += "  return builder.endVector();\n";
715          code += "};\n\n";
716        }
717
718        // Generate a method to start a vector, data to be added manually after
719        GenDocComment(code_ptr,
720          "@param {flatbuffers.Builder} builder\n"
721          "@param {number} numElems");
722        code += object_name + ".start" + MakeCamel(field.name);
723        code += "Vector = function(builder, numElems) {\n";
724        code += "  builder.startVector(" + NumToString(elem_size);
725        code += ", numElems, " + NumToString(alignment) + ");\n";
726        code += "};\n\n";
727      }
728    }
729
730    // Generate a method to stop building a new object
731    GenDocComment(code_ptr,
732      "@param {flatbuffers.Builder} builder\n"
733      "@returns {flatbuffers.Offset}");
734    code += object_name + ".end" + struct_def.name;
735    code += " = function(builder) {\n";
736    code += "  var offset = builder.endObject();\n";
737    for (auto it = struct_def.fields.vec.begin();
738         it != struct_def.fields.vec.end(); ++it) {
739      auto &field = **it;
740      if (!field.deprecated && field.required) {
741        code += "  builder.requiredField(offset, ";
742        code += NumToString(field.value.offset);
743        code += "); // " + field.name + "\n";
744      }
745    }
746    code += "  return offset;\n";
747    code += "};\n\n";
748
749    // Generate the method to complete buffer construction
750    if (parser_.root_struct_def_ == &struct_def) {
751      GenDocComment(code_ptr,
752        "@param {flatbuffers.Builder} builder\n"
753        "@param {flatbuffers.Offset} offset");
754      code += object_name + ".finish" + struct_def.name + "Buffer";
755      code += " = function(builder, offset) {\n";
756      code += "  builder.finish(offset";
757      if (!parser_.file_identifier_.empty()) {
758        code += ", '" + parser_.file_identifier_ + "'";
759      }
760      code += ");\n";
761      code += "};\n\n";
762    }
763  }
764}
765};
766}  // namespace js
767
768bool GenerateJS(const Parser &parser, const std::string &path,
769                const std::string &file_name) {
770  js::JsGenerator generator(parser, path, file_name);
771  return generator.generate();
772}
773
774std::string JSMakeRule(const Parser &parser,
775                       const std::string &path,
776                       const std::string &file_name) {
777  std::string filebase = flatbuffers::StripPath(
778      flatbuffers::StripExtension(file_name));
779  std::string make_rule = GeneratedFileName(path, filebase) + ": ";
780  auto included_files = parser.GetIncludedFilesRecursive(file_name);
781  for (auto it = included_files.begin();
782       it != included_files.end(); ++it) {
783    make_rule += " " + *it;
784  }
785  return make_rule;
786}
787
788}  // namespace flatbuffers
789