OLD | NEW |
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 16 matching lines...) Expand all Loading... |
27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 28 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 29 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
30 | 30 |
31 /** | 31 /** |
32 * @fileoverview Definition of jspb.Message. | 32 * @fileoverview Definition of jspb.Message. |
33 * | 33 * |
34 * @author mwr@google.com (Mark Rawling) | 34 * @author mwr@google.com (Mark Rawling) |
35 */ | 35 */ |
36 | 36 |
| 37 goog.provide('jspb.ExtensionFieldBinaryInfo'); |
37 goog.provide('jspb.ExtensionFieldInfo'); | 38 goog.provide('jspb.ExtensionFieldInfo'); |
38 goog.provide('jspb.Message'); | 39 goog.provide('jspb.Message'); |
39 | 40 |
40 goog.require('goog.array'); | 41 goog.require('goog.array'); |
41 goog.require('goog.asserts'); | 42 goog.require('goog.asserts'); |
42 goog.require('goog.crypt.base64'); | 43 goog.require('goog.crypt.base64'); |
43 goog.require('goog.json'); | 44 goog.require('goog.json'); |
| 45 goog.require('jspb.Map'); |
44 | 46 |
45 // Not needed in compilation units that have no protos with xids. | 47 // Not needed in compilation units that have no protos with xids. |
46 goog.forwardDeclare('xid.String'); | 48 goog.forwardDeclare('xid.String'); |
47 | 49 |
48 | 50 |
49 | 51 |
50 /** | 52 /** |
51 * Stores information for a single extension field. | 53 * Stores information for a single extension field. |
52 * | 54 * |
53 * For example, an extension field defined like so: | 55 * For example, an extension field defined like so: |
(...skipping 22 matching lines...) Expand all Loading... |
76 * always provided. binaryReaderFn and binaryWriterFn are references to the | 78 * always provided. binaryReaderFn and binaryWriterFn are references to the |
77 * appropriate methods on BinaryReader/BinaryWriter to read/write the value of | 79 * appropriate methods on BinaryReader/BinaryWriter to read/write the value of |
78 * this extension, and binaryMessageSerializeFn is a reference to the message | 80 * this extension, and binaryMessageSerializeFn is a reference to the message |
79 * class's .serializeBinary method, if available. | 81 * class's .serializeBinary method, if available. |
80 * | 82 * |
81 * @param {number} fieldNumber | 83 * @param {number} fieldNumber |
82 * @param {Object} fieldName This has the extension field name as a property. | 84 * @param {Object} fieldName This has the extension field name as a property. |
83 * @param {?function(new: jspb.Message, Array=)} ctor | 85 * @param {?function(new: jspb.Message, Array=)} ctor |
84 * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn | 86 * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn |
85 * @param {number} isRepeated | 87 * @param {number} isRepeated |
86 * @param {?function(number,?)=} opt_binaryReaderFn | |
87 * @param {?function(number,?)|function(number,?,?,?,?,?)=} opt_binaryWriterFn | |
88 * @param {?function(?,?)=} opt_binaryMessageSerializeFn | |
89 * @param {?function(?,?)=} opt_binaryMessageDeserializeFn | |
90 * @param {?boolean=} opt_isPacked | |
91 * @constructor | 88 * @constructor |
92 * @struct | 89 * @struct |
93 * @template T | 90 * @template T |
94 */ | 91 */ |
95 jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn, | 92 jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn, |
96 isRepeated, opt_binaryReaderFn, opt_binaryWriterFn, | 93 isRepeated) { |
97 opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn, | |
98 opt_isPacked) { | |
99 /** @const */ | 94 /** @const */ |
100 this.fieldIndex = fieldNumber; | 95 this.fieldIndex = fieldNumber; |
101 /** @const */ | 96 /** @const */ |
102 this.fieldName = fieldName; | 97 this.fieldName = fieldName; |
103 /** @const */ | 98 /** @const */ |
104 this.ctor = ctor; | 99 this.ctor = ctor; |
105 /** @const */ | 100 /** @const */ |
106 this.toObjectFn = toObjectFn; | 101 this.toObjectFn = toObjectFn; |
107 /** @const */ | 102 /** @const */ |
108 this.binaryReaderFn = opt_binaryReaderFn; | 103 this.isRepeated = isRepeated; |
| 104 }; |
| 105 |
| 106 /** |
| 107 * Stores binary-related information for a single extension field. |
| 108 * @param {!jspb.ExtensionFieldInfo<T>} fieldInfo |
| 109 * @param {!function(number,?)} binaryReaderFn |
| 110 * @param {!function(number,?)|function(number,?,?,?,?,?)} binaryWriterFn |
| 111 * @param {function(?,?)=} opt_binaryMessageSerializeFn |
| 112 * @param {function(?,?)=} opt_binaryMessageDeserializeFn |
| 113 * @param {boolean=} opt_isPacked |
| 114 * @constructor |
| 115 * @struct |
| 116 * @template T |
| 117 */ |
| 118 jspb.ExtensionFieldBinaryInfo = function(fieldInfo, binaryReaderFn, binaryWriter
Fn, |
| 119 opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn, opt_isPacked)
{ |
109 /** @const */ | 120 /** @const */ |
110 this.binaryWriterFn = opt_binaryWriterFn; | 121 this.fieldInfo = fieldInfo; |
| 122 /** @const */ |
| 123 this.binaryReaderFn = binaryReaderFn; |
| 124 /** @const */ |
| 125 this.binaryWriterFn = binaryWriterFn; |
111 /** @const */ | 126 /** @const */ |
112 this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn; | 127 this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn; |
113 /** @const */ | 128 /** @const */ |
114 this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn; | 129 this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn; |
115 /** @const */ | 130 /** @const */ |
116 this.isRepeated = isRepeated; | |
117 /** @const */ | |
118 this.isPacked = opt_isPacked; | 131 this.isPacked = opt_isPacked; |
119 }; | 132 }; |
120 | 133 |
121 | |
122 /** | 134 /** |
123 * @return {boolean} Does this field represent a sub Message? | 135 * @return {boolean} Does this field represent a sub Message? |
124 */ | 136 */ |
125 jspb.ExtensionFieldInfo.prototype.isMessageType = function() { | 137 jspb.ExtensionFieldInfo.prototype.isMessageType = function() { |
126 return !!this.ctor; | 138 return !!this.ctor; |
127 }; | 139 }; |
128 | 140 |
129 | 141 |
130 /** | 142 /** |
131 * Base class for all JsPb messages. | 143 * Base class for all JsPb messages. |
(...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
364 */ | 376 */ |
365 jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { | 377 jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { |
366 if (msg.array.length) { | 378 if (msg.array.length) { |
367 var foundIndex = msg.array.length - 1; | 379 var foundIndex = msg.array.length - 1; |
368 var obj = msg.array[foundIndex]; | 380 var obj = msg.array[foundIndex]; |
369 // Normal fields are never objects, so we can be sure that if we find an | 381 // Normal fields are never objects, so we can be sure that if we find an |
370 // object here, then it's the extension object. However, we must ensure that | 382 // object here, then it's the extension object. However, we must ensure that |
371 // the object is not an array, since arrays are valid field values. | 383 // the object is not an array, since arrays are valid field values. |
372 // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug | 384 // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug |
373 // in Safari on iOS 8. See the description of CL/86511464 for details. | 385 // in Safari on iOS 8. See the description of CL/86511464 for details. |
374 if (obj && typeof obj == 'object' && !goog.isArray(obj)) { | 386 if (obj && typeof obj == 'object' && !goog.isArray(obj) && |
| 387 !(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) { |
375 msg.pivot_ = foundIndex - msg.arrayIndexOffset_; | 388 msg.pivot_ = foundIndex - msg.arrayIndexOffset_; |
376 msg.extensionObject_ = obj; | 389 msg.extensionObject_ = obj; |
377 return; | 390 return; |
378 } | 391 } |
379 } | 392 } |
380 // This complexity exists because we keep all extension fields in the | 393 // This complexity exists because we keep all extension fields in the |
381 // extensionObject_ regardless of proto field number. Changing this would | 394 // extensionObject_ regardless of proto field number. Changing this would |
382 // simplify the code here, but it would require changing the serialization | 395 // simplify the code here, but it would require changing the serialization |
383 // format from the server, which is not backwards compatible. | 396 // format from the server, which is not backwards compatible. |
384 // TODO(jshneier): Should we just treat extension fields the same as | 397 // TODO(jshneier): Should we just treat extension fields the same as |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
482 * Writes a proto's extension data to a binary-format output stream. | 495 * Writes a proto's extension data to a binary-format output stream. |
483 * @param {!jspb.Message} proto The proto whose extensions to convert. | 496 * @param {!jspb.Message} proto The proto whose extensions to convert. |
484 * @param {*} writer The binary-format writer to write to. | 497 * @param {*} writer The binary-format writer to write to. |
485 * @param {!Object} extensions The proto class' registered extensions. | 498 * @param {!Object} extensions The proto class' registered extensions. |
486 * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto | 499 * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto |
487 * class' getExtension function. Passed for effective dead code removal. | 500 * class' getExtension function. Passed for effective dead code removal. |
488 */ | 501 */ |
489 jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, | 502 jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, |
490 getExtensionFn) { | 503 getExtensionFn) { |
491 for (var fieldNumber in extensions) { | 504 for (var fieldNumber in extensions) { |
492 var fieldInfo = extensions[fieldNumber]; | 505 var binaryFieldInfo = extensions[fieldNumber]; |
| 506 var fieldInfo = binaryFieldInfo.fieldInfo; |
| 507 |
493 // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we | 508 // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we |
494 // need to gracefully error-out here rather than produce a null dereference | 509 // need to gracefully error-out here rather than produce a null dereference |
495 // below. | 510 // below. |
496 if (!fieldInfo.binaryWriterFn) { | 511 if (!binaryFieldInfo.binaryWriterFn) { |
497 throw new Error('Message extension present that was generated ' + | 512 throw new Error('Message extension present that was generated ' + |
498 'without binary serialization support'); | 513 'without binary serialization support'); |
499 } | 514 } |
500 var value = getExtensionFn.call(proto, fieldInfo); | 515 var value = getExtensionFn.call(proto, fieldInfo); |
501 if (value) { | 516 if (value) { |
502 if (fieldInfo.isMessageType()) { | 517 if (fieldInfo.isMessageType()) { |
503 // If the message type of the extension was generated without binary | 518 // If the message type of the extension was generated without binary |
504 // support, there may not be a binary message serializer function, and | 519 // support, there may not be a binary message serializer function, and |
505 // we can't know when we codegen the extending message that the extended | 520 // we can't know when we codegen the extending message that the extended |
506 // message may require binary support, so we can *only* catch this error | 521 // message may require binary support, so we can *only* catch this error |
507 // here, at runtime (and this decoupled codegen is the whole point of | 522 // here, at runtime (and this decoupled codegen is the whole point of |
508 // extensions!). | 523 // extensions!). |
509 if (fieldInfo.binaryMessageSerializeFn) { | 524 if (binaryFieldInfo.binaryMessageSerializeFn) { |
510 fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, | 525 binaryFieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, |
511 value, fieldInfo.binaryMessageSerializeFn); | 526 value, binaryFieldInfo.binaryMessageSerializeFn); |
512 } else { | 527 } else { |
513 throw new Error('Message extension present holding submessage ' + | 528 throw new Error('Message extension present holding submessage ' + |
514 'without binary support enabled, and message is ' + | 529 'without binary support enabled, and message is ' + |
515 'being serialized to binary format'); | 530 'being serialized to binary format'); |
516 } | 531 } |
517 } else { | 532 } else { |
518 fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, value); | 533 binaryFieldInfo.binaryWriterFn.call( |
| 534 writer, fieldInfo.fieldIndex, value); |
519 } | 535 } |
520 } | 536 } |
521 } | 537 } |
522 }; | 538 }; |
523 | 539 |
524 | 540 |
525 /** | 541 /** |
526 * Reads an extension field from the given reader and, if a valid extension, | 542 * Reads an extension field from the given reader and, if a valid extension, |
527 * sets the extension value. | 543 * sets the extension value. |
528 * @param {!jspb.Message} msg A jspb proto. | 544 * @param {!jspb.Message} msg A jspb proto. |
529 * @param {{skipField:function(),getFieldNumber:function():number}} reader | 545 * @param {{skipField:function(),getFieldNumber:function():number}} reader |
530 * @param {!Object} extensions The extensions object. | 546 * @param {!Object} extensions The extensions object. |
531 * @param {function(jspb.ExtensionFieldInfo)} getExtensionFn | 547 * @param {function(jspb.ExtensionFieldInfo)} getExtensionFn |
532 * @param {function(jspb.ExtensionFieldInfo, ?)} setExtensionFn | 548 * @param {function(jspb.ExtensionFieldInfo, ?)} setExtensionFn |
533 */ | 549 */ |
534 jspb.Message.readBinaryExtension = function(msg, reader, extensions, | 550 jspb.Message.readBinaryExtension = function(msg, reader, extensions, |
535 getExtensionFn, setExtensionFn) { | 551 getExtensionFn, setExtensionFn) { |
536 var fieldInfo = extensions[reader.getFieldNumber()]; | 552 var binaryFieldInfo = extensions[reader.getFieldNumber()]; |
537 if (!fieldInfo) { | 553 if (!binaryFieldInfo) { |
538 reader.skipField(); | 554 reader.skipField(); |
539 return; | 555 return; |
540 } | 556 } |
541 if (!fieldInfo.binaryReaderFn) { | 557 var fieldInfo = binaryFieldInfo.fieldInfo; |
| 558 if (!binaryFieldInfo.binaryReaderFn) { |
542 throw new Error('Deserializing extension whose generated code does not ' + | 559 throw new Error('Deserializing extension whose generated code does not ' + |
543 'support binary format'); | 560 'support binary format'); |
544 } | 561 } |
545 | 562 |
546 var value; | 563 var value; |
547 if (fieldInfo.isMessageType()) { | 564 if (fieldInfo.isMessageType()) { |
548 value = new fieldInfo.ctor(); | 565 value = new fieldInfo.ctor(); |
549 fieldInfo.binaryReaderFn.call( | 566 binaryFieldInfo.binaryReaderFn.call( |
550 reader, value, fieldInfo.binaryMessageDeserializeFn); | 567 reader, value, binaryFieldInfo.binaryMessageDeserializeFn); |
551 } else { | 568 } else { |
552 // All other types. | 569 // All other types. |
553 value = fieldInfo.binaryReaderFn.call(reader); | 570 value = binaryFieldInfo.binaryReaderFn.call(reader); |
554 } | 571 } |
555 | 572 |
556 if (fieldInfo.isRepeated && !fieldInfo.isPacked) { | 573 if (fieldInfo.isRepeated && !binaryFieldInfo.isPacked) { |
557 var currentList = getExtensionFn.call(msg, fieldInfo); | 574 var currentList = getExtensionFn.call(msg, fieldInfo); |
558 if (!currentList) { | 575 if (!currentList) { |
559 setExtensionFn.call(msg, fieldInfo, [value]); | 576 setExtensionFn.call(msg, fieldInfo, [value]); |
560 } else { | 577 } else { |
561 currentList.push(value); | 578 currentList.push(value); |
562 } | 579 } |
563 } else { | 580 } else { |
564 setExtensionFn.call(msg, fieldInfo, value); | 581 setExtensionFn.call(msg, fieldInfo, value); |
565 } | 582 } |
566 }; | 583 }; |
(...skipping 153 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
720 * Gets the value of a non-extension primitive field, with proto3 (non-nullable | 737 * Gets the value of a non-extension primitive field, with proto3 (non-nullable |
721 * primitives) semantics. Returns `defaultValue` if the field is not otherwise | 738 * primitives) semantics. Returns `defaultValue` if the field is not otherwise |
722 * set. | 739 * set. |
723 * @template T | 740 * @template T |
724 * @param {!jspb.Message} msg A jspb proto. | 741 * @param {!jspb.Message} msg A jspb proto. |
725 * @param {number} fieldNumber The field number. | 742 * @param {number} fieldNumber The field number. |
726 * @param {T} defaultValue The default value. | 743 * @param {T} defaultValue The default value. |
727 * @return {T} The field's value. | 744 * @return {T} The field's value. |
728 * @protected | 745 * @protected |
729 */ | 746 */ |
730 jspb.Message.getFieldProto3 = function(msg, fieldNumber, defaultValue) { | 747 jspb.Message.getFieldWithDefault = function(msg, fieldNumber, defaultValue) { |
731 var value = jspb.Message.getField(msg, fieldNumber); | 748 var value = jspb.Message.getField(msg, fieldNumber); |
732 if (value == null) { | 749 if (value == null) { |
733 return defaultValue; | 750 return defaultValue; |
734 } else { | 751 } else { |
735 return value; | 752 return value; |
736 } | 753 } |
737 }; | 754 }; |
738 | 755 |
739 | 756 |
740 /** | 757 /** |
| 758 * Alias for getFieldWithDefault used by older generated code. |
| 759 * @template T |
| 760 * @param {!jspb.Message} msg A jspb proto. |
| 761 * @param {number} fieldNumber The field number. |
| 762 * @param {T} defaultValue The default value. |
| 763 * @return {T} The field's value. |
| 764 * @protected |
| 765 */ |
| 766 jspb.Message.getFieldProto3 = jspb.Message.getFieldWithDefault; |
| 767 |
| 768 |
| 769 /** |
| 770 * Gets the value of a map field, lazily creating the map container if |
| 771 * necessary. |
| 772 * |
| 773 * This should only be called from generated code, because it requires knowledge |
| 774 * of serialization/parsing callbacks (which are required by the map at |
| 775 * construction time, and the map may be constructed here). |
| 776 * |
| 777 * @template K, V |
| 778 * @param {!jspb.Message} msg |
| 779 * @param {number} fieldNumber |
| 780 * @param {boolean|undefined} noLazyCreate |
| 781 * @param {?=} opt_valueCtor |
| 782 * @return {!jspb.Map<K, V>|undefined} |
| 783 * @protected |
| 784 */ |
| 785 jspb.Message.getMapField = function(msg, fieldNumber, noLazyCreate, |
| 786 opt_valueCtor) { |
| 787 if (!msg.wrappers_) { |
| 788 msg.wrappers_ = {}; |
| 789 } |
| 790 // If we already have a map in the map wrappers, return that. |
| 791 if (fieldNumber in msg.wrappers_) { |
| 792 return msg.wrappers_[fieldNumber]; |
| 793 } else if (noLazyCreate) { |
| 794 return undefined; |
| 795 } else { |
| 796 // Wrap the underlying elements array with a Map. |
| 797 var arr = jspb.Message.getField(msg, fieldNumber); |
| 798 if (!arr) { |
| 799 arr = []; |
| 800 jspb.Message.setField(msg, fieldNumber, arr); |
| 801 } |
| 802 return msg.wrappers_[fieldNumber] = |
| 803 new jspb.Map( |
| 804 /** @type {!Array<!Array<!Object>>} */ (arr), opt_valueCtor); |
| 805 } |
| 806 }; |
| 807 |
| 808 |
| 809 /** |
741 * Sets the value of a non-extension field. | 810 * Sets the value of a non-extension field. |
742 * @param {!jspb.Message} msg A jspb proto. | 811 * @param {!jspb.Message} msg A jspb proto. |
743 * @param {number} fieldNumber The field number. | 812 * @param {number} fieldNumber The field number. |
744 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value | 813 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value |
745 * @protected | 814 * @protected |
746 */ | 815 */ |
747 jspb.Message.setField = function(msg, fieldNumber, value) { | 816 jspb.Message.setField = function(msg, fieldNumber, value) { |
748 if (fieldNumber < msg.pivot_) { | 817 if (fieldNumber < msg.pivot_) { |
749 msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; | 818 msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; |
750 } else { | 819 } else { |
751 msg.extensionObject_[fieldNumber] = value; | 820 msg.extensionObject_[fieldNumber] = value; |
752 } | 821 } |
753 }; | 822 }; |
754 | 823 |
755 | 824 |
756 /** | 825 /** |
| 826 * Adds a value to a repeated, primitive field. |
| 827 * @param {!jspb.Message} msg A jspb proto. |
| 828 * @param {number} fieldNumber The field number. |
| 829 * @param {string|number|boolean|!Uint8Array} value New value |
| 830 * @param {number=} opt_index Index where to put new value. |
| 831 * @protected |
| 832 */ |
| 833 jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) { |
| 834 var arr = jspb.Message.getField(msg, fieldNumber); |
| 835 if (opt_index != undefined) { |
| 836 arr.splice(opt_index, 0, value); |
| 837 } else { |
| 838 arr.push(value); |
| 839 } |
| 840 }; |
| 841 |
| 842 |
| 843 /** |
757 * Sets the value of a field in a oneof union and clears all other fields in | 844 * Sets the value of a field in a oneof union and clears all other fields in |
758 * the union. | 845 * the union. |
759 * @param {!jspb.Message} msg A jspb proto. | 846 * @param {!jspb.Message} msg A jspb proto. |
760 * @param {number} fieldNumber The field number. | 847 * @param {number} fieldNumber The field number. |
761 * @param {!Array<number>} oneof The fields belonging to the union. | 848 * @param {!Array<number>} oneof The fields belonging to the union. |
762 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value | 849 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value |
763 * @protected | 850 * @protected |
764 */ | 851 */ |
765 jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) { | 852 jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) { |
766 var currentCase = jspb.Message.computeOneofCase(msg, oneof); | 853 var currentCase = jspb.Message.computeOneofCase(msg, oneof); |
(...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
843 | 930 |
844 /** | 931 /** |
845 * Gets and wraps a repeated proto field on access. | 932 * Gets and wraps a repeated proto field on access. |
846 * @param {!jspb.Message} msg A jspb proto. | 933 * @param {!jspb.Message} msg A jspb proto. |
847 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. | 934 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. |
848 * @param {number} fieldNumber The field number. | 935 * @param {number} fieldNumber The field number. |
849 * @return {Array<!jspb.Message>} The repeated field as an array of protos. | 936 * @return {Array<!jspb.Message>} The repeated field as an array of protos. |
850 * @protected | 937 * @protected |
851 */ | 938 */ |
852 jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) { | 939 jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) { |
| 940 jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber); |
| 941 var val = msg.wrappers_[fieldNumber]; |
| 942 if (val == jspb.Message.EMPTY_LIST_SENTINEL_) { |
| 943 val = msg.wrappers_[fieldNumber] = []; |
| 944 } |
| 945 return /** @type {!Array<!jspb.Message>} */ (val); |
| 946 }; |
| 947 |
| 948 |
| 949 /** |
| 950 * Wraps underlying array into proto message representation if it wasn't done |
| 951 * before. |
| 952 * @param {!jspb.Message} msg A jspb proto. |
| 953 * @param {function(new:jspb.Message, ?Array)} ctor Constructor for the field. |
| 954 * @param {number} fieldNumber The field number. |
| 955 * @private |
| 956 */ |
| 957 jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) { |
853 if (!msg.wrappers_) { | 958 if (!msg.wrappers_) { |
854 msg.wrappers_ = {}; | 959 msg.wrappers_ = {}; |
855 } | 960 } |
856 if (!msg.wrappers_[fieldNumber]) { | 961 if (!msg.wrappers_[fieldNumber]) { |
857 var data = jspb.Message.getField(msg, fieldNumber); | 962 var data = jspb.Message.getField(msg, fieldNumber); |
858 for (var wrappers = [], i = 0; i < data.length; i++) { | 963 for (var wrappers = [], i = 0; i < data.length; i++) { |
859 wrappers[i] = new ctor(data[i]); | 964 wrappers[i] = new ctor(data[i]); |
860 } | 965 } |
861 msg.wrappers_[fieldNumber] = wrappers; | 966 msg.wrappers_[fieldNumber] = wrappers; |
862 } | 967 } |
863 var val = msg.wrappers_[fieldNumber]; | |
864 if (val == jspb.Message.EMPTY_LIST_SENTINEL_) { | |
865 val = msg.wrappers_[fieldNumber] = []; | |
866 } | |
867 return /** @type {Array<!jspb.Message>} */ (val); | |
868 }; | 968 }; |
869 | 969 |
870 | 970 |
871 /** | 971 /** |
872 * Sets a proto field and syncs it to the backing array. | 972 * Sets a proto field and syncs it to the backing array. |
873 * @param {!jspb.Message} msg A jspb proto. | 973 * @param {!jspb.Message} msg A jspb proto. |
874 * @param {number} fieldNumber The field number. | 974 * @param {number} fieldNumber The field number. |
875 * @param {jspb.Message|undefined} value A new value for this proto field. | 975 * @param {?jspb.Message|?jspb.Map|undefined} value A new value for this proto |
| 976 * field. |
876 * @protected | 977 * @protected |
877 */ | 978 */ |
878 jspb.Message.setWrapperField = function(msg, fieldNumber, value) { | 979 jspb.Message.setWrapperField = function(msg, fieldNumber, value) { |
879 if (!msg.wrappers_) { | 980 if (!msg.wrappers_) { |
880 msg.wrappers_ = {}; | 981 msg.wrappers_ = {}; |
881 } | 982 } |
882 var data = value ? value.toArray() : value; | 983 var data = value ? value.toArray() : value; |
883 msg.wrappers_[fieldNumber] = value; | 984 msg.wrappers_[fieldNumber] = value; |
884 jspb.Message.setField(msg, fieldNumber, data); | 985 jspb.Message.setField(msg, fieldNumber, data); |
885 }; | 986 }; |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
917 value = value || []; | 1018 value = value || []; |
918 for (var data = [], i = 0; i < value.length; i++) { | 1019 for (var data = [], i = 0; i < value.length; i++) { |
919 data[i] = value[i].toArray(); | 1020 data[i] = value[i].toArray(); |
920 } | 1021 } |
921 msg.wrappers_[fieldNumber] = value; | 1022 msg.wrappers_[fieldNumber] = value; |
922 jspb.Message.setField(msg, fieldNumber, data); | 1023 jspb.Message.setField(msg, fieldNumber, data); |
923 }; | 1024 }; |
924 | 1025 |
925 | 1026 |
926 /** | 1027 /** |
| 1028 * Add a message to a repeated proto field. |
| 1029 * @param {!jspb.Message} msg A jspb proto. |
| 1030 * @param {number} fieldNumber The field number. |
| 1031 * @param {T_CHILD|undefined} value Proto that will be added to the |
| 1032 * repeated field. |
| 1033 * @param {function(new:T_CHILD, ?Array=)} ctor The constructor of the |
| 1034 * message type. |
| 1035 * @param {number|undefined} index Index at which to insert the value. |
| 1036 * @return {T_CHILD_NOT_UNDEFINED} proto that was inserted to the repeated field |
| 1037 * @template MessageType |
| 1038 * Use go/closure-ttl to declare a non-undefined version of T_CHILD. Replace the |
| 1039 * undefined in blah|undefined with none. This is necessary because the compiler |
| 1040 * will infer T_CHILD to be |undefined. |
| 1041 * @template T_CHILD |
| 1042 * @template T_CHILD_NOT_UNDEFINED := |
| 1043 * cond(isUnknown(T_CHILD), unknown(), |
| 1044 * mapunion(T_CHILD, (X) => |
| 1045 * cond(eq(X, 'undefined'), none(), X))) |
| 1046 * =: |
| 1047 * @protected |
| 1048 */ |
| 1049 jspb.Message.addToRepeatedWrapperField = function( |
| 1050 msg, fieldNumber, value, ctor, index) { |
| 1051 jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber); |
| 1052 var wrapperArray = msg.wrappers_[fieldNumber]; |
| 1053 if (!wrapperArray) { |
| 1054 wrapperArray = msg.wrappers_[fieldNumber] = []; |
| 1055 } |
| 1056 var insertedValue = value ? value : new ctor(); |
| 1057 var array = jspb.Message.getField(msg, fieldNumber); |
| 1058 if (index != undefined) { |
| 1059 wrapperArray.splice(index, 0, insertedValue); |
| 1060 array.splice(index, 0, insertedValue.toArray()); |
| 1061 } else { |
| 1062 wrapperArray.push(insertedValue); |
| 1063 array.push(insertedValue.toArray()); |
| 1064 } |
| 1065 return insertedValue; |
| 1066 }; |
| 1067 |
| 1068 |
| 1069 /** |
927 * Converts a JsPb repeated message field into a map. The map will contain | 1070 * Converts a JsPb repeated message field into a map. The map will contain |
928 * protos unless an optional toObject function is given, in which case it will | 1071 * protos unless an optional toObject function is given, in which case it will |
929 * contain objects suitable for Soy rendering. | 1072 * contain objects suitable for Soy rendering. |
930 * @param {!Array<T>} field The repeated message field to be | 1073 * @param {!Array<T>} field The repeated message field to be |
931 * converted. | 1074 * converted. |
932 * @param {function() : string?} mapKeyGetterFn The function to get the key of | 1075 * @param {function() : string?} mapKeyGetterFn The function to get the key of |
933 * the map. | 1076 * the map. |
934 * @param {?function(boolean=): Object| | 1077 * @param {?function(boolean=): Object| |
935 * function((boolean|undefined),T): Object} opt_toObjectFn The | 1078 * function((boolean|undefined),T): Object} opt_toObjectFn The |
936 * toObject function for this field. We need to pass this for effective | 1079 * toObject function for this field. We need to pass this for effective |
937 * dead code removal. | 1080 * dead code removal. |
938 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance | 1081 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance |
939 * for transitional soy proto support: http://goto/soy-param-migration | 1082 * for transitional soy proto support: http://goto/soy-param-migration |
940 * @return {!Object.<string, Object>} A map of proto or Soy objects. | 1083 * @return {!Object.<string, Object>} A map of proto or Soy objects. |
941 * @template T | 1084 * @template T |
942 */ | 1085 */ |
943 jspb.Message.toMap = function( | 1086 jspb.Message.toMap = function( |
944 field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) { | 1087 field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) { |
945 var result = {}; | 1088 var result = {}; |
946 for (var i = 0; i < field.length; i++) { | 1089 for (var i = 0; i < field.length; i++) { |
947 result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ? | 1090 result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ? |
948 opt_toObjectFn.call(field[i], opt_includeInstance, | 1091 opt_toObjectFn.call(field[i], opt_includeInstance, |
949 /** @type {!jspb.Message} */ (field[i])) : field[i]; | 1092 /** @type {!jspb.Message} */ (field[i])) : field[i]; |
950 } | 1093 } |
951 return result; | 1094 return result; |
952 }; | 1095 }; |
953 | 1096 |
954 | 1097 |
955 /** | 1098 /** |
| 1099 * Syncs all map fields' contents back to their underlying arrays. |
| 1100 * @private |
| 1101 */ |
| 1102 jspb.Message.prototype.syncMapFields_ = function() { |
| 1103 // This iterates over submessage, map, and repeated fields, which is intended. |
| 1104 // Submessages can contain maps which also need to be synced. |
| 1105 // |
| 1106 // There is a lot of opportunity for optimization here. For example we could |
| 1107 // statically determine that some messages have no submessages with maps and |
| 1108 // optimize this method away for those just by generating one extra static |
| 1109 // boolean per message type. |
| 1110 if (this.wrappers_) { |
| 1111 for (var fieldNumber in this.wrappers_) { |
| 1112 var val = this.wrappers_[fieldNumber]; |
| 1113 if (goog.isArray(val)) { |
| 1114 for (var i = 0; i < val.length; i++) { |
| 1115 if (val[i]) { |
| 1116 val[i].toArray(); |
| 1117 } |
| 1118 } |
| 1119 } else { |
| 1120 // Works for submessages and maps. |
| 1121 if (val) { |
| 1122 val.toArray(); |
| 1123 } |
| 1124 } |
| 1125 } |
| 1126 } |
| 1127 }; |
| 1128 |
| 1129 |
| 1130 /** |
956 * Returns the internal array of this proto. | 1131 * Returns the internal array of this proto. |
957 * <p>Note: If you use this array to construct a second proto, the content | 1132 * <p>Note: If you use this array to construct a second proto, the content |
958 * would then be partially shared between the two protos. | 1133 * would then be partially shared between the two protos. |
959 * @return {!Array} The proto represented as an array. | 1134 * @return {!Array} The proto represented as an array. |
960 */ | 1135 */ |
961 jspb.Message.prototype.toArray = function() { | 1136 jspb.Message.prototype.toArray = function() { |
| 1137 this.syncMapFields_(); |
962 return this.array; | 1138 return this.array; |
963 }; | 1139 }; |
964 | 1140 |
965 | 1141 |
966 | 1142 |
967 | 1143 |
968 /** | 1144 /** |
969 * Creates a string representation of the internal data array of this proto. | 1145 * Creates a string representation of the internal data array of this proto. |
970 * <p>NOTE: This string is *not* suitable for use in server requests. | 1146 * <p>NOTE: This string is *not* suitable for use in server requests. |
971 * @return {string} A string representation of this proto. | 1147 * @return {string} A string representation of this proto. |
972 * @override | 1148 * @override |
973 */ | 1149 */ |
974 jspb.Message.prototype.toString = function() { | 1150 jspb.Message.prototype.toString = function() { |
| 1151 this.syncMapFields_(); |
975 return this.array.toString(); | 1152 return this.array.toString(); |
976 }; | 1153 }; |
977 | 1154 |
978 | 1155 |
979 /** | 1156 /** |
980 * Gets the value of the extension field from the extended object. | 1157 * Gets the value of the extension field from the extended object. |
981 * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get. | 1158 * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get. |
982 * @return {T} The value of the field. | 1159 * @return {T} The value of the field. |
983 * @template T | 1160 * @template T |
984 */ | 1161 */ |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1016 } | 1193 } |
1017 } | 1194 } |
1018 }; | 1195 }; |
1019 | 1196 |
1020 | 1197 |
1021 /** | 1198 /** |
1022 * Sets the value of the extension field in the extended object. | 1199 * Sets the value of the extension field in the extended object. |
1023 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. | 1200 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. |
1024 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value | 1201 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value |
1025 * to set. | 1202 * to set. |
| 1203 * @return {THIS} For chaining |
| 1204 * @this {THIS} |
| 1205 * @template THIS |
1026 */ | 1206 */ |
1027 jspb.Message.prototype.setExtension = function(fieldInfo, value) { | 1207 jspb.Message.prototype.setExtension = function(fieldInfo, value) { |
1028 if (!this.wrappers_) { | 1208 // Cast self, since the inferred THIS is unknown inside the function body. |
1029 this.wrappers_ = {}; | 1209 // https://github.com/google/closure-compiler/issues/1411#issuecomment-2324422
20 |
| 1210 var self = /** @type {!jspb.Message} */ (this); |
| 1211 if (!self.wrappers_) { |
| 1212 self.wrappers_ = {}; |
1030 } | 1213 } |
1031 jspb.Message.maybeInitEmptyExtensionObject_(this); | 1214 jspb.Message.maybeInitEmptyExtensionObject_(self); |
1032 var fieldNumber = fieldInfo.fieldIndex; | 1215 var fieldNumber = fieldInfo.fieldIndex; |
1033 if (fieldInfo.isRepeated) { | 1216 if (fieldInfo.isRepeated) { |
1034 value = value || []; | 1217 value = value || []; |
1035 if (fieldInfo.isMessageType()) { | 1218 if (fieldInfo.isMessageType()) { |
1036 this.wrappers_[fieldNumber] = value; | 1219 self.wrappers_[fieldNumber] = value; |
1037 this.extensionObject_[fieldNumber] = goog.array.map( | 1220 self.extensionObject_[fieldNumber] = goog.array.map( |
1038 /** @type {Array<jspb.Message>} */ (value), function(msg) { | 1221 /** @type {Array<jspb.Message>} */ (value), function(msg) { |
1039 return msg.toArray(); | 1222 return msg.toArray(); |
1040 }); | 1223 }); |
1041 } else { | 1224 } else { |
1042 this.extensionObject_[fieldNumber] = value; | 1225 self.extensionObject_[fieldNumber] = value; |
1043 } | 1226 } |
1044 } else { | 1227 } else { |
1045 if (fieldInfo.isMessageType()) { | 1228 if (fieldInfo.isMessageType()) { |
1046 this.wrappers_[fieldNumber] = value; | 1229 self.wrappers_[fieldNumber] = value; |
1047 this.extensionObject_[fieldNumber] = value ? value.toArray() : value; | 1230 self.extensionObject_[fieldNumber] = value ? value.toArray() : value; |
1048 } else { | 1231 } else { |
1049 this.extensionObject_[fieldNumber] = value; | 1232 self.extensionObject_[fieldNumber] = value; |
1050 } | 1233 } |
1051 } | 1234 } |
| 1235 return self; |
1052 }; | 1236 }; |
1053 | 1237 |
1054 | 1238 |
1055 /** | 1239 /** |
1056 * Creates a difference object between two messages. | 1240 * Creates a difference object between two messages. |
1057 * | 1241 * |
1058 * The result will contain the top-level fields of m2 that differ from those of | 1242 * The result will contain the top-level fields of m2 that differ from those of |
1059 * m1 at any level of nesting. No data is cloned, the result object will | 1243 * m1 at any level of nesting. No data is cloned, the result object will |
1060 * share its top-level elements with m2 (but not with m1). | 1244 * share its top-level elements with m2 (but not with m1). |
1061 * | 1245 * |
(...skipping 148 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1210 // extensions. | 1394 // extensions. |
1211 if (field1.constructor === Object) { | 1395 if (field1.constructor === Object) { |
1212 return jspb.Message.compareExtensions(field1, field2); | 1396 return jspb.Message.compareExtensions(field1, field2); |
1213 } | 1397 } |
1214 | 1398 |
1215 throw new Error('Invalid type in JSPB array'); | 1399 throw new Error('Invalid type in JSPB array'); |
1216 }; | 1400 }; |
1217 | 1401 |
1218 | 1402 |
1219 /** | 1403 /** |
1220 * Static clone function. NOTE: A type-safe method called "cloneMessage" exists | 1404 * Templated, type-safe cloneMessage definition. |
| 1405 * @return {THIS} |
| 1406 * @this {THIS} |
| 1407 * @template THIS |
| 1408 */ |
| 1409 jspb.Message.prototype.cloneMessage = function() { |
| 1410 return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this)); |
| 1411 }; |
| 1412 |
| 1413 /** |
| 1414 * Alias clone to cloneMessage. goog.object.unsafeClone uses clone to |
| 1415 * efficiently copy objects. Without this alias, copying jspb messages comes |
| 1416 * with a large performance penalty. |
| 1417 * @return {THIS} |
| 1418 * @this {THIS} |
| 1419 * @template THIS |
| 1420 */ |
| 1421 jspb.Message.prototype.clone = function() { |
| 1422 return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this)); |
| 1423 }; |
| 1424 |
| 1425 /** |
| 1426 * Static clone function. NOTE: A type-safe method called "cloneMessage" |
| 1427 * exists |
1221 * on each generated JsPb class. Do not call this function directly. | 1428 * on each generated JsPb class. Do not call this function directly. |
1222 * @param {!jspb.Message} msg A message to clone. | 1429 * @param {!jspb.Message} msg A message to clone. |
1223 * @return {!jspb.Message} A deep clone of the given message. | 1430 * @return {!jspb.Message} A deep clone of the given message. |
1224 */ | 1431 */ |
1225 jspb.Message.clone = function(msg) { | 1432 jspb.Message.clone = function(msg) { |
1226 // Although we could include the wrappers, we leave them out here. | 1433 // Although we could include the wrappers, we leave them out here. |
1227 return jspb.Message.cloneMessage(msg); | 1434 return jspb.Message.cloneMessage(msg); |
1228 }; | 1435 }; |
1229 | 1436 |
1230 | 1437 |
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1286 // Allocate array of correct size. | 1493 // Allocate array of correct size. |
1287 var clonedArray = new Array(obj.length); | 1494 var clonedArray = new Array(obj.length); |
1288 // Use array iteration where possible because it is faster than for-in. | 1495 // Use array iteration where possible because it is faster than for-in. |
1289 for (var i = 0; i < obj.length; i++) { | 1496 for (var i = 0; i < obj.length; i++) { |
1290 if ((o = obj[i]) != null) { | 1497 if ((o = obj[i]) != null) { |
1291 clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o; | 1498 clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o; |
1292 } | 1499 } |
1293 } | 1500 } |
1294 return clonedArray; | 1501 return clonedArray; |
1295 } | 1502 } |
| 1503 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) { |
| 1504 return new Uint8Array(obj); |
| 1505 } |
1296 var clone = {}; | 1506 var clone = {}; |
1297 for (var key in obj) { | 1507 for (var key in obj) { |
1298 if ((o = obj[key]) != null) { | 1508 if ((o = obj[key]) != null) { |
1299 clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o; | 1509 clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o; |
1300 } | 1510 } |
1301 } | 1511 } |
1302 return clone; | 1512 return clone; |
1303 }; | 1513 }; |
1304 | 1514 |
1305 | 1515 |
(...skipping 22 matching lines...) Expand all Loading... |
1328 * field number to field info object. This should be considered as a | 1538 * field number to field info object. This should be considered as a |
1329 * private API. | 1539 * private API. |
1330 * | 1540 * |
1331 * This is similar to [jspb class name].extensions object for | 1541 * This is similar to [jspb class name].extensions object for |
1332 * non-MessageSet. We special case MessageSet so that we do not need | 1542 * non-MessageSet. We special case MessageSet so that we do not need |
1333 * to goog.require MessageSet from classes that extends MessageSet. | 1543 * to goog.require MessageSet from classes that extends MessageSet. |
1334 * | 1544 * |
1335 * @type {!Object.<number, jspb.ExtensionFieldInfo>} | 1545 * @type {!Object.<number, jspb.ExtensionFieldInfo>} |
1336 */ | 1546 */ |
1337 jspb.Message.messageSetExtensions = {}; | 1547 jspb.Message.messageSetExtensions = {}; |
| 1548 jspb.Message.messageSetExtensionsBinary = {}; |
OLD | NEW |