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

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

Issue 2599263002: third_party/protobuf: Update to HEAD (f52e188fe4) (Closed)
Patch Set: Address comments Created 3 years, 12 months 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 27 matching lines...) Expand all
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>
42 #include <utility> 42 #include <utility>
43 #include <vector> 43 #include <vector>
44 44
45 #include <google/protobuf/stubs/logging.h> 45 #include <google/protobuf/stubs/logging.h>
46 #include <google/protobuf/stubs/common.h> 46 #include <google/protobuf/stubs/common.h>
47 #include <google/protobuf/stubs/stringprintf.h> 47 #include <google/protobuf/stubs/stringprintf.h>
48 #include <google/protobuf/compiler/js/well_known_types_embed.h>
48 #include <google/protobuf/io/printer.h> 49 #include <google/protobuf/io/printer.h>
49 #include <google/protobuf/io/zero_copy_stream.h> 50 #include <google/protobuf/io/zero_copy_stream.h>
50 #include <google/protobuf/descriptor.pb.h> 51 #include <google/protobuf/descriptor.pb.h>
51 #include <google/protobuf/descriptor.h> 52 #include <google/protobuf/descriptor.h>
52 #include <google/protobuf/stubs/strutil.h> 53 #include <google/protobuf/stubs/strutil.h>
53 54
54 namespace google { 55 namespace google {
55 namespace protobuf { 56 namespace protobuf {
56 namespace compiler { 57 namespace compiler {
57 namespace js { 58 namespace js {
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
144 145
145 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto 146 // Returns a copy of |filename| with any trailing ".protodevel" or ".proto
146 // suffix stripped. 147 // suffix stripped.
147 // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc. 148 // TODO(haberman): Unify with copy in compiler/cpp/internal/helpers.cc.
148 string StripProto(const string& filename) { 149 string StripProto(const string& filename) {
149 const char* suffix = HasSuffixString(filename, ".protodevel") 150 const char* suffix = HasSuffixString(filename, ".protodevel")
150 ? ".protodevel" : ".proto"; 151 ? ".protodevel" : ".proto";
151 return StripSuffixString(filename, suffix); 152 return StripSuffixString(filename, suffix);
152 } 153 }
153 154
154 // Given a filename like foo/bar/baz.proto, returns the correspoding JavaScript 155 // Given a filename like foo/bar/baz.proto, returns the corresponding JavaScript
155 // file foo/bar/baz.js. 156 // file foo/bar/baz.js.
156 string GetJSFilename(const string& filename) { 157 string GetJSFilename(const GeneratorOptions& options, const string& filename) {
157 return StripProto(filename) + "_pb.js"; 158 return StripProto(filename) + options.GetFileNameExtension();
158 } 159 }
159 160
160 // Given a filename like foo/bar/baz.proto, returns the root directory 161 // Given a filename like foo/bar/baz.proto, returns the root directory
161 // path ../../ 162 // path ../../
162 string GetRootPath(const string& filename) { 163 string GetRootPath(const string& from_filename, const string& to_filename) {
163 size_t slashes = std::count(filename.begin(), filename.end(), '/'); 164 if (to_filename.find("google/protobuf") == 0) {
165 // Well-known types (.proto files in the google/protobuf directory) are
166 // assumed to come from the 'google-protobuf' npm package. We may want to
167 // generalize this exception later by letting others put generated code in
168 // their own npm packages.
169 return "google-protobuf/";
170 }
171
172 size_t slashes = std::count(from_filename.begin(), from_filename.end(), '/');
164 if (slashes == 0) { 173 if (slashes == 0) {
165 return "./"; 174 return "./";
166 } 175 }
167 string result = ""; 176 string result = "";
168 for (size_t i = 0; i < slashes; i++) { 177 for (size_t i = 0; i < slashes; i++) {
169 result += "../"; 178 result += "../";
170 } 179 }
171 return result; 180 return result;
172 } 181 }
173 182
(...skipping 19 matching lines...) Expand all
193 const FileDescriptor* file) { 202 const FileDescriptor* file) {
194 if (!options.namespace_prefix.empty()) { 203 if (!options.namespace_prefix.empty()) {
195 return options.namespace_prefix; 204 return options.namespace_prefix;
196 } else if (!file->package().empty()) { 205 } else if (!file->package().empty()) {
197 return "proto." + file->package(); 206 return "proto." + file->package();
198 } else { 207 } else {
199 return "proto"; 208 return "proto";
200 } 209 }
201 } 210 }
202 211
203 // Forward declare, so that GetPrefix can call this method, 212 // Returns the name of the message with a leading dot and taking into account
204 // which in turn, calls GetPrefix. 213 // nesting, for example ".OuterMessage.InnerMessage", or returns empty if
205 string GetPath(const GeneratorOptions& options, 214 // descriptor is null. This function does not handle namespacing, only message
206 const Descriptor* descriptor); 215 // nesting.
216 string GetNestedMessageName(const Descriptor* descriptor) {
217 if (descriptor == NULL) {
218 return "";
219 }
220 string result =
221 StripPrefixString(descriptor->full_name(), descriptor->file()->package());
222 // Add a leading dot if one is not already present.
223 if (!result.empty() && result[0] != '.') {
224 result = "." + result;
225 }
226 return result;
227 }
207 228
208 // Returns the path prefix for a message or enumeration that 229 // Returns the path prefix for a message or enumeration that
209 // lives under the given file and containing type. 230 // lives under the given file and containing type.
210 string GetPrefix(const GeneratorOptions& options, 231 string GetPrefix(const GeneratorOptions& options,
211 const FileDescriptor* file_descriptor, 232 const FileDescriptor* file_descriptor,
212 const Descriptor* containing_type) { 233 const Descriptor* containing_type) {
213 string prefix = ""; 234 string prefix =
214 235 GetPath(options, file_descriptor) + 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()) { 236 if (!prefix.empty()) {
222 prefix += "."; 237 prefix += ".";
223 } 238 }
224
225 return prefix; 239 return prefix;
226 } 240 }
227 241
228 242
229 // Returns the fully normalized JavaScript path for the given 243 // Returns the fully normalized JavaScript path for the given
230 // message descriptor. 244 // message descriptor.
231 string GetPath(const GeneratorOptions& options, 245 string GetPath(const GeneratorOptions& options,
232 const Descriptor* descriptor) { 246 const Descriptor* descriptor) {
233 return GetPrefix( 247 return GetPrefix(
234 options, descriptor->file(), 248 options, descriptor->file(),
(...skipping 23 matching lines...) Expand all
258 string GetPath(const GeneratorOptions& options, 272 string GetPath(const GeneratorOptions& options,
259 const EnumValueDescriptor* value_descriptor) { 273 const EnumValueDescriptor* value_descriptor) {
260 return GetPath( 274 return GetPath(
261 options, 275 options,
262 value_descriptor->type()) + "." + value_descriptor->name(); 276 value_descriptor->type()) + "." + value_descriptor->name();
263 } 277 }
264 278
265 string MaybeCrossFileRef(const GeneratorOptions& options, 279 string MaybeCrossFileRef(const GeneratorOptions& options,
266 const FileDescriptor* from_file, 280 const FileDescriptor* from_file,
267 const Descriptor* to_message) { 281 const Descriptor* to_message) {
268 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS && 282 if (options.import_style == GeneratorOptions::kImportCommonJs &&
269 from_file != to_message->file()) { 283 from_file != to_message->file()) {
270 // Cross-file ref in CommonJS needs to use the module alias instead of 284 // Cross-file ref in CommonJS needs to use the module alias instead of
271 // the global name. 285 // the global name.
272 return ModuleAlias(to_message->file()->name()) + "." + to_message->name(); 286 return ModuleAlias(to_message->file()->name()) +
287 GetNestedMessageName(to_message->containing_type()) + "." +
288 to_message->name();
273 } else { 289 } else {
274 // Within a single file we use a full name. 290 // Within a single file we use a full name.
275 return GetPath(options, to_message); 291 return GetPath(options, to_message);
276 } 292 }
277 } 293 }
278 294
279 string SubmessageTypeRef(const GeneratorOptions& options, 295 string SubmessageTypeRef(const GeneratorOptions& options,
280 const FieldDescriptor* field) { 296 const FieldDescriptor* field) {
281 GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE); 297 GOOGLE_CHECK(field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE);
282 return MaybeCrossFileRef(options, field->file(), field->message_type()); 298 return MaybeCrossFileRef(options, field->file(), field->message_type());
283 } 299 }
284 300
285 // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields 301 // - Object field name: LOWER_UNDERSCORE -> LOWER_CAMEL, except for group fields
286 // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate, 302 // (UPPER_CAMEL -> LOWER_CAMEL), with "List" (or "Map") appended if appropriate,
287 // and with reserved words triggering a "pb_" prefix. 303 // and with reserved words triggering a "pb_" prefix.
288 // - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields 304 // - Getters/setters: LOWER_UNDERSCORE -> UPPER_CAMEL, except for group fields
289 // (use the name directly), then append "List" if appropriate, then append "$" 305 // (use the name directly), then append "List" if appropriate, then append "$"
290 // if resulting name is equal to a reserved word. 306 // if resulting name is equal to a reserved word.
291 // - Enums: just uppercase. 307 // - Enums: just uppercase.
292 308
293 // Locale-independent version of ToLower that deals only with ASCII A-Z. 309 // Locale-independent version of ToLower that deals only with ASCII A-Z.
294 char ToLowerASCII(char c) { 310 char ToLowerASCII(char c) {
295 if (c >= 'A' && c <= 'Z') { 311 if (c >= 'A' && c <= 'Z') {
296 return (c - 'A') + 'a'; 312 return (c - 'A') + 'a';
297 } else { 313 } else {
298 return c; 314 return c;
299 } 315 }
300 } 316 }
301 317
302 vector<string> ParseLowerUnderscore(const string& input) { 318 std::vector<string> ParseLowerUnderscore(const string& input) {
303 vector<string> words; 319 std::vector<string> words;
304 string running = ""; 320 string running = "";
305 for (int i = 0; i < input.size(); i++) { 321 for (int i = 0; i < input.size(); i++) {
306 if (input[i] == '_') { 322 if (input[i] == '_') {
307 if (!running.empty()) { 323 if (!running.empty()) {
308 words.push_back(running); 324 words.push_back(running);
309 running.clear(); 325 running.clear();
310 } 326 }
311 } else { 327 } else {
312 running += ToLowerASCII(input[i]); 328 running += ToLowerASCII(input[i]);
313 } 329 }
314 } 330 }
315 if (!running.empty()) { 331 if (!running.empty()) {
316 words.push_back(running); 332 words.push_back(running);
317 } 333 }
318 return words; 334 return words;
319 } 335 }
320 336
321 vector<string> ParseUpperCamel(const string& input) { 337 std::vector<string> ParseUpperCamel(const string& input) {
322 vector<string> words; 338 std::vector<string> words;
323 string running = ""; 339 string running = "";
324 for (int i = 0; i < input.size(); i++) { 340 for (int i = 0; i < input.size(); i++) {
325 if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) { 341 if (input[i] >= 'A' && input[i] <= 'Z' && !running.empty()) {
326 words.push_back(running); 342 words.push_back(running);
327 running.clear(); 343 running.clear();
328 } 344 }
329 running += ToLowerASCII(input[i]); 345 running += ToLowerASCII(input[i]);
330 } 346 }
331 if (!running.empty()) { 347 if (!running.empty()) {
332 words.push_back(running); 348 words.push_back(running);
333 } 349 }
334 return words; 350 return words;
335 } 351 }
336 352
337 string ToLowerCamel(const vector<string>& words) { 353 string ToLowerCamel(const std::vector<string>& words) {
338 string result; 354 string result;
339 for (int i = 0; i < words.size(); i++) { 355 for (int i = 0; i < words.size(); i++) {
340 string word = words[i]; 356 string word = words[i];
341 if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) { 357 if (i == 0 && (word[0] >= 'A' && word[0] <= 'Z')) {
342 word[0] = (word[0] - 'A') + 'a'; 358 word[0] = (word[0] - 'A') + 'a';
343 } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) { 359 } else if (i != 0 && (word[0] >= 'a' && word[0] <= 'z')) {
344 word[0] = (word[0] - 'a') + 'A'; 360 word[0] = (word[0] - 'a') + 'A';
345 } 361 }
346 result += word; 362 result += word;
347 } 363 }
348 return result; 364 return result;
349 } 365 }
350 366
351 string ToUpperCamel(const vector<string>& words) { 367 string ToUpperCamel(const std::vector<string>& words) {
352 string result; 368 string result;
353 for (int i = 0; i < words.size(); i++) { 369 for (int i = 0; i < words.size(); i++) {
354 string word = words[i]; 370 string word = words[i];
355 if (word[0] >= 'a' && word[0] <= 'z') { 371 if (word[0] >= 'a' && word[0] <= 'z') {
356 word[0] = (word[0] - 'a') + 'A'; 372 word[0] = (word[0] - 'a') + 'A';
357 } 373 }
358 result += word; 374 result += word;
359 } 375 }
360 return result; 376 return result;
361 } 377 }
(...skipping 28 matching lines...) Expand all
390 } 406 }
391 } 407 }
392 408
393 return result; 409 return result;
394 } 410 }
395 411
396 // When we're generating one output file per type name, this is the filename 412 // When we're generating one output file per type name, this is the filename
397 // that top-level extensions should go in. 413 // that top-level extensions should go in.
398 string GetExtensionFileName(const GeneratorOptions& options, 414 string GetExtensionFileName(const GeneratorOptions& options,
399 const FileDescriptor* file) { 415 const FileDescriptor* file) {
400 return options.output_dir + "/" + ToFileName(GetPath(options, file)) + ".js"; 416 return options.output_dir + "/" + ToFileName(GetPath(options, file)) +
417 options.GetFileNameExtension();
401 } 418 }
402 419
403 // When we're generating one output file per type name, this is the filename 420 // When we're generating one output file per type name, this is the filename
404 // that a top-level message should go in. 421 // that a top-level message should go in.
405 string GetMessageFileName(const GeneratorOptions& options, 422 string GetMessageFileName(const GeneratorOptions& options,
406 const Descriptor* desc) { 423 const Descriptor* desc) {
407 return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; 424 return options.output_dir + "/" + ToFileName(desc->name()) +
425 options.GetFileNameExtension();
408 } 426 }
409 427
410 // When we're generating one output file per type name, this is the filename 428 // When we're generating one output file per type name, this is the filename
411 // that a top-level message should go in. 429 // that a top-level message should go in.
412 string GetEnumFileName(const GeneratorOptions& options, 430 string GetEnumFileName(const GeneratorOptions& options,
413 const EnumDescriptor* desc) { 431 const EnumDescriptor* desc) {
414 return options.output_dir + "/" + ToFileName(desc->name()) + ".js"; 432 return options.output_dir + "/" + ToFileName(desc->name()) +
433 options.GetFileNameExtension();
415 } 434 }
416 435
417 // Returns the message/response ID, if set. 436 // Returns the message/response ID, if set.
418 string GetMessageId(const Descriptor* desc) { 437 string GetMessageId(const Descriptor* desc) {
419 return string(); 438 return string();
420 } 439 }
421 440
422 bool IgnoreExtensionField(const FieldDescriptor* field) { 441 bool IgnoreExtensionField(const FieldDescriptor* field) {
423 // Exclude descriptor extensions from output "to avoid clutter" (from original 442 // Exclude descriptor extensions from output "to avoid clutter" (from original
424 // codegen). 443 // codegen).
425 return field->is_extension() && 444 return field->is_extension() &&
426 field->containing_type()->file()->name() == 445 field->containing_type()->file()->name() ==
427 "google/protobuf/descriptor.proto"; 446 "google/protobuf/descriptor.proto";
428 } 447 }
429 448
430 449
431 // Used inside Google only -- do not remove. 450 // Used inside Google only -- do not remove.
432 bool IsResponse(const Descriptor* desc) { return false; } 451 bool IsResponse(const Descriptor* desc) { return false; }
433 452
434 bool IgnoreField(const FieldDescriptor* field) { 453 bool IgnoreField(const FieldDescriptor* field) {
435 return IgnoreExtensionField(field); 454 return IgnoreExtensionField(field);
436 } 455 }
437 456
438 457
458 // Used inside Google only -- do not remove.
459 bool ShouldTreatMapsAsRepeatedFields(const FileDescriptor& descriptor) {
460 return false;
461 }
462
463 // Do we ignore this message type?
464 bool IgnoreMessage(const GeneratorOptions& options, const Descriptor* d) {
465 return d->options().map_entry() &&
466 !ShouldTreatMapsAsRepeatedFields(*d->file());
467 }
468
469 bool IsMap(const GeneratorOptions& options, const FieldDescriptor* field) {
470 return field->is_map() && !ShouldTreatMapsAsRepeatedFields(*field->file());
471 }
472
439 // Does JSPB ignore this entire oneof? True only if all fields are ignored. 473 // Does JSPB ignore this entire oneof? True only if all fields are ignored.
440 bool IgnoreOneof(const OneofDescriptor* oneof) { 474 bool IgnoreOneof(const OneofDescriptor* oneof) {
441 for (int i = 0; i < oneof->field_count(); i++) { 475 for (int i = 0; i < oneof->field_count(); i++) {
442 if (!IgnoreField(oneof->field(i))) { 476 if (!IgnoreField(oneof->field(i))) {
443 return false; 477 return false;
444 } 478 }
445 } 479 }
446 return true; 480 return true;
447 } 481 }
448 482
449 string JSIdent(const FieldDescriptor* field, 483 string JSIdent(const GeneratorOptions& options, const FieldDescriptor* field,
450 bool is_upper_camel, 484 bool is_upper_camel, bool is_map, bool drop_list) {
451 bool is_map) {
452 string result; 485 string result;
453 if (field->type() == FieldDescriptor::TYPE_GROUP) { 486 if (field->type() == FieldDescriptor::TYPE_GROUP) {
454 result = is_upper_camel ? 487 result = is_upper_camel ?
455 ToUpperCamel(ParseUpperCamel(field->message_type()->name())) : 488 ToUpperCamel(ParseUpperCamel(field->message_type()->name())) :
456 ToLowerCamel(ParseUpperCamel(field->message_type()->name())); 489 ToLowerCamel(ParseUpperCamel(field->message_type()->name()));
457 } else { 490 } else {
458 result = is_upper_camel ? 491 result = is_upper_camel ?
459 ToUpperCamel(ParseLowerUnderscore(field->name())) : 492 ToUpperCamel(ParseLowerUnderscore(field->name())) :
460 ToLowerCamel(ParseLowerUnderscore(field->name())); 493 ToLowerCamel(ParseLowerUnderscore(field->name()));
461 } 494 }
462 if (is_map) { 495 if (is_map || IsMap(options, field)) {
496 // JSPB-style or proto3-style map.
463 result += "Map"; 497 result += "Map";
464 } else if (field->is_repeated()) { 498 } else if (!drop_list && field->is_repeated()) {
499 // Repeated field.
465 result += "List"; 500 result += "List";
466 } 501 }
467 return result; 502 return result;
468 } 503 }
469 504
470 string JSObjectFieldName(const FieldDescriptor* field) { 505 string JSObjectFieldName(const GeneratorOptions& options,
471 string name = JSIdent( 506 const FieldDescriptor* field) {
472 field, 507 string name = JSIdent(options, field,
473 /* is_upper_camel = */ false, 508 /* is_upper_camel = */ false,
474 /* is_map = */ false); 509 /* is_map = */ false,
510 /* drop_list = */ false);
475 if (IsReserved(name)) { 511 if (IsReserved(name)) {
476 name = "pb_" + name; 512 name = "pb_" + name;
477 } 513 }
478 return name; 514 return name;
479 } 515 }
480 516
481 string JSByteGetterSuffix(BytesMode bytes_mode) { 517 string JSByteGetterSuffix(BytesMode bytes_mode) {
482 switch (bytes_mode) { 518 switch (bytes_mode) {
483 case BYTES_DEFAULT: 519 case BYTES_DEFAULT:
484 return ""; 520 return "";
485 case BYTES_B64: 521 case BYTES_B64:
486 return "B64"; 522 return "B64";
487 case BYTES_U8: 523 case BYTES_U8:
488 return "U8"; 524 return "U8";
489 default: 525 default:
490 assert(false); 526 assert(false);
491 } 527 }
528 return "";
492 } 529 }
493 530
494 // Returns the field name as a capitalized portion of a getter/setter method 531 // Returns the field name as a capitalized portion of a getter/setter method
495 // name, e.g. MyField for .getMyField(). 532 // name, e.g. MyField for .getMyField().
496 string JSGetterName(const FieldDescriptor* field, 533 string JSGetterName(const GeneratorOptions& options,
497 BytesMode bytes_mode = BYTES_DEFAULT) { 534 const FieldDescriptor* field,
498 string name = JSIdent(field, 535 BytesMode bytes_mode = BYTES_DEFAULT,
536 bool drop_list = false) {
537 string name = JSIdent(options, field,
499 /* is_upper_camel = */ true, 538 /* is_upper_camel = */ true,
500 /* is_map = */ false); 539 /* is_map = */ false, drop_list);
501 if (field->type() == FieldDescriptor::TYPE_BYTES) { 540 if (field->type() == FieldDescriptor::TYPE_BYTES) {
502 string suffix = JSByteGetterSuffix(bytes_mode); 541 string suffix = JSByteGetterSuffix(bytes_mode);
503 if (!suffix.empty()) { 542 if (!suffix.empty()) {
504 name += "_as" + suffix; 543 name += "_as" + suffix;
505 } 544 }
506 } 545 }
507 if (name == "Extension" || name == "JsPbMessageId") { 546 if (name == "Extension" || name == "JsPbMessageId") {
508 // Avoid conflicts with base-class names. 547 // Avoid conflicts with base-class names.
509 name += "$"; 548 name += "$";
510 } 549 }
511 return name; 550 return name;
512 } 551 }
513 552
514 string JSMapGetterName(const FieldDescriptor* field) { 553 string JSMapGetterName(const GeneratorOptions& options,
515 return JSIdent(field, 554 const FieldDescriptor* field) {
555 return JSIdent(options, field,
516 /* is_upper_camel = */ true, 556 /* is_upper_camel = */ true,
517 /* is_map = */ true); 557 /* is_map = */ true,
558 /* drop_list = */ false);
518 } 559 }
519 560
520 561
521 562
522 string JSOneofName(const OneofDescriptor* oneof) { 563 string JSOneofName(const OneofDescriptor* oneof) {
523 return ToUpperCamel(ParseLowerUnderscore(oneof->name())); 564 return ToUpperCamel(ParseLowerUnderscore(oneof->name()));
524 } 565 }
525 566
526 // Returns the index corresponding to this field in the JSPB array (underlying 567 // Returns the index corresponding to this field in the JSPB array (underlying
527 // data storage array). 568 // data storage array).
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
740 string DoubleToString(double value) { 781 string DoubleToString(double value) {
741 string result = SimpleDtoa(value); 782 string result = SimpleDtoa(value);
742 return PostProcessFloat(result); 783 return PostProcessFloat(result);
743 } 784 }
744 785
745 string MaybeNumberString(const FieldDescriptor* field, const string& orig) { 786 string MaybeNumberString(const FieldDescriptor* field, const string& orig) {
746 return orig; 787 return orig;
747 } 788 }
748 789
749 string JSFieldDefault(const FieldDescriptor* field) { 790 string JSFieldDefault(const FieldDescriptor* field) {
750 assert(field->has_default_value()); 791 if (field->is_repeated()) {
792 return "[]";
793 }
794
751 switch (field->cpp_type()) { 795 switch (field->cpp_type()) {
752 case FieldDescriptor::CPPTYPE_INT32: 796 case FieldDescriptor::CPPTYPE_INT32:
753 return MaybeNumberString( 797 return MaybeNumberString(
754 field, SimpleItoa(field->default_value_int32())); 798 field, SimpleItoa(field->default_value_int32()));
755 case FieldDescriptor::CPPTYPE_UINT32: 799 case FieldDescriptor::CPPTYPE_UINT32:
756 // The original codegen is in Java, and Java protobufs store unsigned 800 // The original codegen is in Java, and Java protobufs store unsigned
757 // integer values as signed integer values. In order to exactly match the 801 // integer values as signed integer values. In order to exactly match the
758 // output, we need to reinterpret as base-2 signed. Ugh. 802 // output, we need to reinterpret as base-2 signed. Ugh.
759 return MaybeNumberString( 803 return MaybeNumberString(
760 field, SimpleItoa(static_cast<int32>(field->default_value_uint32()))); 804 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); 926 return JSStringTypeName(options, field, bytes_mode);
883 case FieldDescriptor::CPPTYPE_ENUM: 927 case FieldDescriptor::CPPTYPE_ENUM:
884 return GetPath(options, field->enum_type()); 928 return GetPath(options, field->enum_type());
885 case FieldDescriptor::CPPTYPE_MESSAGE: 929 case FieldDescriptor::CPPTYPE_MESSAGE:
886 return GetPath(options, field->message_type()); 930 return GetPath(options, field->message_type());
887 default: 931 default:
888 return ""; 932 return "";
889 } 933 }
890 } 934 }
891 935
892 bool HasFieldPresence(const FieldDescriptor* field); 936 // Used inside Google only -- do not remove.
937 bool UseBrokenPresenceSemantics(const GeneratorOptions& options,
938 const FieldDescriptor* field) {
939 return false;
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 return "jspb.BinaryReader.prototype.read" +
1081 JSBinaryReadWriteMethodName(field, /* is_writer = */ false);
963 } 1082 }
964 1083
965 string JSBinaryWriterMethodName(const FieldDescriptor* field) { 1084 string JSBinaryWriterMethodName(const GeneratorOptions& options,
966 return "write" + JSBinaryReadWriteMethodName(field, /* is_writer = */ true); 1085 const FieldDescriptor* field) {
1086 return "jspb.BinaryWriter.prototype.write" +
1087 JSBinaryReadWriteMethodName(field, /* is_writer = */ true);
967 } 1088 }
968 1089
969 string JSReturnClause(const FieldDescriptor* desc) { 1090 string JSReturnClause(const FieldDescriptor* desc) {
970 return ""; 1091 return "";
971 } 1092 }
972 1093
973 string JSReturnDoc(const GeneratorOptions& options, 1094 string JSReturnDoc(const GeneratorOptions& options,
974 const FieldDescriptor* desc) { 1095 const FieldDescriptor* desc) {
975 return ""; 1096 return "";
976 } 1097 }
977 1098
978 bool HasRepeatedFields(const Descriptor* desc) { 1099 bool HasRepeatedFields(const GeneratorOptions& options,
1100 const Descriptor* desc) {
979 for (int i = 0; i < desc->field_count(); i++) { 1101 for (int i = 0; i < desc->field_count(); i++) {
980 if (desc->field(i)->is_repeated()) { 1102 if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) {
981 return true; 1103 return true;
982 } 1104 }
983 } 1105 }
984 return false; 1106 return false;
985 } 1107 }
986 1108
987 static const char* kRepeatedFieldArrayName = ".repeatedFields_"; 1109 static const char* kRepeatedFieldArrayName = ".repeatedFields_";
988 1110
989 string RepeatedFieldsArrayName(const GeneratorOptions& options, 1111 string RepeatedFieldsArrayName(const GeneratorOptions& options,
990 const Descriptor* desc) { 1112 const Descriptor* desc) {
991 return HasRepeatedFields(desc) ? 1113 return HasRepeatedFields(options, desc)
992 (GetPath(options, desc) + kRepeatedFieldArrayName) : "null"; 1114 ? (GetPath(options, desc) + kRepeatedFieldArrayName)
1115 : "null";
993 } 1116 }
994 1117
995 bool HasOneofFields(const Descriptor* desc) { 1118 bool HasOneofFields(const Descriptor* desc) {
996 for (int i = 0; i < desc->field_count(); i++) { 1119 for (int i = 0; i < desc->field_count(); i++) {
997 if (desc->field(i)->containing_oneof()) { 1120 if (desc->field(i)->containing_oneof()) {
998 return true; 1121 return true;
999 } 1122 }
1000 } 1123 }
1001 return false; 1124 return false;
1002 } 1125 }
1003 1126
1004 static const char* kOneofGroupArrayName = ".oneofGroups_"; 1127 static const char* kOneofGroupArrayName = ".oneofGroups_";
1005 1128
1006 string OneofFieldsArrayName(const GeneratorOptions& options, 1129 string OneofFieldsArrayName(const GeneratorOptions& options,
1007 const Descriptor* desc) { 1130 const Descriptor* desc) {
1008 return HasOneofFields(desc) ? 1131 return HasOneofFields(desc) ?
1009 (GetPath(options, desc) + kOneofGroupArrayName) : "null"; 1132 (GetPath(options, desc) + kOneofGroupArrayName) : "null";
1010 } 1133 }
1011 1134
1012 string RepeatedFieldNumberList(const Descriptor* desc) { 1135 string RepeatedFieldNumberList(const GeneratorOptions& options,
1136 const Descriptor* desc) {
1013 std::vector<string> numbers; 1137 std::vector<string> numbers;
1014 for (int i = 0; i < desc->field_count(); i++) { 1138 for (int i = 0; i < desc->field_count(); i++) {
1015 if (desc->field(i)->is_repeated()) { 1139 if (desc->field(i)->is_repeated() && !IsMap(options, desc->field(i))) {
1016 numbers.push_back(JSFieldIndex(desc->field(i))); 1140 numbers.push_back(JSFieldIndex(desc->field(i)));
1017 } 1141 }
1018 } 1142 }
1019 return "[" + Join(numbers, ",") + "]"; 1143 return "[" + Join(numbers, ",") + "]";
1020 } 1144 }
1021 1145
1022 string OneofGroupList(const Descriptor* desc) { 1146 string OneofGroupList(const Descriptor* desc) {
1023 // List of arrays (one per oneof), each of which is a list of field indices 1147 // List of arrays (one per oneof), each of which is a list of field indices
1024 std::vector<string> oneof_entries; 1148 std::vector<string> oneof_entries;
1025 for (int i = 0; i < desc->oneof_decl_count(); i++) { 1149 for (int i = 0; i < desc->oneof_decl_count(); i++) {
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
1069 } 1193 }
1070 } 1194 }
1071 1195
1072 return type.substr(prefix); 1196 return type.substr(prefix);
1073 } 1197 }
1074 1198
1075 string JSExtensionsObjectName(const GeneratorOptions& options, 1199 string JSExtensionsObjectName(const GeneratorOptions& options,
1076 const FileDescriptor* from_file, 1200 const FileDescriptor* from_file,
1077 const Descriptor* desc) { 1201 const Descriptor* desc) {
1078 if (desc->full_name() == "google.protobuf.bridge.MessageSet") { 1202 if (desc->full_name() == "google.protobuf.bridge.MessageSet") {
1079 // TODO(haberman): fix this for the IMPORT_COMMONJS case. 1203 // TODO(haberman): fix this for the kImportCommonJs case.
1080 return "jspb.Message.messageSetExtensions"; 1204 return "jspb.Message.messageSetExtensions";
1081 } else { 1205 } else {
1082 return MaybeCrossFileRef(options, from_file, desc) + ".extensions"; 1206 return MaybeCrossFileRef(options, from_file, desc) + ".extensions";
1083 } 1207 }
1084 } 1208 }
1085 1209
1210 static const int kMapKeyField = 1;
1211 static const int kMapValueField = 2;
1212
1213 const FieldDescriptor* MapFieldKey(const FieldDescriptor* field) {
1214 assert(field->is_map());
1215 return field->message_type()->FindFieldByNumber(kMapKeyField);
1216 }
1217
1218 const FieldDescriptor* MapFieldValue(const FieldDescriptor* field) {
1219 assert(field->is_map());
1220 return field->message_type()->FindFieldByNumber(kMapValueField);
1221 }
1222
1086 string FieldDefinition(const GeneratorOptions& options, 1223 string FieldDefinition(const GeneratorOptions& options,
1087 const FieldDescriptor* field) { 1224 const FieldDescriptor* field) {
1088 string qualifier = field->is_repeated() ? "repeated" : 1225 if (IsMap(options, field)) {
1089 (field->is_optional() ? "optional" : "required"); 1226 const FieldDescriptor* key_field = MapFieldKey(field);
1090 string type, name; 1227 const FieldDescriptor* value_field = MapFieldValue(field);
1091 if (field->type() == FieldDescriptor::TYPE_ENUM || 1228 string key_type = ProtoTypeName(options, key_field);
1092 field->type() == FieldDescriptor::TYPE_MESSAGE) { 1229 string value_type;
1093 type = RelativeTypeName(field); 1230 if (value_field->type() == FieldDescriptor::TYPE_ENUM ||
1094 name = field->name(); 1231 value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
1095 } else if (field->type() == FieldDescriptor::TYPE_GROUP) { 1232 value_type = RelativeTypeName(value_field);
1096 type = "group"; 1233 } else {
1097 name = field->message_type()->name(); 1234 value_type = ProtoTypeName(options, value_field);
1235 }
1236 return StringPrintf("map<%s, %s> %s = %d;",
1237 key_type.c_str(),
1238 value_type.c_str(),
1239 field->name().c_str(),
1240 field->number());
1098 } else { 1241 } else {
1099 type = ProtoTypeName(options, field); 1242 string qualifier = field->is_repeated() ? "repeated" :
1100 name = field->name(); 1243 (field->is_optional() ? "optional" : "required");
1244 string type, name;
1245 if (field->type() == FieldDescriptor::TYPE_ENUM ||
1246 field->type() == FieldDescriptor::TYPE_MESSAGE) {
1247 type = RelativeTypeName(field);
1248 name = field->name();
1249 } else if (field->type() == FieldDescriptor::TYPE_GROUP) {
1250 type = "group";
1251 name = field->message_type()->name();
1252 } else {
1253 type = ProtoTypeName(options, field);
1254 name = field->name();
1255 }
1256 return StringPrintf("%s %s %s = %d;",
1257 qualifier.c_str(),
1258 type.c_str(),
1259 name.c_str(),
1260 field->number());
1101 } 1261 }
1102 return StringPrintf("%s %s %s = %d;",
1103 qualifier.c_str(),
1104 type.c_str(),
1105 name.c_str(),
1106 field->number());
1107 } 1262 }
1108 1263
1109 string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) { 1264 string FieldComments(const FieldDescriptor* field, BytesMode bytes_mode) {
1110 string comments; 1265 string comments;
1111 if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) { 1266 if (field->cpp_type() == FieldDescriptor::CPPTYPE_BOOL) {
1112 comments += 1267 comments +=
1113 " * Note that Boolean fields may be set to 0/1 when serialized from " 1268 " * Note that Boolean fields may be set to 0/1 when serialized from "
1114 "a Java server.\n" 1269 "a Java server.\n"
1115 " * You should avoid comparisons like {@code val === true/false} in " 1270 " * You should avoid comparisons like {@code val === true/false} in "
1116 "those cases.\n"; 1271 "those cases.\n";
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
1157 } 1312 }
1158 } 1313 }
1159 for (int i = 0; i < file->message_type_count(); i++) { 1314 for (int i = 0; i < file->message_type_count(); i++) {
1160 if (HasExtensions(file->message_type(i))) { 1315 if (HasExtensions(file->message_type(i))) {
1161 return true; 1316 return true;
1162 } 1317 }
1163 } 1318 }
1164 return false; 1319 return false;
1165 } 1320 }
1166 1321
1322 bool HasMap(const GeneratorOptions& options, const Descriptor* desc) {
1323 for (int i = 0; i < desc->field_count(); i++) {
1324 if (IsMap(options, desc->field(i))) {
1325 return true;
1326 }
1327 }
1328 for (int i = 0; i < desc->nested_type_count(); i++) {
1329 if (HasMap(options, desc->nested_type(i))) {
1330 return true;
1331 }
1332 }
1333 return false;
1334 }
1335
1336 bool FileHasMap(const GeneratorOptions& options, const FileDescriptor* desc) {
1337 for (int i = 0; i < desc->message_type_count(); i++) {
1338 if (HasMap(options, desc->message_type(i))) {
1339 return true;
1340 }
1341 }
1342 return false;
1343 }
1344
1167 bool IsExtendable(const Descriptor* desc) { 1345 bool IsExtendable(const Descriptor* desc) {
1168 return desc->extension_range_count() > 0; 1346 return desc->extension_range_count() > 0;
1169 } 1347 }
1170 1348
1171 // Returns the max index in the underlying data storage array beyond which the 1349 // Returns the max index in the underlying data storage array beyond which the
1172 // extension object is used. 1350 // extension object is used.
1173 string GetPivot(const Descriptor* desc) { 1351 string GetPivot(const Descriptor* desc) {
1174 static const int kDefaultPivot = (1 << 29); // max field number (29 bits) 1352 static const int kDefaultPivot = (1 << 29); // max field number (29 bits)
1175 1353
1176 // Find the max field number 1354 // Find the max field number
1177 int max_field_number = 0; 1355 int max_field_number = 0;
1178 for (int i = 0; i < desc->field_count(); i++) { 1356 for (int i = 0; i < desc->field_count(); i++) {
1179 if (!IgnoreField(desc->field(i)) && 1357 if (!IgnoreField(desc->field(i)) &&
1180 desc->field(i)->number() > max_field_number) { 1358 desc->field(i)->number() > max_field_number) {
1181 max_field_number = desc->field(i)->number(); 1359 max_field_number = desc->field(i)->number();
1182 } 1360 }
1183 } 1361 }
1184 1362
1185 int pivot = -1; 1363 int pivot = -1;
1186 if (IsExtendable(desc)) { 1364 if (IsExtendable(desc)) {
1187 pivot = ((max_field_number + 1) < kDefaultPivot) ? 1365 pivot = ((max_field_number + 1) < kDefaultPivot) ?
1188 (max_field_number + 1) : kDefaultPivot; 1366 (max_field_number + 1) : kDefaultPivot;
1189 } 1367 }
1190 1368
1191 return SimpleItoa(pivot); 1369 return SimpleItoa(pivot);
1192 } 1370 }
1193 1371
1194 // Returns true for fields that represent "null" as distinct from the default 1372 // Whether this field represents presence. For fields with presence, we
1195 // value. See http://go/proto3#heading=h.kozewqqcqhuz for more information. 1373 // generate extra methods (clearFoo() and hasFoo()) for this field.
1196 bool HasFieldPresence(const FieldDescriptor* field) { 1374 bool HasFieldPresence(const GeneratorOptions& options,
1197 return 1375 const FieldDescriptor* field) {
1198 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) || 1376 if (field->is_repeated() || field->is_map()) {
1199 (field->containing_oneof() != NULL) || 1377 // We say repeated fields and maps don't have presence, but we still do
1200 (field->file()->syntax() != FileDescriptor::SYNTAX_PROTO3); 1378 // generate clearFoo() methods for them through a special case elsewhere.
1201 } 1379 return false;
1380 }
1202 1381
1203 // For proto3 fields without presence, returns a string representing the default 1382 if (UseBrokenPresenceSemantics(options, field)) {
1204 // value in JavaScript. See http://go/proto3#heading=h.kozewqqcqhuz for more 1383 // Proto3 files with broken presence semantics have field presence.
1205 // information. 1384 return true;
1206 string Proto3PrimitiveFieldDefault(const FieldDescriptor* field) { 1385 }
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 1386
1215 case FieldDescriptor::CPPTYPE_ENUM: 1387 return field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ||
1216 case FieldDescriptor::CPPTYPE_FLOAT: 1388 field->containing_oneof() != NULL ||
1217 case FieldDescriptor::CPPTYPE_DOUBLE: 1389 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 } 1390 }
1232 1391
1233 // We use this to implement the semantics that same file can be generated 1392 // 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, 1393 // 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 1394 // but we keep a set of which descriptors were the final one for a given
1236 // filename. 1395 // filename.
1237 class FileDeduplicator { 1396 class FileDeduplicator {
1238 public: 1397 public:
1239 explicit FileDeduplicator(const GeneratorOptions& options) 1398 explicit FileDeduplicator(const GeneratorOptions& options)
1240 : error_on_conflict_(options.error_on_name_conflict) {} 1399 : error_on_conflict_(options.error_on_name_conflict) {}
1241 1400
1242 bool AddFile(const string& filename, const void* desc, string* error) { 1401 bool AddFile(const string& filename, const void* desc, string* error) {
1243 if (descs_by_filename_.find(filename) != descs_by_filename_.end()) { 1402 if (descs_by_filename_.find(filename) != descs_by_filename_.end()) {
1244 if (error_on_conflict_) { 1403 if (error_on_conflict_) {
1245 *error = "Name conflict: file name " + filename + 1404 *error = "Name conflict: file name " + filename +
1246 " would be generated by two descriptors"; 1405 " would be generated by two descriptors";
1247 return false; 1406 return false;
1248 } 1407 }
1249 allowed_descs_.erase(descs_by_filename_[filename]); 1408 allowed_descs_.erase(descs_by_filename_[filename]);
1250 } 1409 }
1251 1410
1252 descs_by_filename_[filename] = desc; 1411 descs_by_filename_[filename] = desc;
1253 allowed_descs_.insert(desc); 1412 allowed_descs_.insert(desc);
1254 return true; 1413 return true;
1255 } 1414 }
1256 1415
1257 void GetAllowedSet(set<const void*>* allowed_set) { 1416 void GetAllowedSet(std::set<const void*>* allowed_set) {
1258 *allowed_set = allowed_descs_; 1417 *allowed_set = allowed_descs_;
1259 } 1418 }
1260 1419
1261 private: 1420 private:
1262 bool error_on_conflict_; 1421 bool error_on_conflict_;
1263 map<string, const void*> descs_by_filename_; 1422 std::map<string, const void*> descs_by_filename_;
1264 set<const void*> allowed_descs_; 1423 std::set<const void*> allowed_descs_;
1265 }; 1424 };
1266 1425
1267 void DepthFirstSearch(const FileDescriptor* file, 1426 void DepthFirstSearch(const FileDescriptor* file,
1268 vector<const FileDescriptor*>* list, 1427 std::vector<const FileDescriptor*>* list,
1269 set<const FileDescriptor*>* seen) { 1428 std::set<const FileDescriptor*>* seen) {
1270 if (!seen->insert(file).second) { 1429 if (!seen->insert(file).second) {
1271 return; 1430 return;
1272 } 1431 }
1273 1432
1274 // Add all dependencies. 1433 // Add all dependencies.
1275 for (int i = 0; i < file->dependency_count(); i++) { 1434 for (int i = 0; i < file->dependency_count(); i++) {
1276 DepthFirstSearch(file->dependency(i), list, seen); 1435 DepthFirstSearch(file->dependency(i), list, seen);
1277 } 1436 }
1278 1437
1279 // Add this file. 1438 // Add this file.
1280 list->push_back(file); 1439 list->push_back(file);
1281 } 1440 }
1282 1441
1283 // A functor for the predicate to remove_if() below. Returns true if a given 1442 // A functor for the predicate to remove_if() below. Returns true if a given
1284 // FileDescriptor is not in the given set. 1443 // FileDescriptor is not in the given set.
1285 class NotInSet { 1444 class NotInSet {
1286 public: 1445 public:
1287 explicit NotInSet(const set<const FileDescriptor*>& file_set) 1446 explicit NotInSet(const std::set<const FileDescriptor*>& file_set)
1288 : file_set_(file_set) {} 1447 : file_set_(file_set) {}
1289 1448
1290 bool operator()(const FileDescriptor* file) { 1449 bool operator()(const FileDescriptor* file) {
1291 return file_set_.count(file) == 0; 1450 return file_set_.count(file) == 0;
1292 } 1451 }
1293 1452
1294 private: 1453 private:
1295 const set<const FileDescriptor*>& file_set_; 1454 const std::set<const FileDescriptor*>& file_set_;
1296 }; 1455 };
1297 1456
1298 // This function generates an ordering of the input FileDescriptors that matches 1457 // This function generates an ordering of the input FileDescriptors that matches
1299 // the logic of the old code generator. The order is significant because two 1458 // the logic of the old code generator. The order is significant because two
1300 // different input files can generate the same output file, and the last one 1459 // different input files can generate the same output file, and the last one
1301 // needs to win. 1460 // needs to win.
1302 void GenerateJspbFileOrder(const vector<const FileDescriptor*>& input, 1461 void GenerateJspbFileOrder(const std::vector<const FileDescriptor*>& input,
1303 vector<const FileDescriptor*>* ordered) { 1462 std::vector<const FileDescriptor*>* ordered) {
1304 // First generate an ordering of all reachable files (including dependencies) 1463 // First generate an ordering of all reachable files (including dependencies)
1305 // with depth-first search. This mimics the behavior of --include_imports, 1464 // with depth-first search. This mimics the behavior of --include_imports,
1306 // which is what the old codegen used. 1465 // which is what the old codegen used.
1307 ordered->clear(); 1466 ordered->clear();
1308 set<const FileDescriptor*> seen; 1467 std::set<const FileDescriptor*> seen;
1309 set<const FileDescriptor*> input_set; 1468 std::set<const FileDescriptor*> input_set;
1310 for (int i = 0; i < input.size(); i++) { 1469 for (int i = 0; i < input.size(); i++) {
1311 DepthFirstSearch(input[i], ordered, &seen); 1470 DepthFirstSearch(input[i], ordered, &seen);
1312 input_set.insert(input[i]); 1471 input_set.insert(input[i]);
1313 } 1472 }
1314 1473
1315 // Now remove the entries that are not actually in our input list. 1474 // Now remove the entries that are not actually in our input list.
1316 ordered->erase( 1475 ordered->erase(
1317 std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)), 1476 std::remove_if(ordered->begin(), ordered->end(), NotInSet(input_set)),
1318 ordered->end()); 1477 ordered->end());
1319 } 1478 }
1320 1479
1321 // If we're generating code in file-per-type mode, avoid overwriting files 1480 // If we're generating code in file-per-type mode, avoid overwriting files
1322 // by choosing the last descriptor that writes each filename and permitting 1481 // by choosing the last descriptor that writes each filename and permitting
1323 // only those to generate code. 1482 // only those to generate code.
1324 1483
1325 bool GenerateJspbAllowedSet(const GeneratorOptions& options, 1484 bool GenerateJspbAllowedSet(const GeneratorOptions& options,
1326 const vector<const FileDescriptor*>& files, 1485 const std::vector<const FileDescriptor*>& files,
1327 set<const void*>* allowed_set, 1486 std::set<const void*>* allowed_set,
1328 string* error) { 1487 string* error) {
1329 vector<const FileDescriptor*> files_ordered; 1488 std::vector<const FileDescriptor*> files_ordered;
1330 GenerateJspbFileOrder(files, &files_ordered); 1489 GenerateJspbFileOrder(files, &files_ordered);
1331 1490
1332 // Choose the last descriptor for each filename. 1491 // Choose the last descriptor for each filename.
1333 FileDeduplicator dedup(options); 1492 FileDeduplicator dedup(options);
1334 for (int i = 0; i < files_ordered.size(); i++) { 1493 for (int i = 0; i < files_ordered.size(); i++) {
1335 for (int j = 0; j < files_ordered[i]->message_type_count(); j++) { 1494 for (int j = 0; j < files_ordered[i]->message_type_count(); j++) {
1336 const Descriptor* desc = files_ordered[i]->message_type(j); 1495 const Descriptor* desc = files_ordered[i]->message_type(j);
1337 if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) { 1496 if (!dedup.AddFile(GetMessageFileName(options, desc), desc, error)) {
1338 return false; 1497 return false;
1339 } 1498 }
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
1387 for (int i = 0; i < file->message_type_count(); i++) { 1546 for (int i = 0; i < file->message_type_count(); i++) {
1388 FindProvidesForMessage(options, printer, file->message_type(i), provided); 1547 FindProvidesForMessage(options, printer, file->message_type(i), provided);
1389 } 1548 }
1390 for (int i = 0; i < file->enum_type_count(); i++) { 1549 for (int i = 0; i < file->enum_type_count(); i++) {
1391 FindProvidesForEnum(options, printer, file->enum_type(i), provided); 1550 FindProvidesForEnum(options, printer, file->enum_type(i), provided);
1392 } 1551 }
1393 } 1552 }
1394 1553
1395 void Generator::FindProvides(const GeneratorOptions& options, 1554 void Generator::FindProvides(const GeneratorOptions& options,
1396 io::Printer* printer, 1555 io::Printer* printer,
1397 const vector<const FileDescriptor*>& files, 1556 const std::vector<const FileDescriptor*>& files,
1398 std::set<string>* provided) const { 1557 std::set<string>* provided) const {
1399 for (int i = 0; i < files.size(); i++) { 1558 for (int i = 0; i < files.size(); i++) {
1400 FindProvidesForFile(options, printer, files[i], provided); 1559 FindProvidesForFile(options, printer, files[i], provided);
1401 } 1560 }
1402 1561
1403 printer->Print("\n"); 1562 printer->Print("\n");
1404 } 1563 }
1405 1564
1406 void Generator::FindProvidesForMessage( 1565 void Generator::FindProvidesForMessage(
1407 const GeneratorOptions& options, 1566 const GeneratorOptions& options,
1408 io::Printer* printer, 1567 io::Printer* printer,
1409 const Descriptor* desc, 1568 const Descriptor* desc,
1410 std::set<string>* provided) const { 1569 std::set<string>* provided) const {
1570 if (IgnoreMessage(options, desc)) {
1571 return;
1572 }
1573
1411 string name = GetPath(options, desc); 1574 string name = GetPath(options, desc);
1412 provided->insert(name); 1575 provided->insert(name);
1413 1576
1414 for (int i = 0; i < desc->enum_type_count(); i++) { 1577 for (int i = 0; i < desc->enum_type_count(); i++) {
1415 FindProvidesForEnum(options, printer, desc->enum_type(i), 1578 FindProvidesForEnum(options, printer, desc->enum_type(i),
1416 provided); 1579 provided);
1417 } 1580 }
1418 for (int i = 0; i < desc->nested_type_count(); i++) { 1581 for (int i = 0; i < desc->nested_type_count(); i++) {
1419 FindProvidesForMessage(options, printer, desc->nested_type(i), 1582 FindProvidesForMessage(options, printer, desc->nested_type(i),
1420 provided); 1583 provided);
1421 } 1584 }
1422 } 1585 }
1423 1586
1424 void Generator::FindProvidesForEnum(const GeneratorOptions& options, 1587 void Generator::FindProvidesForEnum(const GeneratorOptions& options,
1425 io::Printer* printer, 1588 io::Printer* printer,
1426 const EnumDescriptor* enumdesc, 1589 const EnumDescriptor* enumdesc,
1427 std::set<string>* provided) const { 1590 std::set<string>* provided) const {
1428 string name = GetPath(options, enumdesc); 1591 string name = GetPath(options, enumdesc);
1429 provided->insert(name); 1592 provided->insert(name);
1430 } 1593 }
1431 1594
1432 void Generator::FindProvidesForFields( 1595 void Generator::FindProvidesForFields(
1433 const GeneratorOptions& options, 1596 const GeneratorOptions& options,
1434 io::Printer* printer, 1597 io::Printer* printer,
1435 const vector<const FieldDescriptor*>& fields, 1598 const std::vector<const FieldDescriptor*>& fields,
1436 std::set<string>* provided) const { 1599 std::set<string>* provided) const {
1437 for (int i = 0; i < fields.size(); i++) { 1600 for (int i = 0; i < fields.size(); i++) {
1438 const FieldDescriptor* field = fields[i]; 1601 const FieldDescriptor* field = fields[i];
1439 1602
1440 if (IgnoreField(field)) { 1603 if (IgnoreField(field)) {
1441 continue; 1604 continue;
1442 } 1605 }
1443 1606
1444 string name = 1607 string name =
1445 GetPath(options, field->file()) + "." + JSObjectFieldName(field); 1608 GetPath(options, field->file()) + "." +
1609 JSObjectFieldName(options, field);
1446 provided->insert(name); 1610 provided->insert(name);
1447 } 1611 }
1448 } 1612 }
1449 1613
1450 void Generator::GenerateProvides(const GeneratorOptions& options, 1614 void Generator::GenerateProvides(const GeneratorOptions& options,
1451 io::Printer* printer, 1615 io::Printer* printer,
1452 std::set<string>* provided) const { 1616 std::set<string>* provided) const {
1453 for (std::set<string>::iterator it = provided->begin(); 1617 for (std::set<string>::iterator it = provided->begin();
1454 it != provided->end(); ++it) { 1618 it != provided->end(); ++it) {
1455 printer->Print("goog.provide('$name$');\n", 1619 if (options.import_style == GeneratorOptions::kImportClosure) {
1456 "name", *it); 1620 printer->Print("goog.provide('$name$');\n", "name", *it);
1621 } else {
1622 // We aren't using Closure's import system, but we use goog.exportSymbol()
1623 // to construct the expected tree of objects, eg.
1624 //
1625 // goog.exportSymbol('foo.bar.Baz', null, this);
1626 //
1627 // // Later generated code expects foo.bar = {} to exist:
1628 // foo.bar.Baz = function() { /* ... */ }
1629 printer->Print("goog.exportSymbol('$name$', null, global);\n", "name",
1630 *it);
1631 }
1457 } 1632 }
1458 } 1633 }
1459 1634
1460 void Generator::GenerateRequiresForMessage(const GeneratorOptions& options, 1635 void Generator::GenerateRequiresForMessage(const GeneratorOptions& options,
1461 io::Printer* printer, 1636 io::Printer* printer,
1462 const Descriptor* desc, 1637 const Descriptor* desc,
1463 std::set<string>* provided) const { 1638 std::set<string>* provided) const {
1464 std::set<string> required; 1639 std::set<string> required;
1465 std::set<string> forwards; 1640 std::set<string> forwards;
1466 bool have_message = false; 1641 bool have_message = false;
1467 FindRequiresForMessage(options, desc, 1642 FindRequiresForMessage(options, desc,
1468 &required, &forwards, &have_message); 1643 &required, &forwards, &have_message);
1469 1644
1470 GenerateRequiresImpl(options, printer, &required, &forwards, provided, 1645 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1471 /* require_jspb = */ have_message, 1646 /* require_jspb = */ have_message,
1472 /* require_extension = */ HasExtensions(desc)); 1647 /* require_extension = */ HasExtensions(desc),
1648 /* require_map = */ HasMap(options, desc));
1473 } 1649 }
1474 1650
1475 void Generator::GenerateRequiresForLibrary( 1651 void Generator::GenerateRequiresForLibrary(
1476 const GeneratorOptions& options, io::Printer* printer, 1652 const GeneratorOptions& options, io::Printer* printer,
1477 const vector<const FileDescriptor*>& files, 1653 const std::vector<const FileDescriptor*>& files,
1478 std::set<string>* provided) const { 1654 std::set<string>* provided) const {
1479 GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::IMPORT_CLOSURE); 1655 GOOGLE_CHECK_EQ(options.import_style, GeneratorOptions::kImportClosure);
1480 // For Closure imports we need to import every message type individually. 1656 // For Closure imports we need to import every message type individually.
1481 std::set<string> required; 1657 std::set<string> required;
1482 std::set<string> forwards; 1658 std::set<string> forwards;
1483 bool have_extensions = false; 1659 bool have_extensions = false;
1660 bool have_map = false;
1484 bool have_message = false; 1661 bool have_message = false;
1485 1662
1486 for (int i = 0; i < files.size(); i++) { 1663 for (int i = 0; i < files.size(); i++) {
1487 for (int j = 0; j < files[i]->message_type_count(); j++) { 1664 for (int j = 0; j < files[i]->message_type_count(); j++) {
1488 FindRequiresForMessage(options, 1665 const Descriptor* desc = files[i]->message_type(j);
1489 files[i]->message_type(j), 1666 if (!IgnoreMessage(options, desc)) {
1490 &required, &forwards, &have_message); 1667 FindRequiresForMessage(options, desc, &required, &forwards,
1668 &have_message);
1669 }
1491 } 1670 }
1671
1492 if (!have_extensions && HasExtensions(files[i])) { 1672 if (!have_extensions && HasExtensions(files[i])) {
1493 have_extensions = true; 1673 have_extensions = true;
1494 } 1674 }
1495 1675
1676 if (!have_map && FileHasMap(options, files[i])) {
1677 have_map = true;
1678 }
1679
1496 for (int j = 0; j < files[i]->extension_count(); j++) { 1680 for (int j = 0; j < files[i]->extension_count(); j++) {
1497 const FieldDescriptor* extension = files[i]->extension(j); 1681 const FieldDescriptor* extension = files[i]->extension(j);
1498 if (IgnoreField(extension)) { 1682 if (IgnoreField(extension)) {
1499 continue; 1683 continue;
1500 } 1684 }
1501 if (extension->containing_type()->full_name() != 1685 if (extension->containing_type()->full_name() !=
1502 "google.protobuf.bridge.MessageSet") { 1686 "google.protobuf.bridge.MessageSet") {
1503 required.insert(GetPath(options, extension->containing_type())); 1687 required.insert(GetPath(options, extension->containing_type()));
1504 } 1688 }
1505 FindRequiresForField(options, extension, &required, &forwards); 1689 FindRequiresForField(options, extension, &required, &forwards);
1506 have_extensions = true; 1690 have_extensions = true;
1507 } 1691 }
1508 } 1692 }
1509 1693
1510 GenerateRequiresImpl(options, printer, &required, &forwards, provided, 1694 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1511 /* require_jspb = */ have_message, 1695 /* require_jspb = */ have_message,
1512 /* require_extension = */ have_extensions); 1696 /* require_extension = */ have_extensions,
1697 /* require_map = */ have_map);
1513 } 1698 }
1514 1699
1515 void Generator::GenerateRequiresForExtensions( 1700 void Generator::GenerateRequiresForExtensions(
1516 const GeneratorOptions& options, io::Printer* printer, 1701 const GeneratorOptions& options, io::Printer* printer,
1517 const vector<const FieldDescriptor*>& fields, 1702 const std::vector<const FieldDescriptor*>& fields,
1518 std::set<string>* provided) const { 1703 std::set<string>* provided) const {
1519 std::set<string> required; 1704 std::set<string> required;
1520 std::set<string> forwards; 1705 std::set<string> forwards;
1521 for (int i = 0; i < fields.size(); i++) { 1706 for (int i = 0; i < fields.size(); i++) {
1522 const FieldDescriptor* field = fields[i]; 1707 const FieldDescriptor* field = fields[i];
1523 if (IgnoreField(field)) { 1708 if (IgnoreField(field)) {
1524 continue; 1709 continue;
1525 } 1710 }
1526 FindRequiresForExtension(options, field, &required, &forwards); 1711 FindRequiresForExtension(options, field, &required, &forwards);
1527 } 1712 }
1528 1713
1529 GenerateRequiresImpl(options, printer, &required, &forwards, provided, 1714 GenerateRequiresImpl(options, printer, &required, &forwards, provided,
1530 /* require_jspb = */ false, 1715 /* require_jspb = */ false,
1531 /* require_extension = */ fields.size() > 0); 1716 /* require_extension = */ fields.size() > 0,
1717 /* require_map = */ false);
1532 } 1718 }
1533 1719
1534 void Generator::GenerateRequiresImpl(const GeneratorOptions& options, 1720 void Generator::GenerateRequiresImpl(const GeneratorOptions& options,
1535 io::Printer* printer, 1721 io::Printer* printer,
1536 std::set<string>* required, 1722 std::set<string>* required,
1537 std::set<string>* forwards, 1723 std::set<string>* forwards,
1538 std::set<string>* provided, 1724 std::set<string>* provided,
1539 bool require_jspb, 1725 bool require_jspb, bool require_extension,
1540 bool require_extension) const { 1726 bool require_map) const {
1541 if (require_jspb) { 1727 if (require_jspb) {
1542 printer->Print( 1728 printer->Print(
1543 "goog.require('jspb.Message');\n"); 1729 "goog.require('jspb.Message');\n"
1544 if (options.binary) { 1730 "goog.require('jspb.BinaryReader');\n"
1545 printer->Print( 1731 "goog.require('jspb.BinaryWriter');\n");
1546 "goog.require('jspb.BinaryReader');\n"
1547 "goog.require('jspb.BinaryWriter');\n");
1548 }
1549 } 1732 }
1550 if (require_extension) { 1733 if (require_extension) {
1734 printer->Print("goog.require('jspb.ExtensionFieldBinaryInfo');\n");
1551 printer->Print( 1735 printer->Print(
1552 "goog.require('jspb.ExtensionFieldInfo');\n"); 1736 "goog.require('jspb.ExtensionFieldInfo');\n");
1553 } 1737 }
1738 if (require_map) {
1739 printer->Print("goog.require('jspb.Map');\n");
1740 }
1554 1741
1555 std::set<string>::iterator it; 1742 std::set<string>::iterator it;
1556 for (it = required->begin(); it != required->end(); ++it) { 1743 for (it = required->begin(); it != required->end(); ++it) {
1557 if (provided->find(*it) != provided->end()) { 1744 if (provided->find(*it) != provided->end()) {
1558 continue; 1745 continue;
1559 } 1746 }
1560 printer->Print("goog.require('$name$');\n", 1747 printer->Print("goog.require('$name$');\n",
1561 "name", *it); 1748 "name", *it);
1562 } 1749 }
1563 1750
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
1616 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM && 1803 if (field->cpp_type() == FieldDescriptor::CPPTYPE_ENUM &&
1617 // N.B.: file-level extensions with enum type do *not* create 1804 // N.B.: file-level extensions with enum type do *not* create
1618 // dependencies, as per original codegen. 1805 // dependencies, as per original codegen.
1619 !(field->is_extension() && field->extension_scope() == NULL)) { 1806 !(field->is_extension() && field->extension_scope() == NULL)) {
1620 if (options.add_require_for_enums) { 1807 if (options.add_require_for_enums) {
1621 required->insert(GetPath(options, field->enum_type())); 1808 required->insert(GetPath(options, field->enum_type()));
1622 } else { 1809 } else {
1623 forwards->insert(GetPath(options, field->enum_type())); 1810 forwards->insert(GetPath(options, field->enum_type()));
1624 } 1811 }
1625 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 1812 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1626 required->insert(GetPath(options, field->message_type())); 1813 if (!IgnoreMessage(options, field->message_type())) {
1814 required->insert(GetPath(options, field->message_type()));
1815 }
1627 } 1816 }
1628 } 1817 }
1629 1818
1630 void Generator::FindRequiresForExtension(const GeneratorOptions& options, 1819 void Generator::FindRequiresForExtension(const GeneratorOptions& options,
1631 const FieldDescriptor* field, 1820 const FieldDescriptor* field,
1632 std::set<string>* required, 1821 std::set<string>* required,
1633 std::set<string>* forwards) const { 1822 std::set<string>* forwards) const {
1634 if (field->containing_type()->full_name() != "google.protobuf.bridge.Message Set") { 1823 if (field->containing_type()->full_name() != "google.protobuf.bridge.Message Set") {
1635 required->insert(GetPath(options, field->containing_type())); 1824 required->insert(GetPath(options, field->containing_type()));
1636 } 1825 }
(...skipping 15 matching lines...) Expand all
1652 GenerateClass(options, printer, file->message_type(i)); 1841 GenerateClass(options, printer, file->message_type(i));
1653 } 1842 }
1654 for (int i = 0; i < file->enum_type_count(); i++) { 1843 for (int i = 0; i < file->enum_type_count(); i++) {
1655 GenerateEnum(options, printer, file->enum_type(i)); 1844 GenerateEnum(options, printer, file->enum_type(i));
1656 } 1845 }
1657 } 1846 }
1658 1847
1659 void Generator::GenerateClass(const GeneratorOptions& options, 1848 void Generator::GenerateClass(const GeneratorOptions& options,
1660 io::Printer* printer, 1849 io::Printer* printer,
1661 const Descriptor* desc) const { 1850 const Descriptor* desc) const {
1851 if (IgnoreMessage(options, desc)) {
1852 return;
1853 }
1854
1662 if (!NamespaceOnly(desc)) { 1855 if (!NamespaceOnly(desc)) {
1663 printer->Print("\n"); 1856 printer->Print("\n");
1664 GenerateClassConstructor(options, printer, desc); 1857 GenerateClassConstructor(options, printer, desc);
1665 GenerateClassFieldInfo(options, printer, desc); 1858 GenerateClassFieldInfo(options, printer, desc);
1666 1859
1667 1860
1668 GenerateClassToObject(options, printer, desc); 1861 GenerateClassToObject(options, printer, desc);
1669 if (options.binary) { 1862 // These must come *before* the extension-field info generation in
1670 // These must come *before* the extension-field info generation in 1863 // GenerateClassRegistration so that references to the binary
1671 // GenerateClassRegistration so that references to the binary 1864 // serialization/deserialization functions may be placed in the extension
1672 // serialization/deserialization functions may be placed in the extension 1865 // objects.
1673 // objects. 1866 GenerateClassDeserializeBinary(options, printer, desc);
1674 GenerateClassDeserializeBinary(options, printer, desc); 1867 GenerateClassSerializeBinary(options, printer, desc);
1675 GenerateClassSerializeBinary(options, printer, desc); 1868
1676 }
1677 GenerateClassClone(options, printer, desc);
1678 GenerateClassRegistration(options, printer, desc); 1869 GenerateClassRegistration(options, printer, desc);
1679 GenerateClassFields(options, printer, desc); 1870 GenerateClassFields(options, printer, desc);
1680 if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.Messa geSet") { 1871 if (IsExtendable(desc) && desc->full_name() != "google.protobuf.bridge.Messa geSet") {
1681 GenerateClassExtensionFieldInfo(options, printer, desc); 1872 GenerateClassExtensionFieldInfo(options, printer, desc);
1682 } 1873 }
1683 1874
1684 if (options.import_style != GeneratorOptions:: IMPORT_CLOSURE) { 1875 if (options.import_style != GeneratorOptions::kImportClosure) {
1685 for (int i = 0; i < desc->extension_count(); i++) { 1876 for (int i = 0; i < desc->extension_count(); i++) {
1686 GenerateExtension(options, printer, desc->extension(i)); 1877 GenerateExtension(options, printer, desc->extension(i));
1687 } 1878 }
1688 } 1879 }
1689 } 1880 }
1690 1881
1691 // Recurse on nested types. 1882 // Recurse on nested types.
1692 for (int i = 0; i < desc->enum_type_count(); i++) { 1883 for (int i = 0; i < desc->enum_type_count(); i++) {
1693 GenerateEnum(options, printer, desc->enum_type(i)); 1884 GenerateEnum(options, printer, desc->enum_type(i));
1694 } 1885 }
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after
1732 "goog.inherits($classname$, jspb.Message);\n" 1923 "goog.inherits($classname$, jspb.Message);\n"
1733 "if (goog.DEBUG && !COMPILED) {\n" 1924 "if (goog.DEBUG && !COMPILED) {\n"
1734 " $classname$.displayName = '$classname$';\n" 1925 " $classname$.displayName = '$classname$';\n"
1735 "}\n", 1926 "}\n",
1736 "classname", GetPath(options, desc)); 1927 "classname", GetPath(options, desc));
1737 } 1928 }
1738 1929
1739 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options, 1930 void Generator::GenerateClassFieldInfo(const GeneratorOptions& options,
1740 io::Printer* printer, 1931 io::Printer* printer,
1741 const Descriptor* desc) const { 1932 const Descriptor* desc) const {
1742 if (HasRepeatedFields(desc)) { 1933 if (HasRepeatedFields(options, desc)) {
1743 printer->Print( 1934 printer->Print(
1744 "/**\n" 1935 "/**\n"
1745 " * List of repeated fields within this message type.\n" 1936 " * List of repeated fields within this message type.\n"
1746 " * @private {!Array<number>}\n" 1937 " * @private {!Array<number>}\n"
1747 " * @const\n" 1938 " * @const\n"
1748 " */\n" 1939 " */\n"
1749 "$classname$$rptfieldarray$ = $rptfields$;\n" 1940 "$classname$$rptfieldarray$ = $rptfields$;\n"
1750 "\n", 1941 "\n",
1751 "classname", GetPath(options, desc), 1942 "classname", GetPath(options, desc),
1752 "rptfieldarray", kRepeatedFieldArrayName, 1943 "rptfieldarray", kRepeatedFieldArrayName,
1753 "rptfields", RepeatedFieldNumberList(desc)); 1944 "rptfields", RepeatedFieldNumberList(options, desc));
1754 } 1945 }
1755 1946
1756 if (HasOneofFields(desc)) { 1947 if (HasOneofFields(desc)) {
1757 printer->Print( 1948 printer->Print(
1758 "/**\n" 1949 "/**\n"
1759 " * Oneof group definitions for this message. Each group defines the " 1950 " * Oneof group definitions for this message. Each group defines the "
1760 "field\n" 1951 "field\n"
1761 " * numbers belonging to that group. When of these fields' value is " 1952 " * numbers belonging to that group. When of these fields' value is "
1762 "set, all\n" 1953 "set, all\n"
1763 " * other fields in the group are cleared. During deserialization, if " 1954 " * 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" 2103 " obj.$$jspbMessageInstance = msg;\n"
1913 " }\n" 2104 " }\n"
1914 " return obj;\n" 2105 " return obj;\n"
1915 "};\n" 2106 "};\n"
1916 "}\n" 2107 "}\n"
1917 "\n" 2108 "\n"
1918 "\n", 2109 "\n",
1919 "classname", GetPath(options, desc)); 2110 "classname", GetPath(options, desc));
1920 } 2111 }
1921 2112
2113 void Generator::GenerateFieldValueExpression(io::Printer* printer,
2114 const char *obj_reference,
2115 const FieldDescriptor* field,
2116 bool use_default) const {
2117 bool is_float_or_double =
2118 field->cpp_type() == FieldDescriptor::CPPTYPE_FLOAT ||
2119 field->cpp_type() == FieldDescriptor::CPPTYPE_DOUBLE;
2120 if (use_default) {
2121 if (is_float_or_double) {
2122 // Coerce "Nan" and "Infinity" to actual float values.
2123 //
2124 // This will change null to 0, but that doesn't matter since we're getting
2125 // with a default.
2126 printer->Print("+");
2127 }
2128
2129 printer->Print(
2130 "jspb.Message.getFieldWithDefault($obj$, $index$, $default$)",
2131 "obj", obj_reference,
2132 "index", JSFieldIndex(field),
2133 "default", JSFieldDefault(field));
2134 } else {
2135 if (is_float_or_double) {
2136 if (field->is_required()) {
2137 // Use "+" to convert all fields to numeric (including null).
2138 printer->Print(
2139 "+jspb.Message.getField($obj$, $index$)",
2140 "index", JSFieldIndex(field),
2141 "obj", obj_reference);
2142 } else {
2143 // Converts "NaN" and "Infinity" while preserving null.
2144 printer->Print(
2145 "jspb.Message.get$cardinality$FloatingPointField($obj$, $index$)",
2146 "cardinality", field->is_repeated() ? "Repeated" : "Optional",
2147 "index", JSFieldIndex(field),
2148 "obj", obj_reference);
2149 }
2150 } else {
2151 printer->Print("jspb.Message.getField($obj$, $index$)",
2152 "index", JSFieldIndex(field),
2153 "obj", obj_reference);
2154 }
2155 }
2156 }
2157
1922 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options, 2158 void Generator::GenerateClassFieldToObject(const GeneratorOptions& options,
1923 io::Printer* printer, 2159 io::Printer* printer,
1924 const FieldDescriptor* field) const { 2160 const FieldDescriptor* field) const {
1925 printer->Print("$fieldname$: ", 2161 printer->Print("$fieldname$: ",
1926 "fieldname", JSObjectFieldName(field)); 2162 "fieldname", JSObjectFieldName(options, field));
1927 2163
1928 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2164 if (IsMap(options, field)) {
2165 const FieldDescriptor* value_field = MapFieldValue(field);
2166 // If the map values are of a message type, we must provide their static
2167 // toObject() method; otherwise we pass undefined for that argument.
2168 string value_to_object;
2169 if (value_field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2170 value_to_object =
2171 GetPath(options, value_field->message_type()) + ".toObject";
2172 } else {
2173 value_to_object = "undefined";
2174 }
2175 printer->Print(
2176 "(f = msg.get$name$()) ? f.toObject(includeInstance, $valuetoobject$) "
2177 ": []",
2178 "name", JSGetterName(options, field), "valuetoobject", value_to_object);
2179 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
1929 // Message field. 2180 // Message field.
1930 if (field->is_repeated()) { 2181 if (field->is_repeated()) {
1931 { 2182 {
1932 printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n" 2183 printer->Print("jspb.Message.toObjectList(msg.get$getter$(),\n"
1933 " $type$.toObject, includeInstance)", 2184 " $type$.toObject, includeInstance)",
1934 "getter", JSGetterName(field), 2185 "getter", JSGetterName(options, field),
1935 "type", SubmessageTypeRef(options, field)); 2186 "type", SubmessageTypeRef(options, field));
1936 } 2187 }
1937 } else { 2188 } else {
1938 printer->Print("(f = msg.get$getter$()) && " 2189 printer->Print("(f = msg.get$getter$()) && "
1939 "$type$.toObject(includeInstance, f)", 2190 "$type$.toObject(includeInstance, f)",
1940 "getter", JSGetterName(field), 2191 "getter", JSGetterName(options, field),
1941 "type", SubmessageTypeRef(options, field)); 2192 "type", SubmessageTypeRef(options, field));
1942 } 2193 }
2194 } else if (field->type() == FieldDescriptor::TYPE_BYTES) {
2195 // For bytes fields we want to always return the B64 data.
2196 printer->Print("msg.get$getter$()",
2197 "getter", JSGetterName(options, field, BYTES_B64));
1943 } else { 2198 } else {
1944 // Simple field (singular or repeated). 2199 bool use_default = field->has_default_value();
1945 if ((!HasFieldPresence(field) && !field->is_repeated()) || 2200
1946 field->type() == FieldDescriptor::TYPE_BYTES) { 2201 if (field->file()->syntax() == FileDescriptor::SYNTAX_PROTO3 &&
1947 // Delegate to the generated get<field>() method in order not to duplicate 2202 // Repeated fields get initialized to their default in the constructor
1948 // the proto3-field-default-value or byte-coercion logic here. 2203 // (why?), so we emit a plain getField() call for them.
1949 printer->Print("msg.get$getter$()", 2204 !field->is_repeated() && !UseBrokenPresenceSemantics(options, field)) {
1950 "getter", JSGetterName(field, BYTES_B64)); 2205 // Proto3 puts all defaults (including implicit defaults) in toObject().
1951 } else { 2206 // But for proto2 we leave the existing semantics unchanged: unset fields
1952 if (field->has_default_value()) { 2207 // without default are unset.
1953 printer->Print("jspb.Message.getField(msg, $index$) == null ? " 2208 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 } 2209 }
2210
2211 // We don't implement this by calling the accessors, because the semantics
2212 // of the accessors are changing independently of the toObject() semantics.
2213 // We are migrating the accessors to return defaults instead of null, but
2214 // it may take longer to migrate toObject (or we might not want to do it at
2215 // all). So we want to generate independent code.
2216 GenerateFieldValueExpression(printer, "msg", field, use_default);
1978 } 2217 }
1979 } 2218 }
1980 2219
1981 void Generator::GenerateClassFromObject(const GeneratorOptions& options, 2220 void Generator::GenerateClassFromObject(const GeneratorOptions& options,
1982 io::Printer* printer, 2221 io::Printer* printer,
1983 const Descriptor* desc) const { 2222 const Descriptor* desc) const {
1984 printer->Print( 2223 printer->Print(
1985 "if (jspb.Message.GENERATE_FROM_OBJECT) {\n" 2224 "if (jspb.Message.GENERATE_FROM_OBJECT) {\n"
1986 "/**\n" 2225 "/**\n"
1987 " * Loads data from an object into a new instance of this proto.\n" 2226 " * Loads data from an object into a new instance of this proto.\n"
(...skipping 13 matching lines...) Expand all
2001 printer->Print( 2240 printer->Print(
2002 " return msg;\n" 2241 " return msg;\n"
2003 "};\n" 2242 "};\n"
2004 "}\n"); 2243 "}\n");
2005 } 2244 }
2006 2245
2007 void Generator::GenerateClassFieldFromObject( 2246 void Generator::GenerateClassFieldFromObject(
2008 const GeneratorOptions& options, 2247 const GeneratorOptions& options,
2009 io::Printer* printer, 2248 io::Printer* printer,
2010 const FieldDescriptor* field) const { 2249 const FieldDescriptor* field) const {
2011 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2250 if (IsMap(options, field)) {
2251 const FieldDescriptor* value_field = MapFieldValue(field);
2252 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2253 // Since the map values are of message type, we have to do some extra work
2254 // to recursively call fromObject() on them before setting the map field.
2255 printer->Print(
2256 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
2257 " msg, $index$, jspb.Map.fromObject(obj.$name$, $fieldclass$, "
2258 "$fieldclass$.fromObject));\n",
2259 "name", JSObjectFieldName(options, field),
2260 "index", JSFieldIndex(field),
2261 "fieldclass", GetPath(options, value_field->message_type()));
2262 } else {
2263 // `msg` is a newly-constructed message object that has not yet built any
2264 // map containers wrapping underlying arrays, so we can simply directly
2265 // set the array here without fear of a stale wrapper.
2266 printer->Print(
2267 " goog.isDef(obj.$name$) && "
2268 "jspb.Message.setField(msg, $index$, obj.$name$);\n",
2269 "name", JSObjectFieldName(options, field),
2270 "index", JSFieldIndex(field));
2271 }
2272 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2012 // Message field (singular or repeated) 2273 // Message field (singular or repeated)
2013 if (field->is_repeated()) { 2274 if (field->is_repeated()) {
2014 { 2275 {
2015 printer->Print( 2276 printer->Print(
2016 " goog.isDef(obj.$name$) && " 2277 " goog.isDef(obj.$name$) && "
2017 "jspb.Message.setRepeatedWrapperField(\n" 2278 "jspb.Message.setRepeatedWrapperField(\n"
2018 " msg, $index$, goog.array.map(obj.$name$, function(i) {\n" 2279 " msg, $index$, goog.array.map(obj.$name$, function(i) {\n"
2019 " return $fieldclass$.fromObject(i);\n" 2280 " return $fieldclass$.fromObject(i);\n"
2020 " }));\n", 2281 " }));\n",
2021 "name", JSObjectFieldName(field), 2282 "name", JSObjectFieldName(options, field),
2022 "index", JSFieldIndex(field), 2283 "index", JSFieldIndex(field),
2023 "fieldclass", SubmessageTypeRef(options, field)); 2284 "fieldclass", SubmessageTypeRef(options, field));
2024 } 2285 }
2025 } else { 2286 } else {
2026 printer->Print( 2287 printer->Print(
2027 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n" 2288 " goog.isDef(obj.$name$) && jspb.Message.setWrapperField(\n"
2028 " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n", 2289 " msg, $index$, $fieldclass$.fromObject(obj.$name$));\n",
2029 "name", JSObjectFieldName(field), 2290 "name", JSObjectFieldName(options, field),
2030 "index", JSFieldIndex(field), 2291 "index", JSFieldIndex(field),
2031 "fieldclass", SubmessageTypeRef(options, field)); 2292 "fieldclass", SubmessageTypeRef(options, field));
2032 } 2293 }
2033 } else { 2294 } else {
2034 // Simple (primitive) field. 2295 // Simple (primitive) field.
2035 printer->Print( 2296 printer->Print(
2036 " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, " 2297 " goog.isDef(obj.$name$) && jspb.Message.setField(msg, $index$, "
2037 "obj.$name$);\n", 2298 "obj.$name$);\n",
2038 "name", JSObjectFieldName(field), 2299 "name", JSObjectFieldName(options, field),
2039 "index", JSFieldIndex(field)); 2300 "index", JSFieldIndex(field));
2040 } 2301 }
2041 } 2302 }
2042 2303
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, 2304 void Generator::GenerateClassRegistration(const GeneratorOptions& options,
2059 io::Printer* printer, 2305 io::Printer* printer,
2060 const Descriptor* desc) const { 2306 const Descriptor* desc) const {
2061 // Register any extensions defined inside this message type. 2307 // Register any extensions defined inside this message type.
2062 for (int i = 0; i < desc->extension_count(); i++) { 2308 for (int i = 0; i < desc->extension_count(); i++) {
2063 const FieldDescriptor* extension = desc->extension(i); 2309 const FieldDescriptor* extension = desc->extension(i);
2064 if (ShouldGenerateExtension(extension)) { 2310 if (ShouldGenerateExtension(extension)) {
2065 GenerateExtension(options, printer, extension); 2311 GenerateExtension(options, printer, extension);
2066 } 2312 }
2067 } 2313 }
2068 2314
2069 } 2315 }
2070 2316
2071 void Generator::GenerateClassFields(const GeneratorOptions& options, 2317 void Generator::GenerateClassFields(const GeneratorOptions& options,
2072 io::Printer* printer, 2318 io::Printer* printer,
2073 const Descriptor* desc) const { 2319 const Descriptor* desc) const {
2074 for (int i = 0; i < desc->field_count(); i++) { 2320 for (int i = 0; i < desc->field_count(); i++) {
2075 if (!IgnoreField(desc->field(i))) { 2321 if (!IgnoreField(desc->field(i))) {
2076 GenerateClassField(options, printer, desc->field(i)); 2322 GenerateClassField(options, printer, desc->field(i));
2077 } 2323 }
2078 } 2324 }
2079 } 2325 }
2080 2326
2081 void GenerateBytesWrapper(const GeneratorOptions& options, 2327 void GenerateBytesWrapper(const GeneratorOptions& options,
2082 io::Printer* printer, 2328 io::Printer* printer,
2083 const FieldDescriptor* field, 2329 const FieldDescriptor* field,
2084 BytesMode bytes_mode) { 2330 BytesMode bytes_mode) {
2085 string type = 2331 string type = JSFieldTypeAnnotation(
2086 JSFieldTypeAnnotation(options, field, 2332 options, field,
2087 /* force_optional = */ false, 2333 /* is_setter_argument = */ false,
2088 /* force_present = */ !HasFieldPresence(field), 2334 /* force_present = */ false,
2089 /* singular_if_not_packed = */ false, 2335 /* singular_if_not_packed = */ false, bytes_mode);
2090 bytes_mode);
2091 printer->Print( 2336 printer->Print(
2092 "/**\n" 2337 "/**\n"
2093 " * $fielddef$\n" 2338 " * $fielddef$\n"
2094 "$comment$" 2339 "$comment$"
2095 " * This is a type-conversion wrapper around `get$defname$()`\n" 2340 " * This is a type-conversion wrapper around `get$defname$()`\n"
2096 " * @return {$type$}\n" 2341 " * @return {$type$}\n"
2097 " */\n" 2342 " */\n"
2098 "$class$.prototype.get$name$ = function() {\n" 2343 "$class$.prototype.get$name$ = function() {\n"
2099 " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n" 2344 " return /** @type {$type$} */ (jspb.Message.bytes$list$As$suffix$(\n"
2100 " this.get$defname$()));\n" 2345 " this.get$defname$()));\n"
2101 "};\n" 2346 "};\n"
2102 "\n" 2347 "\n"
2103 "\n", 2348 "\n",
2104 "fielddef", FieldDefinition(options, field), 2349 "fielddef", FieldDefinition(options, field),
2105 "comment", FieldComments(field, bytes_mode), 2350 "comment", FieldComments(field, bytes_mode),
2106 "type", type, 2351 "type", type,
2107 "class", GetPath(options, field->containing_type()), 2352 "class", GetPath(options, field->containing_type()),
2108 "name", JSGetterName(field, bytes_mode), 2353 "name", JSGetterName(options, field, bytes_mode),
2109 "list", field->is_repeated() ? "List" : "", 2354 "list", field->is_repeated() ? "List" : "",
2110 "suffix", JSByteGetterSuffix(bytes_mode), 2355 "suffix", JSByteGetterSuffix(bytes_mode),
2111 "defname", JSGetterName(field, BYTES_DEFAULT)); 2356 "defname", JSGetterName(options, field, BYTES_DEFAULT));
2112 } 2357 }
2113 2358
2114 2359
2115 void Generator::GenerateClassField(const GeneratorOptions& options, 2360 void Generator::GenerateClassField(const GeneratorOptions& options,
2116 io::Printer* printer, 2361 io::Printer* printer,
2117 const FieldDescriptor* field) const { 2362 const FieldDescriptor* field) const {
2118 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2363 if (IsMap(options, field)) {
2364 const FieldDescriptor* key_field = MapFieldKey(field);
2365 const FieldDescriptor* value_field = MapFieldValue(field);
2366 // Map field: special handling to instantiate the map object on demand.
2367 string key_type =
2368 JSFieldTypeAnnotation(
2369 options, key_field,
2370 /* is_setter_argument = */ false,
2371 /* force_present = */ true,
2372 /* singular_if_not_packed = */ false);
2373 string value_type =
2374 JSFieldTypeAnnotation(
2375 options, value_field,
2376 /* is_setter_argument = */ false,
2377 /* force_present = */ true,
2378 /* singular_if_not_packed = */ false);
2379
2119 printer->Print( 2380 printer->Print(
2120 "/**\n" 2381 "/**\n"
2121 " * $fielddef$\n" 2382 " * $fielddef$\n"
2383 " * @param {boolean=} opt_noLazyCreate Do not create the map if\n"
2384 " * empty, instead returning `undefined`\n"
2385 " * @return {!jspb.Map<$keytype$,$valuetype$>}\n"
2386 " */\n",
2387 "fielddef", FieldDefinition(options, field),
2388 "keytype", key_type,
2389 "valuetype", value_type);
2390 printer->Print(
2391 "$class$.prototype.get$name$ = function(opt_noLazyCreate) {\n"
2392 " return /** @type {!jspb.Map<$keytype$,$valuetype$>} */ (\n",
2393 "class", GetPath(options, field->containing_type()),
2394 "name", JSGetterName(options, field),
2395 "keytype", key_type,
2396 "valuetype", value_type);
2397 printer->Print(
2398 " jspb.Message.getMapField(this, $index$, opt_noLazyCreate",
2399 "index", JSFieldIndex(field));
2400
2401 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2402 printer->Print(",\n"
2403 " $messageType$",
2404 "messageType", GetPath(options, value_field->message_type()));
2405 } else {
2406 printer->Print(",\n"
2407 " null");
2408 }
2409
2410 printer->Print(
2411 "));\n");
2412
2413 printer->Print(
2414 "};\n"
2415 "\n"
2416 "\n");
2417 } else if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2418 // Message field: special handling in order to wrap the underlying data
2419 // array with a message object.
2420
2421 printer->Print(
2422 "/**\n"
2423 " * $fielddef$\n"
2122 "$comment$" 2424 "$comment$"
2123 " * @return {$type$}\n" 2425 " * @return {$type$}\n"
2124 " */\n", 2426 " */\n",
2125 "fielddef", FieldDefinition(options, field), 2427 "fielddef", FieldDefinition(options, field),
2126 "comment", FieldComments(field, BYTES_DEFAULT), 2428 "comment", FieldComments(field, BYTES_DEFAULT),
2127 "type", JSFieldTypeAnnotation(options, field, 2429 "type", JSFieldTypeAnnotation(options, field,
2128 /* force_optional = */ false, 2430 /* is_setter_argument = */ false,
2129 /* force_present = */ false, 2431 /* force_present = */ false,
2130 /* singular_if_not_packed = */ false)); 2432 /* singular_if_not_packed = */ false));
2131 printer->Print( 2433 printer->Print(
2132 "$class$.prototype.get$name$ = function() {\n" 2434 "$class$.prototype.get$name$ = function() {\n"
2133 " return /** @type{$type$} */ (\n" 2435 " return /** @type{$type$} */ (\n"
2134 " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, " 2436 " jspb.Message.get$rpt$WrapperField(this, $wrapperclass$, "
2135 "$index$$required$));\n" 2437 "$index$$required$));\n"
2136 "};\n" 2438 "};\n"
2137 "\n" 2439 "\n"
2138 "\n", 2440 "\n",
2139 "class", GetPath(options, field->containing_type()), 2441 "class", GetPath(options, field->containing_type()),
2140 "name", JSGetterName(field), 2442 "name", JSGetterName(options, field),
2141 "type", JSFieldTypeAnnotation(options, field, 2443 "type", JSFieldTypeAnnotation(options, field,
2142 /* force_optional = */ false, 2444 /* is_setter_argument = */ false,
2143 /* force_present = */ false, 2445 /* force_present = */ false,
2144 /* singular_if_not_packed = */ false), 2446 /* singular_if_not_packed = */ false),
2145 "rpt", (field->is_repeated() ? "Repeated" : ""), 2447 "rpt", (field->is_repeated() ? "Repeated" : ""),
2146 "index", JSFieldIndex(field), 2448 "index", JSFieldIndex(field),
2147 "wrapperclass", SubmessageTypeRef(options, field), 2449 "wrapperclass", SubmessageTypeRef(options, field),
2148 "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ? 2450 "required", (field->label() == FieldDescriptor::LABEL_REQUIRED ?
2149 ", 1" : "")); 2451 ", 1" : ""));
2150 printer->Print( 2452 printer->Print(
2151 "/** @param {$optionaltype$} value $returndoc$ */\n" 2453 "/** @param {$optionaltype$} value$returndoc$ */\n"
2152 "$class$.prototype.set$name$ = function(value) {\n" 2454 "$class$.prototype.set$name$ = function(value) {\n"
2153 " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(", 2455 " jspb.Message.set$oneoftag$$repeatedtag$WrapperField(",
2154 "optionaltype", 2456 "optionaltype",
2155 JSFieldTypeAnnotation(options, field, 2457 JSFieldTypeAnnotation(options, field,
2156 /* force_optional = */ true, 2458 /* is_setter_argument = */ true,
2157 /* force_present = */ false, 2459 /* force_present = */ false,
2158 /* singular_if_not_packed = */ false), 2460 /* singular_if_not_packed = */ false),
2159 "returndoc", JSReturnDoc(options, field), 2461 "returndoc", JSReturnDoc(options, field),
2160 "class", GetPath(options, field->containing_type()), 2462 "class", GetPath(options, field->containing_type()),
2161 "name", JSGetterName(field), 2463 "name", JSGetterName(options, field),
2162 "oneoftag", (field->containing_oneof() ? "Oneof" : ""), 2464 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2163 "repeatedtag", (field->is_repeated() ? "Repeated" : "")); 2465 "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2164 2466
2165 printer->Print( 2467 printer->Print(
2166 "this, $index$$oneofgroup$, value);$returnvalue$\n" 2468 "this, $index$$oneofgroup$, value);$returnvalue$\n"
2167 "};\n" 2469 "};\n"
2168 "\n" 2470 "\n"
2169 "\n", 2471 "\n",
2170 "index", JSFieldIndex(field), 2472 "index", JSFieldIndex(field),
2171 "oneofgroup", (field->containing_oneof() ? 2473 "oneofgroup", (field->containing_oneof() ?
2172 (", " + JSOneofArray(options, field)) : ""), 2474 (", " + JSOneofArray(options, field)) : ""),
2173 "returnvalue", JSReturnClause(field)); 2475 "returnvalue", JSReturnClause(field));
2174 2476
2175 printer->Print( 2477 if (field->is_repeated()) {
2176 "$class$.prototype.clear$name$ = function() {\n" 2478 GenerateRepeatedMessageHelperMethods(options, printer, field);
2177 " this.set$name$($clearedvalue$);$returnvalue$\n" 2479 }
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 2480
2186 } else { 2481 } else {
2187 bool untyped = 2482 bool untyped =
2188 false; 2483 false;
2189 2484
2190 // Simple (primitive) field, either singular or repeated. 2485 // Simple (primitive) field, either singular or repeated.
2191 2486
2192 // TODO(b/26173701): Always use BYTES_DEFAULT for the getter return type; 2487 // 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 2488 // 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. 2489 // type is always base64 string, pending a LSC to migrate to typed getters.
2195 BytesMode bytes_mode = 2490 BytesMode bytes_mode =
2196 field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ? 2491 field->type() == FieldDescriptor::TYPE_BYTES && !options.binary ?
2197 BYTES_B64 : BYTES_DEFAULT; 2492 BYTES_B64 : BYTES_DEFAULT;
2198 string typed_annotation = 2493 string typed_annotation = JSFieldTypeAnnotation(
2199 JSFieldTypeAnnotation(options, field, 2494 options, field,
2200 /* force_optional = */ false, 2495 /* is_setter_argument = */ false,
2201 /* force_present = */ !HasFieldPresence(field), 2496 /* force_present = */ false,
2202 /* singular_if_not_packed = */ false, 2497 /* singular_if_not_packed = */ false,
2203 /* bytes_mode = */ bytes_mode); 2498 /* bytes_mode = */ bytes_mode);
2204 if (untyped) { 2499 if (untyped) {
2205 printer->Print( 2500 printer->Print(
2206 "/**\n" 2501 "/**\n"
2207 " * @return {?} Raw field, untyped.\n" 2502 " * @return {?} Raw field, untyped.\n"
2208 " */\n"); 2503 " */\n");
2209 } else { 2504 } else {
2210 printer->Print( 2505 printer->Print(
2211 "/**\n" 2506 "/**\n"
2212 " * $fielddef$\n" 2507 " * $fielddef$\n"
2213 "$comment$" 2508 "$comment$"
2214 " * @return {$type$}\n" 2509 " * @return {$type$}\n"
2215 " */\n", 2510 " */\n",
2216 "fielddef", FieldDefinition(options, field), 2511 "fielddef", FieldDefinition(options, field),
2217 "comment", FieldComments(field, bytes_mode), 2512 "comment", FieldComments(field, bytes_mode),
2218 "type", typed_annotation); 2513 "type", typed_annotation);
2219 } 2514 }
2220 2515
2221 printer->Print( 2516 printer->Print(
2222 "$class$.prototype.get$name$ = function() {\n", 2517 "$class$.prototype.get$name$ = function() {\n",
2223 "class", GetPath(options, field->containing_type()), 2518 "class", GetPath(options, field->containing_type()),
2224 "name", JSGetterName(field)); 2519 "name", JSGetterName(options, field));
2225 2520
2226 if (untyped) { 2521 if (untyped) {
2227 printer->Print( 2522 printer->Print(
2228 " return "); 2523 " return ");
2229 } else { 2524 } else {
2230 printer->Print( 2525 printer->Print(
2231 " return /** @type {$type$} */ (", 2526 " return /** @type {$type$} */ (",
2232 "type", typed_annotation); 2527 "type", typed_annotation);
2233 } 2528 }
2234 2529
2235 // For proto3 fields without presence, use special getters that will return 2530 bool use_default = !ReturnsNullWhenUnset(options, field);
2236 // defaults when the field is unset, possibly constructing a value if 2531
2237 // required. 2532 // Raw fields with no default set should just return undefined.
2238 if (!HasFieldPresence(field) && !field->is_repeated()) { 2533 if (untyped && !field->has_default_value()) {
2239 printer->Print("jspb.Message.getFieldProto3(this, $index$, $default$)", 2534 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 } 2535 }
2269 2536
2537 // Repeated fields get initialized to their default in the constructor
2538 // (why?), so we emit a plain getField() call for them.
2539 if (field->is_repeated()) {
2540 use_default = false;
2541 }
2542
2543 GenerateFieldValueExpression(printer, "this", field, use_default);
2544
2270 if (untyped) { 2545 if (untyped) {
2271 printer->Print( 2546 printer->Print(
2272 ";\n" 2547 ";\n"
2273 "};\n" 2548 "};\n"
2274 "\n" 2549 "\n"
2275 "\n"); 2550 "\n");
2276 } else { 2551 } else {
2277 printer->Print( 2552 printer->Print(
2278 ");\n" 2553 ");\n"
2279 "};\n" 2554 "};\n"
2280 "\n" 2555 "\n"
2281 "\n"); 2556 "\n");
2282 } 2557 }
2283 2558
2284 if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) { 2559 if (field->type() == FieldDescriptor::TYPE_BYTES && !untyped) {
2285 GenerateBytesWrapper(options, printer, field, BYTES_B64); 2560 GenerateBytesWrapper(options, printer, field, BYTES_B64);
2286 GenerateBytesWrapper(options, printer, field, BYTES_U8); 2561 GenerateBytesWrapper(options, printer, field, BYTES_U8);
2287 } 2562 }
2288 2563
2289 if (untyped) { 2564 if (untyped) {
2290 printer->Print( 2565 printer->Print(
2291 "/**\n" 2566 "/**\n"
2292 " * @param {*} value $returndoc$\n" 2567 " * @param {*} value$returndoc$\n"
2293 " */\n", 2568 " */\n",
2294 "returndoc", JSReturnDoc(options, field)); 2569 "returndoc", JSReturnDoc(options, field));
2295 } else { 2570 } else {
2296 printer->Print( 2571 printer->Print(
2297 "/** @param {$optionaltype$} value $returndoc$ */\n", 2572 "/** @param {$optionaltype$} value$returndoc$ */\n", "optionaltype",
2298 "optionaltype", 2573 JSFieldTypeAnnotation(
2299 JSFieldTypeAnnotation(options, field, 2574 options, field,
2300 /* force_optional = */ true, 2575 /* is_setter_argument = */ true,
2301 /* force_present = */ !HasFieldPresence(field), 2576 /* force_present = */ false,
2302 /* singular_if_not_packed = */ false), 2577 /* singular_if_not_packed = */ false),
2303 "returndoc", JSReturnDoc(options, field)); 2578 "returndoc", JSReturnDoc(options, field));
2304 } 2579 }
2305 printer->Print( 2580 printer->Print(
2306 "$class$.prototype.set$name$ = function(value) {\n" 2581 "$class$.prototype.set$name$ = function(value) {\n"
2307 " jspb.Message.set$oneoftag$Field(this, $index$", 2582 " jspb.Message.set$oneoftag$Field(this, $index$",
2308 "class", GetPath(options, field->containing_type()), 2583 "class", GetPath(options, field->containing_type()),
2309 "name", JSGetterName(field), 2584 "name", JSGetterName(options, field),
2310 "oneoftag", (field->containing_oneof() ? "Oneof" : ""), 2585 "oneoftag", (field->containing_oneof() ? "Oneof" : ""),
2311 "index", JSFieldIndex(field)); 2586 "index", JSFieldIndex(field));
2312 printer->Print( 2587 printer->Print(
2313 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n" 2588 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$);$returnvalue$\n"
2314 "};\n" 2589 "};\n"
2315 "\n" 2590 "\n"
2316 "\n", 2591 "\n",
2317 "type", 2592 "type",
2318 untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "", 2593 untyped ? "/** @type{string|number|boolean|Array|undefined} */(" : "",
2319 "typeclose", untyped ? ")" : "", 2594 "typeclose", untyped ? ")" : "",
2320 "oneofgroup", 2595 "oneofgroup",
2321 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) 2596 (field->containing_oneof() ? (", " + JSOneofArray(options, field))
2322 : ""), 2597 : ""),
2323 "returnvalue", JSReturnClause(field), "rptvalueinit", 2598 "returnvalue", JSReturnClause(field), "rptvalueinit",
2324 (field->is_repeated() ? " || []" : "")); 2599 (field->is_repeated() ? " || []" : ""));
2325 2600
2326 if (untyped) { 2601 if (untyped) {
2327 printer->Print( 2602 printer->Print(
2328 "/**\n" 2603 "/**\n"
2329 " * Clears the value. $returndoc$\n" 2604 " * Clears the value.$returndoc$\n"
2330 " */\n", 2605 " */\n",
2331 "returndoc", JSReturnDoc(options, field)); 2606 "returndoc", JSReturnDoc(options, field));
2332 } 2607 }
2333 2608
2334 if (HasFieldPresence(field)) { 2609
2335 printer->Print( 2610 if (field->is_repeated()) {
2336 "$class$.prototype.clear$name$ = function() {\n" 2611 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 } 2612 }
2352 } 2613 }
2614
2615 // Generate clearFoo() method for map fields, repeated fields, and other
2616 // fields with presence.
2617 if (IsMap(options, field)) {
2618 printer->Print(
2619 "$class$.prototype.clear$name$ = function() {\n"
2620 " this.get$name$().clear();$returnvalue$\n"
2621 "};\n"
2622 "\n"
2623 "\n",
2624 "class", GetPath(options, field->containing_type()),
2625 "name", JSGetterName(options, field),
2626 "returnvalue", JSReturnClause(field));
2627 } else if (field->is_repeated() ||
2628 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
2629 !field->is_required())) {
2630 // Fields where we can delegate to the regular setter.
2631 printer->Print(
2632 "$class$.prototype.clear$name$ = function() {\n"
2633 " this.set$name$($clearedvalue$);$returnvalue$\n"
2634 "};\n"
2635 "\n"
2636 "\n",
2637 "class", GetPath(options, field->containing_type()),
2638 "name", JSGetterName(options, field),
2639 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2640 "returnvalue", JSReturnClause(field));
2641 } else if (HasFieldPresence(options, field)) {
2642 // Fields where we can't delegate to the regular setter because it doesn't
2643 // accept "undefined" as an argument.
2644 printer->Print(
2645 "$class$.prototype.clear$name$ = function() {\n"
2646 " jspb.Message.set$maybeoneof$Field(this, "
2647 "$index$$maybeoneofgroup$, ",
2648 "class", GetPath(options, field->containing_type()),
2649 "name", JSGetterName(options, field),
2650 "maybeoneof", (field->containing_oneof() ? "Oneof" : ""),
2651 "maybeoneofgroup", (field->containing_oneof() ?
2652 (", " + JSOneofArray(options, field)) : ""),
2653 "index", JSFieldIndex(field));
2654 printer->Print(
2655 "$clearedvalue$);$returnvalue$\n"
2656 "};\n"
2657 "\n"
2658 "\n",
2659 "clearedvalue", (field->is_repeated() ? "[]" : "undefined"),
2660 "returnvalue", JSReturnClause(field));
2661 }
2662
2663 if (HasFieldPresence(options, field)) {
2664 printer->Print(
2665 "/**\n"
2666 " * Returns whether this field is set.\n"
2667 " * @return {!boolean}\n"
2668 " */\n"
2669 "$class$.prototype.has$name$ = function() {\n"
2670 " return jspb.Message.getField(this, $index$) != null;\n"
2671 "};\n"
2672 "\n"
2673 "\n",
2674 "class", GetPath(options, field->containing_type()),
2675 "name", JSGetterName(options, field),
2676 "index", JSFieldIndex(field));
2677 }
2353 } 2678 }
2354 2679
2680 void Generator::GenerateRepeatedPrimitiveHelperMethods(
2681 const GeneratorOptions& options, io::Printer* printer,
2682 const FieldDescriptor* field, bool untyped) const {
2683 printer->Print(
2684 "/**\n"
2685 " * @param {!$optionaltype$} value\n"
2686 " * @param {number=} opt_index\n"
2687 " */\n"
2688 "$class$.prototype.add$name$ = function(value, opt_index) {\n"
2689 " jspb.Message.addToRepeatedField(this, $index$",
2690 "class", GetPath(options, field->containing_type()), "name",
2691 JSGetterName(options, field, BYTES_DEFAULT,
2692 /* drop_list = */ true),
2693 "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "index",
2694 JSFieldIndex(field));
2695 printer->Print(
2696 "$oneofgroup$, $type$value$rptvalueinit$$typeclose$, opt_index);\n"
2697 "};\n"
2698 "\n"
2699 "\n",
2700 "type", untyped ? "/** @type{string|number|boolean|!Uint8Array} */(" : "",
2701 "typeclose", untyped ? ")" : "", "oneofgroup",
2702 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
2703 "rptvalueinit", "");
2704 }
2705
2706 void Generator::GenerateRepeatedMessageHelperMethods(
2707 const GeneratorOptions& options, io::Printer* printer,
2708 const FieldDescriptor* field) const {
2709 printer->Print(
2710 "/**\n"
2711 " * @param {!$optionaltype$=} opt_value\n"
2712 " * @param {number=} opt_index\n"
2713 " * @return {!$optionaltype$}\n"
2714 " */\n"
2715 "$class$.prototype.add$name$ = function(opt_value, opt_index) {\n"
2716 " return jspb.Message.addTo$repeatedtag$WrapperField(",
2717 "optionaltype", JSTypeName(options, field, BYTES_DEFAULT), "class",
2718 GetPath(options, field->containing_type()), "name",
2719 JSGetterName(options, field, BYTES_DEFAULT,
2720 /* drop_list = */ true),
2721 "repeatedtag", (field->is_repeated() ? "Repeated" : ""));
2722
2723 printer->Print(
2724 "this, $index$$oneofgroup$, opt_value, $ctor$, opt_index);\n"
2725 "};\n"
2726 "\n"
2727 "\n",
2728 "index", JSFieldIndex(field), "oneofgroup",
2729 (field->containing_oneof() ? (", " + JSOneofArray(options, field)) : ""),
2730 "ctor", GetPath(options, field->message_type()));
2731 }
2732
2355 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options, 2733 void Generator::GenerateClassExtensionFieldInfo(const GeneratorOptions& options,
2356 io::Printer* printer, 2734 io::Printer* printer,
2357 const Descriptor* desc) const { 2735 const Descriptor* desc) const {
2358 if (IsExtendable(desc)) { 2736 if (IsExtendable(desc)) {
2359 printer->Print( 2737 printer->Print(
2360 "\n" 2738 "\n"
2361 "/**\n" 2739 "/**\n"
2362 " * The extensions registered with this message class. This is a " 2740 " * The extensions registered with this message class. This is a "
2363 "map of\n" 2741 "map of\n"
2364 " * extension field number to fieldInfo object.\n" 2742 " * extension field number to fieldInfo object.\n"
2365 " *\n" 2743 " *\n"
2366 " * For example:\n" 2744 " * For example:\n"
2367 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, " 2745 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2368 "ctor: proto.example.MyMessage} }\n" 2746 "ctor: proto.example.MyMessage} }\n"
2369 " *\n" 2747 " *\n"
2370 " * fieldName contains the JsCompiler renamed field name property " 2748 " * fieldName contains the JsCompiler renamed field name property "
2371 "so that it\n" 2749 "so that it\n"
2372 " * works in OPTIMIZED mode.\n" 2750 " * works in OPTIMIZED mode.\n"
2373 " *\n" 2751 " *\n"
2374 " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n" 2752 " * @type {!Object.<number, jspb.ExtensionFieldInfo>}\n"
2375 " */\n" 2753 " */\n"
2376 "$class$.extensions = {};\n" 2754 "$class$.extensions = {};\n"
2377 "\n", 2755 "\n",
2378 "class", GetPath(options, desc)); 2756 "class", GetPath(options, desc));
2757
2758 printer->Print(
2759 "\n"
2760 "/**\n"
2761 " * The extensions registered with this message class. This is a "
2762 "map of\n"
2763 " * extension field number to fieldInfo object.\n"
2764 " *\n"
2765 " * For example:\n"
2766 " * { 123: {fieldIndex: 123, fieldName: {my_field_name: 0}, "
2767 "ctor: proto.example.MyMessage} }\n"
2768 " *\n"
2769 " * fieldName contains the JsCompiler renamed field name property "
2770 "so that it\n"
2771 " * works in OPTIMIZED mode.\n"
2772 " *\n"
2773 " * @type {!Object.<number, jspb.ExtensionFieldBinaryInfo>}\n"
2774 " */\n"
2775 "$class$.extensionsBinary = {};\n"
2776 "\n",
2777 "class", GetPath(options, desc));
2379 } 2778 }
2380 } 2779 }
2381 2780
2382 2781
2383 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options, 2782 void Generator::GenerateClassDeserializeBinary(const GeneratorOptions& options,
2384 io::Printer* printer, 2783 io::Printer* printer,
2385 const Descriptor* desc) const { 2784 const Descriptor* desc) const {
2386 // TODO(cfallin): Handle lazy decoding when requested by field option and/or 2785 // TODO(cfallin): Handle lazy decoding when requested by field option and/or
2387 // by default for 'bytes' fields and packed repeated fields. 2786 // by default for 'bytes' fields and packed repeated fields.
2388 2787
(...skipping 20 matching lines...) Expand all
2409 "$class$.deserializeBinaryFromReader = function(msg, reader) {\n" 2808 "$class$.deserializeBinaryFromReader = function(msg, reader) {\n"
2410 " while (reader.nextField()) {\n" 2809 " while (reader.nextField()) {\n"
2411 " if (reader.isEndGroup()) {\n" 2810 " if (reader.isEndGroup()) {\n"
2412 " break;\n" 2811 " break;\n"
2413 " }\n" 2812 " }\n"
2414 " var field = reader.getFieldNumber();\n" 2813 " var field = reader.getFieldNumber();\n"
2415 " switch (field) {\n", 2814 " switch (field) {\n",
2416 "class", GetPath(options, desc)); 2815 "class", GetPath(options, desc));
2417 2816
2418 for (int i = 0; i < desc->field_count(); i++) { 2817 for (int i = 0; i < desc->field_count(); i++) {
2419 GenerateClassDeserializeBinaryField(options, printer, desc->field(i)); 2818 if (!IgnoreField(desc->field(i))) {
2819 GenerateClassDeserializeBinaryField(options, printer, desc->field(i));
2820 }
2420 } 2821 }
2421 2822
2422 printer->Print( 2823 printer->Print(
2423 " default:\n"); 2824 " default:\n");
2424 if (IsExtendable(desc)) { 2825 if (IsExtendable(desc)) {
2425 printer->Print( 2826 printer->Print(
2426 " jspb.Message.readBinaryExtension(msg, reader, $extobj$,\n" 2827 " jspb.Message.readBinaryExtension(msg, reader, $extobj$Binary,\n"
2427 " $class$.prototype.getExtension,\n" 2828 " $class$.prototype.getExtension,\n"
2428 " $class$.prototype.setExtension);\n" 2829 " $class$.prototype.setExtension);\n"
2429 " break;\n", 2830 " break;\n",
2430 "extobj", JSExtensionsObjectName(options, desc->file(), desc), 2831 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2431 "class", GetPath(options, desc)); 2832 "class", GetPath(options, desc));
2432 } else { 2833 } else {
2433 printer->Print( 2834 printer->Print(
2434 " reader.skipField();\n" 2835 " reader.skipField();\n"
2435 " break;\n"); 2836 " break;\n");
2436 } 2837 }
2437 2838
2438 printer->Print( 2839 printer->Print(
2439 " }\n" 2840 " }\n"
2440 " }\n" 2841 " }\n"
2441 " return msg;\n" 2842 " return msg;\n"
2442 "};\n" 2843 "};\n"
2443 "\n" 2844 "\n"
2444 "\n"); 2845 "\n");
2445 } 2846 }
2446 2847
2447 void Generator::GenerateClassDeserializeBinaryField( 2848 void Generator::GenerateClassDeserializeBinaryField(
2448 const GeneratorOptions& options, 2849 const GeneratorOptions& options,
2449 io::Printer* printer, 2850 io::Printer* printer,
2450 const FieldDescriptor* field) const { 2851 const FieldDescriptor* field) const {
2451 2852
2452 printer->Print(" case $num$:\n", 2853 printer->Print(" case $num$:\n",
2453 "num", SimpleItoa(field->number())); 2854 "num", SimpleItoa(field->number()));
2454 2855
2455 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) { 2856 if (IsMap(options, field)) {
2857 const FieldDescriptor* key_field = MapFieldKey(field);
2858 const FieldDescriptor* value_field = MapFieldValue(field);
2456 printer->Print( 2859 printer->Print(
2457 " var value = new $fieldclass$;\n" 2860 " var value = msg.get$name$();\n"
2458 " reader.read$msgOrGroup$($grpfield$value," 2861 " reader.readMessage(value, function(message, reader) {\n",
2459 "$fieldclass$.deserializeBinaryFromReader);\n", 2862 "name", JSGetterName(options, field));
2863
2864 printer->Print(" jspb.Map.deserializeBinary(message, reader, "
2865 "$keyReaderFn$, $valueReaderFn$",
2866 "keyReaderFn", JSBinaryReaderMethodName(options, key_field),
2867 "valueReaderFn", JSBinaryReaderMethodName(options, value_field));
2868
2869 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
2870 printer->Print(", $messageType$.deserializeBinaryFromReader",
2871 "messageType", GetPath(options, value_field->message_type()));
2872 }
2873
2874 printer->Print(");\n");
2875 printer->Print(" });\n");
2876 } else {
2877 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) {
2878 printer->Print(
2879 " var value = new $fieldclass$;\n"
2880 " reader.read$msgOrGroup$($grpfield$value,"
2881 "$fieldclass$.deserializeBinaryFromReader);\n",
2460 "fieldclass", SubmessageTypeRef(options, field), 2882 "fieldclass", SubmessageTypeRef(options, field),
2461 "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ? 2883 "msgOrGroup", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2462 "Group" : "Message", 2884 "Group" : "Message",
2463 "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ? 2885 "grpfield", (field->type() == FieldDescriptor::TYPE_GROUP) ?
2464 (SimpleItoa(field->number()) + ", ") : ""); 2886 (SimpleItoa(field->number()) + ", ") : "");
2465 } else { 2887 } else {
2466 printer->Print( 2888 printer->Print(
2467 " var value = /** @type {$fieldtype$} */ (reader.$reader$());\n", 2889 " var value = /** @type {$fieldtype$} */ "
2468 "fieldtype", JSFieldTypeAnnotation(options, field, false, true, 2890 "(reader.read$reader$());\n",
2469 /* singular_if_not_packed = */ true, 2891 "fieldtype", JSFieldTypeAnnotation(options, field, false, true,
2470 BYTES_U8), 2892 /* singular_if_not_packed */ true,
2471 "reader", JSBinaryReaderMethodName(field)); 2893 BYTES_U8),
2472 } 2894 "reader",
2895 JSBinaryReadWriteMethodName(field, /* is_writer = */ false));
2896 }
2473 2897
2474 if (field->is_repeated() && !field->is_packed()) { 2898 if (field->is_repeated() && !field->is_packed()) {
2475 // Repeated fields receive a |value| one at at a time; append to array 2899 printer->Print(
2476 // returned by get$name$(). Annoyingly, we have to call 'set' after 2900 " msg.add$name$(value);\n", "name",
2477 // changing the array. 2901 JSGetterName(options, field, BYTES_DEFAULT, /* drop_list = */ true));
2478 printer->Print(" msg.get$name$().push(value);\n", "name", 2902 } else {
2479 JSGetterName(field)); 2903 // Singular fields, and packed repeated fields, receive a |value| either
2480 printer->Print(" msg.set$name$(msg.get$name$());\n", "name", 2904 // as the field's value or as the array of all the field's values; set
2481 JSGetterName(field)); 2905 // this as the field's value directly.
2482 } else { 2906 printer->Print(
2483 // Singular fields, and packed repeated fields, receive a |value| either as 2907 " msg.set$name$(value);\n",
2484 // the field's value or as the array of all the field's values; set this as 2908 "name", JSGetterName(options, field));
2485 // the field's value directly. 2909 }
2486 printer->Print(
2487 " msg.set$name$(value);\n",
2488 "name", JSGetterName(field));
2489 } 2910 }
2490 2911
2491 printer->Print(" break;\n"); 2912 printer->Print(" break;\n");
2492 } 2913 }
2493 2914
2494 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options, 2915 void Generator::GenerateClassSerializeBinary(const GeneratorOptions& options,
2495 io::Printer* printer, 2916 io::Printer* printer,
2496 const Descriptor* desc) const { 2917 const Descriptor* desc) const {
2497 printer->Print( 2918 printer->Print(
2498 "/**\n" 2919 "/**\n"
2499 " * Class method variant: serializes the given message to binary data\n"
2500 " * (in protobuf wire format), writing to the given BinaryWriter.\n"
2501 " * @param {!$class$} message\n"
2502 " * @param {!jspb.BinaryWriter} writer\n"
2503 " */\n"
2504 "$class$.serializeBinaryToWriter = function(message, "
2505 "writer) {\n"
2506 " message.serializeBinaryToWriter(writer);\n"
2507 "};\n"
2508 "\n"
2509 "\n"
2510 "/**\n"
2511 " * Serializes the message to binary data (in protobuf wire format).\n" 2920 " * Serializes the message to binary data (in protobuf wire format).\n"
2512 " * @return {!Uint8Array}\n" 2921 " * @return {!Uint8Array}\n"
2513 " */\n" 2922 " */\n"
2514 "$class$.prototype.serializeBinary = function() {\n" 2923 "$class$.prototype.serializeBinary = function() {\n"
2515 " var writer = new jspb.BinaryWriter();\n" 2924 " var writer = new jspb.BinaryWriter();\n"
2516 " this.serializeBinaryToWriter(writer);\n" 2925 " $class$.serializeBinaryToWriter(this, writer);\n"
2517 " return writer.getResultBuffer();\n" 2926 " return writer.getResultBuffer();\n"
2518 "};\n" 2927 "};\n"
2519 "\n" 2928 "\n"
2520 "\n" 2929 "\n"
2521 "/**\n" 2930 "/**\n"
2522 " * Serializes the message to binary data (in protobuf wire format),\n" 2931 " * Serializes the given message to binary data (in protobuf wire\n"
2523 " * writing to the given BinaryWriter.\n" 2932 " * format), writing to the given BinaryWriter.\n"
2933 " * @param {!$class$} message\n"
2524 " * @param {!jspb.BinaryWriter} writer\n" 2934 " * @param {!jspb.BinaryWriter} writer\n"
2525 " */\n" 2935 " */\n"
2526 "$class$.prototype.serializeBinaryToWriter = function (writer) {\n" 2936 "$class$.serializeBinaryToWriter = function(message, "
2937 "writer) {\n"
2527 " var f = undefined;\n", 2938 " var f = undefined;\n",
2528 "class", GetPath(options, desc)); 2939 "class", GetPath(options, desc));
2529 2940
2530 for (int i = 0; i < desc->field_count(); i++) { 2941 for (int i = 0; i < desc->field_count(); i++) {
2531 GenerateClassSerializeBinaryField(options, printer, desc->field(i)); 2942 if (!IgnoreField(desc->field(i))) {
2943 GenerateClassSerializeBinaryField(options, printer, desc->field(i));
2944 }
2532 } 2945 }
2533 2946
2534 if (IsExtendable(desc)) { 2947 if (IsExtendable(desc)) {
2535 printer->Print( 2948 printer->Print(
2536 " jspb.Message.serializeBinaryExtensions(this, writer, $extobj$,\n" 2949 " jspb.Message.serializeBinaryExtensions(message, writer,\n"
2537 " $class$.prototype.getExtension);\n", 2950 " $extobj$Binary, $class$.prototype.getExtension);\n",
2538 "extobj", JSExtensionsObjectName(options, desc->file(), desc), 2951 "extobj", JSExtensionsObjectName(options, desc->file(), desc),
2539 "class", GetPath(options, desc)); 2952 "class", GetPath(options, desc));
2540 } 2953 }
2541 2954
2542 printer->Print( 2955 printer->Print(
2543 "};\n" 2956 "};\n"
2544 "\n" 2957 "\n"
2545 "\n"); 2958 "\n");
2546 } 2959 }
2547 2960
2548 void Generator::GenerateClassSerializeBinaryField( 2961 void Generator::GenerateClassSerializeBinaryField(
2549 const GeneratorOptions& options, 2962 const GeneratorOptions& options,
2550 io::Printer* printer, 2963 io::Printer* printer,
2551 const FieldDescriptor* field) const { 2964 const FieldDescriptor* field) const {
2552 printer->Print( 2965 if (HasFieldPresence(options, field) &&
2553 " f = this.get$name$();\n", 2966 field->cpp_type() != FieldDescriptor::CPPTYPE_MESSAGE) {
2554 "name", JSGetterName(field, BYTES_U8)); 2967 string typed_annotation = JSFieldTypeAnnotation(
2968 options, field,
2969 /* is_setter_argument = */ false,
2970 /* force_present = */ false,
2971 /* singular_if_not_packed = */ false,
2972 /* bytes_mode = */ BYTES_DEFAULT);
2973 printer->Print(
2974 " f = /** @type {$type$} */ "
2975 "(jspb.Message.getField(message, $index$));\n",
2976 "index", JSFieldIndex(field),
2977 "type", typed_annotation);
2978 } else {
2979 printer->Print(
2980 " f = message.get$name$($nolazy$);\n",
2981 "name", JSGetterName(options, field, BYTES_U8),
2982 // No lazy creation for maps containers -- fastpath the empty case.
2983 "nolazy", IsMap(options, field) ? "true" : "");
2984 }
2555 2985
2556 if (field->is_repeated()) { 2986 // Print an `if (condition)` statement that evaluates to true if the field
2987 // goes on the wire.
2988 if (IsMap(options, field)) {
2989 printer->Print(
2990 " if (f && f.getLength() > 0) {\n");
2991 } else if (field->is_repeated()) {
2557 printer->Print( 2992 printer->Print(
2558 " if (f.length > 0) {\n"); 2993 " if (f.length > 0) {\n");
2559 } else { 2994 } else {
2560 if (HasFieldPresence(field)) { 2995 if (HasFieldPresence(options, field)) {
2561 printer->Print( 2996 printer->Print(
2562 " if (f != null) {\n"); 2997 " if (f != null) {\n");
2563 } else { 2998 } else {
2564 // No field presence: serialize onto the wire only if value is 2999 // No field presence: serialize onto the wire only if value is
2565 // non-default. Defaults are documented here: 3000 // non-default. Defaults are documented here:
2566 // https://goto.google.com/lhdfm 3001 // https://goto.google.com/lhdfm
2567 switch (field->cpp_type()) { 3002 switch (field->cpp_type()) {
2568 case FieldDescriptor::CPPTYPE_INT32: 3003 case FieldDescriptor::CPPTYPE_INT32:
2569 case FieldDescriptor::CPPTYPE_INT64: 3004 case FieldDescriptor::CPPTYPE_INT64:
2570 case FieldDescriptor::CPPTYPE_UINT32: 3005 case FieldDescriptor::CPPTYPE_UINT32:
(...skipping 18 matching lines...) Expand all
2589 printer->Print( 3024 printer->Print(
2590 " if (f.length > 0) {\n"); 3025 " if (f.length > 0) {\n");
2591 break; 3026 break;
2592 default: 3027 default:
2593 assert(false); 3028 assert(false);
2594 break; 3029 break;
2595 } 3030 }
2596 } 3031 }
2597 } 3032 }
2598 3033
3034 // Write the field on the wire.
3035 if (IsMap(options, field)) {
3036 const FieldDescriptor* key_field = MapFieldKey(field);
3037 const FieldDescriptor* value_field = MapFieldValue(field);
3038 printer->Print(
3039 " f.serializeBinary($index$, writer, "
3040 "$keyWriterFn$, $valueWriterFn$",
3041 "index", SimpleItoa(field->number()),
3042 "keyWriterFn", JSBinaryWriterMethodName(options, key_field),
3043 "valueWriterFn", JSBinaryWriterMethodName(options, value_field));
3044
3045 if (value_field->type() == FieldDescriptor::TYPE_MESSAGE) {
3046 printer->Print(", $messageType$.serializeBinaryToWriter",
3047 "messageType", GetPath(options, value_field->message_type()));
3048 }
3049
3050 printer->Print(");\n");
3051 } else {
3052 printer->Print(
3053 " writer.write$method$(\n"
3054 " $index$,\n"
3055 " f",
3056 "method", JSBinaryReadWriteMethodName(field, /* is_writer = */ true),
3057 "index", SimpleItoa(field->number()));
3058
3059 if (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE &&
3060 !IsMap(options, field)) {
3061 printer->Print(
3062 ",\n"
3063 " $submsg$.serializeBinaryToWriter\n",
3064 "submsg", SubmessageTypeRef(options, field));
3065 } else {
3066 printer->Print("\n");
3067 }
3068
3069 printer->Print(
3070 " );\n");
3071 }
3072
3073 // Close the `if`.
2599 printer->Print( 3074 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"); 3075 " }\n");
2617 } 3076 }
2618 3077
2619 void Generator::GenerateEnum(const GeneratorOptions& options, 3078 void Generator::GenerateEnum(const GeneratorOptions& options,
2620 io::Printer* printer, 3079 io::Printer* printer,
2621 const EnumDescriptor* enumdesc) const { 3080 const EnumDescriptor* enumdesc) const {
2622 printer->Print( 3081 printer->Print(
2623 "/**\n" 3082 "/**\n"
2624 " * @enum {number}\n" 3083 " * @enum {number}\n"
2625 " */\n" 3084 " */\n"
(...skipping 23 matching lines...) Expand all
2649 GetPath(options, field->file())); 3108 GetPath(options, field->file()));
2650 3109
2651 printer->Print( 3110 printer->Print(
2652 "\n" 3111 "\n"
2653 "/**\n" 3112 "/**\n"
2654 " * A tuple of {field number, class constructor} for the extension\n" 3113 " * A tuple of {field number, class constructor} for the extension\n"
2655 " * field named `$name$`.\n" 3114 " * field named `$name$`.\n"
2656 " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n" 3115 " * @type {!jspb.ExtensionFieldInfo.<$extensionType$>}\n"
2657 " */\n" 3116 " */\n"
2658 "$class$.$name$ = new jspb.ExtensionFieldInfo(\n", 3117 "$class$.$name$ = new jspb.ExtensionFieldInfo(\n",
2659 "name", JSObjectFieldName(field), 3118 "name", JSObjectFieldName(options, field),
2660 "class", extension_scope, 3119 "class", extension_scope,
2661 "extensionType", JSFieldTypeAnnotation( 3120 "extensionType", JSFieldTypeAnnotation(
2662 options, field, 3121 options, field,
2663 /* force_optional = */ false, 3122 /* is_setter_argument = */ false,
2664 /* force_present = */ true, 3123 /* force_present = */ true,
2665 /* singular_if_not_packed = */ false)); 3124 /* singular_if_not_packed = */ false));
2666 printer->Print( 3125 printer->Print(
2667 " $index$,\n" 3126 " $index$,\n"
2668 " {$name$: 0},\n" 3127 " {$name$: 0},\n"
2669 " $ctor$,\n" 3128 " $ctor$,\n"
2670 " /** @type {?function((boolean|undefined),!jspb.Message=): " 3129 " /** @type {?function((boolean|undefined),!jspb.Message=): "
2671 "!Object} */ (\n" 3130 "!Object} */ (\n"
2672 " $toObject$),\n" 3131 " $toObject$),\n"
2673 " $repeated$", 3132 " $repeated$);\n",
2674 "index", SimpleItoa(field->number()), 3133 "index", SimpleItoa(field->number()),
2675 "name", JSObjectFieldName(field), 3134 "name", JSObjectFieldName(options, field),
2676 "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? 3135 "ctor", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2677 SubmessageTypeRef(options, field) : string("null")), 3136 SubmessageTypeRef(options, field) : string("null")),
2678 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ? 3137 "toObject", (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE ?
2679 (SubmessageTypeRef(options, field) + ".toObject") : 3138 (SubmessageTypeRef(options, field) + ".toObject") :
2680 string("null")), 3139 string("null")),
2681 "repeated", (field->is_repeated() ? "1" : "0")); 3140 "repeated", (field->is_repeated() ? "1" : "0"));
2682 3141
2683 if (options.binary) { 3142 printer->Print(
2684 printer->Print( 3143 "\n"
2685 ",\n" 3144 "$extendName$Binary[$index$] = new jspb.ExtensionFieldBinaryInfo(\n"
2686 " jspb.BinaryReader.prototype.$binaryReaderFn$,\n" 3145 " $class$.$name$,\n"
2687 " jspb.BinaryWriter.prototype.$binaryWriterFn$,\n" 3146 " $binaryReaderFn$,\n"
2688 " $binaryMessageSerializeFn$,\n" 3147 " $binaryWriterFn$,\n"
2689 " $binaryMessageDeserializeFn$,\n" 3148 " $binaryMessageSerializeFn$,\n"
2690 " $isPacked$);\n", 3149 " $binaryMessageDeserializeFn$,\n",
2691 "binaryReaderFn", JSBinaryReaderMethodName(field), 3150 "extendName",
2692 "binaryWriterFn", JSBinaryWriterMethodName(field), 3151 JSExtensionsObjectName(options, field->file(), field->containing_type()),
2693 "binaryMessageSerializeFn", 3152 "index", SimpleItoa(field->number()), "class", extension_scope, "name",
2694 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? 3153 JSObjectFieldName(options, field), "binaryReaderFn",
2695 (SubmessageTypeRef(options, field) + 3154 JSBinaryReaderMethodName(options, field), "binaryWriterFn",
2696 ".serializeBinaryToWriter") : "null", 3155 JSBinaryWriterMethodName(options, field), "binaryMessageSerializeFn",
2697 "binaryMessageDeserializeFn", 3156 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
2698 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE) ? 3157 ? (SubmessageTypeRef(options, field) + ".serializeBinaryToWriter")
2699 (SubmessageTypeRef(options, field) + 3158 : "undefined",
2700 ".deserializeBinaryFromReader") : "null", 3159 "binaryMessageDeserializeFn",
2701 "isPacked", (field->is_packed() ? "true" : "false")); 3160 (field->cpp_type() == FieldDescriptor::CPPTYPE_MESSAGE)
2702 } else { 3161 ? (SubmessageTypeRef(options, field) + ".deserializeBinaryFromReader")
2703 printer->Print(");\n"); 3162 : "undefined");
2704 } 3163
3164 printer->Print(" $isPacked$);\n", "isPacked",
3165 (field->is_packed() ? "true" : "false"));
2705 3166
2706 printer->Print( 3167 printer->Print(
2707 "// This registers the extension field with the extended class, so that\n" 3168 "// This registers the extension field with the extended class, so that\n"
2708 "// toObject() will function correctly.\n" 3169 "// toObject() will function correctly.\n"
2709 "$extendName$[$index$] = $class$.$name$;\n" 3170 "$extendName$[$index$] = $class$.$name$;\n"
2710 "\n", 3171 "\n",
2711 "extendName", JSExtensionsObjectName(options, field->file(), 3172 "extendName", JSExtensionsObjectName(options, field->file(),
2712 field->containing_type()), 3173 field->containing_type()),
2713 "index", SimpleItoa(field->number()), 3174 "index", SimpleItoa(field->number()),
2714 "class", extension_scope, 3175 "class", extension_scope,
2715 "name", JSObjectFieldName(field)); 3176 "name", JSObjectFieldName(options, field));
2716 } 3177 }
2717 3178
2718 bool GeneratorOptions::ParseFromOptions( 3179 bool GeneratorOptions::ParseFromOptions(
2719 const vector< pair< string, string > >& options, 3180 const std::vector< std::pair< string, string > >& options,
2720 string* error) { 3181 string* error) {
2721 for (int i = 0; i < options.size(); i++) { 3182 for (int i = 0; i < options.size(); i++) {
2722 if (options[i].first == "add_require_for_enums") { 3183 if (options[i].first == "add_require_for_enums") {
2723 if (options[i].second != "") { 3184 if (options[i].second != "") {
2724 *error = "Unexpected option value for add_require_for_enums"; 3185 *error = "Unexpected option value for add_require_for_enums";
2725 return false; 3186 return false;
2726 } 3187 }
2727 add_require_for_enums = true; 3188 add_require_for_enums = true;
2728 } else if (options[i].first == "binary") { 3189 } else if (options[i].first == "binary") {
2729 if (options[i].second != "") { 3190 if (options[i].second != "") {
(...skipping 14 matching lines...) Expand all
2744 } 3205 }
2745 error_on_name_conflict = true; 3206 error_on_name_conflict = true;
2746 } else if (options[i].first == "output_dir") { 3207 } else if (options[i].first == "output_dir") {
2747 output_dir = options[i].second; 3208 output_dir = options[i].second;
2748 } else if (options[i].first == "namespace_prefix") { 3209 } else if (options[i].first == "namespace_prefix") {
2749 namespace_prefix = options[i].second; 3210 namespace_prefix = options[i].second;
2750 } else if (options[i].first == "library") { 3211 } else if (options[i].first == "library") {
2751 library = options[i].second; 3212 library = options[i].second;
2752 } else if (options[i].first == "import_style") { 3213 } else if (options[i].first == "import_style") {
2753 if (options[i].second == "closure") { 3214 if (options[i].second == "closure") {
2754 import_style = IMPORT_CLOSURE; 3215 import_style = kImportClosure;
2755 } else if (options[i].second == "commonjs") { 3216 } else if (options[i].second == "commonjs") {
2756 import_style = IMPORT_COMMONJS; 3217 import_style = kImportCommonJs;
2757 } else if (options[i].second == "browser") { 3218 } else if (options[i].second == "browser") {
2758 import_style = IMPORT_BROWSER; 3219 import_style = kImportBrowser;
2759 } else if (options[i].second == "es6") { 3220 } else if (options[i].second == "es6") {
2760 import_style = IMPORT_ES6; 3221 import_style = kImportEs6;
2761 } else { 3222 } else {
2762 *error = "Unknown import style " + options[i].second + ", expected " + 3223 *error = "Unknown import style " + options[i].second + ", expected " +
2763 "one of: closure, commonjs, browser, es6."; 3224 "one of: closure, commonjs, browser, es6.";
2764 } 3225 }
3226 } else if (options[i].first == "extension") {
3227 extension = options[i].second;
3228 } else if (options[i].first == "one_output_file_per_input_file") {
3229 if (!options[i].second.empty()) {
3230 *error = "Unexpected option value for one_output_file_per_input_file";
3231 return false;
3232 }
3233 one_output_file_per_input_file = true;
2765 } else { 3234 } else {
2766 // Assume any other option is an output directory, as long as it is a bare 3235 // Assume any other option is an output directory, as long as it is a bare
2767 // `key` rather than a `key=value` option. 3236 // `key` rather than a `key=value` option.
2768 if (options[i].second != "") { 3237 if (options[i].second != "") {
2769 *error = "Unknown option: " + options[i].first; 3238 *error = "Unknown option: " + options[i].first;
2770 return false; 3239 return false;
2771 } 3240 }
2772 output_dir = options[i].first; 3241 output_dir = options[i].first;
2773 } 3242 }
2774 } 3243 }
2775 3244
2776 if (!library.empty() && import_style != IMPORT_CLOSURE) { 3245 if (import_style != kImportClosure &&
2777 *error = "The library option should only be used for " 3246 (add_require_for_enums || testonly || !library.empty() ||
2778 "import_style=closure"; 3247 error_on_name_conflict || extension != ".js" ||
3248 one_output_file_per_input_file)) {
3249 *error =
3250 "The add_require_for_enums, testonly, library, error_on_name_conflict, "
3251 "extension, and one_output_file_per_input_file options should only be "
3252 "used for import_style=closure";
3253 return false;
2779 } 3254 }
2780 3255
2781 return true; 3256 return true;
2782 } 3257 }
2783 3258
3259 GeneratorOptions::OutputMode GeneratorOptions::output_mode() const {
3260 // We use one output file per input file if we are not using Closure or if
3261 // this is explicitly requested.
3262 if (import_style != kImportClosure || one_output_file_per_input_file) {
3263 return kOneOutputFilePerInputFile;
3264 }
3265
3266 // If a library name is provided, we put everything in that one file.
3267 if (!library.empty()) {
3268 return kEverythingInOneFile;
3269 }
3270
3271 // Otherwise, we create one output file per type.
3272 return kOneOutputFilePerType;
3273 }
3274
2784 void Generator::GenerateFilesInDepOrder( 3275 void Generator::GenerateFilesInDepOrder(
2785 const GeneratorOptions& options, 3276 const GeneratorOptions& options,
2786 io::Printer* printer, 3277 io::Printer* printer,
2787 const vector<const FileDescriptor*>& files) const { 3278 const std::vector<const FileDescriptor*>& files) const {
2788 // Build a std::set over all files so that the DFS can detect when it recurses 3279 // 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. 3280 // into a dep not specified in the user's command line.
2790 std::set<const FileDescriptor*> all_files(files.begin(), files.end()); 3281 std::set<const FileDescriptor*> all_files(files.begin(), files.end());
2791 // Track the in-progress set of files that have been generated already. 3282 // Track the in-progress set of files that have been generated already.
2792 std::set<const FileDescriptor*> generated; 3283 std::set<const FileDescriptor*> generated;
2793 for (int i = 0; i < files.size(); i++) { 3284 for (int i = 0; i < files.size(); i++) {
2794 GenerateFileAndDeps(options, printer, files[i], &all_files, &generated); 3285 GenerateFileAndDeps(options, printer, files[i], &all_files, &generated);
2795 } 3286 }
2796 } 3287 }
2797 3288
(...skipping 22 matching lines...) Expand all
2820 GenerateClassesAndEnums(options, printer, root); 3311 GenerateClassesAndEnums(options, printer, root);
2821 } 3312 }
2822 } 3313 }
2823 3314
2824 void Generator::GenerateFile(const GeneratorOptions& options, 3315 void Generator::GenerateFile(const GeneratorOptions& options,
2825 io::Printer* printer, 3316 io::Printer* printer,
2826 const FileDescriptor* file) const { 3317 const FileDescriptor* file) const {
2827 GenerateHeader(options, printer); 3318 GenerateHeader(options, printer);
2828 3319
2829 // Generate "require" statements. 3320 // Generate "require" statements.
2830 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { 3321 if (options.import_style == GeneratorOptions::kImportCommonJs) {
2831 printer->Print("var jspb = require('google-protobuf');\n"); 3322 printer->Print("var jspb = require('google-protobuf');\n");
2832 printer->Print("var goog = jspb;\n"); 3323 printer->Print("var goog = jspb;\n");
2833 printer->Print("var global = Function('return this')();\n\n"); 3324 printer->Print("var global = Function('return this')();\n\n");
2834 3325
2835 for (int i = 0; i < file->dependency_count(); i++) { 3326 for (int i = 0; i < file->dependency_count(); i++) {
2836 const string& name = file->dependency(i)->name(); 3327 const string& name = file->dependency(i)->name();
2837 printer->Print( 3328 printer->Print(
2838 "var $alias$ = require('$file$');\n", 3329 "var $alias$ = require('$file$');\n",
2839 "alias", ModuleAlias(name), 3330 "alias", ModuleAlias(name),
2840 "file", GetRootPath(file->name()) + GetJSFilename(name)); 3331 "file", GetRootPath(file->name(), name) + GetJSFilename(options, name) );
2841 } 3332 }
2842 } 3333 }
2843 3334
2844 // We aren't using Closure's import system, but we use goog.exportSymbol() 3335 std::set<string> provided;
2845 // to construct the expected tree of objects, eg. 3336 std::set<const FieldDescriptor*> extensions;
2846 //
2847 // goog.exportSymbol('foo.bar.Baz', null, this);
2848 //
2849 // // Later generated code expects foo.bar = {} to exist:
2850 // foo.bar.Baz = function() { /* ... */ }
2851 set<string> provided;
2852
2853 // Cover the case where this file declares extensions but no messages.
2854 // This will ensure that the file-level object will be declared to hold
2855 // the extensions.
2856 for (int i = 0; i < file->extension_count(); i++) { 3337 for (int i = 0; i < file->extension_count(); i++) {
2857 provided.insert(file->extension(i)->full_name()); 3338 // We honor the jspb::ignore option here only when working with
3339 // Closure-style imports. Use of this option is discouraged and so we want
3340 // to avoid adding new support for it.
3341 if (options.import_style == GeneratorOptions::kImportClosure &&
3342 IgnoreField(file->extension(i))) {
3343 continue;
3344 }
3345 provided.insert(GetPath(options, file) + "." +
3346 JSObjectFieldName(options, file->extension(i)));
3347 extensions.insert(file->extension(i));
2858 } 3348 }
2859 3349
2860 FindProvidesForFile(options, printer, file, &provided); 3350 FindProvidesForFile(options, printer, file, &provided);
2861 for (std::set<string>::iterator it = provided.begin(); 3351 GenerateProvides(options, printer, &provided);
2862 it != provided.end(); ++it) { 3352 std::vector<const FileDescriptor*> files;
2863 printer->Print("goog.exportSymbol('$name$', null, global);\n", 3353 files.push_back(file);
2864 "name", *it); 3354 if (options.import_style == GeneratorOptions::kImportClosure) {
3355 GenerateRequiresForLibrary(options, printer, files, &provided);
2865 } 3356 }
2866 3357
2867 GenerateClassesAndEnums(options, printer, file); 3358 GenerateClassesAndEnums(options, printer, file);
2868 3359
2869 // Extensions nested inside messages are emitted inside 3360 // Generate code for top-level extensions. Extensions nested inside messages
2870 // GenerateClassesAndEnums(). 3361 // are emitted inside GenerateClassesAndEnums().
2871 for (int i = 0; i < file->extension_count(); i++) { 3362 for (std::set<const FieldDescriptor*>::const_iterator it = extensions.begin();
2872 GenerateExtension(options, printer, file->extension(i)); 3363 it != extensions.end(); ++it) {
3364 GenerateExtension(options, printer, *it);
2873 } 3365 }
2874 3366
2875 if (options.import_style == GeneratorOptions::IMPORT_COMMONJS) { 3367 if (options.import_style == GeneratorOptions::kImportCommonJs) {
2876 printer->Print("goog.object.extend(exports, $package$);\n", 3368 printer->Print("goog.object.extend(exports, $package$);\n",
2877 "package", GetPath(options, file)); 3369 "package", GetPath(options, file));
2878 } 3370 }
3371
3372 // Emit well-known type methods.
3373 for (FileToc* toc = well_known_types_js; toc->name != NULL; toc++) {
3374 string name = string("google/protobuf/") + toc->name;
3375 if (name == StripProto(file->name()) + ".js") {
3376 printer->Print(toc->data);
3377 }
3378 }
2879 } 3379 }
2880 3380
2881 bool Generator::GenerateAll(const vector<const FileDescriptor*>& files, 3381 bool Generator::GenerateAll(const std::vector<const FileDescriptor*>& files,
2882 const string& parameter, 3382 const string& parameter,
2883 GeneratorContext* context, 3383 GeneratorContext* context,
2884 string* error) const { 3384 string* error) const {
2885 vector< pair< string, string > > option_pairs; 3385 std::vector< std::pair< string, string > > option_pairs;
2886 ParseGeneratorParameter(parameter, &option_pairs); 3386 ParseGeneratorParameter(parameter, &option_pairs);
2887 GeneratorOptions options; 3387 GeneratorOptions options;
2888 if (!options.ParseFromOptions(option_pairs, error)) { 3388 if (!options.ParseFromOptions(option_pairs, error)) {
2889 return false; 3389 return false;
2890 } 3390 }
2891 3391
2892 3392
2893 // There are three schemes for where output files go: 3393 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. 3394 // All output should go in a single file.
2901 string filename = options.output_dir + "/" + options.library + ".js"; 3395 string filename = options.output_dir + "/" + options.library +
3396 options.GetFileNameExtension();
2902 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open( filename)); 3397 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Open( filename));
2903 GOOGLE_CHECK(output.get()); 3398 GOOGLE_CHECK(output.get());
2904 io::Printer printer(output.get(), '$'); 3399 io::Printer printer(output.get(), '$');
2905 3400
2906 // Pull out all extensions -- we need these to generate all 3401 // Pull out all extensions -- we need these to generate all
2907 // provides/requires. 3402 // provides/requires.
2908 vector<const FieldDescriptor*> extensions; 3403 std::vector<const FieldDescriptor*> extensions;
2909 for (int i = 0; i < files.size(); i++) { 3404 for (int i = 0; i < files.size(); i++) {
2910 for (int j = 0; j < files[i]->extension_count(); j++) { 3405 for (int j = 0; j < files[i]->extension_count(); j++) {
2911 const FieldDescriptor* extension = files[i]->extension(j); 3406 const FieldDescriptor* extension = files[i]->extension(j);
2912 extensions.push_back(extension); 3407 extensions.push_back(extension);
2913 } 3408 }
2914 } 3409 }
2915 3410
2916 GenerateHeader(options, &printer); 3411 GenerateHeader(options, &printer);
2917 3412
2918 std::set<string> provided; 3413 std::set<string> provided;
2919 FindProvides(options, &printer, files, &provided); 3414 FindProvides(options, &printer, files, &provided);
2920 FindProvidesForFields(options, &printer, extensions, &provided); 3415 FindProvidesForFields(options, &printer, extensions, &provided);
2921 GenerateProvides(options, &printer, &provided); 3416 GenerateProvides(options, &printer, &provided);
2922 GenerateTestOnly(options, &printer); 3417 GenerateTestOnly(options, &printer);
2923 GenerateRequiresForLibrary(options, &printer, files, &provided); 3418 GenerateRequiresForLibrary(options, &printer, files, &provided);
2924 3419
2925 GenerateFilesInDepOrder(options, &printer, files); 3420 GenerateFilesInDepOrder(options, &printer, files);
2926 3421
2927 for (int i = 0; i < extensions.size(); i++) { 3422 for (int i = 0; i < extensions.size(); i++) {
2928 if (ShouldGenerateExtension(extensions[i])) { 3423 if (ShouldGenerateExtension(extensions[i])) {
2929 GenerateExtension(options, &printer, extensions[i]); 3424 GenerateExtension(options, &printer, extensions[i]);
2930 } 3425 }
2931 } 3426 }
2932 3427
2933 if (printer.failed()) { 3428 if (printer.failed()) {
2934 return false; 3429 return false;
2935 } 3430 }
2936 } else if (options.import_style == GeneratorOptions::IMPORT_CLOSURE) { 3431 } else if (options.output_mode() == GeneratorOptions::kOneOutputFilePerType) {
2937 set<const void*> allowed_set; 3432 std::set<const void*> allowed_set;
2938 if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) { 3433 if (!GenerateJspbAllowedSet(options, files, &allowed_set, error)) {
2939 return false; 3434 return false;
2940 } 3435 }
2941 3436
2942 for (int i = 0; i < files.size(); i++) { 3437 for (int i = 0; i < files.size(); i++) {
2943 const FileDescriptor* file = files[i]; 3438 const FileDescriptor* file = files[i];
2944 for (int j = 0; j < file->message_type_count(); j++) { 3439 for (int j = 0; j < file->message_type_count(); j++) {
2945 const Descriptor* desc = file->message_type(j); 3440 const Descriptor* desc = file->message_type(j);
2946 if (allowed_set.count(desc) == 0) { 3441 if (allowed_set.count(desc) == 0) {
2947 continue; 3442 continue;
(...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after
2998 string filename = GetExtensionFileName(options, file); 3493 string filename = GetExtensionFileName(options, file);
2999 3494
3000 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output( 3495 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(
3001 context->Open(filename)); 3496 context->Open(filename));
3002 GOOGLE_CHECK(output.get()); 3497 GOOGLE_CHECK(output.get());
3003 io::Printer printer(output.get(), '$'); 3498 io::Printer printer(output.get(), '$');
3004 3499
3005 GenerateHeader(options, &printer); 3500 GenerateHeader(options, &printer);
3006 3501
3007 std::set<string> provided; 3502 std::set<string> provided;
3008 vector<const FieldDescriptor*> fields; 3503 std::vector<const FieldDescriptor*> fields;
3009 3504
3010 for (int j = 0; j < files[i]->extension_count(); j++) { 3505 for (int j = 0; j < files[i]->extension_count(); j++) {
3011 if (ShouldGenerateExtension(files[i]->extension(j))) { 3506 if (ShouldGenerateExtension(files[i]->extension(j))) {
3012 fields.push_back(files[i]->extension(j)); 3507 fields.push_back(files[i]->extension(j));
3013 } 3508 }
3014 } 3509 }
3015 3510
3016 FindProvidesForFields(options, &printer, fields, &provided); 3511 FindProvidesForFields(options, &printer, fields, &provided);
3017 GenerateProvides(options, &printer, &provided); 3512 GenerateProvides(options, &printer, &provided);
3018 GenerateTestOnly(options, &printer); 3513 GenerateTestOnly(options, &printer);
3019 GenerateRequiresForExtensions(options, &printer, fields, &provided); 3514 GenerateRequiresForExtensions(options, &printer, fields, &provided);
3020 3515
3021 for (int j = 0; j < files[i]->extension_count(); j++) { 3516 for (int j = 0; j < files[i]->extension_count(); j++) {
3022 if (ShouldGenerateExtension(files[i]->extension(j))) { 3517 if (ShouldGenerateExtension(files[i]->extension(j))) {
3023 GenerateExtension(options, &printer, files[i]->extension(j)); 3518 GenerateExtension(options, &printer, files[i]->extension(j));
3024 } 3519 }
3025 } 3520 }
3026 } 3521 }
3027 } 3522 }
3028 } else { 3523 } else /* options.output_mode() == kOneOutputFilePerInputFile */ {
3029 // Generate one output file per input (.proto) file. 3524 // Generate one output file per input (.proto) file.
3030 3525
3031 for (int i = 0; i < files.size(); i++) { 3526 for (int i = 0; i < files.size(); i++) {
3032 const google::protobuf::FileDescriptor* file = files[i]; 3527 const google::protobuf::FileDescriptor* file = files[i];
3033 3528
3034 string filename = options.output_dir + "/" + GetJSFilename(file->name()); 3529 string filename =
3530 options.output_dir + "/" + GetJSFilename(options, file->name());
3035 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Ope n(filename)); 3531 google::protobuf::scoped_ptr<io::ZeroCopyOutputStream> output(context->Ope n(filename));
3036 GOOGLE_CHECK(output.get()); 3532 GOOGLE_CHECK(output.get());
3037 io::Printer printer(output.get(), '$'); 3533 io::Printer printer(output.get(), '$');
3038 3534
3039 GenerateFile(options, &printer, file); 3535 GenerateFile(options, &printer, file);
3040 3536
3041 if (printer.failed()) { 3537 if (printer.failed()) {
3042 return false; 3538 return false;
3043 } 3539 }
3044 } 3540 }
3045 } 3541 }
3046 3542
3047 return true; 3543 return true;
3048 } 3544 }
3049 3545
3050 } // namespace js 3546 } // namespace js
3051 } // namespace compiler 3547 } // namespace compiler
3052 } // namespace protobuf 3548 } // namespace protobuf
3053 } // namespace google 3549 } // namespace google
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698