• 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::__anon700c02930111::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       "\n");
1658 }
1659 
FindProvidesForFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file,std::set<std::string> * provided) const1660 void Generator::FindProvidesForFile(const GeneratorOptions& options,
1661                                     io::Printer* printer,
1662                                     const FileDescriptor* file,
1663                                     std::set<std::string>* provided) const {
1664   for (int i = 0; i < file->message_type_count(); i++) {
1665     FindProvidesForMessage(options, printer, file->message_type(i), provided);
1666   }
1667   for (int i = 0; i < file->enum_type_count(); i++) {
1668     FindProvidesForEnum(options, printer, file->enum_type(i), provided);
1669   }
1670 }
1671 
FindProvides(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files,std::set<std::string> * provided) const1672 void Generator::FindProvides(const GeneratorOptions& options,
1673                              io::Printer* printer,
1674                              const std::vector<const FileDescriptor*>& files,
1675                              std::set<std::string>* provided) const {
1676   for (int i = 0; i < files.size(); i++) {
1677     FindProvidesForFile(options, printer, files[i], provided);
1678   }
1679 
1680   printer->Print("\n");
1681 }
1682 
FindProvidesForOneOfEnum(const GeneratorOptions & options,const OneofDescriptor * oneof,std::set<std::string> * provided)1683 void FindProvidesForOneOfEnum(const GeneratorOptions& options,
1684                               const OneofDescriptor* oneof,
1685                               std::set<std::string>* provided) {
1686   std::string name = GetMessagePath(options, oneof->containing_type()) + "." +
1687                      JSOneofName(oneof) + "Case";
1688   provided->insert(name);
1689 }
1690 
FindProvidesForOneOfEnums(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<std::string> * provided)1691 void FindProvidesForOneOfEnums(const GeneratorOptions& options,
1692                                io::Printer* printer, const Descriptor* desc,
1693                                std::set<std::string>* provided) {
1694   if (HasOneofFields(desc)) {
1695     for (int i = 0; i < desc->oneof_decl_count(); i++) {
1696       if (IgnoreOneof(desc->oneof_decl(i))) {
1697         continue;
1698       }
1699       FindProvidesForOneOfEnum(options, desc->oneof_decl(i), provided);
1700     }
1701   }
1702 }
1703 
FindProvidesForMessage(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<std::string> * provided) const1704 void Generator::FindProvidesForMessage(const GeneratorOptions& options,
1705                                        io::Printer* printer,
1706                                        const Descriptor* desc,
1707                                        std::set<std::string>* provided) const {
1708   if (IgnoreMessage(desc)) {
1709     return;
1710   }
1711 
1712   std::string name = GetMessagePath(options, desc);
1713   provided->insert(name);
1714 
1715   for (int i = 0; i < desc->enum_type_count(); i++) {
1716     FindProvidesForEnum(options, printer, desc->enum_type(i), provided);
1717   }
1718 
1719   FindProvidesForOneOfEnums(options, printer, desc, provided);
1720 
1721   for (int i = 0; i < desc->nested_type_count(); i++) {
1722     FindProvidesForMessage(options, printer, desc->nested_type(i), provided);
1723   }
1724 }
FindProvidesForEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc,std::set<std::string> * provided) const1725 void Generator::FindProvidesForEnum(const GeneratorOptions& options,
1726                                     io::Printer* printer,
1727                                     const EnumDescriptor* enumdesc,
1728                                     std::set<std::string>* provided) const {
1729   std::string name = GetEnumPath(options, enumdesc);
1730   provided->insert(name);
1731 }
1732 
FindProvidesForFields(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FieldDescriptor * > & fields,std::set<std::string> * provided) const1733 void Generator::FindProvidesForFields(
1734     const GeneratorOptions& options, io::Printer* printer,
1735     const std::vector<const FieldDescriptor*>& fields,
1736     std::set<std::string>* provided) const {
1737   for (int i = 0; i < fields.size(); i++) {
1738     const FieldDescriptor* field = fields[i];
1739 
1740     if (IgnoreField(field)) {
1741       continue;
1742     }
1743 
1744     std::string name = GetNamespace(options, field->file()) + "." +
1745                        JSObjectFieldName(options, field);
1746     provided->insert(name);
1747   }
1748 }
1749 
GenerateProvides(const GeneratorOptions & options,io::Printer * printer,std::set<std::string> * provided) const1750 void Generator::GenerateProvides(const GeneratorOptions& options,
1751                                  io::Printer* printer,
1752                                  std::set<std::string>* provided) const {
1753   for (std::set<std::string>::iterator it = provided->begin();
1754        it != provided->end(); ++it) {
1755     if (options.import_style == GeneratorOptions::kImportClosure) {
1756       printer->Print("goog.provide('$name$');\n", "name", *it);
1757     } else {
1758       // We aren't using Closure's import system, but we use goog.exportSymbol()
1759       // to construct the expected tree of objects, eg.
1760       //
1761       //   goog.exportSymbol('foo.bar.Baz', null, this);
1762       //
1763       //   // Later generated code expects foo.bar = {} to exist:
1764       //   foo.bar.Baz = function() { /* ... */ }
1765 
1766       // Do not use global scope in strict mode
1767       if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
1768         std::string namespaceObject = *it;
1769         // Remove "proto." from the namespace object
1770         GOOGLE_CHECK_EQ(0, namespaceObject.compare(0, 6, "proto."));
1771         namespaceObject.erase(0, 6);
1772         printer->Print("goog.exportSymbol('$name$', null, proto);\n", "name",
1773                        namespaceObject);
1774       } else {
1775         printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
1776                        *it);
1777       }
1778     }
1779   }
1780 }
1781 
GenerateRequiresForSCC(const GeneratorOptions & options,io::Printer * printer,const SCC * scc,std::set<std::string> * provided) const1782 void Generator::GenerateRequiresForSCC(const GeneratorOptions& options,
1783                                        io::Printer* printer, const SCC* scc,
1784                                        std::set<std::string>* provided) const {
1785   std::set<std::string> required;
1786   std::set<std::string> forwards;
1787   bool have_message = false;
1788   bool has_extension = false;
1789   bool has_map = false;
1790   for (auto desc : scc->descriptors) {
1791     if (desc->containing_type() == nullptr) {
1792       FindRequiresForMessage(options, desc, &required, &forwards,
1793                              &have_message);
1794       has_extension = (has_extension || HasExtensions(desc));
1795       has_map = (has_map || HasMap(options, desc));
1796     }
1797   }
1798 
1799   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1800                        /* require_jspb = */ have_message,
1801                        /* require_extension = */ has_extension,
1802                        /* require_map = */ has_map);
1803 }
1804 
GenerateRequiresForLibrary(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files,std::set<std::string> * provided) const1805 void Generator::GenerateRequiresForLibrary(
1806     const GeneratorOptions& options, io::Printer* printer,
1807     const std::vector<const FileDescriptor*>& files,
1808     std::set<std::string>* provided) const {
1809   GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
1810   // For Closure imports we need to import every message type individually.
1811   std::set<std::string> required;
1812   std::set<std::string> forwards;
1813   bool have_extensions = false;
1814   bool have_map = false;
1815   bool have_message = false;
1816 
1817   for (int i = 0; i < files.size(); i++) {
1818     for (int j = 0; j < files[i]->message_type_count(); j++) {
1819       const Descriptor* desc = files[i]->message_type(j);
1820       if (!IgnoreMessage(desc)) {
1821         FindRequiresForMessage(options, desc, &required, &forwards,
1822                                &have_message);
1823       }
1824     }
1825 
1826     if (!have_extensions && HasExtensions(files[i])) {
1827       have_extensions = true;
1828     }
1829 
1830     if (!have_map && FileHasMap(options, files[i])) {
1831       have_map = true;
1832     }
1833 
1834     for (int j = 0; j < files[i]->extension_count(); j++) {
1835       const FieldDescriptor* extension = files[i]->extension(j);
1836       if (IgnoreField(extension)) {
1837         continue;
1838       }
1839       if (extension->containing_type()->full_name() !=
1840           "google.protobuf.bridge.MessageSet") {
1841         required.insert(GetMessagePath(options, extension->containing_type()));
1842       }
1843       FindRequiresForField(options, extension, &required, &forwards);
1844       have_extensions = true;
1845     }
1846   }
1847 
1848   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1849                        /* require_jspb = */ have_message,
1850                        /* require_extension = */ have_extensions,
1851                        /* require_map = */ have_map);
1852 }
1853 
GenerateRequiresForExtensions(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FieldDescriptor * > & fields,std::set<std::string> * provided) const1854 void Generator::GenerateRequiresForExtensions(
1855     const GeneratorOptions& options, io::Printer* printer,
1856     const std::vector<const FieldDescriptor*>& fields,
1857     std::set<std::string>* provided) const {
1858   std::set<std::string> required;
1859   std::set<std::string> forwards;
1860   for (int i = 0; i < fields.size(); i++) {
1861     const FieldDescriptor* field = fields[i];
1862     if (IgnoreField(field)) {
1863       continue;
1864     }
1865     FindRequiresForExtension(options, field, &required, &forwards);
1866   }
1867 
1868   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1869                        /* require_jspb = */ false,
1870                        /* require_extension = */ fields.size() > 0,
1871                        /* require_map = */ false);
1872 }
1873 
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) const1874 void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
1875                                      io::Printer* printer,
1876                                      std::set<std::string>* required,
1877                                      std::set<std::string>* forwards,
1878                                      std::set<std::string>* provided,
1879                                      bool require_jspb, bool require_extension,
1880                                      bool require_map) const {
1881   if (require_jspb) {
1882     required->insert("jspb.Message");
1883     required->insert("jspb.BinaryReader");
1884     required->insert("jspb.BinaryWriter");
1885   }
1886   if (require_extension) {
1887     required->insert("jspb.ExtensionFieldBinaryInfo");
1888     required->insert("jspb.ExtensionFieldInfo");
1889   }
1890   if (require_map) {
1891     required->insert("jspb.Map");
1892   }
1893 
1894   std::set<std::string>::iterator it;
1895   for (it = required->begin(); it != required->end(); ++it) {
1896     if (provided->find(*it) != provided->end()) {
1897       continue;
1898     }
1899     printer->Print("goog.require('$name$');\n", "name", *it);
1900   }
1901 
1902   printer->Print("\n");
1903 
1904   for (it = forwards->begin(); it != forwards->end(); ++it) {
1905     if (provided->find(*it) != provided->end()) {
1906       continue;
1907     }
1908     printer->Print("goog.forwardDeclare('$name$');\n", "name", *it);
1909   }
1910 }
1911 
NamespaceOnly(const Descriptor * desc)1912 bool NamespaceOnly(const Descriptor* desc) {
1913   return false;
1914 }
1915 
FindRequiresForMessage(const GeneratorOptions & options,const Descriptor * desc,std::set<std::string> * required,std::set<std::string> * forwards,bool * have_message) const1916 void Generator::FindRequiresForMessage(const GeneratorOptions& options,
1917                                        const Descriptor* desc,
1918                                        std::set<std::string>* required,
1919                                        std::set<std::string>* forwards,
1920                                        bool* have_message) const {
1921 
1922   if (!NamespaceOnly(desc)) {
1923     *have_message = true;
1924     for (int i = 0; i < desc->field_count(); i++) {
1925       const FieldDescriptor* field = desc->field(i);
1926       if (IgnoreField(field)) {
1927         continue;
1928       }
1929       FindRequiresForField(options, field, required, forwards);
1930     }
1931   }
1932 
1933   for (int i = 0; i < desc->extension_count(); i++) {
1934     const FieldDescriptor* field = desc->extension(i);
1935     if (IgnoreField(field)) {
1936       continue;
1937     }
1938     FindRequiresForExtension(options, field, required, forwards);
1939   }
1940 
1941   for (int i = 0; i < desc->nested_type_count(); i++) {
1942     FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
1943                            have_message);
1944   }
1945 }
1946 
FindRequiresForField(const GeneratorOptions & options,const FieldDescriptor * field,std::set<std::string> * required,std::set<std::string> * forwards) const1947 void Generator::FindRequiresForField(const GeneratorOptions& options,
1948                                      const FieldDescriptor* field,
1949                                      std::set<std::string>* required,
1950                                      std::set<std::string>* forwards) const {
1951   if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1952       // N.B.: file-level extensions with enum type do *not* create
1953       // dependencies, as per original codegen.
1954       !(field->is_extension() && field->extension_scope() == nullptr)) {
1955     if (options.add_require_for_enums) {
1956       required->insert(GetEnumPath(options, field->enum_type()));
1957     } else {
1958       forwards->insert(GetEnumPath(options, field->enum_type()));
1959     }
1960   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1961     if (!IgnoreMessage(field->message_type())) {
1962       required->insert(GetMessagePath(options, field->message_type()));
1963     }
1964   }
1965 }
1966 
FindRequiresForExtension(const GeneratorOptions & options,const FieldDescriptor * field,std::set<std::string> * required,std::set<std::string> * forwards) const1967 void Generator::FindRequiresForExtension(
1968     const GeneratorOptions& options, const FieldDescriptor* field,
1969     std::set<std::string>* required, std::set<std::string>* forwards) const {
1970   if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
1971     required->insert(GetMessagePath(options, field->containing_type()));
1972   }
1973   FindRequiresForField(options, field, required, forwards);
1974 }
1975 
GenerateTestOnly(const GeneratorOptions & options,io::Printer * printer) const1976 void Generator::GenerateTestOnly(const GeneratorOptions& options,
1977                                  io::Printer* printer) const {
1978   if (options.testonly) {
1979     printer->Print("goog.setTestOnly();\n\n");
1980   }
1981   printer->Print("\n");
1982 }
1983 
GenerateClassesAndEnums(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const1984 void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
1985                                         io::Printer* printer,
1986                                         const FileDescriptor* file) const {
1987   for (int i = 0; i < file->message_type_count(); i++) {
1988     GenerateClassConstructorAndDeclareExtensionFieldInfo(options, printer,
1989                                                          file->message_type(i));
1990   }
1991   for (int i = 0; i < file->message_type_count(); i++) {
1992     GenerateClass(options, printer, file->message_type(i));
1993   }
1994   for (int i = 0; i < file->enum_type_count(); i++) {
1995     GenerateEnum(options, printer, file->enum_type(i));
1996   }
1997 }
1998 
GenerateClass(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1999 void Generator::GenerateClass(const GeneratorOptions& options,
2000                               io::Printer* printer,
2001                               const Descriptor* desc) const {
2002   if (IgnoreMessage(desc)) {
2003     return;
2004   }
2005 
2006   if (!NamespaceOnly(desc)) {
2007     printer->Print("\n");
2008     GenerateClassFieldInfo(options, printer, desc);
2009 
2010 
2011     GenerateClassToObject(options, printer, desc);
2012     // These must come *before* the extension-field info generation in
2013     // GenerateClassRegistration so that references to the binary
2014     // serialization/deserialization functions may be placed in the extension
2015     // objects.
2016     GenerateClassDeserializeBinary(options, printer, desc);
2017     GenerateClassSerializeBinary(options, printer, desc);
2018   }
2019 
2020   // Recurse on nested types. These must come *before* the extension-field
2021   // info generation in GenerateClassRegistration so that extensions that
2022   // reference nested types proceed the definitions of the nested types.
2023   for (int i = 0; i < desc->enum_type_count(); i++) {
2024     GenerateEnum(options, printer, desc->enum_type(i));
2025   }
2026   for (int i = 0; i < desc->nested_type_count(); i++) {
2027     GenerateClass(options, printer, desc->nested_type(i));
2028   }
2029 
2030   if (!NamespaceOnly(desc)) {
2031     GenerateClassRegistration(options, printer, desc);
2032     GenerateClassFields(options, printer, desc);
2033 
2034     if (options.import_style != GeneratorOptions::kImportClosure) {
2035       for (int i = 0; i < desc->extension_count(); i++) {
2036         GenerateExtension(options, printer, desc->extension(i));
2037       }
2038     }
2039   }
2040 }
2041 
GenerateClassConstructor(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2042 void Generator::GenerateClassConstructor(const GeneratorOptions& options,
2043                                          io::Printer* printer,
2044                                          const Descriptor* desc) const {
2045   printer->Print(
2046       "/**\n"
2047       " * Generated by JsPbCodeGenerator.\n"
2048       " * @param {Array=} opt_data Optional initial data array, typically "
2049       "from a\n"
2050       " * server response, or constructed directly in Javascript. The array "
2051       "is used\n"
2052       " * in place and becomes part of the constructed object. It is not "
2053       "cloned.\n"
2054       " * If no data is provided, the constructed object will be empty, but "
2055       "still\n"
2056       " * valid.\n"
2057       " * @extends {jspb.Message}\n"
2058       " * @constructor\n"
2059       " */\n"
2060       "$classprefix$$classname$ = function(opt_data) {\n",
2061       "classprefix", GetMessagePathPrefix(options, desc), "classname",
2062       desc->name());
2063   printer->Annotate("classname", desc);
2064   std::string message_id = GetMessageId(desc);
2065   printer->Print(
2066       "  jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
2067       "$rptfields$, $oneoffields$);\n",
2068       "messageId",
2069       !message_id.empty() ? ("'" + message_id + "'")
2070                           : (IsResponse(desc) ? "''" : "0"),
2071       "pivot", GetPivot(desc), "rptfields",
2072       RepeatedFieldsArrayName(options, desc), "oneoffields",
2073       OneofFieldsArrayName(options, desc));
2074   printer->Print(
2075       "};\n"
2076       "goog.inherits($classname$, jspb.Message);\n"
2077       "if (goog.DEBUG && !COMPILED) {\n"
2078       // displayName overrides Function.prototype.displayName
2079       // http://google3/javascript/externs/es3.js?l=511
2080       "  /**\n"
2081       "   * @public\n"
2082       "   * @override\n"
2083       "   */\n"
2084       "  $classname$.displayName = '$classname$';\n"
2085       "}\n",
2086       "classname", GetMessagePath(options, desc));
2087 }
2088 
GenerateClassConstructorAndDeclareExtensionFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2089 void Generator::GenerateClassConstructorAndDeclareExtensionFieldInfo(
2090     const GeneratorOptions& options, io::Printer* printer,
2091     const Descriptor* desc) const {
2092   if (!NamespaceOnly(desc)) {
2093     GenerateClassConstructor(options, printer, desc);
2094     if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
2095       GenerateClassExtensionFieldInfo(options, printer, desc);
2096     }
2097   }
2098   for (int i = 0; i < desc->nested_type_count(); i++) {
2099     if (!IgnoreMessage(desc->nested_type(i))) {
2100       GenerateClassConstructorAndDeclareExtensionFieldInfo(
2101           options, printer, desc->nested_type(i));
2102     }
2103   }
2104 }
2105 
GenerateClassFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2106 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
2107                                        io::Printer* printer,
2108                                        const Descriptor* desc) const {
2109   if (HasRepeatedFields(options, desc)) {
2110     printer->Print(
2111         "/**\n"
2112         " * List of repeated fields within this message type.\n"
2113         " * @private {!Array<number>}\n"
2114         " * @const\n"
2115         " */\n"
2116         "$classname$$rptfieldarray$ = $rptfields$;\n"
2117         "\n",
2118         "classname", GetMessagePath(options, desc), "rptfieldarray",
2119         kRepeatedFieldArrayName, "rptfields",
2120         RepeatedFieldNumberList(options, desc));
2121   }
2122 
2123   if (HasOneofFields(desc)) {
2124     printer->Print(
2125         "/**\n"
2126         " * Oneof group definitions for this message. Each group defines the "
2127         "field\n"
2128         " * numbers belonging to that group. When of these fields' value is "
2129         "set, all\n"
2130         " * other fields in the group are cleared. During deserialization, if "
2131         "multiple\n"
2132         " * fields are encountered for a group, only the last value seen will "
2133         "be kept.\n"
2134         " * @private {!Array<!Array<number>>}\n"
2135         " * @const\n"
2136         " */\n"
2137         "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
2138         "\n",
2139         "classname", GetMessagePath(options, desc), "oneofgrouparray",
2140         kOneofGroupArrayName, "oneofgroups", OneofGroupList(desc));
2141 
2142     for (int i = 0; i < desc->oneof_decl_count(); i++) {
2143       if (IgnoreOneof(desc->oneof_decl(i))) {
2144         continue;
2145       }
2146       GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
2147     }
2148   }
2149 }
2150 
GenerateClassXid(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2151 void Generator::GenerateClassXid(const GeneratorOptions& options,
2152                                  io::Printer* printer,
2153                                  const Descriptor* desc) const {
2154   printer->Print(
2155       "\n"
2156       "\n"
2157       "$class$.prototype.messageXid = xid('$class$');\n",
2158       "class", GetMessagePath(options, desc));
2159 }
2160 
GenerateOneofCaseDefinition(const GeneratorOptions & options,io::Printer * printer,const OneofDescriptor * oneof) const2161 void Generator::GenerateOneofCaseDefinition(
2162     const GeneratorOptions& options, io::Printer* printer,
2163     const OneofDescriptor* oneof) const {
2164   printer->Print(
2165       "/**\n"
2166       " * @enum {number}\n"
2167       " */\n"
2168       "$classname$.$oneof$Case = {\n"
2169       "  $upcase$_NOT_SET: 0",
2170       "classname", GetMessagePath(options, oneof->containing_type()), "oneof",
2171       JSOneofName(oneof), "upcase", ToEnumCase(oneof->name()));
2172 
2173   for (int i = 0; i < oneof->field_count(); i++) {
2174     if (IgnoreField(oneof->field(i))) {
2175       continue;
2176     }
2177 
2178     printer->Print(
2179         ",\n"
2180         "  $upcase$: $number$",
2181         "upcase", ToEnumCase(oneof->field(i)->name()), "number",
2182         JSFieldIndex(oneof->field(i)));
2183     printer->Annotate("upcase", oneof->field(i));
2184   }
2185 
2186   printer->Print(
2187       "\n"
2188       "};\n"
2189       "\n"
2190       "/**\n"
2191       " * @return {$class$.$oneof$Case}\n"
2192       " */\n"
2193       "$class$.prototype.get$oneof$Case = function() {\n"
2194       "  return /** @type {$class$.$oneof$Case} */(jspb.Message."
2195       "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
2196       "};\n"
2197       "\n",
2198       "class", GetMessagePath(options, oneof->containing_type()), "oneof",
2199       JSOneofName(oneof), "oneofindex", JSOneofIndex(oneof));
2200 }
2201 
GenerateClassToObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2202 void Generator::GenerateClassToObject(const GeneratorOptions& options,
2203                                       io::Printer* printer,
2204                                       const Descriptor* desc) const {
2205   printer->Print(
2206       "\n"
2207       "\n"
2208       "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
2209       "/**\n"
2210       " * Creates an object representation of this proto.\n"
2211       " * Field names that are reserved in JavaScript and will be renamed to "
2212       "pb_name.\n"
2213       " * Optional fields that are not set will be set to undefined.\n"
2214       " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
2215       " * For the list of reserved names please see:\n"
2216       " *     net/proto2/compiler/js/internal/generator.cc#kKeyword.\n"
2217       " * @param {boolean=} opt_includeInstance Deprecated. whether to include "
2218       "the\n"
2219       " *     JSPB instance for transitional soy proto support:\n"
2220       " *     http://goto/soy-param-migration\n"
2221       " * @return {!Object}\n"
2222       " */\n"
2223       "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
2224       "  return $classname$.toObject(opt_includeInstance, this);\n"
2225       "};\n"
2226       "\n"
2227       "\n"
2228       "/**\n"
2229       " * Static version of the {@see toObject} method.\n"
2230       " * @param {boolean|undefined} includeInstance Deprecated. Whether to "
2231       "include\n"
2232       " *     the JSPB instance for transitional soy proto support:\n"
2233       " *     http://goto/soy-param-migration\n"
2234       " * @param {!$classname$} msg The msg instance to transform.\n"
2235       " * @return {!Object}\n"
2236       " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
2237       " */\n"
2238       "$classname$.toObject = function(includeInstance, msg) {\n"
2239       "  var f, obj = {",
2240       "classname", GetMessagePath(options, desc));
2241 
2242   bool first = true;
2243   for (int i = 0; i < desc->field_count(); i++) {
2244     const FieldDescriptor* field = desc->field(i);
2245     if (IgnoreField(field)) {
2246       continue;
2247     }
2248 
2249     if (!first) {
2250       printer->Print(",\n    ");
2251     } else {
2252       printer->Print("\n    ");
2253       first = false;
2254     }
2255 
2256     GenerateClassFieldToObject(options, printer, field);
2257   }
2258 
2259   if (!first) {
2260     printer->Print("\n  };\n\n");
2261   } else {
2262     printer->Print("\n\n  };\n\n");
2263   }
2264 
2265   if (IsExtendable(desc)) {
2266     printer->Print(
2267         "  jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
2268         "obj,\n"
2269         "      $extObject$, $class$.prototype.getExtension,\n"
2270         "      includeInstance);\n",
2271         "extObject", JSExtensionsObjectName(options, desc->file(), desc),
2272         "class", GetMessagePath(options, desc));
2273   }
2274 
2275   printer->Print(
2276       "  if (includeInstance) {\n"
2277       "    obj.$$jspbMessageInstance = msg;\n"
2278       "  }\n"
2279       "  return obj;\n"
2280       "};\n"
2281       "}\n"
2282       "\n"
2283       "\n",
2284       "classname", GetMessagePath(options, desc));
2285 }
2286 
GenerateFieldValueExpression(io::Printer * printer,const char * obj_reference,const FieldDescriptor * field,bool use_default) const2287 void Generator::GenerateFieldValueExpression(io::Printer* printer,
2288                                              const char* obj_reference,
2289                                              const FieldDescriptor* field,
2290                                              bool use_default) const {
2291   const bool is_float_or_double =
2292       field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2293       field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE;
2294   const bool is_boolean = field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL;
2295 
2296   const std::string with_default = use_default ? "WithDefault" : "";
2297   const std::string default_arg =
2298       use_default ? StrCat(", ", JSFieldDefault(field)) : "";
2299   const std::string cardinality = field->is_repeated() ? "Repeated" : "";
2300   std::string type = "";
2301   if (is_float_or_double) {
2302     type = "FloatingPoint";
2303   }
2304   if (is_boolean) {
2305     type = "Boolean";
2306   }
2307 
2308   // Prints the appropriate function, among:
2309   // - getField
2310   // - getBooleanField
2311   // - getFloatingPointField => Replaced by getOptionalFloatingPointField to
2312   //   preserve backward compatibility.
2313   // - getFieldWithDefault
2314   // - getBooleanFieldWithDefault
2315   // - getFloatingPointFieldWithDefault
2316   // - getRepeatedField
2317   // - getRepeatedBooleanField
2318   // - getRepeatedFloatingPointField
2319   if (is_float_or_double && !field->is_repeated() && !use_default) {
2320     printer->Print(
2321         "jspb.Message.getOptionalFloatingPointField($obj$, "
2322         "$index$$default$)",
2323         "obj", obj_reference, "index", JSFieldIndex(field), "default",
2324         default_arg);
2325   } else {
2326     printer->Print(
2327         "jspb.Message.get$cardinality$$type$Field$with_default$($obj$, "
2328         "$index$$default$)",
2329         "cardinality", cardinality, "type", type, "with_default", with_default,
2330         "obj", obj_reference, "index", JSFieldIndex(field), "default",
2331         default_arg);
2332   }
2333 }
2334 
GenerateClassFieldToObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2335 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
2336                                            io::Printer* printer,
2337                                            const FieldDescriptor* field) const {
2338   printer->Print("$fieldname$: ", "fieldname",
2339                  JSObjectFieldName(options, field));
2340 
2341   if (field->is_map()) {
2342     const FieldDescriptor* value_field = MapFieldValue(field);
2343     // If the map values are of a message type, we must provide their static
2344     // toObject() method; otherwise we pass undefined for that argument.
2345     std::string value_to_object;
2346     if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2347       value_to_object =
2348           GetMessagePath(options, value_field->message_type()) + ".toObject";
2349     } else {
2350       value_to_object = "undefined";
2351     }
2352     printer->Print(
2353         "(f = msg.get$name$()) ? f.toObject(includeInstance, $valuetoobject$) "
2354         ": []",
2355         "name", JSGetterName(options, field), "valuetoobject", value_to_object);
2356   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2357     // Message field.
2358     if (field->is_repeated()) {
2359       {
2360         printer->Print(
2361             "jspb.Message.toObjectList(msg.get$getter$(),\n"
2362             "    $type$.toObject, includeInstance)",
2363             "getter", JSGetterName(options, field), "type",
2364             SubmessageTypeRef(options, field));
2365       }
2366     } else {
2367       printer->Print(
2368           "(f = msg.get$getter$()) && "
2369           "$type$.toObject(includeInstance, f)",
2370           "getter", JSGetterName(options, field), "type",
2371           SubmessageTypeRef(options, field));
2372     }
2373   } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
2374     // For bytes fields we want to always return the B64 data.
2375     printer->Print("msg.get$getter$()", "getter",
2376                    JSGetterName(options, field, BYTES_B64));
2377   } else {
2378     bool use_default = field->has_default_value();
2379 
2380     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
2381         // Repeated fields get initialized to their default in the constructor
2382         // (why?), so we emit a plain getField() call for them.
2383         !field->is_repeated()) {
2384       // Proto3 puts all defaults (including implicit defaults) in toObject().
2385       // But for proto2 we leave the existing semantics unchanged: unset fields
2386       // without default are unset.
2387       use_default = true;
2388     }
2389 
2390     // We don't implement this by calling the accessors, because the semantics
2391     // of the accessors are changing independently of the toObject() semantics.
2392     // We are migrating the accessors to return defaults instead of null, but
2393     // it may take longer to migrate toObject (or we might not want to do it at
2394     // all).  So we want to generate independent code.
2395     // The accessor for unset optional values without default should return
2396     // null. Those are converted to undefined in the generated object.
2397     if (!use_default) {
2398       printer->Print("(f = ");
2399     }
2400     GenerateFieldValueExpression(printer, "msg", field, use_default);
2401     if (!use_default) {
2402       printer->Print(") == null ? undefined : f");
2403     }
2404   }
2405 }
2406 
GenerateObjectTypedef(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2407 void Generator::GenerateObjectTypedef(const GeneratorOptions& options,
2408                                       io::Printer* printer,
2409                                       const Descriptor* desc) const {
2410   // TODO(b/122687752): Consider renaming nested messages called ObjectFormat
2411   //     to prevent collisions.
2412   const std::string type_name = GetMessagePath(options, desc) + ".ObjectFormat";
2413 
2414   printer->Print(
2415       "/**\n"
2416       " * The raw object form of $messageName$ as accepted by the `fromObject` "
2417       "method.\n"
2418       " * @record\n"
2419       " */\n"
2420       "$typeName$ = function() {\n",
2421       "messageName", desc->name(), "typeName", type_name);
2422 
2423   for (int i = 0; i < desc->field_count(); i++) {
2424     if (i > 0) {
2425       printer->Print("\n");
2426     }
2427     printer->Print(
2428         "  /** @type {$fieldType$|undefined} */\n"
2429         "  this.$fieldName$;\n",
2430         "fieldName", JSObjectFieldName(options, desc->field(i)),
2431         // TODO(b/121097361): Add type checking for field values.
2432         "fieldType", "?");
2433   }
2434 
2435   printer->Print("};\n\n");
2436 }
2437 
GenerateClassFromObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2438 void Generator::GenerateClassFromObject(const GeneratorOptions& options,
2439                                         io::Printer* printer,
2440                                         const Descriptor* desc) const {
2441   printer->Print("if (jspb.Message.GENERATE_FROM_OBJECT) {\n\n");
2442 
2443   GenerateObjectTypedef(options, printer, desc);
2444 
2445   printer->Print(
2446       "/**\n"
2447       " * Loads data from an object into a new instance of this proto.\n"
2448       " * @param {!$classname$.ObjectFormat} obj\n"
2449       " *     The object representation of this proto to load the data from.\n"
2450       " * @return {!$classname$}\n"
2451       " */\n"
2452       "$classname$.fromObject = function(obj) {\n"
2453       "  var msg = new $classname$();\n",
2454       "classname", GetMessagePath(options, desc));
2455 
2456   for (int i = 0; i < desc->field_count(); i++) {
2457     const FieldDescriptor* field = desc->field(i);
2458     if (!IgnoreField(field)) {
2459       GenerateClassFieldFromObject(options, printer, field);
2460     }
2461   }
2462 
2463   printer->Print(
2464       "  return msg;\n"
2465       "};\n"
2466       "}\n\n");
2467 }
2468 
GenerateClassFieldFromObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2469 void Generator::GenerateClassFieldFromObject(
2470     const GeneratorOptions& options, io::Printer* printer,
2471     const FieldDescriptor* field) const {
2472   if (field->is_map()) {
2473     const FieldDescriptor* value_field = MapFieldValue(field);
2474     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2475       // Since the map values are of message type, we have to do some extra work
2476       // to recursively call fromObject() on them before setting the map field.
2477       printer->Print(
2478           "  obj.$name$ && jspb.Message.setWrapperField(\n"
2479           "      msg, $index$, jspb.Map.fromObject(obj.$name$, $fieldclass$, "
2480           "$fieldclass$.fromObject));\n",
2481           "name", JSObjectFieldName(options, field), "index",
2482           JSFieldIndex(field), "fieldclass",
2483           GetMessagePath(options, value_field->message_type()));
2484     } else {
2485       // `msg` is a newly-constructed message object that has not yet built any
2486       // map containers wrapping underlying arrays, so we can simply directly
2487       // set the array here without fear of a stale wrapper.
2488       printer->Print(
2489           "  obj.$name$ && "
2490           "jspb.Message.setField(msg, $index$, obj.$name$);\n",
2491           "name", JSObjectFieldName(options, field), "index",
2492           JSFieldIndex(field));
2493     }
2494   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2495     // Message field (singular or repeated)
2496     if (field->is_repeated()) {
2497       {
2498         printer->Print(
2499             "  obj.$name$ && "
2500             "jspb.Message.setRepeatedWrapperField(\n"
2501             "      msg, $index$, obj.$name$.map(\n"
2502             "          $fieldclass$.fromObject));\n",
2503             "name", JSObjectFieldName(options, field), "index",
2504             JSFieldIndex(field), "fieldclass",
2505             SubmessageTypeRef(options, field));
2506       }
2507     } else {
2508       printer->Print(
2509           "  obj.$name$ && jspb.Message.setWrapperField(\n"
2510           "      msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
2511           "name", JSObjectFieldName(options, field), "index",
2512           JSFieldIndex(field), "fieldclass", SubmessageTypeRef(options, field));
2513     }
2514   } else {
2515     // Simple (primitive) field.
2516     printer->Print(
2517         "  obj.$name$ != null && jspb.Message.setField(msg, $index$, "
2518         "obj.$name$);\n",
2519         "name", JSObjectFieldName(options, field), "index",
2520         JSFieldIndex(field));
2521   }
2522 }
2523 
GenerateClassRegistration(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2524 void Generator::GenerateClassRegistration(const GeneratorOptions& options,
2525                                           io::Printer* printer,
2526                                           const Descriptor* desc) const {
2527   // Register any extensions defined inside this message type.
2528   for (int i = 0; i < desc->extension_count(); i++) {
2529     const FieldDescriptor* extension = desc->extension(i);
2530     if (ShouldGenerateExtension(extension)) {
2531       GenerateExtension(options, printer, extension);
2532     }
2533   }
2534 
2535 }
2536 
GenerateClassFields(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2537 void Generator::GenerateClassFields(const GeneratorOptions& options,
2538                                     io::Printer* printer,
2539                                     const Descriptor* desc) const {
2540   for (int i = 0; i < desc->field_count(); i++) {
2541     if (!IgnoreField(desc->field(i))) {
2542       GenerateClassField(options, printer, desc->field(i));
2543     }
2544   }
2545 }
2546 
GenerateBytesWrapper(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field,BytesMode bytes_mode)2547 void GenerateBytesWrapper(const GeneratorOptions& options, io::Printer* printer,
2548                           const FieldDescriptor* field, BytesMode bytes_mode) {
2549   std::string type =
2550       JSFieldTypeAnnotation(options, field,
2551                             /* is_setter_argument = */ false,
2552                             /* force_present = */ false,
2553                             /* singular_if_not_packed = */ false, bytes_mode);
2554   printer->Print(
2555       "/**\n"
2556       " * $fielddef$\n"
2557       "$comment$"
2558       " * This is a type-conversion wrapper around `get$defname$()`\n"
2559       " * @return {$type$}\n"
2560       " */\n"
2561       "$class$.prototype.get$name$ = function() {\n"
2562       "  return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
2563       "      this.get$defname$()));\n"
2564       "};\n"
2565       "\n"
2566       "\n",
2567       "fielddef", FieldDefinition(options, field), "comment",
2568       FieldComments(field, bytes_mode), "type", type, "class",
2569       GetMessagePath(options, field->containing_type()), "name",
2570       JSGetterName(options, field, bytes_mode), "list",
2571       field->is_repeated() ? "List" : "", "suffix",
2572       JSByteGetterSuffix(bytes_mode), "defname",
2573       JSGetterName(options, field, BYTES_DEFAULT));
2574 }
2575 
GenerateClassField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2576 void Generator::GenerateClassField(const GeneratorOptions& options,
2577                                    io::Printer* printer,
2578                                    const FieldDescriptor* field) const {
2579   if (field->is_map()) {
2580     const FieldDescriptor* key_field = MapFieldKey(field);
2581     const FieldDescriptor* value_field = MapFieldValue(field);
2582     // Map field: special handling to instantiate the map object on demand.
2583     std::string key_type =
2584         JSFieldTypeAnnotation(options, key_field,
2585                               /* is_setter_argument = */ false,
2586                               /* force_present = */ true,
2587                               /* singular_if_not_packed = */ false);
2588     std::string value_type =
2589         JSFieldTypeAnnotation(options, value_field,
2590                               /* is_setter_argument = */ false,
2591                               /* force_present = */ true,
2592                               /* singular_if_not_packed = */ false);
2593 
2594     printer->Print(
2595         "/**\n"
2596         " * $fielddef$\n"
2597         " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
2598         " * empty, instead returning `undefined`\n"
2599         " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
2600         " */\n",
2601         "fielddef", FieldDefinition(options, field), "keytype", key_type,
2602         "valuetype", value_type);
2603     printer->Print(
2604         "$class$.prototype.$gettername$ = function(opt_noLazyCreate) {\n"
2605         "  return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
2606         "class", GetMessagePath(options, field->containing_type()),
2607         "gettername", "get" + JSGetterName(options, field), "keytype", key_type,
2608         "valuetype", value_type);
2609     printer->Annotate("gettername", field);
2610     printer->Print(
2611         "      jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
2612         "index", JSFieldIndex(field));
2613 
2614     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2615       printer->Print(
2616           ",\n"
2617           "      $messageType$",
2618           "messageType", GetMessagePath(options, value_field->message_type()));
2619     } else {
2620       printer->Print(
2621           ",\n"
2622           "      null");
2623     }
2624 
2625     printer->Print("));\n");
2626 
2627     printer->Print(
2628         "};\n"
2629         "\n"
2630         "\n");
2631   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2632     // Message field: special handling in order to wrap the underlying data
2633     // array with a message object.
2634 
2635     printer->Print(
2636         "/**\n"
2637         " * $fielddef$\n"
2638         "$comment$"
2639         " * @return {$type$}\n"
2640         " */\n",
2641         "fielddef", FieldDefinition(options, field), "comment",
2642         FieldComments(field, BYTES_DEFAULT), "type",
2643         JSFieldTypeAnnotation(options, field,
2644                               /* is_setter_argument = */ false,
2645                               /* force_present = */ false,
2646                               /* singular_if_not_packed = */ false));
2647     printer->Print(
2648         "$class$.prototype.$gettername$ = function() {\n"
2649         "  return /** @type{$type$} */ (\n"
2650         "    jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
2651         "$index$$required$));\n"
2652         "};\n"
2653         "\n"
2654         "\n",
2655         "class", GetMessagePath(options, field->containing_type()),
2656         "gettername", "get" + JSGetterName(options, field), "type",
2657         JSFieldTypeAnnotation(options, field,
2658                               /* is_setter_argument = */ false,
2659                               /* force_present = */ false,
2660                               /* singular_if_not_packed = */ false),
2661         "rpt", (field->is_repeated() ? "Repeated" : ""), "index",
2662         JSFieldIndex(field), "wrapperclass", SubmessageTypeRef(options, field),
2663         "required",
2664         (field->label() == FieldDescriptor::LABEL_REQUIRED ? ", 1" : ""));
2665     printer->Annotate("gettername", field);
2666     printer->Print(
2667         "/**\n"
2668         " * @param {$optionaltype$} value\n"
2669         " * @return {!$class$} returns this\n"
2670         "*/\n"
2671         "$class$.prototype.$settername$ = function(value) {\n"
2672         "  return jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
2673         "optionaltype",
2674         JSFieldTypeAnnotation(options, field,
2675                               /* is_setter_argument = */ true,
2676                               /* force_present = */ false,
2677                               /* singular_if_not_packed = */ false),
2678         "class", GetMessagePath(options, field->containing_type()),
2679         "settername", "set" + JSGetterName(options, field), "oneoftag",
2680         (InRealOneof(field) ? "Oneof" : ""), "repeatedtag",
2681         (field->is_repeated() ? "Repeated" : ""));
2682     printer->Annotate("settername", field);
2683 
2684     printer->Print(
2685         "this, $index$$oneofgroup$, value);\n"
2686         "};\n"
2687         "\n"
2688         "\n",
2689         "index", JSFieldIndex(field), "oneofgroup",
2690         (InRealOneof(field) ? (", " + JSOneofArray(options, field))
2691                                    : ""));
2692 
2693     if (field->is_repeated()) {
2694       GenerateRepeatedMessageHelperMethods(options, printer, field);
2695     }
2696 
2697   } else {
2698     bool untyped =
2699         false;
2700 
2701     // Simple (primitive) field, either singular or repeated.
2702 
2703     // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type;
2704     // at this point we "lie" to non-binary users and tell the return
2705     // type is always base64 string, pending a LSC to migrate to typed getters.
2706     BytesMode bytes_mode =
2707         field->type() == FieldDescriptor::TYPE_BYTES && !options.binary
2708             ? BYTES_B64
2709             : BYTES_DEFAULT;
2710     std::string typed_annotation =
2711         JSFieldTypeAnnotation(options, field,
2712                               /* is_setter_argument = */ false,
2713                               /* force_present = */ false,
2714                               /* singular_if_not_packed = */ false,
2715                               /* bytes_mode = */ bytes_mode);
2716     if (untyped) {
2717       printer->Print(
2718           "/**\n"
2719           " * @return {?} Raw field, untyped.\n"
2720           " */\n");
2721     } else {
2722       printer->Print(
2723           "/**\n"
2724           " * $fielddef$\n"
2725           "$comment$"
2726           " * @return {$type$}\n"
2727           " */\n",
2728           "fielddef", FieldDefinition(options, field), "comment",
2729           FieldComments(field, bytes_mode), "type", typed_annotation);
2730     }
2731 
2732     printer->Print("$class$.prototype.$gettername$ = function() {\n", "class",
2733                    GetMessagePath(options, field->containing_type()),
2734                    "gettername", "get" + JSGetterName(options, field));
2735     printer->Annotate("gettername", field);
2736 
2737     if (untyped) {
2738       printer->Print("  return ");
2739     } else {
2740       printer->Print("  return /** @type {$type$} */ (", "type",
2741                      typed_annotation);
2742     }
2743 
2744     bool use_default = !ReturnsNullWhenUnset(options, field);
2745 
2746     // Raw fields with no default set should just return undefined.
2747     if (untyped && !field->has_default_value()) {
2748       use_default = false;
2749     }
2750 
2751     // Repeated fields get initialized to their default in the constructor
2752     // (why?), so we emit a plain getField() call for them.
2753     if (field->is_repeated()) {
2754       use_default = false;
2755     }
2756 
2757     GenerateFieldValueExpression(printer, "this", field, use_default);
2758 
2759     if (untyped) {
2760       printer->Print(
2761           ";\n"
2762           "};\n"
2763           "\n"
2764           "\n");
2765     } else {
2766       printer->Print(
2767           ");\n"
2768           "};\n"
2769           "\n"
2770           "\n");
2771     }
2772 
2773     if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
2774       GenerateBytesWrapper(options, printer, field, BYTES_B64);
2775       GenerateBytesWrapper(options, printer, field, BYTES_U8);
2776     }
2777 
2778     printer->Print(
2779         "/**\n"
2780         " * @param {$optionaltype$} value\n"
2781         " * @return {!$class$} returns this\n"
2782         " */\n",
2783         "class", GetMessagePath(options, field->containing_type()),
2784         "optionaltype",
2785         untyped ? "*"
2786                 : JSFieldTypeAnnotation(options, field,
2787                                         /* is_setter_argument = */ true,
2788                                         /* force_present = */ false,
2789                                         /* singular_if_not_packed = */ false));
2790 
2791     if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
2792         !field->is_repeated() && !field->is_map() &&
2793         !HasFieldPresence(options, field)) {
2794       // Proto3 non-repeated and non-map fields without presence use the
2795       // setProto3*Field function.
2796       printer->Print(
2797           "$class$.prototype.$settername$ = function(value) {\n"
2798           "  return jspb.Message.setProto3$typetag$Field(this, $index$, "
2799           "value);"
2800           "\n"
2801           "};\n"
2802           "\n"
2803           "\n",
2804           "class", GetMessagePath(options, field->containing_type()),
2805           "settername", "set" + JSGetterName(options, field), "typetag",
2806           JSTypeTag(field), "index", JSFieldIndex(field));
2807       printer->Annotate("settername", field);
2808     } else {
2809       // Otherwise, use the regular setField function.
2810       printer->Print(
2811           "$class$.prototype.$settername$ = function(value) {\n"
2812           "  return jspb.Message.set$oneoftag$Field(this, $index$",
2813           "class", GetMessagePath(options, field->containing_type()),
2814           "settername", "set" + JSGetterName(options, field), "oneoftag",
2815           (InRealOneof(field) ? "Oneof" : ""), "index", JSFieldIndex(field));
2816       printer->Annotate("settername", field);
2817       printer->Print(
2818           "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);\n"
2819           "};\n"
2820           "\n"
2821           "\n",
2822           "type",
2823           untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2824           "typeclose", untyped ? ")" : "", "oneofgroup",
2825           (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
2826           "rptvalueinit", (field->is_repeated() ? " || []" : ""));
2827     }
2828 
2829     if (untyped) {
2830       printer->Print(
2831           "/**\n"
2832           " * Clears the value.\n"
2833           " * @return {!$class$} returns this\n"
2834           " */\n",
2835           "class", GetMessagePath(options, field->containing_type()));
2836     }
2837 
2838     if (field->is_repeated()) {
2839       GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
2840     }
2841   }
2842 
2843   // Generate clearFoo() method for map fields, repeated fields, and other
2844   // fields with presence.
2845   if (field->is_map()) {
2846     // clang-format off
2847     printer->Print(
2848         "/**\n"
2849         " * Clears values from the map. The map will be non-null.\n"
2850         " * @return {!$class$} returns this\n"
2851         " */\n"
2852         "$class$.prototype.$clearername$ = function() {\n"
2853         "  this.$gettername$().clear();\n"
2854         "  return this;"
2855         "};\n"
2856         "\n"
2857         "\n",
2858         "class", GetMessagePath(options, field->containing_type()),
2859         "clearername", "clear" + JSGetterName(options, field),
2860         "gettername", "get" + JSGetterName(options, field));
2861     // clang-format on
2862     printer->Annotate("clearername", field);
2863   } else if (field->is_repeated() ||
2864              (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
2865               !field->is_required())) {
2866     // Fields where we can delegate to the regular setter.
2867     // clang-format off
2868     printer->Print(
2869         "/**\n"
2870         " * $jsdoc$\n"
2871         " * @return {!$class$} returns this\n"
2872         " */\n"
2873         "$class$.prototype.$clearername$ = function() {\n"
2874         "  return this.$settername$($clearedvalue$);\n"
2875         "};\n"
2876         "\n"
2877         "\n",
2878        "jsdoc", field->is_repeated()
2879            ? "Clears the list making it empty but non-null."
2880            : "Clears the message field making it undefined.",
2881         "class", GetMessagePath(options, field->containing_type()),
2882         "clearername", "clear" + JSGetterName(options, field),
2883         "settername", "set" + JSGetterName(options, field),
2884         "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
2885     // clang-format on
2886     printer->Annotate("clearername", field);
2887   } else if (HasFieldPresence(options, field)) {
2888     // Fields where we can't delegate to the regular setter because it doesn't
2889     // accept "undefined" as an argument.
2890     // clang-format off
2891     printer->Print(
2892         "/**\n"
2893         " * Clears the field making it undefined.\n"
2894         " * @return {!$class$} returns this\n"
2895         " */\n"
2896         "$class$.prototype.$clearername$ = function() {\n"
2897         "  return jspb.Message.set$maybeoneof$Field(this, "
2898             "$index$$maybeoneofgroup$, ",
2899         "class", GetMessagePath(options, field->containing_type()),
2900         "clearername", "clear" + JSGetterName(options, field),
2901         "maybeoneof", (InRealOneof(field) ? "Oneof" : ""),
2902         "maybeoneofgroup", (InRealOneof(field)
2903                             ? (", " + JSOneofArray(options, field))
2904                             : ""),
2905         "index", JSFieldIndex(field));
2906     // clang-format on
2907     printer->Annotate("clearername", field);
2908     printer->Print(
2909         "$clearedvalue$);\n"
2910         "};\n"
2911         "\n"
2912         "\n",
2913         "clearedvalue", (field->is_repeated() ? "[]" : "undefined"));
2914   }
2915 
2916   if (HasFieldPresence(options, field)) {
2917     printer->Print(
2918         "/**\n"
2919         " * Returns whether this field is set.\n"
2920         " * @return {boolean}\n"
2921         " */\n"
2922         "$class$.prototype.$hasername$ = function() {\n"
2923         "  return jspb.Message.getField(this, $index$) != null;\n"
2924         "};\n"
2925         "\n"
2926         "\n",
2927         "class", GetMessagePath(options, field->containing_type()), "hasername",
2928         "has" + JSGetterName(options, field), "index", JSFieldIndex(field));
2929     printer->Annotate("hasername", field);
2930   }
2931 }
2932 
GenerateRepeatedPrimitiveHelperMethods(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field,bool untyped) const2933 void Generator::GenerateRepeatedPrimitiveHelperMethods(
2934     const GeneratorOptions& options, io::Printer* printer,
2935     const FieldDescriptor* field, bool untyped) const {
2936   // clang-format off
2937   printer->Print(
2938       "/**\n"
2939       " * @param {$optionaltype$} value\n"
2940       " * @param {number=} opt_index\n"
2941       " * @return {!$class$} returns this\n"
2942       " */\n"
2943       "$class$.prototype.$addername$ = function(value, opt_index) {\n"
2944       "  return jspb.Message.addToRepeatedField(this, "
2945       "$index$",
2946       "class", GetMessagePath(options, field->containing_type()), "addername",
2947       "add" + JSGetterName(options, field, BYTES_DEFAULT,
2948                            /* drop_list = */ true),
2949       "optionaltype",
2950           JSFieldTypeAnnotation(
2951                                 options, field,
2952                                 /* is_setter_argument = */ false,
2953                                 /* force_present = */ true,
2954                                 /* singular_if_not_packed = */ false,
2955                                 BYTES_DEFAULT,
2956                                 /* force_singular = */ true),
2957       "index", JSFieldIndex(field));
2958   printer->Annotate("addername", field);
2959   printer->Print(
2960       "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, "
2961       "opt_index);\n"
2962       "};\n"
2963       "\n"
2964       "\n",
2965       "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
2966       "typeclose", untyped ? ")" : "", "oneofgroup",
2967       (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
2968       "rptvalueinit", "");
2969   // clang-format on
2970 }
2971 
GenerateRepeatedMessageHelperMethods(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2972 void Generator::GenerateRepeatedMessageHelperMethods(
2973     const GeneratorOptions& options, io::Printer* printer,
2974     const FieldDescriptor* field) const {
2975   printer->Print(
2976       "/**\n"
2977       " * @param {!$optionaltype$=} opt_value\n"
2978       " * @param {number=} opt_index\n"
2979       " * @return {!$optionaltype$}\n"
2980       " */\n"
2981       "$class$.prototype.$addername$ = function(opt_value, opt_index) {\n"
2982       "  return jspb.Message.addTo$repeatedtag$WrapperField(",
2983       "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class",
2984       GetMessagePath(options, field->containing_type()), "addername",
2985       "add" + JSGetterName(options, field, BYTES_DEFAULT,
2986                            /* drop_list = */ true),
2987       "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2988 
2989   printer->Annotate("addername", field);
2990   printer->Print(
2991       "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n"
2992       "};\n"
2993       "\n"
2994       "\n",
2995       "index", JSFieldIndex(field), "oneofgroup",
2996       (InRealOneof(field) ? (", " + JSOneofArray(options, field)) : ""),
2997       "ctor", GetMessagePath(options, field->message_type()));
2998 }
2999 
GenerateClassExtensionFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const3000 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
3001                                                 io::Printer* printer,
3002                                                 const Descriptor* desc) const {
3003   if (IsExtendable(desc)) {
3004     printer->Print(
3005         "\n"
3006         "/**\n"
3007         " * The extensions registered with this message class. This is a "
3008         "map of\n"
3009         " * extension field number to fieldInfo object.\n"
3010         " *\n"
3011         " * For example:\n"
3012         " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
3013         "ctor: proto.example.MyMessage} }\n"
3014         " *\n"
3015         " * fieldName contains the JsCompiler renamed field name property "
3016         "so that it\n"
3017         " * works in OPTIMIZED mode.\n"
3018         " *\n"
3019         " * @type {!Object<number, jspb.ExtensionFieldInfo>}\n"
3020         " */\n"
3021         "$class$.extensions = {};\n"
3022         "\n",
3023         "class", GetMessagePath(options, desc));
3024 
3025     printer->Print(
3026         "\n"
3027         "/**\n"
3028         " * The extensions registered with this message class. This is a "
3029         "map of\n"
3030         " * extension field number to fieldInfo object.\n"
3031         " *\n"
3032         " * For example:\n"
3033         " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
3034         "ctor: proto.example.MyMessage} }\n"
3035         " *\n"
3036         " * fieldName contains the JsCompiler renamed field name property "
3037         "so that it\n"
3038         " * works in OPTIMIZED mode.\n"
3039         " *\n"
3040         " * @type {!Object<number, jspb.ExtensionFieldBinaryInfo>}\n"
3041         " */\n"
3042         "$class$.extensionsBinary = {};\n"
3043         "\n",
3044         "class", GetMessagePath(options, desc));
3045   }
3046 }
3047 
3048 
GenerateClassDeserializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const3049 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
3050                                                io::Printer* printer,
3051                                                const Descriptor* desc) const {
3052   // TODO(cfallin): Handle lazy decoding when requested by field option and/or
3053   // by default for 'bytes' fields and packed repeated fields.
3054 
3055   printer->Print(
3056       "/**\n"
3057       " * Deserializes binary data (in protobuf wire format).\n"
3058       " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
3059       " * @return {!$class$}\n"
3060       " */\n"
3061       "$class$.deserializeBinary = function(bytes) {\n"
3062       "  var reader = new jspb.BinaryReader(bytes);\n"
3063       "  var msg = new $class$;\n"
3064       "  return $class$.deserializeBinaryFromReader(msg, reader);\n"
3065       "};\n"
3066       "\n"
3067       "\n"
3068       "/**\n"
3069       " * Deserializes binary data (in protobuf wire format) from the\n"
3070       " * given reader into the given message object.\n"
3071       " * @param {!$class$} msg The message object to deserialize into.\n"
3072       " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
3073       " * @return {!$class$}\n"
3074       " */\n"
3075       "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
3076       "  while (reader.nextField()) {\n",
3077       "class", GetMessagePath(options, desc));
3078     printer->Print(
3079         "    if (reader.isEndGroup()) {\n"
3080         "      break;\n"
3081         "    }\n"
3082         "    var field = reader.getFieldNumber();\n"
3083         "    switch (field) {\n");
3084 
3085     for (int i = 0; i < desc->field_count(); i++) {
3086       if (!IgnoreField(desc->field(i))) {
3087         GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
3088       }
3089     }
3090 
3091     printer->Print("    default:\n");
3092     if (IsExtendable(desc)) {
3093       printer->Print(
3094           "      jspb.Message.readBinaryExtension(msg, reader,\n"
3095           "        $extobj$Binary,\n"
3096           "        $class$.prototype.getExtension,\n"
3097           "        $class$.prototype.setExtension);\n"
3098           "      break;\n"
3099           "    }\n",
3100           "extobj", JSExtensionsObjectName(options, desc->file(), desc),
3101           "class", GetMessagePath(options, desc));
3102     } else {
3103       printer->Print(
3104           "      reader.skipField();\n"
3105           "      break;\n"
3106           "    }\n");
3107     }
3108 
3109   printer->Print(
3110       "  }\n"
3111       "  return msg;\n"
3112       "};\n"
3113       "\n"
3114       "\n");
3115 }
3116 
GenerateClassDeserializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3117 void Generator::GenerateClassDeserializeBinaryField(
3118     const GeneratorOptions& options, io::Printer* printer,
3119     const FieldDescriptor* field) const {
3120   printer->Print("    case $num$:\n", "num", StrCat(field->number()));
3121 
3122   if (field->is_map()) {
3123     const FieldDescriptor* key_field = MapFieldKey(field);
3124     const FieldDescriptor* value_field = MapFieldValue(field);
3125     printer->Print(
3126         "      var value = msg.get$name$();\n"
3127         "      reader.readMessage(value, function(message, reader) {\n",
3128         "name", JSGetterName(options, field));
3129 
3130     printer->Print(
3131         "        jspb.Map.deserializeBinary(message, reader, "
3132         "$keyReaderFn$, $valueReaderFn$",
3133         "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
3134         "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
3135 
3136     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3137       printer->Print(", $messageType$.deserializeBinaryFromReader",
3138                      "messageType",
3139                      GetMessagePath(options, value_field->message_type()));
3140     } else {
3141       printer->Print(", null");
3142     }
3143     printer->Print(", $defaultKey$", "defaultKey", JSFieldDefault(key_field));
3144     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3145       printer->Print(", new $messageType$()", "messageType",
3146                      GetMessagePath(options, value_field->message_type()));
3147     } else {
3148       printer->Print(", $defaultValue$", "defaultValue",
3149                      JSFieldDefault(value_field));
3150     }
3151     printer->Print(");\n");
3152     printer->Print("         });\n");
3153   } else {
3154     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
3155       printer->Print(
3156           "      var value = new $fieldclass$;\n"
3157           "      reader.read$msgOrGroup$($grpfield$value,"
3158           "$fieldclass$.deserializeBinaryFromReader);\n",
3159           "fieldclass", SubmessageTypeRef(options, field), "msgOrGroup",
3160           (field->type() == FieldDescriptor::TYPE_GROUP) ? "Group" : "Message",
3161           "grpfield",
3162           (field->type() == FieldDescriptor::TYPE_GROUP)
3163               ? (StrCat(field->number()) + ", ")
3164               : "");
3165     } else {
3166       printer->Print(
3167           "      var value = /** @type {$fieldtype$} */ "
3168           "(reader.read$reader$());\n",
3169           "fieldtype",
3170           JSFieldTypeAnnotation(options, field, false, true,
3171                                 /* singular_if_not_packed */ true, BYTES_U8),
3172           "reader",
3173           JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
3174     }
3175 
3176     if (field->is_repeated() && !field->is_packed()) {
3177       printer->Print(
3178           "      msg.add$name$(value);\n", "name",
3179           JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
3180     } else {
3181       // Singular fields, and packed repeated fields, receive a |value| either
3182       // as the field's value or as the array of all the field's values; set
3183       // this as the field's value directly.
3184       printer->Print("      msg.set$name$(value);\n", "name",
3185                      JSGetterName(options, field));
3186     }
3187   }
3188 
3189   printer->Print("      break;\n");
3190 }
3191 
GenerateClassSerializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const3192 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
3193                                              io::Printer* printer,
3194                                              const Descriptor* desc) const {
3195   printer->Print(
3196       "/**\n"
3197       " * Serializes the message to binary data (in protobuf wire format).\n"
3198       " * @return {!Uint8Array}\n"
3199       " */\n"
3200       "$class$.prototype.serializeBinary = function() {\n"
3201       "  var writer = new jspb.BinaryWriter();\n"
3202       "  $class$.serializeBinaryToWriter(this, writer);\n"
3203       "  return writer.getResultBuffer();\n"
3204       "};\n"
3205       "\n"
3206       "\n"
3207       "/**\n"
3208       " * Serializes the given message to binary data (in protobuf wire\n"
3209       " * format), writing to the given BinaryWriter.\n"
3210       " * @param {!$class$} message\n"
3211       " * @param {!jspb.BinaryWriter} writer\n"
3212       " * @suppress {unusedLocalVariables} f is only used for nested messages\n"
3213       " */\n"
3214       "$class$.serializeBinaryToWriter = function(message, "
3215       "writer) {\n"
3216       "  var f = undefined;\n",
3217       "class", GetMessagePath(options, desc));
3218 
3219   for (int i = 0; i < desc->field_count(); i++) {
3220     if (!IgnoreField(desc->field(i))) {
3221       GenerateClassSerializeBinaryField(options, printer, desc->field(i));
3222     }
3223   }
3224 
3225   if (IsExtendable(desc)) {
3226     printer->Print(
3227         "  jspb.Message.serializeBinaryExtensions(message, writer,\n"
3228         "    $extobj$Binary, $class$.prototype.getExtension);\n",
3229         "extobj", JSExtensionsObjectName(options, desc->file(), desc), "class",
3230         GetMessagePath(options, desc));
3231   }
3232 
3233   printer->Print(
3234       "};\n"
3235       "\n"
3236       "\n");
3237 }
3238 
GenerateClassSerializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3239 void Generator::GenerateClassSerializeBinaryField(
3240     const GeneratorOptions& options, io::Printer* printer,
3241     const FieldDescriptor* field) const {
3242   if (HasFieldPresence(options, field) &&
3243       field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
3244     std::string typed_annotation =
3245         JSFieldTypeAnnotation(options, field,
3246                               /* is_setter_argument = */ false,
3247                               /* force_present = */ false,
3248                               /* singular_if_not_packed = */ false,
3249                               /* bytes_mode = */ BYTES_DEFAULT);
3250     printer->Print(
3251         "  f = /** @type {$type$} */ "
3252         "(jspb.Message.getField(message, $index$));\n",
3253         "index", JSFieldIndex(field), "type", typed_annotation);
3254   } else {
3255     printer->Print(
3256         "  f = message.get$name$($nolazy$);\n", "name",
3257         JSGetterName(options, field, BYTES_U8),
3258         // No lazy creation for maps containers -- fastpath the empty case.
3259         "nolazy", field->is_map() ? "true" : "");
3260   }
3261 
3262   // Print an `if (condition)` statement that evaluates to true if the field
3263   // goes on the wire.
3264   if (field->is_map()) {
3265     printer->Print("  if (f && f.getLength() > 0) {\n");
3266   } else if (field->is_repeated()) {
3267     printer->Print("  if (f.length > 0) {\n");
3268   } else {
3269     if (HasFieldPresence(options, field)) {
3270       printer->Print("  if (f != null) {\n");
3271     } else {
3272       // No field presence: serialize onto the wire only if value is
3273       // non-default.  Defaults are documented here:
3274       // https://goto.google.com/lhdfm
3275       switch (field->cpp_type()) {
3276         case FieldDescriptor::CPPTYPE_INT32:
3277         case FieldDescriptor::CPPTYPE_INT64:
3278         case FieldDescriptor::CPPTYPE_UINT32:
3279         case FieldDescriptor::CPPTYPE_UINT64: {
3280           if (IsIntegralFieldWithStringJSType(field)) {
3281             // We can use `parseInt` here even though it will not be precise for
3282             // 64-bit quantities because we are only testing for zero/nonzero,
3283             // and JS numbers (64-bit floating point values, i.e., doubles) are
3284             // integer-precise in the range that includes zero.
3285             printer->Print("  if (parseInt(f, 10) !== 0) {\n");
3286           } else {
3287             printer->Print("  if (f !== 0) {\n");
3288           }
3289           break;
3290         }
3291 
3292         case FieldDescriptor::CPPTYPE_ENUM:
3293         case FieldDescriptor::CPPTYPE_FLOAT:
3294         case FieldDescriptor::CPPTYPE_DOUBLE:
3295           printer->Print("  if (f !== 0.0) {\n");
3296           break;
3297         case FieldDescriptor::CPPTYPE_BOOL:
3298           printer->Print("  if (f) {\n");
3299           break;
3300         case FieldDescriptor::CPPTYPE_STRING:
3301           printer->Print("  if (f.length > 0) {\n");
3302           break;
3303         default:
3304           assert(false);
3305           break;
3306       }
3307     }
3308   }
3309 
3310   // Write the field on the wire.
3311   if (field->is_map()) {
3312     const FieldDescriptor* key_field = MapFieldKey(field);
3313     const FieldDescriptor* value_field = MapFieldValue(field);
3314     printer->Print(
3315         "    f.serializeBinary($index$, writer, "
3316         "$keyWriterFn$, $valueWriterFn$",
3317         "index", StrCat(field->number()), "keyWriterFn",
3318         JSBinaryWriterMethodName(options, key_field), "valueWriterFn",
3319         JSBinaryWriterMethodName(options, value_field));
3320 
3321     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3322       printer->Print(", $messageType$.serializeBinaryToWriter", "messageType",
3323                      GetMessagePath(options, value_field->message_type()));
3324     }
3325 
3326     printer->Print(");\n");
3327   } else {
3328     printer->Print(
3329         "    writer.write$method$(\n"
3330         "      $index$,\n"
3331         "      f",
3332         "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
3333         "index", StrCat(field->number()));
3334 
3335     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
3336         !field->is_map()) {
3337       printer->Print(
3338           ",\n"
3339           "      $submsg$.serializeBinaryToWriter\n",
3340           "submsg", SubmessageTypeRef(options, field));
3341     } else {
3342       printer->Print("\n");
3343     }
3344 
3345     printer->Print("    );\n");
3346   }
3347 
3348   // Close the `if`.
3349   printer->Print("  }\n");
3350 }
3351 
GenerateEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc) const3352 void Generator::GenerateEnum(const GeneratorOptions& options,
3353                              io::Printer* printer,
3354                              const EnumDescriptor* enumdesc) const {
3355   printer->Print(
3356       "/**\n"
3357       " * @enum {number}\n"
3358       " */\n"
3359       "$enumprefix$$name$ = {\n",
3360       "enumprefix", GetEnumPathPrefix(options, enumdesc), "name",
3361       enumdesc->name());
3362   printer->Annotate("name", enumdesc);
3363 
3364   std::set<std::string> used_name;
3365   std::vector<int> valid_index;
3366   for (int i = 0; i < enumdesc->value_count(); i++) {
3367     if (enumdesc->options().allow_alias() &&
3368         !used_name.insert(ToEnumCase(enumdesc->value(i)->name())).second) {
3369       continue;
3370     }
3371     valid_index.push_back(i);
3372   }
3373   for (auto i : valid_index) {
3374     const EnumValueDescriptor* value = enumdesc->value(i);
3375     printer->Print("  $name$: $value$$comma$\n", "name",
3376                    ToEnumCase(value->name()), "value",
3377                    StrCat(value->number()), "comma",
3378                    (i == valid_index.back()) ? "" : ",");
3379     printer->Annotate("name", value);
3380   }
3381 
3382   printer->Print(
3383       "};\n"
3384       "\n");
3385 }
3386 
GenerateExtension(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const3387 void Generator::GenerateExtension(const GeneratorOptions& options,
3388                                   io::Printer* printer,
3389                                   const FieldDescriptor* field) const {
3390   std::string extension_scope =
3391       (field->extension_scope()
3392            ? GetMessagePath(options, field->extension_scope())
3393            : GetNamespace(options, field->file()));
3394 
3395   const std::string extension_object_name = JSObjectFieldName(options, field);
3396   printer->Print(
3397       "\n"
3398       "/**\n"
3399       " * A tuple of {field number, class constructor} for the extension\n"
3400       " * field named `$nameInComment$`.\n"
3401       " * @type {!jspb.ExtensionFieldInfo<$extensionType$>}\n"
3402       " */\n"
3403       "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
3404       "nameInComment", extension_object_name, "name", extension_object_name,
3405       "class", extension_scope, "extensionType",
3406       JSFieldTypeAnnotation(options, field,
3407                             /* is_setter_argument = */ false,
3408                             /* force_present = */ true,
3409                             /* singular_if_not_packed = */ false));
3410   printer->Annotate("name", field);
3411   printer->Print(
3412       "    $index$,\n"
3413       "    {$name$: 0},\n"
3414       "    $ctor$,\n"
3415       "     /** @type {?function((boolean|undefined),!jspb.Message=): "
3416       "!Object} */ (\n"
3417       "         $toObject$),\n"
3418       "    $repeated$);\n",
3419       "index", StrCat(field->number()), "name", extension_object_name,
3420       "ctor",
3421       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
3422            ? SubmessageTypeRef(options, field)
3423            : std::string("null")),
3424       "toObject",
3425       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE
3426            ? (SubmessageTypeRef(options, field) + ".toObject")
3427            : std::string("null")),
3428       "repeated", (field->is_repeated() ? "1" : "0"));
3429 
3430   printer->Print(
3431       "\n"
3432       "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
3433       "    $class$.$name$,\n"
3434       "    $binaryReaderFn$,\n"
3435       "    $binaryWriterFn$,\n"
3436       "    $binaryMessageSerializeFn$,\n"
3437       "    $binaryMessageDeserializeFn$,\n",
3438       "extendName",
3439       JSExtensionsObjectName(options, field->file(), field->containing_type()),
3440       "index", StrCat(field->number()), "class", extension_scope, "name",
3441       extension_object_name, "binaryReaderFn",
3442       JSBinaryReaderMethodName(options, field), "binaryWriterFn",
3443       JSBinaryWriterMethodName(options, field), "binaryMessageSerializeFn",
3444       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
3445           ? (SubmessageTypeRef(options, field) + ".serializeBinaryToWriter")
3446           : "undefined",
3447       "binaryMessageDeserializeFn",
3448       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
3449           ? (SubmessageTypeRef(options, field) + ".deserializeBinaryFromReader")
3450           : "undefined");
3451 
3452   printer->Print("    $isPacked$);\n", "isPacked",
3453                  (field->is_packed() ? "true" : "false"));
3454 
3455   printer->Print(
3456       "// This registers the extension field with the extended class, so that\n"
3457       "// toObject() will function correctly.\n"
3458       "$extendName$[$index$] = $class$.$name$;\n"
3459       "\n",
3460       "extendName",
3461       JSExtensionsObjectName(options, field->file(), field->containing_type()),
3462       "index", StrCat(field->number()), "class", extension_scope, "name",
3463       extension_object_name);
3464 }
3465 
ParseFromOptions(const std::vector<std::pair<std::string,std::string>> & options,std::string * error)3466 bool GeneratorOptions::ParseFromOptions(
3467     const std::vector<std::pair<std::string, std::string> >& options,
3468     std::string* error) {
3469   for (int i = 0; i < options.size(); i++) {
3470     if (options[i].first == "add_require_for_enums") {
3471       if (options[i].second != "") {
3472         *error = "Unexpected option value for add_require_for_enums";
3473         return false;
3474       }
3475       add_require_for_enums = true;
3476     } else if (options[i].first == "binary") {
3477       if (options[i].second != "") {
3478         *error = "Unexpected option value for binary";
3479         return false;
3480       }
3481       binary = true;
3482     } else if (options[i].first == "testonly") {
3483       if (options[i].second != "") {
3484         *error = "Unexpected option value for testonly";
3485         return false;
3486       }
3487       testonly = true;
3488     } else if (options[i].first == "error_on_name_conflict") {
3489       if (options[i].second != "") {
3490         *error = "Unexpected option value for error_on_name_conflict";
3491         return false;
3492       }
3493       error_on_name_conflict = true;
3494     } else if (options[i].first == "output_dir") {
3495       output_dir = options[i].second;
3496     } else if (options[i].first == "namespace_prefix") {
3497       namespace_prefix = options[i].second;
3498     } else if (options[i].first == "library") {
3499       library = options[i].second;
3500     } else if (options[i].first == "import_style") {
3501       if (options[i].second == "closure") {
3502         import_style = kImportClosure;
3503       } else if (options[i].second == "commonjs") {
3504         import_style = kImportCommonJs;
3505       } else if (options[i].second == "commonjs_strict") {
3506         import_style = kImportCommonJsStrict;
3507       } else if (options[i].second == "browser") {
3508         import_style = kImportBrowser;
3509       } else if (options[i].second == "es6") {
3510         import_style = kImportEs6;
3511       } else {
3512         *error = "Unknown import style " + options[i].second + ", expected " +
3513                  "one of: closure, commonjs, browser, es6.";
3514       }
3515     } else if (options[i].first == "extension") {
3516       extension = options[i].second;
3517     } else if (options[i].first == "one_output_file_per_input_file") {
3518       if (!options[i].second.empty()) {
3519         *error = "Unexpected option value for one_output_file_per_input_file";
3520         return false;
3521       }
3522       one_output_file_per_input_file = true;
3523     } else if (options[i].first == "annotate_code") {
3524       if (!options[i].second.empty()) {
3525         *error = "Unexpected option value for annotate_code";
3526         return false;
3527       }
3528       annotate_code = true;
3529     } else {
3530       // Assume any other option is an output directory, as long as it is a bare
3531       // `key` rather than a `key=value` option.
3532       if (options[i].second != "") {
3533         *error = "Unknown option: " + options[i].first;
3534         return false;
3535       }
3536       output_dir = options[i].first;
3537     }
3538   }
3539 
3540   if (import_style != kImportClosure &&
3541       (add_require_for_enums || testonly || !library.empty() ||
3542        error_on_name_conflict || extension != ".js" ||
3543        one_output_file_per_input_file)) {
3544     *error =
3545         "The add_require_for_enums, testonly, library, error_on_name_conflict, "
3546         "extension, and one_output_file_per_input_file options should only be "
3547         "used for import_style=closure";
3548     return false;
3549   }
3550 
3551   return true;
3552 }
3553 
output_mode() const3554 GeneratorOptions::OutputMode GeneratorOptions::output_mode() const {
3555   // We use one output file per input file if we are not using Closure or if
3556   // this is explicitly requested.
3557   if (import_style != kImportClosure || one_output_file_per_input_file) {
3558     return kOneOutputFilePerInputFile;
3559   }
3560 
3561   // If a library name is provided, we put everything in that one file.
3562   if (!library.empty()) {
3563     return kEverythingInOneFile;
3564   }
3565 
3566   // Otherwise, we create one output file per SCC.
3567   return kOneOutputFilePerSCC;
3568 }
3569 
GenerateFilesInDepOrder(const GeneratorOptions & options,io::Printer * printer,const std::vector<const FileDescriptor * > & files) const3570 void Generator::GenerateFilesInDepOrder(
3571     const GeneratorOptions& options, io::Printer* printer,
3572     const std::vector<const FileDescriptor*>& files) const {
3573   // Build a std::set over all files so that the DFS can detect when it recurses
3574   // into a dep not specified in the user's command line.
3575   std::set<const FileDescriptor*> all_files(files.begin(), files.end());
3576   // Track the in-progress set of files that have been generated already.
3577   std::set<const FileDescriptor*> generated;
3578   for (int i = 0; i < files.size(); i++) {
3579     GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
3580   }
3581 }
3582 
GenerateFileAndDeps(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * root,std::set<const FileDescriptor * > * all_files,std::set<const FileDescriptor * > * generated) const3583 void Generator::GenerateFileAndDeps(
3584     const GeneratorOptions& options, io::Printer* printer,
3585     const FileDescriptor* root, std::set<const FileDescriptor*>* all_files,
3586     std::set<const FileDescriptor*>* generated) const {
3587   // Skip if already generated.
3588   if (generated->find(root) != generated->end()) {
3589     return;
3590   }
3591   generated->insert(root);
3592 
3593   // Generate all dependencies before this file's content.
3594   for (int i = 0; i < root->dependency_count(); i++) {
3595     const FileDescriptor* dep = root->dependency(i);
3596     GenerateFileAndDeps(options, printer, dep, all_files, generated);
3597   }
3598 
3599   // Generate this file's content.  Only generate if the file is part of the
3600   // original set requested to be generated; i.e., don't take all transitive
3601   // deps down to the roots.
3602   if (all_files->find(root) != all_files->end()) {
3603     GenerateClassesAndEnums(options, printer, root);
3604   }
3605 }
3606 
GenerateFile(const FileDescriptor * file,const GeneratorOptions & options,GeneratorContext * context,bool use_short_name) const3607 bool Generator::GenerateFile(const FileDescriptor* file,
3608                              const GeneratorOptions& options,
3609                              GeneratorContext* context,
3610                              bool use_short_name) const {
3611   std::string filename =
3612       options.output_dir + "/" +
3613       GetJSFilename(options, use_short_name
3614                                  ? file->name().substr(file->name().rfind('/'))
3615                                  : file->name());
3616   std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3617   GOOGLE_CHECK(output);
3618   GeneratedCodeInfo annotations;
3619   io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3620       &annotations);
3621   io::Printer printer(output.get(), '$',
3622                       options.annotate_code ? &annotation_collector : nullptr);
3623 
3624   GenerateFile(options, &printer, file);
3625 
3626   if (printer.failed()) {
3627     return false;
3628   }
3629 
3630   if (options.annotate_code) {
3631     EmbedCodeAnnotations(annotations, &printer);
3632   }
3633 
3634   return true;
3635 }
3636 
GenerateFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const3637 void Generator::GenerateFile(const GeneratorOptions& options,
3638                              io::Printer* printer,
3639                              const FileDescriptor* file) const {
3640   GenerateHeader(options, file, printer);
3641 
3642   // Generate "require" statements.
3643   if ((options.import_style == GeneratorOptions::kImportCommonJs ||
3644        options.import_style == GeneratorOptions::kImportCommonJsStrict)) {
3645     printer->Print("var jspb = require('google-protobuf');\n");
3646     printer->Print("var goog = jspb;\n");
3647 
3648     // Do not use global scope in strict mode
3649     if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
3650       printer->Print("var proto = {};\n\n");
3651     } else {
3652       printer->Print("var global = Function('return this')();\n\n");
3653     }
3654 
3655     for (int i = 0; i < file->dependency_count(); i++) {
3656       const std::string& name = file->dependency(i)->name();
3657       printer->Print(
3658           "var $alias$ = require('$file$');\n"
3659           "goog.object.extend(proto, $alias$);\n",
3660           "alias", ModuleAlias(name), "file",
3661           GetRootPath(file->name(), name) + GetJSFilename(options, name));
3662     }
3663   }
3664 
3665   std::set<std::string> provided;
3666   std::set<const FieldDescriptor*> extensions;
3667   for (int i = 0; i < file->extension_count(); i++) {
3668     // We honor the jspb::ignore option here only when working with
3669     // Closure-style imports. Use of this option is discouraged and so we want
3670     // to avoid adding new support for it.
3671     if (options.import_style == GeneratorOptions::kImportClosure &&
3672         IgnoreField(file->extension(i))) {
3673       continue;
3674     }
3675     provided.insert(GetNamespace(options, file) + "." +
3676                     JSObjectFieldName(options, file->extension(i)));
3677     extensions.insert(file->extension(i));
3678   }
3679 
3680   FindProvidesForFile(options, printer, file, &provided);
3681   GenerateProvides(options, printer, &provided);
3682   std::vector<const FileDescriptor*> files;
3683   files.push_back(file);
3684   if (options.import_style == GeneratorOptions::kImportClosure) {
3685     GenerateRequiresForLibrary(options, printer, files, &provided);
3686   }
3687 
3688   GenerateClassesAndEnums(options, printer, file);
3689 
3690   // Generate code for top-level extensions. Extensions nested inside messages
3691   // are emitted inside GenerateClassesAndEnums().
3692   for (std::set<const FieldDescriptor*>::const_iterator it = extensions.begin();
3693        it != extensions.end(); ++it) {
3694     GenerateExtension(options, printer, *it);
3695   }
3696 
3697   // if provided is empty, do not export anything
3698   if (options.import_style == GeneratorOptions::kImportCommonJs &&
3699       !provided.empty()) {
3700     printer->Print("goog.object.extend(exports, $package$);\n", "package",
3701                    GetNamespace(options, file));
3702   } else if (options.import_style == GeneratorOptions::kImportCommonJsStrict) {
3703     printer->Print("goog.object.extend(exports, proto);\n", "package",
3704                    GetNamespace(options, file));
3705   }
3706 
3707   // Emit well-known type methods.
3708   for (FileToc* toc = well_known_types_js; toc->name != NULL; toc++) {
3709     std::string name = std::string("google/protobuf/") + toc->name;
3710     if (name == StripProto(file->name()) + ".js") {
3711       printer->Print(toc->data);
3712     }
3713   }
3714 }
3715 
GenerateAll(const std::vector<const FileDescriptor * > & files,const std::string & parameter,GeneratorContext * context,std::string * error) const3716 bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
3717                             const std::string& parameter,
3718                             GeneratorContext* context,
3719                             std::string* error) const {
3720   std::vector<std::pair<std::string, std::string> > option_pairs;
3721   ParseGeneratorParameter(parameter, &option_pairs);
3722   GeneratorOptions options;
3723   if (!options.ParseFromOptions(option_pairs, error)) {
3724     return false;
3725   }
3726 
3727 
3728   if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
3729     // All output should go in a single file.
3730     std::string filename = options.output_dir + "/" + options.library +
3731                            options.GetFileNameExtension();
3732     std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3733     GOOGLE_CHECK(output.get());
3734     GeneratedCodeInfo annotations;
3735     io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3736         &annotations);
3737     io::Printer printer(
3738         output.get(), '$',
3739         options.annotate_code ? &annotation_collector : nullptr);
3740 
3741     // Pull out all extensions -- we need these to generate all
3742     // provides/requires.
3743     std::vector<const FieldDescriptor*> extensions;
3744     for (int i = 0; i < files.size(); i++) {
3745       for (int j = 0; j < files[i]->extension_count(); j++) {
3746         const FieldDescriptor* extension = files[i]->extension(j);
3747         extensions.push_back(extension);
3748       }
3749     }
3750 
3751     if (files.size() == 1) {
3752       GenerateHeader(options, files[0], &printer);
3753     } else {
3754       GenerateHeader(options, nullptr, &printer);
3755     }
3756 
3757     std::set<std::string> provided;
3758     FindProvides(options, &printer, files, &provided);
3759     FindProvidesForFields(options, &printer, extensions, &provided);
3760     GenerateProvides(options, &printer, &provided);
3761     GenerateTestOnly(options, &printer);
3762     GenerateRequiresForLibrary(options, &printer, files, &provided);
3763 
3764     GenerateFilesInDepOrder(options, &printer, files);
3765 
3766     for (int i = 0; i < extensions.size(); i++) {
3767       if (ShouldGenerateExtension(extensions[i])) {
3768         GenerateExtension(options, &printer, extensions[i]);
3769       }
3770     }
3771 
3772     if (printer.failed()) {
3773       return false;
3774     }
3775     if (options.annotate_code) {
3776       EmbedCodeAnnotations(annotations, &printer);
3777     }
3778   } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerSCC) {
3779     std::set<const Descriptor*> have_printed;
3780     SCCAnalyzer<DepsGenerator> analyzer;
3781     std::map<const void*, std::string> allowed_map;
3782     if (!GenerateJspbAllowedMap(options, files, &allowed_map, &analyzer,
3783                                 error)) {
3784       return false;
3785     }
3786 
3787     bool generated = false;
3788     for (int i = 0; i < files.size(); i++) {
3789       const FileDescriptor* file = files[i];
3790       // Force well known type to generate in a whole file.
3791       if (IsWellKnownTypeFile(file)) {
3792         if (!GenerateFile(file, options, context, true)) {
3793           return false;
3794         }
3795         generated = true;
3796         continue;
3797       }
3798       for (int j = 0; j < file->message_type_count(); j++) {
3799         const Descriptor* desc = file->message_type(j);
3800         if (have_printed.count(desc) ||
3801             allowed_map.count(analyzer.GetSCC(desc)) == 0) {
3802           continue;
3803         }
3804 
3805         generated = true;
3806         const SCC* scc = analyzer.GetSCC(desc);
3807         const std::string& filename = allowed_map[scc];
3808         std::unique_ptr<io::ZeroCopyOutputStream> output(
3809             context->Open(filename));
3810         GOOGLE_CHECK(output.get());
3811         GeneratedCodeInfo annotations;
3812         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3813             &annotations);
3814         io::Printer printer(
3815             output.get(), '$',
3816             options.annotate_code ? &annotation_collector : nullptr);
3817 
3818         GenerateHeader(options, file, &printer);
3819 
3820         std::set<std::string> provided;
3821         for (auto one_desc : scc->descriptors) {
3822           if (one_desc->containing_type() == nullptr) {
3823             FindProvidesForMessage(options, &printer, one_desc, &provided);
3824           }
3825         }
3826         GenerateProvides(options, &printer, &provided);
3827         GenerateTestOnly(options, &printer);
3828         GenerateRequiresForSCC(options, &printer, scc, &provided);
3829 
3830         for (auto one_desc : scc->descriptors) {
3831           if (one_desc->containing_type() == nullptr) {
3832             GenerateClassConstructorAndDeclareExtensionFieldInfo(
3833                 options, &printer, one_desc);
3834           }
3835         }
3836         for (auto one_desc : scc->descriptors) {
3837           if (one_desc->containing_type() == nullptr) {
3838             GenerateClass(options, &printer, one_desc);
3839           }
3840         }
3841 
3842         for (auto one_desc : scc->descriptors) {
3843           have_printed.insert(one_desc);
3844         }
3845 
3846         if (printer.failed()) {
3847           return false;
3848         }
3849         if (options.annotate_code) {
3850           EmbedCodeAnnotations(annotations, &printer);
3851         }
3852       }
3853       for (int j = 0; j < file->enum_type_count(); j++) {
3854         const EnumDescriptor* enumdesc = file->enum_type(j);
3855         if (allowed_map.count(enumdesc) == 0) {
3856           continue;
3857         }
3858 
3859         generated = true;
3860         const std::string& filename = allowed_map[enumdesc];
3861         std::unique_ptr<io::ZeroCopyOutputStream> output(
3862             context->Open(filename));
3863         GOOGLE_CHECK(output.get());
3864         GeneratedCodeInfo annotations;
3865         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3866             &annotations);
3867         io::Printer printer(
3868             output.get(), '$',
3869             options.annotate_code ? &annotation_collector : nullptr);
3870 
3871         GenerateHeader(options, file, &printer);
3872 
3873         std::set<std::string> provided;
3874         FindProvidesForEnum(options, &printer, enumdesc, &provided);
3875         GenerateProvides(options, &printer, &provided);
3876         GenerateTestOnly(options, &printer);
3877 
3878         GenerateEnum(options, &printer, enumdesc);
3879 
3880         if (printer.failed()) {
3881           return false;
3882         }
3883         if (options.annotate_code) {
3884           EmbedCodeAnnotations(annotations, &printer);
3885         }
3886       }
3887       // File-level extensions (message-level extensions are generated under
3888       // the enclosing message).
3889       if (allowed_map.count(file) == 1) {
3890         generated = true;
3891         const std::string& filename = allowed_map[file];
3892 
3893         std::unique_ptr<io::ZeroCopyOutputStream> output(
3894             context->Open(filename));
3895         GOOGLE_CHECK(output.get());
3896         GeneratedCodeInfo annotations;
3897         io::AnnotationProtoCollector<GeneratedCodeInfo> annotation_collector(
3898             &annotations);
3899         io::Printer printer(
3900             output.get(), '$',
3901             options.annotate_code ? &annotation_collector : nullptr);
3902 
3903         GenerateHeader(options, file, &printer);
3904 
3905         std::set<std::string> provided;
3906         std::vector<const FieldDescriptor*> fields;
3907 
3908         for (int j = 0; j < files[i]->extension_count(); j++) {
3909           if (ShouldGenerateExtension(files[i]->extension(j))) {
3910             fields.push_back(files[i]->extension(j));
3911           }
3912         }
3913 
3914         FindProvidesForFields(options, &printer, fields, &provided);
3915         GenerateProvides(options, &printer, &provided);
3916         GenerateTestOnly(options, &printer);
3917         GenerateRequiresForExtensions(options, &printer, fields, &provided);
3918 
3919         for (int j = 0; j < files[i]->extension_count(); j++) {
3920           if (ShouldGenerateExtension(files[i]->extension(j))) {
3921             GenerateExtension(options, &printer, files[i]->extension(j));
3922           }
3923         }
3924         if (options.annotate_code) {
3925           EmbedCodeAnnotations(annotations, &printer);
3926         }
3927       }
3928     }
3929     if (!generated) {
3930       std::string filename = options.output_dir + "/" +
3931                              "empty_no_content_void_file" +
3932                              options.GetFileNameExtension();
3933       std::unique_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3934     }
3935   } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
3936     // Generate one output file per input (.proto) file.
3937 
3938     for (int i = 0; i < files.size(); i++) {
3939       const FileDescriptor* file = files[i];
3940       if (!GenerateFile(file, options, context, false)) {
3941         return false;
3942       }
3943     }
3944   }
3945   return true;
3946 }
3947 
3948 }  // namespace js
3949 }  // namespace compiler
3950 }  // namespace protobuf
3951 }  // namespace google
3952