• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc.  All rights reserved.
3 // https://developers.google.com/protocol-buffers/
4 //
5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are
7 // met:
8 //
9 //     * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer.
11 //     * Redistributions in binary form must reproduce the above
12 // copyright notice, this list of conditions and the following disclaimer
13 // in the documentation and/or other materials provided with the
14 // distribution.
15 //     * Neither the name of Google Inc. nor the names of its
16 // contributors may be used to endorse or promote products derived from
17 // this software without specific prior written permission.
18 //
19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 
31 #include <assert.h>
32 #include <google/protobuf/compiler/js/js_generator.h>
33 #include <google/protobuf/compiler/js/well_known_types_embed.h>
34 #include <google/protobuf/compiler/scc.h>
35 #include <google/protobuf/descriptor.h>
36 #include <google/protobuf/descriptor.pb.h>
37 #include <google/protobuf/io/printer.h>
38 #include <google/protobuf/io/zero_copy_stream.h>
39 #include <google/protobuf/stubs/common.h>
40 #include <google/protobuf/stubs/logging.h>
41 #include <google/protobuf/stubs/stringprintf.h>
42 #include <google/protobuf/stubs/strutil.h>
43 
44 #include <algorithm>
45 #include <limits>
46 #include <map>
47 #include <memory>
48 #include <string>
49 #include <utility>
50 #include <vector>
51 
52 namespace google {
53 namespace protobuf {
54 namespace compiler {
55 namespace js {
56 
57 // Sorted list of JavaScript keywords. These cannot be used as names. If they
58 // appear, we prefix them with "pb_".
59 const char* kKeyword[] = {
60     "abstract",   "boolean",      "break",      "byte",    "case",
61     "catch",      "char",         "class",      "const",   "continue",
62     "debugger",   "default",      "delete",     "do",      "double",
63     "else",       "enum",         "export",     "extends", "false",
64     "final",      "finally",      "float",      "for",     "function",
65     "goto",       "if",           "implements", "import",  "in",
66     "instanceof", "int",          "interface",  "long",    "native",
67     "new",        "null",         "package",    "private", "protected",
68     "public",     "return",       "short",      "static",  "super",
69     "switch",     "synchronized", "this",       "throw",   "throws",
70     "transient",  "try",          "typeof",     "var",     "void",
71     "volatile",   "while",        "with",
72 };
73 
74 static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
75 
76 namespace {
77 
78 // The mode of operation for bytes fields. Historically JSPB always carried
79 // bytes as JS {string}, containing base64 content by convention. With binary
80 // and proto3 serialization the new convention is to represent it as binary
81 // data in Uint8Array. See b/26173701 for background on the migration.
82 enum BytesMode {
83   BYTES_DEFAULT,  // Default type for getBytesField to return.
84   BYTES_B64,      // Explicitly coerce to base64 string where needed.
85   BYTES_U8,       // Explicitly coerce to Uint8Array where needed.
86 };
87 
IsReserved(const std::string & ident)88 bool IsReserved(const std::string& ident) {
89   for (int i = 0; i < kNumKeyword; i++) {
90     if (ident == kKeyword[i]) {
91       return true;
92     }
93   }
94   return false;
95 }
96 
GetSnakeFilename(const std::string & filename)97 std::string GetSnakeFilename(const std::string& filename) {
98   std::string snake_name = filename;
99   ReplaceCharacters(&snake_name, "/", '_');
100   return snake_name;
101 }
102 
103 // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
104 // file foo/bar/baz.js.
GetJSFilename(const GeneratorOptions & options,const std::string & filename)105 std::string GetJSFilename(const GeneratorOptions& options,
106                           const std::string& filename) {
107   return StripProto(filename) + options.GetFileNameExtension();
108 }
109 
110 // Given a filename like foo/bar/baz.proto, returns the root directory
111 // path ../../
GetRootPath(const std::string & from_filename,const std::string & to_filename)112 std::string GetRootPath(const std::string& from_filename,
113                         const std::string& to_filename) {
114   if (to_filename.find("google/protobuf") == 0) {
115     // Well-known types (.proto files in the google/protobuf directory) are
116     // assumed to come from the 'google-protobuf' npm package.  We may want to
117     // generalize this exception later by letting others put generated code in
118     // their own npm packages.
119     return "google-protobuf/";
120   }
121 
122   size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
123   if (slashes == 0) {
124     return "./";
125   }
126   std::string result = "";
127   for (size_t i = 0; i < slashes; i++) {
128     result += "../";
129   }
130   return result;
131 }
132 
133 // Returns the alias we assign to the module of the given .proto filename
134 // when importing.
ModuleAlias(const std::string & filename)135 std::string ModuleAlias(const std::string& filename) {
136   // This scheme could technically cause problems if a file includes any 2 of:
137   //   foo/bar_baz.proto
138   //   foo_bar_baz.proto
139   //   foo_bar/baz.proto
140   //
141   // We'll worry about this problem if/when we actually see it.  This name isn't
142   // exposed to users so we can change it later if we need to.
143   std::string basename = StripProto(filename);
144   ReplaceCharacters(&basename, "-", '$');
145   ReplaceCharacters(&basename, "/", '_');
146   ReplaceCharacters(&basename, ".", '_');
147   return basename + "_pb";
148 }
149 
150 // Returns the fully normalized JavaScript namespace for the given
151 // file descriptor's package.
GetNamespace(const GeneratorOptions & options,const FileDescriptor * file)152 std::string GetNamespace(const GeneratorOptions& options,
153                          const FileDescriptor* file) {
154   if (!options.namespace_prefix.empty()) {
155     return options.namespace_prefix;
156   } else if (!file->package().empty()) {
157     return "proto." + file->package();
158   } else {
159     return "proto";
160   }
161 }
162 
163 // Returns the name of the message with a leading dot and taking into account
164 // nesting, for example ".OuterMessage.InnerMessage", or returns empty if
165 // descriptor is null. This function does not handle namespacing, only message
166 // nesting.
GetNestedMessageName(const Descriptor * descriptor)167 std::string GetNestedMessageName(const Descriptor* descriptor) {
168   if (descriptor == NULL) {
169     return "";
170   }
171   std::string result =
172       StripPrefixString(descriptor->full_name(), descriptor->file()->package());
173   // Add a leading dot if one is not already present.
174   if (!result.empty() && result[0] != '.') {
175     result = "." + result;
176   }
177   return result;
178 }
179 
180 // Returns the path prefix for a message or enumeration that
181 // lives under the given file and containing type.
GetPrefix(const GeneratorOptions & options,const FileDescriptor * file_descriptor,const Descriptor * containing_type)182 std::string GetPrefix(const GeneratorOptions& options,
183                       const FileDescriptor* file_descriptor,
184                       const Descriptor* containing_type) {
185   std::string prefix = GetNamespace(options, file_descriptor) +
186                        GetNestedMessageName(containing_type);
187   if (!prefix.empty()) {
188     prefix += ".";
189   }
190   return prefix;
191 }
192 
193 // Returns the fully normalized JavaScript path prefix for the given
194 // message descriptor.
GetMessagePathPrefix(const GeneratorOptions & options,const Descriptor * descriptor)195 std::string GetMessagePathPrefix(const GeneratorOptions& options,
196                                  const Descriptor* descriptor) {
197   return GetPrefix(options, descriptor->file(), descriptor->containing_type());
198 }
199 
200 // Returns the fully normalized JavaScript path for the given
201 // message descriptor.
GetMessagePath(const GeneratorOptions & options,const Descriptor * descriptor)202 std::string GetMessagePath(const GeneratorOptions& options,
203                            const Descriptor* descriptor) {
204   return GetMessagePathPrefix(options, descriptor) + descriptor->name();
205 }
206 
207 // Returns the fully normalized JavaScript path prefix for the given
208 // enumeration descriptor.
GetEnumPathPrefix(const GeneratorOptions & options,const EnumDescriptor * enum_descriptor)209 std::string GetEnumPathPrefix(const GeneratorOptions& options,
210                               const EnumDescriptor* enum_descriptor) {
211   return GetPrefix(options, enum_descriptor->file(),
212                    enum_descriptor->containing_type());
213 }
214 
215 // Returns the fully normalized JavaScript path for the given
216 // enumeration descriptor.
GetEnumPath(const GeneratorOptions & options,const EnumDescriptor * enum_descriptor)217 std::string GetEnumPath(const GeneratorOptions& options,
218                         const EnumDescriptor* enum_descriptor) {
219   return GetEnumPathPrefix(options, enum_descriptor) + enum_descriptor->name();
220 }
221 
MaybeCrossFileRef(const GeneratorOptions & options,const FileDescriptor * from_file,const Descriptor * to_message)222 std::string MaybeCrossFileRef(const GeneratorOptions& options,
223                               const FileDescriptor* from_file,
224                               const Descriptor* to_message) {
225   if ((options.import_style == GeneratorOptions::kImportCommonJs ||
226        options.import_style == GeneratorOptions::kImportCommonJsStrict) &&
227       from_file != to_message->file()) {
228     // Cross-file ref in CommonJS needs to use the module alias instead of
229     // the global name.
230     return ModuleAlias(to_message->file()->name()) +
231            GetNestedMessageName(to_message->containing_type()) + "." +
232            to_message->name();
233   } else {
234     // Within a single file we use a full name.
235     return GetMessagePath(options, to_message);
236   }
237 }
238 
SubmessageTypeRef(const GeneratorOptions & options,const FieldDescriptor * field)239 std::string SubmessageTypeRef(const GeneratorOptions& options,
240                               const FieldDescriptor* field) {
241   GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
242   return MaybeCrossFileRef(options, field->file(), field->message_type());
243 }
244 
245 // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
246 // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
247 // and with reserved words triggering a "pb_" prefix.
248 // - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
249 // (use the name directly), then append "List" if appropriate, then append "$"
250 // if resulting name is equal to a reserved word.
251 // - Enums: just uppercase.
252 
253 // Locale-independent version of ToLower that deals only with ASCII A-Z.
ToLowerASCII(char c)254 char ToLowerASCII(char c) {
255   if (c >= 'A' && c <= 'Z') {
256     return (c - 'A') + 'a';
257   } else {
258     return c;
259   }
260 }
261 
ParseLowerUnderscore(const std::string & input)262 std::vector<std::string> ParseLowerUnderscore(const std::string& input) {
263   std::vector<std::string> words;
264   std::string running = "";
265   for (int i = 0; i < input.size(); i++) {
266     if (input[i] == '_') {
267       if (!running.empty()) {
268         words.push_back(running);
269         running.clear();
270       }
271     } else {
272       running += ToLowerASCII(input[i]);
273     }
274   }
275   if (!running.empty()) {
276     words.push_back(running);
277   }
278   return words;
279 }
280 
ParseUpperCamel(const std::string & input)281 std::vector<std::string> ParseUpperCamel(const std::string& input) {
282   std::vector<std::string> words;
283   std::string running = "";
284   for (int i = 0; i < input.size(); i++) {
285     if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
286       words.push_back(running);
287       running.clear();
288     }
289     running += ToLowerASCII(input[i]);
290   }
291   if (!running.empty()) {
292     words.push_back(running);
293   }
294   return words;
295 }
296 
ToLowerCamel(const std::vector<std::string> & words)297 std::string ToLowerCamel(const std::vector<std::string>& words) {
298   std::string result;
299   for (int i = 0; i < words.size(); i++) {
300     std::string word = words[i];
301     if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
302       word[0] = (word[0] - 'A') + 'a';
303     } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
304       word[0] = (word[0] - 'a') + 'A';
305     }
306     result += word;
307   }
308   return result;
309 }
310 
ToUpperCamel(const std::vector<std::string> & words)311 std::string ToUpperCamel(const std::vector<std::string>& words) {
312   std::string result;
313   for (int i = 0; i < words.size(); i++) {
314     std::string word = words[i];
315     if (word[0] >= 'a' && word[0] <= 'z') {
316       word[0] = (word[0] - 'a') + 'A';
317     }
318     result += word;
319   }
320   return result;
321 }
322 
323 // Based on code from descriptor.cc (Thanks Kenton!)
324 // Uppercases the entire string, turning ValueName into
325 // VALUENAME.
ToEnumCase(const std::string & input)326 std::string ToEnumCase(const std::string& input) {
327   std::string result;
328   result.reserve(input.size());
329 
330   for (int i = 0; i < input.size(); i++) {
331     if ('a' <= input[i] && input[i] <= 'z') {
332       result.push_back(input[i] - 'a' + 'A');
333     } else {
334       result.push_back(input[i]);
335     }
336   }
337 
338   return result;
339 }
340 
ToLower(const std::string & input)341 std::string ToLower(const std::string& input) {
342   std::string result;
343   result.reserve(input.size());
344 
345   for (int i = 0; i < input.size(); i++) {
346     if ('A' <= input[i] && input[i] <= 'Z') {
347       result.push_back(input[i] - 'A' + 'a');
348     } else {
349       result.push_back(input[i]);
350     }
351   }
352 
353   return result;
354 }
355 
356 // When we're generating one output file per SCC, this is the filename
357 // that top-level extensions should go in.
358 // e.g. one proto file (test.proto):
359 // package a;
360 // extends Foo {
361 //   ...
362 // }
363 // If "with_filename" equals true, the extension filename will be
364 // "proto.a_test_extensions.js", otherwise will be "proto.a.js"
GetExtensionFileName(const GeneratorOptions & options,const FileDescriptor * file,bool with_filename)365 std::string GetExtensionFileName(const GeneratorOptions& options,
366                                  const FileDescriptor* file,
367                                  bool with_filename) {
368   std::string snake_name = StripProto(GetSnakeFilename(file->name()));
369   return options.output_dir + "/" + ToLower(GetNamespace(options, file)) +
370          (with_filename ? ("_" + snake_name + "_extensions") : "") +
371          options.GetFileNameExtension();
372 }
373 // When we're generating one output file per SCC, this is the filename
374 // that all messages in the SCC should go in.
375 // If with_package equals true, filename will have package prefix,
376 // If the filename length is longer than 200, the filename will be the
377 // SCC's proto filename with suffix "_long_sccs_(index)" (if with_package equals
378 // true it still has package prefix)
GetMessagesFileName(const GeneratorOptions & options,const SCC * scc,bool with_package)379 std::string GetMessagesFileName(const GeneratorOptions& options, const SCC* scc,
380                                 bool with_package) {
381   static std::map<const Descriptor*, std::string>* long_name_dict =
382       new std::map<const Descriptor*, std::string>();
383   std::string package_base =
384       with_package
385           ? ToLower(GetNamespace(options, scc->GetRepresentative()->file()) +
386                     "_")
387           : "";
388   std::string filename_base = "";
389   std::vector<std::string> all_message_names;
390   for (auto one_desc : scc->descriptors) {
391     if (one_desc->containing_type() == nullptr) {
392       all_message_names.push_back(ToLower(one_desc->name()));
393     }
394   }
395   sort(all_message_names.begin(), all_message_names.end());
396   for (auto one_message : all_message_names) {
397     if (!filename_base.empty()) {
398       filename_base += "_";
399     }
400     filename_base += one_message;
401   }
402   if (filename_base.size() + package_base.size() > 200) {
403     if ((*long_name_dict).find(scc->GetRepresentative()) ==
404         (*long_name_dict).end()) {
405       std::string snake_name = StripProto(
406           GetSnakeFilename(scc->GetRepresentative()->file()->name()));
407       (*long_name_dict)[scc->GetRepresentative()] =
408           StrCat(snake_name, "_long_sccs_",
409                  static_cast<uint64>((*long_name_dict).size()));
410     }
411     filename_base = (*long_name_dict)[scc->GetRepresentative()];
412   }
413   return options.output_dir + "/" + package_base + filename_base +
414          options.GetFileNameExtension();
415 }
416 
417 // When we're generating one output file per type name, this is the filename
418 // that a top-level enum should go in.
419 // If with_package equals true, filename will have package prefix.
GetEnumFileName(const GeneratorOptions & options,const EnumDescriptor * desc,bool with_package)420 std::string GetEnumFileName(const GeneratorOptions& options,
421                             const EnumDescriptor* desc, bool with_package) {
422   return options.output_dir + "/" +
423          (with_package ? ToLower(GetNamespace(options, desc->file()) + "_")
424                        : "") +
425          ToLower(desc->name()) + options.GetFileNameExtension();
426 }
427 
428 // Returns the message/response ID, if set.
GetMessageId(const Descriptor * desc)429 std::string GetMessageId(const Descriptor* desc) { return std::string(); }
430 
IgnoreExtensionField(const FieldDescriptor * field)431 bool IgnoreExtensionField(const FieldDescriptor* field) {
432   // Exclude descriptor extensions from output "to avoid clutter" (from original
433   // codegen).
434   if (!field->is_extension()) return false;
435   const FileDescriptor* file = field->containing_type()->file();
436   return file->name() == "net/proto2/proto/descriptor.proto" ||
437          file->name() == "google/protobuf/descriptor.proto";
438 }
439 
440 // Used inside Google only -- do not remove.
IsResponse(const Descriptor * desc)441 bool IsResponse(const Descriptor* desc) { return false; }
442 
IgnoreField(const FieldDescriptor * field)443 bool IgnoreField(const FieldDescriptor* field) {
444   return IgnoreExtensionField(field);
445 }
446 
447 // Do we ignore this message type?
IgnoreMessage(const Descriptor * d)448 bool IgnoreMessage(const Descriptor* d) { return d->options().map_entry(); }
449 
450 // Does JSPB ignore this entire oneof? True only if all fields are ignored.
IgnoreOneof(const OneofDescriptor * oneof)451 bool IgnoreOneof(const OneofDescriptor* oneof) {
452   if (oneof->is_synthetic()) return true;
453   for (int i = 0; i < oneof->field_count(); i++) {
454     if (!IgnoreField(oneof->field(i))) {
455       return false;
456     }
457   }
458   return true;
459 }
460 
JSIdent(const GeneratorOptions & options,const FieldDescriptor * field,bool is_upper_camel,bool is_map,bool drop_list)461 std::string JSIdent(const GeneratorOptions& options,
462                     const FieldDescriptor* field, bool is_upper_camel,
463                     bool is_map, bool drop_list) {
464   std::string result;
465   if (field->type() == FieldDescriptor::TYPE_GROUP) {
466     result = is_upper_camel
467                  ? ToUpperCamel(ParseUpperCamel(field->message_type()->name()))
468                  : ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
469   } else {
470     result = is_upper_camel ? ToUpperCamel(ParseLowerUnderscore(field->name()))
471                             : ToLowerCamel(ParseLowerUnderscore(field->name()));
472   }
473   if (is_map || field->is_map()) {
474     // JSPB-style or proto3-style map.
475     result += "Map";
476   } else if (!drop_list && field->is_repeated()) {
477     // Repeated field.
478     result += "List";
479   }
480   return result;
481 }
482 
JSObjectFieldName(const GeneratorOptions & options,const FieldDescriptor * field)483 std::string JSObjectFieldName(const GeneratorOptions& options,
484                               const FieldDescriptor* field) {
485   std::string name = JSIdent(options, field,
486                              /* is_upper_camel = */ false,
487                              /* is_map = */ false,
488                              /* drop_list = */ false);
489   if (IsReserved(name)) {
490     name = "pb_" + name;
491   }
492   return name;
493 }
494 
JSByteGetterSuffix(BytesMode bytes_mode)495 std::string JSByteGetterSuffix(BytesMode bytes_mode) {
496   switch (bytes_mode) {
497     case BYTES_DEFAULT:
498       return "";
499     case BYTES_B64:
500       return "B64";
501     case BYTES_U8:
502       return "U8";
503     default:
504       assert(false);
505   }
506   return "";
507 }
508 
509 // Returns the field name as a capitalized portion of a getter/setter method
510 // name, e.g. MyField for .getMyField().
JSGetterName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode=BYTES_DEFAULT,bool drop_list=false)511 std::string JSGetterName(const GeneratorOptions& options,
512                          const FieldDescriptor* field,
513                          BytesMode bytes_mode = BYTES_DEFAULT,
514                          bool drop_list = false) {
515   std::string name = JSIdent(options, field,
516                              /* is_upper_camel = */ true,
517                              /* is_map = */ false, drop_list);
518   if (field->type() == FieldDescriptor::TYPE_BYTES) {
519     std::string suffix = JSByteGetterSuffix(bytes_mode);
520     if (!suffix.empty()) {
521       name += "_as" + suffix;
522     }
523   }
524   if (name == "Extension" || name == "JsPbMessageId") {
525     // Avoid conflicts with base-class names.
526     name += "$";
527   }
528   return name;
529 }
530 
JSOneofName(const OneofDescriptor * oneof)531 std::string JSOneofName(const OneofDescriptor* oneof) {
532   return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
533 }
534 
535 // Returns the index corresponding to this field in the JSPB array (underlying
536 // data storage array).
JSFieldIndex(const FieldDescriptor * field)537 std::string JSFieldIndex(const FieldDescriptor* field) {
538   // Determine whether this field is a member of a group. Group fields are a bit
539   // wonky: their "containing type" is a message type created just for the
540   // group, and that type's parent type has a field with the group-message type
541   // as its message type and TYPE_GROUP as its field type. For such fields, the
542   // index we use is relative to the field number of the group submessage field.
543   // For all other fields, we just use the field number.
544   const Descriptor* containing_type = field->containing_type();
545   const Descriptor* parent_type = containing_type->containing_type();
546   if (parent_type != NULL) {
547     for (int i = 0; i < parent_type->field_count(); i++) {
548       if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
549           parent_type->field(i)->message_type() == containing_type) {
550         return StrCat(field->number() - parent_type->field(i)->number());
551       }
552     }
553   }
554   return StrCat(field->number());
555 }
556 
JSOneofIndex(const OneofDescriptor * oneof)557 std::string JSOneofIndex(const OneofDescriptor* oneof) {
558   int index = -1;
559   for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
560     const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
561     if (o->is_synthetic()) continue;
562     // If at least one field in this oneof is not JSPB-ignored, count the oneof.
563     for (int j = 0; j < o->field_count(); j++) {
564       const FieldDescriptor* f = o->field(j);
565       if (!IgnoreField(f)) {
566         index++;
567         break;  // inner loop
568       }
569     }
570     if (o == oneof) {
571       break;
572     }
573   }
574   return StrCat(index);
575 }
576 
577 // Decodes a codepoint in \x0000 -- \xFFFF.
DecodeUTF8Codepoint(uint8 * bytes,size_t * length)578 uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
579   if (*length == 0) {
580     return 0;
581   }
582   size_t expected = 0;
583   if ((*bytes & 0x80) == 0) {
584     expected = 1;
585   } else if ((*bytes & 0xe0) == 0xc0) {
586     expected = 2;
587   } else if ((*bytes & 0xf0) == 0xe0) {
588     expected = 3;
589   } else {
590     // Too long -- don't accept.
591     *length = 0;
592     return 0;
593   }
594 
595   if (*length < expected) {
596     // Not enough bytes -- don't accept.
597     *length = 0;
598     return 0;
599   }
600 
601   *length = expected;
602   switch (expected) {
603     case 1:
604       return bytes[0];
605     case 2:
606       return ((bytes[0] & 0x1F) << 6) | ((bytes[1] & 0x3F) << 0);
607     case 3:
608       return ((bytes[0] & 0x0F) << 12) | ((bytes[1] & 0x3F) << 6) |
609              ((bytes[2] & 0x3F) << 0);
610     default:
611       return 0;
612   }
613 }
614 
615 // Escapes the contents of a string to be included within double-quotes ("") in
616 // JavaScript. The input data should be a UTF-8 encoded C++ string of chars.
617 // Returns false if |out| was truncated because |in| contained invalid UTF-8 or
618 // codepoints outside the BMP.
619 // TODO(b/115551870): Support codepoints outside the BMP.
EscapeJSString(const std::string & in,std::string * out)620 bool EscapeJSString(const std::string& in, std::string* out) {
621   size_t decoded = 0;
622   for (size_t i = 0; i < in.size(); i += decoded) {
623     uint16 codepoint = 0;
624     // Decode the next UTF-8 codepoint.
625     size_t have_bytes = in.size() - i;
626     uint8 bytes[3] = {
627         static_cast<uint8>(in[i]),
628         static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0),
629         static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0),
630     };
631     codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
632     if (have_bytes == 0) {
633       return false;
634     }
635     decoded = have_bytes;
636 
637     switch (codepoint) {
638       case '\'':
639         *out += "\\x27";
640         break;
641       case '"':
642         *out += "\\x22";
643         break;
644       case '<':
645         *out += "\\x3c";
646         break;
647       case '=':
648         *out += "\\x3d";
649         break;
650       case '>':
651         *out += "\\x3e";
652         break;
653       case '&':
654         *out += "\\x26";
655         break;
656       case '\b':
657         *out += "\\b";
658         break;
659       case '\t':
660         *out += "\\t";
661         break;
662       case '\n':
663         *out += "\\n";
664         break;
665       case '\f':
666         *out += "\\f";
667         break;
668       case '\r':
669         *out += "\\r";
670         break;
671       case '\\':
672         *out += "\\\\";
673         break;
674       default:
675         // TODO(b/115551870): Once we're supporting codepoints outside the BMP,
676         // use a single Unicode codepoint escape if the output language is
677         // ECMAScript 2015 or above. Otherwise, use a surrogate pair.
678         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals
679         if (codepoint >= 0x20 && codepoint <= 0x7e) {
680           *out += static_cast<char>(codepoint);
681         } else if (codepoint >= 0x100) {
682           *out += StringPrintf("\\u%04x", codepoint);
683         } else {
684           *out += StringPrintf("\\x%02x", codepoint);
685         }
686         break;
687     }
688   }
689   return true;
690 }
691 
EscapeBase64(const std::string & in)692 std::string EscapeBase64(const std::string& in) {
693   static const char* kAlphabet =
694       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
695   std::string result;
696 
697   for (size_t i = 0; i < in.size(); i += 3) {
698     int value = (in[i] << 16) | (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
699                 (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
700     result += kAlphabet[(value >> 18) & 0x3f];
701     result += kAlphabet[(value >> 12) & 0x3f];
702     if ((i + 1) < in.size()) {
703       result += kAlphabet[(value >> 6) & 0x3f];
704     } else {
705       result += '=';
706     }
707     if ((i + 2) < in.size()) {
708       result += kAlphabet[(value >> 0) & 0x3f];
709     } else {
710       result += '=';
711     }
712   }
713 
714   return result;
715 }
716 
717 // Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
718 // original codegen's formatting (which is just .toString() on java.lang.Double
719 // or java.lang.Float).
PostProcessFloat(std::string result)720 std::string PostProcessFloat(std::string result) {
721   // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
722   if (result == "inf") {
723     return "Infinity";
724   } else if (result == "-inf") {
725     return "-Infinity";
726   } else if (result == "nan") {
727     return "NaN";
728   }
729 
730   // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
731   // ensure that the mantissa (portion prior to the "e") has at least one
732   // fractional digit (after the decimal point), and (iii) strip any unnecessary
733   // leading zeroes and/or '+' signs from the exponent.
734   std::string::size_type exp_pos = result.find('e');
735   if (exp_pos != std::string::npos) {
736     std::string mantissa = result.substr(0, exp_pos);
737     std::string exponent = result.substr(exp_pos + 1);
738 
739     // Add ".0" to mantissa if no fractional part exists.
740     if (mantissa.find('.') == std::string::npos) {
741       mantissa += ".0";
742     }
743 
744     // Strip the sign off the exponent and store as |exp_neg|.
745     bool exp_neg = false;
746     if (!exponent.empty() && exponent[0] == '+') {
747       exponent = exponent.substr(1);
748     } else if (!exponent.empty() && exponent[0] == '-') {
749       exp_neg = true;
750       exponent = exponent.substr(1);
751     }
752 
753     // Strip any leading zeroes off the exponent.
754     while (exponent.size() > 1 && exponent[0] == '0') {
755       exponent = exponent.substr(1);
756     }
757 
758     return mantissa + "E" + std::string(exp_neg ? "-" : "") + exponent;
759   }
760 
761   // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
762   // decimal/fractional part in order to match output of original codegen.
763   if (result.find('.') == std::string::npos) {
764     result += ".0";
765   }
766 
767   return result;
768 }
769 
FloatToString(float value)770 std::string FloatToString(float value) {
771   std::string result = SimpleFtoa(value);
772   return PostProcessFloat(result);
773 }
774 
DoubleToString(double value)775 std::string DoubleToString(double value) {
776   std::string result = SimpleDtoa(value);
777   return PostProcessFloat(result);
778 }
779 
InRealOneof(const FieldDescriptor * field)780 bool InRealOneof(const FieldDescriptor* field) {
781   return field->containing_oneof() &&
782          !field->containing_oneof()->is_synthetic();
783 }
784 
785 // Return true if this is an integral field that should be represented as string
786 // in JS.
IsIntegralFieldWithStringJSType(const FieldDescriptor * field)787 bool IsIntegralFieldWithStringJSType(const FieldDescriptor* field) {
788   switch (field->cpp_type()) {
789     case FieldDescriptor::CPPTYPE_INT64:
790     case FieldDescriptor::CPPTYPE_UINT64:
791       // The default value of JSType is JS_NORMAL, which behaves the same as
792       // JS_NUMBER.
793       return field->options().jstype() == FieldOptions::JS_STRING;
794     default:
795       return false;
796   }
797 }
798 
MaybeNumberString(const FieldDescriptor * field,const std::string & orig)799 std::string MaybeNumberString(const FieldDescriptor* field,
800                               const std::string& orig) {
801   return IsIntegralFieldWithStringJSType(field) ? ("\"" + orig + "\"") : orig;
802 }
803 
JSFieldDefault(const FieldDescriptor * field)804 std::string JSFieldDefault(const FieldDescriptor* field) {
805   if (field->is_repeated()) {
806     return "[]";
807   }
808 
809   switch (field->cpp_type()) {
810     case FieldDescriptor::CPPTYPE_INT32:
811       return MaybeNumberString(field, StrCat(field->default_value_int32()));
812     case FieldDescriptor::CPPTYPE_UINT32:
813       // The original codegen is in Java, and Java protobufs store unsigned
814       // integer values as signed integer values. In order to exactly match the
815       // output, we need to reinterpret as base-2 signed. Ugh.
816       return MaybeNumberString(
817           field, StrCat(static_cast<int32>(field->default_value_uint32())));
818     case FieldDescriptor::CPPTYPE_INT64:
819       return MaybeNumberString(field, StrCat(field->default_value_int64()));
820     case FieldDescriptor::CPPTYPE_UINT64:
821       // See above note for uint32 -- reinterpreting as signed.
822       return MaybeNumberString(
823           field, StrCat(static_cast<int64>(field->default_value_uint64())));
824     case FieldDescriptor::CPPTYPE_ENUM:
825       return StrCat(field->default_value_enum()->number());
826     case FieldDescriptor::CPPTYPE_BOOL:
827       return field->default_value_bool() ? "true" : "false";
828     case FieldDescriptor::CPPTYPE_FLOAT:
829       return FloatToString(field->default_value_float());
830     case FieldDescriptor::CPPTYPE_DOUBLE:
831       return DoubleToString(field->default_value_double());
832     case FieldDescriptor::CPPTYPE_STRING:
833       if (field->type() == FieldDescriptor::TYPE_STRING) {
834         std::string out;
835         bool is_valid = EscapeJSString(field->default_value_string(), &out);
836         if (!is_valid) {
837           // TODO(b/115551870): Decide whether this should be a hard error.
838           GOOGLE_LOG(WARNING)
839               << "The default value for field " << field->full_name()
840               << " was truncated since it contained invalid UTF-8 or"
841                  " codepoints outside the basic multilingual plane.";
842         }
843         return "\"" + out + "\"";
844       } else {  // Bytes
845         return "\"" + EscapeBase64(field->default_value_string()) + "\"";
846       }
847     case FieldDescriptor::CPPTYPE_MESSAGE:
848       return "null";
849   }
850   GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
851   return "";
852 }
853 
ProtoTypeName(const GeneratorOptions & options,const FieldDescriptor * field)854 std::string ProtoTypeName(const GeneratorOptions& options,
855                           const FieldDescriptor* field) {
856   switch (field->type()) {
857     case FieldDescriptor::TYPE_BOOL:
858       return "bool";
859     case FieldDescriptor::TYPE_INT32:
860       return "int32";
861     case FieldDescriptor::TYPE_UINT32:
862       return "uint32";
863     case FieldDescriptor::TYPE_SINT32:
864       return "sint32";
865     case FieldDescriptor::TYPE_FIXED32:
866       return "fixed32";
867     case FieldDescriptor::TYPE_SFIXED32:
868       return "sfixed32";
869     case FieldDescriptor::TYPE_INT64:
870       return "int64";
871     case FieldDescriptor::TYPE_UINT64:
872       return "uint64";
873     case FieldDescriptor::TYPE_SINT64:
874       return "sint64";
875     case FieldDescriptor::TYPE_FIXED64:
876       return "fixed64";
877     case FieldDescriptor::TYPE_SFIXED64:
878       return "sfixed64";
879     case FieldDescriptor::TYPE_FLOAT:
880       return "float";
881     case FieldDescriptor::TYPE_DOUBLE:
882       return "double";
883     case FieldDescriptor::TYPE_STRING:
884       return "string";
885     case FieldDescriptor::TYPE_BYTES:
886       return "bytes";
887     case FieldDescriptor::TYPE_GROUP:
888       return GetMessagePath(options, field->message_type());
889     case FieldDescriptor::TYPE_ENUM:
890       return GetEnumPath(options, field->enum_type());
891     case FieldDescriptor::TYPE_MESSAGE:
892       return GetMessagePath(options, field->message_type());
893     default:
894       return "";
895   }
896 }
897 
JSIntegerTypeName(const FieldDescriptor * field)898 std::string JSIntegerTypeName(const FieldDescriptor* field) {
899   return IsIntegralFieldWithStringJSType(field) ? "string" : "number";
900 }
901 
JSStringTypeName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode)902 std::string JSStringTypeName(const GeneratorOptions& options,
903                              const FieldDescriptor* field,
904                              BytesMode bytes_mode) {
905   if (field->type() == FieldDescriptor::TYPE_BYTES) {
906     switch (bytes_mode) {
907       case BYTES_DEFAULT:
908         return "(string|Uint8Array)";
909       case BYTES_B64:
910         return "string";
911       case BYTES_U8:
912         return "Uint8Array";
913       default:
914         assert(false);
915     }
916   }
917   return "string";
918 }
919 
JSTypeName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode)920 std::string JSTypeName(const GeneratorOptions& options,
921                        const FieldDescriptor* field, BytesMode bytes_mode) {
922   switch (field->cpp_type()) {
923     case FieldDescriptor::CPPTYPE_BOOL:
924       return "boolean";
925     case FieldDescriptor::CPPTYPE_INT32:
926       return JSIntegerTypeName(field);
927     case FieldDescriptor::CPPTYPE_INT64:
928       return JSIntegerTypeName(field);
929     case FieldDescriptor::CPPTYPE_UINT32:
930       return JSIntegerTypeName(field);
931     case FieldDescriptor::CPPTYPE_UINT64:
932       return JSIntegerTypeName(field);
933     case FieldDescriptor::CPPTYPE_FLOAT:
934       return "number";
935     case FieldDescriptor::CPPTYPE_DOUBLE:
936       return "number";
937     case FieldDescriptor::CPPTYPE_STRING:
938       return JSStringTypeName(options, field, bytes_mode);
939     case FieldDescriptor::CPPTYPE_ENUM:
940       return GetEnumPath(options, field->enum_type());
941     case FieldDescriptor::CPPTYPE_MESSAGE:
942       return GetMessagePath(options, field->message_type());
943     default:
944       return "";
945   }
946 }
947 
948 // Used inside Google only -- do not remove.
UseBrokenPresenceSemantics(const GeneratorOptions & options,const FieldDescriptor * field)949 bool UseBrokenPresenceSemantics(const GeneratorOptions& options,
950                                 const FieldDescriptor* field) {
951   return false;
952 }
953 
954 // Returns true for fields that return "null" from accessors when they are
955 // unset. This should normally only be true for non-repeated submessages, but we
956 // have legacy users who relied on old behavior where accessors behaved this
957 // way.
ReturnsNullWhenUnset(const GeneratorOptions & options,const FieldDescriptor * field)958 bool ReturnsNullWhenUnset(const GeneratorOptions& options,
959                           const FieldDescriptor* field) {
960   if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
961       field->is_optional()) {
962     return true;
963   }
964 
965   // TODO(haberman): remove this case and unconditionally return false.
966   return UseBrokenPresenceSemantics(options, field) && !field->is_repeated() &&
967          !field->has_default_value();
968 }
969 
970 // In a sane world, this would be the same as ReturnsNullWhenUnset().  But in
971 // the status quo, some fields declare that they never return null/undefined
972 // even though they actually do:
973 //   * required fields
974 //   * optional enum fields
975 //   * proto3 primitive fields.
DeclaredReturnTypeIsNullable(const GeneratorOptions & options,const FieldDescriptor * field)976 bool DeclaredReturnTypeIsNullable(const GeneratorOptions& options,
977                                   const FieldDescriptor* field) {
978   if (field->is_required() || field->type() == FieldDescriptor::TYPE_ENUM) {
979     return false;
980   }
981 
982   if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
983       field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
984     return false;
985   }
986 
987   return ReturnsNullWhenUnset(options, field);
988 }
989 
SetterAcceptsUndefined(const GeneratorOptions & options,const FieldDescriptor * field)990 bool SetterAcceptsUndefined(const GeneratorOptions& options,
991                             const FieldDescriptor* field) {
992   if (ReturnsNullWhenUnset(options, field)) {
993     return true;
994   }
995 
996   // Broken presence semantics always accepts undefined for setters.
997   return UseBrokenPresenceSemantics(options, field);
998 }
999 
SetterAcceptsNull(const GeneratorOptions & options,const FieldDescriptor * field)1000 bool SetterAcceptsNull(const GeneratorOptions& options,
1001                        const FieldDescriptor* field) {
1002   if (ReturnsNullWhenUnset(options, field)) {
1003     return true;
1004   }
1005 
1006   // With broken presence semantics, fields with defaults accept "null" for
1007   // setters, but other fields do not.  This is a strange quirk of the old
1008   // codegen.
1009   return UseBrokenPresenceSemantics(options, field) &&
1010          field->has_default_value();
1011 }
1012 
1013 // Returns types which are known to by non-nullable by default.
1014 // The style guide requires that we omit "!" in this case.
IsPrimitive(const std::string & type)1015 bool IsPrimitive(const std::string& type) {
1016   return type == "undefined" || type == "string" || type == "number" ||
1017          type == "boolean";
1018 }
1019 
JSFieldTypeAnnotation(const GeneratorOptions & options,const FieldDescriptor * field,bool is_setter_argument,bool force_present,bool singular_if_not_packed,BytesMode bytes_mode=BYTES_DEFAULT,bool force_singular=false)1020 std::string JSFieldTypeAnnotation(const GeneratorOptions& options,
1021                                   const FieldDescriptor* field,
1022                                   bool is_setter_argument, bool force_present,
1023                                   bool singular_if_not_packed,
1024                                   BytesMode bytes_mode = BYTES_DEFAULT,
1025                                   bool force_singular = false) {
1026   std::string jstype = JSTypeName(options, field, bytes_mode);
1027 
1028   if (!force_singular && field->is_repeated() &&
1029       (field->is_packed() || !singular_if_not_packed)) {
1030     if (field->type() == FieldDescriptor::TYPE_BYTES &&
1031         bytes_mode == BYTES_DEFAULT) {
1032       jstype = "(Array<!Uint8Array>|Array<string>)";
1033     } else {
1034       if (!IsPrimitive(jstype)) {
1035         jstype = "!" + jstype;
1036       }
1037       jstype = "Array<" + jstype + ">";
1038     }
1039   }
1040 
1041   bool is_null_or_undefined = false;
1042 
1043   if (is_setter_argument) {
1044     if (SetterAcceptsNull(options, field)) {
1045       jstype = "?" + jstype;
1046       is_null_or_undefined = true;
1047     }
1048 
1049     if (SetterAcceptsUndefined(options, field)) {
1050       jstype += "|undefined";
1051       is_null_or_undefined = true;
1052     }
1053   } else if (force_present) {
1054     // Don't add null or undefined.
1055   } else {
1056     if (DeclaredReturnTypeIsNullable(options, field)) {
1057       jstype = "?" + jstype;
1058       is_null_or_undefined = true;
1059     }
1060   }
1061 
1062   if (!is_null_or_undefined && !IsPrimitive(jstype)) {
1063     jstype = "!" + jstype;
1064   }
1065 
1066   return jstype;
1067 }
1068 
JSBinaryReaderMethodType(const FieldDescriptor * field)1069 std::string JSBinaryReaderMethodType(const FieldDescriptor* field) {
1070   std::string name = field->type_name();
1071   if (name[0] >= 'a' && name[0] <= 'z') {
1072     name[0] = (name[0] - 'a') + 'A';
1073   }
1074   return IsIntegralFieldWithStringJSType(field) ? (name + "String") : name;
1075 }
1076 
JSBinaryReadWriteMethodName(const FieldDescriptor * field,bool is_writer)1077 std::string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
1078                                         bool is_writer) {
1079   std::string name = JSBinaryReaderMethodType(field);
1080   if (field->is_packed()) {
1081     name = "Packed" + name;
1082   } else if (is_writer && field->is_repeated()) {
1083     name = "Repeated" + name;
1084   }
1085   return name;
1086 }
1087 
JSBinaryReaderMethodName(const GeneratorOptions & options,const FieldDescriptor * field)1088 std::string JSBinaryReaderMethodName(const GeneratorOptions& options,
1089                                      const FieldDescriptor* field) {
1090   return "jspb.BinaryReader.prototype.read" +
1091          JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
1092 }
1093 
JSBinaryWriterMethodName(const GeneratorOptions & options,const FieldDescriptor * field)1094 std::string JSBinaryWriterMethodName(const GeneratorOptions& options,
1095                                      const FieldDescriptor* field) {
1096   if (field->containing_type() &&
1097       field->containing_type()->options().message_set_wire_format()) {
1098     return "jspb.BinaryWriter.prototype.writeMessageSet";
1099   }
1100   return "jspb.BinaryWriter.prototype.write" +
1101          JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
1102 }
1103 
JSTypeTag(const FieldDescriptor * desc)1104 std::string JSTypeTag(const FieldDescriptor* desc) {
1105   switch (desc->type()) {
1106     case FieldDescriptor::TYPE_DOUBLE:
1107     case FieldDescriptor::TYPE_FLOAT:
1108       return "Float";
1109     case FieldDescriptor::TYPE_INT32:
1110     case FieldDescriptor::TYPE_UINT32:
1111     case FieldDescriptor::TYPE_INT64:
1112     case FieldDescriptor::TYPE_UINT64:
1113     case FieldDescriptor::TYPE_FIXED32:
1114     case FieldDescriptor::TYPE_FIXED64:
1115     case FieldDescriptor::TYPE_SINT32:
1116     case FieldDescriptor::TYPE_SINT64:
1117     case FieldDescriptor::TYPE_SFIXED32:
1118     case FieldDescriptor::TYPE_SFIXED64:
1119       if (IsIntegralFieldWithStringJSType(desc)) {
1120         return "StringInt";
1121       } else {
1122         return "Int";
1123       }
1124     case FieldDescriptor::TYPE_BOOL:
1125       return "Boolean";
1126     case FieldDescriptor::TYPE_STRING:
1127       return "String";
1128     case FieldDescriptor::TYPE_BYTES:
1129       return "Bytes";
1130     case FieldDescriptor::TYPE_ENUM:
1131       return "Enum";
1132     default:
1133       assert(false);
1134   }
1135   return "";
1136 }
1137 
HasRepeatedFields(const GeneratorOptions & options,const Descriptor * desc)1138 bool HasRepeatedFields(const GeneratorOptions& options,
1139                        const Descriptor* desc) {
1140   for (int i = 0; i < desc->field_count(); i++) {
1141     if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
1142       return true;
1143     }
1144   }
1145   return false;
1146 }
1147 
1148 static const char* kRepeatedFieldArrayName = ".repeatedFields_";
1149 
RepeatedFieldsArrayName(const GeneratorOptions & options,const Descriptor * desc)1150 std::string RepeatedFieldsArrayName(const GeneratorOptions& options,
1151                                     const Descriptor* desc) {
1152   return HasRepeatedFields(options, desc)
1153              ? (GetMessagePath(options, desc) + kRepeatedFieldArrayName)
1154              : "null";
1155 }
1156 
HasOneofFields(const Descriptor * desc)1157 bool HasOneofFields(const Descriptor* desc) {
1158   for (int i = 0; i < desc->field_count(); i++) {
1159     if (InRealOneof(desc->field(i))) {
1160       return true;
1161     }
1162   }
1163   return false;
1164 }
1165 
1166 static const char* kOneofGroupArrayName = ".oneofGroups_";
1167 
OneofFieldsArrayName(const GeneratorOptions & options,const Descriptor * desc)1168 std::string OneofFieldsArrayName(const GeneratorOptions& options,
1169                                  const Descriptor* desc) {
1170   return HasOneofFields(desc)
1171              ? (GetMessagePath(options, desc) + kOneofGroupArrayName)
1172              : "null";
1173 }
1174 
RepeatedFieldNumberList(const GeneratorOptions & options,const Descriptor * desc)1175 std::string RepeatedFieldNumberList(const GeneratorOptions& options,
1176                                     const Descriptor* desc) {
1177   std::vector<std::string> numbers;
1178   for (int i = 0; i < desc->field_count(); i++) {
1179     if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
1180       numbers.push_back(JSFieldIndex(desc->field(i)));
1181     }
1182   }
1183   return "[" + Join(numbers, ",") + "]";
1184 }
1185 
OneofGroupList(const Descriptor * desc)1186 std::string OneofGroupList(const Descriptor* desc) {
1187   // List of arrays (one per oneof), each of which is a list of field indices
1188   std::vector<std::string> oneof_entries;
1189   for (int i = 0; i < desc->oneof_decl_count(); i++) {
1190     const OneofDescriptor* oneof = desc->oneof_decl(i);
1191     if (IgnoreOneof(oneof)) {
1192       continue;
1193     }
1194 
1195     std::vector<std::string> oneof_fields;
1196     for (int j = 0; j < oneof->field_count(); j++) {
1197       if (IgnoreField(oneof->field(j))) {
1198         continue;
1199       }
1200       oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
1201     }
1202     oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
1203   }
1204   return "[" + Join(oneof_entries, ",") + "]";
1205 }
1206 
JSOneofArray(const GeneratorOptions & options,const FieldDescriptor * field)1207 std::string JSOneofArray(const GeneratorOptions& options,
1208                          const FieldDescriptor* field) {
1209   return OneofFieldsArrayName(options, field->containing_type()) + "[" +
1210          JSOneofIndex(field->containing_oneof()) + "]";
1211 }
1212 
RelativeTypeName(const FieldDescriptor * field)1213 std::string RelativeTypeName(const FieldDescriptor* field) {
1214   assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
1215          field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
1216   // For a field with an enum or message type, compute a name relative to the
1217   // path name of the message type containing this field.
1218   std::string package = field->file()->package();
1219   std::string containing_type = field->containing_type()->full_name() + ".";
1220   std::string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM)
1221                          ? field->enum_type()->full_name()
1222                          : field->message_type()->full_name();
1223 
1224   // |prefix| is advanced as we find separators '.' past the common package
1225   // prefix that yield common prefixes in the containing type's name and this
1226   // type's name.
1227   int prefix = 0;
1228   for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
1229     if (type[i] != containing_type[i]) {
1230       break;
1231     }
1232     if (type[i] == '.' && i >= package.size()) {
1233       prefix = i + 1;
1234     }
1235   }
1236 
1237   return type.substr(prefix);
1238 }
1239 
JSExtensionsObjectName(const GeneratorOptions & options,const FileDescriptor * from_file,const Descriptor * desc)1240 std::string JSExtensionsObjectName(const GeneratorOptions& options,
1241                                    const FileDescriptor* from_file,
1242                                    const Descriptor* desc) {
1243   if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
1244     // TODO(haberman): fix this for the kImportCommonJs case.
1245     return "jspb.Message.messageSetExtensions";
1246   } else {
1247     return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
1248   }
1249 }
1250 
1251 static const int kMapKeyField = 1;
1252 static const int kMapValueField = 2;
1253 
MapFieldKey(const FieldDescriptor * field)1254 const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) {
1255   assert(field->is_map());
1256   return field->message_type()->FindFieldByNumber(kMapKeyField);
1257 }
1258 
MapFieldValue(const FieldDescriptor * field)1259 const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) {
1260   assert(field->is_map());
1261   return field->message_type()->FindFieldByNumber(kMapValueField);
1262 }
1263 
FieldDefinition(const GeneratorOptions & options,const FieldDescriptor * field)1264 std::string FieldDefinition(const GeneratorOptions& options,
1265                             const FieldDescriptor* field) {
1266   if (field->is_map()) {
1267     const FieldDescriptor* key_field = MapFieldKey(field);
1268     const FieldDescriptor* value_field = MapFieldValue(field);
1269     std::string key_type = ProtoTypeName(options, key_field);
1270     std::string value_type;
1271     if (value_field->type() == FieldDescriptor::TYPE_ENUM ||
1272         value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
1273       value_type = RelativeTypeName(value_field);
1274     } else {
1275       value_type = ProtoTypeName(options, value_field);
1276     }
1277     return StringPrintf("map<%s, %s> %s = %d;", key_type.c_str(),
1278                         value_type.c_str(), field->name().c_str(),
1279                         field->number());
1280   } else {
1281     std::string qualifier =
1282         field->is_repeated() ? "repeated"
1283                              : (field->is_optional() ? "optional" : "required");
1284     std::string type, name;
1285     if (field->type() == FieldDescriptor::TYPE_ENUM ||
1286         field->type() == FieldDescriptor::TYPE_MESSAGE) {
1287       type = RelativeTypeName(field);
1288       name = field->name();
1289     } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
1290       type = "group";
1291       name = field->message_type()->name();
1292     } else {
1293       type = ProtoTypeName(options, field);
1294       name = field->name();
1295     }
1296     return StringPrintf("%s %s %s = %d;", qualifier.c_str(), type.c_str(),
1297                         name.c_str(), field->number());
1298   }
1299 }
1300 
FieldComments(const FieldDescriptor * field,BytesMode bytes_mode)1301 std::string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
1302   std::string comments;
1303   if (field->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) {
1304     comments +=
1305         " * Note that Uint8Array is not supported on all browsers.\n"
1306         " * @see http://caniuse.com/Uint8Array\n";
1307   }
1308   return comments;
1309 }
1310 
ShouldGenerateExtension(const FieldDescriptor * field)1311 bool ShouldGenerateExtension(const FieldDescriptor* field) {
1312   return field->is_extension() && !IgnoreField(field);
1313 }
1314 
HasExtensions(const Descriptor * desc)1315 bool HasExtensions(const Descriptor* desc) {
1316   for (int i = 0; i < desc->extension_count(); i++) {
1317     if (ShouldGenerateExtension(desc->extension(i))) {
1318       return true;
1319     }
1320   }
1321   for (int i = 0; i < desc->nested_type_count(); i++) {
1322     if (HasExtensions(desc->nested_type(i))) {
1323       return true;
1324     }
1325   }
1326   return false;
1327 }
1328 
HasExtensions(const FileDescriptor * file)1329 bool HasExtensions(const FileDescriptor* file) {
1330   for (int i = 0; i < file->extension_count(); i++) {
1331     if (ShouldGenerateExtension(file->extension(i))) {
1332       return true;
1333     }
1334   }
1335   for (int i = 0; i < file->message_type_count(); i++) {
1336     if (HasExtensions(file->message_type(i))) {
1337       return true;
1338     }
1339   }
1340   return false;
1341 }
1342 
HasMap(const GeneratorOptions & options,const Descriptor * desc)1343 bool HasMap(const GeneratorOptions& options, const Descriptor* desc) {
1344   for (int i = 0; i < desc->field_count(); i++) {
1345     if (desc->field(i)->is_map()) {
1346       return true;
1347     }
1348   }
1349   for (int i = 0; i < desc->nested_type_count(); i++) {
1350     if (HasMap(options, desc->nested_type(i))) {
1351       return true;
1352     }
1353   }
1354   return false;
1355 }
1356 
FileHasMap(const GeneratorOptions & options,const FileDescriptor * desc)1357 bool FileHasMap(const GeneratorOptions& options, const FileDescriptor* desc) {
1358   for (int i = 0; i < desc->message_type_count(); i++) {
1359     if (HasMap(options, desc->message_type(i))) {
1360       return true;
1361     }
1362   }
1363   return false;
1364 }
1365 
IsExtendable(const Descriptor * desc)1366 bool IsExtendable(const Descriptor* desc) {
1367   return desc->extension_range_count() > 0;
1368 }
1369 
1370 // Returns the max index in the underlying data storage array beyond which the
1371 // extension object is used.
GetPivot(const Descriptor * desc)1372 std::string GetPivot(const Descriptor* desc) {
1373   static const int kDefaultPivot = 500;
1374 
1375   // Find the max field number
1376   int max_field_number = 0;
1377   for (int i = 0; i < desc->field_count(); i++) {
1378     if (!IgnoreField(desc->field(i)) &&
1379         desc->field(i)->number() > max_field_number) {
1380       max_field_number = desc->field(i)->number();
1381     }
1382   }
1383 
1384   int pivot = -1;
1385   if (IsExtendable(desc) || (max_field_number >= kDefaultPivot)) {
1386     pivot = ((max_field_number + 1) < kDefaultPivot) ? (max_field_number + 1)
1387                                                      : kDefaultPivot;
1388   }
1389 
1390   return StrCat(pivot);
1391 }
1392 
1393 // Whether this field represents presence.  For fields with presence, we
1394 // generate extra methods (clearFoo() and hasFoo()) for this field.
HasFieldPresence(const GeneratorOptions & options,const FieldDescriptor * field)1395 bool HasFieldPresence(const GeneratorOptions& options,
1396                       const FieldDescriptor* field) {
1397   // This returns false for repeated fields and maps, but we still do
1398   // generate clearFoo() methods for these through a special case elsewhere.
1399   return field->has_presence();
1400 }
1401 
1402 // We use this to implement the semantics that same file can be generated
1403 // multiple times, but only the last one keep the short name. Others all use
1404 // long name with extra information to distinguish (For message and enum, the
1405 // extra information is package name, for file level extension, the extra
1406 // information is proto's filename).
1407 // We never actually write the files, but we keep a set of which descriptors
1408 // were the final one for a given filename.
1409 class FileDeduplicator {
1410  public:
FileDeduplicator(const GeneratorOptions & options)1411   explicit FileDeduplicator(const GeneratorOptions& options) {}
1412 
1413   // params:
1414   //   filenames: a pair of {short filename, full filename}
1415   //              (short filename don't have extra information, full filename
1416   //               contains extra information)
1417   //   desc: The Descriptor or SCC pointer or EnumDescriptor.
AddFile(const std::pair<std::string,std::string> filenames,const void * desc)1418   bool AddFile(const std::pair<std::string, std::string> filenames,
1419                const void* desc) {
1420     if (descs_by_shortname_.find(filenames.first) !=
1421         descs_by_shortname_.end()) {
1422       // Change old pointer's actual name to full name.
1423       auto short_name_desc = descs_by_shortname_[filenames.first];
1424       allowed_descs_actual_name_[short_name_desc] =
1425           allowed_descs_full_name_[short_name_desc];
1426     }
1427     descs_by_shortname_[filenames.first] = desc;
1428     allowed_descs_actual_name_[desc] = filenames.first;
1429     allowed_descs_full_name_[desc] = filenames.second;
1430 
1431     return true;
1432   }
1433 
GetAllowedMap(std::map<const void *,std::string> * allowed_set)1434   void GetAllowedMap(std::map<const void*, std::string>* allowed_set) {
1435     *allowed_set = allowed_descs_actual_name_;
1436   }
1437 
1438  private:
1439   // The map that restores all the descs that are using short name as filename.
1440   std::map<std::string, const void*> descs_by_shortname_;
1441   // The final actual filename map.
1442   std::map<const void*, std::string> allowed_descs_actual_name_;
1443   // The full name map.
1444   std::map<const void*, std::string> allowed_descs_full_name_;
1445 };
1446 
DepthFirstSearch(const FileDescriptor * file,std::vector<const FileDescriptor * > * list,std::set<const FileDescriptor * > * seen)1447 void DepthFirstSearch(const FileDescriptor* file,
1448                       std::vector<const FileDescriptor*>* list,
1449                       std::set<const FileDescriptor*>* seen) {
1450   if (!seen->insert(file).second) {
1451     return;
1452   }
1453 
1454   // Add all dependencies.
1455   for (int i = 0; i < file->dependency_count(); i++) {
1456     DepthFirstSearch(file->dependency(i), list, seen);
1457   }
1458 
1459   // Add this file.
1460   list->push_back(file);
1461 }
1462 
1463 // A functor for the predicate to remove_if() below.  Returns true if a given
1464 // FileDescriptor is not in the given set.
1465 class NotInSet {
1466  public:
NotInSet(const std::set<const FileDescriptor * > & file_set)1467   explicit NotInSet(const std::set<const FileDescriptor*>& file_set)
1468       : file_set_(file_set) {}
1469 
operator ()(const FileDescriptor * file)1470   bool operator()(const FileDescriptor* file) {
1471     return file_set_.count(file) == 0;
1472   }
1473 
1474  private:
1475   const std::set<const FileDescriptor*>& file_set_;
1476 };
1477 
1478 // This function generates an ordering of the input FileDescriptors that matches
1479 // the logic of the old code generator.  The order is significant because two
1480 // different input files can generate the same output file, and the last one
1481 // needs to win.
GenerateJspbFileOrder(const std::vector<const FileDescriptor * > & input,std::vector<const FileDescriptor * > * ordered)1482 void GenerateJspbFileOrder(const std::vector<const FileDescriptor*>& input,
1483                            std::vector<const FileDescriptor*>* ordered) {
1484   // First generate an ordering of all reachable files (including dependencies)
1485   // with depth-first search.  This mimics the behavior of --include_imports,
1486   // which is what the old codegen used.
1487   ordered->clear();
1488   std::set<const FileDescriptor*> seen;
1489   std::set<const FileDescriptor*> input_set;
1490   for (int i = 0; i < input.size(); i++) {
1491     DepthFirstSearch(input[i], ordered, &seen);
1492     input_set.insert(input[i]);
1493   }
1494 
1495   // Now remove the entries that are not actually in our input list.
1496   ordered->erase(
1497       std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)),
1498       ordered->end());
1499 }
1500 
1501 // If we're generating code in file-per-type mode, avoid overwriting files
1502 // by choosing the last descriptor that writes each filename and permitting
1503 // only those to generate code.
1504 
1505 struct DepsGenerator {
operator ()google::protobuf::compiler::js::__anon16c20ed90111::DepsGenerator1506   std::vector<const Descriptor*> operator()(const Descriptor* desc) const {
1507     std::vector<const Descriptor*> deps;
1508     auto maybe_add = [&](const Descriptor* d) {
1509       if (d) deps.push_back(d);
1510     };
1511     for (int i = 0; i < desc->field_count(); i++) {
1512       if (!IgnoreField(desc->field(i))) {
1513         maybe_add(desc->field(i)->message_type());
1514       }
1515     }
1516     for (int i = 0; i < desc->extension_count(); i++) {
1517       maybe_add(desc->extension(i)->message_type());
1518       maybe_add(desc->extension(i)->containing_type());
1519     }
1520     for (int i = 0; i < desc->nested_type_count(); i++) {
1521       maybe_add(desc->nested_type(i));
1522     }
1523     maybe_add(desc->containing_type());
1524 
1525     return deps;
1526   }
1527 };
1528 
GenerateJspbAllowedMap(const GeneratorOptions & options,const std::vector<const FileDescriptor * > & files,std::map<const void *,std::string> * allowed_set,SCCAnalyzer<DepsGenerator> * analyzer)1529 bool GenerateJspbAllowedMap(const GeneratorOptions& options,
1530                             const std::vector<const FileDescriptor*>& files,
1531                             std::map<const void*, std::string>* allowed_set,
1532                             SCCAnalyzer<DepsGenerator>* analyzer) {
1533   std::vector<const FileDescriptor*> files_ordered;
1534   GenerateJspbFileOrder(files, &files_ordered);
1535 
1536   // Choose the last descriptor for each filename.
1537   FileDeduplicator dedup(options);
1538   std::set<const SCC*> added;
1539   for (int i = 0; i < files_ordered.size(); i++) {
1540     for (int j = 0; j < files_ordered[i]->message_type_count(); j++) {
1541       const Descriptor* desc = files_ordered[i]->message_type(j);
1542       if (added.insert(analyzer->GetSCC(desc)).second &&
1543           !dedup.AddFile(
1544               std::make_pair(
1545                   GetMessagesFileName(options, analyzer->GetSCC(desc), false),
1546                   GetMessagesFileName(options, analyzer->GetSCC(desc), true)),
1547               analyzer->GetSCC(desc))) {
1548         return false;
1549       }
1550     }
1551     for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) {
1552       const EnumDescriptor* desc = files_ordered[i]->enum_type(j);
1553       if (!dedup.AddFile(std::make_pair(GetEnumFileName(options, desc, false),
1554                                         GetEnumFileName(options, desc, true)),
1555                          desc)) {
1556         return false;
1557       }
1558     }
1559 
1560     // Pull out all free-floating extensions and generate files for those too.
1561     bool has_extension = false;
1562 
1563     for (int j = 0; j < files_ordered[i]->extension_count(); j++) {
1564       if (ShouldGenerateExtension(files_ordered[i]->extension(j))) {
1565         has_extension = true;
1566       }
1567     }
1568 
1569     if (has_extension) {
1570       if (!dedup.AddFile(
1571               std::make_pair(
1572                   GetExtensionFileName(options, files_ordered[i], false),
1573                   GetExtensionFileName(options, files_ordered[i], true)),
1574               files_ordered[i])) {
1575         return false;
1576       }
1577     }
1578   }
1579 
1580   dedup.GetAllowedMap(allowed_set);
1581 
1582   return true;
1583 }
1584 
1585 // Embeds base64 encoded GeneratedCodeInfo proto in a comment at the end of
1586 // file.
EmbedCodeAnnotations(const GeneratedCodeInfo & annotations,io::Printer * printer)1587 void EmbedCodeAnnotations(const GeneratedCodeInfo& annotations,
1588                           io::Printer* printer) {
1589   // Serialize annotations proto into base64 string.
1590   std::string meta_content;
1591   annotations.SerializeToString(&meta_content);
1592   std::string meta_64;
1593   Base64Escape(meta_content, &meta_64);
1594 
1595   // Print base64 encoded annotations at the end of output file in
1596   // a comment.
1597   printer->Print("\n// Below is base64 encoded GeneratedCodeInfo proto");
1598   printer->Print("\n// $encoded_proto$\n", "encoded_proto", meta_64);
1599 }
1600 
IsWellKnownTypeFile(const FileDescriptor * file)1601 bool IsWellKnownTypeFile(const FileDescriptor* file) {
1602   return HasPrefixString(file->name(), "google/protobuf/");
1603 }
1604 
1605 }  // anonymous namespace
1606 
GenerateHeader(const GeneratorOptions & options,const FileDescriptor * file,io::Printer * printer) const1607 void Generator::GenerateHeader(const GeneratorOptions& options,
1608                                const FileDescriptor* file,
1609                                io::Printer* printer) const {
1610   if (file != nullptr) {
1611     printer->Print("// source: $filename$\n", "filename", file->name());
1612   }
1613   printer->Print(
1614       "/**\n"
1615       " * @fileoverview\n"
1616       " * @enhanceable\n"
1617       // TODO(b/152440355): requireType/requires diverged from internal version.
1618       " * @suppress {missingRequire} reports error on implicit type usages.\n"
1619       " * @suppress {messageConventions} JS Compiler reports an "
1620       "error if a variable or\n"
1621       " *     field starts with 'MSG_' and isn't a translatable "
1622       "message.\n"
1623       " * @public\n"
1624       " */\n"
1625       "// GENERATED CODE -- DO NOT EDIT!\n"
1626       "/* eslint-disable */\n"
1627       "// @ts-nocheck\n"
1628       "\n");
1629 }
1630 
FindProvidesForFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file,std::set<std::string> * provided) const1631 void Generator::FindProvidesForFile(const GeneratorOptions& options,
1632                                     io::Printer* printer,
1633                                     const FileDescriptor* file,
1634                                     std::set<std::string>* provided) const {
1635   for (int i = 0; i < file->message_type_count(); i++) {
1636     FindProvidesForMessage(options, printer, file->message_type(i), provided);
1637   }
1638   for (int i = 0; i < file->enum_type_count(); i++) {
1639     FindProvidesForEnum(options, printer, file->enum_type(i), provided);
1640   }
1641 }
1642 
FindProvides(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files,std::set<std::string> * provided) const1643 void Generator::FindProvides(const GeneratorOptions& options,
1644                              io::Printer* printer,
1645                              const std::vector<const FileDescriptor*>& files,
1646                              std::set<std::string>* provided) const {
1647   for (int i = 0; i < files.size(); i++) {
1648     FindProvidesForFile(options, printer, files[i], provided);
1649   }
1650 
1651   printer->Print("\n");
1652 }
1653 
FindProvidesForOneOfEnum(const GeneratorOptions & options,const OneofDescriptor * oneof,std::set<std::string> * provided)1654 void FindProvidesForOneOfEnum(const GeneratorOptions& options,
1655                               const OneofDescriptor* oneof,
1656                               std::set<std::string>* provided) {
1657   std::string name = GetMessagePath(options, oneof->containing_type()) + "." +
1658                      JSOneofName(oneof) + "Case";
1659   provided->insert(name);
1660 }
1661 
FindProvidesForOneOfEnums(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<std::string> * provided)1662 void FindProvidesForOneOfEnums(const GeneratorOptions& options,
1663                                io::Printer* printer, const Descriptor* desc,
1664                                std::set<std::string>* provided) {
1665   if (HasOneofFields(desc)) {
1666     for (int i = 0; i < desc->oneof_decl_count(); i++) {
1667       if (IgnoreOneof(desc->oneof_decl(i))) {
1668         continue;
1669       }
1670       FindProvidesForOneOfEnum(options, desc->oneof_decl(i), provided);
1671     }
1672   }
1673 }
1674 
FindProvidesForMessage(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<std::string> * provided) const1675 void Generator::FindProvidesForMessage(const GeneratorOptions& options,
1676                                        io::Printer* printer,
1677                                        const Descriptor* desc,
1678                                        std::set<std::string>* provided) const {
1679   if (IgnoreMessage(desc)) {
1680     return;
1681   }
1682 
1683   std::string name = GetMessagePath(options, desc);
1684   provided->insert(name);
1685 
1686   for (int i = 0; i < desc->enum_type_count(); i++) {
1687     FindProvidesForEnum(options, printer, desc->enum_type(i), provided);
1688   }
1689 
1690   FindProvidesForOneOfEnums(options, printer, desc, provided);
1691 
1692   for (int i = 0; i < desc->nested_type_count(); i++) {
1693     FindProvidesForMessage(options, printer, desc->nested_type(i), provided);
1694   }
1695 }
FindProvidesForEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc,std::set<std::string> * provided) const1696 void Generator::FindProvidesForEnum(const GeneratorOptions& options,
1697                                     io::Printer* printer,
1698                                     const EnumDescriptor* enumdesc,
1699                                     std::set<std::string>* provided) const {
1700   std::string name = GetEnumPath(options, enumdesc);
1701   provided->insert(name);
1702 }
1703 
FindProvidesForFields(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FieldDescriptor * > & fields,std::set<std::string> * provided) const1704 void Generator::FindProvidesForFields(
1705     const GeneratorOptions& options, io::Printer* printer,
1706     const std::vector<const FieldDescriptor*>& fields,
1707     std::set<std::string>* provided) const {
1708   for (int i = 0; i < fields.size(); i++) {
1709     const FieldDescriptor* field = fields[i];
1710 
1711     if (IgnoreField(field)) {
1712       continue;
1713     }
1714 
1715     std::string name = GetNamespace(options, field->file()) + "." +
1716                        JSObjectFieldName(options, field);
1717     provided->insert(name);
1718   }
1719 }
1720 
GenerateProvides(const GeneratorOptions & options,io::Printer * printer,std::set<std::string> * provided) const1721 void Generator::GenerateProvides(const GeneratorOptions& options,
1722                                  io::Printer* printer,
1723                                  std::set<std::string>* provided) const {
1724   for (std::set<std::string>::iterator it = provided->begin();
1725        it != provided->end(); ++it) {
1726     if (options.import_style == GeneratorOptions::kImportClosure) {
1727       printer->Print("goog.provide('$name$');\n", "name", *it);
1728     } else {
1729       // We aren't using Closure's import system, but we use goog.exportSymbol()
1730       // to construct the expected tree of objects, eg.
1731       //
1732       //   goog.exportSymbol('foo.bar.Baz', null, this);
1733       //
1734       //   // Later generated code expects foo.bar = {} to exist:
1735       //   foo.bar.Baz = function() { /* ... */ }
1736 
1737       // Do not use global scope in strict mode
1738       if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
1739         std::string namespaceObject = *it;
1740         // Remove "proto." from the namespace object
1741         GOOGLE_CHECK_EQ(0, namespaceObject.compare(0, 6, "proto."));
1742         namespaceObject.erase(0, 6);
1743         printer->Print("goog.exportSymbol('$name$', null, proto);\n", "name",
1744                        namespaceObject);
1745       } else {
1746         printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
1747                        *it);
1748       }
1749     }
1750   }
1751 }
1752 
GenerateRequiresForSCC(const GeneratorOptions & options,io::Printer * printer,const SCC * scc,std::set<std::string> * provided) const1753 void Generator::GenerateRequiresForSCC(const GeneratorOptions& options,
1754                                        io::Printer* printer, const SCC* scc,
1755                                        std::set<std::string>* provided) const {
1756   std::set<std::string> required;
1757   std::set<std::string> forwards;
1758   bool have_message = false;
1759   bool has_extension = false;
1760   bool has_map = false;
1761   for (auto desc : scc->descriptors) {
1762     if (desc->containing_type() == nullptr) {
1763       FindRequiresForMessage(options, desc, &required, &forwards,
1764                              &have_message);
1765       has_extension = (has_extension || HasExtensions(desc));
1766       has_map = (has_map || HasMap(options, desc));
1767     }
1768   }
1769 
1770   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1771                        /* require_jspb = */ have_message,
1772                        /* require_extension = */ has_extension,
1773                        /* require_map = */ has_map);
1774 }
1775 
GenerateRequiresForLibrary(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files,std::set<std::string> * provided) const1776 void Generator::GenerateRequiresForLibrary(
1777     const GeneratorOptions& options, io::Printer* printer,
1778     const std::vector<const FileDescriptor*>& files,
1779     std::set<std::string>* provided) const {
1780   GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
1781   // For Closure imports we need to import every message type individually.
1782   std::set<std::string> required;
1783   std::set<std::string> forwards;
1784   bool have_extensions = false;
1785   bool have_map = false;
1786   bool have_message = false;
1787 
1788   for (int i = 0; i < files.size(); i++) {
1789     for (int j = 0; j < files[i]->message_type_count(); j++) {
1790       const Descriptor* desc = files[i]->message_type(j);
1791       if (!IgnoreMessage(desc)) {
1792         FindRequiresForMessage(options, desc, &required, &forwards,
1793                                &have_message);
1794       }
1795     }
1796 
1797     if (!have_extensions && HasExtensions(files[i])) {
1798       have_extensions = true;
1799     }
1800 
1801     if (!have_map && FileHasMap(options, files[i])) {
1802       have_map = true;
1803     }
1804 
1805     for (int j = 0; j < files[i]->extension_count(); j++) {
1806       const FieldDescriptor* extension = files[i]->extension(j);
1807       if (IgnoreField(extension)) {
1808         continue;
1809       }
1810       if (extension->containing_type()->full_name() !=
1811           "google.protobuf.bridge.MessageSet") {
1812         required.insert(GetMessagePath(options, extension->containing_type()));
1813       }
1814       FindRequiresForField(options, extension, &required, &forwards);
1815       have_extensions = true;
1816     }
1817   }
1818 
1819   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1820                        /* require_jspb = */ have_message,
1821                        /* require_extension = */ have_extensions,
1822                        /* require_map = */ have_map);
1823 }
1824 
GenerateRequiresForExtensions(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FieldDescriptor * > & fields,std::set<std::string> * provided) const1825 void Generator::GenerateRequiresForExtensions(
1826     const GeneratorOptions& options, io::Printer* printer,
1827     const std::vector<const FieldDescriptor*>& fields,
1828     std::set<std::string>* provided) const {
1829   std::set<std::string> required;
1830   std::set<std::string> forwards;
1831   for (int i = 0; i < fields.size(); i++) {
1832     const FieldDescriptor* field = fields[i];
1833     if (IgnoreField(field)) {
1834       continue;
1835     }
1836     FindRequiresForExtension(options, field, &required, &forwards);
1837   }
1838 
1839   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1840                        /* require_jspb = */ false,
1841                        /* require_extension = */ fields.size() > 0,
1842                        /* require_map = */ false);
1843 }
1844 
GenerateRequiresImpl(const GeneratorOptions & options,io::Printer * printer,std::set<std::string> * required,std::set<std::string> * forwards,std::set<std::string> * provided,bool require_jspb,bool require_extension,bool require_map) const1845 void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
1846                                      io::Printer* printer,
1847                                      std::set<std::string>* required,
1848                                      std::set<std::string>* forwards,
1849                                      std::set<std::string>* provided,
1850                                      bool require_jspb, bool require_extension,
1851                                      bool require_map) const {
1852   if (require_jspb) {
1853     required->insert("jspb.Message");
1854     required->insert("jspb.BinaryReader");
1855     required->insert("jspb.BinaryWriter");
1856   }
1857   if (require_extension) {
1858     required->insert("jspb.ExtensionFieldBinaryInfo");
1859     required->insert("jspb.ExtensionFieldInfo");
1860   }
1861   if (require_map) {
1862     required->insert("jspb.Map");
1863   }
1864 
1865   std::set<std::string>::iterator it;
1866   for (it = required->begin(); it != required->end(); ++it) {
1867     if (provided->find(*it) != provided->end()) {
1868       continue;
1869     }
1870     printer->Print("goog.require('$name$');\n", "name", *it);
1871   }
1872 
1873   printer->Print("\n");
1874 
1875   for (it = forwards->begin(); it != forwards->end(); ++it) {
1876     if (provided->find(*it) != provided->end()) {
1877       continue;
1878     }
1879     printer->Print("goog.forwardDeclare('$name$');\n", "name", *it);
1880   }
1881 }
1882 
NamespaceOnly(const Descriptor * desc)1883 bool NamespaceOnly(const Descriptor* desc) { return false; }
1884 
FindRequiresForMessage(const GeneratorOptions & options,const Descriptor * desc,std::set<std::string> * required,std::set<std::string> * forwards,bool * have_message) const1885 void Generator::FindRequiresForMessage(const GeneratorOptions& options,
1886                                        const Descriptor* desc,
1887                                        std::set<std::string>* required,
1888                                        std::set<std::string>* forwards,
1889                                        bool* have_message) const {
1890   if (!NamespaceOnly(desc)) {
1891     *have_message = true;
1892     for (int i = 0; i < desc->field_count(); i++) {
1893       const FieldDescriptor* field = desc->field(i);
1894       if (IgnoreField(field)) {
1895         continue;
1896       }
1897       FindRequiresForField(options, field, required, forwards);
1898     }
1899   }
1900 
1901   for (int i = 0; i < desc->extension_count(); i++) {
1902     const FieldDescriptor* field = desc->extension(i);
1903     if (IgnoreField(field)) {
1904       continue;
1905     }
1906     FindRequiresForExtension(options, field, required, forwards);
1907   }
1908 
1909   for (int i = 0; i < desc->nested_type_count(); i++) {
1910     FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
1911                            have_message);
1912   }
1913 }
1914 
FindRequiresForField(const GeneratorOptions & options,const FieldDescriptor * field,std::set<std::string> * required,std::set<std::string> * forwards) const1915 void Generator::FindRequiresForField(const GeneratorOptions& options,
1916                                      const FieldDescriptor* field,
1917                                      std::set<std::string>* required,
1918                                      std::set<std::string>* forwards) const {
1919   if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1920       // N.B.: file-level extensions with enum type do *not* create
1921       // dependencies, as per original codegen.
1922       !(field->is_extension() && field->extension_scope() == nullptr)) {
1923     if (options.add_require_for_enums) {
1924       required->insert(GetEnumPath(options, field->enum_type()));
1925     } else {
1926       forwards->insert(GetEnumPath(options, field->enum_type()));
1927     }
1928   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1929     if (!IgnoreMessage(field->message_type())) {
1930       required->insert(GetMessagePath(options, field->message_type()));
1931     }
1932   }
1933 }
1934 
FindRequiresForExtension(const GeneratorOptions & options,const FieldDescriptor * field,std::set<std::string> * required,std::set<std::string> * forwards) const1935 void Generator::FindRequiresForExtension(
1936     const GeneratorOptions& options, const FieldDescriptor* field,
1937     std::set<std::string>* required, std::set<std::string>* forwards) const {
1938   if (field->containing_type()->full_name() !=
1939       "google.protobuf.bridge.MessageSet") {
1940     required->insert(GetMessagePath(options, field->containing_type()));
1941   }
1942   FindRequiresForField(options, field, required, forwards);
1943 }
1944 
GenerateTestOnly(const GeneratorOptions & options,io::Printer * printer) const1945 void Generator::GenerateTestOnly(const GeneratorOptions& options,
1946                                  io::Printer* printer) const {
1947   if (options.testonly) {
1948     printer->Print("goog.setTestOnly();\n\n");
1949   }
1950   printer->Print("\n");
1951 }
1952 
GenerateClassesAndEnums(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const1953 void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
1954                                         io::Printer* printer,
1955                                         const FileDescriptor* file) const {
1956   for (int i = 0; i < file->message_type_count(); i++) {
1957     GenerateClassConstructorAndDeclareExtensionFieldInfo(options, printer,
1958                                                          file->message_type(i));
1959   }
1960   for (int i = 0; i < file->message_type_count(); i++) {
1961     GenerateClass(options, printer, file->message_type(i));
1962   }
1963   for (int i = 0; i < file->enum_type_count(); i++) {
1964     GenerateEnum(options, printer, file->enum_type(i));
1965   }
1966 }
1967 
GenerateClass(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1968 void Generator::GenerateClass(const GeneratorOptions& options,
1969                               io::Printer* printer,
1970                               const Descriptor* desc) const {
1971   if (IgnoreMessage(desc)) {
1972     return;
1973   }
1974 
1975   if (!NamespaceOnly(desc)) {
1976     printer->Print("\n");
1977     GenerateClassFieldInfo(options, printer, desc);
1978 
1979     GenerateClassToObject(options, printer, desc);
1980     // These must come *before* the extension-field info generation in
1981     // GenerateClassRegistration so that references to the binary
1982     // serialization/deserialization functions may be placed in the extension
1983     // objects.
1984     GenerateClassDeserializeBinary(options, printer, desc);
1985     GenerateClassSerializeBinary(options, printer, desc);
1986   }
1987 
1988   // Recurse on nested types. These must come *before* the extension-field
1989   // info generation in GenerateClassRegistration so that extensions that
1990   // reference nested types proceed the definitions of the nested types.
1991   for (int i = 0; i < desc->enum_type_count(); i++) {
1992     GenerateEnum(options, printer, desc->enum_type(i));
1993   }
1994   for (int i = 0; i < desc->nested_type_count(); i++) {
1995     GenerateClass(options, printer, desc->nested_type(i));
1996   }
1997 
1998   if (!NamespaceOnly(desc)) {
1999     GenerateClassRegistration(options, printer, desc);
2000     GenerateClassFields(options, printer, desc);
2001 
2002     if (options.import_style != GeneratorOptions::kImportClosure) {
2003       for (int i = 0; i < desc->extension_count(); i++) {
2004         GenerateExtension(options, printer, desc->extension(i));
2005       }
2006     }
2007   }
2008 }
2009 
GenerateClassConstructor(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2010 void Generator::GenerateClassConstructor(const GeneratorOptions& options,
2011                                          io::Printer* printer,
2012                                          const Descriptor* desc) const {
2013   printer->Print(
2014       "/**\n"
2015       " * Generated by JsPbCodeGenerator.\n"
2016       " * @param {Array=} opt_data Optional initial data array, typically "
2017       "from a\n"
2018       " * server response, or constructed directly in Javascript. The array "
2019       "is used\n"
2020       " * in place and becomes part of the constructed object. It is not "
2021       "cloned.\n"
2022       " * If no data is provided, the constructed object will be empty, but "
2023       "still\n"
2024       " * valid.\n"
2025       " * @extends {jspb.Message}\n"
2026       " * @constructor\n"
2027       " */\n"
2028       "$classprefix$$classname$ = function(opt_data) {\n",
2029       "classprefix", GetMessagePathPrefix(options, desc), "classname",
2030       desc->name());
2031   printer->Annotate("classname", desc);
2032   std::string message_id = GetMessageId(desc);
2033   printer->Print(
2034       "  jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
2035       "$rptfields$, $oneoffields$);\n",
2036       "messageId",
2037       !message_id.empty() ? ("'" + message_id + "'")
2038                           : (IsResponse(desc) ? "''" : "0"),
2039       "pivot", GetPivot(desc), "rptfields",
2040       RepeatedFieldsArrayName(options, desc), "oneoffields",
2041       OneofFieldsArrayName(options, desc));
2042   printer->Print(
2043       "};\n"
2044       "goog.inherits($classname$, jspb.Message);\n"
2045       "if (goog.DEBUG && !COMPILED) {\n"
2046       // displayName overrides Function.prototype.displayName
2047       // http://google3/javascript/externs/es3.js?l=511
2048       "  /**\n"
2049       "   * @public\n"
2050       "   * @override\n"
2051       "   */\n"
2052       "  $classname$.displayName = '$classname$';\n"
2053       "}\n",
2054       "classname", GetMessagePath(options, desc));
2055 }
2056 
GenerateClassConstructorAndDeclareExtensionFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2057 void Generator::GenerateClassConstructorAndDeclareExtensionFieldInfo(
2058     const GeneratorOptions& options, io::Printer* printer,
2059     const Descriptor* desc) const {
2060   if (!NamespaceOnly(desc)) {
2061     GenerateClassConstructor(options, printer, desc);
2062     if (IsExtendable(desc) &&
2063         desc->full_name() != "google.protobuf.bridge.MessageSet") {
2064       GenerateClassExtensionFieldInfo(options, printer, desc);
2065     }
2066   }
2067   for (int i = 0; i < desc->nested_type_count(); i++) {
2068     if (!IgnoreMessage(desc->nested_type(i))) {
2069       GenerateClassConstructorAndDeclareExtensionFieldInfo(
2070           options, printer, desc->nested_type(i));
2071     }
2072   }
2073 }
2074 
GenerateClassFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2075 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
2076                                        io::Printer* printer,
2077                                        const Descriptor* desc) const {
2078   if (HasRepeatedFields(options, desc)) {
2079     printer->Print(
2080         "/**\n"
2081         " * List of repeated fields within this message type.\n"
2082         " * @private {!Array<number>}\n"
2083         " * @const\n"
2084         " */\n"
2085         "$classname$$rptfieldarray$ = $rptfields$;\n"
2086         "\n",
2087         "classname", GetMessagePath(options, desc), "rptfieldarray",
2088         kRepeatedFieldArrayName, "rptfields",
2089         RepeatedFieldNumberList(options, desc));
2090   }
2091 
2092   if (HasOneofFields(desc)) {
2093     printer->Print(
2094         "/**\n"
2095         " * Oneof group definitions for this message. Each group defines the "
2096         "field\n"
2097         " * numbers belonging to that group. When of these fields' value is "
2098         "set, all\n"
2099         " * other fields in the group are cleared. During deserialization, if "
2100         "multiple\n"
2101         " * fields are encountered for a group, only the last value seen will "
2102         "be kept.\n"
2103         " * @private {!Array<!Array<number>>}\n"
2104         " * @const\n"
2105         " */\n"
2106         "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
2107         "\n",
2108         "classname", GetMessagePath(options, desc), "oneofgrouparray",
2109         kOneofGroupArrayName, "oneofgroups", OneofGroupList(desc));
2110 
2111     for (int i = 0; i < desc->oneof_decl_count(); i++) {
2112       if (IgnoreOneof(desc->oneof_decl(i))) {
2113         continue;
2114       }
2115       GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
2116     }
2117   }
2118 }
2119 
GenerateClassXid(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2120 void Generator::GenerateClassXid(const GeneratorOptions& options,
2121                                  io::Printer* printer,
2122                                  const Descriptor* desc) const {
2123   printer->Print(
2124       "\n"
2125       "\n"
2126       "$class$.prototype.messageXid = xid('$class$');\n",
2127       "class", GetMessagePath(options, desc));
2128 }
2129 
GenerateOneofCaseDefinition(const GeneratorOptions & options,io::Printer * printer,const OneofDescriptor * oneof) const2130 void Generator::GenerateOneofCaseDefinition(
2131     const GeneratorOptions& options, io::Printer* printer,
2132     const OneofDescriptor* oneof) const {
2133   printer->Print(
2134       "/**\n"
2135       " * @enum {number}\n"
2136       " */\n"
2137       "$classname$.$oneof$Case = {\n"
2138       "  $upcase$_NOT_SET: 0",
2139       "classname", GetMessagePath(options, oneof->containing_type()), "oneof",
2140       JSOneofName(oneof), "upcase", ToEnumCase(oneof->name()));
2141 
2142   for (int i = 0; i < oneof->field_count(); i++) {
2143     if (IgnoreField(oneof->field(i))) {
2144       continue;
2145     }
2146 
2147     printer->Print(
2148         ",\n"
2149         "  $upcase$: $number$",
2150         "upcase", ToEnumCase(oneof->field(i)->name()), "number",
2151         JSFieldIndex(oneof->field(i)));
2152     printer->Annotate("upcase", oneof->field(i));
2153   }
2154 
2155   printer->Print(
2156       "\n"
2157       "};\n"
2158       "\n"
2159       "/**\n"
2160       " * @return {$class$.$oneof$Case}\n"
2161       " */\n"
2162       "$class$.prototype.get$oneof$Case = function() {\n"
2163       "  return /** @type {$class$.$oneof$Case} */(jspb.Message."
2164       "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
2165       "};\n"
2166       "\n",
2167       "class", GetMessagePath(options, oneof->containing_type()), "oneof",
2168       JSOneofName(oneof), "oneofindex", JSOneofIndex(oneof));
2169 }
2170 
GenerateClassToObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2171 void Generator::GenerateClassToObject(const GeneratorOptions& options,
2172                                       io::Printer* printer,
2173                                       const Descriptor* desc) const {
2174   printer->Print(
2175       "\n"
2176       "\n"
2177       "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
2178       "/**\n"
2179       " * Creates an object representation of this proto.\n"
2180       " * Field names that are reserved in JavaScript and will be renamed to "
2181       "pb_name.\n"
2182       " * Optional fields that are not set will be set to undefined.\n"
2183       " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
2184       " * For the list of reserved names please see:\n"
2185       " *     net/proto2/compiler/js/internal/generator.cc#kKeyword.\n"
2186       " * @param {boolean=} opt_includeInstance Deprecated. whether to include "
2187       "the\n"
2188       " *     JSPB instance for transitional soy proto support:\n"
2189       " *     http://goto/soy-param-migration\n"
2190       " * @return {!Object}\n"
2191       " */\n"
2192       "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
2193       "  return $classname$.toObject(opt_includeInstance, this);\n"
2194       "};\n"
2195       "\n"
2196       "\n"
2197       "/**\n"
2198       " * Static version of the {@see toObject} method.\n"
2199       " * @param {boolean|undefined} includeInstance Deprecated. Whether to "
2200       "include\n"
2201       " *     the JSPB instance for transitional soy proto support:\n"
2202       " *     http://goto/soy-param-migration\n"
2203       " * @param {!$classname$} msg The msg instance to transform.\n"
2204       " * @return {!Object}\n"
2205       " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
2206       " */\n"
2207       "$classname$.toObject = function(includeInstance, msg) {\n"
2208       "  var f, obj = {",
2209       "classname", GetMessagePath(options, desc));
2210 
2211   bool first = true;
2212   for (int i = 0; i < desc->field_count(); i++) {
2213     const FieldDescriptor* field = desc->field(i);
2214     if (IgnoreField(field)) {
2215       continue;
2216     }
2217 
2218     if (!first) {
2219       printer->Print(",\n    ");
2220     } else {
2221       printer->Print("\n    ");
2222       first = false;
2223     }
2224 
2225     GenerateClassFieldToObject(options, printer, field);
2226   }
2227 
2228   if (!first) {
2229     printer->Print("\n  };\n\n");
2230   } else {
2231     printer->Print("\n\n  };\n\n");
2232   }
2233 
2234   if (IsExtendable(desc)) {
2235     printer->Print(
2236         "  jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
2237         "obj,\n"
2238         "      $extObject$, $class$.prototype.getExtension,\n"
2239         "      includeInstance);\n",
2240         "extObject", JSExtensionsObjectName(options, desc->file(), desc),
2241         "class", GetMessagePath(options, desc));
2242   }
2243 
2244   printer->Print(
2245       "  if (includeInstance) {\n"
2246       "    obj.$$jspbMessageInstance = msg;\n"
2247       "  }\n"
2248       "  return obj;\n"
2249       "};\n"
2250       "}\n"
2251       "\n"
2252       "\n",
2253       "classname", GetMessagePath(options, desc));
2254 }
2255 
GenerateFieldValueExpression(io::Printer * printer,const char * obj_reference,const FieldDescriptor * field,bool use_default) const2256 void Generator::GenerateFieldValueExpression(io::Printer* printer,
2257                                              const char* obj_reference,
2258                                              const FieldDescriptor* field,
2259                                              bool use_default) const {
2260   const bool is_float_or_double =
2261       field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2262       field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE;
2263   const bool is_boolean = field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL;
2264 
2265   const std::string with_default = use_default ? "WithDefault" : "";
2266   const std::string default_arg =
2267       use_default ? StrCat(", ", JSFieldDefault(field)) : "";
2268   const std::string cardinality = field->is_repeated() ? "Repeated" : "";
2269   std::string type = "";
2270   if (is_float_or_double) {
2271     type = "FloatingPoint";
2272   }
2273   if (is_boolean) {
2274     type = "Boolean";
2275   }
2276 
2277   // Prints the appropriate function, among:
2278   // - getField
2279   // - getBooleanField
2280   // - getFloatingPointField => Replaced by getOptionalFloatingPointField to
2281   //   preserve backward compatibility.
2282   // - getFieldWithDefault
2283   // - getBooleanFieldWithDefault
2284   // - getFloatingPointFieldWithDefault
2285   // - getRepeatedField
2286   // - getRepeatedBooleanField
2287   // - getRepeatedFloatingPointField
2288   if (is_float_or_double && !field->is_repeated() && !use_default) {
2289     printer->Print(
2290         "jspb.Message.getOptionalFloatingPointField($obj$, "
2291         "$index$$default$)",
2292         "obj", obj_reference, "index", JSFieldIndex(field), "default",
2293         default_arg);
2294   } else {
2295     printer->Print(
2296         "jspb.Message.get$cardinality$$type$Field$with_default$($obj$, "
2297         "$index$$default$)",
2298         "cardinality", cardinality, "type", type, "with_default", with_default,
2299         "obj", obj_reference, "index", JSFieldIndex(field), "default",
2300         default_arg);
2301   }
2302 }
2303 
GenerateClassFieldToObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2304 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
2305                                            io::Printer* printer,
2306                                            const FieldDescriptor* field) const {
2307   printer->Print("$fieldname$: ", "fieldname",
2308                  JSObjectFieldName(options, field));
2309 
2310   if (field->is_map()) {
2311     const FieldDescriptor* value_field = MapFieldValue(field);
2312     // If the map values are of a message type, we must provide their static
2313     // toObject() method; otherwise we pass undefined for that argument.
2314     std::string value_to_object;
2315     if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2316       value_to_object =
2317           GetMessagePath(options, value_field->message_type()) + ".toObject";
2318     } else {
2319       value_to_object = "undefined";
2320     }
2321     printer->Print(
2322         "(f = msg.get$name$()) ? f.toObject(includeInstance, $valuetoobject$) "
2323         ": []",
2324         "name", JSGetterName(options, field), "valuetoobject", value_to_object);
2325   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2326     // Message field.
2327     if (field->is_repeated()) {
2328       {
2329         printer->Print(
2330             "jspb.Message.toObjectList(msg.get$getter$(),\n"
2331             "    $type$.toObject, includeInstance)",
2332             "getter", JSGetterName(options, field), "type",
2333             SubmessageTypeRef(options, field));
2334       }
2335     } else {
2336       printer->Print(
2337           "(f = msg.get$getter$()) && "
2338           "$type$.toObject(includeInstance, f)",
2339           "getter", JSGetterName(options, field), "type",
2340           SubmessageTypeRef(options, field));
2341     }
2342   } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
2343     // For bytes fields we want to always return the B64 data.
2344     printer->Print("msg.get$getter$()", "getter",
2345                    JSGetterName(options, field, BYTES_B64));
2346   } else {
2347     bool use_default = field->has_default_value();
2348 
2349     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
2350         // Repeated fields get initialized to their default in the constructor
2351         // (why?), so we emit a plain getField() call for them.
2352         !field->is_repeated()) {
2353       // Proto3 puts all defaults (including implicit defaults) in toObject().
2354       // But for proto2 we leave the existing semantics unchanged: unset fields
2355       // without default are unset.
2356       use_default = true;
2357     }
2358 
2359     // We don't implement this by calling the accessors, because the semantics
2360     // of the accessors are changing independently of the toObject() semantics.
2361     // We are migrating the accessors to return defaults instead of null, but
2362     // it may take longer to migrate toObject (or we might not want to do it at
2363     // all).  So we want to generate independent code.
2364     // The accessor for unset optional values without default should return
2365     // null. Those are converted to undefined in the generated object.
2366     if (!use_default) {
2367       printer->Print("(f = ");
2368     }
2369     GenerateFieldValueExpression(printer, "msg", field, use_default);
2370     if (!use_default) {
2371       printer->Print(") == null ? undefined : f");
2372     }
2373   }
2374 }
2375 
GenerateObjectTypedef(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2376 void Generator::GenerateObjectTypedef(const GeneratorOptions& options,
2377                                       io::Printer* printer,
2378                                       const Descriptor* desc) const {
2379   // TODO(b/122687752): Consider renaming nested messages called ObjectFormat
2380   //     to prevent collisions.
2381   const std::string type_name = GetMessagePath(options, desc) + ".ObjectFormat";
2382 
2383   printer->Print(
2384       "/**\n"
2385       " * The raw object form of $messageName$ as accepted by the `fromObject` "
2386       "method.\n"
2387       " * @record\n"
2388       " */\n"
2389       "$typeName$ = function() {\n",
2390       "messageName", desc->name(), "typeName", type_name);
2391 
2392   for (int i = 0; i < desc->field_count(); i++) {
2393     if (i > 0) {
2394       printer->Print("\n");
2395     }
2396     printer->Print(
2397         "  /** @type {$fieldType$|undefined} */\n"
2398         "  this.$fieldName$;\n",
2399         "fieldName", JSObjectFieldName(options, desc->field(i)),
2400         // TODO(b/121097361): Add type checking for field values.
2401         "fieldType", "?");
2402   }
2403 
2404   printer->Print("};\n\n");
2405 }
2406 
GenerateClassFromObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2407 void Generator::GenerateClassFromObject(const GeneratorOptions& options,
2408                                         io::Printer* printer,
2409                                         const Descriptor* desc) const {
2410   printer->Print("if (jspb.Message.GENERATE_FROM_OBJECT) {\n\n");
2411 
2412   GenerateObjectTypedef(options, printer, desc);
2413 
2414   printer->Print(
2415       "/**\n"
2416       " * Loads data from an object into a new instance of this proto.\n"
2417       " * @param {!$classname$.ObjectFormat} obj\n"
2418       " *     The object representation of this proto to load the data from.\n"
2419       " * @return {!$classname$}\n"
2420       " */\n"
2421       "$classname$.fromObject = function(obj) {\n"
2422       "  var msg = new $classname$();\n",
2423       "classname", GetMessagePath(options, desc));
2424 
2425   for (int i = 0; i < desc->field_count(); i++) {
2426     const FieldDescriptor* field = desc->field(i);
2427     if (!IgnoreField(field)) {
2428       GenerateClassFieldFromObject(options, printer, field);
2429     }
2430   }
2431 
2432   printer->Print(
2433       "  return msg;\n"
2434       "};\n"
2435       "}\n\n");
2436 }
2437 
GenerateClassFieldFromObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2438 void Generator::GenerateClassFieldFromObject(
2439     const GeneratorOptions& options, io::Printer* printer,
2440     const FieldDescriptor* field) const {
2441   if (field->is_map()) {
2442     const FieldDescriptor* value_field = MapFieldValue(field);
2443     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2444       // Since the map values are of message type, we have to do some extra work
2445       // to recursively call fromObject() on them before setting the map field.
2446       printer->Print(
2447           "  obj.$name$ && jspb.Message.setWrapperField(\n"
2448           "      msg, $index$, jspb.Map.fromObject(obj.$name$, $fieldclass$, "
2449           "$fieldclass$.fromObject));\n",
2450           "name", JSObjectFieldName(options, field), "index",
2451           JSFieldIndex(field), "fieldclass",
2452           GetMessagePath(options, value_field->message_type()));
2453     } else {
2454       // `msg` is a newly-constructed message object that has not yet built any
2455       // map containers wrapping underlying arrays, so we can simply directly
2456       // set the array here without fear of a stale wrapper.
2457       printer->Print(
2458           "  obj.$name$ && "
2459           "jspb.Message.setField(msg, $index$, obj.$name$);\n",
2460           "name", JSObjectFieldName(options, field), "index",
2461           JSFieldIndex(field));
2462     }
2463   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2464     // Message field (singular or repeated)
2465     if (field->is_repeated()) {
2466       {
2467         printer->Print(
2468             "  obj.$name$ && "
2469             "jspb.Message.setRepeatedWrapperField(\n"
2470             "      msg, $index$, obj.$name$.map(\n"
2471             "          $fieldclass$.fromObject));\n",
2472             "name", JSObjectFieldName(options, field), "index",
2473             JSFieldIndex(field), "fieldclass",
2474             SubmessageTypeRef(options, field));
2475       }
2476     } else {
2477       printer->Print(
2478           "  obj.$name$ && jspb.Message.setWrapperField(\n"
2479           "      msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
2480           "name", JSObjectFieldName(options, field), "index",
2481           JSFieldIndex(field), "fieldclass", SubmessageTypeRef(options, field));
2482     }
2483   } else {
2484     // Simple (primitive) field.
2485     printer->Print(
2486         "  obj.$name$ != null && jspb.Message.setField(msg, $index$, "
2487         "obj.$name$);\n",
2488         "name", JSObjectFieldName(options, field), "index",
2489         JSFieldIndex(field));
2490   }
2491 }
2492 
GenerateClassRegistration(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2493 void Generator::GenerateClassRegistration(const GeneratorOptions& options,
2494                                           io::Printer* printer,
2495                                           const Descriptor* desc) const {
2496   // Register any extensions defined inside this message type.
2497   for (int i = 0; i < desc->extension_count(); i++) {
2498     const FieldDescriptor* extension = desc->extension(i);
2499     if (ShouldGenerateExtension(extension)) {
2500       GenerateExtension(options, printer, extension);
2501     }
2502   }
2503 }
2504 
GenerateClassFields(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2505 void Generator::GenerateClassFields(const GeneratorOptions& options,
2506                                     io::Printer* printer,
2507                                     const Descriptor* desc) const {
2508   for (int i = 0; i < desc->field_count(); i++) {
2509     if (!IgnoreField(desc->field(i))) {
2510       GenerateClassField(options, printer, desc->field(i));
2511     }
2512   }
2513 }
2514 
GenerateBytesWrapper(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field,BytesMode bytes_mode)2515 void GenerateBytesWrapper(const GeneratorOptions& options, io::Printer* printer,
2516                           const FieldDescriptor* field, BytesMode bytes_mode) {
2517   std::string type =
2518       JSFieldTypeAnnotation(options, field,
2519                             /* is_setter_argument = */ false,
2520                             /* force_present = */ false,
2521                             /* singular_if_not_packed = */ false, bytes_mode);
2522   printer->Print(
2523       "/**\n"
2524       " * $fielddef$\n"
2525       "$comment$"
2526       " * This is a type-conversion wrapper around `get$defname$()`\n"
2527       " * @return {$type$}\n"
2528       " */\n"
2529       "$class$.prototype.get$name$ = function() {\n"
2530       "  return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
2531       "      this.get$defname$()));\n"
2532       "};\n"
2533       "\n"
2534       "\n",
2535       "fielddef", FieldDefinition(options, field), "comment",
2536       FieldComments(field, bytes_mode), "type", type, "class",
2537       GetMessagePath(options, field->containing_type()), "name",
2538       JSGetterName(options, field, bytes_mode), "list",
2539       field->is_repeated() ? "List" : "", "suffix",
2540       JSByteGetterSuffix(bytes_mode), "defname",
2541       JSGetterName(options, field, BYTES_DEFAULT));
2542 }
2543 
GenerateClassField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2544 void Generator::GenerateClassField(const GeneratorOptions& options,
2545                                    io::Printer* printer,
2546                                    const FieldDescriptor* field) const {
2547   if (field->is_map()) {
2548     const FieldDescriptor* key_field = MapFieldKey(field);
2549     const FieldDescriptor* value_field = MapFieldValue(field);
2550     // Map field: special handling to instantiate the map object on demand.
2551     std::string key_type =
2552         JSFieldTypeAnnotation(options, key_field,
2553                               /* is_setter_argument = */ false,
2554                               /* force_present = */ true,
2555                               /* singular_if_not_packed = */ false);
2556     std::string value_type =
2557         JSFieldTypeAnnotation(options, value_field,
2558                               /* is_setter_argument = */ false,
2559                               /* force_present = */ true,
2560                               /* singular_if_not_packed = */ false);
2561 
2562     printer->Print(
2563         "/**\n"
2564         " * $fielddef$\n"
2565         " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
2566         " * empty, instead returning `undefined`\n"
2567         " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
2568         " */\n",
2569         "fielddef", FieldDefinition(options, field), "keytype", key_type,
2570         "valuetype", value_type);
2571     printer->Print(
2572         "$class$.prototype.$gettername$ = function(opt_noLazyCreate) {\n"
2573         "  return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
2574         "class", GetMessagePath(options, field->containing_type()),
2575         "gettername", "get" + JSGetterName(options, field), "keytype", key_type,
2576         "valuetype", value_type);
2577     printer->Annotate("gettername", field);
2578     printer->Print(
2579         "      jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
2580         "index", JSFieldIndex(field));
2581 
2582     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2583       printer->Print(
2584           ",\n"
2585           "      $messageType$",
2586           "messageType", GetMessagePath(options, value_field->message_type()));
2587     } else {
2588       printer->Print(
2589           ",\n"
2590           "      null");
2591     }
2592 
2593     printer->Print("));\n");
2594 
2595     printer->Print(
2596         "};\n"
2597         "\n"
2598         "\n");
2599   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2600     // Message field: special handling in order to wrap the underlying data
2601     // array with a message object.
2602 
2603     printer->Print(
2604         "/**\n"
2605         " * $fielddef$\n"
2606         "$comment$"
2607         " * @return {$type$}\n"
2608         " */\n",
2609         "fielddef", FieldDefinition(options, field), "comment",
2610         FieldComments(field, BYTES_DEFAULT), "type",
2611         JSFieldTypeAnnotation(options, field,
2612                               /* is_setter_argument = */ false,
2613                               /* force_present = */ false,
2614                               /* singular_if_not_packed = */ false));
2615     printer->Print(
2616         "$class$.prototype.$gettername$ = function() {\n"
2617         "  return /** @type{$type$} */ (\n"
2618         "    jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
2619         "$index$$required$));\n"
2620         "};\n"
2621         "\n"
2622         "\n",
2623         "class", GetMessagePath(options, field->containing_type()),
2624         "gettername", "get" + JSGetterName(options, field), "type",
2625         JSFieldTypeAnnotation(options, field,
2626                               /* is_setter_argument = */ false,
2627                               /* force_present = */ false,
2628                               /* singular_if_not_packed = */ false),
2629         "rpt", (field->is_repeated() ? "Repeated" : ""), "index",
2630         JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field),
2631         "required",
2632         (field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
2633     printer->Annotate("gettername", field);
2634     printer->Print(
2635         "/**\n"
2636         " * @param {$optionaltype$} value\n"
2637         " * @return {!$class$} returns this\n"
2638         "*/\n"
2639         "$class$.prototype.$settername$ = function(value) {\n"
2640         "  return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
2641         "optionaltype",
2642         JSFieldTypeAnnotation(options, field,
2643                               /* is_setter_argument = */ true,
2644                               /* force_present = */ false,
2645                               /* singular_if_not_packed = */ false),
2646         "class", GetMessagePath(options, field->containing_type()),
2647         "settername", "set" + JSGetterName(options, field), "oneoftag",
2648         (InRealOneof(field) ? "Oneof" : ""), "repeatedtag",
2649         (field->is_repeated() ? "Repeated" : ""));
2650     printer->Annotate("settername", field);
2651 
2652     printer->Print(
2653         "this, $index$$oneofgroup$, value);\n"
2654         "};\n"
2655         "\n"
2656         "\n",
2657         "index", JSFieldIndex(field), "oneofgroup",
2658         (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""));
2659 
2660     if (field->is_repeated()) {
2661       GenerateRepeatedMessageHelperMethods(options, printer, field);
2662     }
2663 
2664   } else {
2665     bool untyped = false;
2666 
2667     // Simple (primitive) field, either singular or repeated.
2668 
2669     // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type;
2670     // at this point we "lie" to non-binary users and tell the return
2671     // type is always base64 string, pending a LSC to migrate to typed getters.
2672     BytesMode bytes_mode =
2673         field->type() == FieldDescriptor::TYPE_BYTES && !options.binary
2674             ? BYTES_B64
2675             : BYTES_DEFAULT;
2676     std::string typed_annotation =
2677         JSFieldTypeAnnotation(options, field,
2678                               /* is_setter_argument = */ false,
2679                               /* force_present = */ false,
2680                               /* singular_if_not_packed = */ false,
2681                               /* bytes_mode = */ bytes_mode);
2682     if (untyped) {
2683       printer->Print(
2684           "/**\n"
2685           " * @return {?} Raw field, untyped.\n"
2686           " */\n");
2687     } else {
2688       printer->Print(
2689           "/**\n"
2690           " * $fielddef$\n"
2691           "$comment$"
2692           " * @return {$type$}\n"
2693           " */\n",
2694           "fielddef", FieldDefinition(options, field), "comment",
2695           FieldComments(field, bytes_mode), "type", typed_annotation);
2696     }
2697 
2698     printer->Print("$class$.prototype.$gettername$ = function() {\n", "class",
2699                    GetMessagePath(options, field->containing_type()),
2700                    "gettername", "get" + JSGetterName(options, field));
2701     printer->Annotate("gettername", field);
2702 
2703     if (untyped) {
2704       printer->Print("  return ");
2705     } else {
2706       printer->Print("  return /** @type {$type$} */ (", "type",
2707                      typed_annotation);
2708     }
2709 
2710     bool use_default = !ReturnsNullWhenUnset(options, field);
2711 
2712     // Raw fields with no default set should just return undefined.
2713     if (untyped && !field->has_default_value()) {
2714       use_default = false;
2715     }
2716 
2717     // Repeated fields get initialized to their default in the constructor
2718     // (why?), so we emit a plain getField() call for them.
2719     if (field->is_repeated()) {
2720       use_default = false;
2721     }
2722 
2723     GenerateFieldValueExpression(printer, "this", field, use_default);
2724 
2725     if (untyped) {
2726       printer->Print(
2727           ";\n"
2728           "};\n"
2729           "\n"
2730           "\n");
2731     } else {
2732       printer->Print(
2733           ");\n"
2734           "};\n"
2735           "\n"
2736           "\n");
2737     }
2738 
2739     if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
2740       GenerateBytesWrapper(options, printer, field, BYTES_B64);
2741       GenerateBytesWrapper(options, printer, field, BYTES_U8);
2742     }
2743 
2744     printer->Print(
2745         "/**\n"
2746         " * @param {$optionaltype$} value\n"
2747         " * @return {!$class$} returns this\n"
2748         " */\n",
2749         "class", GetMessagePath(options, field->containing_type()),
2750         "optionaltype",
2751         untyped ? "*"
2752                 : JSFieldTypeAnnotation(options, field,
2753                                         /* is_setter_argument = */ true,
2754                                         /* force_present = */ false,
2755                                         /* singular_if_not_packed = */ false));
2756 
2757     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
2758         !field->is_repeated() && !field->is_map() &&
2759         !HasFieldPresence(options, field)) {
2760       // Proto3 non-repeated and non-map fields without presence use the
2761       // setProto3*Field function.
2762       printer->Print(
2763           "$class$.prototype.$settername$ = function(value) {\n"
2764           "  return jspb.Message.setProto3$typetag$Field(this, $index$, "
2765           "value);"
2766           "\n"
2767           "};\n"
2768           "\n"
2769           "\n",
2770           "class", GetMessagePath(options, field->containing_type()),
2771           "settername", "set" + JSGetterName(options, field), "typetag",
2772           JSTypeTag(field), "index", JSFieldIndex(field));
2773       printer->Annotate("settername", field);
2774     } else {
2775       // Otherwise, use the regular setField function.
2776       printer->Print(
2777           "$class$.prototype.$settername$ = function(value) {\n"
2778           "  return jspb.Message.set$oneoftag$Field(this, $index$",
2779           "class", GetMessagePath(options, field->containing_type()),
2780           "settername", "set" + JSGetterName(options, field), "oneoftag",
2781           (InRealOneof(field) ? "Oneof" : ""), "index", JSFieldIndex(field));
2782       printer->Annotate("settername", field);
2783       printer->Print(
2784           "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
2785           "};\n"
2786           "\n"
2787           "\n",
2788           "type",
2789           untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2790           "typeclose", untyped ? ")" : "", "oneofgroup",
2791           (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
2792           "rptvalueinit", (field->is_repeated() ? " || []" : ""));
2793     }
2794 
2795     if (untyped) {
2796       printer->Print(
2797           "/**\n"
2798           " * Clears the value.\n"
2799           " * @return {!$class$} returns this\n"
2800           " */\n",
2801           "class", GetMessagePath(options, field->containing_type()));
2802     }
2803 
2804     if (field->is_repeated()) {
2805       GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
2806     }
2807   }
2808 
2809   // Generate clearFoo() method for map fields, repeated fields, and other
2810   // fields with presence.
2811   if (field->is_map()) {
2812     // clang-format off
2813     printer->Print(
2814         "/**\n"
2815         " * Clears values from the map. The map will be non-null.\n"
2816         " * @return {!$class$} returns this\n"
2817         " */\n"
2818         "$class$.prototype.$clearername$ = function() {\n"
2819         "  this.$gettername$().clear();\n"
2820         "  return this;"
2821         "};\n"
2822         "\n"
2823         "\n",
2824         "class", GetMessagePath(options, field->containing_type()),
2825         "clearername", "clear" + JSGetterName(options, field),
2826         "gettername", "get" + JSGetterName(options, field));
2827     // clang-format on
2828     printer->Annotate("clearername", field);
2829   } else if (field->is_repeated() ||
2830              (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
2831               !field->is_required())) {
2832     // Fields where we can delegate to the regular setter.
2833     // clang-format off
2834     printer->Print(
2835         "/**\n"
2836         " * $jsdoc$\n"
2837         " * @return {!$class$} returns this\n"
2838         " */\n"
2839         "$class$.prototype.$clearername$ = function() {\n"
2840         "  return this.$settername$($clearedvalue$);\n"
2841         "};\n"
2842         "\n"
2843         "\n",
2844        "jsdoc", field->is_repeated()
2845            ? "Clears the list making it empty but non-null."
2846            : "Clears the message field making it undefined.",
2847         "class", GetMessagePath(options, field->containing_type()),
2848         "clearername", "clear" + JSGetterName(options, field),
2849         "settername", "set" + JSGetterName(options, field),
2850         "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
2851     // clang-format on
2852     printer->Annotate("clearername", field);
2853   } else if (HasFieldPresence(options, field)) {
2854     // Fields where we can't delegate to the regular setter because it doesn't
2855     // accept "undefined" as an argument.
2856     // clang-format off
2857     printer->Print(
2858         "/**\n"
2859         " * Clears the field making it undefined.\n"
2860         " * @return {!$class$} returns this\n"
2861         " */\n"
2862         "$class$.prototype.$clearername$ = function() {\n"
2863         "  return jspb.Message.set$maybeoneof$Field(this, "
2864             "$index$$maybeoneofgroup$, ",
2865         "class", GetMessagePath(options, field->containing_type()),
2866         "clearername", "clear" + JSGetterName(options, field),
2867         "maybeoneof", (InRealOneof(field) ? "Oneof" : ""),
2868         "maybeoneofgroup", (InRealOneof(field)
2869                             ? (", " + JSOneofArray(options, field))
2870                             : ""),
2871         "index", JSFieldIndex(field));
2872     // clang-format on
2873     printer->Annotate("clearername", field);
2874     printer->Print(
2875         "$clearedvalue$);\n"
2876         "};\n"
2877         "\n"
2878         "\n",
2879         "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
2880   }
2881 
2882   if (HasFieldPresence(options, field)) {
2883     printer->Print(
2884         "/**\n"
2885         " * Returns whether this field is set.\n"
2886         " * @return {boolean}\n"
2887         " */\n"
2888         "$class$.prototype.$hasername$ = function() {\n"
2889         "  return jspb.Message.getField(this, $index$) != null;\n"
2890         "};\n"
2891         "\n"
2892         "\n",
2893         "class", GetMessagePath(options, field->containing_type()), "hasername",
2894         "has" + JSGetterName(options, field), "index", JSFieldIndex(field));
2895     printer->Annotate("hasername", field);
2896   }
2897 }
2898 
GenerateRepeatedPrimitiveHelperMethods(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field,bool untyped) const2899 void Generator::GenerateRepeatedPrimitiveHelperMethods(
2900     const GeneratorOptions& options, io::Printer* printer,
2901     const FieldDescriptor* field, bool untyped) const {
2902   // clang-format off
2903   printer->Print(
2904       "/**\n"
2905       " * @param {$optionaltype$} value\n"
2906       " * @param {number=} opt_index\n"
2907       " * @return {!$class$} returns this\n"
2908       " */\n"
2909       "$class$.prototype.$addername$ = function(value, opt_index) {\n"
2910       "  return jspb.Message.addToRepeatedField(this, "
2911       "$index$",
2912       "class", GetMessagePath(options, field->containing_type()), "addername",
2913       "add" + JSGetterName(options, field, BYTES_DEFAULT,
2914                            /* drop_list = */ true),
2915       "optionaltype",
2916           JSFieldTypeAnnotation(
2917                                 options, field,
2918                                 /* is_setter_argument = */ false,
2919                                 /* force_present = */ true,
2920                                 /* singular_if_not_packed = */ false,
2921                                 BYTES_DEFAULT,
2922                                 /* force_singular = */ true),
2923       "index", JSFieldIndex(field));
2924   printer->Annotate("addername", field);
2925   printer->Print(
2926       "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
2927       "opt_index);\n"
2928       "};\n"
2929       "\n"
2930       "\n",
2931       "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
2932       "typeclose", untyped ? ")" : "", "oneofgroup",
2933       (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
2934       "rptvalueinit", "");
2935   // clang-format on
2936 }
2937 
GenerateRepeatedMessageHelperMethods(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2938 void Generator::GenerateRepeatedMessageHelperMethods(
2939     const GeneratorOptions& options, io::Printer* printer,
2940     const FieldDescriptor* field) const {
2941   printer->Print(
2942       "/**\n"
2943       " * @param {!$optionaltype$=} opt_value\n"
2944       " * @param {number=} opt_index\n"
2945       " * @return {!$optionaltype$}\n"
2946       " */\n"
2947       "$class$.prototype.$addername$ = function(opt_value, opt_index) {\n"
2948       "  return jspb.Message.addTo$repeatedtag$WrapperField(",
2949       "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class",
2950       GetMessagePath(options, field->containing_type()), "addername",
2951       "add" + JSGetterName(options, field, BYTES_DEFAULT,
2952                            /* drop_list = */ true),
2953       "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2954 
2955   printer->Annotate("addername", field);
2956   printer->Print(
2957       "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n"
2958       "};\n"
2959       "\n"
2960       "\n",
2961       "index", JSFieldIndex(field), "oneofgroup",
2962       (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""), "ctor",
2963       GetMessagePath(options, field->message_type()));
2964 }
2965 
GenerateClassExtensionFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2966 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
2967                                                 io::Printer* printer,
2968                                                 const Descriptor* desc) const {
2969   if (IsExtendable(desc)) {
2970     printer->Print(
2971         "\n"
2972         "/**\n"
2973         " * The extensions registered with this message class. This is a "
2974         "map of\n"
2975         " * extension field number to fieldInfo object.\n"
2976         " *\n"
2977         " * For example:\n"
2978         " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2979         "ctor: proto.example.MyMessage} }\n"
2980         " *\n"
2981         " * fieldName contains the JsCompiler renamed field name property "
2982         "so that it\n"
2983         " * works in OPTIMIZED mode.\n"
2984         " *\n"
2985         " * @type {!Object<number, jspb.ExtensionFieldInfo>}\n"
2986         " */\n"
2987         "$class$.extensions = {};\n"
2988         "\n",
2989         "class", GetMessagePath(options, desc));
2990 
2991     printer->Print(
2992         "\n"
2993         "/**\n"
2994         " * The extensions registered with this message class. This is a "
2995         "map of\n"
2996         " * extension field number to fieldInfo object.\n"
2997         " *\n"
2998         " * For example:\n"
2999         " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
3000         "ctor: proto.example.MyMessage} }\n"
3001         " *\n"
3002         " * fieldName contains the JsCompiler renamed field name property "
3003         "so that it\n"
3004         " * works in OPTIMIZED mode.\n"
3005         " *\n"
3006         " * @type {!Object<number, jspb.ExtensionFieldBinaryInfo>}\n"
3007         " */\n"
3008         "$class$.extensionsBinary = {};\n"
3009         "\n",
3010         "class", GetMessagePath(options, desc));
3011   }
3012 }
3013 
GenerateClassDeserializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const3014 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
3015                                                io::Printer* printer,
3016                                                const Descriptor* desc) const {
3017   // TODO(cfallin): Handle lazy decoding when requested by field option and/or
3018   // by default for 'bytes' fields and packed repeated fields.
3019 
3020   printer->Print(
3021       "/**\n"
3022       " * Deserializes binary data (in protobuf wire format).\n"
3023       " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
3024       " * @return {!$class$}\n"
3025       " */\n"
3026       "$class$.deserializeBinary = function(bytes) {\n"
3027       "  var reader = new jspb.BinaryReader(bytes);\n"
3028       "  var msg = new $class$;\n"
3029       "  return $class$.deserializeBinaryFromReader(msg, reader);\n"
3030       "};\n"
3031       "\n"
3032       "\n"
3033       "/**\n"
3034       " * Deserializes binary data (in protobuf wire format) from the\n"
3035       " * given reader into the given message object.\n"
3036       " * @param {!$class$} msg The message object to deserialize into.\n"
3037       " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
3038       " * @return {!$class$}\n"
3039       " */\n"
3040       "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
3041       "  while (reader.nextField()) {\n",
3042       "class", GetMessagePath(options, desc));
3043   printer->Print(
3044       "    if (reader.isEndGroup()) {\n"
3045       "      break;\n"
3046       "    }\n"
3047       "    var field = reader.getFieldNumber();\n"
3048       "    switch (field) {\n");
3049 
3050   for (int i = 0; i < desc->field_count(); i++) {
3051     if (!IgnoreField(desc->field(i))) {
3052       GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
3053     }
3054   }
3055 
3056   printer->Print("    default:\n");
3057   if (IsExtendable(desc)) {
3058     printer->Print(
3059         "      jspb.Message.readBinaryExtension(msg, reader,\n"
3060         "        $extobj$Binary,\n"
3061         "        $class$.prototype.getExtension,\n"
3062         "        $class$.prototype.setExtension);\n"
3063         "      break;\n"
3064         "    }\n",
3065         "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
3066         GetMessagePath(options, desc));
3067   } else {
3068     printer->Print(
3069         "      reader.skipField();\n"
3070         "      break;\n"
3071         "    }\n");
3072   }
3073 
3074   printer->Print(
3075       "  }\n"
3076       "  return msg;\n"
3077       "};\n"
3078       "\n"
3079       "\n");
3080 }
3081 
GenerateClassDeserializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3082 void Generator::GenerateClassDeserializeBinaryField(
3083     const GeneratorOptions& options, io::Printer* printer,
3084     const FieldDescriptor* field) const {
3085   printer->Print("    case $num$:\n", "num", StrCat(field->number()));
3086 
3087   if (field->is_map()) {
3088     const FieldDescriptor* key_field = MapFieldKey(field);
3089     const FieldDescriptor* value_field = MapFieldValue(field);
3090     printer->Print(
3091         "      var value = msg.get$name$();\n"
3092         "      reader.readMessage(value, function(message, reader) {\n",
3093         "name", JSGetterName(options, field));
3094 
3095     printer->Print(
3096         "        jspb.Map.deserializeBinary(message, reader, "
3097         "$keyReaderFn$, $valueReaderFn$",
3098         "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
3099         "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
3100 
3101     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3102       printer->Print(", $messageType$.deserializeBinaryFromReader",
3103                      "messageType",
3104                      GetMessagePath(options, value_field->message_type()));
3105     } else {
3106       printer->Print(", null");
3107     }
3108     printer->Print(", $defaultKey$", "defaultKey", JSFieldDefault(key_field));
3109     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3110       printer->Print(", new $messageType$()", "messageType",
3111                      GetMessagePath(options, value_field->message_type()));
3112     } else {
3113       printer->Print(", $defaultValue$", "defaultValue",
3114                      JSFieldDefault(value_field));
3115     }
3116     printer->Print(");\n");
3117     printer->Print("         });\n");
3118   } else {
3119     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
3120       printer->Print(
3121           "      var value = new $fieldclass$;\n"
3122           "      reader.read$msgOrGroup$($grpfield$value,"
3123           "$fieldclass$.deserializeBinaryFromReader);\n",
3124           "fieldclass", SubmessageTypeRef(options, field), "msgOrGroup",
3125           (field->type() == FieldDescriptor::TYPE_GROUP) ? "Group" : "Message",
3126           "grpfield",
3127           (field->type() == FieldDescriptor::TYPE_GROUP)
3128               ? (StrCat(field->number()) + ", ")
3129               : "");
3130     } else if (field->is_packable()) {
3131       printer->Print(
3132           "      var values = /** @type {$fieldtype$} */ "
3133           "(reader.isDelimited() "
3134           "? reader.readPacked$reader$() : [reader.read$reader$()]);\n",
3135           "fieldtype",
3136           JSFieldTypeAnnotation(options, field, false, true,
3137                                 /* singular_if_not_packed */ false, BYTES_U8),
3138           "reader", JSBinaryReaderMethodType(field));
3139     } else {
3140       printer->Print(
3141           "      var value = /** @type {$fieldtype$} */ "
3142           "(reader.read$reader$());\n",
3143           "fieldtype",
3144           JSFieldTypeAnnotation(options, field, false, true,
3145                                 /* singular_if_not_packed */ true, BYTES_U8),
3146           "reader",
3147           JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
3148     }
3149 
3150     if (field->is_packable()) {
3151       printer->Print(
3152           "      for (var i = 0; i < values.length; i++) {\n"
3153           "        msg.add$name$(values[i]);\n"
3154           "      }\n",
3155           "name",
3156           JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
3157     } else if (field->is_repeated()) {
3158       printer->Print(
3159           "      msg.add$name$(value);\n", "name",
3160           JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
3161     } else {
3162       // Singular fields, and packed repeated fields, receive a |value| either
3163       // as the field's value or as the array of all the field's values; set
3164       // this as the field's value directly.
3165       printer->Print("      msg.set$name$(value);\n", "name",
3166                      JSGetterName(options, field));
3167     }
3168   }
3169 
3170   printer->Print("      break;\n");
3171 }
3172 
GenerateClassSerializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const3173 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
3174                                              io::Printer* printer,
3175                                              const Descriptor* desc) const {
3176   printer->Print(
3177       "/**\n"
3178       " * Serializes the message to binary data (in protobuf wire format).\n"
3179       " * @return {!Uint8Array}\n"
3180       " */\n"
3181       "$class$.prototype.serializeBinary = function() {\n"
3182       "  var writer = new jspb.BinaryWriter();\n"
3183       "  $class$.serializeBinaryToWriter(this, writer);\n"
3184       "  return writer.getResultBuffer();\n"
3185       "};\n"
3186       "\n"
3187       "\n"
3188       "/**\n"
3189       " * Serializes the given message to binary data (in protobuf wire\n"
3190       " * format), writing to the given BinaryWriter.\n"
3191       " * @param {!$class$} message\n"
3192       " * @param {!jspb.BinaryWriter} writer\n"
3193       " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
3194       " */\n"
3195       "$class$.serializeBinaryToWriter = function(message, "
3196       "writer) {\n"
3197       "  var f = undefined;\n",
3198       "class", GetMessagePath(options, desc));
3199 
3200   for (int i = 0; i < desc->field_count(); i++) {
3201     if (!IgnoreField(desc->field(i))) {
3202       GenerateClassSerializeBinaryField(options, printer, desc->field(i));
3203     }
3204   }
3205 
3206   if (IsExtendable(desc)) {
3207     printer->Print(
3208         "  jspb.Message.serializeBinaryExtensions(message, writer,\n"
3209         "    $extobj$Binary, $class$.prototype.getExtension);\n",
3210         "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
3211         GetMessagePath(options, desc));
3212   }
3213 
3214   printer->Print(
3215       "};\n"
3216       "\n"
3217       "\n");
3218 }
3219 
GenerateClassSerializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3220 void Generator::GenerateClassSerializeBinaryField(
3221     const GeneratorOptions& options, io::Printer* printer,
3222     const FieldDescriptor* field) const {
3223   if (HasFieldPresence(options, field) &&
3224       field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
3225     std::string typed_annotation =
3226         JSFieldTypeAnnotation(options, field,
3227                               /* is_setter_argument = */ false,
3228                               /* force_present = */ false,
3229                               /* singular_if_not_packed = */ false,
3230                               /* bytes_mode = */ BYTES_DEFAULT);
3231     printer->Print(
3232         "  f = /** @type {$type$} */ "
3233         "(jspb.Message.getField(message, $index$));\n",
3234         "index", JSFieldIndex(field), "type", typed_annotation);
3235   } else {
3236     printer->Print(
3237         "  f = message.get$name$($nolazy$);\n", "name",
3238         JSGetterName(options, field, BYTES_U8),
3239         // No lazy creation for maps containers -- fastpath the empty case.
3240         "nolazy", field->is_map() ? "true" : "");
3241   }
3242 
3243   // Print an `if (condition)` statement that evaluates to true if the field
3244   // goes on the wire.
3245   if (field->is_map()) {
3246     printer->Print("  if (f && f.getLength() > 0) {\n");
3247   } else if (field->is_repeated()) {
3248     printer->Print("  if (f.length > 0) {\n");
3249   } else {
3250     if (HasFieldPresence(options, field)) {
3251       printer->Print("  if (f != null) {\n");
3252     } else {
3253       // No field presence: serialize onto the wire only if value is
3254       // non-default.  Defaults are documented here:
3255       // https://goto.google.com/lhdfm
3256       switch (field->cpp_type()) {
3257         case FieldDescriptor::CPPTYPE_INT32:
3258         case FieldDescriptor::CPPTYPE_INT64:
3259         case FieldDescriptor::CPPTYPE_UINT32:
3260         case FieldDescriptor::CPPTYPE_UINT64: {
3261           if (IsIntegralFieldWithStringJSType(field)) {
3262             // We can use `parseInt` here even though it will not be precise for
3263             // 64-bit quantities because we are only testing for zero/nonzero,
3264             // and JS numbers (64-bit floating point values, i.e., doubles) are
3265             // integer-precise in the range that includes zero.
3266             printer->Print("  if (parseInt(f, 10) !== 0) {\n");
3267           } else {
3268             printer->Print("  if (f !== 0) {\n");
3269           }
3270           break;
3271         }
3272 
3273         case FieldDescriptor::CPPTYPE_ENUM:
3274         case FieldDescriptor::CPPTYPE_FLOAT:
3275         case FieldDescriptor::CPPTYPE_DOUBLE:
3276           printer->Print("  if (f !== 0.0) {\n");
3277           break;
3278         case FieldDescriptor::CPPTYPE_BOOL:
3279           printer->Print("  if (f) {\n");
3280           break;
3281         case FieldDescriptor::CPPTYPE_STRING:
3282           printer->Print("  if (f.length > 0) {\n");
3283           break;
3284         default:
3285           assert(false);
3286           break;
3287       }
3288     }
3289   }
3290 
3291   // Write the field on the wire.
3292   if (field->is_map()) {
3293     const FieldDescriptor* key_field = MapFieldKey(field);
3294     const FieldDescriptor* value_field = MapFieldValue(field);
3295     printer->Print(
3296         "    f.serializeBinary($index$, writer, "
3297         "$keyWriterFn$, $valueWriterFn$",
3298         "index", StrCat(field->number()), "keyWriterFn",
3299         JSBinaryWriterMethodName(options, key_field), "valueWriterFn",
3300         JSBinaryWriterMethodName(options, value_field));
3301 
3302     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3303       printer->Print(", $messageType$.serializeBinaryToWriter", "messageType",
3304                      GetMessagePath(options, value_field->message_type()));
3305     }
3306 
3307     printer->Print(");\n");
3308   } else {
3309     printer->Print(
3310         "    writer.write$method$(\n"
3311         "      $index$,\n"
3312         "      f",
3313         "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
3314         "index", StrCat(field->number()));
3315 
3316     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
3317         !field->is_map()) {
3318       printer->Print(
3319           ",\n"
3320           "      $submsg$.serializeBinaryToWriter\n",
3321           "submsg", SubmessageTypeRef(options, field));
3322     } else {
3323       printer->Print("\n");
3324     }
3325 
3326     printer->Print("    );\n");
3327   }
3328 
3329   // Close the `if`.
3330   printer->Print("  }\n");
3331 }
3332 
GenerateEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc) const3333 void Generator::GenerateEnum(const GeneratorOptions& options,
3334                              io::Printer* printer,
3335                              const EnumDescriptor* enumdesc) const {
3336   printer->Print(
3337       "/**\n"
3338       " * @enum {number}\n"
3339       " */\n"
3340       "$enumprefix$$name$ = {\n",
3341       "enumprefix", GetEnumPathPrefix(options, enumdesc), "name",
3342       enumdesc->name());
3343   printer->Annotate("name", enumdesc);
3344 
3345   std::set<std::string> used_name;
3346   std::vector<int> valid_index;
3347   for (int i = 0; i < enumdesc->value_count(); i++) {
3348     if (enumdesc->options().allow_alias() &&
3349         !used_name.insert(ToEnumCase(enumdesc->value(i)->name())).second) {
3350       continue;
3351     }
3352     valid_index.push_back(i);
3353   }
3354   for (auto i : valid_index) {
3355     const EnumValueDescriptor* value = enumdesc->value(i);
3356     printer->Print("  $name$: $value$$comma$\n", "name",
3357                    ToEnumCase(value->name()), "value", StrCat(value->number()),
3358                    "comma", (i == valid_index.back()) ? "" : ",");
3359     printer->Annotate("name", value);
3360   }
3361 
3362   printer->Print(
3363       "};\n"
3364       "\n");
3365 }
3366 
GenerateExtension(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3367 void Generator::GenerateExtension(const GeneratorOptions& options,
3368                                   io::Printer* printer,
3369                                   const FieldDescriptor* field) const {
3370   std::string extension_scope =
3371       (field->extension_scope()
3372            ? GetMessagePath(options, field->extension_scope())
3373            : GetNamespace(options, field->file()));
3374 
3375   const std::string extension_object_name = JSObjectFieldName(options, field);
3376   printer->Print(
3377       "\n"
3378       "/**\n"
3379       " * A tuple of {field number, class constructor} for the extension\n"
3380       " * field named `$nameInComment$`.\n"
3381       " * @type {!jspb.ExtensionFieldInfo<$extensionType$>}\n"
3382       " */\n"
3383       "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
3384       "nameInComment", extension_object_name, "name", extension_object_name,
3385       "class", extension_scope, "extensionType",
3386       JSFieldTypeAnnotation(options, field,
3387                             /* is_setter_argument = */ false,
3388                             /* force_present = */ true,
3389                             /* singular_if_not_packed = */ false));
3390   printer->Annotate("name", field);
3391   printer->Print(
3392       "    $index$,\n"
3393       "    {$name$: 0},\n"
3394       "    $ctor$,\n"
3395       "     /** @type {?function((boolean|undefined),!jspb.Message=): "
3396       "!Object} */ (\n"
3397       "         $toObject$),\n"
3398       "    $repeated$);\n",
3399       "index", StrCat(field->number()), "name", extension_object_name, "ctor",
3400       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
3401            ? SubmessageTypeRef(options, field)
3402            : std::string("null")),
3403       "toObject",
3404       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
3405            ? (SubmessageTypeRef(options, field) + ".toObject")
3406            : std::string("null")),
3407       "repeated", (field->is_repeated() ? "1" : "0"));
3408 
3409   printer->Print(
3410       "\n"
3411       "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
3412       "    $class$.$name$,\n"
3413       "    $binaryReaderFn$,\n"
3414       "    $binaryWriterFn$,\n"
3415       "    $binaryMessageSerializeFn$,\n"
3416       "    $binaryMessageDeserializeFn$,\n",
3417       "extendName",
3418       JSExtensionsObjectName(options, field->file(), field->containing_type()),
3419       "index", StrCat(field->number()), "class", extension_scope, "name",
3420       extension_object_name, "binaryReaderFn",
3421       JSBinaryReaderMethodName(options, field), "binaryWriterFn",
3422       JSBinaryWriterMethodName(options, field), "binaryMessageSerializeFn",
3423       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
3424           ? (SubmessageTypeRef(options, field) + ".serializeBinaryToWriter")
3425           : "undefined",
3426       "binaryMessageDeserializeFn",
3427       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
3428           ? (SubmessageTypeRef(options, field) + ".deserializeBinaryFromReader")
3429           : "undefined");
3430 
3431   printer->Print("    $isPacked$);\n", "isPacked",
3432                  (field->is_packed() ? "true" : "false"));
3433 
3434   printer->Print(
3435       "// This registers the extension field with the extended class, so that\n"
3436       "// toObject() will function correctly.\n"
3437       "$extendName$[$index$] = $class$.$name$;\n"
3438       "\n",
3439       "extendName",
3440       JSExtensionsObjectName(options, field->file(), field->containing_type()),
3441       "index", StrCat(field->number()), "class", extension_scope, "name",
3442       extension_object_name);
3443 }
3444 
ParseFromOptions(const std::vector<std::pair<std::string,std::string>> & options,std::string * error)3445 bool GeneratorOptions::ParseFromOptions(
3446     const std::vector<std::pair<std::string, std::string> >& options,
3447     std::string* error) {
3448   for (int i = 0; i < options.size(); i++) {
3449     if (options[i].first == "add_require_for_enums") {
3450       if (options[i].second != "") {
3451         *error = "Unexpected option value for add_require_for_enums";
3452         return false;
3453       }
3454       add_require_for_enums = true;
3455     } else if (options[i].first == "binary") {
3456       if (options[i].second != "") {
3457         *error = "Unexpected option value for binary";
3458         return false;
3459       }
3460       binary = true;
3461     } else if (options[i].first == "testonly") {
3462       if (options[i].second != "") {
3463         *error = "Unexpected option value for testonly";
3464         return false;
3465       }
3466       testonly = true;
3467 
3468     } else if (options[i].first == "error_on_name_conflict") {
3469       GOOGLE_LOG(WARNING) << "Ignoring error_on_name_conflict option, this "
3470                              "will be removed in a future release";
3471     } else if (options[i].first == "output_dir") {
3472       output_dir = options[i].second;
3473     } else if (options[i].first == "namespace_prefix") {
3474       namespace_prefix = options[i].second;
3475     } else if (options[i].first == "library") {
3476       library = options[i].second;
3477     } else if (options[i].first == "import_style") {
3478       if (options[i].second == "closure") {
3479         import_style = kImportClosure;
3480       } else if (options[i].second == "commonjs") {
3481         import_style = kImportCommonJs;
3482       } else if (options[i].second == "commonjs_strict") {
3483         import_style = kImportCommonJsStrict;
3484       } else if (options[i].second == "browser") {
3485         import_style = kImportBrowser;
3486       } else if (options[i].second == "es6") {
3487         import_style = kImportEs6;
3488       } else {
3489         *error = "Unknown import style " + options[i].second + ", expected " +
3490                  "one of: closure, commonjs, browser, es6.";
3491       }
3492     } else if (options[i].first == "extension") {
3493       extension = options[i].second;
3494     } else if (options[i].first == "one_output_file_per_input_file") {
3495       if (!options[i].second.empty()) {
3496         *error = "Unexpected option value for one_output_file_per_input_file";
3497         return false;
3498       }
3499       one_output_file_per_input_file = true;
3500     } else if (options[i].first == "annotate_code") {
3501       if (!options[i].second.empty()) {
3502         *error = "Unexpected option value for annotate_code";
3503         return false;
3504       }
3505       annotate_code = true;
3506     } else {
3507       // Assume any other option is an output directory, as long as it is a bare
3508       // `key` rather than a `key=value` option.
3509       if (options[i].second != "") {
3510         *error = "Unknown option: " + options[i].first;
3511         return false;
3512       }
3513       output_dir = options[i].first;
3514     }
3515   }
3516 
3517   if (import_style != kImportClosure &&
3518       (add_require_for_enums || testonly || !library.empty() ||
3519        extension != ".js" || one_output_file_per_input_file)) {
3520     *error =
3521         "The add_require_for_enums, testonly, library, extension, and "
3522         "one_output_file_per_input_file options should only be "
3523         "used for import_style=closure";
3524     return false;
3525   }
3526 
3527   return true;
3528 }
3529 
output_mode() const3530 GeneratorOptions::OutputMode GeneratorOptions::output_mode() const {
3531   // We use one output file per input file if we are not using Closure or if
3532   // this is explicitly requested.
3533   if (import_style != kImportClosure || one_output_file_per_input_file) {
3534     return kOneOutputFilePerInputFile;
3535   }
3536 
3537   // If a library name is provided, we put everything in that one file.
3538   if (!library.empty()) {
3539     return kEverythingInOneFile;
3540   }
3541 
3542   // Otherwise, we create one output file per SCC.
3543   return kOneOutputFilePerSCC;
3544 }
3545 
GenerateFilesInDepOrder(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files) const3546 void Generator::GenerateFilesInDepOrder(
3547     const GeneratorOptions& options, io::Printer* printer,
3548     const std::vector<const FileDescriptor*>& files) const {
3549   // Build a std::set over all files so that the DFS can detect when it recurses
3550   // into a dep not specified in the user's command line.
3551   std::set<const FileDescriptor*> all_files(files.begin(), files.end());
3552   // Track the in-progress set of files that have been generated already.
3553   std::set<const FileDescriptor*> generated;
3554   for (int i = 0; i < files.size(); i++) {
3555     GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
3556   }
3557 }
3558 
GenerateFileAndDeps(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * root,std::set<const FileDescriptor * > * all_files,std::set<const FileDescriptor * > * generated) const3559 void Generator::GenerateFileAndDeps(
3560     const GeneratorOptions& options, io::Printer* printer,
3561     const FileDescriptor* root, std::set<const FileDescriptor*>* all_files,
3562     std::set<const FileDescriptor*>* generated) const {
3563   // Skip if already generated.
3564   if (generated->find(root) != generated->end()) {
3565     return;
3566   }
3567   generated->insert(root);
3568 
3569   // Generate all dependencies before this file's content.
3570   for (int i = 0; i < root->dependency_count(); i++) {
3571     const FileDescriptor* dep = root->dependency(i);
3572     GenerateFileAndDeps(options, printer, dep, all_files, generated);
3573   }
3574 
3575   // Generate this file's content.  Only generate if the file is part of the
3576   // original set requested to be generated; i.e., don't take all transitive
3577   // deps down to the roots.
3578   if (all_files->find(root) != all_files->end()) {
3579     GenerateClassesAndEnums(options, printer, root);
3580   }
3581 }
3582 
GenerateFile(const FileDescriptor * file,const GeneratorOptions & options,GeneratorContext * context,bool use_short_name) const3583 bool Generator::GenerateFile(const FileDescriptor* file,
3584                              const GeneratorOptions& options,
3585                              GeneratorContext* context,
3586                              bool use_short_name) const {
3587   std::string filename =
3588       options.output_dir + "/" +
3589       GetJSFilename(options, use_short_name
3590                                  ? file->name().substr(file->name().rfind('/'))
3591                                  : file->name());
3592   std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3593   GOOGLE_CHECK(output);
3594   GeneratedCodeInfo annotations;
3595   io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3596       &annotations);
3597   io::Printer printer(output.get(), '$',
3598                       options.annotate_code ? &annotation_collector : nullptr);
3599 
3600   GenerateFile(options, &printer, file);
3601 
3602   if (printer.failed()) {
3603     return false;
3604   }
3605 
3606   if (options.annotate_code) {
3607     EmbedCodeAnnotations(annotations, &printer);
3608   }
3609 
3610   return true;
3611 }
3612 
GenerateFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const3613 void Generator::GenerateFile(const GeneratorOptions& options,
3614                              io::Printer* printer,
3615                              const FileDescriptor* file) const {
3616   GenerateHeader(options, file, printer);
3617 
3618   // Generate "require" statements.
3619   if ((options.import_style == GeneratorOptions::kImportCommonJs ||
3620        options.import_style == GeneratorOptions::kImportCommonJsStrict)) {
3621     printer->Print("var jspb = require('google-protobuf');\n");
3622     printer->Print("var goog = jspb;\n");
3623 
3624     // Do not use global scope in strict mode
3625     if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
3626       printer->Print("var proto = {};\n\n");
3627     } else {
3628       // To get the global object we call a function with .call(null), this will
3629       // set "this" inside the function to the global object. This does not work
3630       // if we are running in strict mode ("use strict"), so we fallback to the
3631       // following things (in order from first to last):
3632       // - window: defined in browsers
3633       // - global: defined in most server side environments like NodeJS
3634       // - self: defined inside Web Workers (WorkerGlobalScope)
3635       // - Function('return this')(): this will work on most platforms, but it
3636       // may be blocked by things like CSP.
3637       //   Function('') is almost the same as eval('')
3638       printer->Print(
3639           "var global = (function() { return this || window || global || self "
3640           "|| Function('return this')(); }).call(null);\n\n");
3641     }
3642 
3643     for (int i = 0; i < file->dependency_count(); i++) {
3644       const std::string& name = file->dependency(i)->name();
3645       printer->Print(
3646           "var $alias$ = require('$file$');\n"
3647           "goog.object.extend(proto, $alias$);\n",
3648           "alias", ModuleAlias(name), "file",
3649           GetRootPath(file->name(), name) + GetJSFilename(options, name));
3650     }
3651   }
3652 
3653   std::set<std::string> provided;
3654   std::set<const FieldDescriptor*> extensions;
3655   for (int i = 0; i < file->extension_count(); i++) {
3656     // We honor the jspb::ignore option here only when working with
3657     // Closure-style imports. Use of this option is discouraged and so we want
3658     // to avoid adding new support for it.
3659     if (options.import_style == GeneratorOptions::kImportClosure &&
3660         IgnoreField(file->extension(i))) {
3661       continue;
3662     }
3663     provided.insert(GetNamespace(options, file) + "." +
3664                     JSObjectFieldName(options, file->extension(i)));
3665     extensions.insert(file->extension(i));
3666   }
3667 
3668   FindProvidesForFile(options, printer, file, &provided);
3669   GenerateProvides(options, printer, &provided);
3670   std::vector<const FileDescriptor*> files;
3671   files.push_back(file);
3672   if (options.import_style == GeneratorOptions::kImportClosure) {
3673     GenerateRequiresForLibrary(options, printer, files, &provided);
3674   }
3675 
3676   GenerateClassesAndEnums(options, printer, file);
3677 
3678   // Generate code for top-level extensions. Extensions nested inside messages
3679   // are emitted inside GenerateClassesAndEnums().
3680   for (std::set<const FieldDescriptor*>::const_iterator it = extensions.begin();
3681        it != extensions.end(); ++it) {
3682     GenerateExtension(options, printer, *it);
3683   }
3684 
3685   // if provided is empty, do not export anything
3686   if (options.import_style == GeneratorOptions::kImportCommonJs &&
3687       !provided.empty()) {
3688     printer->Print("goog.object.extend(exports, $package$);\n", "package",
3689                    GetNamespace(options, file));
3690   } else if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
3691     printer->Print("goog.object.extend(exports, proto);\n", "package",
3692                    GetNamespace(options, file));
3693   }
3694 
3695   // Emit well-known type methods.
3696   for (FileToc* toc = well_known_types_js; toc->name != NULL; toc++) {
3697     std::string name = std::string("google/protobuf/") + toc->name;
3698     if (name == StripProto(file->name()) + ".js") {
3699       printer->Print(toc->data);
3700     }
3701   }
3702 }
3703 
GenerateAll(const std::vector<const FileDescriptor * > & files,const std::string & parameter,GeneratorContext * context,std::string * error) const3704 bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
3705                             const std::string& parameter,
3706                             GeneratorContext* context,
3707                             std::string* error) const {
3708   std::vector<std::pair<std::string, std::string> > option_pairs;
3709   ParseGeneratorParameter(parameter, &option_pairs);
3710   GeneratorOptions options;
3711   if (!options.ParseFromOptions(option_pairs, error)) {
3712     return false;
3713   }
3714 
3715   if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
3716     // All output should go in a single file.
3717     std::string filename = options.output_dir + "/" + options.library +
3718                            options.GetFileNameExtension();
3719     std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3720     GOOGLE_CHECK(output.get());
3721     GeneratedCodeInfo annotations;
3722     io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3723         &annotations);
3724     io::Printer printer(
3725         output.get(), '$',
3726         options.annotate_code ? &annotation_collector : nullptr);
3727 
3728     // Pull out all extensions -- we need these to generate all
3729     // provides/requires.
3730     std::vector<const FieldDescriptor*> extensions;
3731     for (int i = 0; i < files.size(); i++) {
3732       for (int j = 0; j < files[i]->extension_count(); j++) {
3733         const FieldDescriptor* extension = files[i]->extension(j);
3734         extensions.push_back(extension);
3735       }
3736     }
3737 
3738     if (files.size() == 1) {
3739       GenerateHeader(options, files[0], &printer);
3740     } else {
3741       GenerateHeader(options, nullptr, &printer);
3742     }
3743 
3744     std::set<std::string> provided;
3745     FindProvides(options, &printer, files, &provided);
3746     FindProvidesForFields(options, &printer, extensions, &provided);
3747     GenerateProvides(options, &printer, &provided);
3748     GenerateTestOnly(options, &printer);
3749     GenerateRequiresForLibrary(options, &printer, files, &provided);
3750 
3751     GenerateFilesInDepOrder(options, &printer, files);
3752 
3753     for (int i = 0; i < extensions.size(); i++) {
3754       if (ShouldGenerateExtension(extensions[i])) {
3755         GenerateExtension(options, &printer, extensions[i]);
3756       }
3757     }
3758 
3759     if (printer.failed()) {
3760       return false;
3761     }
3762     if (options.annotate_code) {
3763       EmbedCodeAnnotations(annotations, &printer);
3764     }
3765   } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerSCC) {
3766     std::set<const Descriptor*> have_printed;
3767     SCCAnalyzer<DepsGenerator> analyzer;
3768     std::map<const void*, std::string> allowed_map;
3769     if (!GenerateJspbAllowedMap(options, files, &allowed_map, &analyzer)) {
3770       return false;
3771     }
3772 
3773     bool generated = false;
3774     for (int i = 0; i < files.size(); i++) {
3775       const FileDescriptor* file = files[i];
3776       // Force well known type to generate in a whole file.
3777       if (IsWellKnownTypeFile(file)) {
3778         if (!GenerateFile(file, options, context, true)) {
3779           return false;
3780         }
3781         generated = true;
3782         continue;
3783       }
3784       for (int j = 0; j < file->message_type_count(); j++) {
3785         const Descriptor* desc = file->message_type(j);
3786         if (have_printed.count(desc) ||
3787             allowed_map.count(analyzer.GetSCC(desc)) == 0) {
3788           continue;
3789         }
3790 
3791         generated = true;
3792         const SCC* scc = analyzer.GetSCC(desc);
3793         const std::string& filename = allowed_map[scc];
3794         std::unique_ptr<io::ZeroCopyOutputStream> output(
3795             context->Open(filename));
3796         GOOGLE_CHECK(output.get());
3797         GeneratedCodeInfo annotations;
3798         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3799             &annotations);
3800         io::Printer printer(
3801             output.get(), '$',
3802             options.annotate_code ? &annotation_collector : nullptr);
3803 
3804         GenerateHeader(options, file, &printer);
3805 
3806         std::set<std::string> provided;
3807         for (auto one_desc : scc->descriptors) {
3808           if (one_desc->containing_type() == nullptr) {
3809             FindProvidesForMessage(options, &printer, one_desc, &provided);
3810           }
3811         }
3812         GenerateProvides(options, &printer, &provided);
3813         GenerateTestOnly(options, &printer);
3814         GenerateRequiresForSCC(options, &printer, scc, &provided);
3815 
3816         for (auto one_desc : scc->descriptors) {
3817           if (one_desc->containing_type() == nullptr) {
3818             GenerateClassConstructorAndDeclareExtensionFieldInfo(
3819                 options, &printer, one_desc);
3820           }
3821         }
3822         for (auto one_desc : scc->descriptors) {
3823           if (one_desc->containing_type() == nullptr) {
3824             GenerateClass(options, &printer, one_desc);
3825           }
3826         }
3827 
3828         for (auto one_desc : scc->descriptors) {
3829           have_printed.insert(one_desc);
3830         }
3831 
3832         if (printer.failed()) {
3833           return false;
3834         }
3835         if (options.annotate_code) {
3836           EmbedCodeAnnotations(annotations, &printer);
3837         }
3838       }
3839       for (int j = 0; j < file->enum_type_count(); j++) {
3840         const EnumDescriptor* enumdesc = file->enum_type(j);
3841         if (allowed_map.count(enumdesc) == 0) {
3842           continue;
3843         }
3844 
3845         generated = true;
3846         const std::string& filename = allowed_map[enumdesc];
3847         std::unique_ptr<io::ZeroCopyOutputStream> output(
3848             context->Open(filename));
3849         GOOGLE_CHECK(output.get());
3850         GeneratedCodeInfo annotations;
3851         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3852             &annotations);
3853         io::Printer printer(
3854             output.get(), '$',
3855             options.annotate_code ? &annotation_collector : nullptr);
3856 
3857         GenerateHeader(options, file, &printer);
3858 
3859         std::set<std::string> provided;
3860         FindProvidesForEnum(options, &printer, enumdesc, &provided);
3861         GenerateProvides(options, &printer, &provided);
3862         GenerateTestOnly(options, &printer);
3863 
3864         GenerateEnum(options, &printer, enumdesc);
3865 
3866         if (printer.failed()) {
3867           return false;
3868         }
3869         if (options.annotate_code) {
3870           EmbedCodeAnnotations(annotations, &printer);
3871         }
3872       }
3873       // File-level extensions (message-level extensions are generated under
3874       // the enclosing message).
3875       if (allowed_map.count(file) == 1) {
3876         generated = true;
3877         const std::string& filename = allowed_map[file];
3878 
3879         std::unique_ptr<io::ZeroCopyOutputStream> output(
3880             context->Open(filename));
3881         GOOGLE_CHECK(output.get());
3882         GeneratedCodeInfo annotations;
3883         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3884             &annotations);
3885         io::Printer printer(
3886             output.get(), '$',
3887             options.annotate_code ? &annotation_collector : nullptr);
3888 
3889         GenerateHeader(options, file, &printer);
3890 
3891         std::set<std::string> provided;
3892         std::vector<const FieldDescriptor*> fields;
3893 
3894         for (int j = 0; j < files[i]->extension_count(); j++) {
3895           if (ShouldGenerateExtension(files[i]->extension(j))) {
3896             fields.push_back(files[i]->extension(j));
3897           }
3898         }
3899 
3900         FindProvidesForFields(options, &printer, fields, &provided);
3901         GenerateProvides(options, &printer, &provided);
3902         GenerateTestOnly(options, &printer);
3903         GenerateRequiresForExtensions(options, &printer, fields, &provided);
3904 
3905         for (int j = 0; j < files[i]->extension_count(); j++) {
3906           if (ShouldGenerateExtension(files[i]->extension(j))) {
3907             GenerateExtension(options, &printer, files[i]->extension(j));
3908           }
3909         }
3910         if (options.annotate_code) {
3911           EmbedCodeAnnotations(annotations, &printer);
3912         }
3913       }
3914     }
3915     if (!generated) {
3916       std::string filename = options.output_dir + "/" +
3917                              "empty_no_content_void_file" +
3918                              options.GetFileNameExtension();
3919       std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3920     }
3921   } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
3922     // Generate one output file per input (.proto) file.
3923 
3924     for (int i = 0; i < files.size(); i++) {
3925       const FileDescriptor* file = files[i];
3926       if (!GenerateFile(file, options, context, false)) {
3927         return false;
3928       }
3929     }
3930   }
3931   return true;
3932 }
3933 
3934 }  // namespace js
3935 }  // namespace compiler
3936 }  // namespace protobuf
3937 }  // namespace google
3938