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) { return typeName; }
552 return GenFileNamespacePrefix(file) + "." + typeName;
553 }
554
GenStructArgs(const StructDef & struct_def,std::string * annotations,std::string * arguments,const std::string & nameprefix)555 void GenStructArgs(const StructDef &struct_def, std::string *annotations,
556 std::string *arguments, const std::string &nameprefix) {
557 for (auto it = struct_def.fields.vec.begin();
558 it != struct_def.fields.vec.end(); ++it) {
559 auto &field = **it;
560 if (IsStruct(field.value.type)) {
561 // Generate arguments for a struct inside a struct. To ensure names
562 // don't clash, and to make it obvious these arguments are constructing
563 // a nested struct, prefix the name with the field name.
564 GenStructArgs(*field.value.type.struct_def, annotations, arguments,
565 nameprefix + field.name + "_");
566 } else {
567 *annotations +=
568 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
569 nameprefix + field.name);
570 if (lang_.language == IDLOptions::kTs) {
571 *arguments += ", " + nameprefix + field.name + ": " +
572 GenTypeName(field.value.type, true);
573 } else {
574 *arguments += ", " + nameprefix + field.name;
575 }
576 }
577 }
578 }
579
GenStructBody(const StructDef & struct_def,std::string * body,const std::string & nameprefix)580 static void GenStructBody(const StructDef &struct_def, std::string *body,
581 const std::string &nameprefix) {
582 *body += " builder.prep(";
583 *body += NumToString(struct_def.minalign) + ", ";
584 *body += NumToString(struct_def.bytesize) + ");\n";
585
586 for (auto it = struct_def.fields.vec.rbegin();
587 it != struct_def.fields.vec.rend(); ++it) {
588 auto &field = **it;
589 if (field.padding) {
590 *body += " builder.pad(" + NumToString(field.padding) + ");\n";
591 }
592 if (IsStruct(field.value.type)) {
593 // Generate arguments for a struct inside a struct. To ensure names
594 // don't clash, and to make it obvious these arguments are constructing
595 // a nested struct, prefix the name with the field name.
596 GenStructBody(*field.value.type.struct_def, body,
597 nameprefix + field.name + "_");
598 } else {
599 *body += " builder.write" + GenWriteMethod(field.value.type) + "(";
600 if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+"; }
601 *body += nameprefix + field.name + ");\n";
602 }
603 }
604 }
605
606 // 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)607 void GenStruct(const Parser &parser, StructDef &struct_def,
608 std::string *code_ptr, std::string *exports_ptr,
609 imported_fileset &imported_files) {
610 if (struct_def.generated) return;
611 std::string &code = *code_ptr;
612 std::string &exports = *exports_ptr;
613
614 std::string object_name;
615 std::string object_namespace = GetNameSpace(struct_def);
616
617 // Emit constructor
618 if (lang_.language == IDLOptions::kTs) {
619 object_name = struct_def.name;
620 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
621 if (!object_namespace.empty()) {
622 code += "export namespace " + object_namespace + "{\n";
623 }
624 code += "export class " + struct_def.name;
625 code += " {\n";
626 if (lang_.language != IDLOptions::kTs) {
627 code += " /**\n";
628 code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
629 code += " */\n";
630 }
631 code += " bb: flatbuffers.ByteBuffer|null = null;\n";
632 code += "\n";
633 if (lang_.language != IDLOptions::kTs) {
634 code += " /**\n";
635 code += " * " + GenTypeAnnotation(kType, "number", "");
636 code += " */\n";
637 }
638 code += " bb_pos:number = 0;\n";
639 } else {
640 bool isStatement = struct_def.defined_namespace->components.empty();
641 object_name = WrapInNameSpace(struct_def);
642 GenDocComment(struct_def.doc_comment, code_ptr, "@constructor");
643 if (isStatement) {
644 if (parser_.opts.use_goog_js_export_format) {
645 exports += "goog.exportSymbol('" + struct_def.name + "', " +
646 struct_def.name + ");\n";
647 } else if (parser_.opts.use_ES6_js_export_format) {
648 exports += "export {" + struct_def.name + "};\n";
649 } else {
650 exports +=
651 "this." + struct_def.name + " = " + struct_def.name + ";\n";
652 }
653 code += "function " + object_name;
654 } else {
655 code += object_name + " = function";
656 }
657 code += "() {\n";
658 code += " /**\n";
659 code += " * " + GenTypeAnnotation(kType, "flatbuffers.ByteBuffer", "");
660 code += " */\n";
661 code += " this.bb = null;\n";
662 code += "\n";
663 code += " /**\n";
664 code += " * " + GenTypeAnnotation(kType, "number", "");
665 code += " */\n";
666 code += " this.bb_pos = 0;\n";
667 code += isStatement ? "}\n\n" : "};\n\n";
668 }
669
670 // Generate the __init method that sets the field in a pre-existing
671 // accessor object. This is to allow object reuse.
672 code += "/**\n";
673 code += " * " + GenTypeAnnotation(kParam, "number", "i");
674 code += " * " + GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb");
675 code += " * " + GenTypeAnnotation(kReturns, object_name, "");
676 code += " */\n";
677
678 if (lang_.language == IDLOptions::kTs) {
679 code +=
680 "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n";
681 } else {
682 code += object_name + ".prototype.__init = function(i, bb) {\n";
683 }
684
685 code += " this.bb_pos = i;\n";
686 code += " this.bb = bb;\n";
687 code += " return this;\n";
688 code += "};\n\n";
689
690 // Generate a special accessor for the table that when used as the root of a
691 // FlatBuffer
692 if (!struct_def.fixed) {
693 GenDocComment(code_ptr,
694 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
695 GenTypeAnnotation(kParam, object_name + "=", "obj") +
696 GenTypeAnnotation(kReturns, object_name, "", false));
697 if (lang_.language == IDLOptions::kTs) {
698 code += "static getRootAs" + struct_def.name;
699 code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name +
700 "):" + object_name + " {\n";
701 } else {
702 code += object_name + ".getRootAs" + struct_def.name;
703 code += " = function(bb, obj) {\n";
704 }
705 code += " return (obj || new " + object_name;
706 code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n";
707 code += "};\n\n";
708
709 // Generate the identifier check method
710 if (parser_.root_struct_def_ == &struct_def &&
711 !parser_.file_identifier_.empty()) {
712 GenDocComment(
713 code_ptr,
714 GenTypeAnnotation(kParam, "flatbuffers.ByteBuffer", "bb") +
715 GenTypeAnnotation(kReturns, "boolean", "", false));
716 if (lang_.language == IDLOptions::kTs) {
717 code +=
718 "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean "
719 "{\n";
720 } else {
721 code += object_name + ".bufferHasIdentifier = function(bb) {\n";
722 }
723
724 code += " return bb.__has_identifier('" + parser_.file_identifier_;
725 code += "');\n};\n\n";
726 }
727 }
728
729 // Emit field accessors
730 for (auto it = struct_def.fields.vec.begin();
731 it != struct_def.fields.vec.end(); ++it) {
732 auto &field = **it;
733 if (field.deprecated) continue;
734 auto offset_prefix =
735 " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
736 NumToString(field.value.offset) + ");\n return offset ? ";
737
738 // Emit a scalar field
739 if (IsScalar(field.value.type.base_type) ||
740 field.value.type.base_type == BASE_TYPE_STRING) {
741 GenDocComment(
742 field.doc_comment, code_ptr,
743 std::string(field.value.type.base_type == BASE_TYPE_STRING
744 ? GenTypeAnnotation(kParam, "flatbuffers.Encoding=",
745 "optionalEncoding")
746 : "") +
747 GenTypeAnnotation(kReturns,
748 GenTypeName(field.value.type, false, true),
749 "", false));
750 if (lang_.language == IDLOptions::kTs) {
751 std::string prefix = MakeCamel(field.name, false) + "(";
752 if (field.value.type.base_type == BASE_TYPE_STRING) {
753 code += prefix + "):string|null\n";
754 code += prefix + "optionalEncoding:flatbuffers.Encoding" +
755 "):" + GenTypeName(field.value.type, false, true) + "\n";
756 code += prefix + "optionalEncoding?:any";
757 } else {
758 code += prefix;
759 }
760 if (field.value.type.enum_def) {
761 code +=
762 "):" +
763 GenPrefixedTypeName(GenTypeName(field.value.type, false, true),
764 field.value.type.enum_def->file) +
765 " {\n";
766 } else {
767 code += "):" + GenTypeName(field.value.type, false, true) + " {\n";
768 }
769 } else {
770 code += object_name + ".prototype." + MakeCamel(field.name, false);
771 code += " = function(";
772 if (field.value.type.base_type == BASE_TYPE_STRING) {
773 code += "optionalEncoding";
774 }
775 code += ") {\n";
776 }
777
778 if (struct_def.fixed) {
779 code +=
780 " return " +
781 GenGetter(field.value.type,
782 "(this.bb_pos" + MaybeAdd(field.value.offset) + ")") +
783 ";\n";
784 } else {
785 std::string index = "this.bb_pos + offset";
786 if (field.value.type.base_type == BASE_TYPE_STRING) {
787 index += ", optionalEncoding";
788 }
789 code += offset_prefix +
790 GenGetter(field.value.type, "(" + index + ")") + " : " +
791 GenDefaultValue(field.value, GenBBAccess());
792 code += ";\n";
793 }
794 }
795
796 // Emit an object field
797 else {
798 switch (field.value.type.base_type) {
799 case BASE_TYPE_STRUCT: {
800 auto type = WrapInNameSpace(*field.value.type.struct_def);
801 GenDocComment(
802 field.doc_comment, code_ptr,
803 GenTypeAnnotation(kParam, type + "=", "obj") +
804 GenTypeAnnotation(kReturns, type + "|null", "", false));
805 if (lang_.language == IDLOptions::kTs) {
806 type =
807 GenPrefixedTypeName(type, field.value.type.struct_def->file);
808 code += MakeCamel(field.name, false);
809 code += "(obj?:" + type + "):" + type + "|null {\n";
810 } else {
811 code +=
812 object_name + ".prototype." + MakeCamel(field.name, false);
813 code += " = function(obj) {\n";
814 }
815
816 if (struct_def.fixed) {
817 code += " return (obj || new " + type;
818 code += ").__init(this.bb_pos";
819 code +=
820 MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n";
821 } else {
822 code += offset_prefix + "(obj || new " + type + ").__init(";
823 code += field.value.type.struct_def->fixed
824 ? "this.bb_pos + offset"
825 : GenBBAccess() + ".__indirect(this.bb_pos + offset)";
826 code += ", " + GenBBAccess() + ") : null;\n";
827 }
828
829 if (lang_.language == IDLOptions::kTs && !parser_.opts.generate_all) {
830 imported_files.insert(field.value.type.struct_def->file);
831 }
832
833 break;
834 }
835
836 case BASE_TYPE_VECTOR: {
837 auto vectortype = field.value.type.VectorType();
838 auto vectortypename = GenTypeName(vectortype, false);
839 auto inline_size = InlineSize(vectortype);
840 auto index = GenBBAccess() +
841 ".__vector(this.bb_pos + offset) + index" +
842 MaybeScale(inline_size);
843 std::string args = GenTypeAnnotation(kParam, "number", "index");
844 std::string ret_type;
845 bool is_union = false;
846 switch (vectortype.base_type) {
847 case BASE_TYPE_STRUCT:
848 args += GenTypeAnnotation(kParam, vectortypename + "=", "obj");
849 ret_type = vectortypename;
850 break;
851 case BASE_TYPE_STRING:
852 args += GenTypeAnnotation(
853 kParam, "flatbuffers.Encoding=", "optionalEncoding");
854 ret_type = vectortypename;
855 break;
856 case BASE_TYPE_UNION:
857 args += GenTypeAnnotation(kParam, "flatbuffers.Table=", "obj");
858 ret_type = "?flatbuffers.Table";
859 is_union = true;
860 break;
861 default: ret_type = vectortypename;
862 }
863 GenDocComment(
864 field.doc_comment, code_ptr,
865 args + GenTypeAnnotation(kReturns, ret_type, "", false));
866 if (lang_.language == IDLOptions::kTs) {
867 std::string prefix = MakeCamel(field.name, false);
868 if (is_union) { prefix += "<T extends flatbuffers.Table>"; }
869 prefix += "(index: number";
870 if (is_union) {
871 vectortypename = "T";
872 code += prefix + ", obj:T";
873 } else if (vectortype.base_type == BASE_TYPE_STRUCT) {
874 vectortypename = GenPrefixedTypeName(
875 vectortypename, vectortype.struct_def->file);
876 code += prefix + ", obj?:" + vectortypename;
877
878 if (!parser_.opts.generate_all) {
879 imported_files.insert(vectortype.struct_def->file);
880 }
881 } else if (vectortype.base_type == BASE_TYPE_STRING) {
882 code += prefix + "):string\n";
883 code += prefix + ",optionalEncoding:flatbuffers.Encoding" +
884 "):" + vectortypename + "\n";
885 code += prefix + ",optionalEncoding?:any";
886 } else {
887 code += prefix;
888 }
889 code += "):" + vectortypename + "|null {\n";
890 } else {
891 code +=
892 object_name + ".prototype." + MakeCamel(field.name, false);
893 code += " = function(index";
894 if (vectortype.base_type == BASE_TYPE_STRUCT || is_union) {
895 code += ", obj";
896 } else if (vectortype.base_type == BASE_TYPE_STRING) {
897 code += ", optionalEncoding";
898 }
899 code += ") {\n";
900 }
901
902 if (vectortype.base_type == BASE_TYPE_STRUCT) {
903 code += offset_prefix + "(obj || new " + vectortypename;
904 code += ").__init(";
905 code += vectortype.struct_def->fixed
906 ? index
907 : GenBBAccess() + ".__indirect(" + index + ")";
908 code += ", " + GenBBAccess() + ")";
909 } else {
910 if (is_union) {
911 index = "obj, " + index;
912 } else if (vectortype.base_type == BASE_TYPE_STRING) {
913 index += ", optionalEncoding";
914 }
915 code += offset_prefix + GenGetter(vectortype, "(" + index + ")");
916 }
917 code += " : ";
918 if (field.value.type.element == BASE_TYPE_BOOL) {
919 code += "false";
920 } else if (field.value.type.element == BASE_TYPE_LONG ||
921 field.value.type.element == BASE_TYPE_ULONG) {
922 code += GenBBAccess() + ".createLong(0, 0)";
923 } else if (IsScalar(field.value.type.element)) {
924 if (field.value.type.enum_def) {
925 code += "/** " +
926 GenTypeAnnotation(
927 kType, WrapInNameSpace(*field.value.type.enum_def),
928 "", false) +
929 " */ (" + field.value.constant + ")";
930 } else {
931 code += "0";
932 }
933 } else {
934 code += "null";
935 }
936 code += ";\n";
937 break;
938 }
939
940 case BASE_TYPE_UNION:
941 GenDocComment(
942 field.doc_comment, code_ptr,
943 GenTypeAnnotation(kParam, "flatbuffers.Table", "obj") +
944 GenTypeAnnotation(kReturns, "?flatbuffers.Table", "",
945 false));
946 if (lang_.language == IDLOptions::kTs) {
947 code += MakeCamel(field.name, false);
948 code += "<T extends flatbuffers.Table>(obj:T):T|null {\n";
949 } else {
950 code +=
951 object_name + ".prototype." + MakeCamel(field.name, false);
952 code += " = function(obj) {\n";
953 }
954
955 code += offset_prefix +
956 GenGetter(field.value.type, "(obj, this.bb_pos + offset)") +
957 " : null;\n";
958 break;
959
960 default: FLATBUFFERS_ASSERT(0);
961 }
962 }
963 code += "};\n\n";
964
965 if (parser_.opts.use_goog_js_export_format) {
966 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
967 MakeCamel(field.name, false) + "', " + object_name +
968 ".prototype." + MakeCamel(field.name, false) + ");\n";
969 }
970
971 // Adds the mutable scalar value to the output
972 if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer) {
973 std::string annotations = GenTypeAnnotation(
974 kParam, GenTypeName(field.value.type, true), "value");
975 GenDocComment(
976 code_ptr,
977 annotations + GenTypeAnnotation(kReturns, "boolean", "", false));
978
979 if (lang_.language == IDLOptions::kTs) {
980 std::string type;
981 if (field.value.type.enum_def) {
982 type = GenPrefixedTypeName(GenTypeName(field.value.type, true),
983 field.value.type.enum_def->file);
984 } else {
985 type = GenTypeName(field.value.type, true);
986 }
987
988 code += "mutate_" + field.name + "(value:" + type + "):boolean {\n";
989 } else {
990 code += object_name + ".prototype.mutate_" + field.name +
991 " = function(value) {\n";
992 }
993
994 code += " var offset = " + GenBBAccess() + ".__offset(this.bb_pos, " +
995 NumToString(field.value.offset) + ");\n\n";
996 code += " if (offset === 0) {\n";
997 code += " return false;\n";
998 code += " }\n\n";
999
1000 // special case for bools, which are treated as uint8
1001 code += " " + GenBBAccess() + ".write" +
1002 MakeCamel(GenType(field.value.type)) +
1003 "(this.bb_pos + offset, ";
1004 if (field.value.type.base_type == BASE_TYPE_BOOL &&
1005 lang_.language == IDLOptions::kTs) {
1006 code += "+";
1007 }
1008
1009 code += "value);\n";
1010 code += " return true;\n";
1011 code += "};\n\n";
1012
1013 if (parser_.opts.use_goog_js_export_format) {
1014 exports += "goog.exportProperty(" + object_name +
1015 ".prototype, 'mutate_" + field.name + "', " + object_name +
1016 ".prototype.mutate_" + field.name + ");\n";
1017 }
1018 }
1019
1020 // Emit vector helpers
1021 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1022 // Emit a length helper
1023 GenDocComment(code_ptr,
1024 GenTypeAnnotation(kReturns, "number", "", false));
1025 if (lang_.language == IDLOptions::kTs) {
1026 code += MakeCamel(field.name, false);
1027 code += "Length():number {\n" + offset_prefix;
1028 } else {
1029 code += object_name + ".prototype." + MakeCamel(field.name, false);
1030 code += "Length = function() {\n" + offset_prefix;
1031 }
1032
1033 code +=
1034 GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n};\n\n";
1035
1036 if (parser_.opts.use_goog_js_export_format) {
1037 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1038 MakeCamel(field.name, false) + "Length', " + object_name +
1039 ".prototype." + MakeCamel(field.name, false) +
1040 "Length);\n";
1041 }
1042
1043 // For scalar types, emit a typed array helper
1044 auto vectorType = field.value.type.VectorType();
1045 if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) {
1046 GenDocComment(code_ptr, GenTypeAnnotation(
1047 kReturns, GenType(vectorType) + "Array",
1048 "", false));
1049
1050 if (lang_.language == IDLOptions::kTs) {
1051 code += MakeCamel(field.name, false);
1052 code += "Array():" + GenType(vectorType) + "Array|null {\n" +
1053 offset_prefix;
1054 } else {
1055 code += object_name + ".prototype." + MakeCamel(field.name, false);
1056 code += "Array = function() {\n" + offset_prefix;
1057 }
1058
1059 code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() +
1060 ".bytes().buffer, " + GenBBAccess() +
1061 ".bytes().byteOffset + " + GenBBAccess() +
1062 ".__vector(this.bb_pos + offset), " + GenBBAccess() +
1063 ".__vector_len(this.bb_pos + offset)) : null;\n};\n\n";
1064
1065 if (parser_.opts.use_goog_js_export_format) {
1066 exports += "goog.exportProperty(" + object_name + ".prototype, '" +
1067 MakeCamel(field.name, false) + "Array', " + object_name +
1068 ".prototype." + MakeCamel(field.name, false) +
1069 "Array);\n";
1070 }
1071 }
1072 }
1073 }
1074
1075 // Emit a factory constructor
1076 if (struct_def.fixed) {
1077 std::string annotations =
1078 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder");
1079 std::string arguments;
1080 GenStructArgs(struct_def, &annotations, &arguments, "");
1081 GenDocComment(code_ptr, annotations + GenTypeAnnotation(
1082 kReturns, "flatbuffers.Offset",
1083 "", false));
1084
1085 if (lang_.language == IDLOptions::kTs) {
1086 code +=
1087 "static create" + struct_def.name + "(builder:flatbuffers.Builder";
1088 code += arguments + "):flatbuffers.Offset {\n";
1089 } else {
1090 code +=
1091 object_name + ".create" + struct_def.name + " = function(builder";
1092 code += arguments + ") {\n";
1093 }
1094
1095 GenStructBody(struct_def, &code, "");
1096 code += " return builder.offset();\n};\n\n";
1097 } else {
1098 // Generate a method to start building a new object
1099 GenDocComment(code_ptr, GenTypeAnnotation(kParam, "flatbuffers.Builder",
1100 "builder", false));
1101
1102 if (lang_.language == IDLOptions::kTs) {
1103 code += "static start" + struct_def.name;
1104 code += "(builder:flatbuffers.Builder) {\n";
1105 } else {
1106 code += object_name + ".start" + struct_def.name;
1107 code += " = function(builder) {\n";
1108 }
1109
1110 code += " builder.startObject(" +
1111 NumToString(struct_def.fields.vec.size()) + ");\n";
1112 code += "};\n\n";
1113
1114 // Generate a set of static methods that allow table construction
1115 for (auto it = struct_def.fields.vec.begin();
1116 it != struct_def.fields.vec.end(); ++it) {
1117 auto &field = **it;
1118 if (field.deprecated) continue;
1119 const auto argname = GetArgName(field);
1120
1121 // Generate the field insertion method
1122 GenDocComment(
1123 code_ptr,
1124 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1125 GenTypeAnnotation(kParam, GenTypeName(field.value.type, true),
1126 argname, false));
1127
1128 if (lang_.language == IDLOptions::kTs) {
1129 code += "static add" + MakeCamel(field.name);
1130 code += "(builder:flatbuffers.Builder, " + argname + ":" + GetArgType(field) +
1131 ") {\n";
1132 } else {
1133 code += object_name + ".add" + MakeCamel(field.name);
1134 code += " = function(builder, " + argname + ") {\n";
1135 }
1136
1137 code += " builder.addField" + GenWriteMethod(field.value.type) + "(";
1138 code += NumToString(it - struct_def.fields.vec.begin()) + ", ";
1139 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1140 code += argname + ", ";
1141 if (!IsScalar(field.value.type.base_type)) {
1142 code += "0";
1143 } else {
1144 if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1145 code += GenDefaultValue(field.value, "builder");
1146 }
1147 code += ");\n};\n\n";
1148
1149 if (field.value.type.base_type == BASE_TYPE_VECTOR) {
1150 auto vector_type = field.value.type.VectorType();
1151 auto alignment = InlineAlignment(vector_type);
1152 auto elem_size = InlineSize(vector_type);
1153
1154 // Generate a method to create a vector from a JavaScript array
1155 if (!IsStruct(vector_type)) {
1156 GenDocComment(
1157 code_ptr,
1158 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1159 GenTypeAnnotation(
1160 kParam,
1161 "Array.<" + GenTypeName(vector_type, true) + ">",
1162 "data") +
1163 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "",
1164 false));
1165
1166 if (lang_.language == IDLOptions::kTs) {
1167 code += "static create" + MakeCamel(field.name);
1168 std::string type = GenTypeName(vector_type, true) + "[]";
1169 if (type == "number[]") { type += " | Uint8Array"; }
1170 code += "Vector(builder:flatbuffers.Builder, data:" + type +
1171 "):flatbuffers.Offset {\n";
1172 } else {
1173 code += object_name + ".create" + MakeCamel(field.name);
1174 code += "Vector = function(builder, data) {\n";
1175 }
1176
1177 code += " builder.startVector(" + NumToString(elem_size);
1178 code += ", data.length, " + NumToString(alignment) + ");\n";
1179 code += " for (var i = data.length - 1; i >= 0; i--) {\n";
1180 code += " builder.add" + GenWriteMethod(vector_type) + "(";
1181 if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+"; }
1182 code += "data[i]);\n";
1183 code += " }\n";
1184 code += " return builder.endVector();\n";
1185 code += "};\n\n";
1186 }
1187
1188 // Generate a method to start a vector, data to be added manually
1189 // after
1190 GenDocComment(
1191 code_ptr,
1192 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1193 GenTypeAnnotation(kParam, "number", "numElems", false));
1194
1195 if (lang_.language == IDLOptions::kTs) {
1196 code += "static start" + MakeCamel(field.name);
1197 code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n";
1198 } else {
1199 code += object_name + ".start" + MakeCamel(field.name);
1200 code += "Vector = function(builder, numElems) {\n";
1201 }
1202
1203 code += " builder.startVector(" + NumToString(elem_size);
1204 code += ", numElems, " + NumToString(alignment) + ");\n";
1205 code += "};\n\n";
1206 }
1207 }
1208
1209 // Generate a method to stop building a new object
1210 GenDocComment(
1211 code_ptr,
1212 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1213 GenTypeAnnotation(kReturns, "flatbuffers.Offset", "", false));
1214
1215 if (lang_.language == IDLOptions::kTs) {
1216 code += "static end" + struct_def.name;
1217 code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n";
1218 } else {
1219 code += object_name + ".end" + struct_def.name;
1220 code += " = function(builder) {\n";
1221 }
1222
1223 code += " var offset = builder.endObject();\n";
1224 for (auto it = struct_def.fields.vec.begin();
1225 it != struct_def.fields.vec.end(); ++it) {
1226 auto &field = **it;
1227 if (!field.deprecated && field.required) {
1228 code += " builder.requiredField(offset, ";
1229 code += NumToString(field.value.offset);
1230 code += "); // " + field.name + "\n";
1231 }
1232 }
1233 code += " return offset;\n";
1234 code += "};\n\n";
1235
1236 // Generate the method to complete buffer construction
1237 if (parser_.root_struct_def_ == &struct_def) {
1238 GenDocComment(
1239 code_ptr,
1240 GenTypeAnnotation(kParam, "flatbuffers.Builder", "builder") +
1241 GenTypeAnnotation(kParam, "flatbuffers.Offset", "offset",
1242 false));
1243
1244 if (lang_.language == IDLOptions::kTs) {
1245 code += "static finish" + struct_def.name + "Buffer";
1246 code +=
1247 "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n";
1248 } else {
1249 code += object_name + ".finish" + struct_def.name + "Buffer";
1250 code += " = function(builder, offset) {\n";
1251 }
1252
1253 code += " builder.finish(offset";
1254 if (!parser_.file_identifier_.empty()) {
1255 code += ", '" + parser_.file_identifier_ + "'";
1256 }
1257 code += ");\n";
1258 code += "};\n\n";
1259 }
1260
1261 if (lang_.language == IDLOptions::kTs) {
1262 // Generate a convenient CreateX function
1263 code += "static create" + struct_def.name + "(builder:flatbuffers.Builder";
1264 for (auto it = struct_def.fields.vec.begin();
1265 it != struct_def.fields.vec.end(); ++it) {
1266 const auto &field = **it;
1267 if (field.deprecated)
1268 continue;
1269
1270 code += ", " + GetArgName(field) + ":" + GetArgType(field);
1271 }
1272
1273 code += "):flatbuffers.Offset {\n";
1274 code += " " + struct_def.name + ".start" + struct_def.name + "(builder);\n";
1275
1276 for (auto it = struct_def.fields.vec.begin();
1277 it != struct_def.fields.vec.end(); ++it) {
1278 const auto &field = **it;
1279 if (field.deprecated)
1280 continue;
1281
1282 code += " " + struct_def.name + ".add" + MakeCamel(field.name) +"(";
1283 code += "builder, " + GetArgName(field) + ");\n";
1284 }
1285
1286 code += " return " + struct_def.name + ".end" + struct_def.name + "(builder);\n";
1287 code += "}\n";
1288 }
1289 }
1290
1291 if (lang_.language == IDLOptions::kTs) {
1292 if (!object_namespace.empty()) { code += "}\n"; }
1293 code += "}\n";
1294 }
1295 }
1296
GetArgType(const FieldDef & field)1297 std::string GetArgType(const FieldDef &field) {
1298 if (field.value.type.enum_def)
1299 return GenPrefixedTypeName(GenTypeName(field.value.type, true),
1300 field.value.type.enum_def->file);
1301 return GenTypeName(field.value.type, true);
1302 }
1303
GetArgName(const FieldDef & field)1304 static std::string GetArgName(const FieldDef &field) {
1305 auto argname = MakeCamel(field.name, false);
1306 if (!IsScalar(field.value.type.base_type)) { argname += "Offset"; }
1307
1308 return argname;
1309 }
1310 };
1311 } // namespace jsts
1312
GenerateJSTS(const Parser & parser,const std::string & path,const std::string & file_name)1313 bool GenerateJSTS(const Parser &parser, const std::string &path,
1314 const std::string &file_name) {
1315 jsts::JsTsGenerator generator(parser, path, file_name);
1316 return generator.generate();
1317 }
1318
JSTSMakeRule(const Parser & parser,const std::string & path,const std::string & file_name)1319 std::string JSTSMakeRule(const Parser &parser, const std::string &path,
1320 const std::string &file_name) {
1321 FLATBUFFERS_ASSERT(parser.opts.lang <= IDLOptions::kMAX);
1322 const auto &lang = GetJsLangParams(parser.opts.lang);
1323
1324 std::string filebase =
1325 flatbuffers::StripPath(flatbuffers::StripExtension(file_name));
1326 std::string make_rule = GeneratedFileName(path, filebase, lang) + ": ";
1327
1328 auto included_files = parser.GetIncludedFilesRecursive(file_name);
1329 for (auto it = included_files.begin(); it != included_files.end(); ++it) {
1330 make_rule += " " + *it;
1331 }
1332 return make_rule;
1333 }
1334
1335 } // namespace flatbuffers
1336