Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(18)

Side by Side Diff: third_party/protobuf/src/google/protobuf/compiler/js/js_generator.cc

Issue 2495533002: third_party/protobuf: Update to HEAD (83d681ee2c) (Closed)
Patch Set: Created 4 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Protocol Buffers - Google's data interchange format 1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2008 Google Inc. All rights reserved. 2 // Copyright 2008 Google Inc. All rights reserved.
3 // https://developers.google.com/protocol-buffers/ 3 // https://developers.google.com/protocol-buffers/
4 // 4 //
5 // Redistribution and use in source and binary forms, with or without 5 // Redistribution and use in source and binary forms, with or without
6 // modification, are permitted provided that the following conditions are 6 // modification, are permitted provided that the following conditions are
7 // met: 7 // met:
8 // 8 //
9 // * Redistributions of source code must retain the above copyright 9 // * Redistributions of source code must retain the above copyright
10 // notice, this list of conditions and the following disclaimer. 10 // notice, this list of conditions and the following disclaimer.
(...skipping 10 matching lines...) Expand all
21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 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. 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30 30
31 #include <google/protobuf/compiler/js/js_generator.h> 31 #include "google/protobuf/compiler/js/js_generator.h"
32 32
33 #include <assert.h> 33 #include <assert.h>
34 #include <algorithm> 34 #include <algorithm>
35 #include <limits> 35 #include <limits>
36 #include <map> 36 #include <map>
37 #include <memory> 37 #include <memory>
38 #ifndef _SHARED_PTR_H 38 #ifndef _SHARED_PTR_H
39 #include <google/protobuf/stubs/shared_ptr.h> 39 #include <google/protobuf/stubs/shared_ptr.h>
40 #endif 40 #endif
41 #include <string> 41 #include <string>
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 144
145 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto 145 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto
146 // suffix stripped. 146 // suffix stripped.
147 // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc. 147 // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc.
148 string StripProto(const string& filename) { 148 string StripProto(const string& filename) {
149 const char* suffix = HasSuffixString(filename, ".protodevel") 149 const char* suffix = HasSuffixString(filename, ".protodevel")
150 ? ".protodevel" : ".proto"; 150 ? ".protodevel" : ".proto";
151 return StripSuffixString(filename, suffix); 151 return StripSuffixString(filename, suffix);
152 } 152 }
153 153
154 // Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript 154 // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
155 // file foo/bar/baz.js. 155 // file foo/bar/baz.js.
156 string GetJSFilename(const string& filename) { 156 string GetJSFilename(const GeneratorOptions& options, const string& filename) {
157 return StripProto(filename) + "_pb.js"; 157 return StripProto(filename) + options.GetFileNameExtension();
158 } 158 }
159 159
160 // Given a filename like foo/bar/baz.proto, returns the root directory 160 // Given a filename like foo/bar/baz.proto, returns the root directory
161 // path ../../ 161 // path ../../
162 string GetRootPath(const string& filename) { 162 string GetRootPath(const string& from_filename, const string& to_filename) {
163 size_t slashes = std::count(filename.begin(), filename.end(), '/'); 163 if (to_filename.find("google/protobuf") == 0) {
164 // Well-known types (.proto files in the google/protobuf directory) are
165 // assumed to come from the 'google-protobuf' npm package. We may want to
166 // generalize this exception later by letting others put generated code in
167 // their own npm packages.
168 return "google-protobuf/";
169 }
170
171 size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
164 if (slashes == 0) { 172 if (slashes == 0) {
165 return "./"; 173 return "./";
166 } 174 }
167 string result = ""; 175 string result = "";
168 for (size_t i = 0; i < slashes; i++) { 176 for (size_t i = 0; i < slashes; i++) {
169 result += "../"; 177 result += "../";
170 } 178 }
171 return result; 179 return result;
172 } 180 }
173 181
(...skipping 19 matching lines...) Expand all
193 const FileDescriptor* file) { 201 const FileDescriptor* file) {
194 if (!options.namespace_prefix.empty()) { 202 if (!options.namespace_prefix.empty()) {
195 return options.namespace_prefix; 203 return options.namespace_prefix;
196 } else if (!file->package().empty()) { 204 } else if (!file->package().empty()) {
197 return "proto." + file->package(); 205 return "proto." + file->package();
198 } else { 206 } else {
199 return "proto"; 207 return "proto";
200 } 208 }
201 } 209 }
202 210
203 // Forward declare, so that GetPrefix can call this method, 211 // Returns the name of the message with a leading dot and taking into account
204 // which in turn, calls GetPrefix. 212 // nesting, for example ".OuterMessage.InnerMessage", or returns empty if
205 string GetPath(const GeneratorOptions& options, 213 // descriptor is null. This function does not handle namespacing, only message
206 const Descriptor* descriptor); 214 // nesting.
215 string GetNestedMessageName(const Descriptor* descriptor) {
216 if (descriptor == NULL) {
217 return "";
218 }
219 return StripPrefixString(descriptor->full_name(),
220 descriptor->file()->package());
221 }
207 222
208 // Returns the path prefix for a message or enumeration that 223 // Returns the path prefix for a message or enumeration that
209 // lives under the given file and containing type. 224 // lives under the given file and containing type.
210 string GetPrefix(const GeneratorOptions& options, 225 string GetPrefix(const GeneratorOptions& options,
211 const FileDescriptor* file_descriptor, 226 const FileDescriptor* file_descriptor,
212 const Descriptor* containing_type) { 227 const Descriptor* containing_type) {
213 string prefix = ""; 228 string prefix = GetPath(options, file_descriptor) +
214 229 GetNestedMessageName(containing_type);
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()) { 230 if (!prefix.empty()) {
222 prefix += "."; 231 prefix += ".";
223 } 232 }
224
225 return prefix; 233 return prefix;
226 } 234 }
227 235
228 236
229 // Returns the fully normalized JavaScript path for the given 237 // Returns the fully normalized JavaScript path for the given
230 // message descriptor. 238 // message descriptor.
231 string GetPath(const GeneratorOptions& options, 239 string GetPath(const GeneratorOptions& options,
232 const Descriptor* descriptor) { 240 const Descriptor* descriptor) {
233 return GetPrefix( 241 return GetPrefix(
234 options, descriptor->file(), 242 options, descriptor->file(),
(...skipping 23 matching lines...) Expand all
258 string GetPath(const GeneratorOptions& options, 266 string GetPath(const GeneratorOptions& options,
259 const EnumValueDescriptor* value_descriptor) { 267 const EnumValueDescriptor* value_descriptor) {
260 return GetPath( 268 return GetPath(
261 options, 269 options,
262 value_descriptor->type()) + "." + value_descriptor->name(); 270 value_descriptor->type()) + "." + value_descriptor->name();
263 } 271 }
264 272
265 string MaybeCrossFileRef(const GeneratorOptions& options, 273 string MaybeCrossFileRef(const GeneratorOptions& options,
266 const FileDescriptor* from_file, 274 const FileDescriptor* from_file,
267 const Descriptor* to_message) { 275 const Descriptor* to_message) {
268 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS && 276 if (options.import_style == GeneratorOptions::kImportCommonJs &&
269 from_file != to_message->file()) { 277 from_file != to_message->file()) {
270 // Cross-file ref in CommonJS needs to use the module alias instead of 278 // Cross-file ref in CommonJS needs to use the module alias instead of
271 // the global name. 279 // the global name.
272 return ModuleAlias(to_message->file()->name()) + "." + to_message->name(); 280 return ModuleAlias(to_message->file()->name()) +
281 GetNestedMessageName(to_message->containing_type()) +
282 "." + to_message->name();
273 } else { 283 } else {
274 // Within a single file we use a full name. 284 // Within a single file we use a full name.
275 return GetPath(options, to_message); 285 return GetPath(options, to_message);
276 } 286 }
277 } 287 }
278 288
279 string SubmessageTypeRef(const GeneratorOptions& options, 289 string SubmessageTypeRef(const GeneratorOptions& options,
280 const FieldDescriptor* field) { 290 const FieldDescriptor* field) {
281 GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE); 291 GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
282 return MaybeCrossFileRef(options, field->file(), field->message_type()); 292 return MaybeCrossFileRef(options, field->file(), field->message_type());
(...skipping 107 matching lines...) Expand 10 before | Expand all | Expand 10 after
390 } 400 }
391 } 401 }
392 402
393 return result; 403 return result;
394 } 404 }
395 405
396 // When we're generating one output file per type name, this is the filename 406 // When we're generating one output file per type name, this is the filename
397 // that top-level extensions should go in. 407 // that top-level extensions should go in.
398 string GetExtensionFileName(const GeneratorOptions& options, 408 string GetExtensionFileName(const GeneratorOptions& options,
399 const FileDescriptor* file) { 409 const FileDescriptor* file) {
400 return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js"; 410 return options.output_dir + "/" + ToFileName(GetPath(options, file)) +
411 options.GetFileNameExtension();
401 } 412 }
402 413
403 // When we're generating one output file per type name, this is the filename 414 // When we're generating one output file per type name, this is the filename
404 // that a top-level message should go in. 415 // that a top-level message should go in.
405 string GetMessageFileName(const GeneratorOptions& options, 416 string GetMessageFileName(const GeneratorOptions& options,
406 const Descriptor* desc) { 417 const Descriptor* desc) {
407 return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; 418 return options.output_dir + "/" + ToFileName(desc->name()) +
419 options.GetFileNameExtension();
408 } 420 }
409 421
410 // When we're generating one output file per type name, this is the filename 422 // When we're generating one output file per type name, this is the filename
411 // that a top-level message should go in. 423 // that a top-level message should go in.
412 string GetEnumFileName(const GeneratorOptions& options, 424 string GetEnumFileName(const GeneratorOptions& options,
413 const EnumDescriptor* desc) { 425 const EnumDescriptor* desc) {
414 return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; 426 return options.output_dir + "/" + ToFileName(desc->name()) +
427 options.GetFileNameExtension();
415 } 428 }
416 429
417 // Returns the message/response ID, if set. 430 // Returns the message/response ID, if set.
418 string GetMessageId(const Descriptor* desc) { 431 string GetMessageId(const Descriptor* desc) {
419 return string(); 432 return string();
420 } 433 }
421 434
422 bool IgnoreExtensionField(const FieldDescriptor* field) { 435 bool IgnoreExtensionField(const FieldDescriptor* field) {
423 // Exclude descriptor extensions from output "to avoid clutter" (from original 436 // Exclude descriptor extensions from output "to avoid clutter" (from original
424 // codegen). 437 // codegen).
425 return field->is_extension() && 438 return field->is_extension() &&
426 field->containing_type()->file()->name() == 439 field->containing_type()->file()->name() ==
427 "google/protobuf/descriptor.proto"; 440 "google/protobuf/descriptor.proto";
428 } 441 }
429 442
430 443
431 // Used inside Google only -- do not remove. 444 // Used inside Google only -- do not remove.
432 bool IsResponse(const Descriptor* desc) { return false; } 445 bool IsResponse(const Descriptor* desc) { return false; }
433 446
434 bool IgnoreField(const FieldDescriptor* field) { 447 bool IgnoreField(const FieldDescriptor* field) {
435 return IgnoreExtensionField(field); 448 return IgnoreExtensionField(field);
436 } 449 }
437 450
438 451
452 // Used inside Google only -- do not remove.
453 bool ShouldTreatMapsAsRepeatedFields(const FileDescriptor& descriptor) {
454 return false;
455 }
456
457 // Do we ignore this message type?
458 bool IgnoreMessage(const GeneratorOptions& options, const Descriptor* d) {
459 return d->options().map_entry() &&
460 !ShouldTreatMapsAsRepeatedFields(*d->file());
461 }
462
463 bool IsMap(const GeneratorOptions& options, const FieldDescriptor* field) {
464 return field->is_map() && !ShouldTreatMapsAsRepeatedFields(*field->file());
465 }
466
439 // Does JSPB ignore this entire oneof? True only if all fields are ignored. 467 // Does JSPB ignore this entire oneof? True only if all fields are ignored.
440 bool IgnoreOneof(const OneofDescriptor* oneof) { 468 bool IgnoreOneof(const OneofDescriptor* oneof) {
441 for (int i = 0; i < oneof->field_count(); i++) { 469 for (int i = 0; i < oneof->field_count(); i++) {
442 if (!IgnoreField(oneof->field(i))) { 470 if (!IgnoreField(oneof->field(i))) {
443 return false; 471 return false;
444 } 472 }
445 } 473 }
446 return true; 474 return true;
447 } 475 }
448 476
449 string JSIdent(const FieldDescriptor* field, 477 string JSIdent(const GeneratorOptions& options, const FieldDescriptor* field,
450 bool is_upper_camel, 478 bool is_upper_camel, bool is_map, bool drop_list) {
451 bool is_map) {
452 string result; 479 string result;
453 if (field->type() == FieldDescriptor::TYPE_GROUP) { 480 if (field->type() == FieldDescriptor::TYPE_GROUP) {
454 result = is_upper_camel ? 481 result = is_upper_camel ?
455 ToUpperCamel(ParseUpperCamel(field->message_type()->name())) : 482 ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
456 ToLowerCamel(ParseUpperCamel(field->message_type()->name())); 483 ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
457 } else { 484 } else {
458 result = is_upper_camel ? 485 result = is_upper_camel ?
459 ToUpperCamel(ParseLowerUnderscore(field->name())) : 486 ToUpperCamel(ParseLowerUnderscore(field->name())) :
460 ToLowerCamel(ParseLowerUnderscore(field->name())); 487 ToLowerCamel(ParseLowerUnderscore(field->name()));
461 } 488 }
462 if (is_map) { 489 if (is_map || IsMap(options, field)) {
490 // JSPB-style or proto3-style map.
463 result += "Map"; 491 result += "Map";
464 } else if (field->is_repeated()) { 492 } else if (!drop_list && field->is_repeated()) {
493 // Repeated field.
465 result += "List"; 494 result += "List";
466 } 495 }
467 return result; 496 return result;
468 } 497 }
469 498
470 string JSObjectFieldName(const FieldDescriptor* field) { 499 string JSObjectFieldName(const GeneratorOptions& options,
471 string name = JSIdent( 500 const FieldDescriptor* field) {
472 field, 501 string name = JSIdent(options, field,
473 /* is_upper_camel = */ false, 502 /* is_upper_camel = */ false,
474 /* is_map = */ false); 503 /* is_map = */ false,
504 /* drop_list = */ false);
475 if (IsReserved(name)) { 505 if (IsReserved(name)) {
476 name = "pb_" + name; 506 name = "pb_" + name;
477 } 507 }
478 return name; 508 return name;
479 } 509 }
480 510
481 string JSByteGetterSuffix(BytesMode bytes_mode) { 511 string JSByteGetterSuffix(BytesMode bytes_mode) {
482 switch (bytes_mode) { 512 switch (bytes_mode) {
483 case BYTES_DEFAULT: 513 case BYTES_DEFAULT:
484 return ""; 514 return "";
485 case BYTES_B64: 515 case BYTES_B64:
486 return "B64"; 516 return "B64";
487 case BYTES_U8: 517 case BYTES_U8:
488 return "U8"; 518 return "U8";
489 default: 519 default:
490 assert(false); 520 assert(false);
491 } 521 }
522 return "";
492 } 523 }
493 524
494 // Returns the field name as a capitalized portion of a getter/setter method 525 // Returns the field name as a capitalized portion of a getter/setter method
495 // name, e.g. MyField for .getMyField(). 526 // name, e.g. MyField for .getMyField().
496 string JSGetterName(const FieldDescriptor* field, 527 string JSGetterName(const GeneratorOptions& options,
497 BytesMode bytes_mode = BYTES_DEFAULT) { 528 const FieldDescriptor* field,
498 string name = JSIdent(field, 529 BytesMode bytes_mode = BYTES_DEFAULT,
530 bool drop_list = false) {
531 string name = JSIdent(options, field,
499 /* is_upper_camel = */ true, 532 /* is_upper_camel = */ true,
500 /* is_map = */ false); 533 /* is_map = */ false, drop_list);
501 if (field->type() == FieldDescriptor::TYPE_BYTES) { 534 if (field->type() == FieldDescriptor::TYPE_BYTES) {
502 string suffix = JSByteGetterSuffix(bytes_mode); 535 string suffix = JSByteGetterSuffix(bytes_mode);
503 if (!suffix.empty()) { 536 if (!suffix.empty()) {
504 name += "_as" + suffix; 537 name += "_as" + suffix;
505 } 538 }
506 } 539 }
507 if (name == "Extension" || name == "JsPbMessageId") { 540 if (name == "Extension" || name == "JsPbMessageId") {
508 // Avoid conflicts with base-class names. 541 // Avoid conflicts with base-class names.
509 name += "$"; 542 name += "$";
510 } 543 }
511 return name; 544 return name;
512 } 545 }
513 546
514 string JSMapGetterName(const FieldDescriptor* field) { 547 string JSMapGetterName(const GeneratorOptions& options,
515 return JSIdent(field, 548 const FieldDescriptor* field) {
549 return JSIdent(options, field,
516 /* is_upper_camel = */ true, 550 /* is_upper_camel = */ true,
517 /* is_map = */ true); 551 /* is_map = */ true,
552 /* drop_list = */ false);
518 } 553 }
519 554
520 555
521 556
522 string JSOneofName(const OneofDescriptor* oneof) { 557 string JSOneofName(const OneofDescriptor* oneof) {
523 return ToUpperCamel(ParseLowerUnderscore(oneof->name())); 558 return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
524 } 559 }
525 560
526 // Returns the index corresponding to this field in the JSPB array (underlying 561 // Returns the index corresponding to this field in the JSPB array (underlying
527 // data storage array). 562 // data storage array).
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 string DoubleToString(double value) { 775 string DoubleToString(double value) {
741 string result = SimpleDtoa(value); 776 string result = SimpleDtoa(value);
742 return PostProcessFloat(result); 777 return PostProcessFloat(result);
743 } 778 }
744 779
745 string MaybeNumberString(const FieldDescriptor* field, const string& orig) { 780 string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
746 return orig; 781 return orig;
747 } 782 }
748 783
749 string JSFieldDefault(const FieldDescriptor* field) { 784 string JSFieldDefault(const FieldDescriptor* field) {
750 assert(field->has_default_value()); 785 if (field->is_repeated()) {
786 return "[]";
787 }
788
751 switch (field->cpp_type()) { 789 switch (field->cpp_type()) {
752 case FieldDescriptor::CPPTYPE_INT32: 790 case FieldDescriptor::CPPTYPE_INT32:
753 return MaybeNumberString( 791 return MaybeNumberString(
754 field, SimpleItoa(field->default_value_int32())); 792 field, SimpleItoa(field->default_value_int32()));
755 case FieldDescriptor::CPPTYPE_UINT32: 793 case FieldDescriptor::CPPTYPE_UINT32:
756 // The original codegen is in Java, and Java protobufs store unsigned 794 // The original codegen is in Java, and Java protobufs store unsigned
757 // integer values as signed integer values. In order to exactly match the 795 // integer values as signed integer values. In order to exactly match the
758 // output, we need to reinterpret as base-2 signed. Ugh. 796 // output, we need to reinterpret as base-2 signed. Ugh.
759 return MaybeNumberString( 797 return MaybeNumberString(
760 field, SimpleItoa(static_cast<int32>(field->default_value_uint32()))); 798 field, SimpleItoa(static_cast<int32>(field->default_value_uint32())));
(...skipping 121 matching lines...) Expand 10 before | Expand all | Expand 10 after
882 return JSStringTypeName(options, field, bytes_mode); 920 return JSStringTypeName(options, field, bytes_mode);
883 case FieldDescriptor::CPPTYPE_ENUM: 921 case FieldDescriptor::CPPTYPE_ENUM:
884 return GetPath(options, field->enum_type()); 922 return GetPath(options, field->enum_type());
885 case FieldDescriptor::CPPTYPE_MESSAGE: 923 case FieldDescriptor::CPPTYPE_MESSAGE:
886 return GetPath(options, field->message_type()); 924 return GetPath(options, field->message_type());
887 default: 925 default:
888 return ""; 926 return "";
889 } 927 }
890 } 928 }
891 929
892 bool HasFieldPresence(const FieldDescriptor* field); 930 bool UseBrokenPresenceSemantics(const GeneratorOptions& options,
931 const FieldDescriptor* field) {
932 if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3) {
933 return options.broken_proto3_semantics;
934 } else if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2) {
935 return false;
936 } else {
937 GOOGLE_LOG(FATAL) << "We can only handle syntax=proto2 and syntax=proto3.";
938 return false;
939 }
940 }
941
942 // Returns true for fields that return "null" from accessors when they are
943 // unset. This should normally only be true for non-repeated submessages, but
944 // we have legacy users who relied on old behavior where accessors behaved this
945 // way.
946 bool ReturnsNullWhenUnset(const GeneratorOptions& options,
947 const FieldDescriptor* field) {
948 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
949 field->is_optional()) {
950 return true;
951 }
952
953 // TODO(haberman): remove this case and unconditionally return false.
954 return UseBrokenPresenceSemantics(options, field) && !field->is_repeated() &&
955 !field->has_default_value();
956 }
957
958 // In a sane world, this would be the same as ReturnsNullWhenUnset(). But in
959 // the status quo, some fields declare that they never return null/undefined
960 // even though they actually do:
961 // * required fields
962 // * optional enum fields
963 // * proto3 primitive fields.
964 bool DeclaredReturnTypeIsNullable(const GeneratorOptions& options,
965 const FieldDescriptor* field) {
966 if (field->is_required() || field->type() == FieldDescriptor::TYPE_ENUM) {
967 return false;
968 }
969
970 if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
971 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
972 return false;
973 }
974
975 return ReturnsNullWhenUnset(options, field);
976 }
977
978 bool SetterAcceptsUndefined(const GeneratorOptions& options,
979 const FieldDescriptor* field) {
980 if (ReturnsNullWhenUnset(options, field)) {
981 return true;
982 }
983
984 // Broken presence semantics always accepts undefined for setters.
985 return UseBrokenPresenceSemantics(options, field);
986 }
987
988 bool SetterAcceptsNull(const GeneratorOptions& options,
989 const FieldDescriptor* field) {
990 if (ReturnsNullWhenUnset(options, field)) {
991 return true;
992 }
993
994 // With broken presence semantics, fields with defaults accept "null" for
995 // setters, but other fields do not. This is a strange quirk of the old
996 // codegen.
997 return UseBrokenPresenceSemantics(options, field) &&
998 field->has_default_value();
999 }
1000
1001 // Returns types which are known to by non-nullable by default.
1002 // The style guide requires that we omit "!" in this case.
1003 bool IsPrimitive(const string& type) {
1004 return type == "undefined" || type == "string" || type == "number" ||
1005 type == "boolean";
1006 }
893 1007
894 string JSFieldTypeAnnotation(const GeneratorOptions& options, 1008 string JSFieldTypeAnnotation(const GeneratorOptions& options,
895 const FieldDescriptor* field, 1009 const FieldDescriptor* field,
896 bool force_optional, 1010 bool is_setter_argument,
897 bool force_present, 1011 bool force_present,
898 bool singular_if_not_packed, 1012 bool singular_if_not_packed,
899 BytesMode bytes_mode = BYTES_DEFAULT) { 1013 BytesMode bytes_mode = BYTES_DEFAULT) {
900 bool is_primitive = 1014 GOOGLE_CHECK(!(is_setter_argument && force_present));
901 (field->cpp_type() != FieldDescriptor::CPPTYPE_ENUM &&
902 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE &&
903 (field->type() != FieldDescriptor::TYPE_BYTES ||
904 bytes_mode == BYTES_B64));
905
906 string jstype = JSTypeName(options, field, bytes_mode); 1015 string jstype = JSTypeName(options, field, bytes_mode);
907 1016
908 if (field->is_repeated() && 1017 if (field->is_repeated() &&
909 (field->is_packed() || !singular_if_not_packed)) { 1018 (field->is_packed() || !singular_if_not_packed)) {
910 if (field->type() == FieldDescriptor::TYPE_BYTES && 1019 if (field->type() == FieldDescriptor::TYPE_BYTES &&
911 bytes_mode == BYTES_DEFAULT) { 1020 bytes_mode == BYTES_DEFAULT) {
912 jstype = "(Array<!Uint8Array>|Array<string>)"; 1021 jstype = "(Array<!Uint8Array>|Array<string>)";
913 } else { 1022 } else {
914 if (!is_primitive) { 1023 if (!IsPrimitive(jstype)) {
915 jstype = "!" + jstype; 1024 jstype = "!" + jstype;
916 } 1025 }
917 jstype = "Array.<" + jstype + ">"; 1026 jstype = "Array.<" + jstype + ">";
918 } 1027 }
919 if (!force_optional) { 1028 }
920 jstype = "!" + jstype; 1029
1030 bool is_null_or_undefined = false;
1031
1032 if (is_setter_argument) {
1033 if (SetterAcceptsNull(options, field)) {
1034 jstype = "?" + jstype;
1035 is_null_or_undefined = true;
1036 }
1037
1038 if (SetterAcceptsUndefined(options, field)) {
1039 jstype += "|undefined";
1040 is_null_or_undefined = true;
1041 }
1042 } else if (force_present) {
1043 // Don't add null or undefined.
1044 } else {
1045 if (DeclaredReturnTypeIsNullable(options, field)) {
1046 jstype = "?" + jstype;
1047 is_null_or_undefined = true;
921 } 1048 }
922 } 1049 }
923 1050
924 if (field->is_optional() && is_primitive && 1051 if (!is_null_or_undefined && !IsPrimitive(jstype)) {
925 (!field->has_default_value() || force_optional) && !force_present) {
926 jstype += "?";
927 } else if (field->is_required() && !is_primitive && !force_optional) {
928 jstype = "!" + jstype; 1052 jstype = "!" + jstype;
929 } 1053 }
930 1054
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; 1055 return jstype;
939 } 1056 }
940 1057
941 string JSBinaryReaderMethodType(const FieldDescriptor* field) { 1058 string JSBinaryReaderMethodType(const FieldDescriptor* field) {
942 string name = field->type_name(); 1059 string name = field->type_name();
943 if (name[0] >= 'a' && name[0] <= 'z') { 1060 if (name[0] >= 'a' && name[0] <= 'z') {
944 name[0] = (name[0] - 'a') + 'A'; 1061 name[0] = (name[0] - 'a') + 'A';
945 } 1062 }
946 1063
947 return name; 1064 return name;
948 } 1065 }
949 1066
950 string JSBinaryReadWriteMethodName(const FieldDescriptor* field, 1067 string JSBinaryReadWriteMethodName(const FieldDescriptor* field,
951 bool is_writer) { 1068 bool is_writer) {
952 string name = JSBinaryReaderMethodType(field); 1069 string name = JSBinaryReaderMethodType(field);
953 if (field->is_packed()) { 1070 if (field->is_packed()) {
954 name = "Packed" + name; 1071 name = "Packed" + name;
955 } else if (is_writer && field->is_repeated()) { 1072 } else if (is_writer && field->is_repeated()) {
956 name = "Repeated" + name; 1073 name = "Repeated" + name;
957 } 1074 }
958 return name; 1075 return name;
959 } 1076 }
960 1077
961 string JSBinaryReaderMethodName(const FieldDescriptor* field) { 1078 string JSBinaryReaderMethodName(const GeneratorOptions& options,
962 return "read" + JSBinaryReadWriteMethodName(field, /* is_writer = */ false); 1079 const FieldDescriptor* field) {
1080 if (options.binary) {
1081 return "jspb.BinaryReader.prototype.read" +
1082 JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
1083 } else {
1084 return "null";
1085 }
963 } 1086 }
964 1087
965 string JSBinaryWriterMethodName(const FieldDescriptor* field) { 1088 string JSBinaryWriterMethodName(const GeneratorOptions& options,
966 return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true); 1089 const FieldDescriptor* field) {
1090 if (options.binary) {
1091 return "jspb.BinaryWriter.prototype.write" +
1092 JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
1093 } else {
1094 return "null";
1095 }
967 } 1096 }
968 1097
969 string JSReturnClause(const FieldDescriptor* desc) { 1098 string JSReturnClause(const FieldDescriptor* desc) {
970 return ""; 1099 return "";
971 } 1100 }
972 1101
973 string JSReturnDoc(const GeneratorOptions& options, 1102 string JSReturnDoc(const GeneratorOptions& options,
974 const FieldDescriptor* desc) { 1103 const FieldDescriptor* desc) {
975 return ""; 1104 return "";
976 } 1105 }
977 1106
978 bool HasRepeatedFields(const Descriptor* desc) { 1107 bool HasRepeatedFields(const GeneratorOptions& options,
1108 const Descriptor* desc) {
979 for (int i = 0; i < desc->field_count(); i++) { 1109 for (int i = 0; i < desc->field_count(); i++) {
980 if (desc->field(i)->is_repeated()) { 1110 if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) {
981 return true; 1111 return true;
982 } 1112 }
983 } 1113 }
984 return false; 1114 return false;
985 } 1115 }
986 1116
987 static const char* kRepeatedFieldArrayName = ".repeatedFields_"; 1117 static const char* kRepeatedFieldArrayName = ".repeatedFields_";
988 1118
989 string RepeatedFieldsArrayName(const GeneratorOptions& options, 1119 string RepeatedFieldsArrayName(const GeneratorOptions& options,
990 const Descriptor* desc) { 1120 const Descriptor* desc) {
991 return HasRepeatedFields(desc) ? 1121 return HasRepeatedFields(options, desc)
992 (GetPath(options, desc) + kRepeatedFieldArrayName) : "null"; 1122 ? (GetPath(options, desc) + kRepeatedFieldArrayName)
1123 : "null";
993 } 1124 }
994 1125
995 bool HasOneofFields(const Descriptor* desc) { 1126 bool HasOneofFields(const Descriptor* desc) {
996 for (int i = 0; i < desc->field_count(); i++) { 1127 for (int i = 0; i < desc->field_count(); i++) {
997 if (desc->field(i)->containing_oneof()) { 1128 if (desc->field(i)->containing_oneof()) {
998 return true; 1129 return true;
999 } 1130 }
1000 } 1131 }
1001 return false; 1132 return false;
1002 } 1133 }
1003 1134
1004 static const char* kOneofGroupArrayName = ".oneofGroups_"; 1135 static const char* kOneofGroupArrayName = ".oneofGroups_";
1005 1136
1006 string OneofFieldsArrayName(const GeneratorOptions& options, 1137 string OneofFieldsArrayName(const GeneratorOptions& options,
1007 const Descriptor* desc) { 1138 const Descriptor* desc) {
1008 return HasOneofFields(desc) ? 1139 return HasOneofFields(desc) ?
1009 (GetPath(options, desc) + kOneofGroupArrayName) : "null"; 1140 (GetPath(options, desc) + kOneofGroupArrayName) : "null";
1010 } 1141 }
1011 1142
1012 string RepeatedFieldNumberList(const Descriptor* desc) { 1143 string RepeatedFieldNumberList(const GeneratorOptions& options,
1144 const Descriptor* desc) {
1013 std::vector<string> numbers; 1145 std::vector<string> numbers;
1014 for (int i = 0; i < desc->field_count(); i++) { 1146 for (int i = 0; i < desc->field_count(); i++) {
1015 if (desc->field(i)->is_repeated()) { 1147 if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) {
1016 numbers.push_back(JSFieldIndex(desc->field(i))); 1148 numbers.push_back(JSFieldIndex(desc->field(i)));
1017 } 1149 }
1018 } 1150 }
1019 return "[" + Join(numbers, ",") + "]"; 1151 return "[" + Join(numbers, ",") + "]";
1020 } 1152 }
1021 1153
1022 string OneofGroupList(const Descriptor* desc) { 1154 string OneofGroupList(const Descriptor* desc) {
1023 // List of arrays (one per oneof), each of which is a list of field indices 1155 // List of arrays (one per oneof), each of which is a list of field indices
1024 std::vector<string> oneof_entries; 1156 std::vector<string> oneof_entries;
1025 for (int i = 0; i < desc->oneof_decl_count(); i++) { 1157 for (int i = 0; i < desc->oneof_decl_count(); i++) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1069 } 1201 }
1070 } 1202 }
1071 1203
1072 return type.substr(prefix); 1204 return type.substr(prefix);
1073 } 1205 }
1074 1206
1075 string JSExtensionsObjectName(const GeneratorOptions& options, 1207 string JSExtensionsObjectName(const GeneratorOptions& options,
1076 const FileDescriptor* from_file, 1208 const FileDescriptor* from_file,
1077 const Descriptor* desc) { 1209 const Descriptor* desc) {
1078 if (desc->full_name() == "google.protobuf.bridge.MessageSet") { 1210 if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
1079 // TODO(haberman): fix this for the IMPORT_COMMONJS case. 1211 // TODO(haberman): fix this for the kImportCommonJs case.
1080 return "jspb.Message.messageSetExtensions"; 1212 return "jspb.Message.messageSetExtensions";
1081 } else { 1213 } else {
1082 return MaybeCrossFileRef(options, from_file, desc) + ".extensions"; 1214 return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
1083 } 1215 }
1084 } 1216 }
1085 1217
1218 static const int kMapKeyField = 1;
1219 static const int kMapValueField = 2;
1220
1221 const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) {
1222 assert(field->is_map());
1223 return field->message_type()->FindFieldByNumber(kMapKeyField);
1224 }
1225
1226 const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) {
1227 assert(field->is_map());
1228 return field->message_type()->FindFieldByNumber(kMapValueField);
1229 }
1230
1086 string FieldDefinition(const GeneratorOptions& options, 1231 string FieldDefinition(const GeneratorOptions& options,
1087 const FieldDescriptor* field) { 1232 const FieldDescriptor* field) {
1088 string qualifier = field->is_repeated() ? "repeated" : 1233 if (IsMap(options, field)) {
1089 (field->is_optional() ? "optional" : "required"); 1234 const FieldDescriptor* key_field = MapFieldKey(field);
1090 string type, name; 1235 const FieldDescriptor* value_field = MapFieldValue(field);
1091 if (field->type() == FieldDescriptor::TYPE_ENUM || 1236 string key_type = ProtoTypeName(options, key_field);
1092 field->type() == FieldDescriptor::TYPE_MESSAGE) { 1237 string value_type;
1093 type = RelativeTypeName(field); 1238 if (value_field->type() == FieldDescriptor::TYPE_ENUM ||
1094 name = field->name(); 1239 value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
1095 } else if (field->type() == FieldDescriptor::TYPE_GROUP) { 1240 value_type = RelativeTypeName(value_field);
1096 type = "group"; 1241 } else {
1097 name = field->message_type()->name(); 1242 value_type = ProtoTypeName(options, value_field);
1243 }
1244 return StringPrintf("map<%s, %s> %s = %d;",
1245 key_type.c_str(),
1246 value_type.c_str(),
1247 field->name().c_str(),
1248 field->number());
1098 } else { 1249 } else {
1099 type = ProtoTypeName(options, field); 1250 string qualifier = field->is_repeated() ? "repeated" :
1100 name = field->name(); 1251 (field->is_optional() ? "optional" : "required");
1252 string type, name;
1253 if (field->type() == FieldDescriptor::TYPE_ENUM ||
1254 field->type() == FieldDescriptor::TYPE_MESSAGE) {
1255 type = RelativeTypeName(field);
1256 name = field->name();
1257 } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
1258 type = "group";
1259 name = field->message_type()->name();
1260 } else {
1261 type = ProtoTypeName(options, field);
1262 name = field->name();
1263 }
1264 return StringPrintf("%s %s %s = %d;",
1265 qualifier.c_str(),
1266 type.c_str(),
1267 name.c_str(),
1268 field->number());
1101 } 1269 }
1102 return StringPrintf("%s %s %s = %d;",
1103 qualifier.c_str(),
1104 type.c_str(),
1105 name.c_str(),
1106 field->number());
1107 } 1270 }
1108 1271
1109 string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) { 1272 string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
1110 string comments; 1273 string comments;
1111 if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) { 1274 if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
1112 comments += 1275 comments +=
1113 " * Note that Boolean fields may be set to 0/1 when serialized from " 1276 " * Note that Boolean fields may be set to 0/1 when serialized from "
1114 "a Java server.\n" 1277 "a Java server.\n"
1115 " * You should avoid comparisons like {@code val === true/false} in " 1278 " * You should avoid comparisons like {@code val === true/false} in "
1116 "those cases.\n"; 1279 "those cases.\n";
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after
1184 1347
1185 int pivot = -1; 1348 int pivot = -1;
1186 if (IsExtendable(desc)) { 1349 if (IsExtendable(desc)) {
1187 pivot = ((max_field_number + 1) < kDefaultPivot) ? 1350 pivot = ((max_field_number + 1) < kDefaultPivot) ?
1188 (max_field_number + 1) : kDefaultPivot; 1351 (max_field_number + 1) : kDefaultPivot;
1189 } 1352 }
1190 1353
1191 return SimpleItoa(pivot); 1354 return SimpleItoa(pivot);
1192 } 1355 }
1193 1356
1194 // Returns true for fields that represent "null" as distinct from the default 1357 // Whether this field represents presence. For fields with presence, we
1195 // value. See http://go/proto3#heading=h.kozewqqcqhuz for more information. 1358 // generate extra methods (clearFoo() and hasFoo()) for this field.
1196 bool HasFieldPresence(const FieldDescriptor* field) { 1359 bool HasFieldPresence(const GeneratorOptions& options,
1197 return 1360 const FieldDescriptor* field) {
1198 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || 1361 if (field->is_repeated() || field->is_map()) {
1199 (field->containing_oneof() != NULL) || 1362 // We say repeated fields and maps don't have presence, but we still do
1200 (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3); 1363 // generate clearFoo() methods for them through a special case elsewhere.
1201 } 1364 return false;
1365 }
1202 1366
1203 // For proto3 fields without presence, returns a string representing the default 1367 if (UseBrokenPresenceSemantics(options, field)) {
1204 // value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more 1368 // Proto3 files with broken presence semantics have field presence.
1205 // information. 1369 return true;
1206 string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { 1370 }
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 1371
1215 case FieldDescriptor::CPPTYPE_ENUM: 1372 return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
1216 case FieldDescriptor::CPPTYPE_FLOAT: 1373 field->containing_oneof() != NULL ||
1217 case FieldDescriptor::CPPTYPE_DOUBLE: 1374 field->file()->syntax() == FileDescriptor::SYNTAX_PROTO2;
1218 return "0";
1219
1220 case FieldDescriptor::CPPTYPE_BOOL:
1221 return "false";
1222
1223 case FieldDescriptor::CPPTYPE_STRING: // includes BYTES
1224 return "\"\"";
1225
1226 default:
1227 // MESSAGE is handled separately.
1228 assert(false);
1229 return "";
1230 }
1231 } 1375 }
1232 1376
1233 // We use this to implement the semantics that same file can be generated 1377 // 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, 1378 // 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 1379 // but we keep a set of which descriptors were the final one for a given
1236 // filename. 1380 // filename.
1237 class FileDeduplicator { 1381 class FileDeduplicator {
1238 public: 1382 public:
1239 explicit FileDeduplicator(const GeneratorOptions& options) 1383 explicit FileDeduplicator(const GeneratorOptions& options)
1240 : error_on_conflict_(options.error_on_name_conflict) {} 1384 : error_on_conflict_(options.error_on_name_conflict) {}
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after
1401 } 1545 }
1402 1546
1403 printer->Print("\n"); 1547 printer->Print("\n");
1404 } 1548 }
1405 1549
1406 void Generator::FindProvidesForMessage( 1550 void Generator::FindProvidesForMessage(
1407 const GeneratorOptions& options, 1551 const GeneratorOptions& options,
1408 io::Printer* printer, 1552 io::Printer* printer,
1409 const Descriptor* desc, 1553 const Descriptor* desc,
1410 std::set<string>* provided) const { 1554 std::set<string>* provided) const {
1555 if (IgnoreMessage(options, desc)) {
1556 return;
1557 }
1558
1411 string name = GetPath(options, desc); 1559 string name = GetPath(options, desc);
1412 provided->insert(name); 1560 provided->insert(name);
1413 1561
1414 for (int i = 0; i < desc->enum_type_count(); i++) { 1562 for (int i = 0; i < desc->enum_type_count(); i++) {
1415 FindProvidesForEnum(options, printer, desc->enum_type(i), 1563 FindProvidesForEnum(options, printer, desc->enum_type(i),
1416 provided); 1564 provided);
1417 } 1565 }
1418 for (int i = 0; i < desc->nested_type_count(); i++) { 1566 for (int i = 0; i < desc->nested_type_count(); i++) {
1419 FindProvidesForMessage(options, printer, desc->nested_type(i), 1567 FindProvidesForMessage(options, printer, desc->nested_type(i),
1420 provided); 1568 provided);
(...skipping 14 matching lines...) Expand all
1435 const vector<const FieldDescriptor*>& fields, 1583 const vector<const FieldDescriptor*>& fields,
1436 std::set<string>* provided) const { 1584 std::set<string>* provided) const {
1437 for (int i = 0; i < fields.size(); i++) { 1585 for (int i = 0; i < fields.size(); i++) {
1438 const FieldDescriptor* field = fields[i]; 1586 const FieldDescriptor* field = fields[i];
1439 1587
1440 if (IgnoreField(field)) { 1588 if (IgnoreField(field)) {
1441 continue; 1589 continue;
1442 } 1590 }
1443 1591
1444 string name = 1592 string name =
1445 GetPath(options, field->file()) + "." + JSObjectFieldName(field); 1593 GetPath(options, field->file()) + "." +
1594 JSObjectFieldName(options, field);
1446 provided->insert(name); 1595 provided->insert(name);
1447 } 1596 }
1448 } 1597 }
1449 1598
1450 void Generator::GenerateProvides(const GeneratorOptions& options, 1599 void Generator::GenerateProvides(const GeneratorOptions& options,
1451 io::Printer* printer, 1600 io::Printer* printer,
1452 std::set<string>* provided) const { 1601 std::set<string>* provided) const {
1453 for (std::set<string>::iterator it = provided->begin(); 1602 for (std::set<string>::iterator it = provided->begin();
1454 it != provided->end(); ++it) { 1603 it != provided->end(); ++it) {
1455 printer->Print("goog.provide('$name$');\n", 1604 if (options.import_style == GeneratorOptions::kImportClosure) {
1456 "name", *it); 1605 printer->Print("goog.provide('$name$');\n", "name", *it);
1606 } else {
1607 // We aren't using Closure's import system, but we use goog.exportSymbol()
1608 // to construct the expected tree of objects, eg.
1609 //
1610 // goog.exportSymbol('foo.bar.Baz', null, this);
1611 //
1612 // // Later generated code expects foo.bar = {} to exist:
1613 // foo.bar.Baz = function() { /* ... */ }
1614 printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
1615 *it);
1616 }
1457 } 1617 }
1458 } 1618 }
1459 1619
1460 void Generator::GenerateRequiresForMessage(const GeneratorOptions& options, 1620 void Generator::GenerateRequiresForMessage(const GeneratorOptions& options,
1461 io::Printer* printer, 1621 io::Printer* printer,
1462 const Descriptor* desc, 1622 const Descriptor* desc,
1463 std::set<string>* provided) const { 1623 std::set<string>* provided) const {
1464 std::set<string> required; 1624 std::set<string> required;
1465 std::set<string> forwards; 1625 std::set<string> forwards;
1466 bool have_message = false; 1626 bool have_message = false;
1467 FindRequiresForMessage(options, desc, 1627 FindRequiresForMessage(options, desc,
1468 &required, &forwards, &have_message); 1628 &required, &forwards, &have_message);
1469 1629
1470 GenerateRequiresImpl(options, printer, &required, &forwards, provided, 1630 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1471 /* require_jspb = */ have_message, 1631 /* require_jspb = */ have_message,
1472 /* require_extension = */ HasExtensions(desc)); 1632 /* require_extension = */ HasExtensions(desc));
1473 } 1633 }
1474 1634
1475 void Generator::GenerateRequiresForLibrary( 1635 void Generator::GenerateRequiresForLibrary(
1476 const GeneratorOptions& options, io::Printer* printer, 1636 const GeneratorOptions& options, io::Printer* printer,
1477 const vector<const FileDescriptor*>& files, 1637 const vector<const FileDescriptor*>& files,
1478 std::set<string>* provided) const { 1638 std::set<string>* provided) const {
1479 GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE); 1639 GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
1480 // For Closure imports we need to import every message type individually. 1640 // For Closure imports we need to import every message type individually.
1481 std::set<string> required; 1641 std::set<string> required;
1482 std::set<string> forwards; 1642 std::set<string> forwards;
1483 bool have_extensions = false; 1643 bool have_extensions = false;
1484 bool have_message = false; 1644 bool have_message = false;
1485 1645
1486 for (int i = 0; i < files.size(); i++) { 1646 for (int i = 0; i < files.size(); i++) {
1487 for (int j = 0; j < files[i]->message_type_count(); j++) { 1647 for (int j = 0; j < files[i]->message_type_count(); j++) {
1488 FindRequiresForMessage(options, 1648 const Descriptor* desc = files[i]->message_type(j);
1489 files[i]->message_type(j), 1649 if (!IgnoreMessage(options, desc)) {
1490 &required, &forwards, &have_message); 1650 FindRequiresForMessage(options, desc, &required, &forwards,
1651 &have_message);
1652 }
1491 } 1653 }
1654
1492 if (!have_extensions && HasExtensions(files[i])) { 1655 if (!have_extensions && HasExtensions(files[i])) {
1493 have_extensions = true; 1656 have_extensions = true;
1494 } 1657 }
1495 1658
1496 for (int j = 0; j < files[i]->extension_count(); j++) { 1659 for (int j = 0; j < files[i]->extension_count(); j++) {
1497 const FieldDescriptor* extension = files[i]->extension(j); 1660 const FieldDescriptor* extension = files[i]->extension(j);
1498 if (IgnoreField(extension)) { 1661 if (IgnoreField(extension)) {
1499 continue; 1662 continue;
1500 } 1663 }
1501 if (extension->containing_type()->full_name() != 1664 if (extension->containing_type()->full_name() !=
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
1541 if (require_jspb) { 1704 if (require_jspb) {
1542 printer->Print( 1705 printer->Print(
1543 "goog.require('jspb.Message');\n"); 1706 "goog.require('jspb.Message');\n");
1544 if (options.binary) { 1707 if (options.binary) {
1545 printer->Print( 1708 printer->Print(
1546 "goog.require('jspb.BinaryReader');\n" 1709 "goog.require('jspb.BinaryReader');\n"
1547 "goog.require('jspb.BinaryWriter');\n"); 1710 "goog.require('jspb.BinaryWriter');\n");
1548 } 1711 }
1549 } 1712 }
1550 if (require_extension) { 1713 if (require_extension) {
1714 if (options.binary) {
1715 printer->Print(
1716 "goog.require('jspb.ExtensionFieldBinaryInfo');\n");
1717 }
1551 printer->Print( 1718 printer->Print(
1552 "goog.require('jspb.ExtensionFieldInfo');\n"); 1719 "goog.require('jspb.ExtensionFieldInfo');\n");
1553 } 1720 }
1554 1721
1555 std::set<string>::iterator it; 1722 std::set<string>::iterator it;
1556 for (it = required->begin(); it != required->end(); ++it) { 1723 for (it = required->begin(); it != required->end(); ++it) {
1557 if (provided->find(*it) != provided->end()) { 1724 if (provided->find(*it) != provided->end()) {
1558 continue; 1725 continue;
1559 } 1726 }
1560 printer->Print("goog.require('$name$');\n", 1727 printer->Print("goog.require('$name$');\n",
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
1616 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM && 1783 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1617 // N.B.: file-level extensions with enum type do *not* create 1784 // N.B.: file-level extensions with enum type do *not* create
1618 // dependencies, as per original codegen. 1785 // dependencies, as per original codegen.
1619 !(field->is_extension() && field->extension_scope() == NULL)) { 1786 !(field->is_extension() && field->extension_scope() == NULL)) {
1620 if (options.add_require_for_enums) { 1787 if (options.add_require_for_enums) {
1621 required->insert(GetPath(options, field->enum_type())); 1788 required->insert(GetPath(options, field->enum_type()));
1622 } else { 1789 } else {
1623 forwards->insert(GetPath(options, field->enum_type())); 1790 forwards->insert(GetPath(options, field->enum_type()));
1624 } 1791 }
1625 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 1792 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1626 required->insert(GetPath(options, field->message_type())); 1793 if (!IgnoreMessage(options, field->message_type())) {
1794 required->insert(GetPath(options, field->message_type()));
1795 }
1627 } 1796 }
1628 } 1797 }
1629 1798
1630 void Generator::FindRequiresForExtension(const GeneratorOptions& options, 1799 void Generator::FindRequiresForExtension(const GeneratorOptions& options,
1631 const FieldDescriptor* field, 1800 const FieldDescriptor* field,
1632 std::set<string>* required, 1801 std::set<string>* required,
1633 std::set<string>* forwards) const { 1802 std::set<string>* forwards) const {
1634 if (field->containing_type()->full_name() != "google.protobuf.bridge.Message Set") { 1803 if (field->containing_type()->full_name() != "google.protobuf.bridge.Message Set") {
1635 required->insert(GetPath(options, field->containing_type())); 1804 required->insert(GetPath(options, field->containing_type()));
1636 } 1805 }
(...skipping 15 matching lines...) Expand all
1652 GenerateClass(options, printer, file->message_type(i)); 1821 GenerateClass(options, printer, file->message_type(i));
1653 } 1822 }
1654 for (int i = 0; i < file->enum_type_count(); i++) { 1823 for (int i = 0; i < file->enum_type_count(); i++) {
1655 GenerateEnum(options, printer, file->enum_type(i)); 1824 GenerateEnum(options, printer, file->enum_type(i));
1656 } 1825 }
1657 } 1826 }
1658 1827
1659 void Generator::GenerateClass(const GeneratorOptions& options, 1828 void Generator::GenerateClass(const GeneratorOptions& options,
1660 io::Printer* printer, 1829 io::Printer* printer,
1661 const Descriptor* desc) const { 1830 const Descriptor* desc) const {
1831 if (IgnoreMessage(options, desc)) {
1832 return;
1833 }
1834
1662 if (!NamespaceOnly(desc)) { 1835 if (!NamespaceOnly(desc)) {
1663 printer->Print("\n"); 1836 printer->Print("\n");
1664 GenerateClassConstructor(options, printer, desc); 1837 GenerateClassConstructor(options, printer, desc);
1665 GenerateClassFieldInfo(options, printer, desc); 1838 GenerateClassFieldInfo(options, printer, desc);
1666 1839
1667 1840
1668 GenerateClassToObject(options, printer, desc); 1841 GenerateClassToObject(options, printer, desc);
1669 if (options.binary) { 1842 if (options.binary) {
1670 // These must come *before* the extension-field info generation in 1843 // These must come *before* the extension-field info generation in
1671 // GenerateClassRegistration so that references to the binary 1844 // GenerateClassRegistration so that references to the binary
1672 // serialization/deserialization functions may be placed in the extension 1845 // serialization/deserialization functions may be placed in the extension
1673 // objects. 1846 // objects.
1674 GenerateClassDeserializeBinary(options, printer, desc); 1847 GenerateClassDeserializeBinary(options, printer, desc);
1675 GenerateClassSerializeBinary(options, printer, desc); 1848 GenerateClassSerializeBinary(options, printer, desc);
1676 } 1849 }
1677 GenerateClassClone(options, printer, desc);
1678 GenerateClassRegistration(options, printer, desc); 1850 GenerateClassRegistration(options, printer, desc);
1679 GenerateClassFields(options, printer, desc); 1851 GenerateClassFields(options, printer, desc);
1680 if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.Messa geSet") { 1852 if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.Messa geSet") {
1681 GenerateClassExtensionFieldInfo(options, printer, desc); 1853 GenerateClassExtensionFieldInfo(options, printer, desc);
1682 } 1854 }
1683 1855
1684 if (options.import_style != GeneratorOptions:: IMPORT_CLOSURE) { 1856 if (options.import_style != GeneratorOptions::kImportClosure) {
1685 for (int i = 0; i < desc->extension_count(); i++) { 1857 for (int i = 0; i < desc->extension_count(); i++) {
1686 GenerateExtension(options, printer, desc->extension(i)); 1858 GenerateExtension(options, printer, desc->extension(i));
1687 } 1859 }
1688 } 1860 }
1689 } 1861 }
1690 1862
1691 // Recurse on nested types. 1863 // Recurse on nested types.
1692 for (int i = 0; i < desc->enum_type_count(); i++) { 1864 for (int i = 0; i < desc->enum_type_count(); i++) {
1693 GenerateEnum(options, printer, desc->enum_type(i)); 1865 GenerateEnum(options, printer, desc->enum_type(i));
1694 } 1866 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1732 "goog.inherits($classname$, jspb.Message);\n" 1904 "goog.inherits($classname$, jspb.Message);\n"
1733 "if (goog.DEBUG && !COMPILED) {\n" 1905 "if (goog.DEBUG && !COMPILED) {\n"
1734 " $classname$.displayName = '$classname$';\n" 1906 " $classname$.displayName = '$classname$';\n"
1735 "}\n", 1907 "}\n",
1736 "classname", GetPath(options, desc)); 1908 "classname", GetPath(options, desc));
1737 } 1909 }
1738 1910
1739 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options, 1911 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
1740 io::Printer* printer, 1912 io::Printer* printer,
1741 const Descriptor* desc) const { 1913 const Descriptor* desc) const {
1742 if (HasRepeatedFields(desc)) { 1914 if (HasRepeatedFields(options, desc)) {
1743 printer->Print( 1915 printer->Print(
1744 "/**\n" 1916 "/**\n"
1745 " * List of repeated fields within this message type.\n" 1917 " * List of repeated fields within this message type.\n"
1746 " * @private {!Array<number>}\n" 1918 " * @private {!Array<number>}\n"
1747 " * @const\n" 1919 " * @const\n"
1748 " */\n" 1920 " */\n"
1749 "$classname$$rptfieldarray$ = $rptfields$;\n" 1921 "$classname$$rptfieldarray$ = $rptfields$;\n"
1750 "\n", 1922 "\n",
1751 "classname", GetPath(options, desc), 1923 "classname", GetPath(options, desc),
1752 "rptfieldarray", kRepeatedFieldArrayName, 1924 "rptfieldarray", kRepeatedFieldArrayName,
1753 "rptfields", RepeatedFieldNumberList(desc)); 1925 "rptfields", RepeatedFieldNumberList(options, desc));
1754 } 1926 }
1755 1927
1756 if (HasOneofFields(desc)) { 1928 if (HasOneofFields(desc)) {
1757 printer->Print( 1929 printer->Print(
1758 "/**\n" 1930 "/**\n"
1759 " * Oneof group definitions for this message. Each group defines the " 1931 " * Oneof group definitions for this message. Each group defines the "
1760 "field\n" 1932 "field\n"
1761 " * numbers belonging to that group. When of these fields' value is " 1933 " * numbers belonging to that group. When of these fields' value is "
1762 "set, all\n" 1934 "set, all\n"
1763 " * other fields in the group are cleared. During deserialization, if " 1935 " * other fields in the group are cleared. During deserialization, if "
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after
1912 " obj.$$jspbMessageInstance = msg;\n" 2084 " obj.$$jspbMessageInstance = msg;\n"
1913 " }\n" 2085 " }\n"
1914 " return obj;\n" 2086 " return obj;\n"
1915 "};\n" 2087 "};\n"
1916 "}\n" 2088 "}\n"
1917 "\n" 2089 "\n"
1918 "\n", 2090 "\n",
1919 "classname", GetPath(options, desc)); 2091 "classname", GetPath(options, desc));
1920 } 2092 }
1921 2093
2094 void Generator::GenerateFieldValueExpression(io::Printer* printer,
2095 const char *obj_reference,
2096 const FieldDescriptor* field,
2097 bool use_default) const {
2098 bool is_float_or_double =
2099 field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2100 field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE;
2101 if (use_default) {
2102 if (is_float_or_double) {
2103 // Coerce "Nan" and "Infinity" to actual float values.
2104 //
2105 // This will change null to 0, but that doesn't matter since we're getting
2106 // with a default.
2107 printer->Print("+");
2108 }
2109
2110 printer->Print(
2111 "jspb.Message.getFieldWithDefault($obj$, $index$, $default$)",
2112 "obj", obj_reference,
2113 "index", JSFieldIndex(field),
2114 "default", JSFieldDefault(field));
2115 } else {
2116 if (is_float_or_double) {
2117 if (field->is_required()) {
2118 // Use "+" to convert all fields to numeric (including null).
2119 printer->Print(
2120 "+jspb.Message.getField($obj$, $index$)",
2121 "index", JSFieldIndex(field),
2122 "obj", obj_reference);
2123 } else {
2124 // Converts "NaN" and "Infinity" while preserving null.
2125 printer->Print(
2126 "jspb.Message.get$cardinality$FloatingPointField($obj$, $index$)",
2127 "cardinality", field->is_repeated() ? "Repeated" : "Optional",
2128 "index", JSFieldIndex(field),
2129 "obj", obj_reference);
2130 }
2131 } else {
2132 printer->Print("jspb.Message.getField($obj$, $index$)",
2133 "index", JSFieldIndex(field),
2134 "obj", obj_reference);
2135 }
2136 }
2137 }
2138
1922 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, 2139 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
1923 io::Printer* printer, 2140 io::Printer* printer,
1924 const FieldDescriptor* field) const { 2141 const FieldDescriptor* field) const {
1925 printer->Print("$fieldname$: ", 2142 printer->Print("$fieldname$: ",
1926 "fieldname", JSObjectFieldName(field)); 2143 "fieldname", JSObjectFieldName(options, field));
1927 2144
1928 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2145 if (IsMap(options, field)) {
2146 printer->Print("(f = msg.get$name$()) ? f.toArray() : []",
2147 "name", JSGetterName(options, field));
2148 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1929 // Message field. 2149 // Message field.
1930 if (field->is_repeated()) { 2150 if (field->is_repeated()) {
1931 { 2151 {
1932 printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n" 2152 printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
1933 " $type$.toObject, includeInstance)", 2153 " $type$.toObject, includeInstance)",
1934 "getter", JSGetterName(field), 2154 "getter", JSGetterName(options, field),
1935 "type", SubmessageTypeRef(options, field)); 2155 "type", SubmessageTypeRef(options, field));
1936 } 2156 }
1937 } else { 2157 } else {
1938 printer->Print("(f = msg.get$getter$()) && " 2158 printer->Print("(f = msg.get$getter$()) && "
1939 "$type$.toObject(includeInstance, f)", 2159 "$type$.toObject(includeInstance, f)",
1940 "getter", JSGetterName(field), 2160 "getter", JSGetterName(options, field),
1941 "type", SubmessageTypeRef(options, field)); 2161 "type", SubmessageTypeRef(options, field));
1942 } 2162 }
2163 } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
2164 // For bytes fields we want to always return the B64 data.
2165 printer->Print("msg.get$getter$()",
2166 "getter", JSGetterName(options, field, BYTES_B64));
1943 } else { 2167 } else {
1944 // Simple field (singular or repeated). 2168 bool use_default = field->has_default_value();
1945 if ((!HasFieldPresence(field) && !field->is_repeated()) || 2169
1946 field->type() == FieldDescriptor::TYPE_BYTES) { 2170 if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
1947 // Delegate to the generated get<field>() method in order not to duplicate 2171 // Repeated fields get initialized to their default in the constructor
1948 // the proto3-field-default-value or byte-coercion logic here. 2172 // (why?), so we emit a plain getField() call for them.
1949 printer->Print("msg.get$getter$()", 2173 !field->is_repeated() && !UseBrokenPresenceSemantics(options, field)) {
1950 "getter", JSGetterName(field, BYTES_B64)); 2174 // Proto3 puts all defaults (including implicit defaults) in toObject().
1951 } else { 2175 // But for proto2 we leave the existing semantics unchanged: unset fields
1952 if (field->has_default_value()) { 2176 // without default are unset.
1953 printer->Print("jspb.Message.getField(msg, $index$) == null ? " 2177 use_default = true;
1954 "$defaultValue$ : ",
1955 "index", JSFieldIndex(field),
1956 "defaultValue", JSFieldDefault(field));
1957 }
1958 if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
1959 field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) {
1960 if (field->is_repeated()) {
1961 printer->Print("jspb.Message.getRepeatedFloatingPointField("
1962 "msg, $index$)",
1963 "index", JSFieldIndex(field));
1964 } else if (field->is_optional() && !field->has_default_value()) {
1965 printer->Print("jspb.Message.getOptionalFloatingPointField("
1966 "msg, $index$)",
1967 "index", JSFieldIndex(field));
1968 } else {
1969 // Convert "NaN" to NaN.
1970 printer->Print("+jspb.Message.getField(msg, $index$)",
1971 "index", JSFieldIndex(field));
1972 }
1973 } else {
1974 printer->Print("jspb.Message.getField(msg, $index$)",
1975 "index", JSFieldIndex(field));
1976 }
1977 } 2178 }
2179
2180 // We don't implement this by calling the accessors, because the semantics
2181 // of the accessors are changing independently of the toObject() semantics.
2182 // We are migrating the accessors to return defaults instead of null, but
2183 // it may take longer to migrate toObject (or we might not want to do it at
2184 // all). So we want to generate independent code.
2185 GenerateFieldValueExpression(printer, "msg", field, use_default);
1978 } 2186 }
1979 } 2187 }
1980 2188
1981 void Generator::GenerateClassFromObject(const GeneratorOptions& options, 2189 void Generator::GenerateClassFromObject(const GeneratorOptions& options,
1982 io::Printer* printer, 2190 io::Printer* printer,
1983 const Descriptor* desc) const { 2191 const Descriptor* desc) const {
1984 printer->Print( 2192 printer->Print(
1985 "if (jspb.Message.GENERATE_FROM_OBJECT) {\n" 2193 "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
1986 "/**\n" 2194 "/**\n"
1987 " * Loads data from an object into a new instance of this proto.\n" 2195 " * Loads data from an object into a new instance of this proto.\n"
(...skipping 13 matching lines...) Expand all
2001 printer->Print( 2209 printer->Print(
2002 " return msg;\n" 2210 " return msg;\n"
2003 "};\n" 2211 "};\n"
2004 "}\n"); 2212 "}\n");
2005 } 2213 }
2006 2214
2007 void Generator::GenerateClassFieldFromObject( 2215 void Generator::GenerateClassFieldFromObject(
2008 const GeneratorOptions& options, 2216 const GeneratorOptions& options,
2009 io::Printer* printer, 2217 io::Printer* printer,
2010 const FieldDescriptor* field) const { 2218 const FieldDescriptor* field) const {
2011 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2219 if (IsMap(options, field)) {
2220 // `msg` is a newly-constructed message object that has not yet built any
2221 // map containers wrapping underlying arrays, so we can simply directly set
2222 // the array here without fear of a stale wrapper.
2223 printer->Print(
2224 " goog.isDef(obj.$name$) && "
2225 "jspb.Message.setField(msg, $index$, obj.$name$);\n",
2226 "name", JSObjectFieldName(options, field),
2227 "index", JSFieldIndex(field));
2228 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2012 // Message field (singular or repeated) 2229 // Message field (singular or repeated)
2013 if (field->is_repeated()) { 2230 if (field->is_repeated()) {
2014 { 2231 {
2015 printer->Print( 2232 printer->Print(
2016 " goog.isDef(obj.$name$) && " 2233 " goog.isDef(obj.$name$) && "
2017 "jspb.Message.setRepeatedWrapperField(\n" 2234 "jspb.Message.setRepeatedWrapperField(\n"
2018 " msg, $index$, goog.array.map(obj.$name$, function(i) {\n" 2235 " msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
2019 " return $fieldclass$.fromObject(i);\n" 2236 " return $fieldclass$.fromObject(i);\n"
2020 " }));\n", 2237 " }));\n",
2021 "name", JSObjectFieldName(field), 2238 "name", JSObjectFieldName(options, field),
2022 "index", JSFieldIndex(field), 2239 "index", JSFieldIndex(field),
2023 "fieldclass", SubmessageTypeRef(options, field)); 2240 "fieldclass", SubmessageTypeRef(options, field));
2024 } 2241 }
2025 } else { 2242 } else {
2026 printer->Print( 2243 printer->Print(
2027 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n" 2244 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
2028 " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n", 2245 " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
2029 "name", JSObjectFieldName(field), 2246 "name", JSObjectFieldName(options, field),
2030 "index", JSFieldIndex(field), 2247 "index", JSFieldIndex(field),
2031 "fieldclass", SubmessageTypeRef(options, field)); 2248 "fieldclass", SubmessageTypeRef(options, field));
2032 } 2249 }
2033 } else { 2250 } else {
2034 // Simple (primitive) field. 2251 // Simple (primitive) field.
2035 printer->Print( 2252 printer->Print(
2036 " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, " 2253 " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
2037 "obj.$name$);\n", 2254 "obj.$name$);\n",
2038 "name", JSObjectFieldName(field), 2255 "name", JSObjectFieldName(options, field),
2039 "index", JSFieldIndex(field)); 2256 "index", JSFieldIndex(field));
2040 } 2257 }
2041 } 2258 }
2042 2259
2043 void Generator::GenerateClassClone(const GeneratorOptions& options,
2044 io::Printer* printer,
2045 const Descriptor* desc) const {
2046 printer->Print(
2047 "/**\n"
2048 " * Creates a deep clone of this proto. No data is shared with the "
2049 "original.\n"
2050 " * @return {!$name$} The clone.\n"
2051 " */\n"
2052 "$name$.prototype.cloneMessage = function() {\n"
2053 " return /** @type {!$name$} */ (jspb.Message.cloneMessage(this));\n"
2054 "};\n\n\n",
2055 "name", GetPath(options, desc));
2056 }
2057
2058 void Generator::GenerateClassRegistration(const GeneratorOptions& options, 2260 void Generator::GenerateClassRegistration(const GeneratorOptions& options,
2059 io::Printer* printer, 2261 io::Printer* printer,
2060 const Descriptor* desc) const { 2262 const Descriptor* desc) const {
2061 // Register any extensions defined inside this message type. 2263 // Register any extensions defined inside this message type.
2062 for (int i = 0; i < desc->extension_count(); i++) { 2264 for (int i = 0; i < desc->extension_count(); i++) {
2063 const FieldDescriptor* extension = desc->extension(i); 2265 const FieldDescriptor* extension = desc->extension(i);
2064 if (ShouldGenerateExtension(extension)) { 2266 if (ShouldGenerateExtension(extension)) {
2065 GenerateExtension(options, printer, extension); 2267 GenerateExtension(options, printer, extension);
2066 } 2268 }
2067 } 2269 }
2068 2270
2069 } 2271 }
2070 2272
2071 void Generator::GenerateClassFields(const GeneratorOptions& options, 2273 void Generator::GenerateClassFields(const GeneratorOptions& options,
2072 io::Printer* printer, 2274 io::Printer* printer,
2073 const Descriptor* desc) const { 2275 const Descriptor* desc) const {
2074 for (int i = 0; i < desc->field_count(); i++) { 2276 for (int i = 0; i < desc->field_count(); i++) {
2075 if (!IgnoreField(desc->field(i))) { 2277 if (!IgnoreField(desc->field(i))) {
2076 GenerateClassField(options, printer, desc->field(i)); 2278 GenerateClassField(options, printer, desc->field(i));
2077 } 2279 }
2078 } 2280 }
2079 } 2281 }
2080 2282
2081 void GenerateBytesWrapper(const GeneratorOptions& options, 2283 void GenerateBytesWrapper(const GeneratorOptions& options,
2082 io::Printer* printer, 2284 io::Printer* printer,
2083 const FieldDescriptor* field, 2285 const FieldDescriptor* field,
2084 BytesMode bytes_mode) { 2286 BytesMode bytes_mode) {
2085 string type = 2287 string type = JSFieldTypeAnnotation(
2086 JSFieldTypeAnnotation(options, field, 2288 options, field,
2087 /* force_optional = */ false, 2289 /* is_setter_argument = */ false,
2088 /* force_present = */ !HasFieldPresence(field), 2290 /* force_present = */ false,
2089 /* singular_if_not_packed = */ false, 2291 /* singular_if_not_packed = */ false, bytes_mode);
2090 bytes_mode);
2091 printer->Print( 2292 printer->Print(
2092 "/**\n" 2293 "/**\n"
2093 " * $fielddef$\n" 2294 " * $fielddef$\n"
2094 "$comment$" 2295 "$comment$"
2095 " * This is a type-conversion wrapper around `get$defname$()`\n" 2296 " * This is a type-conversion wrapper around `get$defname$()`\n"
2096 " * @return {$type$}\n" 2297 " * @return {$type$}\n"
2097 " */\n" 2298 " */\n"
2098 "$class$.prototype.get$name$ = function() {\n" 2299 "$class$.prototype.get$name$ = function() {\n"
2099 " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n" 2300 " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
2100 " this.get$defname$()));\n" 2301 " this.get$defname$()));\n"
2101 "};\n" 2302 "};\n"
2102 "\n" 2303 "\n"
2103 "\n", 2304 "\n",
2104 "fielddef", FieldDefinition(options, field), 2305 "fielddef", FieldDefinition(options, field),
2105 "comment", FieldComments(field, bytes_mode), 2306 "comment", FieldComments(field, bytes_mode),
2106 "type", type, 2307 "type", type,
2107 "class", GetPath(options, field->containing_type()), 2308 "class", GetPath(options, field->containing_type()),
2108 "name", JSGetterName(field, bytes_mode), 2309 "name", JSGetterName(options, field, bytes_mode),
2109 "list", field->is_repeated() ? "List" : "", 2310 "list", field->is_repeated() ? "List" : "",
2110 "suffix", JSByteGetterSuffix(bytes_mode), 2311 "suffix", JSByteGetterSuffix(bytes_mode),
2111 "defname", JSGetterName(field, BYTES_DEFAULT)); 2312 "defname", JSGetterName(options, field, BYTES_DEFAULT));
2112 } 2313 }
2113 2314
2114 2315
2115 void Generator::GenerateClassField(const GeneratorOptions& options, 2316 void Generator::GenerateClassField(const GeneratorOptions& options,
2116 io::Printer* printer, 2317 io::Printer* printer,
2117 const FieldDescriptor* field) const { 2318 const FieldDescriptor* field) const {
2118 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2319 if (IsMap(options, field)) {
2320 const FieldDescriptor* key_field = MapFieldKey(field);
2321 const FieldDescriptor* value_field = MapFieldValue(field);
2322 // Map field: special handling to instantiate the map object on demand.
2323 string key_type =
2324 JSFieldTypeAnnotation(
2325 options, key_field,
2326 /* is_setter_argument = */ false,
2327 /* force_present = */ true,
2328 /* singular_if_not_packed = */ false);
2329 string value_type =
2330 JSFieldTypeAnnotation(
2331 options, value_field,
2332 /* is_setter_argument = */ false,
2333 /* force_present = */ true,
2334 /* singular_if_not_packed = */ false);
2335
2119 printer->Print( 2336 printer->Print(
2120 "/**\n" 2337 "/**\n"
2121 " * $fielddef$\n" 2338 " * $fielddef$\n"
2339 " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
2340 " * empty, instead returning `undefined`\n"
2341 " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
2342 " */\n",
2343 "fielddef", FieldDefinition(options, field),
2344 "keytype", key_type,
2345 "valuetype", value_type);
2346 printer->Print(
2347 "$class$.prototype.get$name$ = function(opt_noLazyCreate) {\n"
2348 " return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
2349 "class", GetPath(options, field->containing_type()),
2350 "name", JSGetterName(options, field),
2351 "keytype", key_type,
2352 "valuetype", value_type);
2353 printer->Print(
2354 " jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
2355 "index", JSFieldIndex(field));
2356
2357 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2358 printer->Print(",\n"
2359 " $messageType$",
2360 "messageType", GetPath(options, value_field->message_type()));
2361 } else if (options.binary) {
2362 printer->Print(",\n"
2363 " null");
2364 }
2365
2366 printer->Print(
2367 "));\n");
2368
2369 printer->Print(
2370 "};\n"
2371 "\n"
2372 "\n");
2373 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2374 // Message field: special handling in order to wrap the underlying data
2375 // array with a message object.
2376
2377 printer->Print(
2378 "/**\n"
2379 " * $fielddef$\n"
2122 "$comment$" 2380 "$comment$"
2123 " * @return {$type$}\n" 2381 " * @return {$type$}\n"
2124 " */\n", 2382 " */\n",
2125 "fielddef", FieldDefinition(options, field), 2383 "fielddef", FieldDefinition(options, field),
2126 "comment", FieldComments(field, BYTES_DEFAULT), 2384 "comment", FieldComments(field, BYTES_DEFAULT),
2127 "type", JSFieldTypeAnnotation(options, field, 2385 "type", JSFieldTypeAnnotation(options, field,
2128 /* force_optional = */ false, 2386 /* is_setter_argument = */ false,
2129 /* force_present = */ false, 2387 /* force_present = */ false,
2130 /* singular_if_not_packed = */ false)); 2388 /* singular_if_not_packed = */ false));
2131 printer->Print( 2389 printer->Print(
2132 "$class$.prototype.get$name$ = function() {\n" 2390 "$class$.prototype.get$name$ = function() {\n"
2133 " return /** @type{$type$} */ (\n" 2391 " return /** @type{$type$} */ (\n"
2134 " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, " 2392 " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
2135 "$index$$required$));\n" 2393 "$index$$required$));\n"
2136 "};\n" 2394 "};\n"
2137 "\n" 2395 "\n"
2138 "\n", 2396 "\n",
2139 "class", GetPath(options, field->containing_type()), 2397 "class", GetPath(options, field->containing_type()),
2140 "name", JSGetterName(field), 2398 "name", JSGetterName(options, field),
2141 "type", JSFieldTypeAnnotation(options, field, 2399 "type", JSFieldTypeAnnotation(options, field,
2142 /* force_optional = */ false, 2400 /* is_setter_argument = */ false,
2143 /* force_present = */ false, 2401 /* force_present = */ false,
2144 /* singular_if_not_packed = */ false), 2402 /* singular_if_not_packed = */ false),
2145 "rpt", (field->is_repeated() ? "Repeated" : ""), 2403 "rpt", (field->is_repeated() ? "Repeated" : ""),
2146 "index", JSFieldIndex(field), 2404 "index", JSFieldIndex(field),
2147 "wrapperclass", SubmessageTypeRef(options, field), 2405 "wrapperclass", SubmessageTypeRef(options, field),
2148 "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ? 2406 "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
2149 ", 1" : "")); 2407 ", 1" : ""));
2150 printer->Print( 2408 printer->Print(
2151 "/** @param {$optionaltype$} value $returndoc$ */\n" 2409 "/** @param {$optionaltype$} value$returndoc$ */\n"
2152 "$class$.prototype.set$name$ = function(value) {\n" 2410 "$class$.prototype.set$name$ = function(value) {\n"
2153 " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(", 2411 " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
2154 "optionaltype", 2412 "optionaltype",
2155 JSFieldTypeAnnotation(options, field, 2413 JSFieldTypeAnnotation(options, field,
2156 /* force_optional = */ true, 2414 /* is_setter_argument = */ true,
2157 /* force_present = */ false, 2415 /* force_present = */ false,
2158 /* singular_if_not_packed = */ false), 2416 /* singular_if_not_packed = */ false),
2159 "returndoc", JSReturnDoc(options, field), 2417 "returndoc", JSReturnDoc(options, field),
2160 "class", GetPath(options, field->containing_type()), 2418 "class", GetPath(options, field->containing_type()),
2161 "name", JSGetterName(field), 2419 "name", JSGetterName(options, field),
2162 "oneoftag", (field->containing_oneof() ? "Oneof" : ""), 2420 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2163 "repeatedtag", (field->is_repeated() ? "Repeated" : "")); 2421 "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2164 2422
2165 printer->Print( 2423 printer->Print(
2166 "this, $index$$oneofgroup$, value);$returnvalue$\n" 2424 "this, $index$$oneofgroup$, value);$returnvalue$\n"
2167 "};\n" 2425 "};\n"
2168 "\n" 2426 "\n"
2169 "\n", 2427 "\n",
2170 "index", JSFieldIndex(field), 2428 "index", JSFieldIndex(field),
2171 "oneofgroup", (field->containing_oneof() ? 2429 "oneofgroup", (field->containing_oneof() ?
2172 (", " + JSOneofArray(options, field)) : ""), 2430 (", " + JSOneofArray(options, field)) : ""),
2173 "returnvalue", JSReturnClause(field)); 2431 "returnvalue", JSReturnClause(field));
2174 2432
2175 printer->Print( 2433 if (field->is_repeated()) {
2176 "$class$.prototype.clear$name$ = function() {\n" 2434 GenerateRepeatedMessageHelperMethods(options, printer, field);
2177 " this.set$name$($clearedvalue$);$returnvalue$\n" 2435 }
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 2436
2186 } else { 2437 } else {
2187 bool untyped = 2438 bool untyped =
2188 false; 2439 false;
2189 2440
2190 // Simple (primitive) field, either singular or repeated. 2441 // Simple (primitive) field, either singular or repeated.
2191 2442
2192 // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type; 2443 // 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 2444 // 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. 2445 // type is always base64 string, pending a LSC to migrate to typed getters.
2195 BytesMode bytes_mode = 2446 BytesMode bytes_mode =
2196 field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ? 2447 field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ?
2197 BYTES_B64 : BYTES_DEFAULT; 2448 BYTES_B64 : BYTES_DEFAULT;
2198 string typed_annotation = 2449 string typed_annotation = JSFieldTypeAnnotation(
2199 JSFieldTypeAnnotation(options, field, 2450 options, field,
2200 /* force_optional = */ false, 2451 /* is_setter_argument = */ false,
2201 /* force_present = */ !HasFieldPresence(field), 2452 /* force_present = */ false,
2202 /* singular_if_not_packed = */ false, 2453 /* singular_if_not_packed = */ false,
2203 /* bytes_mode = */ bytes_mode); 2454 /* bytes_mode = */ bytes_mode);
2204 if (untyped) { 2455 if (untyped) {
2205 printer->Print( 2456 printer->Print(
2206 "/**\n" 2457 "/**\n"
2207 " * @return {?} Raw field, untyped.\n" 2458 " * @return {?} Raw field, untyped.\n"
2208 " */\n"); 2459 " */\n");
2209 } else { 2460 } else {
2210 printer->Print( 2461 printer->Print(
2211 "/**\n" 2462 "/**\n"
2212 " * $fielddef$\n" 2463 " * $fielddef$\n"
2213 "$comment$" 2464 "$comment$"
2214 " * @return {$type$}\n" 2465 " * @return {$type$}\n"
2215 " */\n", 2466 " */\n",
2216 "fielddef", FieldDefinition(options, field), 2467 "fielddef", FieldDefinition(options, field),
2217 "comment", FieldComments(field, bytes_mode), 2468 "comment", FieldComments(field, bytes_mode),
2218 "type", typed_annotation); 2469 "type", typed_annotation);
2219 } 2470 }
2220 2471
2221 printer->Print( 2472 printer->Print(
2222 "$class$.prototype.get$name$ = function() {\n", 2473 "$class$.prototype.get$name$ = function() {\n",
2223 "class", GetPath(options, field->containing_type()), 2474 "class", GetPath(options, field->containing_type()),
2224 "name", JSGetterName(field)); 2475 "name", JSGetterName(options, field));
2225 2476
2226 if (untyped) { 2477 if (untyped) {
2227 printer->Print( 2478 printer->Print(
2228 " return "); 2479 " return ");
2229 } else { 2480 } else {
2230 printer->Print( 2481 printer->Print(
2231 " return /** @type {$type$} */ (", 2482 " return /** @type {$type$} */ (",
2232 "type", typed_annotation); 2483 "type", typed_annotation);
2233 } 2484 }
2234 2485
2235 // For proto3 fields without presence, use special getters that will return 2486 bool use_default = !ReturnsNullWhenUnset(options, field);
2236 // defaults when the field is unset, possibly constructing a value if 2487
2237 // required. 2488 // Raw fields with no default set should just return undefined.
2238 if (!HasFieldPresence(field) && !field->is_repeated()) { 2489 if (untyped && !field->has_default_value()) {
2239 printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)", 2490 use_default = false;
2240 "index", JSFieldIndex(field),
2241 "default", Proto3PrimitiveFieldDefault(field));
2242 } else {
2243 if (field->has_default_value()) {
2244 printer->Print("jspb.Message.getField(this, $index$) == null ? "
2245 "$defaultValue$ : ",
2246 "index", JSFieldIndex(field),
2247 "defaultValue", JSFieldDefault(field));
2248 }
2249 if (field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2250 field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE) {
2251 if (field->is_repeated()) {
2252 printer->Print("jspb.Message.getRepeatedFloatingPointField("
2253 "this, $index$)",
2254 "index", JSFieldIndex(field));
2255 } else if (field->is_optional() && !field->has_default_value()) {
2256 printer->Print("jspb.Message.getOptionalFloatingPointField("
2257 "this, $index$)",
2258 "index", JSFieldIndex(field));
2259 } else {
2260 // Convert "NaN" to NaN.
2261 printer->Print("+jspb.Message.getField(this, $index$)",
2262 "index", JSFieldIndex(field));
2263 }
2264 } else {
2265 printer->Print("jspb.Message.getField(this, $index$)",
2266 "index", JSFieldIndex(field));
2267 }
2268 } 2491 }
2269 2492
2493 // Repeated fields get initialized to their default in the constructor
2494 // (why?), so we emit a plain getField() call for them.
2495 if (field->is_repeated()) {
2496 use_default = false;
2497 }
2498
2499 GenerateFieldValueExpression(printer, "this", field, use_default);
2500
2270 if (untyped) { 2501 if (untyped) {
2271 printer->Print( 2502 printer->Print(
2272 ";\n" 2503 ";\n"
2273 "};\n" 2504 "};\n"
2274 "\n" 2505 "\n"
2275 "\n"); 2506 "\n");
2276 } else { 2507 } else {
2277 printer->Print( 2508 printer->Print(
2278 ");\n" 2509 ");\n"
2279 "};\n" 2510 "};\n"
2280 "\n" 2511 "\n"
2281 "\n"); 2512 "\n");
2282 } 2513 }
2283 2514
2284 if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) { 2515 if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
2285 GenerateBytesWrapper(options, printer, field, BYTES_B64); 2516 GenerateBytesWrapper(options, printer, field, BYTES_B64);
2286 GenerateBytesWrapper(options, printer, field, BYTES_U8); 2517 GenerateBytesWrapper(options, printer, field, BYTES_U8);
2287 } 2518 }
2288 2519
2289 if (untyped) { 2520 if (untyped) {
2290 printer->Print( 2521 printer->Print(
2291 "/**\n" 2522 "/**\n"
2292 " * @param {*} value $returndoc$\n" 2523 " * @param {*} value$returndoc$\n"
2293 " */\n", 2524 " */\n",
2294 "returndoc", JSReturnDoc(options, field)); 2525 "returndoc", JSReturnDoc(options, field));
2295 } else { 2526 } else {
2296 printer->Print( 2527 printer->Print(
2297 "/** @param {$optionaltype$} value $returndoc$ */\n", 2528 "/** @param {$optionaltype$} value$returndoc$ */\n", "optionaltype",
2298 "optionaltype", 2529 JSFieldTypeAnnotation(
2299 JSFieldTypeAnnotation(options, field, 2530 options, field,
2300 /* force_optional = */ true, 2531 /* is_setter_argument = */ true,
2301 /* force_present = */ !HasFieldPresence(field), 2532 /* force_present = */ false,
2302 /* singular_if_not_packed = */ false), 2533 /* singular_if_not_packed = */ false),
2303 "returndoc", JSReturnDoc(options, field)); 2534 "returndoc", JSReturnDoc(options, field));
2304 } 2535 }
2305 printer->Print( 2536 printer->Print(
2306 "$class$.prototype.set$name$ = function(value) {\n" 2537 "$class$.prototype.set$name$ = function(value) {\n"
2307 " jspb.Message.set$oneoftag$Field(this, $index$", 2538 " jspb.Message.set$oneoftag$Field(this, $index$",
2308 "class", GetPath(options, field->containing_type()), 2539 "class", GetPath(options, field->containing_type()),
2309 "name", JSGetterName(field), 2540 "name", JSGetterName(options, field),
2310 "oneoftag", (field->containing_oneof() ? "Oneof" : ""), 2541 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2311 "index", JSFieldIndex(field)); 2542 "index", JSFieldIndex(field));
2312 printer->Print( 2543 printer->Print(
2313 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n" 2544 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
2314 "};\n" 2545 "};\n"
2315 "\n" 2546 "\n"
2316 "\n", 2547 "\n",
2317 "type", 2548 "type",
2318 untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "", 2549 untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2319 "typeclose", untyped ? ")" : "", 2550 "typeclose", untyped ? ")" : "",
2320 "oneofgroup", 2551 "oneofgroup",
2321 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) 2552 (field->containing_oneof() ? (", " + JSOneofArray(options, field))
2322 : ""), 2553 : ""),
2323 "returnvalue", JSReturnClause(field), "rptvalueinit", 2554 "returnvalue", JSReturnClause(field), "rptvalueinit",
2324 (field->is_repeated() ? " || []" : "")); 2555 (field->is_repeated() ? " || []" : ""));
2325 2556
2326 if (untyped) { 2557 if (untyped) {
2327 printer->Print( 2558 printer->Print(
2328 "/**\n" 2559 "/**\n"
2329 " * Clears the value. $returndoc$\n" 2560 " * Clears the value.$returndoc$\n"
2330 " */\n", 2561 " */\n",
2331 "returndoc", JSReturnDoc(options, field)); 2562 "returndoc", JSReturnDoc(options, field));
2332 } 2563 }
2333 2564
2334 if (HasFieldPresence(field)) { 2565
2335 printer->Print( 2566 if (field->is_repeated()) {
2336 "$class$.prototype.clear$name$ = function() {\n" 2567 GenerateRepeatedPrimitiveHelperMethods(options, printer, field, untyped);
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 } 2568 }
2352 } 2569 }
2570
2571 // Generate clearFoo() method for map fields, repeated fields, and other
2572 // fields with presence.
2573 if (IsMap(options, field)) {
2574 printer->Print(
2575 "$class$.prototype.clear$name$ = function() {\n"
2576 " this.get$name$().clear();$returnvalue$\n"
2577 "};\n"
2578 "\n"
2579 "\n",
2580 "class", GetPath(options, field->containing_type()),
2581 "name", JSGetterName(options, field),
2582 "returnvalue", JSReturnClause(field));
2583 } else if (field->is_repeated() ||
2584 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
2585 !field->is_required())) {
2586 // Fields where we can delegate to the regular setter.
2587 printer->Print(
2588 "$class$.prototype.clear$name$ = function() {\n"
2589 " this.set$name$($clearedvalue$);$returnvalue$\n"
2590 "};\n"
2591 "\n"
2592 "\n",
2593 "class", GetPath(options, field->containing_type()),
2594 "name", JSGetterName(options, field),
2595 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2596 "returnvalue", JSReturnClause(field));
2597 } else if (HasFieldPresence(options, field)) {
2598 // Fields where we can't delegate to the regular setter because it doesn't
2599 // accept "undefined" as an argument.
2600 printer->Print(
2601 "$class$.prototype.clear$name$ = function() {\n"
2602 " jspb.Message.set$maybeoneof$Field(this, "
2603 "$index$$maybeoneofgroup$, ",
2604 "class", GetPath(options, field->containing_type()),
2605 "name", JSGetterName(options, field),
2606 "maybeoneof", (field->containing_oneof() ? "Oneof" : ""),
2607 "maybeoneofgroup", (field->containing_oneof() ?
2608 (", " + JSOneofArray(options, field)) : ""),
2609 "index", JSFieldIndex(field));
2610 printer->Print(
2611 "$clearedvalue$);$returnvalue$\n"
2612 "};\n"
2613 "\n"
2614 "\n",
2615 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2616 "returnvalue", JSReturnClause(field));
2617 }
2618
2619 if (HasFieldPresence(options, field)) {
2620 printer->Print(
2621 "/**\n"
2622 " * Returns whether this field is set.\n"
2623 " * @return {!boolean}\n"
2624 " */\n"
2625 "$class$.prototype.has$name$ = function() {\n"
2626 " return jspb.Message.getField(this, $index$) != null;\n"
2627 "};\n"
2628 "\n"
2629 "\n",
2630 "class", GetPath(options, field->containing_type()),
2631 "name", JSGetterName(options, field),
2632 "index", JSFieldIndex(field));
2633 }
2353 } 2634 }
2354 2635
2636 void Generator::GenerateRepeatedPrimitiveHelperMethods(
2637 const GeneratorOptions& options, io::Printer* printer,
2638 const FieldDescriptor* field, bool untyped) const {
2639 printer->Print(
2640 "/**\n"
2641 " * @param {!$optionaltype$} value\n"
2642 " * @param {number=} opt_index\n"
2643 " */\n"
2644 "$class$.prototype.add$name$ = function(value, opt_index) {\n"
2645 " jspb.Message.addToRepeatedField(this, $index$",
2646 "class", GetPath(options, field->containing_type()), "name",
2647 JSGetterName(options, field, BYTES_DEFAULT,
2648 /* drop_list = */ true),
2649 "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "index",
2650 JSFieldIndex(field));
2651 printer->Print(
2652 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, opt_index);\n"
2653 "};\n"
2654 "\n"
2655 "\n",
2656 "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
2657 "typeclose", untyped ? ")" : "", "oneofgroup",
2658 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
2659 "rptvalueinit", "");
2660 }
2661
2662 void Generator::GenerateRepeatedMessageHelperMethods(
2663 const GeneratorOptions& options, io::Printer* printer,
2664 const FieldDescriptor* field) const {
2665 printer->Print(
2666 "/**\n"
2667 " * @param {!$optionaltype$=} opt_value\n"
2668 " * @param {number=} opt_index\n"
2669 " * @return {!$optionaltype$}\n"
2670 " */\n"
2671 "$class$.prototype.add$name$ = function(opt_value, opt_index) {\n"
2672 " return jspb.Message.addTo$repeatedtag$WrapperField(",
2673 "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class",
2674 GetPath(options, field->containing_type()), "name",
2675 JSGetterName(options, field, BYTES_DEFAULT,
2676 /* drop_list = */ true),
2677 "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2678
2679 printer->Print(
2680 "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n"
2681 "};\n"
2682 "\n"
2683 "\n",
2684 "index", JSFieldIndex(field), "oneofgroup",
2685 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
2686 "ctor", GetPath(options, field->message_type()));
2687 }
2688
2355 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options, 2689 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
2356 io::Printer* printer, 2690 io::Printer* printer,
2357 const Descriptor* desc) const { 2691 const Descriptor* desc) const {
2358 if (IsExtendable(desc)) { 2692 if (IsExtendable(desc)) {
2359 printer->Print( 2693 printer->Print(
2360 "\n" 2694 "\n"
2361 "/**\n" 2695 "/**\n"
2362 " * The extensions registered with this message class. This is a " 2696 " * The extensions registered with this message class. This is a "
2363 "map of\n" 2697 "map of\n"
2364 " * extension field number to fieldInfo object.\n" 2698 " * extension field number to fieldInfo object.\n"
2365 " *\n" 2699 " *\n"
2366 " * For example:\n" 2700 " * For example:\n"
2367 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, " 2701 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2368 "ctor: proto.example.MyMessage} }\n" 2702 "ctor: proto.example.MyMessage} }\n"
2369 " *\n" 2703 " *\n"
2370 " * fieldName contains the JsCompiler renamed field name property " 2704 " * fieldName contains the JsCompiler renamed field name property "
2371 "so that it\n" 2705 "so that it\n"
2372 " * works in OPTIMIZED mode.\n" 2706 " * works in OPTIMIZED mode.\n"
2373 " *\n" 2707 " *\n"
2374 " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n" 2708 " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
2375 " */\n" 2709 " */\n"
2376 "$class$.extensions = {};\n" 2710 "$class$.extensions = {};\n"
2377 "\n", 2711 "\n",
2378 "class", GetPath(options, desc)); 2712 "class", GetPath(options, desc));
2713
2714 if (options.binary) {
2715 printer->Print(
2716 "\n"
2717 "/**\n"
2718 " * The extensions registered with this message class. This is a "
2719 "map of\n"
2720 " * extension field number to fieldInfo object.\n"
2721 " *\n"
2722 " * For example:\n"
2723 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2724 "ctor: proto.example.MyMessage} }\n"
2725 " *\n"
2726 " * fieldName contains the JsCompiler renamed field name property "
2727 "so that it\n"
2728 " * works in OPTIMIZED mode.\n"
2729 " *\n"
2730 " * @type {!Object.<number, jspb.ExtensionFieldBinaryInfo>}\n"
2731 " */\n"
2732 "$class$.extensionsBinary = {};\n"
2733 "\n",
2734 "class", GetPath(options, desc));
2735 }
2379 } 2736 }
2380 } 2737 }
2381 2738
2382 2739
2383 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options, 2740 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
2384 io::Printer* printer, 2741 io::Printer* printer,
2385 const Descriptor* desc) const { 2742 const Descriptor* desc) const {
2386 // TODO(cfallin): Handle lazy decoding when requested by field option and/or 2743 // TODO(cfallin): Handle lazy decoding when requested by field option and/or
2387 // by default for 'bytes' fields and packed repeated fields. 2744 // by default for 'bytes' fields and packed repeated fields.
2388 2745
(...skipping 27 matching lines...) Expand all
2416 "class", GetPath(options, desc)); 2773 "class", GetPath(options, desc));
2417 2774
2418 for (int i = 0; i < desc->field_count(); i++) { 2775 for (int i = 0; i < desc->field_count(); i++) {
2419 GenerateClassDeserializeBinaryField(options, printer, desc->field(i)); 2776 GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
2420 } 2777 }
2421 2778
2422 printer->Print( 2779 printer->Print(
2423 " default:\n"); 2780 " default:\n");
2424 if (IsExtendable(desc)) { 2781 if (IsExtendable(desc)) {
2425 printer->Print( 2782 printer->Print(
2426 " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n" 2783 " jspb.Message.readBinaryExtension(msg, reader, $extobj$Binary,\n"
2427 " $class$.prototype.getExtension,\n" 2784 " $class$.prototype.getExtension,\n"
2428 " $class$.prototype.setExtension);\n" 2785 " $class$.prototype.setExtension);\n"
2429 " break;\n", 2786 " break;\n",
2430 "extobj", JSExtensionsObjectName(options, desc->file(), desc), 2787 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2431 "class", GetPath(options, desc)); 2788 "class", GetPath(options, desc));
2432 } else { 2789 } else {
2433 printer->Print( 2790 printer->Print(
2434 " reader.skipField();\n" 2791 " reader.skipField();\n"
2435 " break;\n"); 2792 " break;\n");
2436 } 2793 }
2437 2794
2438 printer->Print( 2795 printer->Print(
2439 " }\n" 2796 " }\n"
2440 " }\n" 2797 " }\n"
2441 " return msg;\n" 2798 " return msg;\n"
2442 "};\n" 2799 "};\n"
2443 "\n" 2800 "\n"
2444 "\n"); 2801 "\n");
2445 } 2802 }
2446 2803
2447 void Generator::GenerateClassDeserializeBinaryField( 2804 void Generator::GenerateClassDeserializeBinaryField(
2448 const GeneratorOptions& options, 2805 const GeneratorOptions& options,
2449 io::Printer* printer, 2806 io::Printer* printer,
2450 const FieldDescriptor* field) const { 2807 const FieldDescriptor* field) const {
2451 2808
2452 printer->Print(" case $num$:\n", 2809 printer->Print(" case $num$:\n",
2453 "num", SimpleItoa(field->number())); 2810 "num", SimpleItoa(field->number()));
2454 2811
2455 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2812 if (IsMap(options, field)) {
2813 const FieldDescriptor* key_field = MapFieldKey(field);
2814 const FieldDescriptor* value_field = MapFieldValue(field);
2456 printer->Print( 2815 printer->Print(
2457 " var value = new $fieldclass$;\n" 2816 " var value = msg.get$name$();\n"
2458 " reader.read$msgOrGroup$($grpfield$value," 2817 " reader.readMessage(value, function(message, reader) {\n",
2459 "$fieldclass$.deserializeBinaryFromReader);\n", 2818 "name", JSGetterName(options, field));
2819
2820 printer->Print(" jspb.Map.deserializeBinary(message, reader, "
2821 "$keyReaderFn$, $valueReaderFn$",
2822 "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
2823 "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
2824
2825 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2826 printer->Print(", $messageType$.deserializeBinaryFromReader",
2827 "messageType", GetPath(options, value_field->message_type()));
2828 }
2829
2830 printer->Print(");\n");
2831 printer->Print(" });\n");
2832 } else {
2833 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2834 printer->Print(
2835 " var value = new $fieldclass$;\n"
2836 " reader.read$msgOrGroup$($grpfield$value,"
2837 "$fieldclass$.deserializeBinaryFromReader);\n",
2460 "fieldclass", SubmessageTypeRef(options, field), 2838 "fieldclass", SubmessageTypeRef(options, field),
2461 "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ? 2839 "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2462 "Group" : "Message", 2840 "Group" : "Message",
2463 "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ? 2841 "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2464 (SimpleItoa(field->number()) + ", ") : ""); 2842 (SimpleItoa(field->number()) + ", ") : "");
2465 } else { 2843 } else {
2466 printer->Print( 2844 printer->Print(
2467 " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", 2845 " var value = /** @type {$fieldtype$} */ "
2468 "fieldtype", JSFieldTypeAnnotation(options, field, false, true, 2846 "(reader.read$reader$());\n",
2469 /* singular_if_not_packed = */ true, 2847 "fieldtype", JSFieldTypeAnnotation(options, field, false, true,
2470 BYTES_U8), 2848 /* singular_if_not_packed */ true,
2471 "reader", JSBinaryReaderMethodName(field)); 2849 BYTES_U8),
2472 } 2850 "reader",
2851 JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
2852 }
2473 2853
2474 if (field->is_repeated() && !field->is_packed()) { 2854 if (field->is_repeated() && !field->is_packed()) {
2475 // Repeated fields receive a |value| one at at a time; append to array 2855 printer->Print(
2476 // returned by get$name$(). Annoyingly, we have to call 'set' after 2856 " msg.add$name$(value);\n", "name",
2477 // changing the array. 2857 JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
2478 printer->Print(" msg.get$name$().push(value);\n", "name", 2858 } else {
2479 JSGetterName(field)); 2859 // Singular fields, and packed repeated fields, receive a |value| either
2480 printer->Print(" msg.set$name$(msg.get$name$());\n", "name", 2860 // as the field's value or as the array of all the field's values; set
2481 JSGetterName(field)); 2861 // this as the field's value directly.
2482 } else { 2862 printer->Print(
2483 // Singular fields, and packed repeated fields, receive a |value| either as 2863 " msg.set$name$(value);\n",
2484 // the field's value or as the array of all the field's values; set this as 2864 "name", JSGetterName(options, field));
2485 // the field's value directly. 2865 }
2486 printer->Print(
2487 " msg.set$name$(value);\n",
2488 "name", JSGetterName(field));
2489 } 2866 }
2490 2867
2491 printer->Print(" break;\n"); 2868 printer->Print(" break;\n");
2492 } 2869 }
2493 2870
2494 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options, 2871 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
2495 io::Printer* printer, 2872 io::Printer* printer,
2496 const Descriptor* desc) const { 2873 const Descriptor* desc) const {
2497 printer->Print( 2874 printer->Print(
2498 "/**\n" 2875 "/**\n"
(...skipping 27 matching lines...) Expand all
2526 "$class$.prototype.serializeBinaryToWriter = function (writer) {\n" 2903 "$class$.prototype.serializeBinaryToWriter = function (writer) {\n"
2527 " var f = undefined;\n", 2904 " var f = undefined;\n",
2528 "class", GetPath(options, desc)); 2905 "class", GetPath(options, desc));
2529 2906
2530 for (int i = 0; i < desc->field_count(); i++) { 2907 for (int i = 0; i < desc->field_count(); i++) {
2531 GenerateClassSerializeBinaryField(options, printer, desc->field(i)); 2908 GenerateClassSerializeBinaryField(options, printer, desc->field(i));
2532 } 2909 }
2533 2910
2534 if (IsExtendable(desc)) { 2911 if (IsExtendable(desc)) {
2535 printer->Print( 2912 printer->Print(
2536 " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n" 2913 " jspb.Message.serializeBinaryExtensions(this, writer,\n"
2537 " $class$.prototype.getExtension);\n", 2914 " $extobj$Binary, $class$.prototype.getExtension);\n",
2538 "extobj", JSExtensionsObjectName(options, desc->file(), desc), 2915 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2539 "class", GetPath(options, desc)); 2916 "class", GetPath(options, desc));
2540 } 2917 }
2541 2918
2542 printer->Print( 2919 printer->Print(
2543 "};\n" 2920 "};\n"
2544 "\n" 2921 "\n"
2545 "\n"); 2922 "\n");
2546 } 2923 }
2547 2924
2548 void Generator::GenerateClassSerializeBinaryField( 2925 void Generator::GenerateClassSerializeBinaryField(
2549 const GeneratorOptions& options, 2926 const GeneratorOptions& options,
2550 io::Printer* printer, 2927 io::Printer* printer,
2551 const FieldDescriptor* field) const { 2928 const FieldDescriptor* field) const {
2552 printer->Print( 2929 if (HasFieldPresence(options, field) &&
2553 " f = this.get$name$();\n", 2930 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
2554 "name", JSGetterName(field, BYTES_U8)); 2931 string typed_annotation = JSFieldTypeAnnotation(
2932 options, field,
2933 /* is_setter_argument = */ false,
2934 /* force_present = */ false,
2935 /* singular_if_not_packed = */ false,
2936 /* bytes_mode = */ BYTES_DEFAULT);
2937 printer->Print(
2938 " f = /** @type {$type$} */ (jspb.Message.getField(this, $index$));\n",
2939 "index", JSFieldIndex(field),
2940 "type", typed_annotation);
2941 } else {
2942 printer->Print(
2943 " f = this.get$name$($nolazy$);\n",
2944 "name", JSGetterName(options, field, BYTES_U8),
2945 // No lazy creation for maps containers -- fastpath the empty case.
2946 "nolazy", (field->is_map()) ? "true" : "");
2947 }
2555 2948
2556 if (field->is_repeated()) { 2949 // Print an `if (condition)` statement that evaluates to true if the field
2950 // goes on the wire.
2951 if (IsMap(options, field)) {
2952 printer->Print(
2953 " if (f && f.getLength() > 0) {\n");
2954 } else if (field->is_repeated()) {
2557 printer->Print( 2955 printer->Print(
2558 " if (f.length > 0) {\n"); 2956 " if (f.length > 0) {\n");
2559 } else { 2957 } else {
2560 if (HasFieldPresence(field)) { 2958 if (HasFieldPresence(options, field)) {
2561 printer->Print( 2959 printer->Print(
2562 " if (f != null) {\n"); 2960 " if (f != null) {\n");
2563 } else { 2961 } else {
2564 // No field presence: serialize onto the wire only if value is 2962 // No field presence: serialize onto the wire only if value is
2565 // non-default. Defaults are documented here: 2963 // non-default. Defaults are documented here:
2566 // https://goto.google.com/lhdfm 2964 // https://goto.google.com/lhdfm
2567 switch (field->cpp_type()) { 2965 switch (field->cpp_type()) {
2568 case FieldDescriptor::CPPTYPE_INT32: 2966 case FieldDescriptor::CPPTYPE_INT32:
2569 case FieldDescriptor::CPPTYPE_INT64: 2967 case FieldDescriptor::CPPTYPE_INT64:
2570 case FieldDescriptor::CPPTYPE_UINT32: 2968 case FieldDescriptor::CPPTYPE_UINT32:
(...skipping 18 matching lines...) Expand all
2589 printer->Print( 2987 printer->Print(
2590 " if (f.length > 0) {\n"); 2988 " if (f.length > 0) {\n");
2591 break; 2989 break;
2592 default: 2990 default:
2593 assert(false); 2991 assert(false);
2594 break; 2992 break;
2595 } 2993 }
2596 } 2994 }
2597 } 2995 }
2598 2996
2997 // Write the field on the wire.
2998 if (IsMap(options, field)) {
2999 const FieldDescriptor* key_field = MapFieldKey(field);
3000 const FieldDescriptor* value_field = MapFieldValue(field);
3001 printer->Print(
3002 " f.serializeBinary($index$, writer, "
3003 "$keyWriterFn$, $valueWriterFn$",
3004 "index", SimpleItoa(field->number()),
3005 "keyWriterFn", JSBinaryWriterMethodName(options, key_field),
3006 "valueWriterFn", JSBinaryWriterMethodName(options, value_field));
3007
3008 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3009 printer->Print(", $messageType$.serializeBinaryToWriter",
3010 "messageType", GetPath(options, value_field->message_type()));
3011 }
3012
3013 printer->Print(");\n");
3014 } else {
3015 printer->Print(
3016 " writer.write$method$(\n"
3017 " $index$,\n"
3018 " f",
3019 "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
3020 "index", SimpleItoa(field->number()));
3021
3022 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
3023 !IsMap(options, field)) {
3024 printer->Print(
3025 ",\n"
3026 " $submsg$.serializeBinaryToWriter\n",
3027 "submsg", SubmessageTypeRef(options, field));
3028 } else {
3029 printer->Print("\n");
3030 }
3031
3032 printer->Print(
3033 " );\n");
3034 }
3035
3036 // Close the `if`.
2599 printer->Print( 3037 printer->Print(
2600 " writer.$writer$(\n"
2601 " $index$,\n"
2602 " f",
2603 "writer", JSBinaryWriterMethodName(field),
2604 "index", SimpleItoa(field->number()));
2605
2606 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2607 printer->Print(
2608 ",\n"
2609 " $submsg$.serializeBinaryToWriter\n",
2610 "submsg", SubmessageTypeRef(options, field));
2611 } else {
2612 printer->Print("\n");
2613 }
2614 printer->Print(
2615 " );\n"
2616 " }\n"); 3038 " }\n");
2617 } 3039 }
2618 3040
2619 void Generator::GenerateEnum(const GeneratorOptions& options, 3041 void Generator::GenerateEnum(const GeneratorOptions& options,
2620 io::Printer* printer, 3042 io::Printer* printer,
2621 const EnumDescriptor* enumdesc) const { 3043 const EnumDescriptor* enumdesc) const {
2622 printer->Print( 3044 printer->Print(
2623 "/**\n" 3045 "/**\n"
2624 " * @enum {number}\n" 3046 " * @enum {number}\n"
2625 " */\n" 3047 " */\n"
(...skipping 23 matching lines...) Expand all
2649 GetPath(options, field->file())); 3071 GetPath(options, field->file()));
2650 3072
2651 printer->Print( 3073 printer->Print(
2652 "\n" 3074 "\n"
2653 "/**\n" 3075 "/**\n"
2654 " * A tuple of {field number, class constructor} for the extension\n" 3076 " * A tuple of {field number, class constructor} for the extension\n"
2655 " * field named `$name$`.\n" 3077 " * field named `$name$`.\n"
2656 " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n" 3078 " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
2657 " */\n" 3079 " */\n"
2658 "$class$.$name$ = new jspb.ExtensionFieldInfo(\n", 3080 "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
2659 "name", JSObjectFieldName(field), 3081 "name", JSObjectFieldName(options, field),
2660 "class", extension_scope, 3082 "class", extension_scope,
2661 "extensionType", JSFieldTypeAnnotation( 3083 "extensionType", JSFieldTypeAnnotation(
2662 options, field, 3084 options, field,
2663 /* force_optional = */ false, 3085 /* is_setter_argument = */ false,
2664 /* force_present = */ true, 3086 /* force_present = */ true,
2665 /* singular_if_not_packed = */ false)); 3087 /* singular_if_not_packed = */ false));
2666 printer->Print( 3088 printer->Print(
2667 " $index$,\n" 3089 " $index$,\n"
2668 " {$name$: 0},\n" 3090 " {$name$: 0},\n"
2669 " $ctor$,\n" 3091 " $ctor$,\n"
2670 " /** @type {?function((boolean|undefined),!jspb.Message=): " 3092 " /** @type {?function((boolean|undefined),!jspb.Message=): "
2671 "!Object} */ (\n" 3093 "!Object} */ (\n"
2672 " $toObject$),\n" 3094 " $toObject$),\n"
2673 " $repeated$", 3095 " $repeated$);\n",
2674 "index", SimpleItoa(field->number()), 3096 "index", SimpleItoa(field->number()),
2675 "name", JSObjectFieldName(field), 3097 "name", JSObjectFieldName(options, field),
2676 "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? 3098 "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2677 SubmessageTypeRef(options, field) : string("null")), 3099 SubmessageTypeRef(options, field) : string("null")),
2678 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? 3100 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2679 (SubmessageTypeRef(options, field) + ".toObject") : 3101 (SubmessageTypeRef(options, field) + ".toObject") :
2680 string("null")), 3102 string("null")),
2681 "repeated", (field->is_repeated() ? "1" : "0")); 3103 "repeated", (field->is_repeated() ? "1" : "0"));
2682 3104
2683 if (options.binary) { 3105 if (options.binary) {
2684 printer->Print( 3106 printer->Print(
2685 ",\n" 3107 "\n"
2686 " jspb.BinaryReader.prototype.$binaryReaderFn$,\n" 3108 "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
2687 " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n" 3109 " $class$.$name$,\n"
3110 " $binaryReaderFn$,\n"
3111 " $binaryWriterFn$,\n"
2688 " $binaryMessageSerializeFn$,\n" 3112 " $binaryMessageSerializeFn$,\n"
2689 " $binaryMessageDeserializeFn$,\n" 3113 " $binaryMessageDeserializeFn$,\n",
2690 " $isPacked$);\n", 3114 "extendName", JSExtensionsObjectName(options, field->file(),
2691 "binaryReaderFn", JSBinaryReaderMethodName(field), 3115 field->containing_type()),
2692 "binaryWriterFn", JSBinaryWriterMethodName(field), 3116 "index", SimpleItoa(field->number()),
3117 "class", extension_scope,
3118 "name", JSObjectFieldName(options, field),
3119 "binaryReaderFn", JSBinaryReaderMethodName(options, field),
3120 "binaryWriterFn", JSBinaryWriterMethodName(options, field),
2693 "binaryMessageSerializeFn", 3121 "binaryMessageSerializeFn",
2694 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? 3122 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2695 (SubmessageTypeRef(options, field) + 3123 (SubmessageTypeRef(options, field) +
2696 ".serializeBinaryToWriter") : "null", 3124 ".serializeBinaryToWriter") : "undefined",
2697 "binaryMessageDeserializeFn", 3125 "binaryMessageDeserializeFn",
2698 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? 3126 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ?
2699 (SubmessageTypeRef(options, field) + 3127 (SubmessageTypeRef(options, field) +
2700 ".deserializeBinaryFromReader") : "null", 3128 ".deserializeBinaryFromReader") : "undefined");
3129
3130 printer->Print(
3131 " $isPacked$);\n",
2701 "isPacked", (field->is_packed() ? "true" : "false")); 3132 "isPacked", (field->is_packed() ? "true" : "false"));
2702 } else {
2703 printer->Print(");\n");
2704 } 3133 }
2705 3134
2706 printer->Print( 3135 printer->Print(
2707 "// This registers the extension field with the extended class, so that\n" 3136 "// This registers the extension field with the extended class, so that\n"
2708 "// toObject() will function correctly.\n" 3137 "// toObject() will function correctly.\n"
2709 "$extendName$[$index$] = $class$.$name$;\n" 3138 "$extendName$[$index$] = $class$.$name$;\n"
2710 "\n", 3139 "\n",
2711 "extendName", JSExtensionsObjectName(options, field->file(), 3140 "extendName", JSExtensionsObjectName(options, field->file(),
2712 field->containing_type()), 3141 field->containing_type()),
2713 "index", SimpleItoa(field->number()), 3142 "index", SimpleItoa(field->number()),
2714 "class", extension_scope, 3143 "class", extension_scope,
2715 "name", JSObjectFieldName(field)); 3144 "name", JSObjectFieldName(options, field));
2716 } 3145 }
2717 3146
2718 bool GeneratorOptions::ParseFromOptions( 3147 bool GeneratorOptions::ParseFromOptions(
2719 const vector< pair< string, string > >& options, 3148 const vector< pair< string, string > >& options,
2720 string* error) { 3149 string* error) {
2721 for (int i = 0; i < options.size(); i++) { 3150 for (int i = 0; i < options.size(); i++) {
2722 if (options[i].first == "add_require_for_enums") { 3151 if (options[i].first == "add_require_for_enums") {
2723 if (options[i].second != "") { 3152 if (options[i].second != "") {
2724 *error = "Unexpected option value for add_require_for_enums"; 3153 *error = "Unexpected option value for add_require_for_enums";
2725 return false; 3154 return false;
(...skipping 18 matching lines...) Expand all
2744 } 3173 }
2745 error_on_name_conflict = true; 3174 error_on_name_conflict = true;
2746 } else if (options[i].first == "output_dir") { 3175 } else if (options[i].first == "output_dir") {
2747 output_dir = options[i].second; 3176 output_dir = options[i].second;
2748 } else if (options[i].first == "namespace_prefix") { 3177 } else if (options[i].first == "namespace_prefix") {
2749 namespace_prefix = options[i].second; 3178 namespace_prefix = options[i].second;
2750 } else if (options[i].first == "library") { 3179 } else if (options[i].first == "library") {
2751 library = options[i].second; 3180 library = options[i].second;
2752 } else if (options[i].first == "import_style") { 3181 } else if (options[i].first == "import_style") {
2753 if (options[i].second == "closure") { 3182 if (options[i].second == "closure") {
2754 import_style = IMPORT_CLOSURE; 3183 import_style = kImportClosure;
2755 } else if (options[i].second == "commonjs") { 3184 } else if (options[i].second == "commonjs") {
2756 import_style = IMPORT_COMMONJS; 3185 import_style = kImportCommonJs;
2757 } else if (options[i].second == "browser") { 3186 } else if (options[i].second == "browser") {
2758 import_style = IMPORT_BROWSER; 3187 import_style = kImportBrowser;
2759 } else if (options[i].second == "es6") { 3188 } else if (options[i].second == "es6") {
2760 import_style = IMPORT_ES6; 3189 import_style = kImportEs6;
2761 } else { 3190 } else {
2762 *error = "Unknown import style " + options[i].second + ", expected " + 3191 *error = "Unknown import style " + options[i].second + ", expected " +
2763 "one of: closure, commonjs, browser, es6."; 3192 "one of: closure, commonjs, browser, es6.";
2764 } 3193 }
3194 } else if (options[i].first == "extension") {
3195 extension = options[i].second;
3196 } else if (options[i].first == "one_output_file_per_input_file") {
3197 if (!options[i].second.empty()) {
3198 *error = "Unexpected option value for one_output_file_per_input_file";
3199 return false;
3200 }
3201 one_output_file_per_input_file = true;
3202 } else if (options[i].first == "broken_proto3_semantics") {
3203 if (!options[i].second.empty()) {
3204 *error = "Unexpected option value for broken_proto3_semantics";
3205 return false;
3206 }
3207 broken_proto3_semantics = true;
2765 } else { 3208 } else {
2766 // Assume any other option is an output directory, as long as it is a bare 3209 // Assume any other option is an output directory, as long as it is a bare
2767 // `key` rather than a `key=value` option. 3210 // `key` rather than a `key=value` option.
2768 if (options[i].second != "") { 3211 if (options[i].second != "") {
2769 *error = "Unknown option: " + options[i].first; 3212 *error = "Unknown option: " + options[i].first;
2770 return false; 3213 return false;
2771 } 3214 }
2772 output_dir = options[i].first; 3215 output_dir = options[i].first;
2773 } 3216 }
2774 } 3217 }
2775 3218
2776 if (!library.empty() && import_style != IMPORT_CLOSURE) { 3219 if (import_style != kImportClosure &&
2777 *error = "The library option should only be used for " 3220 (add_require_for_enums || testonly || !library.empty() ||
2778 "import_style=closure"; 3221 error_on_name_conflict || broken_proto3_semantics ||
3222 extension != ".js" || one_output_file_per_input_file)) {
3223 *error =
3224 "The add_require_for_enums, testonly, library, error_on_name_conflict, "
3225 "broken_proto3_semantics, extension, and "
3226 "one_output_file_per_input_file options should only be used for "
3227 "import_style=closure";
3228 return false;
2779 } 3229 }
2780 3230
2781 return true; 3231 return true;
2782 } 3232 }
2783 3233
3234 GeneratorOptions::OutputMode GeneratorOptions::output_mode() const {
3235 // We use one output file per input file if we are not using Closure or if
3236 // this is explicitly requested.
3237 if (import_style != kImportClosure || one_output_file_per_input_file) {
3238 return kOneOutputFilePerInputFile;
3239 }
3240
3241 // If a library name is provided, we put everything in that one file.
3242 if (!library.empty()) {
3243 return kEverythingInOneFile;
3244 }
3245
3246 // Otherwise, we create one output file per type.
3247 return kOneOutputFilePerType;
3248 }
3249
2784 void Generator::GenerateFilesInDepOrder( 3250 void Generator::GenerateFilesInDepOrder(
2785 const GeneratorOptions& options, 3251 const GeneratorOptions& options,
2786 io::Printer* printer, 3252 io::Printer* printer,
2787 const vector<const FileDescriptor*>& files) const { 3253 const vector<const FileDescriptor*>& files) const {
2788 // Build a std::set over all files so that the DFS can detect when it recurses 3254 // 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. 3255 // into a dep not specified in the user's command line.
2790 std::set<const FileDescriptor*> all_files(files.begin(), files.end()); 3256 std::set<const FileDescriptor*> all_files(files.begin(), files.end());
2791 // Track the in-progress set of files that have been generated already. 3257 // Track the in-progress set of files that have been generated already.
2792 std::set<const FileDescriptor*> generated; 3258 std::set<const FileDescriptor*> generated;
2793 for (int i = 0; i < files.size(); i++) { 3259 for (int i = 0; i < files.size(); i++) {
(...skipping 26 matching lines...) Expand all
2820 GenerateClassesAndEnums(options, printer, root); 3286 GenerateClassesAndEnums(options, printer, root);
2821 } 3287 }
2822 } 3288 }
2823 3289
2824 void Generator::GenerateFile(const GeneratorOptions& options, 3290 void Generator::GenerateFile(const GeneratorOptions& options,
2825 io::Printer* printer, 3291 io::Printer* printer,
2826 const FileDescriptor* file) const { 3292 const FileDescriptor* file) const {
2827 GenerateHeader(options, printer); 3293 GenerateHeader(options, printer);
2828 3294
2829 // Generate "require" statements. 3295 // Generate "require" statements.
2830 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { 3296 if (options.import_style == GeneratorOptions::kImportCommonJs) {
2831 printer->Print("var jspb = require('google-protobuf');\n"); 3297 printer->Print("var jspb = require('google-protobuf');\n");
2832 printer->Print("var goog = jspb;\n"); 3298 printer->Print("var goog = jspb;\n");
2833 printer->Print("var global = Function('return this')();\n\n"); 3299 printer->Print("var global = Function('return this')();\n\n");
2834 3300
2835 for (int i = 0; i < file->dependency_count(); i++) { 3301 for (int i = 0; i < file->dependency_count(); i++) {
2836 const string& name = file->dependency(i)->name(); 3302 const string& name = file->dependency(i)->name();
2837 printer->Print( 3303 printer->Print(
2838 "var $alias$ = require('$file$');\n", 3304 "var $alias$ = require('$file$');\n",
2839 "alias", ModuleAlias(name), 3305 "alias", ModuleAlias(name),
2840 "file", GetRootPath(file->name()) + GetJSFilename(name)); 3306 "file", GetRootPath(file->name(), name) + GetJSFilename(options, name) );
2841 } 3307 }
2842 } 3308 }
2843 3309
2844 // We aren't using Closure's import system, but we use goog.exportSymbol()
2845 // to construct the expected tree of objects, eg.
2846 //
2847 // goog.exportSymbol('foo.bar.Baz', null, this);
2848 //
2849 // // Later generated code expects foo.bar = {} to exist:
2850 // foo.bar.Baz = function() { /* ... */ }
2851 set<string> provided; 3310 set<string> provided;
2852 3311 set<const FieldDescriptor*> extensions;
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++) { 3312 for (int i = 0; i < file->extension_count(); i++) {
2857 provided.insert(file->extension(i)->full_name()); 3313 // We honor the jspb::ignore option here only when working with
3314 // Closure-style imports. Use of this option is discouraged and so we want
3315 // to avoid adding new support for it.
3316 if (options.import_style == GeneratorOptions::kImportClosure &&
3317 IgnoreField(file->extension(i))) {
3318 continue;
3319 }
3320 provided.insert(GetPath(options, file) + "." +
3321 JSObjectFieldName(options, file->extension(i)));
3322 extensions.insert(file->extension(i));
2858 } 3323 }
2859 3324
2860 FindProvidesForFile(options, printer, file, &provided); 3325 FindProvidesForFile(options, printer, file, &provided);
2861 for (std::set<string>::iterator it = provided.begin(); 3326 GenerateProvides(options, printer, &provided);
2862 it != provided.end(); ++it) { 3327 vector<const FileDescriptor*> files;
2863 printer->Print("goog.exportSymbol('$name$', null, global);\n", 3328 files.push_back(file);
2864 "name", *it); 3329 if (options.import_style == GeneratorOptions::kImportClosure) {
3330 GenerateRequiresForLibrary(options, printer, files, &provided);
2865 } 3331 }
2866 3332
2867 GenerateClassesAndEnums(options, printer, file); 3333 GenerateClassesAndEnums(options, printer, file);
2868 3334
2869 // Extensions nested inside messages are emitted inside 3335 // Generate code for top-level extensions. Extensions nested inside messages
2870 // GenerateClassesAndEnums(). 3336 // are emitted inside GenerateClassesAndEnums().
2871 for (int i = 0; i < file->extension_count(); i++) { 3337 for (set<const FieldDescriptor*>::const_iterator it = extensions.begin();
2872 GenerateExtension(options, printer, file->extension(i)); 3338 it != extensions.end(); ++it) {
3339 GenerateExtension(options, printer, *it);
2873 } 3340 }
2874 3341
2875 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { 3342 if (options.import_style == GeneratorOptions::kImportCommonJs) {
2876 printer->Print("goog.object.extend(exports, $package$);\n", 3343 printer->Print("goog.object.extend(exports, $package$);\n",
2877 "package", GetPath(options, file)); 3344 "package", GetPath(options, file));
2878 } 3345 }
2879 } 3346 }
2880 3347
2881 bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, 3348 bool Generator::GenerateAll(const vector<const FileDescriptor*>& files,
2882 const string& parameter, 3349 const string& parameter,
2883 GeneratorContext* context, 3350 GeneratorContext* context,
2884 string* error) const { 3351 string* error) const {
2885 vector< pair< string, string > > option_pairs; 3352 vector< pair< string, string > > option_pairs;
2886 ParseGeneratorParameter(parameter, &option_pairs); 3353 ParseGeneratorParameter(parameter, &option_pairs);
2887 GeneratorOptions options; 3354 GeneratorOptions options;
2888 if (!options.ParseFromOptions(option_pairs, error)) { 3355 if (!options.ParseFromOptions(option_pairs, error)) {
2889 return false; 3356 return false;
2890 } 3357 }
2891 3358
2892 3359
2893 // There are three schemes for where output files go: 3360 if (options.output_mode() == GeneratorOptions::kEverythingInOneFile) {
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. 3361 // All output should go in a single file.
2901 string filename = options.output_dir + "/" + options.library + ".js"; 3362 string filename = options.output_dir + "/" + options.library +
3363 options.GetFileNameExtension();
2902 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open( filename)); 3364 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open( filename));
2903 GOOGLE_CHECK(output.get()); 3365 GOOGLE_CHECK(output.get());
2904 io::Printer printer(output.get(), '$'); 3366 io::Printer printer(output.get(), '$');
2905 3367
2906 // Pull out all extensions -- we need these to generate all 3368 // Pull out all extensions -- we need these to generate all
2907 // provides/requires. 3369 // provides/requires.
2908 vector<const FieldDescriptor*> extensions; 3370 vector<const FieldDescriptor*> extensions;
2909 for (int i = 0; i < files.size(); i++) { 3371 for (int i = 0; i < files.size(); i++) {
2910 for (int j = 0; j < files[i]->extension_count(); j++) { 3372 for (int j = 0; j < files[i]->extension_count(); j++) {
2911 const FieldDescriptor* extension = files[i]->extension(j); 3373 const FieldDescriptor* extension = files[i]->extension(j);
(...skipping 14 matching lines...) Expand all
2926 3388
2927 for (int i = 0; i < extensions.size(); i++) { 3389 for (int i = 0; i < extensions.size(); i++) {
2928 if (ShouldGenerateExtension(extensions[i])) { 3390 if (ShouldGenerateExtension(extensions[i])) {
2929 GenerateExtension(options, &printer, extensions[i]); 3391 GenerateExtension(options, &printer, extensions[i]);
2930 } 3392 }
2931 } 3393 }
2932 3394
2933 if (printer.failed()) { 3395 if (printer.failed()) {
2934 return false; 3396 return false;
2935 } 3397 }
2936 } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { 3398 } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerType) {
2937 set<const void*> allowed_set; 3399 set<const void*> allowed_set;
2938 if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) { 3400 if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) {
2939 return false; 3401 return false;
2940 } 3402 }
2941 3403
2942 for (int i = 0; i < files.size(); i++) { 3404 for (int i = 0; i < files.size(); i++) {
2943 const FileDescriptor* file = files[i]; 3405 const FileDescriptor* file = files[i];
2944 for (int j = 0; j < file->message_type_count(); j++) { 3406 for (int j = 0; j < file->message_type_count(); j++) {
2945 const Descriptor* desc = file->message_type(j); 3407 const Descriptor* desc = file->message_type(j);
2946 if (allowed_set.count(desc) == 0) { 3408 if (allowed_set.count(desc) == 0) {
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after
3018 GenerateTestOnly(options, &printer); 3480 GenerateTestOnly(options, &printer);
3019 GenerateRequiresForExtensions(options, &printer, fields, &provided); 3481 GenerateRequiresForExtensions(options, &printer, fields, &provided);
3020 3482
3021 for (int j = 0; j < files[i]->extension_count(); j++) { 3483 for (int j = 0; j < files[i]->extension_count(); j++) {
3022 if (ShouldGenerateExtension(files[i]->extension(j))) { 3484 if (ShouldGenerateExtension(files[i]->extension(j))) {
3023 GenerateExtension(options, &printer, files[i]->extension(j)); 3485 GenerateExtension(options, &printer, files[i]->extension(j));
3024 } 3486 }
3025 } 3487 }
3026 } 3488 }
3027 } 3489 }
3028 } else { 3490 } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
3029 // Generate one output file per input (.proto) file. 3491 // Generate one output file per input (.proto) file.
3030 3492
3031 for (int i = 0; i < files.size(); i++) { 3493 for (int i = 0; i < files.size(); i++) {
3032 const google::protobuf::FileDescriptor* file = files[i]; 3494 const google::protobuf::FileDescriptor* file = files[i];
3033 3495
3034 string filename = options.output_dir + "/" + GetJSFilename(file->name()); 3496 string filename =
3497 options.output_dir + "/" + GetJSFilename(options, file->name());
3035 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Ope n(filename)); 3498 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Ope n(filename));
3036 GOOGLE_CHECK(output.get()); 3499 GOOGLE_CHECK(output.get());
3037 io::Printer printer(output.get(), '$'); 3500 io::Printer printer(output.get(), '$');
3038 3501
3039 GenerateFile(options, &printer, file); 3502 GenerateFile(options, &printer, file);
3040 3503
3041 if (printer.failed()) { 3504 if (printer.failed()) {
3042 return false; 3505 return false;
3043 } 3506 }
3044 } 3507 }
3045 } 3508 }
3046 3509
3047 return true; 3510 return true;
3048 } 3511 }
3049 3512
3050 } // namespace js 3513 } // namespace js
3051 } // namespace compiler 3514 } // namespace compiler
3052 } // namespace protobuf 3515 } // namespace protobuf
3053 } // namespace google 3516 } // namespace google
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698