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 |