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 <algorithm> |
19 | #include <cassert> |
20 | #include <unordered_map> |
21 | #include <unordered_set> |
22 | |
23 | #include "flatbuffers/code_generators.h" |
24 | #include "flatbuffers/flatbuffers.h" |
25 | #include "flatbuffers/idl.h" |
26 | #include "flatbuffers/util.h" |
27 | |
28 | namespace flatbuffers { |
29 | |
30 | struct ImportDefinition { |
31 | std::string name; |
32 | std::string import_statement; |
33 | std::string export_statement; |
34 | std::string bare_file_path; |
35 | std::string rel_file_path; |
36 | const Definition *dependent; |
37 | const Definition *dependency; |
38 | }; |
39 | |
40 | enum AnnotationType { kParam = 0, kType = 1, kReturns = 2 }; |
41 | |
42 | namespace ts { |
43 | // Iterate through all definitions we haven't generate code for (enums, structs, |
44 | // and tables) and output them to a single file. |
45 | class TsGenerator : public BaseGenerator { |
46 | public: |
47 | typedef std::map<std::string, ImportDefinition> import_set; |
48 | |
49 | TsGenerator(const Parser &parser, const std::string &path, |
50 | const std::string &file_name) |
51 | : BaseGenerator(parser, path, file_name, "" , "." , "ts" ) { |
52 | // clang-format off |
53 | |
54 | // List of keywords retrieved from here: |
55 | // https://github.com/microsoft/TypeScript/issues/2536 |
56 | // One per line to ease comparisons to that list are easier |
57 | static const char *const keywords[] = { |
58 | "break" , |
59 | "case" , |
60 | "catch" , |
61 | "class" , |
62 | "const" , |
63 | "continue" , |
64 | "debugger" , |
65 | "default" , |
66 | "delete" , |
67 | "do" , |
68 | "else" , |
69 | "enum" , |
70 | "export" , |
71 | "extends" , |
72 | "false" , |
73 | "finally" , |
74 | "for" , |
75 | "function" , |
76 | "if" , |
77 | "import" , |
78 | "in" , |
79 | "instanceof" , |
80 | "new" , |
81 | "null" , |
82 | "return" , |
83 | "super" , |
84 | "switch" , |
85 | "this" , |
86 | "throw" , |
87 | "true" , |
88 | "try" , |
89 | "typeof" , |
90 | "var" , |
91 | "void" , |
92 | "while" , |
93 | "with" , |
94 | "as" , |
95 | "implements" , |
96 | "interface" , |
97 | "let" , |
98 | "package" , |
99 | "private" , |
100 | "protected" , |
101 | "public" , |
102 | "static" , |
103 | "yield" , |
104 | nullptr, |
105 | // clang-format on |
106 | }; |
107 | |
108 | for (auto kw = keywords; *kw; kw++) keywords_.insert(*kw); |
109 | } |
110 | bool generate() { |
111 | generateEnums(); |
112 | generateStructs(); |
113 | generateEntry(); |
114 | return true; |
115 | } |
116 | |
117 | // Save out the generated code for a single class while adding |
118 | // declaration boilerplate. |
119 | bool SaveType(const Definition &definition, const std::string &classcode, |
120 | import_set &imports, import_set &bare_imports) const { |
121 | if (!classcode.length()) return true; |
122 | |
123 | std::string code = |
124 | "// " + std::string(FlatBuffersGeneratedWarning()) + "\n\n" ; |
125 | |
126 | for (auto it = bare_imports.begin(); it != bare_imports.end(); it++) |
127 | code += it->second.import_statement + "\n" ; |
128 | if (!bare_imports.empty()) code += "\n" ; |
129 | |
130 | for (auto it = imports.begin(); it != imports.end(); it++) |
131 | if (it->second.dependency != &definition) // do not import itself |
132 | code += it->second.import_statement + "\n" ; |
133 | if (!imports.empty()) code += "\n\n" ; |
134 | |
135 | code += classcode; |
136 | auto filename = NamespaceDir(*definition.defined_namespace, true) + |
137 | ToDasherizedCase(definition.name) + ".ts" ; |
138 | return SaveFile(filename.c_str(), code, false); |
139 | } |
140 | |
141 | private: |
142 | std::unordered_set<std::string> keywords_; |
143 | |
144 | std::string EscapeKeyword(const std::string &name) const { |
145 | return keywords_.find(name) == keywords_.end() ? name : name + "_" ; |
146 | } |
147 | |
148 | import_set imports_all_; |
149 | |
150 | // Generate code for all enums. |
151 | void generateEnums() { |
152 | for (auto it = parser_.enums_.vec.begin(); it != parser_.enums_.vec.end(); |
153 | ++it) { |
154 | import_set bare_imports; |
155 | import_set imports; |
156 | std::string enumcode; |
157 | auto &enum_def = **it; |
158 | GenEnum(enum_def, &enumcode, imports, false); |
159 | GenEnum(enum_def, &enumcode, imports, true); |
160 | SaveType(enum_def, enumcode, imports, bare_imports); |
161 | imports_all_.insert(imports.begin(), imports.end()); |
162 | } |
163 | } |
164 | |
165 | // Generate code for all structs. |
166 | void generateStructs() { |
167 | for (auto it = parser_.structs_.vec.begin(); |
168 | it != parser_.structs_.vec.end(); ++it) { |
169 | import_set bare_imports; |
170 | import_set imports; |
171 | AddImport(bare_imports, "* as flatbuffers" , "flatbuffers" ); |
172 | auto &struct_def = **it; |
173 | std::string declcode; |
174 | GenStruct(parser_, struct_def, &declcode, imports); |
175 | SaveType(struct_def, declcode, imports, bare_imports); |
176 | imports_all_.insert(imports.begin(), imports.end()); |
177 | } |
178 | } |
179 | |
180 | // Generate code for a single entry point module. |
181 | void generateEntry() { |
182 | std::string code; |
183 | for (auto it = imports_all_.begin(); it != imports_all_.end(); it++) |
184 | code += it->second.export_statement + "\n" ; |
185 | std::string path = "./" + path_ + file_name_ + ".ts" ; |
186 | SaveFile(path.c_str(), code, false); |
187 | } |
188 | |
189 | // Generate a documentation comment, if available. |
190 | static void (const std::vector<std::string> &dc, |
191 | std::string *code_ptr, |
192 | const char *indent = nullptr) { |
193 | if (dc.empty()) { |
194 | // Don't output empty comment blocks with 0 lines of comment content. |
195 | return; |
196 | } |
197 | |
198 | std::string &code = *code_ptr; |
199 | if (indent) code += indent; |
200 | code += "/**\n" ; |
201 | for (auto it = dc.begin(); it != dc.end(); ++it) { |
202 | if (indent) code += indent; |
203 | code += " *" + *it + "\n" ; |
204 | } |
205 | if (indent) code += indent; |
206 | code += " */\n" ; |
207 | } |
208 | |
209 | static void (std::string *code_ptr) { |
210 | GenDocComment(std::vector<std::string>(), code_ptr); |
211 | } |
212 | |
213 | // Generate an enum declaration and an enum string lookup table. |
214 | void GenEnum(EnumDef &enum_def, std::string *code_ptr, import_set &imports, |
215 | bool reverse) { |
216 | if (enum_def.generated) return; |
217 | if (reverse) return; // FIXME. |
218 | std::string &code = *code_ptr; |
219 | GenDocComment(enum_def.doc_comment, code_ptr); |
220 | std::string ns = GetNameSpace(enum_def); |
221 | std::string enum_def_name = enum_def.name + (reverse ? "Name" : "" ); |
222 | code += "export enum " + enum_def.name + "{\n" ; |
223 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); ++it) { |
224 | auto &ev = **it; |
225 | if (!ev.doc_comment.empty()) { |
226 | if (it != enum_def.Vals().begin()) { code += '\n'; } |
227 | GenDocComment(ev.doc_comment, code_ptr, " " ); |
228 | } |
229 | |
230 | // Generate mapping between EnumName: EnumValue(int) |
231 | if (reverse) { |
232 | code += " '" + enum_def.ToString(ev) + "'" ; |
233 | code += " = " ; |
234 | code += "'" + ev.name + "'" ; |
235 | } else { |
236 | code += " " + ev.name; |
237 | code += " = " ; |
238 | code += enum_def.ToString(ev); |
239 | } |
240 | |
241 | code += (it + 1) != enum_def.Vals().end() ? ",\n" : "\n" ; |
242 | } |
243 | code += "}" ; |
244 | |
245 | if (enum_def.is_union) { |
246 | code += GenUnionConvFunc(enum_def.underlying_type, imports); |
247 | } |
248 | |
249 | code += "\n\n" ; |
250 | } |
251 | |
252 | static std::string GenType(const Type &type) { |
253 | switch (type.base_type) { |
254 | case BASE_TYPE_BOOL: |
255 | case BASE_TYPE_CHAR: return "Int8" ; |
256 | case BASE_TYPE_UTYPE: |
257 | case BASE_TYPE_UCHAR: return "Uint8" ; |
258 | case BASE_TYPE_SHORT: return "Int16" ; |
259 | case BASE_TYPE_USHORT: return "Uint16" ; |
260 | case BASE_TYPE_INT: return "Int32" ; |
261 | case BASE_TYPE_UINT: return "Uint32" ; |
262 | case BASE_TYPE_LONG: return "Int64" ; |
263 | case BASE_TYPE_ULONG: return "Uint64" ; |
264 | case BASE_TYPE_FLOAT: return "Float32" ; |
265 | case BASE_TYPE_DOUBLE: return "Float64" ; |
266 | case BASE_TYPE_STRING: return "String" ; |
267 | case BASE_TYPE_VECTOR: return GenType(type.VectorType()); |
268 | case BASE_TYPE_STRUCT: return type.struct_def->name; |
269 | default: return "flatbuffers.Table" ; |
270 | } |
271 | } |
272 | |
273 | std::string GenGetter(const Type &type, const std::string &arguments) { |
274 | switch (type.base_type) { |
275 | case BASE_TYPE_STRING: return GenBBAccess() + ".__string" + arguments; |
276 | case BASE_TYPE_STRUCT: return GenBBAccess() + ".__struct" + arguments; |
277 | case BASE_TYPE_UNION: |
278 | if (!UnionHasStringType(*type.enum_def)) { |
279 | return GenBBAccess() + ".__union" + arguments; |
280 | } |
281 | return GenBBAccess() + ".__union_with_string" + arguments; |
282 | case BASE_TYPE_VECTOR: return GenGetter(type.VectorType(), arguments); |
283 | default: { |
284 | auto getter = |
285 | GenBBAccess() + ".read" + MakeCamel(GenType(type)) + arguments; |
286 | if (type.base_type == BASE_TYPE_BOOL) { getter = "!!" + getter; } |
287 | return getter; |
288 | } |
289 | } |
290 | } |
291 | |
292 | std::string GenBBAccess() const { return "this.bb!" ; } |
293 | |
294 | std::string GenDefaultValue(const FieldDef &field, import_set &imports) { |
295 | if (field.IsScalarOptional()) { return "null" ; } |
296 | |
297 | const auto &value = field.value; |
298 | if (value.type.enum_def && value.type.base_type != BASE_TYPE_UNION && |
299 | value.type.base_type != BASE_TYPE_VECTOR) { |
300 | if (auto val = value.type.enum_def->FindByValue(value.constant)) { |
301 | return AddImport(imports, *value.type.enum_def, *value.type.enum_def) + |
302 | "." + val->name; |
303 | } else { |
304 | return value.constant; |
305 | } |
306 | } |
307 | |
308 | switch (value.type.base_type) { |
309 | case BASE_TYPE_BOOL: return value.constant == "0" ? "false" : "true" ; |
310 | |
311 | case BASE_TYPE_STRING: |
312 | case BASE_TYPE_UNION: |
313 | case BASE_TYPE_STRUCT: { |
314 | return "null" ; |
315 | } |
316 | |
317 | case BASE_TYPE_VECTOR: return "[]" ; |
318 | |
319 | case BASE_TYPE_LONG: |
320 | case BASE_TYPE_ULONG: { |
321 | return "BigInt('" + value.constant + "')" ; |
322 | } |
323 | |
324 | default: return value.constant; |
325 | } |
326 | } |
327 | |
328 | std::string GenTypeName(import_set &imports, const Definition &owner, |
329 | const Type &type, bool input, |
330 | bool allowNull = false) { |
331 | if (!input) { |
332 | if (IsString(type) || type.base_type == BASE_TYPE_STRUCT) { |
333 | std::string name; |
334 | if (IsString(type)) { |
335 | name = "string|Uint8Array" ; |
336 | } else { |
337 | name = AddImport(imports, owner, *type.struct_def); |
338 | } |
339 | return allowNull ? (name + "|null" ) : name; |
340 | } |
341 | } |
342 | |
343 | switch (type.base_type) { |
344 | case BASE_TYPE_BOOL: return allowNull ? "boolean|null" : "boolean" ; |
345 | case BASE_TYPE_LONG: |
346 | case BASE_TYPE_ULONG: return allowNull ? "bigint|null" : "bigint" ; |
347 | default: |
348 | if (IsScalar(type.base_type)) { |
349 | if (type.enum_def) { |
350 | const auto enum_name = AddImport(imports, owner, *type.enum_def); |
351 | return allowNull ? (enum_name + "|null" ) : enum_name; |
352 | } |
353 | return allowNull ? "number|null" : "number" ; |
354 | } |
355 | return "flatbuffers.Offset" ; |
356 | } |
357 | } |
358 | |
359 | // Returns the method name for use with add/put calls. |
360 | static std::string GenWriteMethod(const Type &type) { |
361 | // Forward to signed versions since unsigned versions don't exist |
362 | switch (type.base_type) { |
363 | case BASE_TYPE_UTYPE: |
364 | case BASE_TYPE_UCHAR: return GenWriteMethod(Type(BASE_TYPE_CHAR)); |
365 | case BASE_TYPE_USHORT: return GenWriteMethod(Type(BASE_TYPE_SHORT)); |
366 | case BASE_TYPE_UINT: return GenWriteMethod(Type(BASE_TYPE_INT)); |
367 | case BASE_TYPE_ULONG: return GenWriteMethod(Type(BASE_TYPE_LONG)); |
368 | default: break; |
369 | } |
370 | |
371 | return IsScalar(type.base_type) ? MakeCamel(GenType(type)) |
372 | : (IsStruct(type) ? "Struct" : "Offset" ); |
373 | } |
374 | |
375 | template<typename T> static std::string MaybeAdd(T value) { |
376 | return value != 0 ? " + " + NumToString(value) : "" ; |
377 | } |
378 | |
379 | template<typename T> static std::string MaybeScale(T value) { |
380 | return value != 1 ? " * " + NumToString(value) : "" ; |
381 | } |
382 | |
383 | void GenStructArgs(import_set &imports, const StructDef &struct_def, |
384 | std::string *arguments, const std::string &nameprefix) { |
385 | for (auto it = struct_def.fields.vec.begin(); |
386 | it != struct_def.fields.vec.end(); ++it) { |
387 | auto &field = **it; |
388 | if (IsStruct(field.value.type)) { |
389 | // Generate arguments for a struct inside a struct. To ensure names |
390 | // don't clash, and to make it obvious these arguments are constructing |
391 | // a nested struct, prefix the name with the field name. |
392 | GenStructArgs(imports, *field.value.type.struct_def, arguments, |
393 | nameprefix + field.name + "_" ); |
394 | } else { |
395 | *arguments += ", " + nameprefix + field.name + ": " + |
396 | GenTypeName(imports, field, field.value.type, true, |
397 | field.IsOptional()); |
398 | } |
399 | } |
400 | } |
401 | |
402 | static void GenStructBody(const StructDef &struct_def, std::string *body, |
403 | const std::string &nameprefix) { |
404 | *body += " builder.prep(" ; |
405 | *body += NumToString(struct_def.minalign) + ", " ; |
406 | *body += NumToString(struct_def.bytesize) + ");\n" ; |
407 | |
408 | for (auto it = struct_def.fields.vec.rbegin(); |
409 | it != struct_def.fields.vec.rend(); ++it) { |
410 | auto &field = **it; |
411 | if (field.padding) { |
412 | *body += " builder.pad(" + NumToString(field.padding) + ");\n" ; |
413 | } |
414 | if (IsStruct(field.value.type)) { |
415 | // Generate arguments for a struct inside a struct. To ensure names |
416 | // don't clash, and to make it obvious these arguments are constructing |
417 | // a nested struct, prefix the name with the field name. |
418 | GenStructBody(*field.value.type.struct_def, body, |
419 | nameprefix + field.name + "_" ); |
420 | } else { |
421 | *body += " builder.write" + GenWriteMethod(field.value.type) + "(" ; |
422 | if (field.value.type.base_type == BASE_TYPE_BOOL) { *body += "+" ; } |
423 | *body += nameprefix + field.name + ");\n" ; |
424 | } |
425 | } |
426 | } |
427 | |
428 | std::string GenerateNewExpression(const std::string &object_name) { |
429 | return "new " + object_name + "()" ; |
430 | } |
431 | |
432 | void GenerateRootAccessor(StructDef &struct_def, std::string *code_ptr, |
433 | std::string &code, const std::string &object_name, |
434 | bool size_prefixed) { |
435 | if (!struct_def.fixed) { |
436 | GenDocComment(code_ptr); |
437 | std::string sizePrefixed("SizePrefixed" ); |
438 | code += "static get" + (size_prefixed ? sizePrefixed : "" ) + "Root" + |
439 | GetPrefixedName(struct_def, "As" ); |
440 | code += "(bb:flatbuffers.ByteBuffer, obj?:" + object_name + |
441 | "):" + object_name + " {\n" ; |
442 | if (size_prefixed) { |
443 | code += |
444 | " bb.setPosition(bb.position() + " |
445 | "flatbuffers.SIZE_PREFIX_LENGTH);\n" ; |
446 | } |
447 | code += " return (obj || " + GenerateNewExpression(object_name); |
448 | code += ").__init(bb.readInt32(bb.position()) + bb.position(), bb);\n" ; |
449 | code += "}\n\n" ; |
450 | } |
451 | } |
452 | |
453 | void GenerateFinisher(StructDef &struct_def, std::string *code_ptr, |
454 | std::string &code, bool size_prefixed) { |
455 | if (parser_.root_struct_def_ == &struct_def) { |
456 | std::string sizePrefixed("SizePrefixed" ); |
457 | GenDocComment(code_ptr); |
458 | |
459 | code += "static finish" + (size_prefixed ? sizePrefixed : "" ) + |
460 | GetPrefixedName(struct_def) + "Buffer" ; |
461 | code += "(builder:flatbuffers.Builder, offset:flatbuffers.Offset) {\n" ; |
462 | code += " builder.finish(offset" ; |
463 | if (!parser_.file_identifier_.empty()) { |
464 | code += ", '" + parser_.file_identifier_ + "'" ; |
465 | } |
466 | if (size_prefixed) { |
467 | if (parser_.file_identifier_.empty()) { code += ", undefined" ; } |
468 | code += ", true" ; |
469 | } |
470 | code += ");\n" ; |
471 | code += "}\n\n" ; |
472 | } |
473 | } |
474 | |
475 | static std::string GetObjApiClassName(const StructDef &sd, |
476 | const IDLOptions &opts) { |
477 | return GetObjApiClassName(sd.name, opts); |
478 | } |
479 | |
480 | static std::string GetObjApiClassName(const std::string &name, |
481 | const IDLOptions &opts) { |
482 | return opts.object_prefix + name + opts.object_suffix; |
483 | } |
484 | |
485 | bool UnionHasStringType(const EnumDef &union_enum) { |
486 | return std::any_of(union_enum.Vals().begin(), union_enum.Vals().end(), |
487 | [](const EnumVal *ev) { |
488 | return !ev->IsZero() && IsString(ev->union_type); |
489 | }); |
490 | } |
491 | |
492 | std::string GenUnionGenericTypeTS(const EnumDef &union_enum) { |
493 | // TODO: make it work without any |
494 | // return std::string("T") + (UnionHasStringType(union_enum) ? "|string" : |
495 | // ""); |
496 | return std::string("any" ) + |
497 | (UnionHasStringType(union_enum) ? "|string" : "" ); |
498 | } |
499 | |
500 | std::string GenUnionTypeTS(const EnumDef &union_enum, import_set &imports) { |
501 | std::string ret; |
502 | std::set<std::string> type_list; |
503 | |
504 | for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end(); |
505 | ++it) { |
506 | const auto &ev = **it; |
507 | if (ev.IsZero()) { continue; } |
508 | |
509 | std::string type = "" ; |
510 | if (IsString(ev.union_type)) { |
511 | type = "string" ; // no need to wrap string type in namespace |
512 | } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { |
513 | type = AddImport(imports, union_enum, *ev.union_type.struct_def); |
514 | } else { |
515 | FLATBUFFERS_ASSERT(false); |
516 | } |
517 | type_list.insert(type); |
518 | } |
519 | |
520 | for (auto it = type_list.begin(); it != type_list.end(); ++it) { |
521 | ret += *it + ((std::next(it) == type_list.end()) ? "" : "|" ); |
522 | } |
523 | |
524 | return ret; |
525 | } |
526 | |
527 | std::string AddImport(import_set &imports, const Definition &dependent, |
528 | const StructDef &dependency) { |
529 | std::string ns; |
530 | const auto &depc_comps = dependency.defined_namespace->components; |
531 | for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) ns += *it; |
532 | std::string unique_name = ns + dependency.name; |
533 | std::string import_name = dependency.name; |
534 | std::string long_import_name; |
535 | if (imports.find(unique_name) != imports.end()) |
536 | return imports.find(unique_name)->second.name; |
537 | for (auto it = imports.begin(); it != imports.end(); it++) { |
538 | if (it->second.name == import_name) { |
539 | long_import_name = ns + import_name; |
540 | break; |
541 | } |
542 | } |
543 | std::string import_statement; |
544 | std::string export_statement; |
545 | import_statement += "import { " ; |
546 | export_statement += "export { " ; |
547 | std::string symbols_expression; |
548 | if (long_import_name.empty()) { |
549 | symbols_expression += import_name; |
550 | if (parser_.opts.generate_object_based_api) |
551 | symbols_expression += ", " + import_name + "T" ; |
552 | } else { |
553 | symbols_expression += dependency.name + " as " + long_import_name; |
554 | if (parser_.opts.generate_object_based_api) |
555 | symbols_expression += |
556 | ", " + dependency.name + "T as " + long_import_name + "T" ; |
557 | } |
558 | import_statement += symbols_expression + " } from '" ; |
559 | export_statement += symbols_expression + " } from '" ; |
560 | std::string bare_file_path; |
561 | std::string rel_file_path; |
562 | const auto &dep_comps = dependent.defined_namespace->components; |
563 | for (size_t i = 0; i < dep_comps.size(); i++) |
564 | rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".." )); |
565 | if (dep_comps.size() == 0) rel_file_path += "." ; |
566 | for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) |
567 | bare_file_path += kPathSeparator + ToDasherizedCase(*it); |
568 | bare_file_path += kPathSeparator + ToDasherizedCase(dependency.name); |
569 | rel_file_path += bare_file_path; |
570 | import_statement += rel_file_path + "';" ; |
571 | export_statement += "." + bare_file_path + "';" ; |
572 | ImportDefinition import; |
573 | import.name = long_import_name.empty() ? import_name : long_import_name; |
574 | import.bare_file_path = bare_file_path; |
575 | import.rel_file_path = rel_file_path; |
576 | import.import_statement = import_statement; |
577 | import.export_statement = export_statement; |
578 | import.dependency = &dependency; |
579 | import.dependent = &dependent; |
580 | imports.insert(std::make_pair(unique_name, import)); |
581 | return import.name; |
582 | } |
583 | |
584 | // TODO: largely (but not identical) duplicated code from above couln't find a |
585 | // good way to refactor |
586 | std::string AddImport(import_set &imports, const Definition &dependent, |
587 | const EnumDef &dependency) { |
588 | std::string ns; |
589 | const auto &depc_comps = dependency.defined_namespace->components; |
590 | for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) ns += *it; |
591 | std::string unique_name = ns + dependency.name; |
592 | std::string import_name = dependency.name; |
593 | std::string long_import_name; |
594 | if (imports.find(unique_name) != imports.end()) |
595 | return imports.find(unique_name)->second.name; |
596 | for (auto it = imports.begin(); it != imports.end(); it++) { |
597 | if (it->second.name == import_name) { |
598 | long_import_name = ns + import_name; |
599 | break; |
600 | } |
601 | } |
602 | std::string import_statement; |
603 | std::string export_statement; |
604 | import_statement += "import { " ; |
605 | export_statement += "export { " ; |
606 | std::string symbols_expression; |
607 | if (long_import_name.empty()) |
608 | symbols_expression += import_name; |
609 | else |
610 | symbols_expression += dependency.name + " as " + long_import_name; |
611 | if (dependency.is_union) { |
612 | symbols_expression += ", unionTo" + import_name; |
613 | symbols_expression += ", unionListTo" + import_name; |
614 | } |
615 | import_statement += symbols_expression + " } from '" ; |
616 | export_statement += symbols_expression + " } from '" ; |
617 | std::string bare_file_path; |
618 | std::string rel_file_path; |
619 | const auto &dep_comps = dependent.defined_namespace->components; |
620 | for (size_t i = 0; i < dep_comps.size(); i++) |
621 | rel_file_path += i == 0 ? ".." : (kPathSeparator + std::string(".." )); |
622 | if (dep_comps.size() == 0) rel_file_path += "." ; |
623 | for (auto it = depc_comps.begin(); it != depc_comps.end(); it++) |
624 | bare_file_path += kPathSeparator + ToDasherizedCase(*it); |
625 | bare_file_path += kPathSeparator + ToDasherizedCase(dependency.name); |
626 | rel_file_path += bare_file_path; |
627 | import_statement += rel_file_path + "';" ; |
628 | export_statement += "." + bare_file_path + "';" ; |
629 | ImportDefinition import; |
630 | import.name = long_import_name.empty() ? import_name : long_import_name; |
631 | import.bare_file_path = bare_file_path; |
632 | import.rel_file_path = rel_file_path; |
633 | import.import_statement = import_statement; |
634 | import.export_statement = export_statement; |
635 | import.dependency = &dependency; |
636 | import.dependent = &dependent; |
637 | imports.insert(std::make_pair(unique_name, import)); |
638 | return import.name; |
639 | } |
640 | |
641 | void AddImport(import_set &imports, std::string import_name, |
642 | std::string fileName) { |
643 | ImportDefinition import; |
644 | import.name = import_name; |
645 | import.import_statement = |
646 | "import " + import_name + " from '" + fileName + "';" ; |
647 | imports.insert(std::make_pair(import_name, import)); |
648 | } |
649 | |
650 | // Generate a TS union type based on a union's enum |
651 | std::string GenObjApiUnionTypeTS(import_set &imports, const IDLOptions &opts, |
652 | const EnumDef &union_enum) { |
653 | std::string ret = "" ; |
654 | std::set<std::string> type_list; |
655 | |
656 | for (auto it = union_enum.Vals().begin(); it != union_enum.Vals().end(); |
657 | ++it) { |
658 | const auto &ev = **it; |
659 | if (ev.IsZero()) { continue; } |
660 | |
661 | std::string type = "" ; |
662 | if (IsString(ev.union_type)) { |
663 | type = "string" ; // no need to wrap string type in namespace |
664 | } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { |
665 | type = GetObjApiClassName( |
666 | AddImport(imports, union_enum, *ev.union_type.struct_def), opts); |
667 | } else { |
668 | FLATBUFFERS_ASSERT(false); |
669 | } |
670 | type_list.insert(type); |
671 | } |
672 | |
673 | size_t totalPrinted = 0; |
674 | for (auto it = type_list.begin(); it != type_list.end(); ++it) { |
675 | ++totalPrinted; |
676 | ret += *it + ((totalPrinted == type_list.size()) ? "" : "|" ); |
677 | } |
678 | |
679 | return ret; |
680 | } |
681 | |
682 | std::string GenUnionConvFuncName(const EnumDef &enum_def) { |
683 | return "unionTo" + enum_def.name; |
684 | } |
685 | |
686 | std::string GenUnionListConvFuncName(const EnumDef &enum_def) { |
687 | return "unionListTo" + enum_def.name; |
688 | } |
689 | |
690 | std::string GenUnionConvFunc(const Type &union_type, import_set &imports) { |
691 | if (union_type.enum_def) { |
692 | const auto &enum_def = *union_type.enum_def; |
693 | |
694 | const auto valid_union_type = GenUnionTypeTS(enum_def, imports); |
695 | const auto valid_union_type_with_null = valid_union_type + "|null" ; |
696 | |
697 | auto ret = "\n\nexport function " + GenUnionConvFuncName(enum_def) + |
698 | "(\n type: " + enum_def.name + |
699 | ",\n accessor: (obj:" + valid_union_type + ") => " + |
700 | valid_union_type_with_null + |
701 | "\n): " + valid_union_type_with_null + " {\n" ; |
702 | |
703 | const auto enum_type = AddImport(imports, enum_def, enum_def); |
704 | |
705 | const auto union_enum_loop = [&](const std::string &accessor_str) { |
706 | ret += " switch(" + enum_type + "[type]) {\n" ; |
707 | ret += " case 'NONE': return null; \n" ; |
708 | |
709 | for (auto it = enum_def.Vals().begin(); it != enum_def.Vals().end(); |
710 | ++it) { |
711 | const auto &ev = **it; |
712 | if (ev.IsZero()) { continue; } |
713 | |
714 | ret += " case '" + ev.name + "': " ; |
715 | |
716 | if (IsString(ev.union_type)) { |
717 | ret += "return " + accessor_str + "'') as string;" ; |
718 | } else if (ev.union_type.base_type == BASE_TYPE_STRUCT) { |
719 | const auto type = |
720 | AddImport(imports, enum_def, *ev.union_type.struct_def); |
721 | ret += "return " + accessor_str + "new " + type + "())! as " + |
722 | type + ";" ; |
723 | } else { |
724 | FLATBUFFERS_ASSERT(false); |
725 | } |
726 | ret += "\n" ; |
727 | } |
728 | |
729 | ret += " default: return null;\n" ; |
730 | ret += " }\n" ; |
731 | }; |
732 | |
733 | union_enum_loop("accessor(" ); |
734 | ret += "}" ; |
735 | |
736 | ret += "\n\nexport function " + GenUnionListConvFuncName(enum_def) + |
737 | "(\n type: " + enum_def.name + |
738 | ", \n accessor: (index: number, obj:" + valid_union_type + |
739 | ") => " + valid_union_type_with_null + |
740 | ", \n index: number\n): " + valid_union_type_with_null + " {\n" ; |
741 | union_enum_loop("accessor(index, " ); |
742 | ret += "}" ; |
743 | |
744 | return ret; |
745 | } |
746 | FLATBUFFERS_ASSERT(0); |
747 | return "" ; |
748 | } |
749 | |
750 | // Used for generating a short function that returns the correct class |
751 | // based on union enum type. Assume the context is inside the non object api |
752 | // type |
753 | std::string GenUnionValTS(import_set &imports, const std::string &field_name, |
754 | const Type &union_type, |
755 | const bool is_array = false) { |
756 | if (union_type.enum_def) { |
757 | const auto &enum_def = *union_type.enum_def; |
758 | const auto enum_type = AddImport(imports, enum_def, enum_def); |
759 | const std::string union_accessor = "this." + field_name; |
760 | |
761 | const auto union_has_string = UnionHasStringType(enum_def); |
762 | const auto field_binded_method = "this." + field_name + ".bind(this)" ; |
763 | |
764 | std::string ret; |
765 | |
766 | if (!is_array) { |
767 | const auto conversion_function = GenUnionConvFuncName(enum_def); |
768 | const auto target_enum = "this." + field_name + "Type()" ; |
769 | |
770 | ret = "(() => {\n" ; |
771 | ret += " let temp = " + conversion_function + "(" + target_enum + |
772 | ", " + field_binded_method + ");\n" ; |
773 | ret += " if(temp === null) { return null; }\n" ; |
774 | ret += union_has_string |
775 | ? " if(typeof temp === 'string') { return temp; }\n" |
776 | : "" ; |
777 | ret += " return temp.unpack()\n" ; |
778 | ret += " })()" ; |
779 | } else { |
780 | const auto conversion_function = GenUnionListConvFuncName(enum_def); |
781 | const auto target_enum_accesor = "this." + field_name + "Type" ; |
782 | const auto target_enum_length = target_enum_accesor + "Length()" ; |
783 | |
784 | ret = "(() => {\n" ; |
785 | ret += " let ret = [];\n" ; |
786 | ret += " for(let targetEnumIndex = 0; targetEnumIndex < " + |
787 | target_enum_length + |
788 | "; " |
789 | "++targetEnumIndex) {\n" ; |
790 | ret += " let targetEnum = " + target_enum_accesor + |
791 | "(targetEnumIndex);\n" ; |
792 | ret += " if(targetEnum === null || " + enum_type + |
793 | "[targetEnum!] === 'NONE') { " |
794 | "continue; }\n\n" ; |
795 | ret += " let temp = " + conversion_function + "(targetEnum, " + |
796 | field_binded_method + ", targetEnumIndex);\n" ; |
797 | ret += " if(temp === null) { continue; }\n" ; |
798 | ret += union_has_string ? " if(typeof temp === 'string') { " |
799 | "ret.push(temp); continue; }\n" |
800 | : "" ; |
801 | ret += " ret.push(temp.unpack());\n" ; |
802 | ret += " }\n" ; |
803 | ret += " return ret;\n" ; |
804 | ret += " })()" ; |
805 | } |
806 | |
807 | return ret; |
808 | } |
809 | |
810 | FLATBUFFERS_ASSERT(0); |
811 | return "" ; |
812 | } |
813 | |
814 | static std::string GenNullCheckConditional( |
815 | const std::string &nullCheckVar, const std::string &trueVal, |
816 | const std::string &falseVal = "null" ) { |
817 | return "(" + nullCheckVar + " !== null ? " + trueVal + " : " + falseVal + |
818 | ")" ; |
819 | } |
820 | |
821 | std::string GenStructMemberValueTS(const StructDef &struct_def, |
822 | const std::string &prefix, |
823 | const std::string &delimiter, |
824 | const bool nullCheck = true) { |
825 | std::string ret; |
826 | for (auto it = struct_def.fields.vec.begin(); |
827 | it != struct_def.fields.vec.end(); ++it) { |
828 | auto &field = **it; |
829 | |
830 | const auto curr_member_accessor = |
831 | prefix + "." + MakeCamel(field.name, false); |
832 | if (IsStruct(field.value.type)) { |
833 | ret += GenStructMemberValueTS(*field.value.type.struct_def, |
834 | curr_member_accessor, delimiter); |
835 | } else { |
836 | if (nullCheck) { |
837 | ret += |
838 | "(" + prefix + " === null ? 0 : " + curr_member_accessor + "!)" ; |
839 | } else { |
840 | ret += curr_member_accessor; |
841 | } |
842 | } |
843 | |
844 | if (std::next(it) != struct_def.fields.vec.end()) { ret += delimiter; } |
845 | } |
846 | |
847 | return ret; |
848 | } |
849 | |
850 | void GenObjApi(const Parser &parser, StructDef &struct_def, |
851 | std::string &obj_api_unpack_func, std::string &obj_api_class, |
852 | import_set &imports) { |
853 | const auto class_name = GetObjApiClassName(struct_def, parser.opts); |
854 | |
855 | std::string unpack_func = "\nunpack(): " + class_name + |
856 | " {\n return new " + class_name + "(" + |
857 | (struct_def.fields.vec.empty() ? "" : "\n" ); |
858 | std::string unpack_to_func = "\nunpackTo(_o: " + class_name + "): void {" + |
859 | +(struct_def.fields.vec.empty() ? "" : "\n" ); |
860 | |
861 | std::string constructor_func = "constructor(" ; |
862 | constructor_func += (struct_def.fields.vec.empty() ? "" : "\n" ); |
863 | |
864 | const auto has_create = |
865 | struct_def.fixed || CanCreateFactoryMethod(struct_def); |
866 | |
867 | std::string pack_func_prototype = |
868 | "\npack(builder:flatbuffers.Builder): flatbuffers.Offset {\n" ; |
869 | |
870 | std::string pack_func_offset_decl; |
871 | std::string pack_func_create_call; |
872 | |
873 | const auto struct_name = AddImport(imports, struct_def, struct_def); |
874 | |
875 | if (has_create) { |
876 | pack_func_create_call = " return " + struct_name + ".create" + |
877 | GetPrefixedName(struct_def) + "(builder" + |
878 | (struct_def.fields.vec.empty() ? "" : ",\n " ); |
879 | } else { |
880 | pack_func_create_call = " " + struct_name + ".start" + |
881 | GetPrefixedName(struct_def) + "(builder);\n" ; |
882 | } |
883 | |
884 | if (struct_def.fixed) { |
885 | // when packing struct, nested struct's members instead of the struct's |
886 | // offset are used |
887 | pack_func_create_call += |
888 | GenStructMemberValueTS(struct_def, "this" , ",\n " , false) + "\n " ; |
889 | } |
890 | |
891 | for (auto it = struct_def.fields.vec.begin(); |
892 | it != struct_def.fields.vec.end(); ++it) { |
893 | auto &field = **it; |
894 | if (field.deprecated) continue; |
895 | |
896 | const auto field_name = MakeCamel(field.name, false); |
897 | const std::string field_binded_method = |
898 | "this." + field_name + ".bind(this)" ; |
899 | |
900 | std::string field_val; |
901 | std::string field_type; |
902 | // a string that declares a variable containing the |
903 | // offset for things that can't be generated inline |
904 | // empty otw |
905 | std::string field_offset_decl; |
906 | // a string that contains values for things that can be created inline or |
907 | // the variable name from field_offset_decl |
908 | std::string field_offset_val; |
909 | const auto field_default_val = GenDefaultValue(field, imports); |
910 | |
911 | // Emit a scalar field |
912 | const auto is_string = IsString(field.value.type); |
913 | if (IsScalar(field.value.type.base_type) || is_string) { |
914 | const auto has_null_default = is_string || HasNullDefault(field); |
915 | |
916 | field_type += GenTypeName(imports, field, field.value.type, false, |
917 | has_null_default); |
918 | field_val = "this." + field_name + "()" ; |
919 | |
920 | if (field.value.type.base_type != BASE_TYPE_STRING) { |
921 | field_offset_val = "this." + field_name; |
922 | } else { |
923 | field_offset_decl = GenNullCheckConditional( |
924 | "this." + field_name, |
925 | "builder.createString(this." + field_name + "!)" , "0" ); |
926 | } |
927 | } |
928 | |
929 | // Emit an object field |
930 | else { |
931 | auto is_vector = false; |
932 | switch (field.value.type.base_type) { |
933 | case BASE_TYPE_STRUCT: { |
934 | const auto &sd = *field.value.type.struct_def; |
935 | field_type += GetObjApiClassName(sd, parser.opts); |
936 | |
937 | const std::string field_accessor = "this." + field_name + "()" ; |
938 | field_val = GenNullCheckConditional(field_accessor, |
939 | field_accessor + "!.unpack()" ); |
940 | auto packing = GenNullCheckConditional( |
941 | "this." + field_name, "this." + field_name + "!.pack(builder)" , |
942 | "0" ); |
943 | |
944 | if (sd.fixed) { |
945 | field_offset_val = std::move(packing); |
946 | } else { |
947 | field_offset_decl = std::move(packing); |
948 | } |
949 | |
950 | break; |
951 | } |
952 | |
953 | case BASE_TYPE_VECTOR: { |
954 | auto vectortype = field.value.type.VectorType(); |
955 | auto vectortypename = |
956 | GenTypeName(imports, struct_def, vectortype, false); |
957 | is_vector = true; |
958 | |
959 | field_type = "(" ; |
960 | |
961 | switch (vectortype.base_type) { |
962 | case BASE_TYPE_STRUCT: { |
963 | const auto &sd = *field.value.type.struct_def; |
964 | field_type += GetObjApiClassName(sd, parser.opts); |
965 | field_type += ")[]" ; |
966 | |
967 | field_val = GenBBAccess() + ".createObjList(" + |
968 | field_binded_method + ", this." + field_name + |
969 | "Length())" ; |
970 | |
971 | if (sd.fixed) { |
972 | field_offset_decl = |
973 | "builder.createStructOffsetList(this." + field_name + |
974 | ", " + AddImport(imports, struct_def, struct_def) + |
975 | ".start" + MakeCamel(field_name) + "Vector)" ; |
976 | } else { |
977 | field_offset_decl = |
978 | AddImport(imports, struct_def, struct_def) + ".create" + |
979 | MakeCamel(field_name) + |
980 | "Vector(builder, builder.createObjectOffsetList(" + |
981 | "this." + field_name + "))" ; |
982 | } |
983 | |
984 | break; |
985 | } |
986 | |
987 | case BASE_TYPE_STRING: { |
988 | field_type += "string)[]" ; |
989 | field_val = GenBBAccess() + ".createScalarList(" + |
990 | field_binded_method + ", this." + field_name + |
991 | "Length())" ; |
992 | field_offset_decl = |
993 | AddImport(imports, struct_def, struct_def) + ".create" + |
994 | MakeCamel(field_name) + |
995 | "Vector(builder, builder.createObjectOffsetList(" + |
996 | "this." + field_name + "))" ; |
997 | break; |
998 | } |
999 | |
1000 | case BASE_TYPE_UNION: { |
1001 | field_type += GenObjApiUnionTypeTS(imports, parser.opts, |
1002 | *(vectortype.enum_def)); |
1003 | field_type += ")[]" ; |
1004 | field_val = |
1005 | GenUnionValTS(imports, field_name, vectortype, true); |
1006 | |
1007 | field_offset_decl = |
1008 | AddImport(imports, struct_def, struct_def) + ".create" + |
1009 | MakeCamel(field_name) + |
1010 | "Vector(builder, builder.createObjectOffsetList(" + |
1011 | "this." + field_name + "))" ; |
1012 | |
1013 | break; |
1014 | } |
1015 | default: { |
1016 | if (vectortype.enum_def) { |
1017 | field_type += GenTypeName(imports, struct_def, vectortype, |
1018 | false, HasNullDefault(field)); |
1019 | } else { |
1020 | field_type += vectortypename; |
1021 | } |
1022 | field_type += ")[]" ; |
1023 | field_val = GenBBAccess() + ".createScalarList(" + |
1024 | field_binded_method + ", this." + field_name + |
1025 | "Length())" ; |
1026 | |
1027 | field_offset_decl = AddImport(imports, struct_def, struct_def) + |
1028 | ".create" + MakeCamel(field_name) + |
1029 | "Vector(builder, this." + field_name + ")" ; |
1030 | |
1031 | break; |
1032 | } |
1033 | } |
1034 | |
1035 | break; |
1036 | } |
1037 | |
1038 | case BASE_TYPE_UNION: { |
1039 | field_type += GenObjApiUnionTypeTS(imports, parser.opts, |
1040 | *(field.value.type.enum_def)); |
1041 | |
1042 | field_val = GenUnionValTS(imports, field_name, field.value.type); |
1043 | field_offset_decl = |
1044 | "builder.createObjectOffset(this." + field_name + ")" ; |
1045 | break; |
1046 | } |
1047 | |
1048 | default: FLATBUFFERS_ASSERT(0); break; |
1049 | } |
1050 | |
1051 | // length 0 vector is simply empty instead of null |
1052 | field_type += is_vector ? "" : "|null" ; |
1053 | } |
1054 | |
1055 | if (!field_offset_decl.empty()) { |
1056 | field_offset_decl = |
1057 | " const " + field_name + " = " + field_offset_decl + ";" ; |
1058 | } |
1059 | if (field_offset_val.empty()) { field_offset_val = field_name; } |
1060 | |
1061 | unpack_func += " " + field_val; |
1062 | unpack_to_func += " _o." + field_name + " = " + field_val + ";" ; |
1063 | |
1064 | constructor_func += " public " + field_name + ": " + field_type + " = " + |
1065 | field_default_val; |
1066 | |
1067 | if (!struct_def.fixed) { |
1068 | if (!field_offset_decl.empty()) { |
1069 | pack_func_offset_decl += field_offset_decl + "\n" ; |
1070 | } |
1071 | |
1072 | if (has_create) { |
1073 | pack_func_create_call += field_offset_val; |
1074 | } else { |
1075 | pack_func_create_call += " " + struct_name + ".add" + |
1076 | MakeCamel(field.name) + "(builder, " + |
1077 | field_offset_val + ");\n" ; |
1078 | } |
1079 | } |
1080 | |
1081 | if (std::next(it) != struct_def.fields.vec.end()) { |
1082 | constructor_func += ",\n" ; |
1083 | |
1084 | if (!struct_def.fixed && has_create) { |
1085 | pack_func_create_call += ",\n " ; |
1086 | } |
1087 | |
1088 | unpack_func += ",\n" ; |
1089 | unpack_to_func += "\n" ; |
1090 | } else { |
1091 | constructor_func += "\n" ; |
1092 | if (!struct_def.fixed) { |
1093 | pack_func_offset_decl += (pack_func_offset_decl.empty() ? "" : "\n" ); |
1094 | pack_func_create_call += "\n " ; |
1095 | } |
1096 | |
1097 | unpack_func += "\n " ; |
1098 | unpack_to_func += "\n" ; |
1099 | } |
1100 | } |
1101 | |
1102 | constructor_func += "){}\n\n" ; |
1103 | |
1104 | if (has_create) { |
1105 | pack_func_create_call += ");" ; |
1106 | } else { |
1107 | pack_func_create_call += "return " + struct_name + ".end" + |
1108 | GetPrefixedName(struct_def) + "(builder);" ; |
1109 | } |
1110 | |
1111 | obj_api_class = "\nexport class " + |
1112 | GetObjApiClassName(struct_def, parser.opts) + " {\n" ; |
1113 | |
1114 | obj_api_class += constructor_func; |
1115 | obj_api_class += pack_func_prototype + pack_func_offset_decl + |
1116 | pack_func_create_call + "\n}" ; |
1117 | |
1118 | obj_api_class += "\n}\n" ; |
1119 | |
1120 | unpack_func += ");\n}" ; |
1121 | unpack_to_func += "}\n" ; |
1122 | |
1123 | obj_api_unpack_func = unpack_func + "\n\n" + unpack_to_func; |
1124 | } |
1125 | |
1126 | static bool CanCreateFactoryMethod(const StructDef &struct_def) { |
1127 | // to preserve backwards compatibility, we allow the first field to be a |
1128 | // struct |
1129 | return struct_def.fields.vec.size() < 2 || |
1130 | std::all_of(std::begin(struct_def.fields.vec) + 1, |
1131 | std::end(struct_def.fields.vec), |
1132 | [](const FieldDef *f) -> bool { |
1133 | FLATBUFFERS_ASSERT(f != nullptr); |
1134 | return f->value.type.base_type != BASE_TYPE_STRUCT; |
1135 | }); |
1136 | } |
1137 | |
1138 | // Generate an accessor struct with constructor for a flatbuffers struct. |
1139 | void GenStruct(const Parser &parser, StructDef &struct_def, |
1140 | std::string *code_ptr, import_set &imports) { |
1141 | if (struct_def.generated) return; |
1142 | std::string &code = *code_ptr; |
1143 | |
1144 | std::string object_name; |
1145 | std::string object_namespace = GetNameSpace(struct_def); |
1146 | |
1147 | // Emit constructor |
1148 | object_name = struct_def.name; |
1149 | GenDocComment(struct_def.doc_comment, code_ptr); |
1150 | code += "export class " + struct_def.name; |
1151 | code += " {\n" ; |
1152 | code += " bb: flatbuffers.ByteBuffer|null = null;\n" ; |
1153 | code += " bb_pos = 0;\n" ; |
1154 | |
1155 | // Generate the __init method that sets the field in a pre-existing |
1156 | // accessor object. This is to allow object reuse. |
1157 | code += |
1158 | "__init(i:number, bb:flatbuffers.ByteBuffer):" + object_name + " {\n" ; |
1159 | code += " this.bb_pos = i;\n" ; |
1160 | code += " this.bb = bb;\n" ; |
1161 | code += " return this;\n" ; |
1162 | code += "}\n\n" ; |
1163 | |
1164 | // Generate special accessors for the table that when used as the root of a |
1165 | // FlatBuffer |
1166 | GenerateRootAccessor(struct_def, code_ptr, code, object_name, false); |
1167 | GenerateRootAccessor(struct_def, code_ptr, code, object_name, true); |
1168 | |
1169 | // Generate the identifier check method |
1170 | if (!struct_def.fixed && parser_.root_struct_def_ == &struct_def && |
1171 | !parser_.file_identifier_.empty()) { |
1172 | GenDocComment(code_ptr); |
1173 | code += |
1174 | "static bufferHasIdentifier(bb:flatbuffers.ByteBuffer):boolean " |
1175 | "{\n" ; |
1176 | code += " return bb.__has_identifier('" + parser_.file_identifier_; |
1177 | code += "');\n}\n\n" ; |
1178 | } |
1179 | |
1180 | // Emit field accessors |
1181 | for (auto it = struct_def.fields.vec.begin(); |
1182 | it != struct_def.fields.vec.end(); ++it) { |
1183 | auto &field = **it; |
1184 | if (field.deprecated) continue; |
1185 | auto offset_prefix = |
1186 | " const offset = " + GenBBAccess() + ".__offset(this.bb_pos, " + |
1187 | NumToString(field.value.offset) + ");\n return offset ? " ; |
1188 | |
1189 | // Emit a scalar field |
1190 | const auto is_string = IsString(field.value.type); |
1191 | if (IsScalar(field.value.type.base_type) || is_string) { |
1192 | const auto has_null_default = is_string || HasNullDefault(field); |
1193 | |
1194 | GenDocComment(field.doc_comment, code_ptr); |
1195 | std::string prefix = MakeCamel(field.name, false) + "(" ; |
1196 | if (is_string) { |
1197 | code += prefix + "):string|null\n" ; |
1198 | code += |
1199 | prefix + "optionalEncoding:flatbuffers.Encoding" + "):" + |
1200 | GenTypeName(imports, struct_def, field.value.type, false, true) + |
1201 | "\n" ; |
1202 | code += prefix + "optionalEncoding?:any" ; |
1203 | } else { |
1204 | code += prefix; |
1205 | } |
1206 | if (field.value.type.enum_def) { |
1207 | code += "):" + |
1208 | GenTypeName(imports, struct_def, field.value.type, false, |
1209 | field.IsOptional()) + |
1210 | " {\n" ; |
1211 | } else { |
1212 | code += "):" + |
1213 | GenTypeName(imports, struct_def, field.value.type, false, |
1214 | has_null_default) + |
1215 | " {\n" ; |
1216 | } |
1217 | |
1218 | if (struct_def.fixed) { |
1219 | code += |
1220 | " return " + |
1221 | GenGetter(field.value.type, |
1222 | "(this.bb_pos" + MaybeAdd(field.value.offset) + ")" ) + |
1223 | ";\n" ; |
1224 | } else { |
1225 | std::string index = "this.bb_pos + offset" ; |
1226 | if (is_string) { index += ", optionalEncoding" ; } |
1227 | code += offset_prefix + |
1228 | GenGetter(field.value.type, "(" + index + ")" ) + " : " + |
1229 | GenDefaultValue(field, imports); |
1230 | code += ";\n" ; |
1231 | } |
1232 | } |
1233 | |
1234 | // Emit an object field |
1235 | else { |
1236 | switch (field.value.type.base_type) { |
1237 | case BASE_TYPE_STRUCT: { |
1238 | const auto type = |
1239 | AddImport(imports, struct_def, *field.value.type.struct_def); |
1240 | GenDocComment(field.doc_comment, code_ptr); |
1241 | code += MakeCamel(field.name, false); |
1242 | code += "(obj?:" + type + "):" + type + "|null {\n" ; |
1243 | |
1244 | if (struct_def.fixed) { |
1245 | code += " return (obj || " + GenerateNewExpression(type); |
1246 | code += ").__init(this.bb_pos" ; |
1247 | code += |
1248 | MaybeAdd(field.value.offset) + ", " + GenBBAccess() + ");\n" ; |
1249 | } else { |
1250 | code += offset_prefix + "(obj || " + GenerateNewExpression(type) + |
1251 | ").__init(" ; |
1252 | code += field.value.type.struct_def->fixed |
1253 | ? "this.bb_pos + offset" |
1254 | : GenBBAccess() + ".__indirect(this.bb_pos + offset)" ; |
1255 | code += ", " + GenBBAccess() + ") : null;\n" ; |
1256 | } |
1257 | |
1258 | break; |
1259 | } |
1260 | |
1261 | case BASE_TYPE_VECTOR: { |
1262 | auto vectortype = field.value.type.VectorType(); |
1263 | auto vectortypename = |
1264 | GenTypeName(imports, struct_def, vectortype, false); |
1265 | auto inline_size = InlineSize(vectortype); |
1266 | auto index = GenBBAccess() + |
1267 | ".__vector(this.bb_pos + offset) + index" + |
1268 | MaybeScale(inline_size); |
1269 | std::string ret_type; |
1270 | bool is_union = false; |
1271 | switch (vectortype.base_type) { |
1272 | case BASE_TYPE_STRUCT: ret_type = vectortypename; break; |
1273 | case BASE_TYPE_STRING: ret_type = vectortypename; break; |
1274 | case BASE_TYPE_UNION: |
1275 | ret_type = "?flatbuffers.Table" ; |
1276 | is_union = true; |
1277 | break; |
1278 | default: ret_type = vectortypename; |
1279 | } |
1280 | GenDocComment(field.doc_comment, code_ptr); |
1281 | std::string prefix = MakeCamel(field.name, false); |
1282 | // TODO: make it work without any |
1283 | // if (is_union) { prefix += "<T extends flatbuffers.Table>"; } |
1284 | if (is_union) { prefix += "" ; } |
1285 | prefix += "(index: number" ; |
1286 | if (is_union) { |
1287 | const auto union_type = |
1288 | GenUnionGenericTypeTS(*(field.value.type.enum_def)); |
1289 | |
1290 | vectortypename = union_type; |
1291 | code += prefix + ", obj:" + union_type; |
1292 | } else if (vectortype.base_type == BASE_TYPE_STRUCT) { |
1293 | code += prefix + ", obj?:" + vectortypename; |
1294 | } else if (IsString(vectortype)) { |
1295 | code += prefix + "):string\n" ; |
1296 | code += prefix + ",optionalEncoding:flatbuffers.Encoding" + |
1297 | "):" + vectortypename + "\n" ; |
1298 | code += prefix + ",optionalEncoding?:any" ; |
1299 | } else { |
1300 | code += prefix; |
1301 | } |
1302 | code += "):" + vectortypename + "|null {\n" ; |
1303 | |
1304 | if (vectortype.base_type == BASE_TYPE_STRUCT) { |
1305 | code += offset_prefix + "(obj || " + |
1306 | GenerateNewExpression(vectortypename); |
1307 | code += ").__init(" ; |
1308 | code += vectortype.struct_def->fixed |
1309 | ? index |
1310 | : GenBBAccess() + ".__indirect(" + index + ")" ; |
1311 | code += ", " + GenBBAccess() + ")" ; |
1312 | } else { |
1313 | if (is_union) { |
1314 | index = "obj, " + index; |
1315 | } else if (IsString(vectortype)) { |
1316 | index += ", optionalEncoding" ; |
1317 | } |
1318 | code += offset_prefix + GenGetter(vectortype, "(" + index + ")" ); |
1319 | } |
1320 | code += " : " ; |
1321 | if (field.value.type.element == BASE_TYPE_BOOL) { |
1322 | code += "false" ; |
1323 | } else if (field.value.type.element == BASE_TYPE_LONG || |
1324 | field.value.type.element == BASE_TYPE_ULONG) { |
1325 | code += "BigInt(0)" ; |
1326 | } else if (IsScalar(field.value.type.element)) { |
1327 | if (field.value.type.enum_def) { |
1328 | code += field.value.constant; |
1329 | } else { |
1330 | code += "0" ; |
1331 | } |
1332 | } else { |
1333 | code += "null" ; |
1334 | } |
1335 | code += ";\n" ; |
1336 | break; |
1337 | } |
1338 | |
1339 | case BASE_TYPE_UNION: { |
1340 | GenDocComment(field.doc_comment, code_ptr); |
1341 | code += MakeCamel(field.name, false); |
1342 | |
1343 | const auto &union_enum = *(field.value.type.enum_def); |
1344 | const auto union_type = GenUnionGenericTypeTS(union_enum); |
1345 | code += "<T extends flatbuffers.Table>(obj:" + union_type + |
1346 | "):" + union_type + |
1347 | "|null " |
1348 | "{\n" ; |
1349 | |
1350 | code += offset_prefix + |
1351 | GenGetter(field.value.type, "(obj, this.bb_pos + offset)" ) + |
1352 | " : null;\n" ; |
1353 | break; |
1354 | } |
1355 | default: FLATBUFFERS_ASSERT(0); |
1356 | } |
1357 | } |
1358 | code += "}\n\n" ; |
1359 | |
1360 | // Adds the mutable scalar value to the output |
1361 | if (IsScalar(field.value.type.base_type) && parser.opts.mutable_buffer && |
1362 | !IsUnion(field.value.type)) { |
1363 | std::string type = |
1364 | GenTypeName(imports, struct_def, field.value.type, true); |
1365 | |
1366 | code += "mutate_" + field.name + "(value:" + type + "):boolean {\n" ; |
1367 | |
1368 | if (struct_def.fixed) { |
1369 | code += " " + GenBBAccess() + ".write" + |
1370 | MakeCamel(GenType(field.value.type)) + "(this.bb_pos + " + |
1371 | NumToString(field.value.offset) + ", " ; |
1372 | } else { |
1373 | code += " const offset = " + GenBBAccess() + |
1374 | ".__offset(this.bb_pos, " + NumToString(field.value.offset) + |
1375 | ");\n\n" ; |
1376 | code += " if (offset === 0) {\n" ; |
1377 | code += " return false;\n" ; |
1378 | code += " }\n\n" ; |
1379 | |
1380 | // special case for bools, which are treated as uint8 |
1381 | code += " " + GenBBAccess() + ".write" + |
1382 | MakeCamel(GenType(field.value.type)) + |
1383 | "(this.bb_pos + offset, " ; |
1384 | if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+" ; } |
1385 | } |
1386 | |
1387 | code += "value);\n" ; |
1388 | code += " return true;\n" ; |
1389 | code += "}\n\n" ; |
1390 | } |
1391 | |
1392 | // Emit vector helpers |
1393 | if (IsVector(field.value.type)) { |
1394 | // Emit a length helper |
1395 | GenDocComment(code_ptr); |
1396 | code += MakeCamel(field.name, false); |
1397 | code += "Length():number {\n" + offset_prefix; |
1398 | |
1399 | code += |
1400 | GenBBAccess() + ".__vector_len(this.bb_pos + offset) : 0;\n}\n\n" ; |
1401 | |
1402 | // For scalar types, emit a typed array helper |
1403 | auto vectorType = field.value.type.VectorType(); |
1404 | if (IsScalar(vectorType.base_type) && !IsLong(vectorType.base_type)) { |
1405 | GenDocComment(code_ptr); |
1406 | |
1407 | code += MakeCamel(field.name, false); |
1408 | code += "Array():" + GenType(vectorType) + "Array|null {\n" + |
1409 | offset_prefix; |
1410 | |
1411 | code += "new " + GenType(vectorType) + "Array(" + GenBBAccess() + |
1412 | ".bytes().buffer, " + GenBBAccess() + |
1413 | ".bytes().byteOffset + " + GenBBAccess() + |
1414 | ".__vector(this.bb_pos + offset), " + GenBBAccess() + |
1415 | ".__vector_len(this.bb_pos + offset)) : null;\n}\n\n" ; |
1416 | } |
1417 | } |
1418 | } |
1419 | |
1420 | // Emit the fully qualified name |
1421 | if (parser_.opts.generate_name_strings) { |
1422 | GenDocComment(code_ptr); |
1423 | code += "static getFullyQualifiedName():string {\n" ; |
1424 | code += " return '" + WrapInNameSpace(struct_def) + "';\n" ; |
1425 | code += "}\n\n" ; |
1426 | } |
1427 | |
1428 | // Emit the size of the struct. |
1429 | if (struct_def.fixed) { |
1430 | GenDocComment(code_ptr); |
1431 | code += "static sizeOf():number {\n" ; |
1432 | code += " return " + NumToString(struct_def.bytesize) + ";\n" ; |
1433 | code += "}\n\n" ; |
1434 | } |
1435 | |
1436 | // Emit a factory constructor |
1437 | if (struct_def.fixed) { |
1438 | std::string arguments; |
1439 | GenStructArgs(imports, struct_def, &arguments, "" ); |
1440 | GenDocComment(code_ptr); |
1441 | |
1442 | code += "static create" + GetPrefixedName(struct_def) + |
1443 | "(builder:flatbuffers.Builder" ; |
1444 | code += arguments + "):flatbuffers.Offset {\n" ; |
1445 | |
1446 | GenStructBody(struct_def, &code, "" ); |
1447 | code += " return builder.offset();\n}\n\n" ; |
1448 | } else { |
1449 | // Generate a method to start building a new object |
1450 | GenDocComment(code_ptr); |
1451 | |
1452 | code += "static start" + GetPrefixedName(struct_def) + |
1453 | "(builder:flatbuffers.Builder) {\n" ; |
1454 | |
1455 | code += " builder.startObject(" + |
1456 | NumToString(struct_def.fields.vec.size()) + ");\n" ; |
1457 | code += "}\n\n" ; |
1458 | |
1459 | // Generate a set of static methods that allow table construction |
1460 | for (auto it = struct_def.fields.vec.begin(); |
1461 | it != struct_def.fields.vec.end(); ++it) { |
1462 | auto &field = **it; |
1463 | if (field.deprecated) continue; |
1464 | const auto argname = GetArgName(field); |
1465 | |
1466 | // Generate the field insertion method |
1467 | GenDocComment(code_ptr); |
1468 | code += "static add" + MakeCamel(field.name); |
1469 | code += "(builder:flatbuffers.Builder, " + argname + ":" + |
1470 | GetArgType(imports, struct_def, field, false) + ") {\n" ; |
1471 | code += " builder.addField" + GenWriteMethod(field.value.type) + "(" ; |
1472 | code += NumToString(it - struct_def.fields.vec.begin()) + ", " ; |
1473 | if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+" ; } |
1474 | code += argname + ", " ; |
1475 | if (!IsScalar(field.value.type.base_type)) { |
1476 | code += "0" ; |
1477 | } else if (HasNullDefault(field)) { |
1478 | if (IsLong(field.value.type.base_type)) { |
1479 | code += "BigInt(0)" ; |
1480 | } else { |
1481 | code += "0" ; |
1482 | } |
1483 | } else { |
1484 | if (field.value.type.base_type == BASE_TYPE_BOOL) { code += "+" ; } |
1485 | code += GenDefaultValue(field, imports); |
1486 | } |
1487 | code += ");\n}\n\n" ; |
1488 | |
1489 | if (IsVector(field.value.type)) { |
1490 | auto vector_type = field.value.type.VectorType(); |
1491 | auto alignment = InlineAlignment(vector_type); |
1492 | auto elem_size = InlineSize(vector_type); |
1493 | |
1494 | // Generate a method to create a vector from a JavaScript array |
1495 | if (!IsStruct(vector_type)) { |
1496 | GenDocComment(code_ptr); |
1497 | |
1498 | const std::string sig_begin = |
1499 | "static create" + MakeCamel(field.name) + |
1500 | "Vector(builder:flatbuffers.Builder, data:" ; |
1501 | const std::string sig_end = "):flatbuffers.Offset" ; |
1502 | std::string type = |
1503 | GenTypeName(imports, struct_def, vector_type, true) + "[]" ; |
1504 | if (type == "number[]" ) { |
1505 | const auto &array_type = GenType(vector_type); |
1506 | // the old type should be deprecated in the future |
1507 | std::string type_old = "number[]|Uint8Array" ; |
1508 | std::string type_new = "number[]|" + array_type + "Array" ; |
1509 | if (type_old == type_new) { |
1510 | type = type_new; |
1511 | } else { |
1512 | // add function overloads |
1513 | code += sig_begin + type_new + sig_end + ";\n" ; |
1514 | code += |
1515 | "/**\n * @deprecated This Uint8Array overload will " |
1516 | "be removed in the future.\n */\n" ; |
1517 | code += sig_begin + type_old + sig_end + ";\n" ; |
1518 | type = type_new + "|Uint8Array" ; |
1519 | } |
1520 | } |
1521 | code += sig_begin + type + sig_end + " {\n" ; |
1522 | code += " builder.startVector(" + NumToString(elem_size); |
1523 | code += ", data.length, " + NumToString(alignment) + ");\n" ; |
1524 | code += " for (let i = data.length - 1; i >= 0; i--) {\n" ; |
1525 | code += " builder.add" + GenWriteMethod(vector_type) + "(" ; |
1526 | if (vector_type.base_type == BASE_TYPE_BOOL) { code += "+" ; } |
1527 | code += "data[i]!);\n" ; |
1528 | code += " }\n" ; |
1529 | code += " return builder.endVector();\n" ; |
1530 | code += "}\n\n" ; |
1531 | } |
1532 | |
1533 | // Generate a method to start a vector, data to be added manually |
1534 | // after |
1535 | GenDocComment(code_ptr); |
1536 | |
1537 | code += "static start" + MakeCamel(field.name); |
1538 | code += "Vector(builder:flatbuffers.Builder, numElems:number) {\n" ; |
1539 | code += " builder.startVector(" + NumToString(elem_size); |
1540 | code += ", numElems, " + NumToString(alignment) + ");\n" ; |
1541 | code += "}\n\n" ; |
1542 | } |
1543 | } |
1544 | |
1545 | // Generate a method to stop building a new object |
1546 | GenDocComment(code_ptr); |
1547 | |
1548 | code += "static end" + GetPrefixedName(struct_def); |
1549 | code += "(builder:flatbuffers.Builder):flatbuffers.Offset {\n" ; |
1550 | |
1551 | code += " const offset = builder.endObject();\n" ; |
1552 | for (auto it = struct_def.fields.vec.begin(); |
1553 | it != struct_def.fields.vec.end(); ++it) { |
1554 | auto &field = **it; |
1555 | if (!field.deprecated && field.IsRequired()) { |
1556 | code += " builder.requiredField(offset, " ; |
1557 | code += NumToString(field.value.offset); |
1558 | code += ") // " + field.name + "\n" ; |
1559 | } |
1560 | } |
1561 | code += " return offset;\n" ; |
1562 | code += "}\n\n" ; |
1563 | |
1564 | // Generate the methods to complete buffer construction |
1565 | GenerateFinisher(struct_def, code_ptr, code, false); |
1566 | GenerateFinisher(struct_def, code_ptr, code, true); |
1567 | |
1568 | // Generate a convenient CreateX function |
1569 | if (CanCreateFactoryMethod(struct_def)) { |
1570 | code += "static create" + GetPrefixedName(struct_def); |
1571 | code += "(builder:flatbuffers.Builder" ; |
1572 | for (auto it = struct_def.fields.vec.begin(); |
1573 | it != struct_def.fields.vec.end(); ++it) { |
1574 | const auto &field = **it; |
1575 | if (field.deprecated) continue; |
1576 | code += ", " + GetArgName(field) + ":" + |
1577 | GetArgType(imports, struct_def, field, true); |
1578 | } |
1579 | |
1580 | code += "):flatbuffers.Offset {\n" ; |
1581 | code += " " + struct_def.name + ".start" + |
1582 | GetPrefixedName(struct_def) + "(builder);\n" ; |
1583 | |
1584 | std::string methodPrefix = struct_def.name; |
1585 | for (auto it = struct_def.fields.vec.begin(); |
1586 | it != struct_def.fields.vec.end(); ++it) { |
1587 | const auto &field = **it; |
1588 | if (field.deprecated) continue; |
1589 | |
1590 | const auto arg_name = GetArgName(field); |
1591 | |
1592 | if (field.IsScalarOptional()) { |
1593 | code += " if (" + arg_name + " !== null)\n " ; |
1594 | } |
1595 | |
1596 | code += " " + methodPrefix + ".add" + MakeCamel(field.name) + "(" ; |
1597 | code += "builder, " + arg_name + ");\n" ; |
1598 | } |
1599 | |
1600 | code += " return " + methodPrefix + ".end" + |
1601 | GetPrefixedName(struct_def) + "(builder);\n" ; |
1602 | code += "}\n" ; |
1603 | } |
1604 | } |
1605 | |
1606 | if (!struct_def.fixed && parser_.services_.vec.size() != 0) { |
1607 | auto name = GetPrefixedName(struct_def, "" ); |
1608 | code += "\n" ; |
1609 | code += "serialize():Uint8Array {\n" ; |
1610 | code += " return this.bb!.bytes();\n" ; |
1611 | code += "}\n" ; |
1612 | |
1613 | code += "\n" ; |
1614 | code += "static deserialize(buffer: Uint8Array):" + name + " {\n" ; |
1615 | code += " return " + AddImport(imports, struct_def, struct_def) + |
1616 | ".getRootAs" + name + "(new flatbuffers.ByteBuffer(buffer))\n" ; |
1617 | code += "}\n" ; |
1618 | } |
1619 | |
1620 | if (parser_.opts.generate_object_based_api) { |
1621 | std::string obj_api_class; |
1622 | std::string obj_api_unpack_func; |
1623 | GenObjApi(parser_, struct_def, obj_api_unpack_func, obj_api_class, |
1624 | imports); |
1625 | |
1626 | code += obj_api_unpack_func + "}\n" + obj_api_class; |
1627 | } else { |
1628 | code += "}\n" ; |
1629 | } |
1630 | } |
1631 | |
1632 | static bool HasNullDefault(const FieldDef &field) { |
1633 | return field.IsOptional() && field.value.constant == "null" ; |
1634 | } |
1635 | |
1636 | std::string GetArgType(import_set &imports, const Definition &owner, |
1637 | const FieldDef &field, bool allowNull) { |
1638 | return GenTypeName(imports, owner, field.value.type, true, |
1639 | allowNull && field.IsOptional()); |
1640 | } |
1641 | |
1642 | std::string GetArgName(const FieldDef &field) { |
1643 | auto argname = MakeCamel(field.name, false); |
1644 | if (!IsScalar(field.value.type.base_type)) { |
1645 | argname += "Offset" ; |
1646 | } else { |
1647 | argname = EscapeKeyword(argname); |
1648 | } |
1649 | return argname; |
1650 | } |
1651 | |
1652 | std::string GetPrefixedName(const StructDef &struct_def, |
1653 | const char *prefix = "" ) { |
1654 | return prefix + struct_def.name; |
1655 | } |
1656 | }; // namespace ts |
1657 | } // namespace ts |
1658 | |
1659 | bool GenerateTS(const Parser &parser, const std::string &path, |
1660 | const std::string &file_name) { |
1661 | ts::TsGenerator generator(parser, path, file_name); |
1662 | return generator.generate(); |
1663 | } |
1664 | |
1665 | std::string TSMakeRule(const Parser &parser, const std::string &path, |
1666 | const std::string &file_name) { |
1667 | std::string filebase = |
1668 | flatbuffers::StripPath(flatbuffers::StripExtension(file_name)); |
1669 | ts::TsGenerator generator(parser, path, file_name); |
1670 | std::string make_rule = |
1671 | generator.GeneratedFileName(path, filebase, parser.opts) + ": " ; |
1672 | |
1673 | auto included_files = parser.GetIncludedFilesRecursive(file_name); |
1674 | for (auto it = included_files.begin(); it != included_files.end(); ++it) { |
1675 | make_rule += " " + *it; |
1676 | } |
1677 | return make_rule; |
1678 | } |
1679 | |
1680 | } // namespace flatbuffers |
1681 | |