| OLD | NEW | 
|---|
| (Empty) |  | 
|  | 1 // Protocol Buffers - Google's data interchange format | 
|  | 2 // Copyright 2008 Google Inc.  All rights reserved. | 
|  | 3 // https://developers.google.com/protocol-buffers/ | 
|  | 4 // | 
|  | 5 // Redistribution and use in source and binary forms, with or without | 
|  | 6 // modification, are permitted provided that the following conditions are | 
|  | 7 // met: | 
|  | 8 // | 
|  | 9 //     * Redistributions of source code must retain the above copyright | 
|  | 10 // notice, this list of conditions and the following disclaimer. | 
|  | 11 //     * Redistributions in binary form must reproduce the above | 
|  | 12 // copyright notice, this list of conditions and the following disclaimer | 
|  | 13 // in the documentation and/or other materials provided with the | 
|  | 14 // distribution. | 
|  | 15 //     * Neither the name of Google Inc. nor the names of its | 
|  | 16 // contributors may be used to endorse or promote products derived from | 
|  | 17 // this software without specific prior written permission. | 
|  | 18 // | 
|  | 19 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
|  | 20 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
|  | 21 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
|  | 22 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
|  | 23 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
|  | 24 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
|  | 25 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
|  | 26 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
|  | 27 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
|  | 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. | 
|  | 30 | 
|  | 31 /** | 
|  | 32  * @fileoverview Definition of jspb.Message. | 
|  | 33  * | 
|  | 34  * @author mwr@google.com (Mark Rawling) | 
|  | 35  */ | 
|  | 36 | 
|  | 37 goog.provide('jspb.ExtensionFieldInfo'); | 
|  | 38 goog.provide('jspb.Message'); | 
|  | 39 | 
|  | 40 goog.require('goog.array'); | 
|  | 41 goog.require('goog.asserts'); | 
|  | 42 goog.require('goog.json'); | 
|  | 43 goog.require('goog.object'); | 
|  | 44 | 
|  | 45 // Not needed in compilation units that have no protos with xids. | 
|  | 46 goog.forwardDeclare('xid.String'); | 
|  | 47 | 
|  | 48 | 
|  | 49 | 
|  | 50 /** | 
|  | 51  * Stores information for a single extension field. | 
|  | 52  * | 
|  | 53  * For example, an extension field defined like so: | 
|  | 54  * | 
|  | 55  *     extend BaseMessage { | 
|  | 56  *       optional MyMessage my_field = 123; | 
|  | 57  *     } | 
|  | 58  * | 
|  | 59  * will result in an ExtensionFieldInfo object with these properties: | 
|  | 60  * | 
|  | 61  *     { | 
|  | 62  *       fieldIndex: 123, | 
|  | 63  *       fieldName: {my_field_renamed: 0}, | 
|  | 64  *       ctor: proto.example.MyMessage, | 
|  | 65  *       toObjectFn: proto.example.MyMessage.toObject, | 
|  | 66  *       isRepeated: 0 | 
|  | 67  *     } | 
|  | 68  * | 
|  | 69  * We include `toObjectFn` to allow the JSCompiler to perform dead-code removal | 
|  | 70  * on unused toObject() methods. | 
|  | 71  * | 
|  | 72  * If an extension field is primitive, ctor and toObjectFn will be null. | 
|  | 73  * isRepeated should be 0 or 1. | 
|  | 74  * | 
|  | 75  * binary{Reader,Writer}Fn and (if message type) binaryMessageSerializeFn are | 
|  | 76  * always provided. binaryReaderFn and binaryWriterFn are references to the | 
|  | 77  * appropriate methods on BinaryReader/BinaryWriter to read/write the value of | 
|  | 78  * this extension, and binaryMessageSerializeFn is a reference to the message | 
|  | 79  * class's .serializeBinary method, if available. | 
|  | 80  * | 
|  | 81  * @param {number} fieldNumber | 
|  | 82  * @param {Object} fieldName This has the extension field name as a property. | 
|  | 83  * @param {?function(new: jspb.Message, Array=)} ctor | 
|  | 84  * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn | 
|  | 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 | 
|  | 91  * @constructor | 
|  | 92  * @struct | 
|  | 93  * @template T | 
|  | 94  */ | 
|  | 95 jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn, | 
|  | 96     isRepeated, opt_binaryReaderFn, opt_binaryWriterFn, | 
|  | 97     opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn, | 
|  | 98     opt_isPacked) { | 
|  | 99   /** @const */ | 
|  | 100   this.fieldIndex = fieldNumber; | 
|  | 101   /** @const */ | 
|  | 102   this.fieldName = fieldName; | 
|  | 103   /** @const */ | 
|  | 104   this.ctor = ctor; | 
|  | 105   /** @const */ | 
|  | 106   this.toObjectFn = toObjectFn; | 
|  | 107   /** @const */ | 
|  | 108   this.binaryReaderFn = opt_binaryReaderFn; | 
|  | 109   /** @const */ | 
|  | 110   this.binaryWriterFn = opt_binaryWriterFn; | 
|  | 111   /** @const */ | 
|  | 112   this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn; | 
|  | 113   /** @const */ | 
|  | 114   this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn; | 
|  | 115   /** @const */ | 
|  | 116   this.isRepeated = isRepeated; | 
|  | 117   /** @const */ | 
|  | 118   this.isPacked = opt_isPacked; | 
|  | 119 }; | 
|  | 120 | 
|  | 121 | 
|  | 122 /** | 
|  | 123  * Base class for all JsPb messages. | 
|  | 124  * @constructor | 
|  | 125  * @struct | 
|  | 126  */ | 
|  | 127 jspb.Message = function() { | 
|  | 128 }; | 
|  | 129 | 
|  | 130 | 
|  | 131 /** | 
|  | 132  * @define {boolean} Whether to generate toObject methods for objects. Turn | 
|  | 133  *     this off, if you do not want toObject to be ever used in your project. | 
|  | 134  *     When turning off this flag, consider adding a conformance test that bans | 
|  | 135  *     calling toObject. Enabling this will disable the JSCompiler's ability to | 
|  | 136  *     dead code eliminate fields used in protocol buffers that are never used | 
|  | 137  *     in an application. | 
|  | 138  */ | 
|  | 139 goog.define('jspb.Message.GENERATE_TO_OBJECT', true); | 
|  | 140 | 
|  | 141 | 
|  | 142 /** | 
|  | 143  * @define {boolean} Whether to generate fromObject methods for objects. Turn | 
|  | 144  *     this off, if you do not want fromObject to be ever used in your project. | 
|  | 145  *     When turning off this flag, consider adding a conformance test that bans | 
|  | 146  *     calling fromObject. Enabling this might disable the JSCompiler's ability | 
|  | 147  *     to dead code eliminate fields used in protocol buffers that are never | 
|  | 148  *     used in an application. | 
|  | 149  *     NOTE: By default no protos actually have a fromObject method. You need to | 
|  | 150  *     add the jspb.generate_from_object options to the proto definition to | 
|  | 151  *     activate the feature. | 
|  | 152  *     By default this is enabled for test code only. | 
|  | 153  */ | 
|  | 154 goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE); | 
|  | 155 | 
|  | 156 | 
|  | 157 /** | 
|  | 158  * @define {boolean} Turning on this flag does NOT change the behavior of JSPB | 
|  | 159  *     and only affects private internal state. It may, however, break some | 
|  | 160  *     tests that use naive deeply-equals algorithms, because using a proto | 
|  | 161  *     mutates its internal state. | 
|  | 162  *     Projects are advised to turn this flag always on. | 
|  | 163  */ | 
|  | 164 goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED); | 
|  | 165 // TODO(b/19419436) Turn this on by default. | 
|  | 166 | 
|  | 167 | 
|  | 168 /** | 
|  | 169  * The internal data array. | 
|  | 170  * @type {!Array} | 
|  | 171  * @protected | 
|  | 172  */ | 
|  | 173 jspb.Message.prototype.array; | 
|  | 174 | 
|  | 175 | 
|  | 176 /** | 
|  | 177  * 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 | 
|  | 179  * fields and extension message fields. Indexed by field number. | 
|  | 180  * @type {Object} | 
|  | 181  * @private | 
|  | 182  */ | 
|  | 183 jspb.Message.prototype.wrappers_; | 
|  | 184 | 
|  | 185 | 
|  | 186 /** | 
|  | 187  * The object that contains extension fields, if any. This is an object that | 
|  | 188  * maps from a proto field number to the field's value. | 
|  | 189  * @type {Object} | 
|  | 190  * @private | 
|  | 191  */ | 
|  | 192 jspb.Message.prototype.extensionObject_; | 
|  | 193 | 
|  | 194 | 
|  | 195 /** | 
|  | 196  * Non-extension fields with a field number at or above the pivot are | 
|  | 197  * stored in the extension object (in addition to all extension fields). | 
|  | 198  * @type {number} | 
|  | 199  * @private | 
|  | 200  */ | 
|  | 201 jspb.Message.prototype.pivot_; | 
|  | 202 | 
|  | 203 | 
|  | 204 /** | 
|  | 205  * The JsPb message_id of this proto. | 
|  | 206  * @type {string|undefined} the message id or undefined if this message | 
|  | 207  *     has no id. | 
|  | 208  * @private | 
|  | 209  */ | 
|  | 210 jspb.Message.prototype.messageId_; | 
|  | 211 | 
|  | 212 | 
|  | 213 /** | 
|  | 214  * 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. | 
|  | 216  * @see {xid}. | 
|  | 217  * Available if {@link jspb.generate_xid} is added as a Message option to | 
|  | 218  * a protocol buffer. | 
|  | 219  * @const {!xid.String|undefined} The xid or undefined if message is | 
|  | 220  *     annotated to generate the xid. | 
|  | 221  */ | 
|  | 222 jspb.Message.prototype.messageXid; | 
|  | 223 | 
|  | 224 | 
|  | 225 | 
|  | 226 /** | 
|  | 227  * Returns the JsPb message_id of this proto. | 
|  | 228  * @return {string|undefined} the message id or undefined if this message | 
|  | 229  *     has no id. | 
|  | 230  */ | 
|  | 231 jspb.Message.prototype.getJsPbMessageId = function() { | 
|  | 232   return this.messageId_; | 
|  | 233 }; | 
|  | 234 | 
|  | 235 | 
|  | 236 /** | 
|  | 237  * An offset applied to lookups into this.array to account for the presence or | 
|  | 238  * absence of a messageId at position 0. For response messages, this will be 0. | 
|  | 239  * Otherwise, it will be -1 so that the first array position is not wasted. | 
|  | 240  * @type {number} | 
|  | 241  * @private | 
|  | 242  */ | 
|  | 243 jspb.Message.prototype.arrayIndexOffset_; | 
|  | 244 | 
|  | 245 | 
|  | 246 /** | 
|  | 247  * Returns the index into msg.array at which the proto field with tag number | 
|  | 248  * fieldNumber will be located. | 
|  | 249  * @param {!jspb.Message} msg Message for which we're calculating an index. | 
|  | 250  * @param {number} fieldNumber The field number. | 
|  | 251  * @return {number} The index. | 
|  | 252  * @private | 
|  | 253  */ | 
|  | 254 jspb.Message.getIndex_ = function(msg, fieldNumber) { | 
|  | 255   return fieldNumber + msg.arrayIndexOffset_; | 
|  | 256 }; | 
|  | 257 | 
|  | 258 | 
|  | 259 /** | 
|  | 260  * Initializes a JsPb Message. | 
|  | 261  * @param {!jspb.Message} msg The JsPb proto to modify. | 
|  | 262  * @param {Array|undefined} data An initial data array. | 
|  | 263  * @param {string|number} messageId For response messages, the message id or '' | 
|  | 264  *     if no message id is specified. For non-response messages, 0. | 
|  | 265  * @param {number} suggestedPivot The field number at which to start putting | 
|  | 266  *     fields into the extension object. This is only used if data does not | 
|  | 267  *     contain an extension object already. -1 if no extension object is | 
|  | 268  *     required for this message type. | 
|  | 269  * @param {Array<number>} repeatedFields The message's repeated fields. | 
|  | 270  * @param {Array<!Array<number>>=} opt_oneofFields The fields belonging to | 
|  | 271  *     each of the message's oneof unions. | 
|  | 272  * @protected | 
|  | 273  */ | 
|  | 274 jspb.Message.initialize = function( | 
|  | 275     msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) { | 
|  | 276   msg.wrappers_ = jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? null : {}; | 
|  | 277   if (!data) { | 
|  | 278     data = messageId ? [messageId] : []; | 
|  | 279   } | 
|  | 280   msg.messageId_ = messageId ? String(messageId) : undefined; | 
|  | 281   // 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, | 
|  | 283   // which would otherwise go unused. | 
|  | 284   msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; | 
|  | 285   msg.array = data; | 
|  | 286   jspb.Message.materializeExtensionObject_(msg, suggestedPivot); | 
|  | 287   if (repeatedFields) { | 
|  | 288     for (var i = 0; i < repeatedFields.length; i++) { | 
|  | 289       var fieldNumber = repeatedFields[i]; | 
|  | 290       if (fieldNumber < msg.pivot_) { | 
|  | 291         var index = jspb.Message.getIndex_(msg, fieldNumber); | 
|  | 292         msg.array[index] = msg.array[index] || | 
|  | 293             (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? | 
|  | 294                 jspb.Message.EMPTY_LIST_SENTINEL_ : | 
|  | 295                 []); | 
|  | 296       } else { | 
|  | 297         msg.extensionObject_[fieldNumber] = | 
|  | 298             msg.extensionObject_[fieldNumber] || | 
|  | 299             (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? | 
|  | 300                 jspb.Message.EMPTY_LIST_SENTINEL_ : | 
|  | 301                 []); | 
|  | 302       } | 
|  | 303     } | 
|  | 304   } | 
|  | 305 | 
|  | 306   if (opt_oneofFields && opt_oneofFields.length) { | 
|  | 307     // Compute the oneof case for each union. This ensures only one value is | 
|  | 308     // set in the union. | 
|  | 309     goog.array.forEach( | 
|  | 310         opt_oneofFields, goog.partial(jspb.Message.computeOneofCase, msg)); | 
|  | 311   } | 
|  | 312 }; | 
|  | 313 | 
|  | 314 | 
|  | 315 /** | 
|  | 316  * Used to mark empty repeated fields. Serializes to null when serialized | 
|  | 317  * to JSON. | 
|  | 318  * When reading a repeated field readers must check the return value against | 
|  | 319  * this value and return and replace it with a new empty array if it is | 
|  | 320  * present. | 
|  | 321  * @private @const {!Object} | 
|  | 322  */ | 
|  | 323 jspb.Message.EMPTY_LIST_SENTINEL_ = goog.DEBUG && Object.freeze ? | 
|  | 324     Object.freeze([]) : | 
|  | 325     []; | 
|  | 326 | 
|  | 327 | 
|  | 328 /** | 
|  | 329  * Ensures that the array contains an extension object if necessary. | 
|  | 330  * If the array contains an extension object in its last position, then the | 
|  | 331  * object is kept in place and its position is used as the pivot.  If not, then | 
|  | 332  * create an extension object using suggestedPivot.  If suggestedPivot is -1, | 
|  | 333  * we don't have an extension object at all, in which case all fields are stored | 
|  | 334  * in the array. | 
|  | 335  * @param {!jspb.Message} msg The JsPb proto to modify. | 
|  | 336  * @param {number} suggestedPivot See description for initialize(). | 
|  | 337  * @private | 
|  | 338  */ | 
|  | 339 jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { | 
|  | 340   if (msg.array.length) { | 
|  | 341     var foundIndex = msg.array.length - 1; | 
|  | 342     var obj = msg.array[foundIndex]; | 
|  | 343     // Normal fields are never objects, so we can be sure that if we find an | 
|  | 344     // object here, then it's the extension object. However, we must ensure that | 
|  | 345     // the object is not an array, since arrays are valid field values. | 
|  | 346     // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug | 
|  | 347     // in Safari on iOS 8. See the description of CL/86511464 for details. | 
|  | 348     if (obj && typeof obj == 'object' && !goog.isArray(obj)) { | 
|  | 349       msg.pivot_ = foundIndex - msg.arrayIndexOffset_; | 
|  | 350       msg.extensionObject_ = obj; | 
|  | 351       return; | 
|  | 352     } | 
|  | 353   } | 
|  | 354   // This complexity exists because we keep all extension fields in the | 
|  | 355   // extensionObject_ regardless of proto field number. Changing this would | 
|  | 356   // simplify the code here, but it would require changing the serialization | 
|  | 357   // format from the server, which is not backwards compatible. | 
|  | 358   // TODO(jshneier): Should we just treat extension fields the same as | 
|  | 359   // non-extension fields, and select whether they appear in the object or in | 
|  | 360   // the array purely based on tag number? This would allow simplifying all the | 
|  | 361   // get/setExtension logic, but it would require the breaking change described | 
|  | 362   // above. | 
|  | 363   if (suggestedPivot > -1) { | 
|  | 364     msg.pivot_ = suggestedPivot; | 
|  | 365     var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot); | 
|  | 366     if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { | 
|  | 367       msg.extensionObject_ = msg.array[pivotIndex] = {}; | 
|  | 368     } else { | 
|  | 369       // Initialize to null to avoid changing the shape of the proto when it | 
|  | 370       // gets eventually set. | 
|  | 371       msg.extensionObject_ = null; | 
|  | 372     } | 
|  | 373   } else { | 
|  | 374     msg.pivot_ = Number.MAX_VALUE; | 
|  | 375   } | 
|  | 376 }; | 
|  | 377 | 
|  | 378 | 
|  | 379 /** | 
|  | 380  * Creates an empty extensionObject_ if non exists. | 
|  | 381  * @param {!jspb.Message} msg The JsPb proto to modify. | 
|  | 382  * @private | 
|  | 383  */ | 
|  | 384 jspb.Message.maybeInitEmptyExtensionObject_ = function(msg) { | 
|  | 385   var pivotIndex = jspb.Message.getIndex_(msg, msg.pivot_); | 
|  | 386   if (!msg.array[pivotIndex]) { | 
|  | 387     msg.extensionObject_ = msg.array[pivotIndex] = {}; | 
|  | 388   } | 
|  | 389 }; | 
|  | 390 | 
|  | 391 | 
|  | 392 /** | 
|  | 393  * Converts a JsPb repeated message field into an object list. | 
|  | 394  * @param {!Array<T>} field The repeated message field to be | 
|  | 395  *     converted. | 
|  | 396  * @param {?function(boolean=): Object| | 
|  | 397  *     function((boolean|undefined),T): Object} toObjectFn The toObject | 
|  | 398  *     function for this field.  We need to pass this for effective dead code | 
|  | 399  *     removal. | 
|  | 400  * @param {boolean=} opt_includeInstance Whether to include the JSPB instance | 
|  | 401  *     for transitional soy proto support: http://goto/soy-param-migration | 
|  | 402  * @return {!Array<Object>} An array of converted message objects. | 
|  | 403  * @template T | 
|  | 404  */ | 
|  | 405 jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) { | 
|  | 406   // Not using goog.array.map in the generated code to keep it small. | 
|  | 407   // And not using it here to avoid a function call. | 
|  | 408   var result = []; | 
|  | 409   for (var i = 0; i < field.length; i++) { | 
|  | 410     result[i] = toObjectFn.call(field[i], opt_includeInstance, | 
|  | 411       /** @type {!jspb.Message} */ (field[i])); | 
|  | 412   } | 
|  | 413   return result; | 
|  | 414 }; | 
|  | 415 | 
|  | 416 | 
|  | 417 /** | 
|  | 418  * Adds a proto's extension data to a Soy rendering object. | 
|  | 419  * @param {!jspb.Message} proto The proto whose extensions to convert. | 
|  | 420  * @param {!Object} obj The Soy object to add converted extension data to. | 
|  | 421  * @param {!Object} extensions The proto class' registered extensions. | 
|  | 422  * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto | 
|  | 423  *     class' getExtension function. Passed for effective dead code removal. | 
|  | 424  * @param {boolean=} opt_includeInstance Whether to include the JSPB instance | 
|  | 425  *     for transitional soy proto support: http://goto/soy-param-migration | 
|  | 426  */ | 
|  | 427 jspb.Message.toObjectExtension = function(proto, obj, extensions, | 
|  | 428     getExtensionFn, opt_includeInstance) { | 
|  | 429   for (var fieldNumber in extensions) { | 
|  | 430     var fieldInfo = extensions[fieldNumber]; | 
|  | 431     var value = getExtensionFn.call(proto, fieldInfo); | 
|  | 432     if (value) { | 
|  | 433       for (var name in fieldInfo.fieldName) { | 
|  | 434         if (fieldInfo.fieldName.hasOwnProperty(name)) { | 
|  | 435           break; // the compiled field name | 
|  | 436         } | 
|  | 437       } | 
|  | 438       if (!fieldInfo.toObjectFn) { | 
|  | 439         obj[name] = value; | 
|  | 440       } else { | 
|  | 441         if (fieldInfo.isRepeated) { | 
|  | 442           obj[name] = jspb.Message.toObjectList( | 
|  | 443               /** @type {!Array<jspb.Message>} */ (value), | 
|  | 444               fieldInfo.toObjectFn, opt_includeInstance); | 
|  | 445         } else { | 
|  | 446           obj[name] = fieldInfo.toObjectFn(opt_includeInstance, value); | 
|  | 447         } | 
|  | 448       } | 
|  | 449     } | 
|  | 450   } | 
|  | 451 }; | 
|  | 452 | 
|  | 453 | 
|  | 454 /** | 
|  | 455  * Writes a proto's extension data to a binary-format output stream. | 
|  | 456  * @param {!jspb.Message} proto The proto whose extensions to convert. | 
|  | 457  * @param {*} writer The binary-format writer to write to. | 
|  | 458  * @param {!Object} extensions The proto class' registered extensions. | 
|  | 459  * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto | 
|  | 460  *     class' getExtension function. Passed for effective dead code removal. | 
|  | 461  */ | 
|  | 462 jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, | 
|  | 463     getExtensionFn) { | 
|  | 464   for (var fieldNumber in extensions) { | 
|  | 465     var fieldInfo = extensions[fieldNumber]; | 
|  | 466     // 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 | 
|  | 468     // below. | 
|  | 469     if (!fieldInfo.binaryWriterFn) { | 
|  | 470       throw new Error('Message extension present that was generated ' + | 
|  | 471                       'without binary serialization support'); | 
|  | 472     } | 
|  | 473     var value = getExtensionFn.call(proto, fieldInfo); | 
|  | 474     if (value) { | 
|  | 475       if (fieldInfo.ctor) {  // is this a message type? | 
|  | 476         // If the message type of the extension was generated without binary | 
|  | 477         // 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 | 
|  | 479         // 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 | 
|  | 481         // extensions!). | 
|  | 482         if (fieldInfo.binaryMessageSerializeFn) { | 
|  | 483           fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, | 
|  | 484               value, fieldInfo.binaryMessageSerializeFn); | 
|  | 485         } else { | 
|  | 486           throw new Error('Message extension present holding submessage ' + | 
|  | 487                           'without binary support enabled, and message is ' + | 
|  | 488                           'being serialized to binary format'); | 
|  | 489         } | 
|  | 490       } else { | 
|  | 491         fieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, value); | 
|  | 492       } | 
|  | 493     } | 
|  | 494   } | 
|  | 495 }; | 
|  | 496 | 
|  | 497 | 
|  | 498 /** | 
|  | 499  * Reads an extension field from the given reader and, if a valid extension, | 
|  | 500  * sets the extension value. | 
|  | 501  * @param {!jspb.Message} msg A jspb proto. | 
|  | 502  * @param {{skipField:function(),getFieldNumber:function():number}} reader | 
|  | 503  * @param {!Object} extensions The extensions object. | 
|  | 504  * @param {function(jspb.ExtensionFieldInfo)} getExtensionFn | 
|  | 505  * @param {function(jspb.ExtensionFieldInfo, ?)} setExtensionFn | 
|  | 506  */ | 
|  | 507 jspb.Message.readBinaryExtension = function(msg, reader, extensions, | 
|  | 508     getExtensionFn, setExtensionFn) { | 
|  | 509   var fieldInfo = extensions[reader.getFieldNumber()]; | 
|  | 510   if (!fieldInfo) { | 
|  | 511     reader.skipField(); | 
|  | 512     return; | 
|  | 513   } | 
|  | 514   if (!fieldInfo.binaryReaderFn) { | 
|  | 515     throw new Error('Deserializing extension whose generated code does not ' + | 
|  | 516                     'support binary format'); | 
|  | 517   } | 
|  | 518 | 
|  | 519   var value; | 
|  | 520   if (fieldInfo.ctor) { | 
|  | 521     // Message type. | 
|  | 522     value = new fieldInfo.ctor(); | 
|  | 523     fieldInfo.binaryReaderFn.call( | 
|  | 524         reader, value, fieldInfo.binaryMessageDeserializeFn); | 
|  | 525   } else { | 
|  | 526     // All other types. | 
|  | 527     value = fieldInfo.binaryReaderFn.call(reader); | 
|  | 528   } | 
|  | 529 | 
|  | 530   if (fieldInfo.isRepeated && !fieldInfo.isPacked) { | 
|  | 531     var currentList = getExtensionFn.call(msg, fieldInfo); | 
|  | 532     if (!currentList) { | 
|  | 533       setExtensionFn.call(msg, fieldInfo, [value]); | 
|  | 534     } else { | 
|  | 535       currentList.push(value); | 
|  | 536     } | 
|  | 537   } else { | 
|  | 538     setExtensionFn.call(msg, fieldInfo, value); | 
|  | 539   } | 
|  | 540 }; | 
|  | 541 | 
|  | 542 | 
|  | 543 /** | 
|  | 544  * Gets the value of a non-extension field. | 
|  | 545  * @param {!jspb.Message} msg A jspb proto. | 
|  | 546  * @param {number} fieldNumber The field number. | 
|  | 547  * @return {string|number|boolean|Uint8Array|Array|null|undefined} | 
|  | 548  * The field's value. | 
|  | 549  * @protected | 
|  | 550  */ | 
|  | 551 jspb.Message.getField = function(msg, fieldNumber) { | 
|  | 552   if (fieldNumber < msg.pivot_) { | 
|  | 553     var index = jspb.Message.getIndex_(msg, fieldNumber); | 
|  | 554     var val = msg.array[index]; | 
|  | 555     if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { | 
|  | 556       return msg.array[index] = []; | 
|  | 557     } | 
|  | 558     return val; | 
|  | 559   } else { | 
|  | 560     var val = msg.extensionObject_[fieldNumber]; | 
|  | 561     if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { | 
|  | 562       return msg.extensionObject_[fieldNumber] = []; | 
|  | 563     } | 
|  | 564     return val; | 
|  | 565   } | 
|  | 566 }; | 
|  | 567 | 
|  | 568 | 
|  | 569 /** | 
|  | 570  * Gets the value of a non-extension primitive field, with proto3 (non-nullable | 
|  | 571  * primitives) semantics. Returns `defaultValue` if the field is not otherwise | 
|  | 572  * set. | 
|  | 573  * @template T | 
|  | 574  * @param {!jspb.Message} msg A jspb proto. | 
|  | 575  * @param {number} fieldNumber The field number. | 
|  | 576  * @param {T} defaultValue The default value. | 
|  | 577  * @return {T} The field's value. | 
|  | 578  * @protected | 
|  | 579  */ | 
|  | 580 jspb.Message.getFieldProto3 = function(msg, fieldNumber, defaultValue) { | 
|  | 581   var value = jspb.Message.getField(msg, fieldNumber); | 
|  | 582   if (value == null) { | 
|  | 583     return defaultValue; | 
|  | 584   } else { | 
|  | 585     return value; | 
|  | 586   } | 
|  | 587 }; | 
|  | 588 | 
|  | 589 | 
|  | 590 /** | 
|  | 591  * Sets the value of a non-extension field. | 
|  | 592  * @param {!jspb.Message} msg A jspb proto. | 
|  | 593  * @param {number} fieldNumber The field number. | 
|  | 594  * @param {string|number|boolean|Uint8Array|Array|undefined} value New value | 
|  | 595  * @protected | 
|  | 596  */ | 
|  | 597 jspb.Message.setField = function(msg, fieldNumber, value) { | 
|  | 598   if (fieldNumber < msg.pivot_) { | 
|  | 599     msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; | 
|  | 600   } else { | 
|  | 601     msg.extensionObject_[fieldNumber] = value; | 
|  | 602   } | 
|  | 603 }; | 
|  | 604 | 
|  | 605 | 
|  | 606 /** | 
|  | 607  * Sets the value of a field in a oneof union and clears all other fields in | 
|  | 608  * the union. | 
|  | 609  * @param {!jspb.Message} msg A jspb proto. | 
|  | 610  * @param {number} fieldNumber The field number. | 
|  | 611  * @param {!Array<number>} oneof The fields belonging to the union. | 
|  | 612  * @param {string|number|boolean|Uint8Array|Array|undefined} value New value | 
|  | 613  * @protected | 
|  | 614  */ | 
|  | 615 jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) { | 
|  | 616   var currentCase = jspb.Message.computeOneofCase(msg, oneof); | 
|  | 617   if (currentCase && currentCase !== fieldNumber && value !== undefined) { | 
|  | 618     if (msg.wrappers_ && currentCase in msg.wrappers_) { | 
|  | 619       msg.wrappers_[currentCase] = undefined; | 
|  | 620     } | 
|  | 621     jspb.Message.setField(msg, currentCase, undefined); | 
|  | 622   } | 
|  | 623   jspb.Message.setField(msg, fieldNumber, value); | 
|  | 624 }; | 
|  | 625 | 
|  | 626 | 
|  | 627 /** | 
|  | 628  * Computes the selection in a oneof group for the given message, ensuring | 
|  | 629  * only one field is set in the process. | 
|  | 630  * | 
|  | 631  * According to the protobuf language guide ( | 
|  | 632  * https://developers.google.com/protocol-buffers/docs/proto#oneof), "if the | 
|  | 633  * parser encounters multiple members of the same oneof on the wire, only the | 
|  | 634  * last member seen is used in the parsed message." Since JSPB serializes | 
|  | 635  * messages to a JSON array, the "last member seen" will always be the field | 
|  | 636  * with the greatest field number (directly corresponding to the greatest | 
|  | 637  * array index). | 
|  | 638  * | 
|  | 639  * @param {!jspb.Message} msg A jspb proto. | 
|  | 640  * @param {!Array<number>} oneof The field numbers belonging to the union. | 
|  | 641  * @return {number} The field number currently set in the union, or 0 if none. | 
|  | 642  * @protected | 
|  | 643  */ | 
|  | 644 jspb.Message.computeOneofCase = function(msg, oneof) { | 
|  | 645   var oneofField; | 
|  | 646   var oneofValue; | 
|  | 647 | 
|  | 648   goog.array.forEach(oneof, function(fieldNumber) { | 
|  | 649     var value = jspb.Message.getField(msg, fieldNumber); | 
|  | 650     if (goog.isDefAndNotNull(value)) { | 
|  | 651       oneofField = fieldNumber; | 
|  | 652       oneofValue = value; | 
|  | 653       jspb.Message.setField(msg, fieldNumber, undefined); | 
|  | 654     } | 
|  | 655   }); | 
|  | 656 | 
|  | 657   if (oneofField) { | 
|  | 658     // NB: We know the value is unique, so we can call jspb.Message.setField | 
|  | 659     // directly instead of jpsb.Message.setOneofField. Also, setOneofField | 
|  | 660     // calls this function. | 
|  | 661     jspb.Message.setField(msg, oneofField, oneofValue); | 
|  | 662     return oneofField; | 
|  | 663   } | 
|  | 664 | 
|  | 665   return 0; | 
|  | 666 }; | 
|  | 667 | 
|  | 668 | 
|  | 669 /** | 
|  | 670  * Gets and wraps a proto field on access. | 
|  | 671  * @param {!jspb.Message} msg A jspb proto. | 
|  | 672  * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. | 
|  | 673  * @param {number} fieldNumber The field number. | 
|  | 674  * @param {number=} opt_required True (1) if this is a required field. | 
|  | 675  * @return {jspb.Message} The field as a jspb proto. | 
|  | 676  * @protected | 
|  | 677  */ | 
|  | 678 jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) { | 
|  | 679   // TODO(mwr): Consider copying data and/or arrays. | 
|  | 680   if (!msg.wrappers_) { | 
|  | 681     msg.wrappers_ = {}; | 
|  | 682   } | 
|  | 683   if (!msg.wrappers_[fieldNumber]) { | 
|  | 684     var data = /** @type {Array} */ (jspb.Message.getField(msg, fieldNumber)); | 
|  | 685     if (opt_required || data) { | 
|  | 686       // TODO(mwr): Remove existence test for always valid default protos. | 
|  | 687       msg.wrappers_[fieldNumber] = new ctor(data); | 
|  | 688     } | 
|  | 689   } | 
|  | 690   return /** @type {jspb.Message} */ (msg.wrappers_[fieldNumber]); | 
|  | 691 }; | 
|  | 692 | 
|  | 693 | 
|  | 694 /** | 
|  | 695  * Gets and wraps a repeated proto field on access. | 
|  | 696  * @param {!jspb.Message} msg A jspb proto. | 
|  | 697  * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. | 
|  | 698  * @param {number} fieldNumber The field number. | 
|  | 699  * @return {Array<!jspb.Message>} The repeated field as an array of protos. | 
|  | 700  * @protected | 
|  | 701  */ | 
|  | 702 jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) { | 
|  | 703   if (!msg.wrappers_) { | 
|  | 704     msg.wrappers_ = {}; | 
|  | 705   } | 
|  | 706   if (!msg.wrappers_[fieldNumber]) { | 
|  | 707     var data = jspb.Message.getField(msg, fieldNumber); | 
|  | 708     for (var wrappers = [], i = 0; i < data.length; i++) { | 
|  | 709       wrappers[i] = new ctor(data[i]); | 
|  | 710     } | 
|  | 711     msg.wrappers_[fieldNumber] = wrappers; | 
|  | 712   } | 
|  | 713   var val = msg.wrappers_[fieldNumber]; | 
|  | 714   if (val == jspb.Message.EMPTY_LIST_SENTINEL_) { | 
|  | 715     val = msg.wrappers_[fieldNumber] = []; | 
|  | 716   } | 
|  | 717   return /** @type {Array<!jspb.Message>} */ (val); | 
|  | 718 }; | 
|  | 719 | 
|  | 720 | 
|  | 721 /** | 
|  | 722  * Sets a proto field and syncs it to the backing array. | 
|  | 723  * @param {!jspb.Message} msg A jspb proto. | 
|  | 724  * @param {number} fieldNumber The field number. | 
|  | 725  * @param {jspb.Message|undefined} value A new value for this proto field. | 
|  | 726  * @protected | 
|  | 727  */ | 
|  | 728 jspb.Message.setWrapperField = function(msg, fieldNumber, value) { | 
|  | 729   if (!msg.wrappers_) { | 
|  | 730     msg.wrappers_ = {}; | 
|  | 731   } | 
|  | 732   var data = value ? value.toArray() : value; | 
|  | 733   msg.wrappers_[fieldNumber] = value; | 
|  | 734   jspb.Message.setField(msg, fieldNumber, data); | 
|  | 735 }; | 
|  | 736 | 
|  | 737 | 
|  | 738 /** | 
|  | 739  * Sets a proto field in a oneof union and syncs it to the backing array. | 
|  | 740  * @param {!jspb.Message} msg A jspb proto. | 
|  | 741  * @param {number} fieldNumber The field number. | 
|  | 742  * @param {!Array<number>} oneof The fields belonging to the union. | 
|  | 743  * @param {jspb.Message|undefined} value A new value for this proto field. | 
|  | 744  * @protected | 
|  | 745  */ | 
|  | 746 jspb.Message.setOneofWrapperField = function(msg, fieldNumber, oneof, value) { | 
|  | 747   if (!msg.wrappers_) { | 
|  | 748     msg.wrappers_ = {}; | 
|  | 749   } | 
|  | 750   var data = value ? value.toArray() : value; | 
|  | 751   msg.wrappers_[fieldNumber] = value; | 
|  | 752   jspb.Message.setOneofField(msg, fieldNumber, oneof, data); | 
|  | 753 }; | 
|  | 754 | 
|  | 755 | 
|  | 756 /** | 
|  | 757  * Sets a repeated proto field and syncs it to the backing array. | 
|  | 758  * @param {!jspb.Message} msg A jspb proto. | 
|  | 759  * @param {number} fieldNumber The field number. | 
|  | 760  * @param {Array<!jspb.Message>|undefined} value An array of protos. | 
|  | 761  * @protected | 
|  | 762  */ | 
|  | 763 jspb.Message.setRepeatedWrapperField = function(msg, fieldNumber, value) { | 
|  | 764   if (!msg.wrappers_) { | 
|  | 765     msg.wrappers_ = {}; | 
|  | 766   } | 
|  | 767   value = value || []; | 
|  | 768   for (var data = [], i = 0; i < value.length; i++) { | 
|  | 769     data[i] = value[i].toArray(); | 
|  | 770   } | 
|  | 771   msg.wrappers_[fieldNumber] = value; | 
|  | 772   jspb.Message.setField(msg, fieldNumber, data); | 
|  | 773 }; | 
|  | 774 | 
|  | 775 | 
|  | 776 /** | 
|  | 777  * Converts a JsPb repeated message field into a map. The map will contain | 
|  | 778  * protos unless an optional toObject function is given, in which case it will | 
|  | 779  * contain objects suitable for Soy rendering. | 
|  | 780  * @param {!Array<T>} field The repeated message field to be | 
|  | 781  *     converted. | 
|  | 782  * @param {function() : string?} mapKeyGetterFn The function to get the key of | 
|  | 783  *     the map. | 
|  | 784  * @param {?function(boolean=): Object| | 
|  | 785  *     function((boolean|undefined),T): Object} opt_toObjectFn The | 
|  | 786  *     toObject function for this field. We need to pass this for effective | 
|  | 787  *     dead code removal. | 
|  | 788  * @param {boolean=} opt_includeInstance Whether to include the JSPB instance | 
|  | 789  *     for transitional soy proto support: http://goto/soy-param-migration | 
|  | 790  * @return {!Object.<string, Object>} A map of proto or Soy objects. | 
|  | 791  * @template T | 
|  | 792  */ | 
|  | 793 jspb.Message.toMap = function( | 
|  | 794     field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) { | 
|  | 795   var result = {}; | 
|  | 796   for (var i = 0; i < field.length; i++) { | 
|  | 797     result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ? | 
|  | 798         opt_toObjectFn.call(field[i], opt_includeInstance, | 
|  | 799             /** @type {!jspb.Message} */ (field[i])) : field[i]; | 
|  | 800   } | 
|  | 801   return result; | 
|  | 802 }; | 
|  | 803 | 
|  | 804 | 
|  | 805 /** | 
|  | 806  * Returns the internal array of this proto. | 
|  | 807  * <p>Note: If you use this array to construct a second proto, the content | 
|  | 808  * would then be partially shared between the two protos. | 
|  | 809  * @return {!Array} The proto represented as an array. | 
|  | 810  */ | 
|  | 811 jspb.Message.prototype.toArray = function() { | 
|  | 812   return this.array; | 
|  | 813 }; | 
|  | 814 | 
|  | 815 | 
|  | 816 | 
|  | 817 | 
|  | 818 /** | 
|  | 819  * Creates a string representation of the internal data array of this proto. | 
|  | 820  * <p>NOTE: This string is *not* suitable for use in server requests. | 
|  | 821  * @return {string} A string representation of this proto. | 
|  | 822  * @override | 
|  | 823  */ | 
|  | 824 jspb.Message.prototype.toString = function() { | 
|  | 825   return this.array.toString(); | 
|  | 826 }; | 
|  | 827 | 
|  | 828 | 
|  | 829 /** | 
|  | 830  * Gets the value of the extension field from the extended object. | 
|  | 831  * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get. | 
|  | 832  * @return {T} The value of the field. | 
|  | 833  * @template T | 
|  | 834  */ | 
|  | 835 jspb.Message.prototype.getExtension = function(fieldInfo) { | 
|  | 836   if (!this.extensionObject_) { | 
|  | 837     return undefined; | 
|  | 838   } | 
|  | 839   if (!this.wrappers_) { | 
|  | 840     this.wrappers_ = {}; | 
|  | 841   } | 
|  | 842   var fieldNumber = fieldInfo.fieldIndex; | 
|  | 843   if (fieldInfo.isRepeated) { | 
|  | 844     if (fieldInfo.ctor) { | 
|  | 845       if (!this.wrappers_[fieldNumber]) { | 
|  | 846         this.wrappers_[fieldNumber] = | 
|  | 847             goog.array.map(this.extensionObject_[fieldNumber] || [], | 
|  | 848                 function(arr) { | 
|  | 849                   return new fieldInfo.ctor(arr); | 
|  | 850                 }); | 
|  | 851       } | 
|  | 852       return this.wrappers_[fieldNumber]; | 
|  | 853     } else { | 
|  | 854       return this.extensionObject_[fieldNumber]; | 
|  | 855     } | 
|  | 856   } else { | 
|  | 857     if (fieldInfo.ctor) { | 
|  | 858       if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) { | 
|  | 859         this.wrappers_[fieldNumber] = new fieldInfo.ctor( | 
|  | 860             /** @type {Array|undefined} */ ( | 
|  | 861                 this.extensionObject_[fieldNumber])); | 
|  | 862       } | 
|  | 863       return this.wrappers_[fieldNumber]; | 
|  | 864     } else { | 
|  | 865       return this.extensionObject_[fieldNumber]; | 
|  | 866     } | 
|  | 867   } | 
|  | 868 }; | 
|  | 869 | 
|  | 870 | 
|  | 871 /** | 
|  | 872  * Sets the value of the extension field in the extended object. | 
|  | 873  * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. | 
|  | 874  * @param {jspb.Message|string|number|boolean|Array} value The value to set. | 
|  | 875  */ | 
|  | 876 jspb.Message.prototype.setExtension = function(fieldInfo, value) { | 
|  | 877   if (!this.wrappers_) { | 
|  | 878     this.wrappers_ = {}; | 
|  | 879   } | 
|  | 880   jspb.Message.maybeInitEmptyExtensionObject_(this); | 
|  | 881   var fieldNumber = fieldInfo.fieldIndex; | 
|  | 882   if (fieldInfo.isRepeated) { | 
|  | 883     value = value || []; | 
|  | 884     if (fieldInfo.ctor) { | 
|  | 885       this.wrappers_[fieldNumber] = value; | 
|  | 886       this.extensionObject_[fieldNumber] = goog.array.map( | 
|  | 887           /** @type {Array<jspb.Message>} */ (value), function(msg) { | 
|  | 888         return msg.toArray(); | 
|  | 889       }); | 
|  | 890     } else { | 
|  | 891       this.extensionObject_[fieldNumber] = value; | 
|  | 892     } | 
|  | 893   } else { | 
|  | 894     if (fieldInfo.ctor) { | 
|  | 895       this.wrappers_[fieldNumber] = value; | 
|  | 896       this.extensionObject_[fieldNumber] = value ? value.toArray() : value; | 
|  | 897     } else { | 
|  | 898       this.extensionObject_[fieldNumber] = value; | 
|  | 899     } | 
|  | 900   } | 
|  | 901 }; | 
|  | 902 | 
|  | 903 | 
|  | 904 /** | 
|  | 905  * Creates a difference object between two messages. | 
|  | 906  * | 
|  | 907  * The result will contain the top-level fields of m2 that differ from those of | 
|  | 908  * m1 at any level of nesting. No data is cloned, the result object will | 
|  | 909  * share its top-level elements with m2 (but not with m1). | 
|  | 910  * | 
|  | 911  * Note that repeated fields should not have null/undefined elements, but if | 
|  | 912  * they do, this operation will treat repeated fields of different length as | 
|  | 913  * the same if the only difference between them is due to trailing | 
|  | 914  * null/undefined values. | 
|  | 915  * | 
|  | 916  * @param {!jspb.Message} m1 The first message object. | 
|  | 917  * @param {!jspb.Message} m2 The second message object. | 
|  | 918  * @return {!jspb.Message} The difference returned as a proto message. | 
|  | 919  *     Note that the returned message may be missing required fields. This is | 
|  | 920  *     currently tolerated in Js, but would cause an error if you tried to | 
|  | 921  *     send such a proto to the server. You can access the raw difference | 
|  | 922  *     array with result.toArray(). | 
|  | 923  * @throws {Error} If the messages are responses with different types. | 
|  | 924  */ | 
|  | 925 jspb.Message.difference = function(m1, m2) { | 
|  | 926   if (!(m1 instanceof m2.constructor)) { | 
|  | 927     throw new Error('Messages have different types.'); | 
|  | 928   } | 
|  | 929   var arr1 = m1.toArray(); | 
|  | 930   var arr2 = m2.toArray(); | 
|  | 931   var res = []; | 
|  | 932   var start = 0; | 
|  | 933   var length = arr1.length > arr2.length ? arr1.length : arr2.length; | 
|  | 934   if (m1.getJsPbMessageId()) { | 
|  | 935     res[0] = m1.getJsPbMessageId(); | 
|  | 936     start = 1; | 
|  | 937   } | 
|  | 938   for (var i = start; i < length; i++) { | 
|  | 939     if (!jspb.Message.compareFields(arr1[i], arr2[i])) { | 
|  | 940       res[i] = arr2[i]; | 
|  | 941     } | 
|  | 942   } | 
|  | 943   return new m1.constructor(res); | 
|  | 944 }; | 
|  | 945 | 
|  | 946 | 
|  | 947 /** | 
|  | 948  * Tests whether two messages are equal. | 
|  | 949  * @param {jspb.Message|undefined} m1 The first message object. | 
|  | 950  * @param {jspb.Message|undefined} m2 The second message object. | 
|  | 951  * @return {boolean} true if both messages are null/undefined, or if both are | 
|  | 952  *     of the same type and have the same field values. | 
|  | 953  */ | 
|  | 954 jspb.Message.equals = function(m1, m2) { | 
|  | 955   return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) && | 
|  | 956       jspb.Message.compareFields(m1.toArray(), m2.toArray())); | 
|  | 957 }; | 
|  | 958 | 
|  | 959 | 
|  | 960 /** | 
|  | 961  * Compares two message fields recursively. | 
|  | 962  * @param {*} field1 The first field. | 
|  | 963  * @param {*} field2 The second field. | 
|  | 964  * @return {boolean} true if the fields are null/undefined, or otherwise equal. | 
|  | 965  */ | 
|  | 966 jspb.Message.compareFields = function(field1, field2) { | 
|  | 967   if (goog.isObject(field1) && goog.isObject(field2)) { | 
|  | 968     var keys = {}, name, extensionObject1, extensionObject2; | 
|  | 969     for (name in field1) { | 
|  | 970       field1.hasOwnProperty(name) && (keys[name] = 0); | 
|  | 971     } | 
|  | 972     for (name in field2) { | 
|  | 973       field2.hasOwnProperty(name) && (keys[name] = 0); | 
|  | 974     } | 
|  | 975     for (name in keys) { | 
|  | 976       var val1 = field1[name], val2 = field2[name]; | 
|  | 977       if (goog.isObject(val1) && !goog.isArray(val1)) { | 
|  | 978         if (extensionObject1 !== undefined) { | 
|  | 979           throw new Error('invalid jspb state'); | 
|  | 980         } | 
|  | 981         extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1; | 
|  | 982         val1 = undefined; | 
|  | 983       } | 
|  | 984       if (goog.isObject(val2) && !goog.isArray(val2)) { | 
|  | 985         if (extensionObject2 !== undefined) { | 
|  | 986           throw new Error('invalid jspb state'); | 
|  | 987         } | 
|  | 988         extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2; | 
|  | 989         val2 = undefined; | 
|  | 990       } | 
|  | 991       if (!jspb.Message.compareFields(val1, val2)) { | 
|  | 992         return false; | 
|  | 993       } | 
|  | 994     } | 
|  | 995     if (extensionObject1 || extensionObject2) { | 
|  | 996       return jspb.Message.compareFields(extensionObject1, extensionObject2); | 
|  | 997     } | 
|  | 998     return true; | 
|  | 999   } | 
|  | 1000   // Primitive fields, null and undefined compare as equal. | 
|  | 1001   // This also forces booleans and 0/1 to compare as equal to ensure | 
|  | 1002   // compatibility with the jspb serializer. | 
|  | 1003   return field1 == field2; | 
|  | 1004 }; | 
|  | 1005 | 
|  | 1006 | 
|  | 1007 /** | 
|  | 1008  * Static clone function. NOTE: A type-safe method called "cloneMessage" exists | 
|  | 1009  * on each generated JsPb class. Do not call this function directly. | 
|  | 1010  * @param {!jspb.Message} msg A message to clone. | 
|  | 1011  * @return {!jspb.Message} A deep clone of the given message. | 
|  | 1012  */ | 
|  | 1013 jspb.Message.clone = function(msg) { | 
|  | 1014   // Although we could include the wrappers, we leave them out here. | 
|  | 1015   return jspb.Message.cloneMessage(msg); | 
|  | 1016 }; | 
|  | 1017 | 
|  | 1018 | 
|  | 1019 /** | 
|  | 1020  * @param {!jspb.Message} msg A message to clone. | 
|  | 1021  * @return {!jspb.Message} A deep clone of the given message. | 
|  | 1022  * @protected | 
|  | 1023  */ | 
|  | 1024 jspb.Message.cloneMessage = function(msg) { | 
|  | 1025   // Although we could include the wrappers, we leave them out here. | 
|  | 1026   return new msg.constructor(jspb.Message.clone_(msg.toArray())); | 
|  | 1027 }; | 
|  | 1028 | 
|  | 1029 | 
|  | 1030 /** | 
|  | 1031  * Takes 2 messages of the same type and copies the contents of the first | 
|  | 1032  * message into the second. After this the 2 messages will equals in terms of | 
|  | 1033  * value semantics but share no state. All data in the destination message will | 
|  | 1034  * be overridden. | 
|  | 1035  * | 
|  | 1036  * @param {MESSAGE} fromMessage Message that will be copied into toMessage. | 
|  | 1037  * @param {MESSAGE} toMessage Message which will receive a copy of fromMessage | 
|  | 1038  *     as its contents. | 
|  | 1039  * @template MESSAGE | 
|  | 1040  */ | 
|  | 1041 jspb.Message.copyInto = function(fromMessage, toMessage) { | 
|  | 1042   goog.asserts.assertInstanceof(fromMessage, jspb.Message); | 
|  | 1043   goog.asserts.assertInstanceof(toMessage, jspb.Message); | 
|  | 1044   goog.asserts.assert(fromMessage.constructor == toMessage.constructor, | 
|  | 1045       'Copy source and target message should have the same type.'); | 
|  | 1046   var copyOfFrom = jspb.Message.clone(fromMessage); | 
|  | 1047 | 
|  | 1048   var to = toMessage.toArray(); | 
|  | 1049   var from = copyOfFrom.toArray(); | 
|  | 1050 | 
|  | 1051   // Empty destination in case it has more values at the end of the array. | 
|  | 1052   to.length = 0; | 
|  | 1053   // and then copy everything from the new to the existing message. | 
|  | 1054   for (var i = 0; i < from.length; i++) { | 
|  | 1055     to[i] = from[i]; | 
|  | 1056   } | 
|  | 1057 | 
|  | 1058   // This is either null or empty for a fresh copy. | 
|  | 1059   toMessage.wrappers_ = copyOfFrom.wrappers_; | 
|  | 1060   // Just a reference into the shared array. | 
|  | 1061   toMessage.extensionObject_ = copyOfFrom.extensionObject_; | 
|  | 1062 }; | 
|  | 1063 | 
|  | 1064 | 
|  | 1065 /** | 
|  | 1066  * Helper for cloning an internal JsPb object. | 
|  | 1067  * @param {!Object} obj A JsPb object, eg, a field, to be cloned. | 
|  | 1068  * @return {!Object} A clone of the input object. | 
|  | 1069  * @private | 
|  | 1070  */ | 
|  | 1071 jspb.Message.clone_ = function(obj) { | 
|  | 1072   var o; | 
|  | 1073   if (goog.isArray(obj)) { | 
|  | 1074     // Allocate array of correct size. | 
|  | 1075     var clonedArray = new Array(obj.length); | 
|  | 1076     // Use array iteration where possible because it is faster than for-in. | 
|  | 1077     for (var i = 0; i < obj.length; i++) { | 
|  | 1078       if ((o = obj[i]) != null) { | 
|  | 1079         clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o; | 
|  | 1080       } | 
|  | 1081     } | 
|  | 1082     return clonedArray; | 
|  | 1083   } | 
|  | 1084   var clone = {}; | 
|  | 1085   for (var key in obj) { | 
|  | 1086     if ((o = obj[key]) != null) { | 
|  | 1087       clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o; | 
|  | 1088     } | 
|  | 1089   } | 
|  | 1090   return clone; | 
|  | 1091 }; | 
|  | 1092 | 
|  | 1093 | 
|  | 1094 /** | 
|  | 1095  * Registers a JsPb message type id with its constructor. | 
|  | 1096  * @param {string} id The id for this type of message. | 
|  | 1097  * @param {Function} constructor The message constructor. | 
|  | 1098  */ | 
|  | 1099 jspb.Message.registerMessageType = function(id, constructor) { | 
|  | 1100   jspb.Message.registry_[id] = constructor; | 
|  | 1101   // This is needed so we can later access messageId directly on the contructor, | 
|  | 1102   // otherwise it is not available due to 'property collapsing' by the compiler. | 
|  | 1103   constructor.messageId = id; | 
|  | 1104 }; | 
|  | 1105 | 
|  | 1106 | 
|  | 1107 /** | 
|  | 1108  * The registry of message ids to message constructors. | 
|  | 1109  * @private | 
|  | 1110  */ | 
|  | 1111 jspb.Message.registry_ = {}; | 
|  | 1112 | 
|  | 1113 | 
|  | 1114 /** | 
|  | 1115  * The extensions registered on MessageSet. This is a map of extension | 
|  | 1116  * field number to field info object. This should be considered as a | 
|  | 1117  * private API. | 
|  | 1118  * | 
|  | 1119  * This is similar to [jspb class name].extensions object for | 
|  | 1120  * non-MessageSet. We special case MessageSet so that we do not need | 
|  | 1121  * to goog.require MessageSet from classes that extends MessageSet. | 
|  | 1122  * | 
|  | 1123  * @type {!Object.<number, jspb.ExtensionFieldInfo>} | 
|  | 1124  */ | 
|  | 1125 jspb.Message.messageSetExtensions = {}; | 
| OLD | NEW | 
|---|