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

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

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

Powered by Google App Engine
This is Rietveld 408576698