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 21 matching lines...) Expand all Loading... |
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.ExtensionFieldInfo'); | 37 goog.provide('jspb.ExtensionFieldInfo'); |
38 goog.provide('jspb.Message'); | 38 goog.provide('jspb.Message'); |
39 | 39 |
40 goog.require('goog.array'); | 40 goog.require('goog.array'); |
41 goog.require('goog.asserts'); | 41 goog.require('goog.asserts'); |
| 42 goog.require('goog.crypt.base64'); |
42 goog.require('goog.json'); | 43 goog.require('goog.json'); |
43 goog.require('goog.object'); | |
44 | 44 |
45 // Not needed in compilation units that have no protos with xids. | 45 // Not needed in compilation units that have no protos with xids. |
46 goog.forwardDeclare('xid.String'); | 46 goog.forwardDeclare('xid.String'); |
47 | 47 |
48 | 48 |
49 | 49 |
50 /** | 50 /** |
51 * Stores information for a single extension field. | 51 * Stores information for a single extension field. |
52 * | 52 * |
53 * For example, an extension field defined like so: | 53 * For example, an extension field defined like so: |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
113 /** @const */ | 113 /** @const */ |
114 this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn; | 114 this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn; |
115 /** @const */ | 115 /** @const */ |
116 this.isRepeated = isRepeated; | 116 this.isRepeated = isRepeated; |
117 /** @const */ | 117 /** @const */ |
118 this.isPacked = opt_isPacked; | 118 this.isPacked = opt_isPacked; |
119 }; | 119 }; |
120 | 120 |
121 | 121 |
122 /** | 122 /** |
| 123 * @return {boolean} Does this field represent a sub Message? |
| 124 */ |
| 125 jspb.ExtensionFieldInfo.prototype.isMessageType = function() { |
| 126 return !!this.ctor; |
| 127 }; |
| 128 |
| 129 |
| 130 /** |
123 * Base class for all JsPb messages. | 131 * Base class for all JsPb messages. |
124 * @constructor | 132 * @constructor |
125 * @struct | 133 * @struct |
126 */ | 134 */ |
127 jspb.Message = function() { | 135 jspb.Message = function() { |
128 }; | 136 }; |
129 | 137 |
130 | 138 |
131 /** | 139 /** |
132 * @define {boolean} Whether to generate toObject methods for objects. Turn | 140 * @define {boolean} Whether to generate toObject methods for objects. Turn |
(...skipping 26 matching lines...) Expand all Loading... |
159 * and only affects private internal state. It may, however, break some | 167 * and only affects private internal state. It may, however, break some |
160 * tests that use naive deeply-equals algorithms, because using a proto | 168 * tests that use naive deeply-equals algorithms, because using a proto |
161 * mutates its internal state. | 169 * mutates its internal state. |
162 * Projects are advised to turn this flag always on. | 170 * Projects are advised to turn this flag always on. |
163 */ | 171 */ |
164 goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED); | 172 goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED); |
165 // TODO(b/19419436) Turn this on by default. | 173 // TODO(b/19419436) Turn this on by default. |
166 | 174 |
167 | 175 |
168 /** | 176 /** |
| 177 * Does this browser support Uint8Aray typed arrays? |
| 178 * @type {boolean} |
| 179 * @private |
| 180 */ |
| 181 jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function'); |
| 182 |
| 183 |
| 184 /** |
169 * The internal data array. | 185 * The internal data array. |
170 * @type {!Array} | 186 * @type {!Array} |
171 * @protected | 187 * @protected |
172 */ | 188 */ |
173 jspb.Message.prototype.array; | 189 jspb.Message.prototype.array; |
174 | 190 |
175 | 191 |
176 /** | 192 /** |
177 * Wrappers are the constructed instances of message-type fields. They are built | 193 * Wrappers are the constructed instances of message-type fields. They are built |
178 * on demand from the raw array data. Includes message fields, repeated message | 194 * on demand from the raw array data. Includes message fields, repeated message |
(...skipping 25 matching lines...) Expand all Loading... |
204 /** | 220 /** |
205 * The JsPb message_id of this proto. | 221 * The JsPb message_id of this proto. |
206 * @type {string|undefined} the message id or undefined if this message | 222 * @type {string|undefined} the message id or undefined if this message |
207 * has no id. | 223 * has no id. |
208 * @private | 224 * @private |
209 */ | 225 */ |
210 jspb.Message.prototype.messageId_; | 226 jspb.Message.prototype.messageId_; |
211 | 227 |
212 | 228 |
213 /** | 229 /** |
| 230 * Repeated float or double fields which have been converted to include only |
| 231 * numbers and not strings holding "NaN", "Infinity" and "-Infinity". |
| 232 * @private {!Object<number,boolean>|undefined} |
| 233 */ |
| 234 jspb.Message.prototype.convertedFloatingPointFields_; |
| 235 |
| 236 |
| 237 /** |
214 * The xid of this proto type (The same for all instances of a proto). Provides | 238 * The xid of this proto type (The same for all instances of a proto). Provides |
215 * a way to identify a proto by stable obfuscated name. | 239 * a way to identify a proto by stable obfuscated name. |
216 * @see {xid}. | 240 * @see {xid}. |
217 * Available if {@link jspb.generate_xid} is added as a Message option to | 241 * Available if {@link jspb.generate_xid} is added as a Message option to |
218 * a protocol buffer. | 242 * a protocol buffer. |
219 * @const {!xid.String|undefined} The xid or undefined if message is | 243 * @const {!xid.String|undefined} The xid or undefined if message is |
220 * annotated to generate the xid. | 244 * annotated to generate the xid. |
221 */ | 245 */ |
222 jspb.Message.prototype.messageXid; | 246 jspb.Message.prototype.messageXid; |
223 | 247 |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
277 if (!data) { | 301 if (!data) { |
278 data = messageId ? [messageId] : []; | 302 data = messageId ? [messageId] : []; |
279 } | 303 } |
280 msg.messageId_ = messageId ? String(messageId) : undefined; | 304 msg.messageId_ = messageId ? String(messageId) : undefined; |
281 // If the messageId is 0, this message is not a response message, so we shift | 305 // If the messageId is 0, this message is not a response message, so we shift |
282 // array indices down by 1 so as not to waste the first position in the array, | 306 // array indices down by 1 so as not to waste the first position in the array, |
283 // which would otherwise go unused. | 307 // which would otherwise go unused. |
284 msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; | 308 msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; |
285 msg.array = data; | 309 msg.array = data; |
286 jspb.Message.materializeExtensionObject_(msg, suggestedPivot); | 310 jspb.Message.materializeExtensionObject_(msg, suggestedPivot); |
| 311 msg.convertedFloatingPointFields_ = {}; |
| 312 |
287 if (repeatedFields) { | 313 if (repeatedFields) { |
288 for (var i = 0; i < repeatedFields.length; i++) { | 314 for (var i = 0; i < repeatedFields.length; i++) { |
289 var fieldNumber = repeatedFields[i]; | 315 var fieldNumber = repeatedFields[i]; |
290 if (fieldNumber < msg.pivot_) { | 316 if (fieldNumber < msg.pivot_) { |
291 var index = jspb.Message.getIndex_(msg, fieldNumber); | 317 var index = jspb.Message.getIndex_(msg, fieldNumber); |
292 msg.array[index] = msg.array[index] || | 318 msg.array[index] = msg.array[index] || |
293 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? | 319 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? |
294 jspb.Message.EMPTY_LIST_SENTINEL_ : | 320 jspb.Message.EMPTY_LIST_SENTINEL_ : |
295 []); | 321 []); |
296 } else { | 322 } else { |
(...skipping 115 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
412 } | 438 } |
413 return result; | 439 return result; |
414 }; | 440 }; |
415 | 441 |
416 | 442 |
417 /** | 443 /** |
418 * Adds a proto's extension data to a Soy rendering object. | 444 * Adds a proto's extension data to a Soy rendering object. |
419 * @param {!jspb.Message} proto The proto whose extensions to convert. | 445 * @param {!jspb.Message} proto The proto whose extensions to convert. |
420 * @param {!Object} obj The Soy object to add converted extension data to. | 446 * @param {!Object} obj The Soy object to add converted extension data to. |
421 * @param {!Object} extensions The proto class' registered extensions. | 447 * @param {!Object} extensions The proto class' registered extensions. |
422 * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto | 448 * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn |
423 * class' getExtension function. Passed for effective dead code removal. | 449 * The proto class' getExtension function. Passed for effective dead code |
| 450 * removal. |
424 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance | 451 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance |
425 * for transitional soy proto support: http://goto/soy-param-migration | 452 * for transitional soy proto support: http://goto/soy-param-migration |
426 */ | 453 */ |
427 jspb.Message.toObjectExtension = function(proto, obj, extensions, | 454 jspb.Message.toObjectExtension = function(proto, obj, extensions, |
428 getExtensionFn, opt_includeInstance) { | 455 getExtensionFn, opt_includeInstance) { |
429 for (var fieldNumber in extensions) { | 456 for (var fieldNumber in extensions) { |
430 var fieldInfo = extensions[fieldNumber]; | 457 var fieldInfo = extensions[fieldNumber]; |
431 var value = getExtensionFn.call(proto, fieldInfo); | 458 var value = getExtensionFn.call(proto, fieldInfo); |
432 if (value) { | 459 if (value) { |
433 for (var name in fieldInfo.fieldName) { | 460 for (var name in fieldInfo.fieldName) { |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
465 var fieldInfo = extensions[fieldNumber]; | 492 var fieldInfo = extensions[fieldNumber]; |
466 // 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 |
467 // 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 |
468 // below. | 495 // below. |
469 if (!fieldInfo.binaryWriterFn) { | 496 if (!fieldInfo.binaryWriterFn) { |
470 throw new Error('Message extension present that was generated ' + | 497 throw new Error('Message extension present that was generated ' + |
471 'without binary serialization support'); | 498 'without binary serialization support'); |
472 } | 499 } |
473 var value = getExtensionFn.call(proto, fieldInfo); | 500 var value = getExtensionFn.call(proto, fieldInfo); |
474 if (value) { | 501 if (value) { |
475 if (fieldInfo.ctor) { // is this a message type? | 502 if (fieldInfo.isMessageType()) { |
476 // If the message type of the extension was generated without binary | 503 // If the message type of the extension was generated without binary |
477 // support, there may not be a binary message serializer function, and | 504 // support, there may not be a binary message serializer function, and |
478 // 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 |
479 // 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 |
480 // 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 |
481 // extensions!). | 508 // extensions!). |
482 if (fieldInfo.binaryMessageSerializeFn) { | 509 if (fieldInfo.binaryMessageSerializeFn) { |
483 fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, | 510 fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, |
484 value, fieldInfo.binaryMessageSerializeFn); | 511 value, fieldInfo.binaryMessageSerializeFn); |
485 } else { | 512 } else { |
(...skipping 24 matching lines...) Expand all Loading... |
510 if (!fieldInfo) { | 537 if (!fieldInfo) { |
511 reader.skipField(); | 538 reader.skipField(); |
512 return; | 539 return; |
513 } | 540 } |
514 if (!fieldInfo.binaryReaderFn) { | 541 if (!fieldInfo.binaryReaderFn) { |
515 throw new Error('Deserializing extension whose generated code does not ' + | 542 throw new Error('Deserializing extension whose generated code does not ' + |
516 'support binary format'); | 543 'support binary format'); |
517 } | 544 } |
518 | 545 |
519 var value; | 546 var value; |
520 if (fieldInfo.ctor) { | 547 if (fieldInfo.isMessageType()) { |
521 // Message type. | |
522 value = new fieldInfo.ctor(); | 548 value = new fieldInfo.ctor(); |
523 fieldInfo.binaryReaderFn.call( | 549 fieldInfo.binaryReaderFn.call( |
524 reader, value, fieldInfo.binaryMessageDeserializeFn); | 550 reader, value, fieldInfo.binaryMessageDeserializeFn); |
525 } else { | 551 } else { |
526 // All other types. | 552 // All other types. |
527 value = fieldInfo.binaryReaderFn.call(reader); | 553 value = fieldInfo.binaryReaderFn.call(reader); |
528 } | 554 } |
529 | 555 |
530 if (fieldInfo.isRepeated && !fieldInfo.isPacked) { | 556 if (fieldInfo.isRepeated && !fieldInfo.isPacked) { |
531 var currentList = getExtensionFn.call(msg, fieldInfo); | 557 var currentList = getExtensionFn.call(msg, fieldInfo); |
(...skipping 28 matching lines...) Expand all Loading... |
560 var val = msg.extensionObject_[fieldNumber]; | 586 var val = msg.extensionObject_[fieldNumber]; |
561 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { | 587 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { |
562 return msg.extensionObject_[fieldNumber] = []; | 588 return msg.extensionObject_[fieldNumber] = []; |
563 } | 589 } |
564 return val; | 590 return val; |
565 } | 591 } |
566 }; | 592 }; |
567 | 593 |
568 | 594 |
569 /** | 595 /** |
| 596 * Gets the value of an optional float or double field. |
| 597 * @param {!jspb.Message} msg A jspb proto. |
| 598 * @param {number} fieldNumber The field number. |
| 599 * @return {?number|undefined} The field's value. |
| 600 * @protected |
| 601 */ |
| 602 jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) { |
| 603 var value = jspb.Message.getField(msg, fieldNumber); |
| 604 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers. |
| 605 return value == null ? value : +value; |
| 606 }; |
| 607 |
| 608 |
| 609 /** |
| 610 * Gets the value of a repeated float or double field. |
| 611 * @param {!jspb.Message} msg A jspb proto. |
| 612 * @param {number} fieldNumber The field number. |
| 613 * @return {!Array<number>} The field's value. |
| 614 * @protected |
| 615 */ |
| 616 jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) { |
| 617 var values = jspb.Message.getField(msg, fieldNumber); |
| 618 if (!msg.convertedFloatingPointFields_) { |
| 619 msg.convertedFloatingPointFields_ = {}; |
| 620 } |
| 621 if (!msg.convertedFloatingPointFields_[fieldNumber]) { |
| 622 for (var i = 0; i < values.length; i++) { |
| 623 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding |
| 624 // numbers. |
| 625 values[i] = +values[i]; |
| 626 } |
| 627 msg.convertedFloatingPointFields_[fieldNumber] = true; |
| 628 } |
| 629 return /** @type {!Array<number>} */ (values); |
| 630 }; |
| 631 |
| 632 |
| 633 /** |
| 634 * Coerce a 'bytes' field to a base 64 string. |
| 635 * @param {string|Uint8Array|null} value |
| 636 * @return {?string} The field's coerced value. |
| 637 */ |
| 638 jspb.Message.bytesAsB64 = function(value) { |
| 639 if (value == null || goog.isString(value)) { |
| 640 return value; |
| 641 } |
| 642 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) { |
| 643 return goog.crypt.base64.encodeByteArray(value); |
| 644 } |
| 645 goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value)); |
| 646 return null; |
| 647 }; |
| 648 |
| 649 |
| 650 /** |
| 651 * Coerce a 'bytes' field to a Uint8Array byte buffer. |
| 652 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera |
| 653 * Mini. @see http://caniuse.com/Uint8Array |
| 654 * @param {string|Uint8Array|null} value |
| 655 * @return {?Uint8Array} The field's coerced value. |
| 656 */ |
| 657 jspb.Message.bytesAsU8 = function(value) { |
| 658 if (value == null || value instanceof Uint8Array) { |
| 659 return value; |
| 660 } |
| 661 if (goog.isString(value)) { |
| 662 return goog.crypt.base64.decodeStringToUint8Array(value); |
| 663 } |
| 664 goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value)); |
| 665 return null; |
| 666 }; |
| 667 |
| 668 |
| 669 /** |
| 670 * Coerce a repeated 'bytes' field to an array of base 64 strings. |
| 671 * Note: the returned array should be treated as immutable. |
| 672 * @param {!Array<string>|!Array<!Uint8Array>} value |
| 673 * @return {!Array<string?>} The field's coerced value. |
| 674 */ |
| 675 jspb.Message.bytesListAsB64 = function(value) { |
| 676 jspb.Message.assertConsistentTypes_(value); |
| 677 if (!value.length || goog.isString(value[0])) { |
| 678 return /** @type {!Array<string>} */ (value); |
| 679 } |
| 680 return goog.array.map(value, jspb.Message.bytesAsB64); |
| 681 }; |
| 682 |
| 683 |
| 684 /** |
| 685 * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers. |
| 686 * Note: the returned array should be treated as immutable. |
| 687 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera |
| 688 * Mini. @see http://caniuse.com/Uint8Array |
| 689 * @param {!Array<string>|!Array<!Uint8Array>} value |
| 690 * @return {!Array<Uint8Array?>} The field's coerced value. |
| 691 */ |
| 692 jspb.Message.bytesListAsU8 = function(value) { |
| 693 jspb.Message.assertConsistentTypes_(value); |
| 694 if (!value.length || value[0] instanceof Uint8Array) { |
| 695 return /** @type {!Array<!Uint8Array>} */ (value); |
| 696 } |
| 697 return goog.array.map(value, jspb.Message.bytesAsU8); |
| 698 }; |
| 699 |
| 700 |
| 701 /** |
| 702 * Asserts that all elements of an array are of the same type. |
| 703 * @param {Array?} array The array to test. |
| 704 * @private |
| 705 */ |
| 706 jspb.Message.assertConsistentTypes_ = function(array) { |
| 707 if (goog.DEBUG && array && array.length > 1) { |
| 708 var expected = goog.typeOf(array[0]); |
| 709 goog.array.forEach(array, function(e) { |
| 710 if (goog.typeOf(e) != expected) { |
| 711 goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' + |
| 712 'Got ' + goog.typeOf(e) + ' expected ' + expected); |
| 713 } |
| 714 }); |
| 715 } |
| 716 }; |
| 717 |
| 718 |
| 719 /** |
570 * 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 |
571 * primitives) semantics. Returns `defaultValue` if the field is not otherwise | 721 * primitives) semantics. Returns `defaultValue` if the field is not otherwise |
572 * set. | 722 * set. |
573 * @template T | 723 * @template T |
574 * @param {!jspb.Message} msg A jspb proto. | 724 * @param {!jspb.Message} msg A jspb proto. |
575 * @param {number} fieldNumber The field number. | 725 * @param {number} fieldNumber The field number. |
576 * @param {T} defaultValue The default value. | 726 * @param {T} defaultValue The default value. |
577 * @return {T} The field's value. | 727 * @return {T} The field's value. |
578 * @protected | 728 * @protected |
579 */ | 729 */ |
(...skipping 254 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
834 */ | 984 */ |
835 jspb.Message.prototype.getExtension = function(fieldInfo) { | 985 jspb.Message.prototype.getExtension = function(fieldInfo) { |
836 if (!this.extensionObject_) { | 986 if (!this.extensionObject_) { |
837 return undefined; | 987 return undefined; |
838 } | 988 } |
839 if (!this.wrappers_) { | 989 if (!this.wrappers_) { |
840 this.wrappers_ = {}; | 990 this.wrappers_ = {}; |
841 } | 991 } |
842 var fieldNumber = fieldInfo.fieldIndex; | 992 var fieldNumber = fieldInfo.fieldIndex; |
843 if (fieldInfo.isRepeated) { | 993 if (fieldInfo.isRepeated) { |
844 if (fieldInfo.ctor) { | 994 if (fieldInfo.isMessageType()) { |
845 if (!this.wrappers_[fieldNumber]) { | 995 if (!this.wrappers_[fieldNumber]) { |
846 this.wrappers_[fieldNumber] = | 996 this.wrappers_[fieldNumber] = |
847 goog.array.map(this.extensionObject_[fieldNumber] || [], | 997 goog.array.map(this.extensionObject_[fieldNumber] || [], |
848 function(arr) { | 998 function(arr) { |
849 return new fieldInfo.ctor(arr); | 999 return new fieldInfo.ctor(arr); |
850 }); | 1000 }); |
851 } | 1001 } |
852 return this.wrappers_[fieldNumber]; | 1002 return this.wrappers_[fieldNumber]; |
853 } else { | 1003 } else { |
854 return this.extensionObject_[fieldNumber]; | 1004 return this.extensionObject_[fieldNumber]; |
855 } | 1005 } |
856 } else { | 1006 } else { |
857 if (fieldInfo.ctor) { | 1007 if (fieldInfo.isMessageType()) { |
858 if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) { | 1008 if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) { |
859 this.wrappers_[fieldNumber] = new fieldInfo.ctor( | 1009 this.wrappers_[fieldNumber] = new fieldInfo.ctor( |
860 /** @type {Array|undefined} */ ( | 1010 /** @type {Array|undefined} */ ( |
861 this.extensionObject_[fieldNumber])); | 1011 this.extensionObject_[fieldNumber])); |
862 } | 1012 } |
863 return this.wrappers_[fieldNumber]; | 1013 return this.wrappers_[fieldNumber]; |
864 } else { | 1014 } else { |
865 return this.extensionObject_[fieldNumber]; | 1015 return this.extensionObject_[fieldNumber]; |
866 } | 1016 } |
867 } | 1017 } |
868 }; | 1018 }; |
869 | 1019 |
870 | 1020 |
871 /** | 1021 /** |
872 * Sets the value of the extension field in the extended object. | 1022 * Sets the value of the extension field in the extended object. |
873 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. | 1023 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. |
874 * @param {jspb.Message|string|number|boolean|Array} value The value to set. | 1024 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value |
| 1025 * to set. |
875 */ | 1026 */ |
876 jspb.Message.prototype.setExtension = function(fieldInfo, value) { | 1027 jspb.Message.prototype.setExtension = function(fieldInfo, value) { |
877 if (!this.wrappers_) { | 1028 if (!this.wrappers_) { |
878 this.wrappers_ = {}; | 1029 this.wrappers_ = {}; |
879 } | 1030 } |
880 jspb.Message.maybeInitEmptyExtensionObject_(this); | 1031 jspb.Message.maybeInitEmptyExtensionObject_(this); |
881 var fieldNumber = fieldInfo.fieldIndex; | 1032 var fieldNumber = fieldInfo.fieldIndex; |
882 if (fieldInfo.isRepeated) { | 1033 if (fieldInfo.isRepeated) { |
883 value = value || []; | 1034 value = value || []; |
884 if (fieldInfo.ctor) { | 1035 if (fieldInfo.isMessageType()) { |
885 this.wrappers_[fieldNumber] = value; | 1036 this.wrappers_[fieldNumber] = value; |
886 this.extensionObject_[fieldNumber] = goog.array.map( | 1037 this.extensionObject_[fieldNumber] = goog.array.map( |
887 /** @type {Array<jspb.Message>} */ (value), function(msg) { | 1038 /** @type {Array<jspb.Message>} */ (value), function(msg) { |
888 return msg.toArray(); | 1039 return msg.toArray(); |
889 }); | 1040 }); |
890 } else { | 1041 } else { |
891 this.extensionObject_[fieldNumber] = value; | 1042 this.extensionObject_[fieldNumber] = value; |
892 } | 1043 } |
893 } else { | 1044 } else { |
894 if (fieldInfo.ctor) { | 1045 if (fieldInfo.isMessageType()) { |
895 this.wrappers_[fieldNumber] = value; | 1046 this.wrappers_[fieldNumber] = value; |
896 this.extensionObject_[fieldNumber] = value ? value.toArray() : value; | 1047 this.extensionObject_[fieldNumber] = value ? value.toArray() : value; |
897 } else { | 1048 } else { |
898 this.extensionObject_[fieldNumber] = value; | 1049 this.extensionObject_[fieldNumber] = value; |
899 } | 1050 } |
900 } | 1051 } |
901 }; | 1052 }; |
902 | 1053 |
903 | 1054 |
904 /** | 1055 /** |
(...skipping 46 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
951 * @return {boolean} true if both messages are null/undefined, or if both are | 1102 * @return {boolean} true if both messages are null/undefined, or if both are |
952 * of the same type and have the same field values. | 1103 * of the same type and have the same field values. |
953 */ | 1104 */ |
954 jspb.Message.equals = function(m1, m2) { | 1105 jspb.Message.equals = function(m1, m2) { |
955 return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) && | 1106 return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) && |
956 jspb.Message.compareFields(m1.toArray(), m2.toArray())); | 1107 jspb.Message.compareFields(m1.toArray(), m2.toArray())); |
957 }; | 1108 }; |
958 | 1109 |
959 | 1110 |
960 /** | 1111 /** |
| 1112 * Compares two message extension fields recursively. |
| 1113 * @param {!Object} extension1 The first field. |
| 1114 * @param {!Object} extension2 The second field. |
| 1115 * @return {boolean} true if the extensions are null/undefined, or otherwise |
| 1116 * equal. |
| 1117 */ |
| 1118 jspb.Message.compareExtensions = function(extension1, extension2) { |
| 1119 extension1 = extension1 || {}; |
| 1120 extension2 = extension2 || {}; |
| 1121 |
| 1122 var keys = {}; |
| 1123 for (var name in extension1) { |
| 1124 keys[name] = 0; |
| 1125 } |
| 1126 for (var name in extension2) { |
| 1127 keys[name] = 0; |
| 1128 } |
| 1129 for (name in keys) { |
| 1130 if (!jspb.Message.compareFields(extension1[name], extension2[name])) { |
| 1131 return false; |
| 1132 } |
| 1133 } |
| 1134 return true; |
| 1135 }; |
| 1136 |
| 1137 |
| 1138 /** |
961 * Compares two message fields recursively. | 1139 * Compares two message fields recursively. |
962 * @param {*} field1 The first field. | 1140 * @param {*} field1 The first field. |
963 * @param {*} field2 The second field. | 1141 * @param {*} field2 The second field. |
964 * @return {boolean} true if the fields are null/undefined, or otherwise equal. | 1142 * @return {boolean} true if the fields are null/undefined, or otherwise equal. |
965 */ | 1143 */ |
966 jspb.Message.compareFields = function(field1, field2) { | 1144 jspb.Message.compareFields = function(field1, field2) { |
967 if (goog.isObject(field1) && goog.isObject(field2)) { | 1145 // If the fields are trivially equal, they're equal. |
968 var keys = {}, name, extensionObject1, extensionObject2; | 1146 if (field1 == field2) return true; |
969 for (name in field1) { | 1147 |
970 field1.hasOwnProperty(name) && (keys[name] = 0); | 1148 // If the fields aren't trivially equal and one of them isn't an object, |
| 1149 // they can't possibly be equal. |
| 1150 if (!goog.isObject(field1) || !goog.isObject(field2)) { |
| 1151 return false; |
| 1152 } |
| 1153 |
| 1154 // We have two objects. If they're different types, they're not equal. |
| 1155 field1 = /** @type {!Object} */(field1); |
| 1156 field2 = /** @type {!Object} */(field2); |
| 1157 if (field1.constructor != field2.constructor) return false; |
| 1158 |
| 1159 // If both are Uint8Arrays, compare them element-by-element. |
| 1160 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) { |
| 1161 var bytes1 = /** @type {!Uint8Array} */(field1); |
| 1162 var bytes2 = /** @type {!Uint8Array} */(field2); |
| 1163 if (bytes1.length != bytes2.length) return false; |
| 1164 for (var i = 0; i < bytes1.length; i++) { |
| 1165 if (bytes1[i] != bytes2[i]) return false; |
971 } | 1166 } |
972 for (name in field2) { | 1167 return true; |
973 field2.hasOwnProperty(name) && (keys[name] = 0); | 1168 } |
974 } | 1169 |
975 for (name in keys) { | 1170 // If they're both Arrays, compare them element by element except for the |
976 var val1 = field1[name], val2 = field2[name]; | 1171 // optional extension objects at the end, which we compare separately. |
977 if (goog.isObject(val1) && !goog.isArray(val1)) { | 1172 if (field1.constructor === Array) { |
978 if (extensionObject1 !== undefined) { | 1173 var extension1 = undefined; |
979 throw new Error('invalid jspb state'); | 1174 var extension2 = undefined; |
980 } | 1175 |
981 extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1; | 1176 var length = Math.max(field1.length, field2.length); |
| 1177 for (var i = 0; i < length; i++) { |
| 1178 var val1 = field1[i]; |
| 1179 var val2 = field2[i]; |
| 1180 |
| 1181 if (val1 && (val1.constructor == Object)) { |
| 1182 goog.asserts.assert(extension1 === undefined); |
| 1183 goog.asserts.assert(i === field1.length - 1); |
| 1184 extension1 = val1; |
982 val1 = undefined; | 1185 val1 = undefined; |
983 } | 1186 } |
984 if (goog.isObject(val2) && !goog.isArray(val2)) { | 1187 |
985 if (extensionObject2 !== undefined) { | 1188 if (val2 && (val2.constructor == Object)) { |
986 throw new Error('invalid jspb state'); | 1189 goog.asserts.assert(extension2 === undefined); |
987 } | 1190 goog.asserts.assert(i === field2.length - 1); |
988 extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2; | 1191 extension2 = val2; |
989 val2 = undefined; | 1192 val2 = undefined; |
990 } | 1193 } |
| 1194 |
991 if (!jspb.Message.compareFields(val1, val2)) { | 1195 if (!jspb.Message.compareFields(val1, val2)) { |
992 return false; | 1196 return false; |
993 } | 1197 } |
994 } | 1198 } |
995 if (extensionObject1 || extensionObject2) { | 1199 |
996 return jspb.Message.compareFields(extensionObject1, extensionObject2); | 1200 if (extension1 || extension2) { |
| 1201 extension1 = extension1 || {}; |
| 1202 extension2 = extension2 || {}; |
| 1203 return jspb.Message.compareExtensions(extension1, extension2); |
997 } | 1204 } |
| 1205 |
998 return true; | 1206 return true; |
999 } | 1207 } |
1000 // Primitive fields, null and undefined compare as equal. | 1208 |
1001 // This also forces booleans and 0/1 to compare as equal to ensure | 1209 // If they're both plain Objects (i.e. extensions), compare them as |
1002 // compatibility with the jspb serializer. | 1210 // extensions. |
1003 return field1 == field2; | 1211 if (field1.constructor === Object) { |
| 1212 return jspb.Message.compareExtensions(field1, field2); |
| 1213 } |
| 1214 |
| 1215 throw new Error('Invalid type in JSPB array'); |
1004 }; | 1216 }; |
1005 | 1217 |
1006 | 1218 |
1007 /** | 1219 /** |
1008 * Static clone function. NOTE: A type-safe method called "cloneMessage" exists | 1220 * Static clone function. NOTE: A type-safe method called "cloneMessage" exists |
1009 * on each generated JsPb class. Do not call this function directly. | 1221 * on each generated JsPb class. Do not call this function directly. |
1010 * @param {!jspb.Message} msg A message to clone. | 1222 * @param {!jspb.Message} msg A message to clone. |
1011 * @return {!jspb.Message} A deep clone of the given message. | 1223 * @return {!jspb.Message} A deep clone of the given message. |
1012 */ | 1224 */ |
1013 jspb.Message.clone = function(msg) { | 1225 jspb.Message.clone = function(msg) { |
(...skipping 102 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1116 * 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 |
1117 * private API. | 1329 * private API. |
1118 * | 1330 * |
1119 * This is similar to [jspb class name].extensions object for | 1331 * This is similar to [jspb class name].extensions object for |
1120 * 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 |
1121 * to goog.require MessageSet from classes that extends MessageSet. | 1333 * to goog.require MessageSet from classes that extends MessageSet. |
1122 * | 1334 * |
1123 * @type {!Object.<number, jspb.ExtensionFieldInfo>} | 1335 * @type {!Object.<number, jspb.ExtensionFieldInfo>} |
1124 */ | 1336 */ |
1125 jspb.Message.messageSetExtensions = {}; | 1337 jspb.Message.messageSetExtensions = {}; |
OLD | NEW |