blob: 1e308464635647d9cf0701ca5251258e24afc014 [file] [log] [blame]
Feng Xiaoe841bac2015-12-11 17:09:20 -08001// 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
54namespace google {
55namespace protobuf {
56namespace compiler {
57namespace js {
58
59// Sorted list of JavaScript keywords. These cannot be used as names. If they
60// appear, we prefix them with "pb_".
61const 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
122static const int kNumKeyword = sizeof(kKeyword) / sizeof(char*);
123
124namespace {
125
126bool IsReserved(const string& ident) {
127 for (int i = 0; i < kNumKeyword; i++) {
128 if (ident == kKeyword[i]) {
129 return true;
130 }
131 }
132 return false;
133}
134
135// Returns a copy of |filename| with any trailing ".protodevel" or ".proto
136// suffix stripped.
137string StripProto(const string& filename) {
138 const char* suffix = HasSuffixString(filename, ".protodevel")
139 ? ".protodevel" : ".proto";
140 return StripSuffixString(filename, suffix);
141}
142
143// Returns the fully normalized JavaScript path for the given
144// file descriptor's package.
145string GetPath(const GeneratorOptions& options,
146 const FileDescriptor* file) {
147 if (!options.namespace_prefix.empty()) {
148 return options.namespace_prefix;
149 } else if (!file->package().empty()) {
150 return "proto." + file->package();
151 } else {
152 return "proto";
153 }
154}
155
156// Forward declare, so that GetPrefix can call this method,
157// which in turn, calls GetPrefix.
158string GetPath(const GeneratorOptions& options,
159 const Descriptor* descriptor);
160
161// Returns the path prefix for a message or enumeration that
162// lives under the given file and containing type.
163string GetPrefix(const GeneratorOptions& options,
164 const FileDescriptor* file_descriptor,
165 const Descriptor* containing_type) {
166 string prefix = "";
167
168 if (containing_type == NULL) {
169 prefix = GetPath(options, file_descriptor);
170 } else {
171 prefix = GetPath(options, containing_type);
172 }
173
174 if (!prefix.empty()) {
175 prefix += ".";
176 }
177
178 return prefix;
179}
180
181
182// Returns the fully normalized JavaScript path for the given
183// message descriptor.
184string GetPath(const GeneratorOptions& options,
185 const Descriptor* descriptor) {
186 return GetPrefix(
187 options, descriptor->file(),
188 descriptor->containing_type()) + descriptor->name();
189}
190
191
192// Returns the fully normalized JavaScript path for the given
193// field's containing message descriptor.
194string GetPath(const GeneratorOptions& options,
195 const FieldDescriptor* descriptor) {
196 return GetPath(options, descriptor->containing_type());
197}
198
199// Returns the fully normalized JavaScript path for the given
200// enumeration descriptor.
201string GetPath(const GeneratorOptions& options,
202 const EnumDescriptor* enum_descriptor) {
203 return GetPrefix(
204 options, enum_descriptor->file(),
205 enum_descriptor->containing_type()) + enum_descriptor->name();
206}
207
208
209// Returns the fully normalized JavaScript path for the given
210// enumeration value descriptor.
211string GetPath(const GeneratorOptions& options,
212 const EnumValueDescriptor* value_descriptor) {
213 return GetPath(
214 options,
215 value_descriptor->type()) + "." + value_descriptor->name();
216}
217
218// - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
219// (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
220// and with reserved words triggering a "pb_" prefix.
221// - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
222// (use the name directly), then append "List" if appropriate, then append "$"
223// if resulting name is equal to a reserved word.
224// - Enums: just uppercase.
225
226// Locale-independent version of ToLower that deals only with ASCII A-Z.
227char ToLowerASCII(char c) {
228 if (c >= 'A' && c <= 'Z') {
229 return (c - 'A') + 'a';
230 } else {
231 return c;
232 }
233}
234
235vector<string> ParseLowerUnderscore(const string& input) {
236 vector<string> words;
237 string running = "";
238 for (int i = 0; i < input.size(); i++) {
239 if (input[i] == '_') {
240 if (!running.empty()) {
241 words.push_back(running);
242 running.clear();
243 }
244 } else {
245 running += ToLowerASCII(input[i]);
246 }
247 }
248 if (!running.empty()) {
249 words.push_back(running);
250 }
251 return words;
252}
253
254vector<string> ParseUpperCamel(const string& input) {
255 vector<string> words;
256 string running = "";
257 for (int i = 0; i < input.size(); i++) {
258 if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
259 words.push_back(running);
260 running.clear();
261 }
262 running += ToLowerASCII(input[i]);
263 }
264 if (!running.empty()) {
265 words.push_back(running);
266 }
267 return words;
268}
269
270string ToLowerCamel(const vector<string>& words) {
271 string result;
272 for (int i = 0; i < words.size(); i++) {
273 string word = words[i];
274 if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
275 word[0] = (word[0] - 'A') + 'a';
276 } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
277 word[0] = (word[0] - 'a') + 'A';
278 }
279 result += word;
280 }
281 return result;
282}
283
284string ToUpperCamel(const vector<string>& words) {
285 string result;
286 for (int i = 0; i < words.size(); i++) {
287 string word = words[i];
288 if (word[0] >= 'a' && word[0] <= 'z') {
289 word[0] = (word[0] - 'a') + 'A';
290 }
291 result += word;
292 }
293 return result;
294}
295
296// Based on code from descriptor.cc (Thanks Kenton!)
297// Uppercases the entire string, turning ValueName into
298// VALUENAME.
299string ToEnumCase(const string& input) {
300 string result;
301 result.reserve(input.size());
302
303 for (int i = 0; i < input.size(); i++) {
304 if ('a' <= input[i] && input[i] <= 'z') {
305 result.push_back(input[i] - 'a' + 'A');
306 } else {
307 result.push_back(input[i]);
308 }
309 }
310
311 return result;
312}
313
314string ToFileName(const string& input) {
315 string result;
316 result.reserve(input.size());
317
318 for (int i = 0; i < input.size(); i++) {
319 if ('A' <= input[i] && input[i] <= 'Z') {
320 result.push_back(input[i] - 'A' + 'a');
321 } else {
322 result.push_back(input[i]);
323 }
324 }
325
326 return result;
327}
328
329// Returns the message/response ID, if set.
330string GetMessageId(const Descriptor* desc) {
331 return string();
332}
333
334
335// Used inside Google only -- do not remove.
336bool IsResponse(const Descriptor* desc) { return false; }
337bool IgnoreField(const FieldDescriptor* field) { return false; }
338
339
340// Does JSPB ignore this entire oneof? True only if all fields are ignored.
341bool IgnoreOneof(const OneofDescriptor* oneof) {
342 for (int i = 0; i < oneof->field_count(); i++) {
343 if (!IgnoreField(oneof->field(i))) {
344 return false;
345 }
346 }
347 return true;
348}
349
350string JSIdent(const FieldDescriptor* field,
351 bool is_upper_camel,
352 bool is_map) {
353 string result;
354 if (field->type() == FieldDescriptor::TYPE_GROUP) {
355 result = is_upper_camel ?
356 ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
357 ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
358 } else {
359 result = is_upper_camel ?
360 ToUpperCamel(ParseLowerUnderscore(field->name())) :
361 ToLowerCamel(ParseLowerUnderscore(field->name()));
362 }
363 if (is_map) {
364 result += "Map";
365 } else if (field->is_repeated()) {
366 result += "List";
367 }
368 return result;
369}
370
371string JSObjectFieldName(const FieldDescriptor* field) {
372 string name = JSIdent(
373 field,
374 /* is_upper_camel = */ false,
375 /* is_map = */ false);
376 if (IsReserved(name)) {
377 name = "pb_" + name;
378 }
379 return name;
380}
381
382// Returns the field name as a capitalized portion of a getter/setter method
383// name, e.g. MyField for .getMyField().
384string JSGetterName(const FieldDescriptor* field) {
385 string name = JSIdent(field,
386 /* is_upper_camel = */ true,
387 /* is_map = */ false);
388 if (name == "Extension" || name == "JsPbMessageId") {
389 // Avoid conflicts with base-class names.
390 name += "$";
391 }
392 return name;
393}
394
395string JSMapGetterName(const FieldDescriptor* field) {
396 return JSIdent(field,
397 /* is_upper_camel = */ true,
398 /* is_map = */ true);
399}
400
401
402
403string JSOneofName(const OneofDescriptor* oneof) {
404 return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
405}
406
407// Returns the index corresponding to this field in the JSPB array (underlying
408// data storage array).
409string JSFieldIndex(const FieldDescriptor* field) {
410 // Determine whether this field is a member of a group. Group fields are a bit
411 // wonky: their "containing type" is a message type created just for the
412 // group, and that type's parent type has a field with the group-message type
413 // as its message type and TYPE_GROUP as its field type. For such fields, the
414 // index we use is relative to the field number of the group submessage field.
415 // For all other fields, we just use the field number.
416 const Descriptor* containing_type = field->containing_type();
417 const Descriptor* parent_type = containing_type->containing_type();
418 if (parent_type != NULL) {
419 for (int i = 0; i < parent_type->field_count(); i++) {
420 if (parent_type->field(i)->type() == FieldDescriptor::TYPE_GROUP &&
421 parent_type->field(i)->message_type() == containing_type) {
422 return SimpleItoa(field->number() - parent_type->field(i)->number());
423 }
424 }
425 }
426 return SimpleItoa(field->number());
427}
428
429string JSOneofIndex(const OneofDescriptor* oneof) {
430 int index = -1;
431 for (int i = 0; i < oneof->containing_type()->oneof_decl_count(); i++) {
432 const OneofDescriptor* o = oneof->containing_type()->oneof_decl(i);
433 // If at least one field in this oneof is not JSPB-ignored, count the oneof.
434 for (int j = 0; j < o->field_count(); j++) {
435 const FieldDescriptor* f = o->field(j);
436 if (!IgnoreField(f)) {
437 index++;
438 break; // inner loop
439 }
440 }
441 if (o == oneof) {
442 break;
443 }
444 }
445 return SimpleItoa(index);
446}
447
448// Decodes a codepoint in \x0000 -- \xFFFF. Since JS strings are UTF-16, we only
449// need to handle the BMP (16-bit range) here.
450uint16_t DecodeUTF8Codepoint(uint8_t* bytes, size_t* length) {
451 if (*length == 0) {
452 return 0;
453 }
454 size_t expected = 0;
455 if ((*bytes & 0x80) == 0) {
456 expected = 1;
457 } else if ((*bytes & 0xe0) == 0xc0) {
458 expected = 2;
459 } else if ((*bytes & 0xf0) == 0xe0) {
460 expected = 3;
461 } else {
462 // Too long -- don't accept.
463 *length = 0;
464 return 0;
465 }
466
467 if (*length < expected) {
468 // Not enough bytes -- don't accept.
469 *length = 0;
470 return 0;
471 }
472
473 *length = expected;
474 switch (expected) {
475 case 1: return bytes[0];
476 case 2: return ((bytes[0] & 0x1F) << 6) |
477 ((bytes[1] & 0x3F) << 0);
478 case 3: return ((bytes[0] & 0x0F) << 12) |
479 ((bytes[1] & 0x3F) << 6) |
480 ((bytes[2] & 0x3F) << 0);
481 default: return 0;
482 }
483}
484
485// Escapes the contents of a string to be included within double-quotes ("") in
486// JavaScript. |is_utf8| determines whether the input data (in a C++ string of
487// chars) is UTF-8 encoded (in which case codepoints become JavaScript string
488// characters, escaped with 16-bit hex escapes where necessary) or raw binary
489// (in which case bytes become JavaScript string characters 0 -- 255).
490string EscapeJSString(const string& in, bool is_utf8) {
491 string result;
492 size_t decoded = 0;
493 for (size_t i = 0; i < in.size(); i += decoded) {
494 uint16_t codepoint = 0;
495 if (is_utf8) {
496 // Decode the next UTF-8 codepoint.
497 size_t have_bytes = in.size() - i;
498 uint8_t bytes[3] = {
499 static_cast<uint8_t>(in[i]),
500 static_cast<uint8_t>(((i + 1) < in.size()) ? in[i + 1] : 0),
501 static_cast<uint8_t>(((i + 2) < in.size()) ? in[i + 2] : 0),
502 };
503 codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
504 if (have_bytes == 0) {
505 break;
506 }
507 decoded = have_bytes;
508 } else {
509 codepoint = static_cast<uint16_t>(static_cast<uint8_t>(in[i]));
510 decoded = 1;
511 }
512
513 // Next byte -- used for minimal octal escapes below.
514 char next_byte = (i + decoded) < in.size() ?
515 in[i + decoded] : 0;
516 bool pad_octal = (next_byte >= '0' && next_byte <= '7');
517
518 switch (codepoint) {
519 case '\0': result += pad_octal ? "\\000" : "\\0"; break;
520 case '\b': result += "\\\b"; break;
521 case '\t': result += "\\\t"; break;
522 case '\n': result += "\\\n"; break;
523 case '\r': result += "\\\r"; break;
524 case '\f': result += "\\\f"; break;
525 case '\\': result += "\\\\"; break;
526 case '"': result += pad_octal ? "\\042" : "\\42"; break;
527 case '&': result += pad_octal ? "\\046" : "\\46"; break;
528 case '\'': result += pad_octal ? "\\047" : "\\47"; break;
529 case '<': result += pad_octal ? "\\074" : "\\74"; break;
530 case '=': result += pad_octal ? "\\075" : "\\75"; break;
531 case '>': result += pad_octal ? "\\076" : "\\76"; break;
532 default:
533 // All other non-ASCII codepoints are escaped.
534 // Original codegen uses hex for >= 0x100 and octal for others.
535 if (codepoint >= 0x20 && codepoint <= 0x7e) {
536 result += static_cast<char>(codepoint);
537 } else {
538 if (codepoint >= 0x100) {
539 result += StringPrintf("\\u%04x", codepoint);
540 } else {
541 if (pad_octal || codepoint >= 0100) {
542 result += "\\";
543 result += ('0' + ((codepoint >> 6) & 07));
544 result += ('0' + ((codepoint >> 3) & 07));
545 result += ('0' + ((codepoint >> 0) & 07));
546 } else if (codepoint >= 010) {
547 result += "\\";
548 result += ('0' + ((codepoint >> 3) & 07));
549 result += ('0' + ((codepoint >> 0) & 07));
550 } else {
551 result += "\\";
552 result += ('0' + ((codepoint >> 0) & 07));
553 }
554 }
555 }
556 break;
557 }
558 }
559 return result;
560}
561
562string EscapeBase64(const string& in) {
563 static const char* kAlphabet =
564 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
565 string result;
566
567 for (size_t i = 0; i < in.size(); i += 3) {
568 int value = (in[i] << 16) |
569 (((i + 1) < in.size()) ? (in[i + 1] << 8) : 0) |
570 (((i + 2) < in.size()) ? (in[i + 2] << 0) : 0);
571 result += kAlphabet[(value >> 18) & 0x3f];
572 result += kAlphabet[(value >> 12) & 0x3f];
573 if ((i + 1) < in.size()) {
574 result += kAlphabet[(value >> 6) & 0x3f];
575 } else {
576 result += '=';
577 }
578 if ((i + 2) < in.size()) {
579 result += kAlphabet[(value >> 0) & 0x3f];
580 } else {
581 result += '=';
582 }
583 }
584
585 return result;
586}
587
588// Post-process the result of SimpleFtoa/SimpleDtoa to *exactly* match the
589// original codegen's formatting (which is just .toString() on java.lang.Double
590// or java.lang.Float).
591string PostProcessFloat(string result) {
592 // If inf, -inf or nan, replace with +Infinity, -Infinity or NaN.
593 if (result == "inf") {
594 return "Infinity";
595 } else if (result == "-inf") {
596 return "-Infinity";
597 } else if (result == "nan") {
598 return "NaN";
599 }
600
601 // If scientific notation (e.g., "1e10"), (i) capitalize the "e", (ii)
602 // ensure that the mantissa (portion prior to the "e") has at least one
603 // fractional digit (after the decimal point), and (iii) strip any unnecessary
604 // leading zeroes and/or '+' signs from the exponent.
605 string::size_type exp_pos = result.find('e');
606 if (exp_pos != string::npos) {
607 string mantissa = result.substr(0, exp_pos);
608 string exponent = result.substr(exp_pos + 1);
609
610 // Add ".0" to mantissa if no fractional part exists.
611 if (mantissa.find('.') == string::npos) {
612 mantissa += ".0";
613 }
614
615 // Strip the sign off the exponent and store as |exp_neg|.
616 bool exp_neg = false;
617 if (!exponent.empty() && exponent[0] == '+') {
618 exponent = exponent.substr(1);
619 } else if (!exponent.empty() && exponent[0] == '-') {
620 exp_neg = true;
621 exponent = exponent.substr(1);
622 }
623
624 // Strip any leading zeroes off the exponent.
625 while (exponent.size() > 1 && exponent[0] == '0') {
626 exponent = exponent.substr(1);
627 }
628
629 return mantissa + "E" + string(exp_neg ? "-" : "") + exponent;
630 }
631
632 // Otherwise, this is an ordinary decimal number. Append ".0" if result has no
633 // decimal/fractional part in order to match output of original codegen.
634 if (result.find('.') == string::npos) {
635 result += ".0";
636 }
637
638 return result;
639}
640
641string FloatToString(float value) {
642 string result = SimpleFtoa(value);
643 return PostProcessFloat(result);
644}
645
646string DoubleToString(double value) {
647 string result = SimpleDtoa(value);
648 return PostProcessFloat(result);
649}
650
651string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
652 return orig;
653}
654
655string JSFieldDefault(const FieldDescriptor* field) {
656 assert(field->has_default_value());
657 switch (field->cpp_type()) {
658 case FieldDescriptor::CPPTYPE_INT32:
659 return MaybeNumberString(
660 field, SimpleItoa(field->default_value_int32()));
661 case FieldDescriptor::CPPTYPE_UINT32:
662 // The original codegen is in Java, and Java protobufs store unsigned
663 // integer values as signed integer values. In order to exactly match the
664 // output, we need to reinterpret as base-2 signed. Ugh.
665 return MaybeNumberString(
666 field, SimpleItoa(static_cast<int32>(field->default_value_uint32())));
667 case FieldDescriptor::CPPTYPE_INT64:
668 return MaybeNumberString(
669 field, SimpleItoa(field->default_value_int64()));
670 case FieldDescriptor::CPPTYPE_UINT64:
671 // See above note for uint32 -- reinterpreting as signed.
672 return MaybeNumberString(
673 field, SimpleItoa(static_cast<int64>(field->default_value_uint64())));
674 case FieldDescriptor::CPPTYPE_ENUM:
675 return SimpleItoa(field->default_value_enum()->number());
676 case FieldDescriptor::CPPTYPE_BOOL:
677 return field->default_value_bool() ? "true" : "false";
678 case FieldDescriptor::CPPTYPE_FLOAT:
679 return FloatToString(field->default_value_float());
680 case FieldDescriptor::CPPTYPE_DOUBLE:
681 return DoubleToString(field->default_value_double());
682 case FieldDescriptor::CPPTYPE_STRING:
683 if (field->type() == FieldDescriptor::TYPE_STRING) {
684 return "\"" + EscapeJSString(field->default_value_string(), true) +
685 "\"";
686 } else {
687 return "\"" + EscapeBase64(field->default_value_string()) +
688 "\"";
689 }
690 case FieldDescriptor::CPPTYPE_MESSAGE:
691 return "null";
692 }
693}
694
695string ProtoTypeName(const GeneratorOptions& options,
696 const FieldDescriptor* field) {
697 switch (field->type()) {
698 case FieldDescriptor::TYPE_BOOL:
699 return "bool";
700 case FieldDescriptor::TYPE_INT32:
701 return "int32";
702 case FieldDescriptor::TYPE_UINT32:
703 return "uint32";
704 case FieldDescriptor::TYPE_SINT32:
705 return "sint32";
706 case FieldDescriptor::TYPE_FIXED32:
707 return "fixed32";
708 case FieldDescriptor::TYPE_SFIXED32:
709 return "sfixed32";
710 case FieldDescriptor::TYPE_INT64:
711 return "int64";
712 case FieldDescriptor::TYPE_UINT64:
713 return "uint64";
714 case FieldDescriptor::TYPE_SINT64:
715 return "sint64";
716 case FieldDescriptor::TYPE_FIXED64:
717 return "fixed64";
718 case FieldDescriptor::TYPE_SFIXED64:
719 return "sfixed64";
720 case FieldDescriptor::TYPE_FLOAT:
721 return "float";
722 case FieldDescriptor::TYPE_DOUBLE:
723 return "double";
724 case FieldDescriptor::TYPE_STRING:
725 return "string";
726 case FieldDescriptor::TYPE_BYTES:
727 return "bytes";
728 case FieldDescriptor::TYPE_GROUP:
729 return GetPath(options, field->message_type());
730 case FieldDescriptor::TYPE_ENUM:
731 return GetPath(options, field->enum_type());
732 case FieldDescriptor::TYPE_MESSAGE:
733 return GetPath(options, field->message_type());
734 default:
735 return "";
736 }
737}
738
739string JSIntegerTypeName(const FieldDescriptor* field) {
740 return "number";
741}
742
743string JSTypeName(const GeneratorOptions& options,
744 const FieldDescriptor* field) {
745 switch (field->cpp_type()) {
746 case FieldDescriptor::CPPTYPE_BOOL:
747 return "boolean";
748 case FieldDescriptor::CPPTYPE_INT32:
749 return JSIntegerTypeName(field);
750 case FieldDescriptor::CPPTYPE_INT64:
751 return JSIntegerTypeName(field);
752 case FieldDescriptor::CPPTYPE_UINT32:
753 return JSIntegerTypeName(field);
754 case FieldDescriptor::CPPTYPE_UINT64:
755 return JSIntegerTypeName(field);
756 case FieldDescriptor::CPPTYPE_FLOAT:
757 return "number";
758 case FieldDescriptor::CPPTYPE_DOUBLE:
759 return "number";
760 case FieldDescriptor::CPPTYPE_STRING:
761 return "string";
762 case FieldDescriptor::CPPTYPE_ENUM:
763 return GetPath(options, field->enum_type());
764 case FieldDescriptor::CPPTYPE_MESSAGE:
765 return GetPath(options, field->message_type());
766 default:
767 return "";
768 }
769}
770
771bool HasFieldPresence(const FieldDescriptor* field);
772
773string JSFieldTypeAnnotation(const GeneratorOptions& options,
774 const FieldDescriptor* field,
775 bool force_optional,
776 bool force_present,
777 bool singular_if_not_packed,
778 bool always_singular) {
779 bool is_primitive =
780 (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
781 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE);
782
783 string jstype = JSTypeName(options, field);
784
785 if (field->is_repeated() &&
786 !always_singular &&
787 (field->is_packed() || !singular_if_not_packed)) {
788 if (!is_primitive) {
789 jstype = "!" + jstype;
790 }
791 jstype = "Array.<" + jstype + ">";
792 if (!force_optional) {
793 jstype = "!" + jstype;
794 }
795 }
796
797 if (field->is_optional() && is_primitive &&
798 (!field->has_default_value() || force_optional) && !force_present) {
799 jstype += "?";
800 } else if (field->is_required() && !is_primitive && !force_optional) {
801 jstype = "!" + jstype;
802 }
803
804 if (force_optional && HasFieldPresence(field)) {
805 jstype += "|undefined";
806 }
807 if (force_present && jstype[0] != '!' && !is_primitive) {
808 jstype = "!" + jstype;
809 }
810
811 return jstype;
812}
813
814string JSBinaryReaderMethodType(const FieldDescriptor* field) {
815 string name = field->type_name();
816 if (name[0] >= 'a' && name[0] <= 'z') {
817 name[0] = (name[0] - 'a') + 'A';
818 }
819
820 return name;
821}
822
823string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
824 bool is_writer) {
825 string name = JSBinaryReaderMethodType(field);
826 if (is_writer && field->type() == FieldDescriptor::TYPE_BYTES) {
827 // Override for `bytes` fields: treat string as raw bytes, not base64.
828 name = "BytesRawString";
829 }
830 if (field->is_packed()) {
831 name = "Packed" + name;
832 } else if (is_writer && field->is_repeated()) {
833 name = "Repeated" + name;
834 }
835 return name;
836}
837
838string JSBinaryReaderMethodName(const FieldDescriptor* field) {
839 return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
840}
841
842string JSBinaryWriterMethodName(const FieldDescriptor* field) {
843 return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
844}
845
846string JSReturnClause(const FieldDescriptor* desc) {
847 return "";
848}
849
850string JSReturnDoc(const GeneratorOptions& options,
851 const FieldDescriptor* desc) {
852 return "";
853}
854
855bool HasRepeatedFields(const Descriptor* desc) {
856 for (int i = 0; i < desc->field_count(); i++) {
857 if (desc->field(i)->is_repeated()) {
858 return true;
859 }
860 }
861 return false;
862}
863
864static const char* kRepeatedFieldArrayName = ".repeatedFields_";
865
866string RepeatedFieldsArrayName(const GeneratorOptions& options,
867 const Descriptor* desc) {
868 return HasRepeatedFields(desc) ?
869 (GetPath(options, desc) + kRepeatedFieldArrayName) : "null";
870}
871
872bool HasOneofFields(const Descriptor* desc) {
873 for (int i = 0; i < desc->field_count(); i++) {
874 if (desc->field(i)->containing_oneof()) {
875 return true;
876 }
877 }
878 return false;
879}
880
881static const char* kOneofGroupArrayName = ".oneofGroups_";
882
883string OneofFieldsArrayName(const GeneratorOptions& options,
884 const Descriptor* desc) {
885 return HasOneofFields(desc) ?
886 (GetPath(options, desc) + kOneofGroupArrayName) : "null";
887}
888
889string RepeatedFieldNumberList(const Descriptor* desc) {
890 std::vector<string> numbers;
891 for (int i = 0; i < desc->field_count(); i++) {
892 if (desc->field(i)->is_repeated()) {
893 numbers.push_back(JSFieldIndex(desc->field(i)));
894 }
895 }
896 return "[" + Join(numbers, ",") + "]";
897}
898
899string OneofGroupList(const Descriptor* desc) {
900 // List of arrays (one per oneof), each of which is a list of field indices
901 std::vector<string> oneof_entries;
902 for (int i = 0; i < desc->oneof_decl_count(); i++) {
903 const OneofDescriptor* oneof = desc->oneof_decl(i);
904 if (IgnoreOneof(oneof)) {
905 continue;
906 }
907
908 std::vector<string> oneof_fields;
909 for (int j = 0; j < oneof->field_count(); j++) {
910 if (IgnoreField(oneof->field(j))) {
911 continue;
912 }
913 oneof_fields.push_back(JSFieldIndex(oneof->field(j)));
914 }
915 oneof_entries.push_back("[" + Join(oneof_fields, ",") + "]");
916 }
917 return "[" + Join(oneof_entries, ",") + "]";
918}
919
920string JSOneofArray(const GeneratorOptions& options,
921 const FieldDescriptor* field) {
922 return OneofFieldsArrayName(options, field->containing_type()) + "[" +
923 JSOneofIndex(field->containing_oneof()) + "]";
924}
925
926string RelativeTypeName(const FieldDescriptor* field) {
927 assert(field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM ||
928 field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
929 // For a field with an enum or message type, compute a name relative to the
930 // path name of the message type containing this field.
931 string package = field->file()->package();
932 string containing_type = field->containing_type()->full_name() + ".";
933 string type = (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM) ?
934 field->enum_type()->full_name() : field->message_type()->full_name();
935
936 // |prefix| is advanced as we find separators '.' past the common package
937 // prefix that yield common prefixes in the containing type's name and this
938 // type's name.
939 int prefix = 0;
940 for (int i = 0; i < type.size() && i < containing_type.size(); i++) {
941 if (type[i] != containing_type[i]) {
942 break;
943 }
944 if (type[i] == '.' && i >= package.size()) {
945 prefix = i + 1;
946 }
947 }
948
949 return type.substr(prefix);
950}
951
952string JSExtensionsObjectName(const GeneratorOptions& options,
953 const Descriptor* desc) {
954 if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
955 return "jspb.Message.messageSetExtensions";
956 } else {
957 return GetPath(options, desc) + ".extensions";
958 }
959}
960
961string FieldDefinition(const GeneratorOptions& options,
962 const FieldDescriptor* field) {
963 string qualifier = field->is_repeated() ? "repeated" :
964 (field->is_optional() ? "optional" : "required");
965 string type, name;
966 if (field->type() == FieldDescriptor::TYPE_ENUM ||
967 field->type() == FieldDescriptor::TYPE_MESSAGE) {
968 type = RelativeTypeName(field);
969 name = field->name();
970 } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
971 type = "group";
972 name = field->message_type()->name();
973 } else {
974 type = ProtoTypeName(options, field);
975 name = field->name();
976 }
977 return StringPrintf("%s %s %s = %d;",
978 qualifier.c_str(),
979 type.c_str(),
980 name.c_str(),
981 field->number());
982}
983
984string FieldComments(const FieldDescriptor* field) {
985 string comments;
986 if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
987 comments +=
988 " * Note that Boolean fields may be set to 0/1 when serialized from "
989 "a Java server.\n"
990 " * You should avoid comparisons like {@code val === true/false} in "
991 "those cases.\n";
992 }
993 if (field->is_repeated()) {
994 comments +=
995 " * If you change this array by adding, removing or replacing "
996 "elements, or if you\n"
997 " * replace the array itself, then you must call the setter to "
998 "update it.\n";
999 }
1000 return comments;
1001}
1002
1003bool ShouldGenerateExtension(const FieldDescriptor* field) {
1004 return
1005 field->is_extension() &&
1006 !IgnoreField(field);
1007}
1008
1009bool HasExtensions(const Descriptor* desc) {
1010 if (desc->extension_count() > 0) {
1011 return true;
1012 }
1013 for (int i = 0; i < desc->nested_type_count(); i++) {
1014 if (HasExtensions(desc->nested_type(i))) {
1015 return true;
1016 }
1017 }
1018 return false;
1019}
1020
1021bool HasExtensions(const FileDescriptor* file) {
1022 for (int i = 0; i < file->extension_count(); i++) {
1023 if (ShouldGenerateExtension(file->extension(i))) {
1024 return true;
1025 }
1026 }
1027 for (int i = 0; i < file->message_type_count(); i++) {
1028 if (HasExtensions(file->message_type(i))) {
1029 return true;
1030 }
1031 }
1032 return false;
1033}
1034
1035bool IsExtendable(const Descriptor* desc) {
1036 return desc->extension_range_count() > 0;
1037}
1038
1039// Returns the max index in the underlying data storage array beyond which the
1040// extension object is used.
1041string GetPivot(const Descriptor* desc) {
1042 static const int kDefaultPivot = (1 << 29); // max field number (29 bits)
1043
1044 // Find the max field number
1045 int max_field_number = 0;
1046 for (int i = 0; i < desc->field_count(); i++) {
1047 if (!IgnoreField(desc->field(i)) &&
1048 desc->field(i)->number() > max_field_number) {
1049 max_field_number = desc->field(i)->number();
1050 }
1051 }
1052
1053 int pivot = -1;
1054 if (IsExtendable(desc)) {
1055 pivot = ((max_field_number + 1) < kDefaultPivot) ?
1056 (max_field_number + 1) : kDefaultPivot;
1057 }
1058
1059 return SimpleItoa(pivot);
1060}
1061
1062// Returns true for fields that represent "null" as distinct from the default
1063// value. See https://go/proto3#heading=h.kozewqqcqhuz for more information.
1064bool HasFieldPresence(const FieldDescriptor* field) {
1065 return
1066 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ||
1067 (field->containing_oneof() != NULL) ||
1068 (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3);
1069}
1070
1071// For proto3 fields without presence, returns a string representing the default
1072// value in JavaScript. See https://go/proto3#heading=h.kozewqqcqhuz for more
1073// information.
1074string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) {
1075 switch (field->cpp_type()) {
1076 case FieldDescriptor::CPPTYPE_INT32:
1077 case FieldDescriptor::CPPTYPE_INT64:
1078 case FieldDescriptor::CPPTYPE_UINT32:
1079 case FieldDescriptor::CPPTYPE_UINT64: {
1080 return "0";
1081 }
1082
1083 case FieldDescriptor::CPPTYPE_ENUM:
1084 case FieldDescriptor::CPPTYPE_FLOAT:
1085 case FieldDescriptor::CPPTYPE_DOUBLE:
1086 return "0";
1087
1088 case FieldDescriptor::CPPTYPE_BOOL:
1089 return "false";
1090
1091 case FieldDescriptor::CPPTYPE_STRING:
1092 return "\"\"";
1093
1094 default:
1095 // BYTES and MESSAGE are handled separately.
1096 assert(false);
1097 return "";
1098 }
1099}
1100
1101} // anonymous namespace
1102
1103void Generator::GenerateHeader(const GeneratorOptions& options,
1104 io::Printer* printer) const {
1105 printer->Print("/**\n"
1106 " * @fileoverview\n"
1107 " * @enhanceable\n"
1108 " * @public\n"
1109 " */\n"
1110 "// GENERATED CODE -- DO NOT EDIT!\n"
1111 "\n");
1112}
1113
1114void Generator::FindProvides(const GeneratorOptions& options,
1115 io::Printer* printer,
1116 const vector<const FileDescriptor*>& files,
1117 std::set<string>* provided) const {
1118 for (int i = 0; i < files.size(); i++) {
1119 for (int j = 0; j < files[i]->message_type_count(); j++) {
1120 FindProvidesForMessage(options, printer, files[i]->message_type(j),
1121 provided);
1122 }
1123 for (int j = 0; j < files[i]->enum_type_count(); j++) {
1124 FindProvidesForEnum(options, printer, files[i]->enum_type(j),
1125 provided);
1126 }
1127 }
1128
1129 printer->Print("\n");
1130}
1131
1132void Generator::FindProvidesForMessage(
1133 const GeneratorOptions& options,
1134 io::Printer* printer,
1135 const Descriptor* desc,
1136 std::set<string>* provided) const {
1137 string name = GetPath(options, desc);
1138 provided->insert(name);
1139
1140 for (int i = 0; i < desc->enum_type_count(); i++) {
1141 FindProvidesForEnum(options, printer, desc->enum_type(i),
1142 provided);
1143 }
1144 for (int i = 0; i < desc->nested_type_count(); i++) {
1145 FindProvidesForMessage(options, printer, desc->nested_type(i),
1146 provided);
1147 }
1148}
1149
1150void Generator::FindProvidesForEnum(const GeneratorOptions& options,
1151 io::Printer* printer,
1152 const EnumDescriptor* enumdesc,
1153 std::set<string>* provided) const {
1154 string name = GetPath(options, enumdesc);
1155 provided->insert(name);
1156}
1157
1158void Generator::FindProvidesForFields(
1159 const GeneratorOptions& options,
1160 io::Printer* printer,
1161 const vector<const FieldDescriptor*>& fields,
1162 std::set<string>* provided) const {
1163 for (int i = 0; i < fields.size(); i++) {
1164 const FieldDescriptor* field = fields[i];
1165
1166 if (IgnoreField(field)) {
1167 continue;
1168 }
1169
1170 string name =
1171 GetPath(options, field->file()) + "." + JSObjectFieldName(field);
1172 provided->insert(name);
1173 }
1174}
1175
1176void Generator::GenerateProvides(const GeneratorOptions& options,
1177 io::Printer* printer,
1178 std::set<string>* provided) const {
1179 for (std::set<string>::iterator it = provided->begin();
1180 it != provided->end(); ++it) {
1181 printer->Print("goog.provide('$name$');\n",
1182 "name", *it);
1183 }
1184}
1185
1186void Generator::GenerateRequires(const GeneratorOptions& options,
1187 io::Printer* printer,
1188 const Descriptor* desc,
1189 std::set<string>* provided) const {
1190 std::set<string> required;
1191 std::set<string> forwards;
1192 bool have_message = false;
1193 FindRequiresForMessage(options, desc,
1194 &required, &forwards, &have_message);
1195
1196 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1197 /* require_jspb = */ have_message,
1198 /* require_extension = */ HasExtensions(desc));
1199}
1200
1201void Generator::GenerateRequires(const GeneratorOptions& options,
1202 io::Printer* printer,
1203 const vector<const FileDescriptor*>& files,
1204 std::set<string>* provided) const {
1205 std::set<string> required;
1206 std::set<string> forwards;
1207 bool have_extensions = false;
1208 bool have_message = false;
1209
1210 for (int i = 0; i < files.size(); i++) {
1211 for (int j = 0; j < files[i]->message_type_count(); j++) {
1212 FindRequiresForMessage(options,
1213 files[i]->message_type(j),
1214 &required, &forwards, &have_message);
1215 }
1216 if (!have_extensions && HasExtensions(files[i])) {
1217 have_extensions = true;
1218 }
1219
1220 for (int j = 0; j < files[i]->extension_count(); j++) {
1221 const FieldDescriptor* extension = files[i]->extension(j);
1222 if (IgnoreField(extension)) {
1223 continue;
1224 }
1225 if (extension->containing_type()->full_name() !=
1226 "google.protobuf.bridge.MessageSet") {
1227 required.insert(GetPath(options, extension->containing_type()));
1228 }
1229 FindRequiresForField(options, extension, &required, &forwards);
1230 have_extensions = true;
1231 }
1232 }
1233
1234 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1235 /* require_jspb = */ have_message,
1236 /* require_extension = */ have_extensions);
1237}
1238
1239void Generator::GenerateRequires(const GeneratorOptions& options,
1240 io::Printer* printer,
1241 const vector<const FieldDescriptor*>& fields,
1242 std::set<string>* provided) const {
1243 std::set<string> required;
1244 std::set<string> forwards;
1245 for (int i = 0; i < fields.size(); i++) {
1246 const FieldDescriptor* field = fields[i];
1247 if (IgnoreField(field)) {
1248 continue;
1249 }
1250 FindRequiresForExtension(options, field, &required, &forwards);
1251 }
1252
1253 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1254 /* require_jspb = */ false,
1255 /* require_extension = */ fields.size() > 0);
1256}
1257
1258void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
1259 io::Printer* printer,
1260 std::set<string>* required,
1261 std::set<string>* forwards,
1262 std::set<string>* provided,
1263 bool require_jspb,
1264 bool require_extension) const {
1265 if (require_jspb) {
1266 printer->Print(
1267 "goog.require('jspb.Message');\n");
1268 if (options.binary) {
1269 printer->Print(
1270 "goog.require('jspb.BinaryReader');\n"
1271 "goog.require('jspb.BinaryWriter');\n");
1272 }
1273 }
1274 if (require_extension) {
1275 printer->Print(
1276 "goog.require('jspb.ExtensionFieldInfo');\n");
1277 }
1278
1279 std::set<string>::iterator it;
1280 for (it = required->begin(); it != required->end(); ++it) {
1281 if (provided->find(*it) != provided->end()) {
1282 continue;
1283 }
1284 printer->Print("goog.require('$name$');\n",
1285 "name", *it);
1286 }
1287
1288 printer->Print("\n");
1289
1290 for (it = forwards->begin(); it != forwards->end(); ++it) {
1291 if (provided->find(*it) != provided->end()) {
1292 continue;
1293 }
1294 printer->Print("goog.forwardDeclare('$name$');\n",
1295 "name", *it);
1296 }
1297}
1298
1299bool NamespaceOnly(const Descriptor* desc) {
1300 return false;
1301}
1302
1303void Generator::FindRequiresForMessage(
1304 const GeneratorOptions& options,
1305 const Descriptor* desc,
1306 std::set<string>* required,
1307 std::set<string>* forwards,
1308 bool* have_message) const {
1309
1310
1311 if (!NamespaceOnly(desc)) {
1312 *have_message = true;
1313 for (int i = 0; i < desc->field_count(); i++) {
1314 const FieldDescriptor* field = desc->field(i);
1315 if (IgnoreField(field)) {
1316 continue;
1317 }
1318 FindRequiresForField(options, field, required, forwards);
1319 }
1320 }
1321
1322 for (int i = 0; i < desc->extension_count(); i++) {
1323 const FieldDescriptor* field = desc->extension(i);
1324 if (IgnoreField(field)) {
1325 continue;
1326 }
1327 FindRequiresForExtension(options, field, required, forwards);
1328 }
1329
1330 for (int i = 0; i < desc->nested_type_count(); i++) {
1331 FindRequiresForMessage(options, desc->nested_type(i), required, forwards,
1332 have_message);
1333 }
1334}
1335
1336void Generator::FindRequiresForField(const GeneratorOptions& options,
1337 const FieldDescriptor* field,
1338 std::set<string>* required,
1339 std::set<string>* forwards) const {
1340 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1341 // N.B.: file-level extensions with enum type do *not* create
1342 // dependencies, as per original codegen.
1343 !(field->is_extension() && field->extension_scope() == NULL)) {
1344 if (options.add_require_for_enums) {
1345 required->insert(GetPath(options, field->enum_type()));
1346 } else {
1347 forwards->insert(GetPath(options, field->enum_type()));
1348 }
1349 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1350 required->insert(GetPath(options, field->message_type()));
1351 }
1352}
1353
1354void Generator::FindRequiresForExtension(const GeneratorOptions& options,
1355 const FieldDescriptor* field,
1356 std::set<string>* required,
1357 std::set<string>* forwards) const {
1358 if (field->containing_type()->full_name() != "google.protobuf.bridge.MessageSet") {
1359 required->insert(GetPath(options, field->containing_type()));
1360 }
1361 FindRequiresForField(options, field, required, forwards);
1362}
1363
1364void Generator::GenerateTestOnly(const GeneratorOptions& options,
1365 io::Printer* printer) const {
1366 if (options.testonly) {
1367 printer->Print("goog.setTestOnly();\n\n");
1368 }
1369 printer->Print("\n");
1370}
1371
1372void Generator::GenerateClassesAndEnums(const GeneratorOptions& options,
1373 io::Printer* printer,
1374 const FileDescriptor* file) const {
1375 for (int i = 0; i < file->message_type_count(); i++) {
1376 GenerateClass(options, printer, file->message_type(i));
1377 }
1378 for (int i = 0; i < file->enum_type_count(); i++) {
1379 GenerateEnum(options, printer, file->enum_type(i));
1380 }
1381}
1382
1383void Generator::GenerateClass(const GeneratorOptions& options,
1384 io::Printer* printer,
1385 const Descriptor* desc) const {
1386 if (!NamespaceOnly(desc)) {
1387 printer->Print("\n");
1388 GenerateClassConstructor(options, printer, desc);
1389 GenerateClassFieldInfo(options, printer, desc);
1390
1391
1392 GenerateClassToObject(options, printer, desc);
1393 if (options.binary) {
1394 // These must come *before* the extension-field info generation in
1395 // GenerateClassRegistration so that references to the binary
1396 // serialization/deserialization functions may be placed in the extension
1397 // objects.
1398 GenerateClassDeserializeBinary(options, printer, desc);
1399 GenerateClassSerializeBinary(options, printer, desc);
1400 }
1401 GenerateClassClone(options, printer, desc);
1402 GenerateClassRegistration(options, printer, desc);
1403 GenerateClassFields(options, printer, desc);
1404 if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.MessageSet") {
1405 GenerateClassExtensionFieldInfo(options, printer, desc);
1406 }
1407 }
1408
1409 // Recurse on nested types.
1410 for (int i = 0; i < desc->enum_type_count(); i++) {
1411 GenerateEnum(options, printer, desc->enum_type(i));
1412 }
1413 for (int i = 0; i < desc->nested_type_count(); i++) {
1414 GenerateClass(options, printer, desc->nested_type(i));
1415 }
1416}
1417
1418void Generator::GenerateClassConstructor(const GeneratorOptions& options,
1419 io::Printer* printer,
1420 const Descriptor* desc) const {
1421 printer->Print(
1422 "/**\n"
1423 " * Generated by JsPbCodeGenerator.\n"
1424 " * @param {Array=} opt_data Optional initial data array, typically "
1425 "from a\n"
1426 " * server response, or constructed directly in Javascript. The array "
1427 "is used\n"
1428 " * in place and becomes part of the constructed object. It is not "
1429 "cloned.\n"
1430 " * If no data is provided, the constructed object will be empty, but "
1431 "still\n"
1432 " * valid.\n"
1433 " * @extends {jspb.Message}\n"
1434 " * @constructor\n"
1435 " */\n"
1436 "$classname$ = function(opt_data) {\n",
1437 "classname", GetPath(options, desc));
1438 string message_id = GetMessageId(desc);
1439 printer->Print(
1440 " jspb.Message.initialize(this, opt_data, $messageId$, $pivot$, "
1441 "$rptfields$, $oneoffields$);\n",
1442 "messageId", !message_id.empty() ?
1443 ("'" + message_id + "'") :
1444 (IsResponse(desc) ? "''" : "0"),
1445 "pivot", GetPivot(desc),
1446 "rptfields", RepeatedFieldsArrayName(options, desc),
1447 "oneoffields", OneofFieldsArrayName(options, desc));
1448 printer->Print(
1449 "};\n"
1450 "goog.inherits($classname$, jspb.Message);\n"
1451 "if (goog.DEBUG && !COMPILED) {\n"
1452 " $classname$.displayName = '$classname$';\n"
1453 "}\n",
1454 "classname", GetPath(options, desc));
1455}
1456
1457void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
1458 io::Printer* printer,
1459 const Descriptor* desc) const {
1460 if (HasRepeatedFields(desc)) {
1461 printer->Print(
1462 "/**\n"
1463 " * List of repeated fields within this message type.\n"
1464 " * @private {!Array<number>}\n"
1465 " * @const\n"
1466 " */\n"
1467 "$classname$$rptfieldarray$ = $rptfields$;\n"
1468 "\n",
1469 "classname", GetPath(options, desc),
1470 "rptfieldarray", kRepeatedFieldArrayName,
1471 "rptfields", RepeatedFieldNumberList(desc));
1472 }
1473
1474 if (HasOneofFields(desc)) {
1475 printer->Print(
1476 "/**\n"
1477 " * Oneof group definitions for this message. Each group defines the "
1478 "field\n"
1479 " * numbers belonging to that group. When of these fields' value is "
1480 "set, all\n"
1481 " * other fields in the group are cleared. During deserialization, if "
1482 "multiple\n"
1483 " * fields are encountered for a group, only the last value seen will "
1484 "be kept.\n"
1485 " * @private {!Array<!Array<number>>}\n"
1486 " * @const\n"
1487 " */\n"
1488 "$classname$$oneofgrouparray$ = $oneofgroups$;\n"
1489 "\n",
1490 "classname", GetPath(options, desc),
1491 "oneofgrouparray", kOneofGroupArrayName,
1492 "oneofgroups", OneofGroupList(desc));
1493
1494 for (int i = 0; i < desc->oneof_decl_count(); i++) {
1495 if (IgnoreOneof(desc->oneof_decl(i))) {
1496 continue;
1497 }
1498 GenerateOneofCaseDefinition(options, printer, desc->oneof_decl(i));
1499 }
1500 }
1501}
1502
1503void Generator::GenerateClassXid(const GeneratorOptions& options,
1504 io::Printer* printer,
1505 const Descriptor* desc) const {
1506 printer->Print(
1507 "\n"
1508 "\n"
1509 "$class$.prototype.messageXid = xid('$class$');\n",
1510 "class", GetPath(options, desc));
1511}
1512
1513void Generator::GenerateOneofCaseDefinition(
1514 const GeneratorOptions& options,
1515 io::Printer* printer,
1516 const OneofDescriptor* oneof) const {
1517 printer->Print(
1518 "/**\n"
1519 " * @enum {number}\n"
1520 " */\n"
1521 "$classname$.$oneof$Case = {\n"
1522 " $upcase$_NOT_SET: 0",
1523 "classname", GetPath(options, oneof->containing_type()),
1524 "oneof", JSOneofName(oneof),
1525 "upcase", ToEnumCase(oneof->name()));
1526
1527 for (int i = 0; i < oneof->field_count(); i++) {
1528 if (IgnoreField(oneof->field(i))) {
1529 continue;
1530 }
1531
1532 printer->Print(
1533 ",\n"
1534 " $upcase$: $number$",
1535 "upcase", ToEnumCase(oneof->field(i)->name()),
1536 "number", JSFieldIndex(oneof->field(i)));
1537 }
1538
1539 printer->Print(
1540 "\n"
1541 "};\n"
1542 "\n"
1543 "/**\n"
1544 " * @return {$class$.$oneof$Case}\n"
1545 " */\n"
1546 "$class$.prototype.get$oneof$Case = function() {\n"
1547 " return /** @type {$class$.$oneof$Case} */(jspb.Message."
1548 "computeOneofCase(this, $class$.oneofGroups_[$oneofindex$]));\n"
1549 "};\n"
1550 "\n",
1551 "class", GetPath(options, oneof->containing_type()),
1552 "oneof", JSOneofName(oneof),
1553 "oneofindex", JSOneofIndex(oneof));
1554}
1555
1556void Generator::GenerateClassToObject(const GeneratorOptions& options,
1557 io::Printer* printer,
1558 const Descriptor* desc) const {
1559 printer->Print(
1560 "\n"
1561 "\n"
1562 "if (jspb.Message.GENERATE_TO_OBJECT) {\n"
1563 "/**\n"
1564 " * Creates an object representation of this proto suitable for use in "
1565 "Soy templates.\n"
1566 " * Field names that are reserved in JavaScript and will be renamed to "
1567 "pb_name.\n"
1568 " * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.\n"
1569 " * For the list of reserved names please see:\n"
1570 " * com.google.apps.jspb.JsClassTemplate.JS_RESERVED_WORDS.\n"
1571 " * @param {boolean=} opt_includeInstance Whether to include the JSPB "
1572 "instance\n"
1573 " * for transitional soy proto support: http://goto/soy-param-"
1574 "migration\n"
1575 " * @return {!Object}\n"
1576 " */\n"
1577 "$classname$.prototype.toObject = function(opt_includeInstance) {\n"
1578 " return $classname$.toObject(opt_includeInstance, this);\n"
1579 "};\n"
1580 "\n"
1581 "\n"
1582 "/**\n"
1583 " * Static version of the {@see toObject} method.\n"
1584 " * @param {boolean|undefined} includeInstance Whether to include the "
1585 "JSPB\n"
1586 " * instance for transitional soy proto support:\n"
1587 " * http://goto/soy-param-migration\n"
1588 " * @param {!$classname$} msg The msg instance to transform.\n"
1589 " * @return {!Object}\n"
1590 " */\n"
1591 "$classname$.toObject = function(includeInstance, msg) {\n"
1592 " var f, obj = {",
1593 "classname", GetPath(options, desc));
1594
1595 bool first = true;
1596 for (int i = 0; i < desc->field_count(); i++) {
1597 const FieldDescriptor* field = desc->field(i);
1598 if (IgnoreField(field)) {
1599 continue;
1600 }
1601
1602 if (!first) {
1603 printer->Print(",\n ");
1604 } else {
1605 printer->Print("\n ");
1606 first = false;
1607 }
1608
1609 GenerateClassFieldToObject(options, printer, field);
1610 }
1611
1612 if (!first) {
1613 printer->Print("\n };\n\n");
1614 } else {
1615 printer->Print("\n\n };\n\n");
1616 }
1617
1618 if (IsExtendable(desc)) {
1619 printer->Print(
1620 " jspb.Message.toObjectExtension(/** @type {!jspb.Message} */ (msg), "
1621 "obj,\n"
1622 " $extObject$, $class$.prototype.getExtension,\n"
1623 " includeInstance);\n",
1624 "extObject", JSExtensionsObjectName(options, desc),
1625 "class", GetPath(options, desc));
1626 }
1627
1628 printer->Print(
1629 " if (includeInstance) {\n"
1630 " obj.$$jspbMessageInstance = msg\n"
1631 " }\n"
1632 " return obj;\n"
1633 "};\n"
1634 "}\n"
1635 "\n"
1636 "\n",
1637 "classname", GetPath(options, desc));
1638}
1639
1640void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
1641 io::Printer* printer,
1642 const FieldDescriptor* field) const {
1643 printer->Print("$fieldname$: ",
1644 "fieldname", JSObjectFieldName(field));
1645
1646 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1647 // Message field.
1648 if (field->is_repeated()) {
1649 {
1650 printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
1651 " $type$.toObject, includeInstance)",
1652 "getter", JSGetterName(field),
1653 "type", GetPath(options, field->message_type()));
1654 }
1655 } else {
1656 printer->Print("(f = msg.get$getter$()) && "
1657 "$type$.toObject(includeInstance, f)",
1658 "getter", JSGetterName(field),
1659 "type", GetPath(options, field->message_type()));
1660 }
1661 } else {
1662 // Simple field (singular or repeated).
1663 if (!HasFieldPresence(field) && !field->is_repeated()) {
1664 // Delegate to the generated get<field>() method in order not to duplicate
1665 // the proto3-field-default-value logic here.
1666 printer->Print("msg.get$getter$()",
1667 "getter", JSGetterName(field));
1668 } else {
1669 if (field->has_default_value()) {
1670 printer->Print("jspb.Message.getField(msg, $index$) != null ? "
1671 "jspb.Message.getField(msg, $index$) : $defaultValue$",
1672 "index", JSFieldIndex(field),
1673 "defaultValue", JSFieldDefault(field));
1674 } else {
1675 printer->Print("jspb.Message.getField(msg, $index$)",
1676 "index", JSFieldIndex(field));
1677 }
1678 }
1679 }
1680}
1681
1682void Generator::GenerateClassFromObject(const GeneratorOptions& options,
1683 io::Printer* printer,
1684 const Descriptor* desc) const {
1685 printer->Print(
1686 "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
1687 "/**\n"
1688 " * Loads data from an object into a new instance of this proto.\n"
1689 " * @param {!Object} obj The object representation of this proto to\n"
1690 " * load the data from.\n"
1691 " * @return {!$classname$}\n"
1692 " */\n"
1693 "$classname$.fromObject = function(obj) {\n"
1694 " var f, msg = new $classname$();\n",
1695 "classname", GetPath(options, desc));
1696
1697 for (int i = 0; i < desc->field_count(); i++) {
1698 const FieldDescriptor* field = desc->field(i);
1699 GenerateClassFieldFromObject(options, printer, field);
1700 }
1701
1702 printer->Print(
1703 " return msg;\n"
1704 "};\n"
1705 "}\n");
1706}
1707
1708void Generator::GenerateClassFieldFromObject(
1709 const GeneratorOptions& options,
1710 io::Printer* printer,
1711 const FieldDescriptor* field) const {
1712 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1713 // Message field (singular or repeated)
1714 if (field->is_repeated()) {
1715 {
1716 printer->Print(
1717 " goog.isDef(obj.$name$) && "
1718 "jspb.Message.setRepeatedWrapperField(\n"
1719 " msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
1720 " return $fieldclass$.fromObject(i);\n"
1721 " }));\n",
1722 "name", JSObjectFieldName(field),
1723 "index", JSFieldIndex(field),
1724 "fieldclass", GetPath(options, field->message_type()));
1725 }
1726 } else {
1727 printer->Print(
1728 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
1729 " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
1730 "name", JSObjectFieldName(field),
1731 "index", JSFieldIndex(field),
1732 "fieldclass", GetPath(options, field->message_type()));
1733 }
1734 } else {
1735 // Simple (primitive) field.
1736 printer->Print(
1737 " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
1738 "obj.$name$);\n",
1739 "name", JSObjectFieldName(field),
1740 "index", JSFieldIndex(field));
1741 }
1742}
1743
1744void Generator::GenerateClassClone(const GeneratorOptions& options,
1745 io::Printer* printer,
1746 const Descriptor* desc) const {
1747 printer->Print(
1748 "/**\n"
1749 " * Creates a deep clone of this proto. No data is shared with the "
1750 "original.\n"
1751 " * @return {!$name$} The clone.\n"
1752 " */\n"
1753 "$name$.prototype.cloneMessage = function() {\n"
1754 " return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n"
1755 "};\n\n\n",
1756 "name", GetPath(options, desc));
1757}
1758
1759void Generator::GenerateClassRegistration(const GeneratorOptions& options,
1760 io::Printer* printer,
1761 const Descriptor* desc) const {
1762 // Register any extensions defined inside this message type.
1763 for (int i = 0; i < desc->extension_count(); i++) {
1764 const FieldDescriptor* extension = desc->extension(i);
1765 if (ShouldGenerateExtension(extension)) {
1766 GenerateExtension(options, printer, extension);
1767 }
1768 }
1769
1770}
1771
1772void Generator::GenerateClassFields(const GeneratorOptions& options,
1773 io::Printer* printer,
1774 const Descriptor* desc) const {
1775 for (int i = 0; i < desc->field_count(); i++) {
1776 if (!IgnoreField(desc->field(i))) {
1777 GenerateClassField(options, printer, desc->field(i));
1778 }
1779 }
1780}
1781
1782void Generator::GenerateClassField(const GeneratorOptions& options,
1783 io::Printer* printer,
1784 const FieldDescriptor* field) const {
1785 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1786 printer->Print(
1787 "/**\n"
1788 " * $fielddef$\n"
1789 "$comment$"
1790 " * @return {$type$}\n"
1791 " */\n",
1792 "fielddef", FieldDefinition(options, field),
1793 "comment", FieldComments(field),
1794 "type", JSFieldTypeAnnotation(options, field,
1795 /* force_optional = */ false,
1796 /* force_present = */ false,
1797 /* singular_if_not_packed = */ false,
1798 /* always_singular = */ false));
1799 printer->Print(
1800 "$class$.prototype.get$name$ = function() {\n"
1801 " return /** @type{$type$} */ (\n"
1802 " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
1803 "$index$$required$));\n"
1804 "};\n"
1805 "\n"
1806 "\n",
1807 "class", GetPath(options, field->containing_type()),
1808 "name", JSGetterName(field),
1809 "type", JSFieldTypeAnnotation(options, field,
1810 /* force_optional = */ false,
1811 /* force_present = */ false,
1812 /* singular_if_not_packed = */ false,
1813 /* always_singular = */ false),
1814 "rpt", (field->is_repeated() ? "Repeated" : ""),
1815 "index", JSFieldIndex(field),
1816 "wrapperclass", GetPath(options, field->message_type()),
1817 "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
1818 ", 1" : ""));
1819 printer->Print(
1820 "/** @param {$optionaltype$} value $returndoc$ */\n"
1821 "$class$.prototype.set$name$ = function(value) {\n"
1822 " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
1823 "optionaltype",
1824 JSFieldTypeAnnotation(options, field,
1825 /* force_optional = */ true,
1826 /* force_present = */ false,
1827 /* singular_if_not_packed = */ false,
1828 /* always_singular = */ false),
1829 "returndoc", JSReturnDoc(options, field),
1830 "class", GetPath(options, field->containing_type()),
1831 "name", JSGetterName(field),
1832 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
1833 "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
1834
1835 printer->Print(
1836 "this, $index$$oneofgroup$, value);$returnvalue$\n"
1837 "};\n"
1838 "\n"
1839 "\n",
1840 "index", JSFieldIndex(field),
1841 "oneofgroup", (field->containing_oneof() ?
1842 (", " + JSOneofArray(options, field)) : ""),
1843 "returnvalue", JSReturnClause(field));
1844
1845 printer->Print(
1846 "$class$.prototype.clear$name$ = function() {\n"
1847 " this.set$name$($clearedvalue$);$returnvalue$\n"
1848 "};\n"
1849 "\n"
1850 "\n",
1851 "class", GetPath(options, field->containing_type()),
1852 "name", JSGetterName(field),
1853 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
1854 "returnvalue", JSReturnClause(field));
1855
1856 } else {
1857 string typed_annotation;
1858
1859 // Simple (primitive) field, either singular or repeated.
1860 {
1861 typed_annotation = JSFieldTypeAnnotation(options, field,
1862 /* force_optional = */ false,
1863 /* force_present = */ !HasFieldPresence(field),
1864 /* singular_if_not_packed = */ false,
1865 /* always_singular = */ false),
1866 printer->Print(
1867 "/**\n"
1868 " * $fielddef$\n"
1869 "$comment$"
1870 " * @return {$type$}\n"
1871 " */\n",
1872 "fielddef", FieldDefinition(options, field),
1873 "comment", FieldComments(field),
1874 "type", typed_annotation);
1875 }
1876
1877 printer->Print(
1878 "$class$.prototype.get$name$ = function() {\n",
1879 "class", GetPath(options, field->containing_type()),
1880 "name", JSGetterName(field));
1881
1882 {
1883 printer->Print(
1884 " return /** @type {$type$} */ (",
1885 "type", typed_annotation);
1886 }
1887
1888 // For proto3 fields without presence, use special getters that will return
1889 // defaults when the field is unset, possibly constructing a value if
1890 // required.
1891 if (!HasFieldPresence(field) && !field->is_repeated()) {
1892 printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)",
1893 "index", JSFieldIndex(field),
1894 "default", Proto3PrimitiveFieldDefault(field));
1895 } else {
1896 if (field->has_default_value()) {
1897 printer->Print("jspb.Message.getField(this, $index$) != null ? "
1898 "jspb.Message.getField(this, $index$) : $defaultValue$",
1899 "index", JSFieldIndex(field),
1900 "defaultValue", JSFieldDefault(field));
1901 } else {
1902 printer->Print("jspb.Message.getField(this, $index$)",
1903 "index", JSFieldIndex(field));
1904 }
1905 }
1906
1907 {
1908 printer->Print(
1909 ");\n"
1910 "};\n"
1911 "\n"
1912 "\n");
1913 }
1914
1915 {
1916 printer->Print(
1917 "/** @param {$optionaltype$} value $returndoc$ */\n",
1918 "optionaltype",
1919 JSFieldTypeAnnotation(options, field,
1920 /* force_optional = */ true,
1921 /* force_present = */ !HasFieldPresence(field),
1922 /* singular_if_not_packed = */ false,
1923 /* always_singular = */ false),
1924 "returndoc", JSReturnDoc(options, field));
1925 }
1926
1927 printer->Print(
1928 "$class$.prototype.set$name$ = function(value) {\n"
1929 " jspb.Message.set$oneoftag$Field(this, $index$",
1930 "class", GetPath(options, field->containing_type()),
1931 "name", JSGetterName(field),
1932 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
1933 "index", JSFieldIndex(field));
1934 printer->Print(
1935 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
1936 "};\n"
1937 "\n"
1938 "\n",
1939 "type", "",
1940 "typeclose", "",
1941 "oneofgroup",
1942 (field->containing_oneof() ? (", " + JSOneofArray(options, field))
1943 : ""),
1944 "returnvalue", JSReturnClause(field), "rptvalueinit",
1945 (field->is_repeated() ? " || []" : ""));
1946
1947
1948 if (HasFieldPresence(field)) {
1949 printer->Print(
1950 "$class$.prototype.clear$name$ = function() {\n"
1951 " jspb.Message.set$oneoftag$Field(this, $index$$oneofgroup$, ",
1952 "class", GetPath(options, field->containing_type()),
1953 "name", JSGetterName(field),
1954 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
1955 "oneofgroup", (field->containing_oneof() ?
1956 (", " + JSOneofArray(options, field)) : ""),
1957 "index", JSFieldIndex(field));
1958 printer->Print(
1959 "$clearedvalue$);$returnvalue$\n"
1960 "};\n"
1961 "\n"
1962 "\n",
1963 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
1964 "returnvalue", JSReturnClause(field));
1965 }
1966 }
1967}
1968
1969void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
1970 io::Printer* printer,
1971 const Descriptor* desc) const {
1972 if (IsExtendable(desc)) {
1973 printer->Print(
1974 "\n"
1975 "/**\n"
1976 " * The extensions registered with this message class. This is a "
1977 "map of\n"
1978 " * extension field number to fieldInfo object.\n"
1979 " *\n"
1980 " * For example:\n"
1981 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
1982 "ctor: proto.example.MyMessage} }\n"
1983 " *\n"
1984 " * fieldName contains the JsCompiler renamed field name property "
1985 "so that it\n"
1986 " * works in OPTIMIZED mode.\n"
1987 " *\n"
1988 " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
1989 " */\n"
1990 "$class$.extensions = {};\n"
1991 "\n",
1992 "class", GetPath(options, desc));
1993 }
1994}
1995
1996
1997void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
1998 io::Printer* printer,
1999 const Descriptor* desc) const {
2000 // TODO(cfallin): Handle lazy decoding when requested by field option and/or
2001 // by default for 'bytes' fields and packed repeated fields.
2002
2003 printer->Print(
2004 "/**\n"
2005 " * Deserializes binary data (in protobuf wire format).\n"
2006 " * @param {jspb.ByteSource} bytes The bytes to deserialize.\n"
2007 " * @return {!$class$}\n"
2008 " */\n"
2009 "$class$.deserializeBinary = function(bytes) {\n"
2010 " var reader = new jspb.BinaryReader(bytes);\n"
2011 " var msg = new $class$;\n"
2012 " return $class$.deserializeBinaryFromReader(msg, reader);\n"
2013 "};\n"
2014 "\n"
2015 "\n"
2016 "/**\n"
2017 " * Deserializes binary data (in protobuf wire format) from the\n"
2018 " * given reader into the given message object.\n"
2019 " * @param {!$class$} msg The message object to deserialize into.\n"
2020 " * @param {!jspb.BinaryReader} reader The BinaryReader to use.\n"
2021 " * @return {!$class$}\n"
2022 " */\n"
2023 "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
2024 " while (reader.nextField()) {\n"
2025 " if (reader.isEndGroup()) {\n"
2026 " break;\n"
2027 " }\n"
2028 " var field = reader.getFieldNumber();\n"
2029 " switch (field) {\n",
2030 "class", GetPath(options, desc));
2031
2032 for (int i = 0; i < desc->field_count(); i++) {
2033 GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
2034 }
2035
2036 printer->Print(
2037 " default:\n");
2038 if (IsExtendable(desc)) {
2039 printer->Print(
2040 " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n"
2041 " $class$.prototype.getExtension,\n"
2042 " $class$.prototype.setExtension);\n"
2043 " break;\n",
2044 "extobj", JSExtensionsObjectName(options, desc),
2045 "class", GetPath(options, desc));
2046 } else {
2047 printer->Print(
2048 " reader.skipField();\n"
2049 " break;\n");
2050 }
2051
2052 printer->Print(
2053 " }\n"
2054 " }\n"
2055 " return msg;\n"
2056 "};\n"
2057 "\n"
2058 "\n");
2059}
2060
2061void Generator::GenerateClassDeserializeBinaryField(
2062 const GeneratorOptions& options,
2063 io::Printer* printer,
2064 const FieldDescriptor* field) const {
2065
2066 printer->Print(" case $num$:\n",
2067 "num", SimpleItoa(field->number()));
2068
2069 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2070 printer->Print(
2071 " var value = new $fieldclass$;\n"
2072 " reader.read$msgOrGroup$($grpfield$value,"
2073 "$fieldclass$.deserializeBinaryFromReader);\n",
2074 "fieldclass", GetPath(options, field->message_type()),
2075 "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2076 "Group" : "Message",
2077 "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2078 (SimpleItoa(field->number()) + ", ") : "");
2079 } else {
2080 printer->Print(
2081 " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n",
2082 "fieldtype", JSFieldTypeAnnotation(options, field, false, true,
2083 /* singular_if_not_packed = */ true,
2084 /* always_singular = */ false),
2085 "reader", JSBinaryReaderMethodName(field));
2086 }
2087
2088 if (field->is_repeated() && !field->is_packed()) {
2089 // Repeated fields receive a |value| one at at a time; append to array
2090 // returned by get$name$().
2091 printer->Print(
2092 " msg.get$name$().push(value);\n",
2093 "name", JSGetterName(field));
2094 } else {
2095 // Singular fields, and packed repeated fields, receive a |value| either as
2096 // the field's value or as the array of all the field's values; set this as
2097 // the field's value directly.
2098 printer->Print(
2099 " msg.set$name$(value);\n",
2100 "name", JSGetterName(field));
2101 }
2102
2103 printer->Print(" break;\n");
2104}
2105
2106void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
2107 io::Printer* printer,
2108 const Descriptor* desc) const {
2109 printer->Print(
2110 "/**\n"
2111 " * Class method variant: serializes the given message to binary data\n"
2112 " * (in protobuf wire format), writing to the given BinaryWriter.\n"
2113 " * @param {!$class$} message\n"
2114 " * @param {!jspb.BinaryWriter} writer\n"
2115 " */\n"
2116 "$class$.serializeBinaryToWriter = function(message, "
2117 "writer) {\n"
2118 " message.serializeBinaryToWriter(writer);\n"
2119 "};\n"
2120 "\n"
2121 "\n"
2122 "/**\n"
2123 " * Serializes the message to binary data (in protobuf wire format).\n"
2124 " * @return {!Uint8Array}\n"
2125 " */\n"
2126 "$class$.prototype.serializeBinary = function() {\n"
2127 " var writer = new jspb.BinaryWriter();\n"
2128 " this.serializeBinaryToWriter(writer);\n"
2129 " return writer.getResultBuffer();\n"
2130 "};\n"
2131 "\n"
2132 "\n"
2133 "/**\n"
2134 " * Serializes the message to binary data (in protobuf wire format),\n"
2135 " * writing to the given BinaryWriter.\n"
2136 " * @param {!jspb.BinaryWriter} writer\n"
2137 " */\n"
2138 "$class$.prototype.serializeBinaryToWriter = function (writer) {\n"
2139 " var f = undefined;\n",
2140 "class", GetPath(options, desc));
2141
2142 for (int i = 0; i < desc->field_count(); i++) {
2143 GenerateClassSerializeBinaryField(options, printer, desc->field(i));
2144 }
2145
2146 if (IsExtendable(desc)) {
2147 printer->Print(
2148 " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n"
2149 " $class$.prototype.getExtension);\n",
2150 "extobj", JSExtensionsObjectName(options, desc),
2151 "class", GetPath(options, desc));
2152 }
2153
2154 printer->Print(
2155 "};\n"
2156 "\n"
2157 "\n");
2158}
2159
2160void Generator::GenerateClassSerializeBinaryField(
2161 const GeneratorOptions& options,
2162 io::Printer* printer,
2163 const FieldDescriptor* field) const {
2164 printer->Print(
2165 " f = this.get$name$();\n",
2166 "name", JSGetterName(field));
2167
2168 if (field->is_repeated()) {
2169 printer->Print(
2170 " if (f.length > 0) {\n");
2171 } else {
2172 if (HasFieldPresence(field)) {
2173 printer->Print(
2174 " if (f != null) {\n");
2175 } else {
2176 // No field presence: serialize onto the wire only if value is
2177 // non-default. Defaults are documented here:
2178 // https://goto.google.com/lhdfm
2179 switch (field->cpp_type()) {
2180 case FieldDescriptor::CPPTYPE_INT32:
2181 case FieldDescriptor::CPPTYPE_INT64:
2182 case FieldDescriptor::CPPTYPE_UINT32:
2183 case FieldDescriptor::CPPTYPE_UINT64: {
2184 {
2185 printer->Print(" if (f !== 0) {\n");
2186 }
2187 break;
2188 }
2189
2190 case FieldDescriptor::CPPTYPE_ENUM:
2191 case FieldDescriptor::CPPTYPE_FLOAT:
2192 case FieldDescriptor::CPPTYPE_DOUBLE:
2193 printer->Print(
2194 " if (f !== 0.0) {\n");
2195 break;
2196 case FieldDescriptor::CPPTYPE_BOOL:
2197 printer->Print(
2198 " if (f) {\n");
2199 break;
2200 case FieldDescriptor::CPPTYPE_STRING:
2201 printer->Print(
2202 " if (f.length > 0) {\n");
2203 break;
2204 default:
2205 assert(false);
2206 break;
2207 }
2208 }
2209 }
2210
2211 printer->Print(
2212 " writer.$writer$(\n"
2213 " $index$,\n"
2214 " f",
2215 "writer", JSBinaryWriterMethodName(field),
2216 "name", JSGetterName(field),
2217 "index", SimpleItoa(field->number()));
2218
2219 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2220 printer->Print(
2221 ",\n"
2222 " $submsg$.serializeBinaryToWriter\n",
2223 "submsg", GetPath(options, field->message_type()));
2224 } else {
2225 printer->Print("\n");
2226 }
2227 printer->Print(
2228 " );\n"
2229 " }\n");
2230}
2231
2232void Generator::GenerateEnum(const GeneratorOptions& options,
2233 io::Printer* printer,
2234 const EnumDescriptor* enumdesc) const {
2235 printer->Print(
2236 "/**\n"
2237 " * @enum {number}\n"
2238 " */\n"
2239 "$name$ = {\n",
2240 "name", GetPath(options, enumdesc));
2241
2242 for (int i = 0; i < enumdesc->value_count(); i++) {
2243 const EnumValueDescriptor* value = enumdesc->value(i);
2244 printer->Print(
2245 " $name$: $value$$comma$\n",
2246 "name", ToEnumCase(value->name()),
2247 "value", SimpleItoa(value->number()),
2248 "comma", (i == enumdesc->value_count() - 1) ? "" : ",");
2249 }
2250
2251 printer->Print(
2252 "};\n"
2253 "\n");
2254}
2255
2256void Generator::GenerateExtension(const GeneratorOptions& options,
2257 io::Printer* printer,
2258 const FieldDescriptor* field) const {
2259 string extension_scope =
2260 (field->extension_scope() ?
2261 GetPath(options, field->extension_scope()) :
2262 GetPath(options, field->file()));
2263
2264 printer->Print(
2265 "\n"
2266 "/**\n"
2267 " * A tuple of {field number, class constructor} for the extension\n"
2268 " * field named `$name$`.\n"
2269 " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
2270 " */\n"
2271 "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
2272 "name", JSObjectFieldName(field),
2273 "class", extension_scope,
2274 "extensionType", JSFieldTypeAnnotation(
2275 options, field,
2276 /* force_optional = */ false,
2277 /* force_present = */ true,
2278 /* singular_if_not_packed = */ false,
2279 /* always_singular = */ false));
2280 printer->Print(
2281 " $index$,\n"
2282 " {$name$: 0},\n"
2283 " $ctor$,\n"
2284 " /** @type {?function((boolean|undefined),!jspb.Message=): "
2285 "!Object} */ (\n"
2286 " $toObject$),\n"
2287 " $repeated$",
2288 "index", SimpleItoa(field->number()),
2289 "name", JSObjectFieldName(field),
2290 "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2291 GetPath(options, field->message_type()) : string("null")),
2292 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2293 (GetPath(options, field->message_type()) + ".toObject") :
2294 string("null")),
2295 "repeated", (field->is_repeated() ? "1" : "0"));
2296
2297 if (options.binary) {
2298 printer->Print(
2299 ",\n"
2300 " jspb.BinaryReader.prototype.$binaryReaderFn$,\n"
2301 " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n"
2302 " $binaryMessageSerializeFn$,\n"
2303 " $binaryMessageDeserializeFn$,\n"
2304 " $isPacked$);\n",
2305 "binaryReaderFn", JSBinaryReaderMethodName(field),
2306 "binaryWriterFn", JSBinaryWriterMethodName(field),
2307 "binaryMessageSerializeFn",
2308 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2309 (GetPath(options, field->message_type()) +
2310 ".serializeBinaryToWriter") : "null",
2311 "binaryMessageDeserializeFn",
2312 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2313 (GetPath(options, field->message_type()) +
2314 ".deserializeBinaryFromReader") : "null",
2315 "isPacked", (field->is_packed() ? "true" : "false"));
2316 } else {
2317 printer->Print(");\n");
2318 }
2319
2320 printer->Print(
2321 "// This registers the extension field with the extended class, so that\n"
2322 "// toObject() will function correctly.\n"
2323 "$extendName$[$index$] = $class$.$name$;\n"
2324 "\n",
2325 "extendName", JSExtensionsObjectName(options, field->containing_type()),
2326 "index", SimpleItoa(field->number()),
2327 "class", extension_scope,
2328 "name", JSObjectFieldName(field));
2329}
2330
2331bool GeneratorOptions::ParseFromOptions(
2332 const vector< pair< string, string > >& options,
2333 string* error) {
2334 for (int i = 0; i < options.size(); i++) {
2335 if (options[i].first == "add_require_for_enums") {
2336 if (options[i].second != "") {
2337 *error = "Unexpected option value for add_require_for_enums";
2338 return false;
2339 }
2340 add_require_for_enums = true;
2341 } else if (options[i].first == "binary") {
2342 if (options[i].second != "") {
2343 *error = "Unexpected option value for binary";
2344 return false;
2345 }
2346 binary = true;
2347 } else if (options[i].first == "testonly") {
2348 if (options[i].second != "") {
2349 *error = "Unexpected option value for testonly";
2350 return false;
2351 }
2352 testonly = true;
2353 } else if (options[i].first == "error_on_name_conflict") {
2354 if (options[i].second != "") {
2355 *error = "Unexpected option value for error_on_name_conflict";
2356 return false;
2357 }
2358 error_on_name_conflict = true;
2359 } else if (options[i].first == "output_dir") {
2360 output_dir = options[i].second;
2361 } else if (options[i].first == "namespace_prefix") {
2362 namespace_prefix = options[i].second;
2363 } else if (options[i].first == "library") {
2364 library = options[i].second;
2365 } else {
2366 // Assume any other option is an output directory, as long as it is a bare
2367 // `key` rather than a `key=value` option.
2368 if (options[i].second != "") {
2369 *error = "Unknown option: " + options[i].first;
2370 return false;
2371 }
2372 output_dir = options[i].first;
2373 }
2374 }
2375
2376 return true;
2377}
2378
2379void Generator::GenerateFilesInDepOrder(
2380 const GeneratorOptions& options,
2381 io::Printer* printer,
2382 const vector<const FileDescriptor*>& files) const {
2383 // Build a std::set over all files so that the DFS can detect when it recurses
2384 // into a dep not specified in the user's command line.
2385 std::set<const FileDescriptor*> all_files(files.begin(), files.end());
2386 // Track the in-progress set of files that have been generated already.
2387 std::set<const FileDescriptor*> generated;
2388 for (int i = 0; i < files.size(); i++) {
2389 GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
2390 }
2391}
2392
2393void Generator::GenerateFileAndDeps(
2394 const GeneratorOptions& options,
2395 io::Printer* printer,
2396 const FileDescriptor* root,
2397 std::set<const FileDescriptor*>* all_files,
2398 std::set<const FileDescriptor*>* generated) const {
2399 // Skip if already generated.
2400 if (generated->find(root) != generated->end()) {
2401 return;
2402 }
2403 generated->insert(root);
2404
2405 // Generate all dependencies before this file's content.
2406 for (int i = 0; i < root->dependency_count(); i++) {
2407 const FileDescriptor* dep = root->dependency(i);
2408 GenerateFileAndDeps(options, printer, dep, all_files, generated);
2409 }
2410
2411 // Generate this file's content. Only generate if the file is part of the
2412 // original set requested to be generated; i.e., don't take all transitive
2413 // deps down to the roots.
2414 if (all_files->find(root) != all_files->end()) {
2415 GenerateClassesAndEnums(options, printer, root);
2416 }
2417}
2418
2419bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
2420 const string& parameter,
2421 GeneratorContext* context,
2422 string* error) const {
2423 vector< pair< string, string > > option_pairs;
2424 ParseGeneratorParameter(parameter, &option_pairs);
2425 GeneratorOptions options;
2426 if (!options.ParseFromOptions(option_pairs, error)) {
2427 return false;
2428 }
2429
2430
2431 // We're either generating a single library file with definitions for message
2432 // and enum types in *all* FileDescriptor inputs, or we're generating a single
2433 // file for each type.
2434 if (options.library != "") {
2435 string filename = options.output_dir + "/" + options.library + ".js";
2436 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open(filename));
2437 GOOGLE_CHECK(output.get());
2438 io::Printer printer(output.get(), '$');
2439
2440 // Pull out all extensions -- we need these to generate all
2441 // provides/requires.
2442 vector<const FieldDescriptor*> extensions;
2443 for (int i = 0; i < files.size(); i++) {
2444 for (int j = 0; j < files[i]->extension_count(); j++) {
2445 const FieldDescriptor* extension = files[i]->extension(j);
2446 extensions.push_back(extension);
2447 }
2448 }
2449
2450 GenerateHeader(options, &printer);
2451
2452 std::set<string> provided;
2453 FindProvides(options, &printer, files, &provided);
2454 FindProvidesForFields(options, &printer, extensions, &provided);
2455 GenerateProvides(options, &printer, &provided);
2456 GenerateTestOnly(options, &printer);
2457 GenerateRequires(options, &printer, files, &provided);
2458
2459 GenerateFilesInDepOrder(options, &printer, files);
2460
2461 for (int i = 0; i < extensions.size(); i++) {
2462 if (ShouldGenerateExtension(extensions[i])) {
2463 GenerateExtension(options, &printer, extensions[i]);
2464 }
2465 }
2466
2467 if (printer.failed()) {
2468 return false;
2469 }
2470 } else {
2471 // Collect all types, and print each type to a separate file. Pull out
2472 // free-floating extensions while we make this pass.
2473 map< string, vector<const FieldDescriptor*> > extensions_by_namespace;
2474
2475 // If we're generating code in file-per-type mode, avoid overwriting files
2476 // by choosing the last descriptor that writes each filename and permitting
2477 // only those to generate code.
2478
2479 // Current descriptor that will generate each filename, indexed by filename.
2480 map<string, const void*> desc_by_filename;
2481 // Set of descriptors allowed to generate files.
2482 set<const void*> allowed_descs;
2483
2484 for (int i = 0; i < files.size(); i++) {
2485 // Collect all (descriptor, filename) pairs.
2486 map<const void*, string> descs_in_file;
2487 for (int j = 0; j < files[i]->message_type_count(); j++) {
2488 const Descriptor* desc = files[i]->message_type(j);
2489 string filename =
2490 options.output_dir + "/" + ToFileName(desc->name()) + ".js";
2491 descs_in_file[desc] = filename;
2492 }
2493 for (int j = 0; j < files[i]->enum_type_count(); j++) {
2494 const EnumDescriptor* desc = files[i]->enum_type(j);
2495 string filename =
2496 options.output_dir + "/" + ToFileName(desc->name()) + ".js";
2497 descs_in_file[desc] = filename;
2498 }
2499
2500 // For each (descriptor, filename) pair, update the
2501 // descriptors-by-filename map, and if a previous descriptor was already
2502 // writing the filename, remove it from the allowed-descriptors set.
2503 map<const void*, string>::iterator it;
2504 for (it = descs_in_file.begin(); it != descs_in_file.end(); ++it) {
2505 const void* desc = it->first;
2506 const string& filename = it->second;
2507 if (desc_by_filename.find(filename) != desc_by_filename.end()) {
2508 if (options.error_on_name_conflict) {
2509 *error = "Name conflict: file name " + filename +
2510 " would be generated by two descriptors";
2511 return false;
2512 }
2513 allowed_descs.erase(desc_by_filename[filename]);
2514 }
2515 desc_by_filename[filename] = desc;
2516 allowed_descs.insert(desc);
2517 }
2518 }
2519
2520 // Generate code.
2521 for (int i = 0; i < files.size(); i++) {
2522 const FileDescriptor* file = files[i];
2523 for (int j = 0; j < file->message_type_count(); j++) {
2524 const Descriptor* desc = file->message_type(j);
2525 if (allowed_descs.find(desc) == allowed_descs.end()) {
2526 continue;
2527 }
2528
2529 string filename = options.output_dir + "/" +
2530 ToFileName(desc->name()) + ".js";
2531 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
2532 context->Open(filename));
2533 GOOGLE_CHECK(output.get());
2534 io::Printer printer(output.get(), '$');
2535
2536 GenerateHeader(options, &printer);
2537
2538 std::set<string> provided;
2539 FindProvidesForMessage(options, &printer, desc, &provided);
2540 GenerateProvides(options, &printer, &provided);
2541 GenerateTestOnly(options, &printer);
2542 GenerateRequires(options, &printer, desc, &provided);
2543
2544 GenerateClass(options, &printer, desc);
2545
2546 if (printer.failed()) {
2547 return false;
2548 }
2549 }
2550 for (int j = 0; j < file->enum_type_count(); j++) {
2551 const EnumDescriptor* enumdesc = file->enum_type(j);
2552 if (allowed_descs.find(enumdesc) == allowed_descs.end()) {
2553 continue;
2554 }
2555
2556 string filename = options.output_dir + "/" +
2557 ToFileName(enumdesc->name()) + ".js";
2558
2559 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
2560 context->Open(filename));
2561 GOOGLE_CHECK(output.get());
2562 io::Printer printer(output.get(), '$');
2563
2564 GenerateHeader(options, &printer);
2565
2566 std::set<string> provided;
2567 FindProvidesForEnum(options, &printer, enumdesc, &provided);
2568 GenerateProvides(options, &printer, &provided);
2569 GenerateTestOnly(options, &printer);
2570
2571 GenerateEnum(options, &printer, enumdesc);
2572
2573 if (printer.failed()) {
2574 return false;
2575 }
2576 }
2577 // Pull out all free-floating extensions and generate files for those too.
2578 for (int j = 0; j < file->extension_count(); j++) {
2579 const FieldDescriptor* extension = file->extension(j);
2580 extensions_by_namespace[GetPath(options, files[i])]
2581 .push_back(extension);
2582 }
2583 }
2584
2585 // Generate extensions in separate files.
2586 map< string, vector<const FieldDescriptor*> >::iterator it;
2587 for (it = extensions_by_namespace.begin();
2588 it != extensions_by_namespace.end();
2589 ++it) {
2590 string filename = options.output_dir + "/" +
2591 ToFileName(it->first) + ".js";
2592
2593 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
2594 context->Open(filename));
2595 GOOGLE_CHECK(output.get());
2596 io::Printer printer(output.get(), '$');
2597
2598 GenerateHeader(options, &printer);
2599
2600 std::set<string> provided;
2601 FindProvidesForFields(options, &printer, it->second, &provided);
2602 GenerateProvides(options, &printer, &provided);
2603 GenerateTestOnly(options, &printer);
2604 GenerateRequires(options, &printer, it->second, &provided);
2605
2606 for (int j = 0; j < it->second.size(); j++) {
2607 if (ShouldGenerateExtension(it->second[j])) {
2608 GenerateExtension(options, &printer, it->second[j]);
2609 }
2610 }
2611 }
2612 }
2613
2614 return true;
2615}
2616
2617} // namespace js
2618} // namespace compiler
2619} // namespace protobuf
2620} // namespace google