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 #ifndef _SHARED_PTR_H
39 #include <google/protobuf/stubs/shared_ptr.h>
40 #endif
41 #include <string>
42 #include <utility>
43 #include <vector>
44 
45 #include <google/protobuf/stubs/logging.h>
46 #include <google/protobuf/stubs/common.h>
47 #include <google/protobuf/stubs/stringprintf.h>
48 #include <google/protobuf/io/printer.h>
49 #include <google/protobuf/io/zero_copy_stream.h>
50 #include <google/protobuf/descriptor.pb.h>
51 #include <google/protobuf/descriptor.h>
52 #include <google/protobuf/stubs/strutil.h>
53 
54 namespace google {
55 namespace protobuf {
56 namespace compiler {
57 namespace js {
58 
59 // Sorted list of JavaScript keywords. These cannot be used as names. If they
60 // appear, we prefix them with "pb_".
61 const char* kKeyword[] = {
62   "abstract",
63   "boolean",
64   "break",
65   "byte",
66   "case",
67   "catch",
68   "char",
69   "class",
70   "const",
71   "continue",
72   "debugger",
73   "default",
74   "delete",
75   "do",
76   "double",
77   "else",
78   "enum",
79   "export",
80   "extends",
81   "false",
82   "final",
83   "finally",
84   "float",
85   "for",
86   "function",
87   "goto",
88   "if",
89   "implements",
90   "import",
91   "in",
92   "instanceof",
93   "int",
94   "interface",
95   "long",
96   "native",
97   "new",
98   "null",
99   "package",
100   "private",
101   "protected",
102   "public",
103   "return",
104   "short",
105   "static",
106   "super",
107   "switch",
108   "synchronized",
109   "this",
110   "throw",
111   "throws",
112   "transient",
113   "try",
114   "typeof",
115   "var",
116   "void",
117   "volatile",
118   "while",
119   "with",
120 };
121 
122 static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
123 
124 namespace {
125 
126 // The mode of operation for bytes fields. Historically JSPB always carried
127 // bytes as JS {string}, containing base64 content by convention. With binary
128 // and proto3 serialization the new convention is to represent it as binary
129 // data in Uint8Array. See b/26173701 for background on the migration.
130 enum BytesMode {
131   BYTES_DEFAULT,  // Default type for getBytesField to return.
132   BYTES_B64,      // Explicitly coerce to base64 string where needed.
133   BYTES_U8,       // Explicitly coerce to Uint8Array where needed.
134 };
135 
IsReserved(const string & ident)136 bool IsReserved(const string& ident) {
137   for (int i = 0; i < kNumKeyword; i++) {
138     if (ident == kKeyword[i]) {
139       return true;
140     }
141   }
142   return false;
143 }
144 
145 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto
146 // suffix stripped.
147 // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc.
StripProto(const string & filename)148 string StripProto(const string& filename) {
149   const char* suffix = HasSuffixString(filename, ".protodevel")
150       ? ".protodevel" : ".proto";
151   return StripSuffixString(filename, suffix);
152 }
153 
154 // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
155 // file foo/bar/baz.js.
GetJSFilename(const string & filename)156 string GetJSFilename(const string& filename) {
157   return StripProto(filename) + "_pb.js";
158 }
159 
160 // Given a filename like foo/bar/baz.proto, returns the root directory
161 // path ../../
GetRootPath(const string & from_filename,const string & to_filename)162 string GetRootPath(const string& from_filename, const string& to_filename) {
163   if (to_filename.find("google/protobuf") == 0) {
164     // Well-known types (.proto files in the google/protobuf directory) are
165     // assumed to come from the 'google-protobuf' npm package.  We may want to
166     // generalize this exception later by letting others put generated code in
167     // their own npm packages.
168     return "google-protobuf/";
169   }
170 
171   size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
172   if (slashes == 0) {
173     return "./";
174   }
175   string result = "";
176   for (size_t i = 0; i < slashes; i++) {
177     result += "../";
178   }
179   return result;
180 }
181 
182 // Returns the alias we assign to the module of the given .proto filename
183 // when importing.
ModuleAlias(const string & filename)184 string ModuleAlias(const string& filename) {
185   // This scheme could technically cause problems if a file includes any 2 of:
186   //   foo/bar_baz.proto
187   //   foo_bar_baz.proto
188   //   foo_bar/baz.proto
189   //
190   // We'll worry about this problem if/when we actually see it.  This name isn't
191   // exposed to users so we can change it later if we need to.
192   string basename = StripProto(filename);
193   StripString(&basename, "-", '$');
194   StripString(&basename, "/", '_');
195   return basename + "_pb";
196 }
197 
198 // Returns the fully normalized JavaScript path for the given
199 // file descriptor's package.
GetPath(const GeneratorOptions & options,const FileDescriptor * file)200 string GetPath(const GeneratorOptions& options,
201                const FileDescriptor* file) {
202   if (!options.namespace_prefix.empty()) {
203     return options.namespace_prefix;
204   } else if (!file->package().empty()) {
205     return "proto." + file->package();
206   } else {
207     return "proto";
208   }
209 }
210 
211 // Forward declare, so that GetPrefix can call this method,
212 // which in turn, calls GetPrefix.
213 string GetPath(const GeneratorOptions& options,
214                const Descriptor* descriptor);
215 
216 // Returns the path prefix for a message or enumeration that
217 // lives under the given file and containing type.
GetPrefix(const GeneratorOptions & options,const FileDescriptor * file_descriptor,const Descriptor * containing_type)218 string GetPrefix(const GeneratorOptions& options,
219                  const FileDescriptor* file_descriptor,
220                  const Descriptor* containing_type) {
221   string prefix = "";
222 
223   if (containing_type == NULL) {
224     prefix = GetPath(options, file_descriptor);
225   } else {
226     prefix = GetPath(options, containing_type);
227   }
228 
229   if (!prefix.empty()) {
230     prefix += ".";
231   }
232 
233   return prefix;
234 }
235 
236 
237 // Returns the fully normalized JavaScript path for the given
238 // message descriptor.
GetPath(const GeneratorOptions & options,const Descriptor * descriptor)239 string GetPath(const GeneratorOptions& options,
240                const Descriptor* descriptor) {
241   return GetPrefix(
242       options, descriptor->file(),
243       descriptor->containing_type()) + descriptor->name();
244 }
245 
246 
247 // Returns the fully normalized JavaScript path for the given
248 // field's containing message descriptor.
GetPath(const GeneratorOptions & options,const FieldDescriptor * descriptor)249 string GetPath(const GeneratorOptions& options,
250                const FieldDescriptor* descriptor) {
251   return GetPath(options, descriptor->containing_type());
252 }
253 
254 // Returns the fully normalized JavaScript path for the given
255 // enumeration descriptor.
GetPath(const GeneratorOptions & options,const EnumDescriptor * enum_descriptor)256 string GetPath(const GeneratorOptions& options,
257                const EnumDescriptor* enum_descriptor) {
258   return GetPrefix(
259       options, enum_descriptor->file(),
260       enum_descriptor->containing_type()) + enum_descriptor->name();
261 }
262 
263 
264 // Returns the fully normalized JavaScript path for the given
265 // enumeration value descriptor.
GetPath(const GeneratorOptions & options,const EnumValueDescriptor * value_descriptor)266 string GetPath(const GeneratorOptions& options,
267                const EnumValueDescriptor* value_descriptor) {
268   return GetPath(
269       options,
270       value_descriptor->type()) + "." + value_descriptor->name();
271 }
272 
MaybeCrossFileRef(const GeneratorOptions & options,const FileDescriptor * from_file,const Descriptor * to_message)273 string MaybeCrossFileRef(const GeneratorOptions& options,
274                          const FileDescriptor* from_file,
275                          const Descriptor* to_message) {
276   if (options.import_style == GeneratorOptions::IMPORT_COMMONJS &&
277       from_file != to_message->file()) {
278     // Cross-file ref in CommonJS needs to use the module alias instead of
279     // the global name.
280     return ModuleAlias(to_message->file()->name()) + "." + to_message->name();
281   } else {
282     // Within a single file we use a full name.
283     return GetPath(options, to_message);
284   }
285 }
286 
SubmessageTypeRef(const GeneratorOptions & options,const FieldDescriptor * field)287 string SubmessageTypeRef(const GeneratorOptions& options,
288                          const FieldDescriptor* field) {
289   GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
290   return MaybeCrossFileRef(options, field->file(), field->message_type());
291 }
292 
293 // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
294 // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
295 // and with reserved words triggering a "pb_" prefix.
296 // - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
297 // (use the name directly), then append "List" if appropriate, then append "$"
298 // if resulting name is equal to a reserved word.
299 // - Enums: just uppercase.
300 
301 // Locale-independent version of ToLower that deals only with ASCII A-Z.
ToLowerASCII(char c)302 char ToLowerASCII(char c) {
303   if (c >= 'A' && c <= 'Z') {
304     return (c - 'A') + 'a';
305   } else {
306     return c;
307   }
308 }
309 
ParseLowerUnderscore(const string & input)310 vector<string> ParseLowerUnderscore(const string& input) {
311   vector<string> words;
312   string running = "";
313   for (int i = 0; i < input.size(); i++) {
314     if (input[i] == '_') {
315       if (!running.empty()) {
316         words.push_back(running);
317         running.clear();
318       }
319     } else {
320       running += ToLowerASCII(input[i]);
321     }
322   }
323   if (!running.empty()) {
324     words.push_back(running);
325   }
326   return words;
327 }
328 
ParseUpperCamel(const string & input)329 vector<string> ParseUpperCamel(const string& input) {
330   vector<string> words;
331   string running = "";
332   for (int i = 0; i < input.size(); i++) {
333     if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
334       words.push_back(running);
335       running.clear();
336     }
337     running += ToLowerASCII(input[i]);
338   }
339   if (!running.empty()) {
340     words.push_back(running);
341   }
342   return words;
343 }
344 
ToLowerCamel(const vector<string> & words)345 string ToLowerCamel(const vector<string>& words) {
346   string result;
347   for (int i = 0; i < words.size(); i++) {
348     string word = words[i];
349     if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
350       word[0] = (word[0] - 'A') + 'a';
351     } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
352       word[0] = (word[0] - 'a') + 'A';
353     }
354     result += word;
355   }
356   return result;
357 }
358 
ToUpperCamel(const vector<string> & words)359 string ToUpperCamel(const vector<string>& words) {
360   string result;
361   for (int i = 0; i < words.size(); i++) {
362     string word = words[i];
363     if (word[0] >= 'a' && word[0] <= 'z') {
364       word[0] = (word[0] - 'a') + 'A';
365     }
366     result += word;
367   }
368   return result;
369 }
370 
371 // Based on code from descriptor.cc (Thanks Kenton!)
372 // Uppercases the entire string, turning ValueName into
373 // VALUENAME.
ToEnumCase(const string & input)374 string ToEnumCase(const string& input) {
375   string result;
376   result.reserve(input.size());
377 
378   for (int i = 0; i < input.size(); i++) {
379     if ('a' <= input[i] && input[i] <= 'z') {
380       result.push_back(input[i] - 'a' + 'A');
381     } else {
382       result.push_back(input[i]);
383     }
384   }
385 
386   return result;
387 }
388 
ToFileName(const string & input)389 string ToFileName(const string& input) {
390   string result;
391   result.reserve(input.size());
392 
393   for (int i = 0; i < input.size(); i++) {
394     if ('A' <= input[i] && input[i] <= 'Z') {
395       result.push_back(input[i] - 'A' + 'a');
396     } else {
397       result.push_back(input[i]);
398     }
399   }
400 
401   return result;
402 }
403 
404 // When we're generating one output file per type name, this is the filename
405 // that top-level extensions should go in.
GetExtensionFileName(const GeneratorOptions & options,const FileDescriptor * file)406 string GetExtensionFileName(const GeneratorOptions& options,
407                             const FileDescriptor* file) {
408   return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js";
409 }
410 
411 // When we're generating one output file per type name, this is the filename
412 // that a top-level message should go in.
GetMessageFileName(const GeneratorOptions & options,const Descriptor * desc)413 string GetMessageFileName(const GeneratorOptions& options,
414                           const Descriptor* desc) {
415   return options.output_dir + "/" + ToFileName(desc->name()) + ".js";
416 }
417 
418 // When we're generating one output file per type name, this is the filename
419 // that a top-level message should go in.
GetEnumFileName(const GeneratorOptions & options,const EnumDescriptor * desc)420 string GetEnumFileName(const GeneratorOptions& options,
421                        const EnumDescriptor* desc) {
422   return options.output_dir + "/" + ToFileName(desc->name()) + ".js";
423 }
424 
425 // Returns the message/response ID, if set.
GetMessageId(const Descriptor * desc)426 string GetMessageId(const Descriptor* desc) {
427   return string();
428 }
429 
IgnoreExtensionField(const FieldDescriptor * field)430 bool IgnoreExtensionField(const FieldDescriptor* field) {
431   // Exclude descriptor extensions from output "to avoid clutter" (from original
432   // codegen).
433   return field->is_extension() &&
434          field->containing_type()->file()->name() ==
435              "google/protobuf/descriptor.proto";
436 }
437 
438 
439 // Used inside Google only -- do not remove.
IsResponse(const Descriptor * desc)440 bool IsResponse(const Descriptor* desc) { return false; }
441 
IgnoreField(const FieldDescriptor * field)442 bool IgnoreField(const FieldDescriptor* field) {
443   return IgnoreExtensionField(field);
444 }
445 
446 
447 // Do we ignore this message type?
IgnoreMessage(const GeneratorOptions & options,const Descriptor * d)448 bool IgnoreMessage(const GeneratorOptions& options, const Descriptor* d) {
449   return d->options().map_entry();
450 }
451 
452 // Does JSPB ignore this entire oneof? True only if all fields are ignored.
IgnoreOneof(const OneofDescriptor * oneof)453 bool IgnoreOneof(const OneofDescriptor* oneof) {
454   for (int i = 0; i < oneof->field_count(); i++) {
455     if (!IgnoreField(oneof->field(i))) {
456       return false;
457     }
458   }
459   return true;
460 }
461 
JSIdent(const GeneratorOptions & options,const FieldDescriptor * field,bool is_upper_camel,bool is_map)462 string JSIdent(const GeneratorOptions& options,
463                const FieldDescriptor* field,
464                bool is_upper_camel,
465                bool is_map) {
466   string result;
467   if (field->type() == FieldDescriptor::TYPE_GROUP) {
468     result = is_upper_camel ?
469         ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
470         ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
471   } else {
472     result = is_upper_camel ?
473         ToUpperCamel(ParseLowerUnderscore(field->name())) :
474         ToLowerCamel(ParseLowerUnderscore(field->name()));
475   }
476   if (is_map || (field->is_map())) {
477     // JSPB-style or proto3-style map.
478     result += "Map";
479   } else if (field->is_repeated()) {
480     // Repeated field.
481     result += "List";
482   }
483   return result;
484 }
485 
JSObjectFieldName(const GeneratorOptions & options,const FieldDescriptor * field)486 string JSObjectFieldName(const GeneratorOptions& options,
487                          const FieldDescriptor* field) {
488   string name = JSIdent(
489       options,
490       field,
491       /* is_upper_camel = */ false,
492       /* is_map = */ false);
493   if (IsReserved(name)) {
494     name = "pb_" + name;
495   }
496   return name;
497 }
498 
JSByteGetterSuffix(BytesMode bytes_mode)499 string JSByteGetterSuffix(BytesMode bytes_mode) {
500   switch (bytes_mode) {
501     case BYTES_DEFAULT:
502       return "";
503     case BYTES_B64:
504       return "B64";
505     case BYTES_U8:
506       return "U8";
507     default:
508       assert(false);
509   }
510   return "";
511 }
512 
513 // Returns the field name as a capitalized portion of a getter/setter method
514 // name, e.g. MyField for .getMyField().
JSGetterName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode=BYTES_DEFAULT)515 string JSGetterName(const GeneratorOptions& options,
516                     const FieldDescriptor* field,
517                     BytesMode bytes_mode = BYTES_DEFAULT) {
518   string name = JSIdent(options, field,
519                         /* is_upper_camel = */ true,
520                         /* is_map = */ false);
521   if (field->type() == FieldDescriptor::TYPE_BYTES) {
522     string suffix = JSByteGetterSuffix(bytes_mode);
523     if (!suffix.empty()) {
524       name += "_as" + suffix;
525     }
526   }
527   if (name == "Extension" || name == "JsPbMessageId") {
528     // Avoid conflicts with base-class names.
529     name += "$";
530   }
531   return name;
532 }
533 
JSMapGetterName(const GeneratorOptions & options,const FieldDescriptor * field)534 string JSMapGetterName(const GeneratorOptions& options,
535                        const FieldDescriptor* field) {
536   return JSIdent(options, field,
537                  /* is_upper_camel = */ true,
538                  /* is_map = */ true);
539 }
540 
541 
542 
JSOneofName(const OneofDescriptor * oneof)543 string JSOneofName(const OneofDescriptor* oneof) {
544   return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
545 }
546 
547 // Returns the index corresponding to this field in the JSPB array (underlying
548 // data storage array).
JSFieldIndex(const FieldDescriptor * field)549 string JSFieldIndex(const FieldDescriptor* field) {
550   // Determine whether this field is a member of a group. Group fields are a bit
551   // wonky: their "containing type" is a message type created just for the
552   // group, and that type's parent type has a field with the group-message type
553   // as its message type and TYPE_GROUP as its field type. For such fields, the
554   // index we use is relative to the field number of the group submessage field.
555   // For all other fields, we just use the field number.
556   const Descriptor* containing_type = field->containing_type();
557   const Descriptor* parent_type = containing_type->containing_type();
558   if (parent_type != NULL) {
559     for (int i = 0; i < parent_type->field_count(); i++) {
560       if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
561           parent_type->field(i)->message_type() == containing_type) {
562         return SimpleItoa(field->number() - parent_type->field(i)->number());
563       }
564     }
565   }
566   return SimpleItoa(field->number());
567 }
568 
JSOneofIndex(const OneofDescriptor * oneof)569 string JSOneofIndex(const OneofDescriptor* oneof) {
570   int index = -1;
571   for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
572     const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
573     // If at least one field in this oneof is not JSPB-ignored, count the oneof.
574     for (int j = 0; j < o->field_count(); j++) {
575       const FieldDescriptor* f = o->field(j);
576       if (!IgnoreField(f)) {
577         index++;
578         break;  // inner loop
579       }
580     }
581     if (o == oneof) {
582       break;
583     }
584   }
585   return SimpleItoa(index);
586 }
587 
588 // Decodes a codepoint in \x0000 -- \xFFFF.
DecodeUTF8Codepoint(uint8 * bytes,size_t * length)589 uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
590   if (*length == 0) {
591     return 0;
592   }
593   size_t expected = 0;
594   if ((*bytes & 0x80) == 0) {
595     expected = 1;
596   } else if ((*bytes & 0xe0) == 0xc0) {
597     expected = 2;
598   } else if ((*bytes & 0xf0) == 0xe0) {
599     expected = 3;
600   } else {
601     // Too long -- don't accept.
602     *length = 0;
603     return 0;
604   }
605 
606   if (*length < expected) {
607     // Not enough bytes -- don't accept.
608     *length = 0;
609     return 0;
610   }
611 
612   *length = expected;
613   switch (expected) {
614     case 1: return bytes[0];
615     case 2: return ((bytes[0] & 0x1F) << 6)  |
616                    ((bytes[1] & 0x3F) << 0);
617     case 3: return ((bytes[0] & 0x0F) << 12) |
618                    ((bytes[1] & 0x3F) << 6)  |
619                    ((bytes[2] & 0x3F) << 0);
620     default: return 0;
621   }
622 }
623 
624 // Escapes the contents of a string to be included within double-quotes ("") in
625 // JavaScript. The input data should be a UTF-8 encoded C++ string of chars.
626 // Returns false if |out| was truncated because |in| contained invalid UTF-8 or
627 // codepoints outside the BMP.
628 // TODO(lukestebbing): Support codepoints outside the BMP.
EscapeJSString(const string & in,string * out)629 bool EscapeJSString(const string& in, string* out) {
630   size_t decoded = 0;
631   for (size_t i = 0; i < in.size(); i += decoded) {
632     uint16 codepoint = 0;
633     // Decode the next UTF-8 codepoint.
634     size_t have_bytes = in.size() - i;
635     uint8 bytes[3] = {
636         static_cast<uint8>(in[i]),
637         static_cast<uint8>(((i + 1) < in.size()) ? in[i + 1] : 0),
638         static_cast<uint8>(((i + 2) < in.size()) ? in[i + 2] : 0),
639     };
640     codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
641     if (have_bytes == 0) {
642       return false;
643     }
644     decoded = have_bytes;
645 
646     switch (codepoint) {
647       case '\'': *out += "\\x27"; break;
648       case '"': *out += "\\x22"; break;
649       case '<': *out += "\\x3c"; break;
650       case '=': *out += "\\x3d"; break;
651       case '>': *out += "\\x3e"; break;
652       case '&': *out += "\\x26"; break;
653       case '\b': *out += "\\b"; break;
654       case '\t': *out += "\\t"; break;
655       case '\n': *out += "\\n"; break;
656       case '\f': *out += "\\f"; break;
657       case '\r': *out += "\\r"; break;
658       case '\\': *out += "\\\\"; break;
659       default:
660         // TODO(lukestebbing): Once we're supporting codepoints outside the BMP,
661         // use a single Unicode codepoint escape if the output language is
662         // ECMAScript 2015 or above. Otherwise, use a surrogate pair.
663         // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Lexical_grammar#String_literals
664         if (codepoint >= 0x20 && codepoint <= 0x7e) {
665           *out += static_cast<char>(codepoint);
666         } else if (codepoint >= 0x100) {
667           *out += StringPrintf("\\u%04x", codepoint);
668         } else {
669           *out += StringPrintf("\\x%02x", codepoint);
670         }
671         break;
672     }
673   }
674   return true;
675 }
676 
EscapeBase64(const string & in)677 string EscapeBase64(const string& in) {
678   static const char* kAlphabet =
679       "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
680   string result;
681 
682   for (size_t i = 0; i < in.size(); i += 3) {
683     int value = (in[i] << 16) |
684         (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
685         (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
686     result += kAlphabet[(value >> 18) & 0x3f];
687     result += kAlphabet[(value >> 12) & 0x3f];
688     if ((i + 1) < in.size()) {
689       result += kAlphabet[(value >>  6) & 0x3f];
690     } else {
691       result += '=';
692     }
693     if ((i + 2) < in.size()) {
694       result += kAlphabet[(value >>  0) & 0x3f];
695     } else {
696       result += '=';
697     }
698   }
699 
700   return result;
701 }
702 
703 // Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
704 // original codegen's formatting (which is just .toString() on java.lang.Double
705 // or java.lang.Float).
PostProcessFloat(string result)706 string PostProcessFloat(string result) {
707   // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
708   if (result == "inf") {
709     return "Infinity";
710   } else if (result == "-inf") {
711     return "-Infinity";
712   } else if (result == "nan") {
713     return "NaN";
714   }
715 
716   // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
717   // ensure that the mantissa (portion prior to the "e") has at least one
718   // fractional digit (after the decimal point), and (iii) strip any unnecessary
719   // leading zeroes and/or '+' signs from the exponent.
720   string::size_type exp_pos = result.find('e');
721   if (exp_pos != string::npos) {
722     string mantissa = result.substr(0, exp_pos);
723     string exponent = result.substr(exp_pos + 1);
724 
725     // Add ".0" to mantissa if no fractional part exists.
726     if (mantissa.find('.') == string::npos) {
727       mantissa += ".0";
728     }
729 
730     // Strip the sign off the exponent and store as |exp_neg|.
731     bool exp_neg = false;
732     if (!exponent.empty() && exponent[0] == '+') {
733       exponent = exponent.substr(1);
734     } else if (!exponent.empty() && exponent[0] == '-') {
735       exp_neg = true;
736       exponent = exponent.substr(1);
737     }
738 
739     // Strip any leading zeroes off the exponent.
740     while (exponent.size() > 1 && exponent[0] == '0') {
741       exponent = exponent.substr(1);
742     }
743 
744     return mantissa + "E" + string(exp_neg ? "-" : "") + exponent;
745   }
746 
747   // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
748   // decimal/fractional part in order to match output of original codegen.
749   if (result.find('.') == string::npos) {
750     result += ".0";
751   }
752 
753   return result;
754 }
755 
FloatToString(float value)756 string FloatToString(float value) {
757   string result = SimpleFtoa(value);
758   return PostProcessFloat(result);
759 }
760 
DoubleToString(double value)761 string DoubleToString(double value) {
762   string result = SimpleDtoa(value);
763   return PostProcessFloat(result);
764 }
765 
MaybeNumberString(const FieldDescriptor * field,const string & orig)766 string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
767   return orig;
768 }
769 
JSFieldDefault(const FieldDescriptor * field)770 string JSFieldDefault(const FieldDescriptor* field) {
771   switch (field->cpp_type()) {
772     case FieldDescriptor::CPPTYPE_INT32:
773       return MaybeNumberString(
774           field, SimpleItoa(field->default_value_int32()));
775     case FieldDescriptor::CPPTYPE_UINT32:
776       // The original codegen is in Java, and Java protobufs store unsigned
777       // integer values as signed integer values. In order to exactly match the
778       // output, we need to reinterpret as base-2 signed. Ugh.
779       return MaybeNumberString(
780           field, SimpleItoa(static_cast<int32>(field->default_value_uint32())));
781     case FieldDescriptor::CPPTYPE_INT64:
782       return MaybeNumberString(
783           field, SimpleItoa(field->default_value_int64()));
784     case FieldDescriptor::CPPTYPE_UINT64:
785       // See above note for uint32 -- reinterpreting as signed.
786       return MaybeNumberString(
787           field, SimpleItoa(static_cast<int64>(field->default_value_uint64())));
788     case FieldDescriptor::CPPTYPE_ENUM:
789       return SimpleItoa(field->default_value_enum()->number());
790     case FieldDescriptor::CPPTYPE_BOOL:
791       return field->default_value_bool() ? "true" : "false";
792     case FieldDescriptor::CPPTYPE_FLOAT:
793       return FloatToString(field->default_value_float());
794     case FieldDescriptor::CPPTYPE_DOUBLE:
795       return DoubleToString(field->default_value_double());
796     case FieldDescriptor::CPPTYPE_STRING:
797       if (field->type() == FieldDescriptor::TYPE_STRING) {
798         string out;
799         bool is_valid = EscapeJSString(field->default_value_string(), &out);
800         if (!is_valid) {
801           // TODO(lukestebbing): Decide whether this should be a hard error.
802           GOOGLE_LOG(WARNING) << "The default value for field " << field->full_name()
803                        << " was truncated since it contained invalid UTF-8 or"
804                           " codepoints outside the basic multilingual plane.";
805         }
806         return "\"" + out + "\"";
807       } else {  // Bytes
808         return "\"" + EscapeBase64(field->default_value_string()) + "\"";
809       }
810     case FieldDescriptor::CPPTYPE_MESSAGE:
811       return "null";
812   }
813   GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
814   return "";
815 }
816 
ProtoTypeName(const GeneratorOptions & options,const FieldDescriptor * field)817 string ProtoTypeName(const GeneratorOptions& options,
818                      const FieldDescriptor* field) {
819   switch (field->type()) {
820     case FieldDescriptor::TYPE_BOOL:
821       return "bool";
822     case FieldDescriptor::TYPE_INT32:
823       return "int32";
824     case FieldDescriptor::TYPE_UINT32:
825       return "uint32";
826     case FieldDescriptor::TYPE_SINT32:
827       return "sint32";
828     case FieldDescriptor::TYPE_FIXED32:
829       return "fixed32";
830     case FieldDescriptor::TYPE_SFIXED32:
831       return "sfixed32";
832     case FieldDescriptor::TYPE_INT64:
833       return "int64";
834     case FieldDescriptor::TYPE_UINT64:
835       return "uint64";
836     case FieldDescriptor::TYPE_SINT64:
837       return "sint64";
838     case FieldDescriptor::TYPE_FIXED64:
839       return "fixed64";
840     case FieldDescriptor::TYPE_SFIXED64:
841       return "sfixed64";
842     case FieldDescriptor::TYPE_FLOAT:
843       return "float";
844     case FieldDescriptor::TYPE_DOUBLE:
845       return "double";
846     case FieldDescriptor::TYPE_STRING:
847       return "string";
848     case FieldDescriptor::TYPE_BYTES:
849       return "bytes";
850     case FieldDescriptor::TYPE_GROUP:
851       return GetPath(options, field->message_type());
852     case FieldDescriptor::TYPE_ENUM:
853       return GetPath(options, field->enum_type());
854     case FieldDescriptor::TYPE_MESSAGE:
855       return GetPath(options, field->message_type());
856     default:
857       return "";
858   }
859 }
860 
JSIntegerTypeName(const FieldDescriptor * field)861 string JSIntegerTypeName(const FieldDescriptor* field) {
862   return "number";
863 }
864 
JSStringTypeName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode)865 string JSStringTypeName(const GeneratorOptions& options,
866                         const FieldDescriptor* field,
867                         BytesMode bytes_mode) {
868   if (field->type() == FieldDescriptor::TYPE_BYTES) {
869     switch (bytes_mode) {
870       case BYTES_DEFAULT:
871         return "(string|Uint8Array)";
872       case BYTES_B64:
873         return "string";
874       case BYTES_U8:
875         return "Uint8Array";
876       default:
877         assert(false);
878     }
879   }
880   return "string";
881 }
882 
JSTypeName(const GeneratorOptions & options,const FieldDescriptor * field,BytesMode bytes_mode)883 string JSTypeName(const GeneratorOptions& options,
884                   const FieldDescriptor* field,
885                   BytesMode bytes_mode) {
886   switch (field->cpp_type()) {
887     case FieldDescriptor::CPPTYPE_BOOL:
888       return "boolean";
889     case FieldDescriptor::CPPTYPE_INT32:
890       return JSIntegerTypeName(field);
891     case FieldDescriptor::CPPTYPE_INT64:
892       return JSIntegerTypeName(field);
893     case FieldDescriptor::CPPTYPE_UINT32:
894       return JSIntegerTypeName(field);
895     case FieldDescriptor::CPPTYPE_UINT64:
896       return JSIntegerTypeName(field);
897     case FieldDescriptor::CPPTYPE_FLOAT:
898       return "number";
899     case FieldDescriptor::CPPTYPE_DOUBLE:
900       return "number";
901     case FieldDescriptor::CPPTYPE_STRING:
902       return JSStringTypeName(options, field, bytes_mode);
903     case FieldDescriptor::CPPTYPE_ENUM:
904       return GetPath(options, field->enum_type());
905     case FieldDescriptor::CPPTYPE_MESSAGE:
906       return GetPath(options, field->message_type());
907     default:
908       return "";
909   }
910 }
911 
912 bool HasFieldPresence(const FieldDescriptor* field);
913 
JSFieldTypeAnnotation(const GeneratorOptions & options,const FieldDescriptor * field,bool force_optional,bool force_present,bool singular_if_not_packed,BytesMode bytes_mode=BYTES_DEFAULT)914 string JSFieldTypeAnnotation(const GeneratorOptions& options,
915                              const FieldDescriptor* field,
916                              bool force_optional,
917                              bool force_present,
918                              bool singular_if_not_packed,
919                              BytesMode bytes_mode = BYTES_DEFAULT) {
920   bool is_primitive =
921       (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
922        field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE &&
923         (field->type() != FieldDescriptor::TYPE_BYTES ||
924             bytes_mode == BYTES_B64));
925 
926   string jstype = JSTypeName(options, field, bytes_mode);
927 
928   if (field->is_repeated() &&
929       (field->is_packed() || !singular_if_not_packed)) {
930     if (field->type() == FieldDescriptor::TYPE_BYTES &&
931         bytes_mode == BYTES_DEFAULT) {
932       jstype = "(Array<!Uint8Array>|Array<string>)";
933     } else {
934       if (!is_primitive) {
935         jstype = "!" + jstype;
936       }
937       jstype = "Array.<" + jstype + ">";
938     }
939     if (!force_optional) {
940       jstype = "!" + jstype;
941     }
942   }
943 
944   if (field->is_optional() && is_primitive &&
945       force_optional && !force_present) {
946     jstype += "?";
947   } else if (field->is_required() && !is_primitive && !force_optional) {
948     jstype = "!" + jstype;
949   }
950 
951   if (force_optional && HasFieldPresence(field)) {
952     jstype += "|undefined";
953   }
954   if (force_present && jstype[0] != '!' && !is_primitive) {
955     jstype = "!" + jstype;
956   }
957 
958   return jstype;
959 }
960 
JSBinaryReaderMethodType(const FieldDescriptor * field)961 string JSBinaryReaderMethodType(const FieldDescriptor* field) {
962   string name = field->type_name();
963   if (name[0] >= 'a' && name[0] <= 'z') {
964     name[0] = (name[0] - 'a') + 'A';
965   }
966 
967   return name;
968 }
969 
JSBinaryReadWriteMethodName(const FieldDescriptor * field,bool is_writer)970 string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
971                                    bool is_writer) {
972   string name = JSBinaryReaderMethodType(field);
973   if (field->is_packed()) {
974     name = "Packed" + name;
975   } else if (is_writer && field->is_repeated()) {
976     name = "Repeated" + name;
977   }
978   return name;
979 }
980 
JSBinaryReaderMethodName(const GeneratorOptions & options,const FieldDescriptor * field)981 string JSBinaryReaderMethodName(const GeneratorOptions& options,
982                                 const FieldDescriptor* field) {
983   if (options.binary) {
984     return "jspb.BinaryReader.prototype.read" +
985            JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
986   } else {
987     return "null";
988   }
989 }
990 
JSBinaryWriterMethodName(const GeneratorOptions & options,const FieldDescriptor * field)991 string JSBinaryWriterMethodName(const GeneratorOptions& options,
992                                 const FieldDescriptor* field) {
993   if (options.binary) {
994     return "jspb.BinaryWriter.prototype.write" +
995            JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
996   } else {
997     return "null";
998   }
999 }
1000 
JSReturnClause(const FieldDescriptor * desc)1001 string JSReturnClause(const FieldDescriptor* desc) {
1002   return "";
1003 }
1004 
JSReturnDoc(const GeneratorOptions & options,const FieldDescriptor * desc)1005 string JSReturnDoc(const GeneratorOptions& options,
1006                    const FieldDescriptor* desc) {
1007   return "";
1008 }
1009 
HasRepeatedFields(const Descriptor * desc)1010 bool HasRepeatedFields(const Descriptor* desc) {
1011   for (int i = 0; i < desc->field_count(); i++) {
1012     if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
1013       return true;
1014     }
1015   }
1016   return false;
1017 }
1018 
1019 static const char* kRepeatedFieldArrayName = ".repeatedFields_";
1020 
RepeatedFieldsArrayName(const GeneratorOptions & options,const Descriptor * desc)1021 string RepeatedFieldsArrayName(const GeneratorOptions& options,
1022                                const Descriptor* desc) {
1023   return HasRepeatedFields(desc) ?
1024       (GetPath(options, desc) + kRepeatedFieldArrayName) : "null";
1025 }
1026 
HasOneofFields(const Descriptor * desc)1027 bool HasOneofFields(const Descriptor* desc) {
1028   for (int i = 0; i < desc->field_count(); i++) {
1029     if (desc->field(i)->containing_oneof()) {
1030       return true;
1031     }
1032   }
1033   return false;
1034 }
1035 
1036 static const char* kOneofGroupArrayName = ".oneofGroups_";
1037 
OneofFieldsArrayName(const GeneratorOptions & options,const Descriptor * desc)1038 string OneofFieldsArrayName(const GeneratorOptions& options,
1039                             const Descriptor* desc) {
1040   return HasOneofFields(desc) ?
1041       (GetPath(options, desc) + kOneofGroupArrayName) : "null";
1042 }
1043 
RepeatedFieldNumberList(const Descriptor * desc)1044 string RepeatedFieldNumberList(const Descriptor* desc) {
1045   std::vector<string> numbers;
1046   for (int i = 0; i < desc->field_count(); i++) {
1047     if (desc->field(i)->is_repeated() && !desc->field(i)->is_map()) {
1048       numbers.push_back(JSFieldIndex(desc->field(i)));
1049     }
1050   }
1051   return "[" + Join(numbers, ",") + "]";
1052 }
1053 
OneofGroupList(const Descriptor * desc)1054 string OneofGroupList(const Descriptor* desc) {
1055   // List of arrays (one per oneof), each of which is a list of field indices
1056   std::vector<string> oneof_entries;
1057   for (int i = 0; i < desc->oneof_decl_count(); i++) {
1058     const OneofDescriptor* oneof = desc->oneof_decl(i);
1059     if (IgnoreOneof(oneof)) {
1060       continue;
1061     }
1062 
1063     std::vector<string> oneof_fields;
1064     for (int j = 0; j < oneof->field_count(); j++) {
1065       if (IgnoreField(oneof->field(j))) {
1066         continue;
1067       }
1068       oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
1069     }
1070     oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
1071   }
1072   return "[" + Join(oneof_entries, ",") + "]";
1073 }
1074 
JSOneofArray(const GeneratorOptions & options,const FieldDescriptor * field)1075 string JSOneofArray(const GeneratorOptions& options,
1076                     const FieldDescriptor* field) {
1077   return OneofFieldsArrayName(options, field->containing_type()) + "[" +
1078       JSOneofIndex(field->containing_oneof()) + "]";
1079 }
1080 
RelativeTypeName(const FieldDescriptor * field)1081 string RelativeTypeName(const FieldDescriptor* field) {
1082   assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
1083          field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
1084   // For a field with an enum or message type, compute a name relative to the
1085   // path name of the message type containing this field.
1086   string package = field->file()->package();
1087   string containing_type = field->containing_type()->full_name() + ".";
1088   string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) ?
1089       field->enum_type()->full_name() : field->message_type()->full_name();
1090 
1091   // |prefix| is advanced as we find separators '.' past the common package
1092   // prefix that yield common prefixes in the containing type's name and this
1093   // type's name.
1094   int prefix = 0;
1095   for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
1096     if (type[i] != containing_type[i]) {
1097       break;
1098     }
1099     if (type[i] == '.' && i >= package.size()) {
1100       prefix = i + 1;
1101     }
1102   }
1103 
1104   return type.substr(prefix);
1105 }
1106 
JSExtensionsObjectName(const GeneratorOptions & options,const FileDescriptor * from_file,const Descriptor * desc)1107 string JSExtensionsObjectName(const GeneratorOptions& options,
1108                               const FileDescriptor* from_file,
1109                               const Descriptor* desc) {
1110   if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
1111     // TODO(haberman): fix this for the IMPORT_COMMONJS case.
1112     return "jspb.Message.messageSetExtensions";
1113   } else {
1114     return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
1115   }
1116 }
1117 
1118 static const int kMapKeyField = 1;
1119 static const int kMapValueField = 2;
1120 
MapFieldKey(const FieldDescriptor * field)1121 const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) {
1122   assert(field->is_map());
1123   return field->message_type()->FindFieldByNumber(kMapKeyField);
1124 }
1125 
MapFieldValue(const FieldDescriptor * field)1126 const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) {
1127   assert(field->is_map());
1128   return field->message_type()->FindFieldByNumber(kMapValueField);
1129 }
1130 
FieldDefinition(const GeneratorOptions & options,const FieldDescriptor * field)1131 string FieldDefinition(const GeneratorOptions& options,
1132                        const FieldDescriptor* field) {
1133   if (field->is_map()) {
1134     const FieldDescriptor* key_field = MapFieldKey(field);
1135     const FieldDescriptor* value_field = MapFieldValue(field);
1136     string key_type = ProtoTypeName(options, key_field);
1137     string value_type;
1138     if (value_field->type() == FieldDescriptor::TYPE_ENUM ||
1139         value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
1140       value_type = RelativeTypeName(value_field);
1141     } else {
1142       value_type = ProtoTypeName(options, value_field);
1143     }
1144     return StringPrintf("map<%s, %s> %s = %d;",
1145                         key_type.c_str(),
1146                         value_type.c_str(),
1147                         field->name().c_str(),
1148                         field->number());
1149   } else {
1150     string qualifier = field->is_repeated() ? "repeated" :
1151         (field->is_optional() ? "optional" : "required");
1152     string type, name;
1153     if (field->type() == FieldDescriptor::TYPE_ENUM ||
1154         field->type() == FieldDescriptor::TYPE_MESSAGE) {
1155       type = RelativeTypeName(field);
1156       name = field->name();
1157     } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
1158       type = "group";
1159       name = field->message_type()->name();
1160     } else {
1161       type = ProtoTypeName(options, field);
1162       name = field->name();
1163     }
1164     return StringPrintf("%s %s %s = %d;",
1165                         qualifier.c_str(),
1166                         type.c_str(),
1167                         name.c_str(),
1168                         field->number());
1169   }
1170 }
1171 
FieldComments(const FieldDescriptor * field,BytesMode bytes_mode)1172 string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
1173   string comments;
1174   if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
1175     comments +=
1176         " * Note that Boolean fields may be set to 0/1 when serialized from "
1177         "a Java server.\n"
1178         " * You should avoid comparisons like {@code val === true/false} in "
1179         "those cases.\n";
1180   }
1181   if (field->is_repeated()) {
1182     comments +=
1183         " * If you change this array by adding, removing or replacing "
1184         "elements, or if you\n"
1185         " * replace the array itself, then you must call the setter to "
1186         "update it.\n";
1187   }
1188   if (field->type() == FieldDescriptor::TYPE_BYTES && bytes_mode == BYTES_U8) {
1189     comments +=
1190         " * Note that Uint8Array is not supported on all browsers.\n"
1191         " * @see http://caniuse.com/Uint8Array\n";
1192   }
1193   return comments;
1194 }
1195 
ShouldGenerateExtension(const FieldDescriptor * field)1196 bool ShouldGenerateExtension(const FieldDescriptor* field) {
1197   return
1198       field->is_extension() &&
1199       !IgnoreField(field);
1200 }
1201 
HasExtensions(const Descriptor * desc)1202 bool HasExtensions(const Descriptor* desc) {
1203   for (int i = 0; i < desc->extension_count(); i++) {
1204     if (ShouldGenerateExtension(desc->extension(i))) {
1205       return true;
1206     }
1207   }
1208   for (int i = 0; i < desc->nested_type_count(); i++) {
1209     if (HasExtensions(desc->nested_type(i))) {
1210       return true;
1211     }
1212   }
1213   return false;
1214 }
1215 
HasExtensions(const FileDescriptor * file)1216 bool HasExtensions(const FileDescriptor* file) {
1217   for (int i = 0; i < file->extension_count(); i++) {
1218     if (ShouldGenerateExtension(file->extension(i))) {
1219       return true;
1220     }
1221   }
1222   for (int i = 0; i < file->message_type_count(); i++) {
1223     if (HasExtensions(file->message_type(i))) {
1224       return true;
1225     }
1226   }
1227   return false;
1228 }
1229 
IsExtendable(const Descriptor * desc)1230 bool IsExtendable(const Descriptor* desc) {
1231   return desc->extension_range_count() > 0;
1232 }
1233 
1234 // Returns the max index in the underlying data storage array beyond which the
1235 // extension object is used.
GetPivot(const Descriptor * desc)1236 string GetPivot(const Descriptor* desc) {
1237   static const int kDefaultPivot = (1 << 29);  // max field number (29 bits)
1238 
1239   // Find the max field number
1240   int max_field_number = 0;
1241   for (int i = 0; i < desc->field_count(); i++) {
1242     if (!IgnoreField(desc->field(i)) &&
1243         desc->field(i)->number() > max_field_number) {
1244       max_field_number = desc->field(i)->number();
1245     }
1246   }
1247 
1248   int pivot = -1;
1249   if (IsExtendable(desc)) {
1250     pivot = ((max_field_number + 1) < kDefaultPivot) ?
1251         (max_field_number + 1) : kDefaultPivot;
1252   }
1253 
1254   return SimpleItoa(pivot);
1255 }
1256 
1257 // Returns true for fields that represent "null" as distinct from the default
1258 // value. See http://go/proto3#heading=h.kozewqqcqhuz for more information.
HasFieldPresence(const FieldDescriptor * field)1259 bool HasFieldPresence(const FieldDescriptor* field) {
1260   if (field->is_repeated()) {
1261     return false;
1262   }
1263 
1264   return
1265       (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ||
1266       (field->containing_oneof() != NULL) ||
1267       (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3);
1268 }
1269 
1270 // For proto3 fields without presence, returns a string representing the default
1271 // value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more
1272 // information.
Proto3PrimitiveFieldDefault(const FieldDescriptor * field)1273 string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) {
1274   switch (field->cpp_type()) {
1275     case FieldDescriptor::CPPTYPE_INT32:
1276     case FieldDescriptor::CPPTYPE_INT64:
1277     case FieldDescriptor::CPPTYPE_UINT32:
1278     case FieldDescriptor::CPPTYPE_UINT64: {
1279       return "0";
1280     }
1281 
1282     case FieldDescriptor::CPPTYPE_ENUM:
1283     case FieldDescriptor::CPPTYPE_FLOAT:
1284     case FieldDescriptor::CPPTYPE_DOUBLE:
1285       return "0";
1286 
1287     case FieldDescriptor::CPPTYPE_BOOL:
1288       return "false";
1289 
1290     case FieldDescriptor::CPPTYPE_STRING:  // includes BYTES
1291       return "\"\"";
1292 
1293     default:
1294       // MESSAGE is handled separately.
1295       assert(false);
1296       return "";
1297   }
1298 }
1299 
1300 // We use this to implement the semantics that same file can be generated
1301 // multiple times, but the last one wins.  We never actually write the files,
1302 // but we keep a set of which descriptors were the final one for a given
1303 // filename.
1304 class FileDeduplicator {
1305  public:
FileDeduplicator(const GeneratorOptions & options)1306   explicit FileDeduplicator(const GeneratorOptions& options)
1307       : error_on_conflict_(options.error_on_name_conflict) {}
1308 
AddFile(const string & filename,const void * desc,string * error)1309   bool AddFile(const string& filename, const void* desc, string* error) {
1310     if (descs_by_filename_.find(filename) != descs_by_filename_.end()) {
1311       if (error_on_conflict_) {
1312         *error = "Name conflict: file name " + filename +
1313                  " would be generated by two descriptors";
1314         return false;
1315       }
1316       allowed_descs_.erase(descs_by_filename_[filename]);
1317     }
1318 
1319     descs_by_filename_[filename] = desc;
1320     allowed_descs_.insert(desc);
1321     return true;
1322   }
1323 
GetAllowedSet(set<const void * > * allowed_set)1324   void GetAllowedSet(set<const void*>* allowed_set) {
1325     *allowed_set = allowed_descs_;
1326   }
1327 
1328  private:
1329   bool error_on_conflict_;
1330   map<string, const void*> descs_by_filename_;
1331   set<const void*> allowed_descs_;
1332 };
1333 
DepthFirstSearch(const FileDescriptor * file,vector<const FileDescriptor * > * list,set<const FileDescriptor * > * seen)1334 void DepthFirstSearch(const FileDescriptor* file,
1335                       vector<const FileDescriptor*>* list,
1336                       set<const FileDescriptor*>* seen) {
1337   if (!seen->insert(file).second) {
1338     return;
1339   }
1340 
1341   // Add all dependencies.
1342   for (int i = 0; i < file->dependency_count(); i++) {
1343     DepthFirstSearch(file->dependency(i), list, seen);
1344   }
1345 
1346   // Add this file.
1347   list->push_back(file);
1348 }
1349 
1350 // A functor for the predicate to remove_if() below.  Returns true if a given
1351 // FileDescriptor is not in the given set.
1352 class NotInSet {
1353  public:
NotInSet(const set<const FileDescriptor * > & file_set)1354   explicit NotInSet(const set<const FileDescriptor*>& file_set)
1355       : file_set_(file_set) {}
1356 
operator ()(const FileDescriptor * file)1357   bool operator()(const FileDescriptor* file) {
1358     return file_set_.count(file) == 0;
1359   }
1360 
1361  private:
1362   const set<const FileDescriptor*>& file_set_;
1363 };
1364 
1365 // This function generates an ordering of the input FileDescriptors that matches
1366 // the logic of the old code generator.  The order is significant because two
1367 // different input files can generate the same output file, and the last one
1368 // needs to win.
GenerateJspbFileOrder(const vector<const FileDescriptor * > & input,vector<const FileDescriptor * > * ordered)1369 void GenerateJspbFileOrder(const vector<const FileDescriptor*>& input,
1370                            vector<const FileDescriptor*>* ordered) {
1371   // First generate an ordering of all reachable files (including dependencies)
1372   // with depth-first search.  This mimics the behavior of --include_imports,
1373   // which is what the old codegen used.
1374   ordered->clear();
1375   set<const FileDescriptor*> seen;
1376   set<const FileDescriptor*> input_set;
1377   for (int i = 0; i < input.size(); i++) {
1378     DepthFirstSearch(input[i], ordered, &seen);
1379     input_set.insert(input[i]);
1380   }
1381 
1382   // Now remove the entries that are not actually in our input list.
1383   ordered->erase(
1384       std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)),
1385       ordered->end());
1386 }
1387 
1388 // If we're generating code in file-per-type mode, avoid overwriting files
1389 // by choosing the last descriptor that writes each filename and permitting
1390 // only those to generate code.
1391 
GenerateJspbAllowedSet(const GeneratorOptions & options,const vector<const FileDescriptor * > & files,set<const void * > * allowed_set,string * error)1392 bool GenerateJspbAllowedSet(const GeneratorOptions& options,
1393                             const vector<const FileDescriptor*>& files,
1394                             set<const void*>* allowed_set,
1395                             string* error) {
1396   vector<const FileDescriptor*> files_ordered;
1397   GenerateJspbFileOrder(files, &files_ordered);
1398 
1399   // Choose the last descriptor for each filename.
1400   FileDeduplicator dedup(options);
1401   for (int i = 0; i < files_ordered.size(); i++) {
1402     for (int j = 0; j < files_ordered[i]->message_type_count(); j++) {
1403       const Descriptor* desc = files_ordered[i]->message_type(j);
1404       if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) {
1405         return false;
1406       }
1407     }
1408     for (int j = 0; j < files_ordered[i]->enum_type_count(); j++) {
1409       const EnumDescriptor* desc = files_ordered[i]->enum_type(j);
1410       if (!dedup.AddFile(GetEnumFileName(options, desc), desc, error)) {
1411         return false;
1412       }
1413     }
1414 
1415     // Pull out all free-floating extensions and generate files for those too.
1416     bool has_extension = false;
1417 
1418     for (int j = 0; j < files_ordered[i]->extension_count(); j++) {
1419       if (ShouldGenerateExtension(files_ordered[i]->extension(j))) {
1420         has_extension = true;
1421       }
1422     }
1423 
1424     if (has_extension) {
1425       if (!dedup.AddFile(GetExtensionFileName(options, files_ordered[i]),
1426                          files_ordered[i], error)) {
1427         return false;
1428       }
1429     }
1430   }
1431 
1432   dedup.GetAllowedSet(allowed_set);
1433 
1434   return true;
1435 }
1436 
1437 }  // anonymous namespace
1438 
GenerateHeader(const GeneratorOptions & options,io::Printer * printer) const1439 void Generator::GenerateHeader(const GeneratorOptions& options,
1440                                io::Printer* printer) const {
1441   printer->Print("/**\n"
1442                  " * @fileoverview\n"
1443                  " * @enhanceable\n"
1444                  " * @public\n"
1445                  " */\n"
1446                  "// GENERATED CODE -- DO NOT EDIT!\n"
1447                  "\n");
1448 }
1449 
FindProvidesForFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file,std::set<string> * provided) const1450 void Generator::FindProvidesForFile(const GeneratorOptions& options,
1451                                     io::Printer* printer,
1452                                     const FileDescriptor* file,
1453                                     std::set<string>* provided) const {
1454   for (int i = 0; i < file->message_type_count(); i++) {
1455     FindProvidesForMessage(options, printer, file->message_type(i), provided);
1456   }
1457   for (int i = 0; i < file->enum_type_count(); i++) {
1458     FindProvidesForEnum(options, printer, file->enum_type(i), provided);
1459   }
1460 }
1461 
FindProvides(const GeneratorOptions & options,io::Printer * printer,const vector<const FileDescriptor * > & files,std::set<string> * provided) const1462 void Generator::FindProvides(const GeneratorOptions& options,
1463                              io::Printer* printer,
1464                              const vector<const FileDescriptor*>& files,
1465                              std::set<string>* provided) const {
1466   for (int i = 0; i < files.size(); i++) {
1467     FindProvidesForFile(options, printer, files[i], provided);
1468   }
1469 
1470   printer->Print("\n");
1471 }
1472 
FindProvidesForMessage(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<string> * provided) const1473 void Generator::FindProvidesForMessage(
1474     const GeneratorOptions& options,
1475     io::Printer* printer,
1476     const Descriptor* desc,
1477     std::set<string>* provided) const {
1478   if (IgnoreMessage(options, desc)) {
1479     return;
1480   }
1481 
1482   string name = GetPath(options, desc);
1483   provided->insert(name);
1484 
1485   for (int i = 0; i < desc->enum_type_count(); i++) {
1486     FindProvidesForEnum(options, printer, desc->enum_type(i),
1487                         provided);
1488   }
1489   for (int i = 0; i < desc->nested_type_count(); i++) {
1490     FindProvidesForMessage(options, printer, desc->nested_type(i),
1491                            provided);
1492   }
1493 }
1494 
FindProvidesForEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc,std::set<string> * provided) const1495 void Generator::FindProvidesForEnum(const GeneratorOptions& options,
1496                                     io::Printer* printer,
1497                                     const EnumDescriptor* enumdesc,
1498                                     std::set<string>* provided) const {
1499   string name = GetPath(options, enumdesc);
1500   provided->insert(name);
1501 }
1502 
FindProvidesForFields(const GeneratorOptions & options,io::Printer * printer,const vector<const FieldDescriptor * > & fields,std::set<string> * provided) const1503 void Generator::FindProvidesForFields(
1504     const GeneratorOptions& options,
1505     io::Printer* printer,
1506     const vector<const FieldDescriptor*>& fields,
1507     std::set<string>* provided) const {
1508   for (int i = 0; i < fields.size(); i++) {
1509     const FieldDescriptor* field = fields[i];
1510 
1511     if (IgnoreField(field)) {
1512       continue;
1513     }
1514 
1515     string name =
1516         GetPath(options, field->file()) + "." +
1517         JSObjectFieldName(options, field);
1518     provided->insert(name);
1519   }
1520 }
1521 
GenerateProvides(const GeneratorOptions & options,io::Printer * printer,std::set<string> * provided) const1522 void Generator::GenerateProvides(const GeneratorOptions& options,
1523                                  io::Printer* printer,
1524                                  std::set<string>* provided) const {
1525   for (std::set<string>::iterator it = provided->begin();
1526        it != provided->end(); ++it) {
1527     printer->Print("goog.provide('$name$');\n",
1528                    "name", *it);
1529   }
1530 }
1531 
GenerateRequiresForMessage(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc,std::set<string> * provided) const1532 void Generator::GenerateRequiresForMessage(const GeneratorOptions& options,
1533                                            io::Printer* printer,
1534                                            const Descriptor* desc,
1535                                            std::set<string>* provided) const {
1536   std::set<string> required;
1537   std::set<string> forwards;
1538   bool have_message = false;
1539   FindRequiresForMessage(options, desc,
1540                          &required, &forwards, &have_message);
1541 
1542   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1543                        /* require_jspb = */ have_message,
1544                        /* require_extension = */ HasExtensions(desc));
1545 }
1546 
GenerateRequiresForLibrary(const GeneratorOptions & options,io::Printer * printer,const vector<const FileDescriptor * > & files,std::set<string> * provided) const1547 void Generator::GenerateRequiresForLibrary(
1548     const GeneratorOptions& options, io::Printer* printer,
1549     const vector<const FileDescriptor*>& files,
1550     std::set<string>* provided) const {
1551   GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE);
1552   // For Closure imports we need to import every message type individually.
1553   std::set<string> required;
1554   std::set<string> forwards;
1555   bool have_extensions = false;
1556   bool have_message = false;
1557 
1558   for (int i = 0; i < files.size(); i++) {
1559     for (int j = 0; j < files[i]->message_type_count(); j++) {
1560       const Descriptor* desc = files[i]->message_type(j);
1561       if (!IgnoreMessage(options, desc)) {
1562         FindRequiresForMessage(options, desc, &required, &forwards,
1563                                &have_message);
1564       }
1565     }
1566 
1567     if (!have_extensions && HasExtensions(files[i])) {
1568       have_extensions = true;
1569     }
1570 
1571     for (int j = 0; j < files[i]->extension_count(); j++) {
1572       const FieldDescriptor* extension = files[i]->extension(j);
1573       if (IgnoreField(extension)) {
1574         continue;
1575       }
1576       if (extension->containing_type()->full_name() !=
1577         "google.protobuf.bridge.MessageSet") {
1578         required.insert(GetPath(options, extension->containing_type()));
1579       }
1580       FindRequiresForField(options, extension, &required, &forwards);
1581       have_extensions = true;
1582     }
1583   }
1584 
1585   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1586                        /* require_jspb = */ have_message,
1587                        /* require_extension = */ have_extensions);
1588 }
1589 
GenerateRequiresForExtensions(const GeneratorOptions & options,io::Printer * printer,const vector<const FieldDescriptor * > & fields,std::set<string> * provided) const1590 void Generator::GenerateRequiresForExtensions(
1591     const GeneratorOptions& options, io::Printer* printer,
1592     const vector<const FieldDescriptor*>& fields,
1593     std::set<string>* provided) const {
1594   std::set<string> required;
1595   std::set<string> forwards;
1596   for (int i = 0; i < fields.size(); i++) {
1597     const FieldDescriptor* field = fields[i];
1598     if (IgnoreField(field)) {
1599       continue;
1600     }
1601     FindRequiresForExtension(options, field, &required, &forwards);
1602   }
1603 
1604   GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1605                        /* require_jspb = */ false,
1606                        /* require_extension = */ fields.size() > 0);
1607 }
1608 
GenerateRequiresImpl(const GeneratorOptions & options,io::Printer * printer,std::set<string> * required,std::set<string> * forwards,std::set<string> * provided,bool require_jspb,bool require_extension) const1609 void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
1610                                      io::Printer* printer,
1611                                      std::set<string>* required,
1612                                      std::set<string>* forwards,
1613                                      std::set<string>* provided,
1614                                      bool require_jspb,
1615                                      bool require_extension) const {
1616   if (require_jspb) {
1617     printer->Print(
1618         "goog.require('jspb.Message');\n");
1619     if (options.binary) {
1620       printer->Print(
1621           "goog.require('jspb.BinaryReader');\n"
1622           "goog.require('jspb.BinaryWriter');\n");
1623     }
1624   }
1625   if (require_extension) {
1626     printer->Print(
1627         "goog.require('jspb.ExtensionFieldInfo');\n");
1628   }
1629 
1630   std::set<string>::iterator it;
1631   for (it = required->begin(); it != required->end(); ++it) {
1632     if (provided->find(*it) != provided->end()) {
1633       continue;
1634     }
1635     printer->Print("goog.require('$name$');\n",
1636                    "name", *it);
1637   }
1638 
1639   printer->Print("\n");
1640 
1641   for (it = forwards->begin(); it != forwards->end(); ++it) {
1642     if (provided->find(*it) != provided->end()) {
1643       continue;
1644     }
1645     printer->Print("goog.forwardDeclare('$name$');\n",
1646                    "name", *it);
1647   }
1648 }
1649 
NamespaceOnly(const Descriptor * desc)1650 bool NamespaceOnly(const Descriptor* desc) {
1651   return false;
1652 }
1653 
FindRequiresForMessage(const GeneratorOptions & options,const Descriptor * desc,std::set<string> * required,std::set<string> * forwards,bool * have_message) const1654 void Generator::FindRequiresForMessage(
1655     const GeneratorOptions& options,
1656     const Descriptor* desc,
1657     std::set<string>* required,
1658     std::set<string>* forwards,
1659     bool* have_message) const {
1660 
1661 
1662   if (!NamespaceOnly(desc)) {
1663     *have_message = true;
1664     for (int i = 0; i < desc->field_count(); i++) {
1665       const FieldDescriptor* field = desc->field(i);
1666       if (IgnoreField(field)) {
1667         continue;
1668       }
1669       FindRequiresForField(options, field, required, forwards);
1670     }
1671   }
1672 
1673   for (int i = 0; i < desc->extension_count(); i++) {
1674     const FieldDescriptor* field = desc->extension(i);
1675     if (IgnoreField(field)) {
1676       continue;
1677     }
1678     FindRequiresForExtension(options, field, required, forwards);
1679   }
1680 
1681   for (int i = 0; i < desc->nested_type_count(); i++) {
1682     FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
1683                            have_message);
1684   }
1685 }
1686 
FindRequiresForField(const GeneratorOptions & options,const FieldDescriptor * field,std::set<string> * required,std::set<string> * forwards) const1687 void Generator::FindRequiresForField(const GeneratorOptions& options,
1688                                      const FieldDescriptor* field,
1689                                      std::set<string>* required,
1690                                      std::set<string>* forwards) const {
1691     if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1692         // N.B.: file-level extensions with enum type do *not* create
1693         // dependencies, as per original codegen.
1694         !(field->is_extension() && field->extension_scope() == NULL)) {
1695       if (options.add_require_for_enums) {
1696         required->insert(GetPath(options, field->enum_type()));
1697       } else {
1698         forwards->insert(GetPath(options, field->enum_type()));
1699       }
1700     } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1701       if (!IgnoreMessage(options, field->message_type())) {
1702         required->insert(GetPath(options, field->message_type()));
1703       }
1704     }
1705 }
1706 
FindRequiresForExtension(const GeneratorOptions & options,const FieldDescriptor * field,std::set<string> * required,std::set<string> * forwards) const1707 void Generator::FindRequiresForExtension(const GeneratorOptions& options,
1708                                          const FieldDescriptor* field,
1709                                          std::set<string>* required,
1710                                          std::set<string>* forwards) const {
1711     if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
1712       required->insert(GetPath(options, field->containing_type()));
1713     }
1714     FindRequiresForField(options, field, required, forwards);
1715 }
1716 
GenerateTestOnly(const GeneratorOptions & options,io::Printer * printer) const1717 void Generator::GenerateTestOnly(const GeneratorOptions& options,
1718                                  io::Printer* printer) const {
1719   if (options.testonly) {
1720     printer->Print("goog.setTestOnly();\n\n");
1721   }
1722   printer->Print("\n");
1723 }
1724 
GenerateClassesAndEnums(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const1725 void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
1726                                         io::Printer* printer,
1727                                         const FileDescriptor* file) const {
1728   for (int i = 0; i < file->message_type_count(); i++) {
1729     GenerateClass(options, printer, file->message_type(i));
1730   }
1731   for (int i = 0; i < file->enum_type_count(); i++) {
1732     GenerateEnum(options, printer, file->enum_type(i));
1733   }
1734 }
1735 
GenerateClass(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1736 void Generator::GenerateClass(const GeneratorOptions& options,
1737                               io::Printer* printer,
1738                               const Descriptor* desc) const {
1739   if (IgnoreMessage(options, desc)) {
1740     return;
1741   }
1742 
1743   if (!NamespaceOnly(desc)) {
1744     printer->Print("\n");
1745     GenerateClassConstructor(options, printer, desc);
1746     GenerateClassFieldInfo(options, printer, desc);
1747 
1748 
1749     GenerateClassToObject(options, printer, desc);
1750     if (options.binary) {
1751       // These must come *before* the extension-field info generation in
1752       // GenerateClassRegistration so that references to the binary
1753       // serialization/deserialization functions may be placed in the extension
1754       // objects.
1755       GenerateClassDeserializeBinary(options, printer, desc);
1756       GenerateClassSerializeBinary(options, printer, desc);
1757     }
1758     GenerateClassClone(options, printer, desc);
1759     GenerateClassRegistration(options, printer, desc);
1760     GenerateClassFields(options, printer, desc);
1761     if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
1762       GenerateClassExtensionFieldInfo(options, printer, desc);
1763     }
1764 
1765     if (options.import_style != GeneratorOptions:: IMPORT_CLOSURE) {
1766       for (int i = 0; i < desc->extension_count(); i++) {
1767         GenerateExtension(options, printer, desc->extension(i));
1768       }
1769     }
1770   }
1771 
1772   // Recurse on nested types.
1773   for (int i = 0; i < desc->enum_type_count(); i++) {
1774     GenerateEnum(options, printer, desc->enum_type(i));
1775   }
1776   for (int i = 0; i < desc->nested_type_count(); i++) {
1777     GenerateClass(options, printer, desc->nested_type(i));
1778   }
1779 }
1780 
GenerateClassConstructor(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1781 void Generator::GenerateClassConstructor(const GeneratorOptions& options,
1782                                          io::Printer* printer,
1783                                          const Descriptor* desc) const {
1784   printer->Print(
1785       "/**\n"
1786       " * Generated by JsPbCodeGenerator.\n"
1787       " * @param {Array=} opt_data Optional initial data array, typically "
1788       "from a\n"
1789       " * server response, or constructed directly in Javascript. The array "
1790       "is used\n"
1791       " * in place and becomes part of the constructed object. It is not "
1792       "cloned.\n"
1793       " * If no data is provided, the constructed object will be empty, but "
1794       "still\n"
1795       " * valid.\n"
1796       " * @extends {jspb.Message}\n"
1797       " * @constructor\n"
1798       " */\n"
1799       "$classname$ = function(opt_data) {\n",
1800       "classname", GetPath(options, desc));
1801   string message_id = GetMessageId(desc);
1802   printer->Print(
1803       "  jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
1804       "$rptfields$, $oneoffields$);\n",
1805       "messageId", !message_id.empty() ?
1806                    ("'" + message_id + "'") :
1807                    (IsResponse(desc) ? "''" : "0"),
1808       "pivot", GetPivot(desc),
1809       "rptfields", RepeatedFieldsArrayName(options, desc),
1810       "oneoffields", OneofFieldsArrayName(options, desc));
1811   printer->Print(
1812       "};\n"
1813       "goog.inherits($classname$, jspb.Message);\n"
1814       "if (goog.DEBUG && !COMPILED) {\n"
1815       "  $classname$.displayName = '$classname$';\n"
1816       "}\n",
1817       "classname", GetPath(options, desc));
1818 }
1819 
GenerateClassFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1820 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
1821                                        io::Printer* printer,
1822                                        const Descriptor* desc) const {
1823   if (HasRepeatedFields(desc)) {
1824     printer->Print(
1825         "/**\n"
1826         " * List of repeated fields within this message type.\n"
1827         " * @private {!Array<number>}\n"
1828         " * @const\n"
1829         " */\n"
1830         "$classname$$rptfieldarray$ = $rptfields$;\n"
1831         "\n",
1832         "classname", GetPath(options, desc),
1833         "rptfieldarray", kRepeatedFieldArrayName,
1834         "rptfields", RepeatedFieldNumberList(desc));
1835   }
1836 
1837   if (HasOneofFields(desc)) {
1838     printer->Print(
1839         "/**\n"
1840         " * Oneof group definitions for this message. Each group defines the "
1841         "field\n"
1842         " * numbers belonging to that group. When of these fields' value is "
1843         "set, all\n"
1844         " * other fields in the group are cleared. During deserialization, if "
1845         "multiple\n"
1846         " * fields are encountered for a group, only the last value seen will "
1847         "be kept.\n"
1848         " * @private {!Array<!Array<number>>}\n"
1849         " * @const\n"
1850         " */\n"
1851         "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
1852         "\n",
1853         "classname", GetPath(options, desc),
1854         "oneofgrouparray", kOneofGroupArrayName,
1855         "oneofgroups", OneofGroupList(desc));
1856 
1857     for (int i = 0; i < desc->oneof_decl_count(); i++) {
1858       if (IgnoreOneof(desc->oneof_decl(i))) {
1859         continue;
1860       }
1861       GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
1862     }
1863   }
1864 }
1865 
GenerateClassXid(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1866 void Generator::GenerateClassXid(const GeneratorOptions& options,
1867                                  io::Printer* printer,
1868                                  const Descriptor* desc) const {
1869   printer->Print(
1870       "\n"
1871       "\n"
1872       "$class$.prototype.messageXid = xid('$class$');\n",
1873       "class", GetPath(options, desc));
1874 }
1875 
GenerateOneofCaseDefinition(const GeneratorOptions & options,io::Printer * printer,const OneofDescriptor * oneof) const1876 void Generator::GenerateOneofCaseDefinition(
1877     const GeneratorOptions& options,
1878     io::Printer* printer,
1879     const OneofDescriptor* oneof) const {
1880   printer->Print(
1881       "/**\n"
1882       " * @enum {number}\n"
1883       " */\n"
1884       "$classname$.$oneof$Case = {\n"
1885       "  $upcase$_NOT_SET: 0",
1886       "classname", GetPath(options, oneof->containing_type()),
1887       "oneof", JSOneofName(oneof),
1888       "upcase", ToEnumCase(oneof->name()));
1889 
1890   for (int i = 0; i < oneof->field_count(); i++) {
1891     if (IgnoreField(oneof->field(i))) {
1892       continue;
1893     }
1894 
1895     printer->Print(
1896         ",\n"
1897         "  $upcase$: $number$",
1898         "upcase", ToEnumCase(oneof->field(i)->name()),
1899         "number", JSFieldIndex(oneof->field(i)));
1900   }
1901 
1902   printer->Print(
1903       "\n"
1904       "};\n"
1905       "\n"
1906       "/**\n"
1907       " * @return {$class$.$oneof$Case}\n"
1908       " */\n"
1909       "$class$.prototype.get$oneof$Case = function() {\n"
1910       "  return /** @type {$class$.$oneof$Case} */(jspb.Message."
1911       "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
1912       "};\n"
1913       "\n",
1914       "class", GetPath(options, oneof->containing_type()),
1915       "oneof", JSOneofName(oneof),
1916       "oneofindex", JSOneofIndex(oneof));
1917 }
1918 
GenerateClassToObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const1919 void Generator::GenerateClassToObject(const GeneratorOptions& options,
1920                                       io::Printer* printer,
1921                                       const Descriptor* desc) const {
1922   printer->Print(
1923       "\n"
1924       "\n"
1925       "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
1926       "/**\n"
1927       " * Creates an object representation of this proto suitable for use in "
1928       "Soy templates.\n"
1929       " * Field names that are reserved in JavaScript and will be renamed to "
1930       "pb_name.\n"
1931       " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
1932       " * For the list of reserved names please see:\n"
1933       " *     com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.\n"
1934       " * @param {boolean=} opt_includeInstance Whether to include the JSPB "
1935       "instance\n"
1936       " *     for transitional soy proto support: http://goto/soy-param-"
1937       "migration\n"
1938       " * @return {!Object}\n"
1939       " */\n"
1940       "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
1941       "  return $classname$.toObject(opt_includeInstance, this);\n"
1942       "};\n"
1943       "\n"
1944       "\n"
1945       "/**\n"
1946       " * Static version of the {@see toObject} method.\n"
1947       " * @param {boolean|undefined} includeInstance Whether to include the "
1948       "JSPB\n"
1949       " *     instance for transitional soy proto support:\n"
1950       " *     http://goto/soy-param-migration\n"
1951       " * @param {!$classname$} msg The msg instance to transform.\n"
1952       " * @return {!Object}\n"
1953       " */\n"
1954       "$classname$.toObject = function(includeInstance, msg) {\n"
1955       "  var f, obj = {",
1956       "classname", GetPath(options, desc));
1957 
1958   bool first = true;
1959   for (int i = 0; i < desc->field_count(); i++) {
1960     const FieldDescriptor* field = desc->field(i);
1961     if (IgnoreField(field)) {
1962       continue;
1963     }
1964 
1965     if (!first) {
1966       printer->Print(",\n    ");
1967     } else {
1968       printer->Print("\n    ");
1969       first = false;
1970     }
1971 
1972     GenerateClassFieldToObject(options, printer, field);
1973   }
1974 
1975   if (!first) {
1976     printer->Print("\n  };\n\n");
1977   } else {
1978     printer->Print("\n\n  };\n\n");
1979   }
1980 
1981   if (IsExtendable(desc)) {
1982     printer->Print(
1983         "  jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
1984         "obj,\n"
1985         "      $extObject$, $class$.prototype.getExtension,\n"
1986         "      includeInstance);\n",
1987         "extObject", JSExtensionsObjectName(options, desc->file(), desc),
1988         "class", GetPath(options, desc));
1989   }
1990 
1991   printer->Print(
1992       "  if (includeInstance) {\n"
1993       "    obj.$$jspbMessageInstance = msg;\n"
1994       "  }\n"
1995       "  return obj;\n"
1996       "};\n"
1997       "}\n"
1998       "\n"
1999       "\n",
2000       "classname", GetPath(options, desc));
2001 }
2002 
GenerateClassFieldToObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2003 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
2004                                            io::Printer* printer,
2005                                            const FieldDescriptor* field) const {
2006   printer->Print("$fieldname$: ",
2007                  "fieldname", JSObjectFieldName(options, field));
2008 
2009   if (field->is_map()) {
2010     printer->Print("(f = msg.get$name$(true)) ? f.toArray() : []",
2011                    "name", JSGetterName(options, field));
2012   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2013     // Message field.
2014     if (field->is_repeated()) {
2015       {
2016         printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
2017                        "    $type$.toObject, includeInstance)",
2018                        "getter", JSGetterName(options, field),
2019                        "type", SubmessageTypeRef(options, field));
2020       }
2021     } else {
2022       printer->Print("(f = msg.get$getter$()) && "
2023                      "$type$.toObject(includeInstance, f)",
2024                      "getter", JSGetterName(options, field),
2025                      "type", SubmessageTypeRef(options, field));
2026     }
2027   } else {
2028     // Simple field (singular or repeated).
2029     if ((!HasFieldPresence(field) && !field->is_repeated()) ||
2030         field->type() == FieldDescriptor::TYPE_BYTES) {
2031       // Delegate to the generated get<field>() method in order not to duplicate
2032       // the proto3-field-default-value or byte-coercion logic here.
2033       printer->Print("msg.get$getter$()",
2034                      "getter", JSGetterName(options, field, BYTES_B64));
2035     } else {
2036       if (field->has_default_value()) {
2037         printer->Print("!msg.has$name$() ? $defaultValue$ : ",
2038                        "name", JSGetterName(options, field),
2039                        "defaultValue", JSFieldDefault(field));
2040       }
2041       if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2042           field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) {
2043         if (field->is_repeated()) {
2044           printer->Print("jspb.Message.getRepeatedFloatingPointField("
2045                          "msg, $index$)",
2046                          "index", JSFieldIndex(field));
2047         } else if (field->is_optional() && !field->has_default_value()) {
2048           printer->Print("jspb.Message.getOptionalFloatingPointField("
2049                          "msg, $index$)",
2050                          "index", JSFieldIndex(field));
2051         } else {
2052           // Convert "NaN" to NaN.
2053           printer->Print("+jspb.Message.getField(msg, $index$)",
2054                          "index", JSFieldIndex(field));
2055         }
2056       } else {
2057         printer->Print("jspb.Message.getField(msg, $index$)",
2058                        "index", JSFieldIndex(field));
2059       }
2060     }
2061   }
2062 }
2063 
GenerateClassFromObject(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2064 void Generator::GenerateClassFromObject(const GeneratorOptions& options,
2065                                         io::Printer* printer,
2066                                         const Descriptor* desc) const {
2067   printer->Print(
2068       "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
2069       "/**\n"
2070       " * Loads data from an object into a new instance of this proto.\n"
2071       " * @param {!Object} obj The object representation of this proto to\n"
2072       " *     load the data from.\n"
2073       " * @return {!$classname$}\n"
2074       " */\n"
2075       "$classname$.fromObject = function(obj) {\n"
2076       "  var f, msg = new $classname$();\n",
2077       "classname", GetPath(options, desc));
2078 
2079   for (int i = 0; i < desc->field_count(); i++) {
2080     const FieldDescriptor* field = desc->field(i);
2081     GenerateClassFieldFromObject(options, printer, field);
2082   }
2083 
2084   printer->Print(
2085       "  return msg;\n"
2086       "};\n"
2087       "}\n");
2088 }
2089 
GenerateClassFieldFromObject(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2090 void Generator::GenerateClassFieldFromObject(
2091     const GeneratorOptions& options,
2092     io::Printer* printer,
2093     const FieldDescriptor* field) const {
2094 
2095   if (field->is_map()) {
2096     // `msg` is a newly-constructed message object that has not yet built any
2097     // map containers wrapping underlying arrays, so we can simply directly set
2098     // the array here without fear of a stale wrapper.
2099     printer->Print(
2100         "  goog.isDef(obj.$name$) && "
2101         "jspb.Message.setField(msg, $index$, obj.$name$);\n",
2102         "name", JSObjectFieldName(options, field),
2103         "index", JSFieldIndex(field));
2104   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2105     // Message field (singular or repeated)
2106     if (field->is_repeated()) {
2107       {
2108         printer->Print(
2109             "  goog.isDef(obj.$name$) && "
2110             "jspb.Message.setRepeatedWrapperField(\n"
2111             "      msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
2112             "        return $fieldclass$.fromObject(i);\n"
2113             "      }));\n",
2114             "name", JSObjectFieldName(options, field),
2115             "index", JSFieldIndex(field),
2116             "fieldclass", SubmessageTypeRef(options, field));
2117       }
2118     } else {
2119       printer->Print(
2120           "  goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
2121           "      msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
2122           "name", JSObjectFieldName(options, field),
2123           "index", JSFieldIndex(field),
2124           "fieldclass", SubmessageTypeRef(options, field));
2125     }
2126   } else {
2127     // Simple (primitive) field.
2128     printer->Print(
2129         "  goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
2130         "obj.$name$);\n",
2131         "name", JSObjectFieldName(options, field),
2132         "index", JSFieldIndex(field));
2133   }
2134 }
2135 
GenerateClassClone(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2136 void Generator::GenerateClassClone(const GeneratorOptions& options,
2137                                    io::Printer* printer,
2138                                    const Descriptor* desc) const {
2139   printer->Print(
2140       "/**\n"
2141       " * Creates a deep clone of this proto. No data is shared with the "
2142       "original.\n"
2143       " * @return {!$name$} The clone.\n"
2144       " */\n"
2145       "$name$.prototype.cloneMessage = function() {\n"
2146       "  return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n"
2147       "};\n\n\n",
2148       "name", GetPath(options, desc));
2149 }
2150 
GenerateClassRegistration(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2151 void Generator::GenerateClassRegistration(const GeneratorOptions& options,
2152                                           io::Printer* printer,
2153                                           const Descriptor* desc) const {
2154   // Register any extensions defined inside this message type.
2155   for (int i = 0; i < desc->extension_count(); i++) {
2156     const FieldDescriptor* extension = desc->extension(i);
2157     if (ShouldGenerateExtension(extension)) {
2158       GenerateExtension(options, printer, extension);
2159     }
2160   }
2161 
2162 }
2163 
GenerateClassFields(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2164 void Generator::GenerateClassFields(const GeneratorOptions& options,
2165                                     io::Printer* printer,
2166                                     const Descriptor* desc) const {
2167   for (int i = 0; i < desc->field_count(); i++) {
2168     if (!IgnoreField(desc->field(i))) {
2169       GenerateClassField(options, printer, desc->field(i));
2170     }
2171   }
2172 }
2173 
GenerateBytesWrapper(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field,BytesMode bytes_mode)2174 void GenerateBytesWrapper(const GeneratorOptions& options,
2175                           io::Printer* printer,
2176                           const FieldDescriptor* field,
2177                           BytesMode bytes_mode) {
2178   string type =
2179       JSFieldTypeAnnotation(options, field,
2180                             /* force_optional = */ false,
2181                             /* force_present = */ !HasFieldPresence(field),
2182                             /* singular_if_not_packed = */ false,
2183                             bytes_mode);
2184   printer->Print(
2185       "/**\n"
2186       " * $fielddef$\n"
2187       "$comment$"
2188       " * This is a type-conversion wrapper around `get$defname$()`\n"
2189       " * @return {$type$}\n"
2190       " */\n"
2191       "$class$.prototype.get$name$ = function() {\n"
2192       "  return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
2193       "      this.get$defname$()));\n"
2194       "};\n"
2195       "\n"
2196       "\n",
2197       "fielddef", FieldDefinition(options, field),
2198       "comment", FieldComments(field, bytes_mode),
2199       "type", type,
2200       "class", GetPath(options, field->containing_type()),
2201       "name", JSGetterName(options, field, bytes_mode),
2202       "list", field->is_repeated() ? "List" : "",
2203       "suffix", JSByteGetterSuffix(bytes_mode),
2204       "defname", JSGetterName(options, field, BYTES_DEFAULT));
2205 }
2206 
2207 
GenerateClassField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2208 void Generator::GenerateClassField(const GeneratorOptions& options,
2209                                    io::Printer* printer,
2210                                    const FieldDescriptor* field) const {
2211   if (field->is_map()) {
2212     const FieldDescriptor* key_field = MapFieldKey(field);
2213     const FieldDescriptor* value_field = MapFieldValue(field);
2214     // Map field: special handling to instantiate the map object on demand.
2215     string key_type =
2216         JSFieldTypeAnnotation(
2217             options, key_field,
2218             /* force_optional = */ false,
2219             /* force_present = */ true,
2220             /* singular_if_not_packed = */ false);
2221     string value_type =
2222         JSFieldTypeAnnotation(
2223             options, value_field,
2224             /* force_optional = */ false,
2225             /* force_present = */ true,
2226             /* singular_if_not_packed = */ false);
2227 
2228     printer->Print(
2229         "/**\n"
2230         " * $fielddef$\n"
2231         " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
2232         " * empty, instead returning `undefined`\n"
2233         " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
2234         " */\n",
2235         "fielddef", FieldDefinition(options, field),
2236         "keytype", key_type,
2237         "valuetype", value_type);
2238     printer->Print(
2239         "$class$.prototype.get$name$ = function(opt_noLazyCreate) {\n"
2240         "  return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
2241         "class", GetPath(options, field->containing_type()),
2242         "name", JSGetterName(options, field),
2243         "keytype", key_type,
2244         "valuetype", value_type);
2245     printer->Print(
2246         "      jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
2247         "index", JSFieldIndex(field));
2248 
2249     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2250       printer->Print(",\n"
2251           "      $messageType$",
2252             "messageType", GetPath(options, value_field->message_type()));
2253     } else if (options.binary) {
2254       printer->Print(",\n"
2255           "      null");
2256     }
2257 
2258     printer->Print(
2259         "));\n");
2260 
2261     printer->Print(
2262         "};\n"
2263         "\n"
2264         "\n");
2265   } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2266     // Message field: special handling in order to wrap the underlying data
2267     // array with a message object.
2268 
2269     printer->Print(
2270         "/**\n"
2271         " * $fielddef$\n"
2272         "$comment$"
2273         " * @return {$type$}\n"
2274         " */\n",
2275         "fielddef", FieldDefinition(options, field),
2276         "comment", FieldComments(field, BYTES_DEFAULT),
2277         "type", JSFieldTypeAnnotation(options, field,
2278                                       /* force_optional = */ false,
2279                                       /* force_present = */ false,
2280                                       /* singular_if_not_packed = */ false));
2281     printer->Print(
2282         "$class$.prototype.get$name$ = function() {\n"
2283         "  return /** @type{$type$} */ (\n"
2284         "    jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
2285         "$index$$required$));\n"
2286         "};\n"
2287         "\n"
2288         "\n",
2289         "class", GetPath(options, field->containing_type()),
2290         "name", JSGetterName(options, field),
2291         "type", JSFieldTypeAnnotation(options, field,
2292                                       /* force_optional = */ false,
2293                                       /* force_present = */ false,
2294                                       /* singular_if_not_packed = */ false),
2295         "rpt", (field->is_repeated() ? "Repeated" : ""),
2296         "index", JSFieldIndex(field),
2297         "wrapperclass", SubmessageTypeRef(options, field),
2298         "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
2299                      ", 1" : ""));
2300     printer->Print(
2301         "/** @param {$optionaltype$} value $returndoc$ */\n"
2302         "$class$.prototype.set$name$ = function(value) {\n"
2303         "  jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
2304         "optionaltype",
2305         JSFieldTypeAnnotation(options, field,
2306                               /* force_optional = */ true,
2307                               /* force_present = */ false,
2308                               /* singular_if_not_packed = */ false),
2309         "returndoc", JSReturnDoc(options, field),
2310         "class", GetPath(options, field->containing_type()),
2311         "name", JSGetterName(options, field),
2312         "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2313         "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2314 
2315     printer->Print(
2316         "this, $index$$oneofgroup$, value);$returnvalue$\n"
2317         "};\n"
2318         "\n"
2319         "\n",
2320         "index", JSFieldIndex(field),
2321         "oneofgroup", (field->containing_oneof() ?
2322                        (", " + JSOneofArray(options, field)) : ""),
2323         "returnvalue", JSReturnClause(field));
2324 
2325     printer->Print(
2326         "$class$.prototype.clear$name$ = function() {\n"
2327         "  this.set$name$($clearedvalue$);$returnvalue$\n"
2328         "};\n"
2329         "\n"
2330         "\n",
2331         "class", GetPath(options, field->containing_type()),
2332         "name", JSGetterName(options, field),
2333         "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2334         "returnvalue", JSReturnClause(field));
2335 
2336   } else {
2337     bool untyped =
2338         false;
2339 
2340     // Simple (primitive) field, either singular or repeated.
2341 
2342     // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type;
2343     // at this point we "lie" to non-binary users and tell the the return
2344     // type is always base64 string, pending a LSC to migrate to typed getters.
2345     BytesMode bytes_mode =
2346         field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ?
2347             BYTES_B64 : BYTES_DEFAULT;
2348     string typed_annotation =
2349         JSFieldTypeAnnotation(options, field,
2350                               /* force_optional = */ false,
2351                               /* force_present = */ !HasFieldPresence(field),
2352                               /* singular_if_not_packed = */ false,
2353                               /* bytes_mode = */ bytes_mode);
2354     if (untyped) {
2355       printer->Print(
2356           "/**\n"
2357           " * @return {?} Raw field, untyped.\n"
2358           " */\n");
2359     } else {
2360       printer->Print(
2361           "/**\n"
2362           " * $fielddef$\n"
2363           "$comment$"
2364           " * @return {$type$}\n"
2365           " */\n",
2366           "fielddef", FieldDefinition(options, field),
2367           "comment", FieldComments(field, bytes_mode),
2368           "type", typed_annotation);
2369     }
2370 
2371     printer->Print(
2372         "$class$.prototype.get$name$ = function() {\n",
2373         "class", GetPath(options, field->containing_type()),
2374         "name", JSGetterName(options, field));
2375 
2376     if (untyped) {
2377       printer->Print(
2378           "  return ");
2379     } else {
2380       printer->Print(
2381           "  return /** @type {$type$} */ (",
2382           "type", typed_annotation);
2383     }
2384 
2385     // For proto3 fields without presence, use special getters that will return
2386     // defaults when the field is unset, possibly constructing a value if
2387     // required.
2388     if (!HasFieldPresence(field) && !field->is_repeated()) {
2389       printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)",
2390                      "index", JSFieldIndex(field),
2391                      "default", Proto3PrimitiveFieldDefault(field));
2392     } else {
2393       if (!field->is_repeated()) {
2394         printer->Print("!this.has$name$() ? $defaultValue$ : ",
2395                        "name", JSGetterName(options, field),
2396                        "defaultValue", JSFieldDefault(field));
2397       }
2398       if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2399           field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) {
2400         if (field->is_repeated()) {
2401           printer->Print("jspb.Message.getRepeatedFloatingPointField("
2402                          "this, $index$)",
2403                          "index", JSFieldIndex(field));
2404         } else {
2405           // Convert "NaN" to NaN.
2406           printer->Print("+jspb.Message.getField(this, $index$)",
2407                          "index", JSFieldIndex(field));
2408         }
2409       } else {
2410         printer->Print("jspb.Message.getField(this, $index$)",
2411                        "index", JSFieldIndex(field));
2412       }
2413     }
2414 
2415     if (untyped) {
2416       printer->Print(
2417           ";\n"
2418           "};\n"
2419           "\n"
2420           "\n");
2421     } else {
2422       printer->Print(
2423           ");\n"
2424           "};\n"
2425           "\n"
2426           "\n");
2427     }
2428 
2429     if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
2430       GenerateBytesWrapper(options, printer, field, BYTES_B64);
2431       GenerateBytesWrapper(options, printer, field, BYTES_U8);
2432     }
2433 
2434     if (untyped) {
2435       printer->Print(
2436           "/**\n"
2437           " * @param {*} value $returndoc$\n"
2438           " */\n",
2439           "returndoc", JSReturnDoc(options, field));
2440     } else {
2441       printer->Print(
2442           "/** @param {$optionaltype$} value $returndoc$ */\n",
2443           "optionaltype",
2444           JSFieldTypeAnnotation(options, field,
2445                                 /* force_optional = */ true,
2446                                 /* force_present = */ !HasFieldPresence(field),
2447                                 /* singular_if_not_packed = */ false),
2448           "returndoc", JSReturnDoc(options, field));
2449     }
2450     printer->Print(
2451         "$class$.prototype.set$name$ = function(value) {\n"
2452         "  jspb.Message.set$oneoftag$Field(this, $index$",
2453         "class", GetPath(options, field->containing_type()),
2454         "name", JSGetterName(options, field),
2455         "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2456         "index", JSFieldIndex(field));
2457     printer->Print(
2458         "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
2459         "};\n"
2460         "\n"
2461         "\n",
2462         "type",
2463         untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2464         "typeclose", untyped ? ")" : "",
2465         "oneofgroup",
2466         (field->containing_oneof() ? (", " + JSOneofArray(options, field))
2467                                    : ""),
2468         "returnvalue", JSReturnClause(field), "rptvalueinit",
2469         (field->is_repeated() ? " || []" : ""));
2470 
2471     if (untyped) {
2472       printer->Print(
2473           "/**\n"
2474           " * Clears the value. $returndoc$\n"
2475           " */\n",
2476           "returndoc", JSReturnDoc(options, field));
2477     }
2478 
2479     if (HasFieldPresence(field) || field->is_repeated()) {
2480       printer->Print(
2481           "$class$.prototype.clear$name$ = function() {\n"
2482           "  jspb.Message.set$oneoftag$Field(this, $index$$oneofgroup$, ",
2483           "class", GetPath(options, field->containing_type()),
2484           "name", JSGetterName(options, field),
2485           "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2486           "oneofgroup", (field->containing_oneof() ?
2487                          (", " + JSOneofArray(options, field)) : ""),
2488           "index", JSFieldIndex(field));
2489       printer->Print(
2490           "$clearedvalue$);$returnvalue$\n"
2491           "};\n"
2492           "\n"
2493           "\n",
2494           "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2495           "returnvalue", JSReturnClause(field));
2496     }
2497   }
2498 
2499   if (HasFieldPresence(field)) {
2500     printer->Print(
2501         "/**\n"
2502         " * Returns whether this field is set.\n"
2503         " * @return{!boolean}\n"
2504         " */\n"
2505         "$class$.prototype.has$name$ = function() {\n"
2506         "  return jspb.Message.getField(this, $index$) != null;\n"
2507         "};\n"
2508         "\n"
2509         "\n",
2510         "class", GetPath(options, field->containing_type()),
2511         "name", JSGetterName(options, field),
2512         "index", JSFieldIndex(field));
2513   }
2514 }
2515 
GenerateClassExtensionFieldInfo(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2516 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
2517                                                 io::Printer* printer,
2518                                                 const Descriptor* desc) const {
2519   if (IsExtendable(desc)) {
2520     printer->Print(
2521         "\n"
2522         "/**\n"
2523         " * The extensions registered with this message class. This is a "
2524         "map of\n"
2525         " * extension field number to fieldInfo object.\n"
2526         " *\n"
2527         " * For example:\n"
2528         " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2529         "ctor: proto.example.MyMessage} }\n"
2530         " *\n"
2531         " * fieldName contains the JsCompiler renamed field name property "
2532         "so that it\n"
2533         " * works in OPTIMIZED mode.\n"
2534         " *\n"
2535         " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
2536         " */\n"
2537         "$class$.extensions = {};\n"
2538         "\n",
2539         "class", GetPath(options, desc));
2540 
2541     if (options.binary) {
2542       printer->Print(
2543           "\n"
2544           "/**\n"
2545           " * The extensions registered with this message class. This is a "
2546           "map of\n"
2547           " * extension field number to fieldInfo object.\n"
2548           " *\n"
2549           " * For example:\n"
2550           " *     { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2551           "ctor: proto.example.MyMessage} }\n"
2552           " *\n"
2553           " * fieldName contains the JsCompiler renamed field name property "
2554           "so that it\n"
2555           " * works in OPTIMIZED mode.\n"
2556           " *\n"
2557           " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
2558           " */\n"
2559           "$class$.extensionsBinary = {};\n"
2560           "\n",
2561           "class", GetPath(options, desc));
2562     }
2563   }
2564 }
2565 
2566 
GenerateClassDeserializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2567 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
2568                                                io::Printer* printer,
2569                                                const Descriptor* desc) const {
2570   // TODO(cfallin): Handle lazy decoding when requested by field option and/or
2571   // by default for 'bytes' fields and packed repeated fields.
2572 
2573   printer->Print(
2574       "/**\n"
2575       " * Deserializes binary data (in protobuf wire format).\n"
2576       " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
2577       " * @return {!$class$}\n"
2578       " */\n"
2579       "$class$.deserializeBinary = function(bytes) {\n"
2580       "  var reader = new jspb.BinaryReader(bytes);\n"
2581       "  var msg = new $class$;\n"
2582       "  return $class$.deserializeBinaryFromReader(msg, reader);\n"
2583       "};\n"
2584       "\n"
2585       "\n"
2586       "/**\n"
2587       " * Deserializes binary data (in protobuf wire format) from the\n"
2588       " * given reader into the given message object.\n"
2589       " * @param {!$class$} msg The message object to deserialize into.\n"
2590       " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
2591       " * @return {!$class$}\n"
2592       " */\n"
2593       "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
2594       "  while (reader.nextField()) {\n"
2595       "    if (reader.isEndGroup()) {\n"
2596       "      break;\n"
2597       "    }\n"
2598       "    var field = reader.getFieldNumber();\n"
2599       "    switch (field) {\n",
2600       "class", GetPath(options, desc));
2601 
2602   for (int i = 0; i < desc->field_count(); i++) {
2603     GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
2604   }
2605 
2606   printer->Print(
2607       "    default:\n");
2608   if (IsExtendable(desc)) {
2609     printer->Print(
2610         "      jspb.Message.readBinaryExtension(msg, reader, $extobj$Binary,\n"
2611         "        $class$.prototype.getExtension,\n"
2612         "        $class$.prototype.setExtension);\n"
2613         "      break;\n",
2614         "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2615         "class", GetPath(options, desc));
2616   } else {
2617     printer->Print(
2618         "      reader.skipField();\n"
2619         "      break;\n");
2620   }
2621 
2622   printer->Print(
2623       "    }\n"
2624       "  }\n"
2625       "  return msg;\n"
2626       "};\n"
2627       "\n"
2628       "\n");
2629 }
2630 
GenerateClassDeserializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2631 void Generator::GenerateClassDeserializeBinaryField(
2632     const GeneratorOptions& options,
2633     io::Printer* printer,
2634     const FieldDescriptor* field) const {
2635 
2636   printer->Print("    case $num$:\n",
2637                  "num", SimpleItoa(field->number()));
2638 
2639   if (field->is_map()) {
2640     const FieldDescriptor* key_field = MapFieldKey(field);
2641     const FieldDescriptor* value_field = MapFieldValue(field);
2642     printer->Print(
2643         "      var value = msg.get$name$();\n"
2644         "      reader.readMessage(value, function(message, reader) {\n",
2645         "name", JSGetterName(options, field));
2646 
2647     printer->Print("        jspb.Map.deserializeBinary(message, reader, "
2648                    "$keyReaderFn$, $valueReaderFn$",
2649           "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
2650           "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
2651 
2652     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2653       printer->Print(", $messageType$.deserializeBinaryFromReader",
2654           "messageType", GetPath(options, value_field->message_type()));
2655     }
2656 
2657     printer->Print(");\n");
2658     printer->Print("         });\n");
2659   } else {
2660     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2661       printer->Print(
2662           "      var value = new $fieldclass$;\n"
2663           "      reader.read$msgOrGroup$($grpfield$value,"
2664           "$fieldclass$.deserializeBinaryFromReader);\n",
2665         "fieldclass", SubmessageTypeRef(options, field),
2666           "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2667                         "Group" : "Message",
2668           "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2669                       (SimpleItoa(field->number()) + ", ") : "");
2670     } else {
2671       printer->Print(
2672           "      var value = /** @type {$fieldtype$} */ "
2673           "(reader.read$reader$());\n",
2674           "fieldtype", JSFieldTypeAnnotation(options, field, false, true,
2675                                              /* singular_if_not_packed */ true,
2676                                              BYTES_U8),
2677           "reader",
2678           JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
2679     }
2680 
2681     if (field->is_repeated() && !field->is_packed()) {
2682       // Repeated fields receive a |value| one at at a time; append to array
2683       // returned by get$name$(). Annoyingly, we have to call 'set' after
2684       // changing the array.
2685       printer->Print("      msg.get$name$().push(value);\n", "name",
2686                      JSGetterName(options, field));
2687       printer->Print("      msg.set$name$(msg.get$name$());\n", "name",
2688                      JSGetterName(options, field));
2689     } else {
2690       // Singular fields, and packed repeated fields, receive a |value| either
2691       // as the field's value or as the array of all the field's values; set
2692       // this as the field's value directly.
2693       printer->Print(
2694           "      msg.set$name$(value);\n",
2695           "name", JSGetterName(options, field));
2696     }
2697   }
2698 
2699   printer->Print("      break;\n");
2700 }
2701 
GenerateClassSerializeBinary(const GeneratorOptions & options,io::Printer * printer,const Descriptor * desc) const2702 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
2703                                              io::Printer* printer,
2704                                              const Descriptor* desc) const {
2705   printer->Print(
2706       "/**\n"
2707       " * Class method variant: serializes the given message to binary data\n"
2708       " * (in protobuf wire format), writing to the given BinaryWriter.\n"
2709       " * @param {!$class$} message\n"
2710       " * @param {!jspb.BinaryWriter} writer\n"
2711       " */\n"
2712       "$class$.serializeBinaryToWriter = function(message, "
2713       "writer) {\n"
2714       "  message.serializeBinaryToWriter(writer);\n"
2715       "};\n"
2716       "\n"
2717       "\n"
2718       "/**\n"
2719       " * Serializes the message to binary data (in protobuf wire format).\n"
2720       " * @return {!Uint8Array}\n"
2721       " */\n"
2722       "$class$.prototype.serializeBinary = function() {\n"
2723       "  var writer = new jspb.BinaryWriter();\n"
2724       "  this.serializeBinaryToWriter(writer);\n"
2725       "  return writer.getResultBuffer();\n"
2726       "};\n"
2727       "\n"
2728       "\n"
2729       "/**\n"
2730       " * Serializes the message to binary data (in protobuf wire format),\n"
2731       " * writing to the given BinaryWriter.\n"
2732       " * @param {!jspb.BinaryWriter} writer\n"
2733       " */\n"
2734       "$class$.prototype.serializeBinaryToWriter = function (writer) {\n"
2735       "  var f = undefined;\n",
2736       "class", GetPath(options, desc));
2737 
2738   for (int i = 0; i < desc->field_count(); i++) {
2739     GenerateClassSerializeBinaryField(options, printer, desc->field(i));
2740   }
2741 
2742   if (IsExtendable(desc)) {
2743     printer->Print(
2744         "  jspb.Message.serializeBinaryExtensions(this, writer,\n"
2745         "    $extobj$Binary, $class$.prototype.getExtension);\n",
2746         "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2747         "class", GetPath(options, desc));
2748   }
2749 
2750   printer->Print(
2751       "};\n"
2752       "\n"
2753       "\n");
2754 }
2755 
GenerateClassSerializeBinaryField(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2756 void Generator::GenerateClassSerializeBinaryField(
2757     const GeneratorOptions& options,
2758     io::Printer* printer,
2759     const FieldDescriptor* field) const {
2760   if (HasFieldPresence(field) &&
2761       field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
2762     printer->Print(
2763         "  f = jspb.Message.getField(this, $index$);\n",
2764         "index", JSFieldIndex(field));
2765   } else {
2766     printer->Print(
2767         "  f = this.get$name$($nolazy$);\n",
2768         "name", JSGetterName(options, field, BYTES_U8),
2769         // No lazy creation for maps containers -- fastpath the empty case.
2770         "nolazy", (field->is_map()) ? "true" : "");
2771   }
2772 
2773 
2774   // Print an `if (condition)` statement that evaluates to true if the field
2775   // goes on the wire.
2776   if (field->is_map()) {
2777     printer->Print(
2778         "  if (f && f.getLength() > 0) {\n");
2779   } else if (field->is_repeated()) {
2780     printer->Print(
2781         "  if (f.length > 0) {\n");
2782   } else {
2783     if (HasFieldPresence(field)) {
2784       printer->Print(
2785           "  if (f != null) {\n");
2786     } else {
2787       // No field presence: serialize onto the wire only if value is
2788       // non-default.  Defaults are documented here:
2789       // https://goto.google.com/lhdfm
2790       switch (field->cpp_type()) {
2791         case FieldDescriptor::CPPTYPE_INT32:
2792         case FieldDescriptor::CPPTYPE_INT64:
2793         case FieldDescriptor::CPPTYPE_UINT32:
2794         case FieldDescriptor::CPPTYPE_UINT64: {
2795           {
2796             printer->Print("  if (f !== 0) {\n");
2797           }
2798           break;
2799         }
2800 
2801         case FieldDescriptor::CPPTYPE_ENUM:
2802         case FieldDescriptor::CPPTYPE_FLOAT:
2803         case FieldDescriptor::CPPTYPE_DOUBLE:
2804           printer->Print(
2805               "  if (f !== 0.0) {\n");
2806           break;
2807         case FieldDescriptor::CPPTYPE_BOOL:
2808           printer->Print(
2809               "  if (f) {\n");
2810           break;
2811         case FieldDescriptor::CPPTYPE_STRING:
2812           printer->Print(
2813               "  if (f.length > 0) {\n");
2814           break;
2815         default:
2816           assert(false);
2817           break;
2818       }
2819     }
2820   }
2821 
2822   // Write the field on the wire.
2823   if (field->is_map()) {
2824     const FieldDescriptor* key_field = MapFieldKey(field);
2825     const FieldDescriptor* value_field = MapFieldValue(field);
2826     printer->Print(
2827         "    f.serializeBinary($index$, writer, "
2828                               "$keyWriterFn$, $valueWriterFn$",
2829         "index", SimpleItoa(field->number()),
2830         "keyWriterFn", JSBinaryWriterMethodName(options, key_field),
2831         "valueWriterFn", JSBinaryWriterMethodName(options, value_field));
2832 
2833     if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2834       printer->Print(", $messageType$.serializeBinaryToWriter",
2835           "messageType", GetPath(options, value_field->message_type()));
2836     }
2837 
2838     printer->Print(");\n");
2839   } else {
2840     printer->Print(
2841         "    writer.write$method$(\n"
2842         "      $index$,\n"
2843         "      f",
2844         "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
2845         "index", SimpleItoa(field->number()));
2846 
2847     if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
2848         !(field->is_map())) {
2849       printer->Print(
2850           ",\n"
2851           "      $submsg$.serializeBinaryToWriter\n",
2852         "submsg", SubmessageTypeRef(options, field));
2853     } else {
2854       printer->Print("\n");
2855     }
2856 
2857     printer->Print(
2858         "    );\n");
2859   }
2860 
2861   // Close the `if`.
2862   printer->Print(
2863       "  }\n");
2864 }
2865 
GenerateEnum(const GeneratorOptions & options,io::Printer * printer,const EnumDescriptor * enumdesc) const2866 void Generator::GenerateEnum(const GeneratorOptions& options,
2867                              io::Printer* printer,
2868                              const EnumDescriptor* enumdesc) const {
2869   printer->Print(
2870       "/**\n"
2871       " * @enum {number}\n"
2872       " */\n"
2873       "$name$ = {\n",
2874       "name", GetPath(options, enumdesc));
2875 
2876   for (int i = 0; i < enumdesc->value_count(); i++) {
2877     const EnumValueDescriptor* value = enumdesc->value(i);
2878     printer->Print(
2879         "  $name$: $value$$comma$\n",
2880         "name", ToEnumCase(value->name()),
2881         "value", SimpleItoa(value->number()),
2882         "comma", (i == enumdesc->value_count() - 1) ? "" : ",");
2883   }
2884 
2885   printer->Print(
2886       "};\n"
2887       "\n");
2888 }
2889 
GenerateExtension(const GeneratorOptions & options,io::Printer * printer,const FieldDescriptor * field) const2890 void Generator::GenerateExtension(const GeneratorOptions& options,
2891                                   io::Printer* printer,
2892                                   const FieldDescriptor* field) const {
2893   string extension_scope =
2894       (field->extension_scope() ?
2895        GetPath(options, field->extension_scope()) :
2896        GetPath(options, field->file()));
2897 
2898   printer->Print(
2899       "\n"
2900       "/**\n"
2901       " * A tuple of {field number, class constructor} for the extension\n"
2902       " * field named `$name$`.\n"
2903       " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
2904       " */\n"
2905       "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
2906       "name", JSObjectFieldName(options, field),
2907       "class", extension_scope,
2908       "extensionType", JSFieldTypeAnnotation(
2909           options, field,
2910           /* force_optional = */ false,
2911           /* force_present = */ true,
2912           /* singular_if_not_packed = */ false));
2913   printer->Print(
2914       "    $index$,\n"
2915       "    {$name$: 0},\n"
2916       "    $ctor$,\n"
2917       "     /** @type {?function((boolean|undefined),!jspb.Message=): "
2918       "!Object} */ (\n"
2919       "         $toObject$),\n"
2920       "    $repeated$);\n",
2921       "index", SimpleItoa(field->number()),
2922       "name", JSObjectFieldName(options, field),
2923       "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2924                SubmessageTypeRef(options, field) : string("null")),
2925       "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2926                    (SubmessageTypeRef(options, field) + ".toObject") :
2927                    string("null")),
2928       "repeated", (field->is_repeated() ? "1" : "0"));
2929 
2930   if (options.binary) {
2931     printer->Print(
2932         "\n"
2933         "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
2934         "    $class$.$name$,\n"
2935         "    $binaryReaderFn$,\n"
2936         "    $binaryWriterFn$,\n"
2937         "    $binaryMessageSerializeFn$,\n"
2938         "    $binaryMessageDeserializeFn$,\n",
2939         "extendName", JSExtensionsObjectName(options, field->file(),
2940                                              field->containing_type()),
2941         "index", SimpleItoa(field->number()),
2942         "class", extension_scope,
2943         "name", JSObjectFieldName(options, field),
2944         "binaryReaderFn", JSBinaryReaderMethodName(options, field),
2945         "binaryWriterFn", JSBinaryWriterMethodName(options, field),
2946         "binaryMessageSerializeFn",
2947         (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2948         (SubmessageTypeRef(options, field) +
2949          ".serializeBinaryToWriter") : "null",
2950         "binaryMessageDeserializeFn",
2951         (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2952         (SubmessageTypeRef(options, field) +
2953          ".deserializeBinaryFromReader") : "null");
2954 
2955     printer->Print(
2956         "    $isPacked$);\n",
2957         "isPacked", (field->is_packed() ? "true" : "false"));
2958   }
2959 
2960   printer->Print(
2961       "// This registers the extension field with the extended class, so that\n"
2962       "// toObject() will function correctly.\n"
2963       "$extendName$[$index$] = $class$.$name$;\n"
2964       "\n",
2965       "extendName", JSExtensionsObjectName(options, field->file(),
2966                                            field->containing_type()),
2967       "index", SimpleItoa(field->number()),
2968       "class", extension_scope,
2969       "name", JSObjectFieldName(options, field));
2970 }
2971 
ParseFromOptions(const vector<pair<string,string>> & options,string * error)2972 bool GeneratorOptions::ParseFromOptions(
2973     const vector< pair< string, string > >& options,
2974     string* error) {
2975   for (int i = 0; i < options.size(); i++) {
2976     if (options[i].first == "add_require_for_enums") {
2977       if (options[i].second != "") {
2978         *error = "Unexpected option value for add_require_for_enums";
2979         return false;
2980       }
2981       add_require_for_enums = true;
2982     } else if (options[i].first == "binary") {
2983       if (options[i].second != "") {
2984         *error = "Unexpected option value for binary";
2985         return false;
2986       }
2987       binary = true;
2988     } else if (options[i].first == "testonly") {
2989       if (options[i].second != "") {
2990         *error = "Unexpected option value for testonly";
2991         return false;
2992       }
2993       testonly = true;
2994     } else if (options[i].first == "error_on_name_conflict") {
2995       if (options[i].second != "") {
2996         *error = "Unexpected option value for error_on_name_conflict";
2997         return false;
2998       }
2999       error_on_name_conflict = true;
3000     } else if (options[i].first == "output_dir") {
3001       output_dir = options[i].second;
3002     } else if (options[i].first == "namespace_prefix") {
3003       namespace_prefix = options[i].second;
3004     } else if (options[i].first == "library") {
3005       library = options[i].second;
3006     } else if (options[i].first == "import_style") {
3007       if (options[i].second == "closure") {
3008         import_style = IMPORT_CLOSURE;
3009       } else if (options[i].second == "commonjs") {
3010         import_style = IMPORT_COMMONJS;
3011       } else if (options[i].second == "browser") {
3012         import_style = IMPORT_BROWSER;
3013       } else if (options[i].second == "es6") {
3014         import_style = IMPORT_ES6;
3015       } else {
3016         *error = "Unknown import style " + options[i].second + ", expected " +
3017                  "one of: closure, commonjs, browser, es6.";
3018       }
3019     } else {
3020       // Assume any other option is an output directory, as long as it is a bare
3021       // `key` rather than a `key=value` option.
3022       if (options[i].second != "") {
3023         *error = "Unknown option: " + options[i].first;
3024         return false;
3025       }
3026       output_dir = options[i].first;
3027     }
3028   }
3029 
3030   if (!library.empty() && import_style != IMPORT_CLOSURE) {
3031     *error = "The library option should only be used for "
3032              "import_style=closure";
3033   }
3034 
3035   return true;
3036 }
3037 
GenerateFilesInDepOrder(const GeneratorOptions & options,io::Printer * printer,const vector<const FileDescriptor * > & files) const3038 void Generator::GenerateFilesInDepOrder(
3039     const GeneratorOptions& options,
3040     io::Printer* printer,
3041     const vector<const FileDescriptor*>& files) const {
3042   // Build a std::set over all files so that the DFS can detect when it recurses
3043   // into a dep not specified in the user's command line.
3044   std::set<const FileDescriptor*> all_files(files.begin(), files.end());
3045   // Track the in-progress set of files that have been generated already.
3046   std::set<const FileDescriptor*> generated;
3047   for (int i = 0; i < files.size(); i++) {
3048     GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
3049   }
3050 }
3051 
GenerateFileAndDeps(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * root,std::set<const FileDescriptor * > * all_files,std::set<const FileDescriptor * > * generated) const3052 void Generator::GenerateFileAndDeps(
3053     const GeneratorOptions& options,
3054     io::Printer* printer,
3055     const FileDescriptor* root,
3056     std::set<const FileDescriptor*>* all_files,
3057     std::set<const FileDescriptor*>* generated) const {
3058   // Skip if already generated.
3059   if (generated->find(root) != generated->end()) {
3060     return;
3061   }
3062   generated->insert(root);
3063 
3064   // Generate all dependencies before this file's content.
3065   for (int i = 0; i < root->dependency_count(); i++) {
3066     const FileDescriptor* dep = root->dependency(i);
3067     GenerateFileAndDeps(options, printer, dep, all_files, generated);
3068   }
3069 
3070   // Generate this file's content.  Only generate if the file is part of the
3071   // original set requested to be generated; i.e., don't take all transitive
3072   // deps down to the roots.
3073   if (all_files->find(root) != all_files->end()) {
3074     GenerateClassesAndEnums(options, printer, root);
3075   }
3076 }
3077 
GenerateFile(const GeneratorOptions & options,io::Printer * printer,const FileDescriptor * file) const3078 void Generator::GenerateFile(const GeneratorOptions& options,
3079                              io::Printer* printer,
3080                              const FileDescriptor* file) const {
3081   GenerateHeader(options, printer);
3082 
3083   // Generate "require" statements.
3084   if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
3085     printer->Print("var jspb = require('google-protobuf');\n");
3086     printer->Print("var goog = jspb;\n");
3087     printer->Print("var global = Function('return this')();\n\n");
3088 
3089     for (int i = 0; i < file->dependency_count(); i++) {
3090       const string& name = file->dependency(i)->name();
3091       printer->Print(
3092           "var $alias$ = require('$file$');\n",
3093           "alias", ModuleAlias(name),
3094           "file", GetRootPath(file->name(), name) + GetJSFilename(name));
3095     }
3096   }
3097 
3098   // We aren't using Closure's import system, but we use goog.exportSymbol()
3099   // to construct the expected tree of objects, eg.
3100   //
3101   //   goog.exportSymbol('foo.bar.Baz', null, this);
3102   //
3103   //   // Later generated code expects foo.bar = {} to exist:
3104   //   foo.bar.Baz = function() { /* ... */ }
3105   set<string> provided;
3106 
3107   // Cover the case where this file declares extensions but no messages.
3108   // This will ensure that the file-level object will be declared to hold
3109   // the extensions.
3110   for (int i = 0; i < file->extension_count(); i++) {
3111     provided.insert(file->extension(i)->full_name());
3112   }
3113 
3114   FindProvidesForFile(options, printer, file, &provided);
3115   for (std::set<string>::iterator it = provided.begin();
3116        it != provided.end(); ++it) {
3117     printer->Print("goog.exportSymbol('$name$', null, global);\n",
3118                    "name", *it);
3119   }
3120 
3121   GenerateClassesAndEnums(options, printer, file);
3122 
3123   // Extensions nested inside messages are emitted inside
3124   // GenerateClassesAndEnums().
3125   for (int i = 0; i < file->extension_count(); i++) {
3126     GenerateExtension(options, printer, file->extension(i));
3127   }
3128 
3129   if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
3130     printer->Print("goog.object.extend(exports, $package$);\n",
3131                    "package", GetPath(options, file));
3132   }
3133 }
3134 
GenerateAll(const vector<const FileDescriptor * > & files,const string & parameter,GeneratorContext * context,string * error) const3135 bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
3136                             const string& parameter,
3137                             GeneratorContext* context,
3138                             string* error) const {
3139   vector< pair< string, string > > option_pairs;
3140   ParseGeneratorParameter(parameter, &option_pairs);
3141   GeneratorOptions options;
3142   if (!options.ParseFromOptions(option_pairs, error)) {
3143     return false;
3144   }
3145 
3146 
3147   // There are three schemes for where output files go:
3148   //
3149   // - import_style = IMPORT_CLOSURE, library non-empty: all output in one file
3150   // - import_style = IMPORT_CLOSURE, library empty: one output file per type
3151   // - import_style != IMPORT_CLOSURE: one output file per .proto file
3152   if (options.import_style == GeneratorOptions::IMPORT_CLOSURE &&
3153       options.library != "") {
3154     // All output should go in a single file.
3155     string filename = options.output_dir + "/" + options.library + ".js";
3156     google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3157     GOOGLE_CHECK(output.get());
3158     io::Printer printer(output.get(), '$');
3159 
3160     // Pull out all extensions -- we need these to generate all
3161     // provides/requires.
3162     vector<const FieldDescriptor*> extensions;
3163     for (int i = 0; i < files.size(); i++) {
3164       for (int j = 0; j < files[i]->extension_count(); j++) {
3165         const FieldDescriptor* extension = files[i]->extension(j);
3166         extensions.push_back(extension);
3167       }
3168     }
3169 
3170     GenerateHeader(options, &printer);
3171 
3172     std::set<string> provided;
3173     FindProvides(options, &printer, files, &provided);
3174     FindProvidesForFields(options, &printer, extensions, &provided);
3175     GenerateProvides(options, &printer, &provided);
3176     GenerateTestOnly(options, &printer);
3177     GenerateRequiresForLibrary(options, &printer, files, &provided);
3178 
3179     GenerateFilesInDepOrder(options, &printer, files);
3180 
3181     for (int i = 0; i < extensions.size(); i++) {
3182       if (ShouldGenerateExtension(extensions[i])) {
3183         GenerateExtension(options, &printer, extensions[i]);
3184       }
3185     }
3186 
3187     if (printer.failed()) {
3188       return false;
3189     }
3190   } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) {
3191     set<const void*> allowed_set;
3192     if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) {
3193       return false;
3194     }
3195 
3196     for (int i = 0; i < files.size(); i++) {
3197       const FileDescriptor* file = files[i];
3198       for (int j = 0; j < file->message_type_count(); j++) {
3199         const Descriptor* desc = file->message_type(j);
3200         if (allowed_set.count(desc) == 0) {
3201           continue;
3202         }
3203 
3204         string filename = GetMessageFileName(options, desc);
3205         google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
3206             context->Open(filename));
3207         GOOGLE_CHECK(output.get());
3208         io::Printer printer(output.get(), '$');
3209 
3210         GenerateHeader(options, &printer);
3211 
3212         std::set<string> provided;
3213         FindProvidesForMessage(options, &printer, desc, &provided);
3214         GenerateProvides(options, &printer, &provided);
3215         GenerateTestOnly(options, &printer);
3216         GenerateRequiresForMessage(options, &printer, desc, &provided);
3217 
3218         GenerateClass(options, &printer, desc);
3219 
3220         if (printer.failed()) {
3221           return false;
3222         }
3223       }
3224       for (int j = 0; j < file->enum_type_count(); j++) {
3225         const EnumDescriptor* enumdesc = file->enum_type(j);
3226         if (allowed_set.count(enumdesc) == 0) {
3227           continue;
3228         }
3229 
3230         string filename = GetEnumFileName(options, enumdesc);
3231         google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
3232             context->Open(filename));
3233         GOOGLE_CHECK(output.get());
3234         io::Printer printer(output.get(), '$');
3235 
3236         GenerateHeader(options, &printer);
3237 
3238         std::set<string> provided;
3239         FindProvidesForEnum(options, &printer, enumdesc, &provided);
3240         GenerateProvides(options, &printer, &provided);
3241         GenerateTestOnly(options, &printer);
3242 
3243         GenerateEnum(options, &printer, enumdesc);
3244 
3245         if (printer.failed()) {
3246           return false;
3247         }
3248       }
3249       // File-level extensions (message-level extensions are generated under
3250       // the enclosing message).
3251       if (allowed_set.count(file) == 1) {
3252         string filename = GetExtensionFileName(options, file);
3253 
3254         google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
3255             context->Open(filename));
3256         GOOGLE_CHECK(output.get());
3257         io::Printer printer(output.get(), '$');
3258 
3259         GenerateHeader(options, &printer);
3260 
3261         std::set<string> provided;
3262         vector<const FieldDescriptor*> fields;
3263 
3264         for (int j = 0; j < files[i]->extension_count(); j++) {
3265           if (ShouldGenerateExtension(files[i]->extension(j))) {
3266             fields.push_back(files[i]->extension(j));
3267           }
3268         }
3269 
3270         FindProvidesForFields(options, &printer, fields, &provided);
3271         GenerateProvides(options, &printer, &provided);
3272         GenerateTestOnly(options, &printer);
3273         GenerateRequiresForExtensions(options, &printer, fields, &provided);
3274 
3275         for (int j = 0; j < files[i]->extension_count(); j++) {
3276           if (ShouldGenerateExtension(files[i]->extension(j))) {
3277             GenerateExtension(options, &printer, files[i]->extension(j));
3278           }
3279         }
3280       }
3281     }
3282   } else {
3283     // Generate one output file per input (.proto) file.
3284 
3285     for (int i = 0; i < files.size(); i++) {
3286       const google::protobuf::FileDescriptor* file = files[i];
3287 
3288       string filename = options.output_dir + "/" + GetJSFilename(file->name());
3289       google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
3290       GOOGLE_CHECK(output.get());
3291       io::Printer printer(output.get(), '$');
3292 
3293       GenerateFile(options, &printer, file);
3294 
3295       if (printer.failed()) {
3296         return false;
3297       }
3298     }
3299   }
3300 
3301   return true;
3302 }
3303 
3304 }  // namespace js
3305 }  // namespace compiler
3306 }  // namespace protobuf
3307 }  // namespace google
3308