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