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