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