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