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