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