• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
24 namespace flatbuffers {
25 
GeneratedFileName(const std::string & path,const std::string & file_name)26 static std::string GeneratedFileName(const std::string &path,
27                                      const std::string &file_name) {
28   return path + file_name + "_generated.js";
29 }
30 
31 namespace js {
32 // Iterate through all definitions we haven't generate code for (enums, structs,
33 // and tables) and output them to a single file.
34 class JsGenerator : public BaseGenerator {
35  public:
JsGenerator(const Parser & parser,const std::string & path,const std::string & file_name)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.
generate()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.
generateEnums(std::string * enum_code_ptr,std::string * exports_code_ptr)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.
generateStructs(std::string * decl_code_ptr,std::string * exports_code_ptr)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   }
GenNamespaces(std::string * code_ptr,std::string * exports_ptr)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.
GenDocComment(const std::vector<std::string> & dc,std::string * code_ptr,const std::string & extra_lines,const char * indent=nullptr)127 static 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 
GenDocComment(std::string * code_ptr,const std::string & extra_lines)165 static 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.
GenEnum(EnumDef & enum_def,std::string * code_ptr,std::string * exports_ptr)171 void 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 
GenType(const Type & type)202 static 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 
GenGetter(const Type & type,const std::string & arguments)223 std::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 
GenDefaultValue(const Value & value,const std::string & context)243 std::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 
GenTypeName(const Type & type,bool input)273 std::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.
GenWriteMethod(const Type & type)299 static 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 
315 template <typename T>
MaybeAdd(T value)316 static std::string MaybeAdd(T value) {
317   return value != 0 ? " + " + NumToString(value) : "";
318 }
319 
320 template <typename T>
MaybeScale(T value)321 static std::string MaybeScale(T value) {
322   return value != 1 ? " * " + NumToString(value) : "";
323 }
324 
GenStructArgs(const StructDef & struct_def,std::string * annotations,std::string * arguments,const std::string & nameprefix)325 void 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 
GenStructBody(const StructDef & struct_def,std::string * body,const std::string & nameprefix)346 static 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.
GenStruct(const Parser & parser,StructDef & struct_def,std::string * code_ptr,std::string * exports_ptr)376 void 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 
GenerateJS(const Parser & parser,const std::string & path,const std::string & file_name)768 bool 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 
JSMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)774 std::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