blob: 534a5b5b9c91cdc92257c1dc67b7b0ae0bcfa3de [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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070031#include "google/protobuf/compiler/js/js_generator.h"
Feng Xiaoe841bac2015-12-11 17:09:20 -080032
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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700126// 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.
130enum 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800136bool 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.
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700147// TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800148string StripProto(const string& filename) {
149 const char* suffix = HasSuffixString(filename, ".protodevel")
150 ? ".protodevel" : ".proto";
151 return StripSuffixString(filename, suffix);
152}
153
Josh Haberman55cc3aa2016-02-02 15:18:34 -0800154// Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript
155// file foo/bar/baz.js.
156string GetJSFilename(const string& filename) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700157 return StripProto(filename) + "_pb.js";
Josh Haberman55cc3aa2016-02-02 15:18:34 -0800158}
159
murgatroid99a862b6b2016-02-24 13:44:57 -0800160// Given a filename like foo/bar/baz.proto, returns the root directory
161// path ../../
162string 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -0800174// Returns the alias we assign to the module of the given .proto filename
175// when importing.
176string 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800190// Returns the fully normalized JavaScript path for the given
191// file descriptor's package.
192string 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.
205string 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.
210string 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.
231string 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.
241string 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.
248string 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.
258string GetPath(const GeneratorOptions& options,
259 const EnumValueDescriptor* value_descriptor) {
260 return GetPath(
261 options,
262 value_descriptor->type()) + "." + value_descriptor->name();
263}
264
Josh Haberman55cc3aa2016-02-02 15:18:34 -0800265string 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
279string 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800285// - 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.
294char ToLowerASCII(char c) {
295 if (c >= 'A' && c <= 'Z') {
296 return (c - 'A') + 'a';
297 } else {
298 return c;
299 }
300}
301
302vector<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
321vector<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
337string 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
351string 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.
366string 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
381string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700396// When we're generating one output file per type name, this is the filename
397// that top-level extensions should go in.
398string 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.
405string 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.
412string GetEnumFileName(const GeneratorOptions& options,
413 const EnumDescriptor* desc) {
414 return options.output_dir + "/" + ToFileName(desc->name()) + ".js";
415}
416
Feng Xiaoe841bac2015-12-11 17:09:20 -0800417// Returns the message/response ID, if set.
418string GetMessageId(const Descriptor* desc) {
419 return string();
420}
421
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700422bool 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800430
431// Used inside Google only -- do not remove.
432bool IsResponse(const Descriptor* desc) { return false; }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700433
434bool IgnoreField(const FieldDescriptor* field) {
435 return IgnoreExtensionField(field);
436}
Feng Xiaoe841bac2015-12-11 17:09:20 -0800437
438
439// Does JSPB ignore this entire oneof? True only if all fields are ignored.
440bool 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
449string 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
470string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700481string 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800494// Returns the field name as a capitalized portion of a getter/setter method
495// name, e.g. MyField for .getMyField().
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700496string JSGetterName(const FieldDescriptor* field,
497 BytesMode bytes_mode = BYTES_DEFAULT) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800498 string name = JSIdent(field,
499 /* is_upper_camel = */ true,
500 /* is_map = */ false);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700501 if (field->type() == FieldDescriptor::TYPE_BYTES) {
502 string suffix = JSByteGetterSuffix(bytes_mode);
503 if (!suffix.empty()) {
504 name += "_as" + suffix;
505 }
506 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800507 if (name == "Extension" || name == "JsPbMessageId") {
508 // Avoid conflicts with base-class names.
509 name += "$";
510 }
511 return name;
512}
513
514string JSMapGetterName(const FieldDescriptor* field) {
515 return JSIdent(field,
516 /* is_upper_camel = */ true,
517 /* is_map = */ true);
518}
519
520
521
522string 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).
528string 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
548string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700567// Decodes a codepoint in \x0000 -- \xFFFF.
Feng Xiao17347e32015-12-30 01:18:22 -0800568uint16 DecodeUTF8Codepoint(uint8* bytes, size_t* length) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800569 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700604// 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.
608bool EscapeJSString(const string& in, string* out) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800609 size_t decoded = 0;
610 for (size_t i = 0; i < in.size(); i += decoded) {
Feng Xiao17347e32015-12-30 01:18:22 -0800611 uint16 codepoint = 0;
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700612 // Decode the next UTF-8 codepoint.
613 size_t have_bytes = in.size() - i;
614 uint8 bytes[3] = {
Feng Xiao17347e32015-12-30 01:18:22 -0800615 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),
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700618 };
619 codepoint = DecodeUTF8Codepoint(bytes, &have_bytes);
620 if (have_bytes == 0) {
621 return false;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800622 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700623 decoded = have_bytes;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800624
625 switch (codepoint) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700626 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;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800638 default:
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700639 // 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800643 if (codepoint >= 0x20 && codepoint <= 0x7e) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700644 *out += static_cast<char>(codepoint);
645 } else if (codepoint >= 0x100) {
646 *out += StringPrintf("\\u%04x", codepoint);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800647 } else {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700648 *out += StringPrintf("\\x%02x", codepoint);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800649 }
650 break;
651 }
652 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700653 return true;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800654}
655
656string 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).
685string 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
735string FloatToString(float value) {
736 string result = SimpleFtoa(value);
737 return PostProcessFloat(result);
738}
739
740string DoubleToString(double value) {
741 string result = SimpleDtoa(value);
742 return PostProcessFloat(result);
743}
744
745string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
746 return orig;
747}
748
749string 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) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700778 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()) + "\"";
Feng Xiaoe841bac2015-12-11 17:09:20 -0800789 }
790 case FieldDescriptor::CPPTYPE_MESSAGE:
791 return "null";
792 }
Feng Xiao7a9d5822015-12-28 16:30:59 -0800793 GOOGLE_LOG(FATAL) << "Shouldn't reach here.";
794 return "";
Feng Xiaoe841bac2015-12-11 17:09:20 -0800795}
796
797string 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
841string JSIntegerTypeName(const FieldDescriptor* field) {
842 return "number";
843}
844
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700845string 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
Feng Xiaoe841bac2015-12-11 17:09:20 -0800863string JSTypeName(const GeneratorOptions& options,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700864 const FieldDescriptor* field,
865 BytesMode bytes_mode) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800866 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:
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700882 return JSStringTypeName(options, field, bytes_mode);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800883 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
892bool HasFieldPresence(const FieldDescriptor* field);
893
894string JSFieldTypeAnnotation(const GeneratorOptions& options,
895 const FieldDescriptor* field,
896 bool force_optional,
897 bool force_present,
898 bool singular_if_not_packed,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700899 BytesMode bytes_mode = BYTES_DEFAULT) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800900 bool is_primitive =
901 (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700902 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE &&
903 (field->type() != FieldDescriptor::TYPE_BYTES ||
904 bytes_mode == BYTES_B64));
Feng Xiaoe841bac2015-12-11 17:09:20 -0800905
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700906 string jstype = JSTypeName(options, field, bytes_mode);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800907
908 if (field->is_repeated() &&
Feng Xiaoe841bac2015-12-11 17:09:20 -0800909 (field->is_packed() || !singular_if_not_packed)) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700910 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 + ">";
Feng Xiaoe841bac2015-12-11 17:09:20 -0800918 }
Feng Xiaoe841bac2015-12-11 17:09:20 -0800919 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
941string 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
950string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
951 bool is_writer) {
952 string name = JSBinaryReaderMethodType(field);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800953 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
961string JSBinaryReaderMethodName(const FieldDescriptor* field) {
962 return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
963}
964
965string JSBinaryWriterMethodName(const FieldDescriptor* field) {
966 return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
967}
968
969string JSReturnClause(const FieldDescriptor* desc) {
970 return "";
971}
972
973string JSReturnDoc(const GeneratorOptions& options,
974 const FieldDescriptor* desc) {
975 return "";
976}
977
978bool 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
987static const char* kRepeatedFieldArrayName = ".repeatedFields_";
988
989string RepeatedFieldsArrayName(const GeneratorOptions& options,
990 const Descriptor* desc) {
991 return HasRepeatedFields(desc) ?
992 (GetPath(options, desc) + kRepeatedFieldArrayName) : "null";
993}
994
995bool 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
1004static const char* kOneofGroupArrayName = ".oneofGroups_";
1005
1006string OneofFieldsArrayName(const GeneratorOptions& options,
1007 const Descriptor* desc) {
1008 return HasOneofFields(desc) ?
1009 (GetPath(options, desc) + kOneofGroupArrayName) : "null";
1010}
1011
1012string 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
1022string 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
1043string JSOneofArray(const GeneratorOptions& options,
1044 const FieldDescriptor* field) {
1045 return OneofFieldsArrayName(options, field->containing_type()) + "[" +
1046 JSOneofIndex(field->containing_oneof()) + "]";
1047}
1048
1049string 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
1075string JSExtensionsObjectName(const GeneratorOptions& options,
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001076 const FileDescriptor* from_file,
Feng Xiaoe841bac2015-12-11 17:09:20 -08001077 const Descriptor* desc) {
1078 if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001079 // TODO(haberman): fix this for the IMPORT_COMMONJS case.
Feng Xiaoe841bac2015-12-11 17:09:20 -08001080 return "jspb.Message.messageSetExtensions";
1081 } else {
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001082 return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
Feng Xiaoe841bac2015-12-11 17:09:20 -08001083 }
1084}
1085
1086string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001109string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001110 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 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001125 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001130 return comments;
1131}
1132
1133bool ShouldGenerateExtension(const FieldDescriptor* field) {
1134 return
1135 field->is_extension() &&
1136 !IgnoreField(field);
1137}
1138
1139bool HasExtensions(const Descriptor* desc) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001140 for (int i = 0; i < desc->extension_count(); i++) {
1141 if (ShouldGenerateExtension(desc->extension(i))) {
1142 return true;
1143 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001144 }
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
1153bool 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
1167bool 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.
1173string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001195// value. See http://go/proto3#heading=h.kozewqqcqhuz for more information.
Feng Xiaoe841bac2015-12-11 17:09:20 -08001196bool 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001204// value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more
Feng Xiaoe841bac2015-12-11 17:09:20 -08001205// information.
1206string 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001223 case FieldDescriptor::CPPTYPE_STRING: // includes BYTES
Feng Xiaoe841bac2015-12-11 17:09:20 -08001224 return "\"\"";
1225
1226 default:
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001227 // MESSAGE is handled separately.
Feng Xiaoe841bac2015-12-11 17:09:20 -08001228 assert(false);
1229 return "";
1230 }
1231}
1232
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001233// 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.
1237class FileDeduplicator {
1238 public:
1239 explicit FileDeduplicator(const GeneratorOptions& options)
1240 : error_on_conflict_(options.error_on_name_conflict) {}
1241
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
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
1267void 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.
1285class NotInSet {
1286 public:
1287 explicit NotInSet(const set<const FileDescriptor*>& file_set)
1288 : file_set_(file_set) {}
1289
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.
1302void 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
1325bool 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
Feng Xiaoe841bac2015-12-11 17:09:20 -08001370} // anonymous namespace
1371
1372void 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001383void 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
Feng Xiaoe841bac2015-12-11 17:09:20 -08001395void 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++) {
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001400 FindProvidesForFile(options, printer, files[i], provided);
Feng Xiaoe841bac2015-12-11 17:09:20 -08001401 }
1402
1403 printer->Print("\n");
1404}
1405
1406void 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
1424void 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
1432void 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
1450void 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001460void Generator::GenerateRequiresForMessage(const GeneratorOptions& options,
1461 io::Printer* printer,
1462 const Descriptor* desc,
1463 std::set<string>* provided) const {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001464 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001475void 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;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001485
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001486 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;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001494 }
1495
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001496 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001508 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001509
1510 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1511 /* require_jspb = */ have_message,
1512 /* require_extension = */ have_extensions);
Feng Xiaoe841bac2015-12-11 17:09:20 -08001513}
1514
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001515void Generator::GenerateRequiresForExtensions(
1516 const GeneratorOptions& options, io::Printer* printer,
1517 const vector<const FieldDescriptor*>& fields,
1518 std::set<string>* provided) const {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001519 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
1534void 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
1575bool NamespaceOnly(const Descriptor* desc) {
1576 return false;
1577}
1578
1579void 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
1612void 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
1630void 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
1640void 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
1648void 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
1659void 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 }
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001683
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001684 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001689 }
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
1700void 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
1739void 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
1785void 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
1795void 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
1838void 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",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001906 "extObject", JSExtensionsObjectName(options, desc->file(), desc),
Feng Xiaoe841bac2015-12-11 17:09:20 -08001907 "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
1922void 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),
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001935 "type", SubmessageTypeRef(options, field));
Feng Xiaoe841bac2015-12-11 17:09:20 -08001936 }
1937 } else {
1938 printer->Print("(f = msg.get$getter$()) && "
1939 "$type$.toObject(includeInstance, f)",
1940 "getter", JSGetterName(field),
Josh Haberman55cc3aa2016-02-02 15:18:34 -08001941 "type", SubmessageTypeRef(options, field));
Feng Xiaoe841bac2015-12-11 17:09:20 -08001942 }
1943 } else {
1944 // Simple field (singular or repeated).
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001945 if ((!HasFieldPresence(field) && !field->is_repeated()) ||
1946 field->type() == FieldDescriptor::TYPE_BYTES) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001947 // Delegate to the generated get<field>() method in order not to duplicate
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001948 // the proto3-field-default-value or byte-coercion logic here.
Feng Xiaoe841bac2015-12-11 17:09:20 -08001949 printer->Print("msg.get$getter$()",
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001950 "getter", JSGetterName(field, BYTES_B64));
Feng Xiaoe841bac2015-12-11 17:09:20 -08001951 } else {
1952 if (field->has_default_value()) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001953 printer->Print("jspb.Message.getField(msg, $index$) == null ? "
1954 "$defaultValue$ : ",
Feng Xiaoe841bac2015-12-11 17:09:20 -08001955 "index", JSFieldIndex(field),
1956 "defaultValue", JSFieldDefault(field));
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001957 }
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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001973 } else {
1974 printer->Print("jspb.Message.getField(msg, $index$)",
1975 "index", JSFieldIndex(field));
1976 }
1977 }
1978 }
1979}
1980
1981void 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
2007void 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),
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002023 "fieldclass", SubmessageTypeRef(options, field));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002024 }
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),
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002031 "fieldclass", SubmessageTypeRef(options, field));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002032 }
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
2043void 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
2058void 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
2071void 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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002081void 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
Feng Xiaoe841bac2015-12-11 17:09:20 -08002115void 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),
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002126 "comment", FieldComments(field, BYTES_DEFAULT),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002127 "type", JSFieldTypeAnnotation(options, field,
2128 /* force_optional = */ false,
2129 /* force_present = */ false,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002130 /* singular_if_not_packed = */ false));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002131 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,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002144 /* singular_if_not_packed = */ false),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002145 "rpt", (field->is_repeated() ? "Repeated" : ""),
2146 "index", JSFieldIndex(field),
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002147 "wrapperclass", SubmessageTypeRef(options, field),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002148 "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,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002158 /* singular_if_not_packed = */ false),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002159 "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 {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002187 bool untyped =
2188 false;
Feng Xiaoe841bac2015-12-11 17:09:20 -08002189
2190 // Simple (primitive) field, either singular or repeated.
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002191
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,
Feng Xiaoe841bac2015-12-11 17:09:20 -08002200 /* force_optional = */ false,
2201 /* force_present = */ !HasFieldPresence(field),
2202 /* singular_if_not_packed = */ false,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002203 /* bytes_mode = */ bytes_mode);
2204 if (untyped) {
2205 printer->Print(
2206 "/**\n"
2207 " * @return {?} Raw field, untyped.\n"
2208 " */\n");
2209 } else {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002210 printer->Print(
2211 "/**\n"
2212 " * $fielddef$\n"
2213 "$comment$"
2214 " * @return {$type$}\n"
2215 " */\n",
2216 "fielddef", FieldDefinition(options, field),
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002217 "comment", FieldComments(field, bytes_mode),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002218 "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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002226 if (untyped) {
2227 printer->Print(
2228 " return ");
2229 } else {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002230 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()) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002244 printer->Print("jspb.Message.getField(this, $index$) == null ? "
2245 "$defaultValue$ : ",
Feng Xiaoe841bac2015-12-11 17:09:20 -08002246 "index", JSFieldIndex(field),
2247 "defaultValue", JSFieldDefault(field));
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002248 }
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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08002264 } else {
2265 printer->Print("jspb.Message.getField(this, $index$)",
2266 "index", JSFieldIndex(field));
2267 }
2268 }
2269
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002270 if (untyped) {
2271 printer->Print(
2272 ";\n"
2273 "};\n"
2274 "\n"
2275 "\n");
2276 } else {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002277 printer->Print(
2278 ");\n"
2279 "};\n"
2280 "\n"
2281 "\n");
2282 }
2283
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002284 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 {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002296 printer->Print(
2297 "/** @param {$optionaltype$} value $returndoc$ */\n",
2298 "optionaltype",
2299 JSFieldTypeAnnotation(options, field,
2300 /* force_optional = */ true,
2301 /* force_present = */ !HasFieldPresence(field),
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002302 /* singular_if_not_packed = */ false),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002303 "returndoc", JSReturnDoc(options, field));
2304 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08002305 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",
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002317 "type",
2318 untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2319 "typeclose", untyped ? ")" : "",
Feng Xiaoe841bac2015-12-11 17:09:20 -08002320 "oneofgroup",
2321 (field->containing_oneof() ? (", " + JSOneofArray(options, field))
2322 : ""),
2323 "returnvalue", JSReturnClause(field), "rptvalueinit",
2324 (field->is_repeated() ? " || []" : ""));
2325
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002326 if (untyped) {
2327 printer->Print(
2328 "/**\n"
2329 " * Clears the value. $returndoc$\n"
2330 " */\n",
2331 "returndoc", JSReturnDoc(options, field));
2332 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08002333
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
2355void 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
2383void 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",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002430 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002431 "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
2447void 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",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002460 "fieldclass", SubmessageTypeRef(options, field),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002461 "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,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002470 BYTES_U8),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002471 "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
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002476 // 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));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002482 } 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
2494void 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",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002538 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002539 "class", GetPath(options, desc));
2540 }
2541
2542 printer->Print(
2543 "};\n"
2544 "\n"
2545 "\n");
2546}
2547
2548void Generator::GenerateClassSerializeBinaryField(
2549 const GeneratorOptions& options,
2550 io::Printer* printer,
2551 const FieldDescriptor* field) const {
2552 printer->Print(
2553 " f = this.get$name$();\n",
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002554 "name", JSGetterName(field, BYTES_U8));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002555
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),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002604 "index", SimpleItoa(field->number()));
2605
2606 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2607 printer->Print(
2608 ",\n"
2609 " $submsg$.serializeBinaryToWriter\n",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002610 "submsg", SubmessageTypeRef(options, field));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002611 } else {
2612 printer->Print("\n");
2613 }
2614 printer->Print(
2615 " );\n"
2616 " }\n");
2617}
2618
2619void 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
2643void 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,
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002665 /* singular_if_not_packed = */ false));
Feng Xiaoe841bac2015-12-11 17:09:20 -08002666 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 ?
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002677 SubmessageTypeRef(options, field) : string("null")),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002678 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002679 (SubmessageTypeRef(options, field) + ".toObject") :
Feng Xiaoe841bac2015-12-11 17:09:20 -08002680 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) ?
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002695 (SubmessageTypeRef(options, field) +
Feng Xiaoe841bac2015-12-11 17:09:20 -08002696 ".serializeBinaryToWriter") : "null",
2697 "binaryMessageDeserializeFn",
2698 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002699 (SubmessageTypeRef(options, field) +
Feng Xiaoe841bac2015-12-11 17:09:20 -08002700 ".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",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002711 "extendName", JSExtensionsObjectName(options, field->file(),
2712 field->containing_type()),
Feng Xiaoe841bac2015-12-11 17:09:20 -08002713 "index", SimpleItoa(field->number()),
2714 "class", extension_scope,
2715 "name", JSObjectFieldName(field));
2716}
2717
2718bool 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;
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002752 } 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08002765 } 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002776 if (!library.empty() && import_style != IMPORT_CLOSURE) {
2777 *error = "The library option should only be used for "
2778 "import_style=closure";
2779 }
2780
Feng Xiaoe841bac2015-12-11 17:09:20 -08002781 return true;
2782}
2783
2784void 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
2798void 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002824void 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) {
Josh Habermane9f31ee2016-02-04 10:29:27 -08002831 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002835 for (int i = 0; i < file->dependency_count(); i++) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002836 const string& name = file->dependency(i)->name();
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002837 printer->Print(
2838 "var $alias$ = require('$file$');\n",
2839 "alias", ModuleAlias(name),
murgatroid99a862b6b2016-02-24 13:44:57 -08002840 "file", GetRootPath(file->name()) + GetJSFilename(name));
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002841 }
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() { /* ... */ }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002851 set<string> provided;
Josh Haberman35298f92016-02-04 17:01:33 -08002852
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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002860 FindProvidesForFile(options, printer, file, &provided);
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002861 for (std::set<string>::iterator it = provided.begin();
2862 it != provided.end(); ++it) {
Josh Habermane9f31ee2016-02-04 10:29:27 -08002863 printer->Print("goog.exportSymbol('$name$', null, global);\n",
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002864 "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 }
Josh Haberman77af5d02016-02-04 16:11:07 -08002874
2875 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) {
2876 printer->Print("goog.object.extend(exports, $package$);\n",
2877 "package", GetPath(options, file));
2878 }
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002879}
2880
Feng Xiaoe841bac2015-12-11 17:09:20 -08002881bool 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
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002893 // 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.
Feng Xiaoe841bac2015-12-11 17:09:20 -08002901 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);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002923 GenerateRequiresForLibrary(options, &printer, files, &provided);
Feng Xiaoe841bac2015-12-11 17:09:20 -08002924
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 }
Josh Haberman55cc3aa2016-02-02 15:18:34 -08002936 } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002937 set<const void*> allowed_set;
2938 if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) {
2939 return false;
Feng Xiaoe841bac2015-12-11 17:09:20 -08002940 }
2941
Feng Xiaoe841bac2015-12-11 17:09:20 -08002942 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);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002946 if (allowed_set.count(desc) == 0) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002947 continue;
2948 }
2949
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002950 string filename = GetMessageFileName(options, desc);
Feng Xiaoe841bac2015-12-11 17:09:20 -08002951 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);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002962 GenerateRequiresForMessage(options, &printer, desc, &provided);
Feng Xiaoe841bac2015-12-11 17:09:20 -08002963
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);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002972 if (allowed_set.count(enumdesc) == 0) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08002973 continue;
2974 }
2975
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002976 string filename = GetEnumFileName(options, enumdesc);
Feng Xiaoe841bac2015-12-11 17:09:20 -08002977 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 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07002995 // 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);
Feng Xiaoe841bac2015-12-11 17:09:20 -08002999
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003000 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
3001 context->Open(filename));
3002 GOOGLE_CHECK(output.get());
3003 io::Printer printer(output.get(), '$');
Feng Xiaoe841bac2015-12-11 17:09:20 -08003004
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003005 GenerateHeader(options, &printer);
Feng Xiaoe841bac2015-12-11 17:09:20 -08003006
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003007 std::set<string> provided;
3008 vector<const FieldDescriptor*> fields;
Feng Xiaoe841bac2015-12-11 17:09:20 -08003009
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003010 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08003015
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003016 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 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08003025 }
3026 }
3027 }
Josh Haberman55cc3aa2016-02-02 15:18:34 -08003028 } 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());
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07003035 scoped_ptr<io::ZeroCopyOutputStream> output(
Josh Haberman55cc3aa2016-02-02 15:18:34 -08003036 context->Open(filename));
3037 GOOGLE_CHECK(output.get());
3038 io::Printer printer(output.get(), '$');
3039
3040 GenerateFile(options, &printer, file);
3041
3042 if (printer.failed()) {
3043 return false;
3044 }
3045 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08003046 }
3047
3048 return true;
3049}
3050
3051} // namespace js
3052} // namespace compiler
3053} // namespace protobuf
3054} // namespace google