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 | |
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 | |
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 | |
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. |
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 |
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 |
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. |
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, false); |
172 | GenEnum(enum_def, enum_code_ptr, exports_code_ptr, reexports, true); |
173 | } |
174 | } |
175 | |
176 | // Generate code for all structs. |
177 | void generateStructs(std::string *decl_code_ptr, |
178 | std::string *exports_code_ptr, |
179 | imported_fileset &imported_files) { |
180 | for (auto it = parser_.structs_.vec.begin(); |
181 | it != parser_.structs_.vec.end(); ++it) { |
182 | auto &struct_def = **it; |
183 | GenStruct(parser_, struct_def, decl_code_ptr, exports_code_ptr, |
184 | imported_files); |
185 | } |
186 | } |
187 | void GenNamespaces(std::string *code_ptr, std::string *exports_ptr) { |
188 | if (lang_.language == IDLOptions::kTs && |
189 | parser_.opts.skip_flatbuffers_import) { |
190 | return; |
191 | } |
192 | |
193 | std::set<std::string> namespaces; |
194 | |
195 | for (auto it = parser_.namespaces_.begin(); it != parser_.namespaces_.end(); |
196 | ++it) { |
197 | std::string namespace_so_far; |
198 | |
199 | // Gather all parent namespaces for this namespace |
200 | for (auto component = (*it)->components.begin(); |
201 | component != (*it)->components.end(); ++component) { |
202 | if (!namespace_so_far.empty()) { namespace_so_far += '.'; } |
203 | namespace_so_far += *component; |
204 | namespaces.insert(namespace_so_far); |
205 | } |
206 | } |
207 | |
208 | // Make sure parent namespaces come before child namespaces |
209 | std::vector<std::string> sorted_namespaces(namespaces.begin(), |
210 | namespaces.end()); |
211 | std::sort(sorted_namespaces.begin(), sorted_namespaces.end()); |
212 | |
213 | // Emit namespaces in a form that Closure Compiler can optimize |
214 | std::string &code = *code_ptr; |
215 | std::string &exports = *exports_ptr; |
216 | for (auto it = sorted_namespaces.begin(); it != sorted_namespaces.end(); |
217 | it++) { |
218 | if (lang_.language == IDLOptions::kTs) { |
219 | if (it->find('.') == std::string::npos) { |
220 | code += "import { flatbuffers } from \"./flatbuffers\"\n" ; |
221 | break; |
222 | } |
223 | } else { |
224 | code += "/**\n * @const\n * @namespace\n */\n" ; |
225 | if (it->find('.') == std::string::npos) { |
226 | code += "var " ; |
227 | if (parser_.opts.use_goog_js_export_format) { |
228 | exports += "goog.exportSymbol('" + *it + "', " + *it + ");\n" ; |
229 | } else if (parser_.opts.use_ES6_js_export_format) { |
230 | exports += "export {" + *it + "};\n" ; |
231 | } else { |
232 | exports += "this." + *it + " = " + *it + ";\n" ; |
233 | } |
234 | } |
235 | code += *it + " = " + *it + " || {};\n\n" ; |
236 | } |
237 | } |
238 | } |
239 | |
240 | // Generate a documentation comment, if available. |
241 | static void (const std::vector<std::string> &dc, |
242 | std::string *code_ptr, |
243 | const std::string &, |
244 | const char *indent = nullptr) { |
245 | if (dc.empty() && extra_lines.empty()) { |
246 | // Don't output empty comment blocks with 0 lines of comment content. |
247 | return; |
248 | } |
249 | |
250 | std::string &code = *code_ptr; |
251 | if (indent) code += indent; |
252 | code += "/**\n" ; |
253 | for (auto it = dc.begin(); it != dc.end(); ++it) { |
254 | if (indent) code += indent; |
255 | code += " *" + *it + "\n" ; |
256 | } |
257 | if (!extra_lines.empty()) { |
258 | if (!dc.empty()) { |
259 | if (indent) code += indent; |
260 | code += " *\n" ; |
261 | } |
262 | if (indent) code += indent; |
263 | std::string::size_type start = 0; |
264 | for (;;) { |
265 | auto end = extra_lines.find('\n', start); |
266 | if (end != std::string::npos) { |
267 | code += " * " + extra_lines.substr(start, end - start) + "\n" ; |
268 | start = end + 1; |
269 | } else { |
270 | code += " * " + extra_lines.substr(start) + "\n" ; |
271 | break; |
272 | } |
273 | } |
274 | } |
275 | if (indent) code += indent; |
276 | code += " */\n" ; |
277 | } |
278 | |
279 | static void (std::string *code_ptr, |
280 | const std::string &) { |
281 | GenDocComment(std::vector<std::string>(), code_ptr, extra_lines); |
282 | } |
283 | |
284 | std::string GenTypeAnnotation(AnnotationType annotation_type, |
285 | const std::string &type_name, |
286 | const std::string &arg_name, |
287 | bool include_newline = true) { |
288 | std::string result = "" ; |
289 | switch (annotation_type) { |
290 | case kParam: { |
291 | result += "@param" ; |
292 | break; |
293 | } |
294 | case kType: { |
295 | if (lang_.language != IDLOptions::kTs) { |
296 | result += "@type" ; |
297 | } else { |
298 | return "" ; |
299 | } |
300 | break; |
301 | } |
302 | case kReturns: { |
303 | result += "@returns" ; |
304 | break; |
305 | } |
306 | } |
307 | switch (lang_.language) { |
308 | case IDLOptions::kTs: { |
309 | result += " " + type_name; |
310 | break; |
311 | } |
312 | default: { result += " {" + type_name + "}" ; } |
313 | } |
314 | if (!arg_name.empty()) { |
315 | result += " " + arg_name; |
316 | } |
317 | if (include_newline) { |
318 | result += "\n" ; |
319 | } |
320 | |
321 | return result; |
322 | } |
323 | |
324 | // Generate an enum declaration and an enum string lookup table. |
325 | void GenEnum(EnumDef &enum_def, std::string *code_ptr, |
326 | std::string *exports_ptr, reexport_map &reexports, |
327 | bool reverse) { |
328 | if (enum_def.generated) return; |
329 | if (reverse && lang_.language == IDLOptions::kTs) return; // FIXME. |
330 | std::string &code = *code_ptr; |
331 | std::string &exports = *exports_ptr; |
332 | GenDocComment(enum_def.doc_comment, code_ptr, |
333 | reverse ? "@enum {string}" : "@enum {number}" ); |
334 | std::string ns = GetNameSpace(enum_def); |
335 | std::string enum_def_name = enum_def.name + (reverse ? "Name" : "" ); |
336 | if (lang_.language == IDLOptions::kTs) { |
337 | if (!ns.empty()) { code += "export namespace " + ns + "{\n" ; } |
338 | code += "export enum " + enum_def.name + "{\n" ; |
339 | } else { |
340 | if (enum_def.defined_namespace->components.empty()) { |
341 | code += "var " ; |
342 | if (parser_.opts.use_goog_js_export_format) { |
343 | exports += "goog.exportSymbol('" + enum_def_name + "', " + |
344 | enum_def.name + ");\n" ; |
345 | } else if (parser_.opts.use_ES6_js_export_format) { |
346 | exports += "export {" + enum_def_name + "};\n" ; |
347 | } else { |
348 | exports += "this." + enum_def_name + " = " + enum_def_name + ";\n" ; |
349 | } |
350 | } |
351 | code += WrapInNameSpace(enum_def) + (reverse ? "Name" : "" ) + " = {\n" ; |
352 | } |
353 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { |
354 | auto &ev = **it; |
355 | if (!ev.doc_comment.empty()) { |
356 | if (it != enum_def.Vals().begin()) { code += '\n'; } |
357 | GenDocComment(ev.doc_comment, code_ptr, "" , " " ); |
358 | } |
359 | |
360 | // Generate mapping between EnumName: EnumValue(int) |
361 | if (reverse) { |
362 | code += " " + NumToString(ev.value); |
363 | code += lang_.language == IDLOptions::kTs ? "= " : ": " ; |
364 | code += "'" + ev.name + "'" ; |
365 | } else { |
366 | code += " " + ev.name; |
367 | code += lang_.language == IDLOptions::kTs ? "= " : ": " ; |
368 | code += NumToString(ev.value); |
369 | } |
370 | |
371 | code += (it + 1) != enum_def.Vals().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 | |
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 | |
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 | |
428 | std::string GenBBAccess() { |
429 | return lang_.language == IDLOptions::kTs ? "this.bb!" : "this.bb" ; |
430 | } |
431 | |
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 | |
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. |
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 | |
512 | template<typename T> static std::string MaybeAdd(T value) { |
513 | return value != 0 ? " + " + NumToString(value) : "" ; |
514 | } |
515 | |
516 | template<typename T> static std::string MaybeScale(T value) { |
517 | return value != 1 ? " * " + NumToString(value) : "" ; |
518 | } |
519 | |
520 | static std::string GenFileNamespacePrefix(const std::string &file) { |
521 | return "NS" + std::to_string(HashFnv1a<uint64_t>(file.c_str())); |
522 | } |
523 | |
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. |
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 | |
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 | |
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. |
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 | |
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 | |
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 | |
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 | |
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 | |
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 | |