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 37goog.provide('jspb.ExtensionFieldBinaryInfo'); 38goog.provide('jspb.ExtensionFieldInfo'); 39goog.provide('jspb.Message'); 40 41goog.require('goog.array'); 42goog.require('goog.asserts'); 43goog.require('goog.crypt.base64'); 44goog.require('goog.json'); 45goog.require('jspb.Map'); 46 47// Not needed in compilation units that have no protos with xids. 48goog.forwardDeclare('xid.String'); 49 50 51 52/** 53 * Stores information for a single extension field. 54 * 55 * For example, an extension field defined like so: 56 * 57 * extend BaseMessage { 58 * optional MyMessage my_field = 123; 59 * } 60 * 61 * will result in an ExtensionFieldInfo object with these properties: 62 * 63 * { 64 * fieldIndex: 123, 65 * fieldName: {my_field_renamed: 0}, 66 * ctor: proto.example.MyMessage, 67 * toObjectFn: proto.example.MyMessage.toObject, 68 * isRepeated: 0 69 * } 70 * 71 * We include `toObjectFn` to allow the JSCompiler to perform dead-code removal 72 * on unused toObject() methods. 73 * 74 * If an extension field is primitive, ctor and toObjectFn will be null. 75 * isRepeated should be 0 or 1. 76 * 77 * binary{Reader,Writer}Fn and (if message type) binaryMessageSerializeFn are 78 * always provided. binaryReaderFn and binaryWriterFn are references to the 79 * appropriate methods on BinaryReader/BinaryWriter to read/write the value of 80 * this extension, and binaryMessageSerializeFn is a reference to the message 81 * class's .serializeBinary method, if available. 82 * 83 * @param {number} fieldNumber 84 * @param {Object} fieldName This has the extension field name as a property. 85 * @param {?function(new: jspb.Message, Array=)} ctor 86 * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn 87 * @param {number} isRepeated 88 * @constructor 89 * @struct 90 * @template T 91 */ 92jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn, 93 isRepeated) { 94 /** @const */ 95 this.fieldIndex = fieldNumber; 96 /** @const */ 97 this.fieldName = fieldName; 98 /** @const */ 99 this.ctor = ctor; 100 /** @const */ 101 this.toObjectFn = toObjectFn; 102 /** @const */ 103 this.isRepeated = isRepeated; 104}; 105 106/** 107 * Stores binary-related information for a single extension field. 108 * @param {!jspb.ExtensionFieldInfo<T>} fieldInfo 109 * @param {?function(number,?)=} binaryReaderFn 110 * @param {?function(number,?)|function(number,?,?,?,?,?)=} binaryWriterFn 111 * @param {?function(?,?)=} opt_binaryMessageSerializeFn 112 * @param {?function(?,?)=} opt_binaryMessageDeserializeFn 113 * @param {?boolean=} opt_isPacked 114 * @constructor 115 * @struct 116 * @template T 117 */ 118jspb.ExtensionFieldBinaryInfo = function(fieldInfo, binaryReaderFn, binaryWriterFn, 119 binaryMessageSerializeFn, binaryMessageDeserializeFn, isPacked) { 120 /** @const */ 121 this.fieldInfo = fieldInfo; 122 /** @const */ 123 this.binaryReaderFn = binaryReaderFn; 124 /** @const */ 125 this.binaryWriterFn = binaryWriterFn; 126 /** @const */ 127 this.binaryMessageSerializeFn = binaryMessageSerializeFn; 128 /** @const */ 129 this.binaryMessageDeserializeFn = binaryMessageDeserializeFn; 130 /** @const */ 131 this.isPacked = isPacked; 132}; 133 134/** 135 * @return {boolean} Does this field represent a sub Message? 136 */ 137jspb.ExtensionFieldInfo.prototype.isMessageType = function() { 138 return !!this.ctor; 139}; 140 141 142/** 143 * Base class for all JsPb messages. 144 * @constructor 145 * @struct 146 */ 147jspb.Message = function() { 148}; 149 150 151/** 152 * @define {boolean} Whether to generate toObject methods for objects. Turn 153 * this off, if you do not want toObject to be ever used in your project. 154 * When turning off this flag, consider adding a conformance test that bans 155 * calling toObject. Enabling this will disable the JSCompiler's ability to 156 * dead code eliminate fields used in protocol buffers that are never used 157 * in an application. 158 */ 159goog.define('jspb.Message.GENERATE_TO_OBJECT', true); 160 161 162/** 163 * @define {boolean} Whether to generate fromObject methods for objects. Turn 164 * this off, if you do not want fromObject to be ever used in your project. 165 * When turning off this flag, consider adding a conformance test that bans 166 * calling fromObject. Enabling this might disable the JSCompiler's ability 167 * to dead code eliminate fields used in protocol buffers that are never 168 * used in an application. 169 * NOTE: By default no protos actually have a fromObject method. You need to 170 * add the jspb.generate_from_object options to the proto definition to 171 * activate the feature. 172 * By default this is enabled for test code only. 173 */ 174goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE); 175 176 177/** 178 * @define {boolean} Turning on this flag does NOT change the behavior of JSPB 179 * and only affects private internal state. It may, however, break some 180 * tests that use naive deeply-equals algorithms, because using a proto 181 * mutates its internal state. 182 * Projects are advised to turn this flag always on. 183 */ 184goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED); 185// TODO(b/19419436) Turn this on by default. 186 187 188/** 189 * Does this browser support Uint8Aray typed arrays? 190 * @type {boolean} 191 * @private 192 */ 193jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function'); 194 195 196/** 197 * The internal data array. 198 * @type {!Array} 199 * @protected 200 */ 201jspb.Message.prototype.array; 202 203 204/** 205 * Wrappers are the constructed instances of message-type fields. They are built 206 * on demand from the raw array data. Includes message fields, repeated message 207 * fields and extension message fields. Indexed by field number. 208 * @type {Object} 209 * @private 210 */ 211jspb.Message.prototype.wrappers_; 212 213 214/** 215 * The object that contains extension fields, if any. This is an object that 216 * maps from a proto field number to the field's value. 217 * @type {Object} 218 * @private 219 */ 220jspb.Message.prototype.extensionObject_; 221 222 223/** 224 * Non-extension fields with a field number at or above the pivot are 225 * stored in the extension object (in addition to all extension fields). 226 * @type {number} 227 * @private 228 */ 229jspb.Message.prototype.pivot_; 230 231 232/** 233 * The JsPb message_id of this proto. 234 * @type {string|undefined} the message id or undefined if this message 235 * has no id. 236 * @private 237 */ 238jspb.Message.prototype.messageId_; 239 240 241/** 242 * Repeated float or double fields which have been converted to include only 243 * numbers and not strings holding "NaN", "Infinity" and "-Infinity". 244 * @private {!Object<number,boolean>|undefined} 245 */ 246jspb.Message.prototype.convertedFloatingPointFields_; 247 248 249/** 250 * The xid of this proto type (The same for all instances of a proto). Provides 251 * a way to identify a proto by stable obfuscated name. 252 * @see {xid}. 253 * Available if {@link jspb.generate_xid} is added as a Message option to 254 * a protocol buffer. 255 * @const {!xid.String|undefined} The xid or undefined if message is 256 * annotated to generate the xid. 257 */ 258jspb.Message.prototype.messageXid; 259 260 261 262/** 263 * Returns the JsPb message_id of this proto. 264 * @return {string|undefined} the message id or undefined if this message 265 * has no id. 266 */ 267jspb.Message.prototype.getJsPbMessageId = function() { 268 return this.messageId_; 269}; 270 271 272/** 273 * An offset applied to lookups into this.array to account for the presence or 274 * absence of a messageId at position 0. For response messages, this will be 0. 275 * Otherwise, it will be -1 so that the first array position is not wasted. 276 * @type {number} 277 * @private 278 */ 279jspb.Message.prototype.arrayIndexOffset_; 280 281 282/** 283 * Returns the index into msg.array at which the proto field with tag number 284 * fieldNumber will be located. 285 * @param {!jspb.Message} msg Message for which we're calculating an index. 286 * @param {number} fieldNumber The field number. 287 * @return {number} The index. 288 * @private 289 */ 290jspb.Message.getIndex_ = function(msg, fieldNumber) { 291 return fieldNumber + msg.arrayIndexOffset_; 292}; 293 294 295/** 296 * Initializes a JsPb Message. 297 * @param {!jspb.Message} msg The JsPb proto to modify. 298 * @param {Array|undefined} data An initial data array. 299 * @param {string|number} messageId For response messages, the message id or '' 300 * if no message id is specified. For non-response messages, 0. 301 * @param {number} suggestedPivot The field number at which to start putting 302 * fields into the extension object. This is only used if data does not 303 * contain an extension object already. -1 if no extension object is 304 * required for this message type. 305 * @param {Array<number>} repeatedFields The message's repeated fields. 306 * @param {Array<!Array<number>>=} opt_oneofFields The fields belonging to 307 * each of the message's oneof unions. 308 * @protected 309 */ 310jspb.Message.initialize = function( 311 msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) { 312 msg.wrappers_ = jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? null : {}; 313 if (!data) { 314 data = messageId ? [messageId] : []; 315 } 316 msg.messageId_ = messageId ? String(messageId) : undefined; 317 // If the messageId is 0, this message is not a response message, so we shift 318 // array indices down by 1 so as not to waste the first position in the array, 319 // which would otherwise go unused. 320 msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; 321 msg.array = data; 322 jspb.Message.materializeExtensionObject_(msg, suggestedPivot); 323 msg.convertedFloatingPointFields_ = {}; 324 325 if (repeatedFields) { 326 for (var i = 0; i < repeatedFields.length; i++) { 327 var fieldNumber = repeatedFields[i]; 328 if (fieldNumber < msg.pivot_) { 329 var index = jspb.Message.getIndex_(msg, fieldNumber); 330 msg.array[index] = msg.array[index] || 331 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? 332 jspb.Message.EMPTY_LIST_SENTINEL_ : 333 []); 334 } else { 335 msg.extensionObject_[fieldNumber] = 336 msg.extensionObject_[fieldNumber] || 337 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? 338 jspb.Message.EMPTY_LIST_SENTINEL_ : 339 []); 340 } 341 } 342 } 343 344 if (opt_oneofFields && opt_oneofFields.length) { 345 // Compute the oneof case for each union. This ensures only one value is 346 // set in the union. 347 goog.array.forEach( 348 opt_oneofFields, goog.partial(jspb.Message.computeOneofCase, msg)); 349 } 350}; 351 352 353/** 354 * Used to mark empty repeated fields. Serializes to null when serialized 355 * to JSON. 356 * When reading a repeated field readers must check the return value against 357 * this value and return and replace it with a new empty array if it is 358 * present. 359 * @private @const {!Object} 360 */ 361jspb.Message.EMPTY_LIST_SENTINEL_ = goog.DEBUG && Object.freeze ? 362 Object.freeze([]) : 363 []; 364 365 366/** 367 * Ensures that the array contains an extension object if necessary. 368 * If the array contains an extension object in its last position, then the 369 * object is kept in place and its position is used as the pivot. If not, then 370 * create an extension object using suggestedPivot. If suggestedPivot is -1, 371 * we don't have an extension object at all, in which case all fields are stored 372 * in the array. 373 * @param {!jspb.Message} msg The JsPb proto to modify. 374 * @param {number} suggestedPivot See description for initialize(). 375 * @private 376 */ 377jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { 378 if (msg.array.length) { 379 var foundIndex = msg.array.length - 1; 380 var obj = msg.array[foundIndex]; 381 // Normal fields are never objects, so we can be sure that if we find an 382 // object here, then it's the extension object. However, we must ensure that 383 // the object is not an array, since arrays are valid field values. 384 // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug 385 // in Safari on iOS 8. See the description of CL/86511464 for details. 386 if (obj && typeof obj == 'object' && !goog.isArray(obj) && 387 !(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) { 388 msg.pivot_ = foundIndex - msg.arrayIndexOffset_; 389 msg.extensionObject_ = obj; 390 return; 391 } 392 } 393 // This complexity exists because we keep all extension fields in the 394 // extensionObject_ regardless of proto field number. Changing this would 395 // simplify the code here, but it would require changing the serialization 396 // format from the server, which is not backwards compatible. 397 // TODO(jshneier): Should we just treat extension fields the same as 398 // non-extension fields, and select whether they appear in the object or in 399 // the array purely based on tag number? This would allow simplifying all the 400 // get/setExtension logic, but it would require the breaking change described 401 // above. 402 if (suggestedPivot > -1) { 403 msg.pivot_ = suggestedPivot; 404 var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot); 405 if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { 406 msg.extensionObject_ = msg.array[pivotIndex] = {}; 407 } else { 408 // Initialize to null to avoid changing the shape of the proto when it 409 // gets eventually set. 410 msg.extensionObject_ = null; 411 } 412 } else { 413 msg.pivot_ = Number.MAX_VALUE; 414 } 415}; 416 417 418/** 419 * Creates an empty extensionObject_ if non exists. 420 * @param {!jspb.Message} msg The JsPb proto to modify. 421 * @private 422 */ 423jspb.Message.maybeInitEmptyExtensionObject_ = function(msg) { 424 var pivotIndex = jspb.Message.getIndex_(msg, msg.pivot_); 425 if (!msg.array[pivotIndex]) { 426 msg.extensionObject_ = msg.array[pivotIndex] = {}; 427 } 428}; 429 430 431/** 432 * Converts a JsPb repeated message field into an object list. 433 * @param {!Array<T>} field The repeated message field to be 434 * converted. 435 * @param {?function(boolean=): Object| 436 * function((boolean|undefined),T): Object} toObjectFn The toObject 437 * function for this field. We need to pass this for effective dead code 438 * removal. 439 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 440 * for transitional soy proto support: http://goto/soy-param-migration 441 * @return {!Array<Object>} An array of converted message objects. 442 * @template T 443 */ 444jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) { 445 // Not using goog.array.map in the generated code to keep it small. 446 // And not using it here to avoid a function call. 447 var result = []; 448 for (var i = 0; i < field.length; i++) { 449 result[i] = toObjectFn.call(field[i], opt_includeInstance, 450 /** @type {!jspb.Message} */ (field[i])); 451 } 452 return result; 453}; 454 455 456/** 457 * Adds a proto's extension data to a Soy rendering object. 458 * @param {!jspb.Message} proto The proto whose extensions to convert. 459 * @param {!Object} obj The Soy object to add converted extension data to. 460 * @param {!Object} extensions The proto class' registered extensions. 461 * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn 462 * The proto class' getExtension function. Passed for effective dead code 463 * removal. 464 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 465 * for transitional soy proto support: http://goto/soy-param-migration 466 */ 467jspb.Message.toObjectExtension = function(proto, obj, extensions, 468 getExtensionFn, opt_includeInstance) { 469 for (var fieldNumber in extensions) { 470 var fieldInfo = extensions[fieldNumber]; 471 var value = getExtensionFn.call(proto, fieldInfo); 472 if (value) { 473 for (var name in fieldInfo.fieldName) { 474 if (fieldInfo.fieldName.hasOwnProperty(name)) { 475 break; // the compiled field name 476 } 477 } 478 if (!fieldInfo.toObjectFn) { 479 obj[name] = value; 480 } else { 481 if (fieldInfo.isRepeated) { 482 obj[name] = jspb.Message.toObjectList( 483 /** @type {!Array<jspb.Message>} */ (value), 484 fieldInfo.toObjectFn, opt_includeInstance); 485 } else { 486 obj[name] = fieldInfo.toObjectFn(opt_includeInstance, value); 487 } 488 } 489 } 490 } 491}; 492 493 494/** 495 * Writes a proto's extension data to a binary-format output stream. 496 * @param {!jspb.Message} proto The proto whose extensions to convert. 497 * @param {*} writer The binary-format writer to write to. 498 * @param {!Object} extensions The proto class' registered extensions. 499 * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto 500 * class' getExtension function. Passed for effective dead code removal. 501 */ 502jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, 503 getExtensionFn) { 504 for (var fieldNumber in extensions) { 505 var binaryFieldInfo = extensions[fieldNumber]; 506 var fieldInfo = binaryFieldInfo.fieldInfo; 507 508 // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we 509 // need to gracefully error-out here rather than produce a null dereference 510 // below. 511 if (!binaryFieldInfo.binaryWriterFn) { 512 throw new Error('Message extension present that was generated ' + 513 'without binary serialization support'); 514 } 515 var value = getExtensionFn.call(proto, fieldInfo); 516 if (value) { 517 if (fieldInfo.isMessageType()) { 518 // If the message type of the extension was generated without binary 519 // support, there may not be a binary message serializer function, and 520 // we can't know when we codegen the extending message that the extended 521 // message may require binary support, so we can *only* catch this error 522 // here, at runtime (and this decoupled codegen is the whole point of 523 // extensions!). 524 if (binaryFieldInfo.binaryMessageSerializeFn) { 525 binaryFieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex, 526 value, binaryFieldInfo.binaryMessageSerializeFn); 527 } else { 528 throw new Error('Message extension present holding submessage ' + 529 'without binary support enabled, and message is ' + 530 'being serialized to binary format'); 531 } 532 } else { 533 binaryFieldInfo.binaryWriterFn.call( 534 writer, fieldInfo.fieldIndex, value); 535 } 536 } 537 } 538}; 539 540 541/** 542 * Reads an extension field from the given reader and, if a valid extension, 543 * sets the extension value. 544 * @param {!jspb.Message} msg A jspb proto. 545 * @param {{skipField:function(),getFieldNumber:function():number}} reader 546 * @param {!Object} extensions The extensions object. 547 * @param {function(jspb.ExtensionFieldInfo)} getExtensionFn 548 * @param {function(jspb.ExtensionFieldInfo, ?)} setExtensionFn 549 */ 550jspb.Message.readBinaryExtension = function(msg, reader, extensions, 551 getExtensionFn, setExtensionFn) { 552 var binaryFieldInfo = extensions[reader.getFieldNumber()]; 553 var fieldInfo = binaryFieldInfo.fieldInfo; 554 if (!binaryFieldInfo) { 555 reader.skipField(); 556 return; 557 } 558 if (!binaryFieldInfo.binaryReaderFn) { 559 throw new Error('Deserializing extension whose generated code does not ' + 560 'support binary format'); 561 } 562 563 var value; 564 if (fieldInfo.isMessageType()) { 565 value = new fieldInfo.ctor(); 566 binaryFieldInfo.binaryReaderFn.call( 567 reader, value, binaryFieldInfo.binaryMessageDeserializeFn); 568 } else { 569 // All other types. 570 value = binaryFieldInfo.binaryReaderFn.call(reader); 571 } 572 573 if (fieldInfo.isRepeated && !binaryFieldInfo.isPacked) { 574 var currentList = getExtensionFn.call(msg, fieldInfo); 575 if (!currentList) { 576 setExtensionFn.call(msg, fieldInfo, [value]); 577 } else { 578 currentList.push(value); 579 } 580 } else { 581 setExtensionFn.call(msg, fieldInfo, value); 582 } 583}; 584 585 586/** 587 * Gets the value of a non-extension field. 588 * @param {!jspb.Message} msg A jspb proto. 589 * @param {number} fieldNumber The field number. 590 * @return {string|number|boolean|Uint8Array|Array|null|undefined} 591 * The field's value. 592 * @protected 593 */ 594jspb.Message.getField = function(msg, fieldNumber) { 595 if (fieldNumber < msg.pivot_) { 596 var index = jspb.Message.getIndex_(msg, fieldNumber); 597 var val = msg.array[index]; 598 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { 599 return msg.array[index] = []; 600 } 601 return val; 602 } else { 603 var val = msg.extensionObject_[fieldNumber]; 604 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { 605 return msg.extensionObject_[fieldNumber] = []; 606 } 607 return val; 608 } 609}; 610 611 612/** 613 * Gets the value of an optional float or double field. 614 * @param {!jspb.Message} msg A jspb proto. 615 * @param {number} fieldNumber The field number. 616 * @return {?number|undefined} The field's value. 617 * @protected 618 */ 619jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) { 620 var value = jspb.Message.getField(msg, fieldNumber); 621 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers. 622 return value == null ? value : +value; 623}; 624 625 626/** 627 * Gets the value of a repeated float or double field. 628 * @param {!jspb.Message} msg A jspb proto. 629 * @param {number} fieldNumber The field number. 630 * @return {!Array<number>} The field's value. 631 * @protected 632 */ 633jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) { 634 var values = jspb.Message.getField(msg, fieldNumber); 635 if (!msg.convertedFloatingPointFields_) { 636 msg.convertedFloatingPointFields_ = {}; 637 } 638 if (!msg.convertedFloatingPointFields_[fieldNumber]) { 639 for (var i = 0; i < values.length; i++) { 640 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding 641 // numbers. 642 values[i] = +values[i]; 643 } 644 msg.convertedFloatingPointFields_[fieldNumber] = true; 645 } 646 return /** @type {!Array<number>} */ (values); 647}; 648 649 650/** 651 * Coerce a 'bytes' field to a base 64 string. 652 * @param {string|Uint8Array|null} value 653 * @return {?string} The field's coerced value. 654 */ 655jspb.Message.bytesAsB64 = function(value) { 656 if (value == null || goog.isString(value)) { 657 return value; 658 } 659 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) { 660 return goog.crypt.base64.encodeByteArray(value); 661 } 662 goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value)); 663 return null; 664}; 665 666 667/** 668 * Coerce a 'bytes' field to a Uint8Array byte buffer. 669 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera 670 * Mini. @see http://caniuse.com/Uint8Array 671 * @param {string|Uint8Array|null} value 672 * @return {?Uint8Array} The field's coerced value. 673 */ 674jspb.Message.bytesAsU8 = function(value) { 675 if (value == null || value instanceof Uint8Array) { 676 return value; 677 } 678 if (goog.isString(value)) { 679 return goog.crypt.base64.decodeStringToUint8Array(value); 680 } 681 goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value)); 682 return null; 683}; 684 685 686/** 687 * Coerce a repeated 'bytes' field to an array of base 64 strings. 688 * Note: the returned array should be treated as immutable. 689 * @param {!Array<string>|!Array<!Uint8Array>} value 690 * @return {!Array<string?>} The field's coerced value. 691 */ 692jspb.Message.bytesListAsB64 = function(value) { 693 jspb.Message.assertConsistentTypes_(value); 694 if (!value.length || goog.isString(value[0])) { 695 return /** @type {!Array<string>} */ (value); 696 } 697 return goog.array.map(value, jspb.Message.bytesAsB64); 698}; 699 700 701/** 702 * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers. 703 * Note: the returned array should be treated as immutable. 704 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera 705 * Mini. @see http://caniuse.com/Uint8Array 706 * @param {!Array<string>|!Array<!Uint8Array>} value 707 * @return {!Array<Uint8Array?>} The field's coerced value. 708 */ 709jspb.Message.bytesListAsU8 = function(value) { 710 jspb.Message.assertConsistentTypes_(value); 711 if (!value.length || value[0] instanceof Uint8Array) { 712 return /** @type {!Array<!Uint8Array>} */ (value); 713 } 714 return goog.array.map(value, jspb.Message.bytesAsU8); 715}; 716 717 718/** 719 * Asserts that all elements of an array are of the same type. 720 * @param {Array?} array The array to test. 721 * @private 722 */ 723jspb.Message.assertConsistentTypes_ = function(array) { 724 if (goog.DEBUG && array && array.length > 1) { 725 var expected = goog.typeOf(array[0]); 726 goog.array.forEach(array, function(e) { 727 if (goog.typeOf(e) != expected) { 728 goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' + 729 'Got ' + goog.typeOf(e) + ' expected ' + expected); 730 } 731 }); 732 } 733}; 734 735 736/** 737 * Gets the value of a non-extension primitive field, with proto3 (non-nullable 738 * primitives) semantics. Returns `defaultValue` if the field is not otherwise 739 * set. 740 * @template T 741 * @param {!jspb.Message} msg A jspb proto. 742 * @param {number} fieldNumber The field number. 743 * @param {T} defaultValue The default value. 744 * @return {T} The field's value. 745 * @protected 746 */ 747jspb.Message.getFieldProto3 = function(msg, fieldNumber, defaultValue) { 748 var value = jspb.Message.getField(msg, fieldNumber); 749 if (value == null) { 750 return defaultValue; 751 } else { 752 return value; 753 } 754}; 755 756 757/** 758 * Gets the value of a map field, lazily creating the map container if 759 * necessary. 760 * 761 * This should only be called from generated code, because it requires knowledge 762 * of serialization/parsing callbacks (which are required by the map at 763 * construction time, and the map may be constructed here). 764 * 765 * @template K, V 766 * @param {!jspb.Message} msg 767 * @param {number} fieldNumber 768 * @param {boolean|undefined} noLazyCreate 769 * @param {?=} opt_valueCtor 770 * @return {!jspb.Map<K, V>|undefined} 771 * @protected 772 */ 773jspb.Message.getMapField = function(msg, fieldNumber, noLazyCreate, 774 opt_valueCtor) { 775 if (!msg.wrappers_) { 776 msg.wrappers_ = {}; 777 } 778 // If we already have a map in the map wrappers, return that. 779 if (fieldNumber in msg.wrappers_) { 780 return msg.wrappers_[fieldNumber]; 781 } else if (noLazyCreate) { 782 return undefined; 783 } else { 784 // Wrap the underlying elements array with a Map. 785 var arr = jspb.Message.getField(msg, fieldNumber); 786 if (!arr) { 787 arr = []; 788 jspb.Message.setField(msg, fieldNumber, arr); 789 } 790 return msg.wrappers_[fieldNumber] = 791 new jspb.Map( 792 /** @type {!Array<!Array<!Object>>} */ (arr), opt_valueCtor); 793 } 794}; 795 796 797/** 798 * Sets the value of a non-extension field. 799 * @param {!jspb.Message} msg A jspb proto. 800 * @param {number} fieldNumber The field number. 801 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value 802 * @protected 803 */ 804jspb.Message.setField = function(msg, fieldNumber, value) { 805 if (fieldNumber < msg.pivot_) { 806 msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; 807 } else { 808 msg.extensionObject_[fieldNumber] = value; 809 } 810}; 811 812 813/** 814 * Sets the value of a field in a oneof union and clears all other fields in 815 * the union. 816 * @param {!jspb.Message} msg A jspb proto. 817 * @param {number} fieldNumber The field number. 818 * @param {!Array<number>} oneof The fields belonging to the union. 819 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value 820 * @protected 821 */ 822jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) { 823 var currentCase = jspb.Message.computeOneofCase(msg, oneof); 824 if (currentCase && currentCase !== fieldNumber && value !== undefined) { 825 if (msg.wrappers_ && currentCase in msg.wrappers_) { 826 msg.wrappers_[currentCase] = undefined; 827 } 828 jspb.Message.setField(msg, currentCase, undefined); 829 } 830 jspb.Message.setField(msg, fieldNumber, value); 831}; 832 833 834/** 835 * Computes the selection in a oneof group for the given message, ensuring 836 * only one field is set in the process. 837 * 838 * According to the protobuf language guide ( 839 * https://developers.google.com/protocol-buffers/docs/proto#oneof), "if the 840 * parser encounters multiple members of the same oneof on the wire, only the 841 * last member seen is used in the parsed message." Since JSPB serializes 842 * messages to a JSON array, the "last member seen" will always be the field 843 * with the greatest field number (directly corresponding to the greatest 844 * array index). 845 * 846 * @param {!jspb.Message} msg A jspb proto. 847 * @param {!Array<number>} oneof The field numbers belonging to the union. 848 * @return {number} The field number currently set in the union, or 0 if none. 849 * @protected 850 */ 851jspb.Message.computeOneofCase = function(msg, oneof) { 852 var oneofField; 853 var oneofValue; 854 855 goog.array.forEach(oneof, function(fieldNumber) { 856 var value = jspb.Message.getField(msg, fieldNumber); 857 if (goog.isDefAndNotNull(value)) { 858 oneofField = fieldNumber; 859 oneofValue = value; 860 jspb.Message.setField(msg, fieldNumber, undefined); 861 } 862 }); 863 864 if (oneofField) { 865 // NB: We know the value is unique, so we can call jspb.Message.setField 866 // directly instead of jpsb.Message.setOneofField. Also, setOneofField 867 // calls this function. 868 jspb.Message.setField(msg, oneofField, oneofValue); 869 return oneofField; 870 } 871 872 return 0; 873}; 874 875 876/** 877 * Gets and wraps a proto field on access. 878 * @param {!jspb.Message} msg A jspb proto. 879 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. 880 * @param {number} fieldNumber The field number. 881 * @param {number=} opt_required True (1) if this is a required field. 882 * @return {jspb.Message} The field as a jspb proto. 883 * @protected 884 */ 885jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) { 886 // TODO(mwr): Consider copying data and/or arrays. 887 if (!msg.wrappers_) { 888 msg.wrappers_ = {}; 889 } 890 if (!msg.wrappers_[fieldNumber]) { 891 var data = /** @type {Array} */ (jspb.Message.getField(msg, fieldNumber)); 892 if (opt_required || data) { 893 // TODO(mwr): Remove existence test for always valid default protos. 894 msg.wrappers_[fieldNumber] = new ctor(data); 895 } 896 } 897 return /** @type {jspb.Message} */ (msg.wrappers_[fieldNumber]); 898}; 899 900 901/** 902 * Gets and wraps a repeated proto field on access. 903 * @param {!jspb.Message} msg A jspb proto. 904 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field. 905 * @param {number} fieldNumber The field number. 906 * @return {Array<!jspb.Message>} The repeated field as an array of protos. 907 * @protected 908 */ 909jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) { 910 if (!msg.wrappers_) { 911 msg.wrappers_ = {}; 912 } 913 if (!msg.wrappers_[fieldNumber]) { 914 var data = jspb.Message.getField(msg, fieldNumber); 915 for (var wrappers = [], i = 0; i < data.length; i++) { 916 wrappers[i] = new ctor(data[i]); 917 } 918 msg.wrappers_[fieldNumber] = wrappers; 919 } 920 var val = msg.wrappers_[fieldNumber]; 921 if (val == jspb.Message.EMPTY_LIST_SENTINEL_) { 922 val = msg.wrappers_[fieldNumber] = []; 923 } 924 return /** @type {Array<!jspb.Message>} */ (val); 925}; 926 927 928/** 929 * Sets a proto field and syncs it to the backing array. 930 * @param {!jspb.Message} msg A jspb proto. 931 * @param {number} fieldNumber The field number. 932 * @param {jspb.Message|undefined} value A new value for this proto field. 933 * @protected 934 */ 935jspb.Message.setWrapperField = function(msg, fieldNumber, value) { 936 if (!msg.wrappers_) { 937 msg.wrappers_ = {}; 938 } 939 var data = value ? value.toArray() : value; 940 msg.wrappers_[fieldNumber] = value; 941 jspb.Message.setField(msg, fieldNumber, data); 942}; 943 944 945/** 946 * Sets a proto field in a oneof union and syncs it to the backing array. 947 * @param {!jspb.Message} msg A jspb proto. 948 * @param {number} fieldNumber The field number. 949 * @param {!Array<number>} oneof The fields belonging to the union. 950 * @param {jspb.Message|undefined} value A new value for this proto field. 951 * @protected 952 */ 953jspb.Message.setOneofWrapperField = function(msg, fieldNumber, oneof, value) { 954 if (!msg.wrappers_) { 955 msg.wrappers_ = {}; 956 } 957 var data = value ? value.toArray() : value; 958 msg.wrappers_[fieldNumber] = value; 959 jspb.Message.setOneofField(msg, fieldNumber, oneof, data); 960}; 961 962 963/** 964 * Sets a repeated proto field and syncs it to the backing array. 965 * @param {!jspb.Message} msg A jspb proto. 966 * @param {number} fieldNumber The field number. 967 * @param {Array<!jspb.Message>|undefined} value An array of protos. 968 * @protected 969 */ 970jspb.Message.setRepeatedWrapperField = function(msg, fieldNumber, value) { 971 if (!msg.wrappers_) { 972 msg.wrappers_ = {}; 973 } 974 value = value || []; 975 for (var data = [], i = 0; i < value.length; i++) { 976 data[i] = value[i].toArray(); 977 } 978 msg.wrappers_[fieldNumber] = value; 979 jspb.Message.setField(msg, fieldNumber, data); 980}; 981 982 983/** 984 * Converts a JsPb repeated message field into a map. The map will contain 985 * protos unless an optional toObject function is given, in which case it will 986 * contain objects suitable for Soy rendering. 987 * @param {!Array<T>} field The repeated message field to be 988 * converted. 989 * @param {function() : string?} mapKeyGetterFn The function to get the key of 990 * the map. 991 * @param {?function(boolean=): Object| 992 * function((boolean|undefined),T): Object} opt_toObjectFn The 993 * toObject function for this field. We need to pass this for effective 994 * dead code removal. 995 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance 996 * for transitional soy proto support: http://goto/soy-param-migration 997 * @return {!Object.<string, Object>} A map of proto or Soy objects. 998 * @template T 999 */ 1000jspb.Message.toMap = function( 1001 field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) { 1002 var result = {}; 1003 for (var i = 0; i < field.length; i++) { 1004 result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ? 1005 opt_toObjectFn.call(field[i], opt_includeInstance, 1006 /** @type {!jspb.Message} */ (field[i])) : field[i]; 1007 } 1008 return result; 1009}; 1010 1011 1012/** 1013 * Syncs all map fields' contents back to their underlying arrays. 1014 * @private 1015 */ 1016jspb.Message.prototype.syncMapFields_ = function() { 1017 // This iterates over submessage, map, and repeated fields, which is intended. 1018 // Submessages can contain maps which also need to be synced. 1019 // 1020 // There is a lot of opportunity for optimization here. For example we could 1021 // statically determine that some messages have no submessages with maps and 1022 // optimize this method away for those just by generating one extra static 1023 // boolean per message type. 1024 if (this.wrappers_) { 1025 for (var fieldNumber in this.wrappers_) { 1026 var val = this.wrappers_[fieldNumber]; 1027 if (goog.isArray(val)) { 1028 for (var i = 0; i < val.length; i++) { 1029 if (val[i]) { 1030 val[i].toArray(); 1031 } 1032 } 1033 } else { 1034 // Works for submessages and maps. 1035 if (val) { 1036 val.toArray(); 1037 } 1038 } 1039 } 1040 } 1041}; 1042 1043 1044/** 1045 * Returns the internal array of this proto. 1046 * <p>Note: If you use this array to construct a second proto, the content 1047 * would then be partially shared between the two protos. 1048 * @return {!Array} The proto represented as an array. 1049 */ 1050jspb.Message.prototype.toArray = function() { 1051 this.syncMapFields_(); 1052 return this.array; 1053}; 1054 1055 1056 1057 1058/** 1059 * Creates a string representation of the internal data array of this proto. 1060 * <p>NOTE: This string is *not* suitable for use in server requests. 1061 * @return {string} A string representation of this proto. 1062 * @override 1063 */ 1064jspb.Message.prototype.toString = function() { 1065 this.syncMapFields_(); 1066 return this.array.toString(); 1067}; 1068 1069 1070/** 1071 * Gets the value of the extension field from the extended object. 1072 * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get. 1073 * @return {T} The value of the field. 1074 * @template T 1075 */ 1076jspb.Message.prototype.getExtension = function(fieldInfo) { 1077 if (!this.extensionObject_) { 1078 return undefined; 1079 } 1080 if (!this.wrappers_) { 1081 this.wrappers_ = {}; 1082 } 1083 var fieldNumber = fieldInfo.fieldIndex; 1084 if (fieldInfo.isRepeated) { 1085 if (fieldInfo.isMessageType()) { 1086 if (!this.wrappers_[fieldNumber]) { 1087 this.wrappers_[fieldNumber] = 1088 goog.array.map(this.extensionObject_[fieldNumber] || [], 1089 function(arr) { 1090 return new fieldInfo.ctor(arr); 1091 }); 1092 } 1093 return this.wrappers_[fieldNumber]; 1094 } else { 1095 return this.extensionObject_[fieldNumber]; 1096 } 1097 } else { 1098 if (fieldInfo.isMessageType()) { 1099 if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) { 1100 this.wrappers_[fieldNumber] = new fieldInfo.ctor( 1101 /** @type {Array|undefined} */ ( 1102 this.extensionObject_[fieldNumber])); 1103 } 1104 return this.wrappers_[fieldNumber]; 1105 } else { 1106 return this.extensionObject_[fieldNumber]; 1107 } 1108 } 1109}; 1110 1111 1112/** 1113 * Sets the value of the extension field in the extended object. 1114 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set. 1115 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value 1116 * to set. 1117 */ 1118jspb.Message.prototype.setExtension = function(fieldInfo, value) { 1119 if (!this.wrappers_) { 1120 this.wrappers_ = {}; 1121 } 1122 jspb.Message.maybeInitEmptyExtensionObject_(this); 1123 var fieldNumber = fieldInfo.fieldIndex; 1124 if (fieldInfo.isRepeated) { 1125 value = value || []; 1126 if (fieldInfo.isMessageType()) { 1127 this.wrappers_[fieldNumber] = value; 1128 this.extensionObject_[fieldNumber] = goog.array.map( 1129 /** @type {Array<jspb.Message>} */ (value), function(msg) { 1130 return msg.toArray(); 1131 }); 1132 } else { 1133 this.extensionObject_[fieldNumber] = value; 1134 } 1135 } else { 1136 if (fieldInfo.isMessageType()) { 1137 this.wrappers_[fieldNumber] = value; 1138 this.extensionObject_[fieldNumber] = value ? value.toArray() : value; 1139 } else { 1140 this.extensionObject_[fieldNumber] = value; 1141 } 1142 } 1143}; 1144 1145 1146/** 1147 * Creates a difference object between two messages. 1148 * 1149 * The result will contain the top-level fields of m2 that differ from those of 1150 * m1 at any level of nesting. No data is cloned, the result object will 1151 * share its top-level elements with m2 (but not with m1). 1152 * 1153 * Note that repeated fields should not have null/undefined elements, but if 1154 * they do, this operation will treat repeated fields of different length as 1155 * the same if the only difference between them is due to trailing 1156 * null/undefined values. 1157 * 1158 * @param {!jspb.Message} m1 The first message object. 1159 * @param {!jspb.Message} m2 The second message object. 1160 * @return {!jspb.Message} The difference returned as a proto message. 1161 * Note that the returned message may be missing required fields. This is 1162 * currently tolerated in Js, but would cause an error if you tried to 1163 * send such a proto to the server. You can access the raw difference 1164 * array with result.toArray(). 1165 * @throws {Error} If the messages are responses with different types. 1166 */ 1167jspb.Message.difference = function(m1, m2) { 1168 if (!(m1 instanceof m2.constructor)) { 1169 throw new Error('Messages have different types.'); 1170 } 1171 var arr1 = m1.toArray(); 1172 var arr2 = m2.toArray(); 1173 var res = []; 1174 var start = 0; 1175 var length = arr1.length > arr2.length ? arr1.length : arr2.length; 1176 if (m1.getJsPbMessageId()) { 1177 res[0] = m1.getJsPbMessageId(); 1178 start = 1; 1179 } 1180 for (var i = start; i < length; i++) { 1181 if (!jspb.Message.compareFields(arr1[i], arr2[i])) { 1182 res[i] = arr2[i]; 1183 } 1184 } 1185 return new m1.constructor(res); 1186}; 1187 1188 1189/** 1190 * Tests whether two messages are equal. 1191 * @param {jspb.Message|undefined} m1 The first message object. 1192 * @param {jspb.Message|undefined} m2 The second message object. 1193 * @return {boolean} true if both messages are null/undefined, or if both are 1194 * of the same type and have the same field values. 1195 */ 1196jspb.Message.equals = function(m1, m2) { 1197 return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) && 1198 jspb.Message.compareFields(m1.toArray(), m2.toArray())); 1199}; 1200 1201 1202/** 1203 * Compares two message extension fields recursively. 1204 * @param {!Object} extension1 The first field. 1205 * @param {!Object} extension2 The second field. 1206 * @return {boolean} true if the extensions are null/undefined, or otherwise 1207 * equal. 1208 */ 1209jspb.Message.compareExtensions = function(extension1, extension2) { 1210 extension1 = extension1 || {}; 1211 extension2 = extension2 || {}; 1212 1213 var keys = {}; 1214 for (var name in extension1) { 1215 keys[name] = 0; 1216 } 1217 for (var name in extension2) { 1218 keys[name] = 0; 1219 } 1220 for (name in keys) { 1221 if (!jspb.Message.compareFields(extension1[name], extension2[name])) { 1222 return false; 1223 } 1224 } 1225 return true; 1226}; 1227 1228 1229/** 1230 * Compares two message fields recursively. 1231 * @param {*} field1 The first field. 1232 * @param {*} field2 The second field. 1233 * @return {boolean} true if the fields are null/undefined, or otherwise equal. 1234 */ 1235jspb.Message.compareFields = function(field1, field2) { 1236 // If the fields are trivially equal, they're equal. 1237 if (field1 == field2) return true; 1238 1239 // If the fields aren't trivially equal and one of them isn't an object, 1240 // they can't possibly be equal. 1241 if (!goog.isObject(field1) || !goog.isObject(field2)) { 1242 return false; 1243 } 1244 1245 // We have two objects. If they're different types, they're not equal. 1246 field1 = /** @type {!Object} */(field1); 1247 field2 = /** @type {!Object} */(field2); 1248 if (field1.constructor != field2.constructor) return false; 1249 1250 // If both are Uint8Arrays, compare them element-by-element. 1251 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) { 1252 var bytes1 = /** @type {!Uint8Array} */(field1); 1253 var bytes2 = /** @type {!Uint8Array} */(field2); 1254 if (bytes1.length != bytes2.length) return false; 1255 for (var i = 0; i < bytes1.length; i++) { 1256 if (bytes1[i] != bytes2[i]) return false; 1257 } 1258 return true; 1259 } 1260 1261 // If they're both Arrays, compare them element by element except for the 1262 // optional extension objects at the end, which we compare separately. 1263 if (field1.constructor === Array) { 1264 var extension1 = undefined; 1265 var extension2 = undefined; 1266 1267 var length = Math.max(field1.length, field2.length); 1268 for (var i = 0; i < length; i++) { 1269 var val1 = field1[i]; 1270 var val2 = field2[i]; 1271 1272 if (val1 && (val1.constructor == Object)) { 1273 goog.asserts.assert(extension1 === undefined); 1274 goog.asserts.assert(i === field1.length - 1); 1275 extension1 = val1; 1276 val1 = undefined; 1277 } 1278 1279 if (val2 && (val2.constructor == Object)) { 1280 goog.asserts.assert(extension2 === undefined); 1281 goog.asserts.assert(i === field2.length - 1); 1282 extension2 = val2; 1283 val2 = undefined; 1284 } 1285 1286 if (!jspb.Message.compareFields(val1, val2)) { 1287 return false; 1288 } 1289 } 1290 1291 if (extension1 || extension2) { 1292 extension1 = extension1 || {}; 1293 extension2 = extension2 || {}; 1294 return jspb.Message.compareExtensions(extension1, extension2); 1295 } 1296 1297 return true; 1298 } 1299 1300 // If they're both plain Objects (i.e. extensions), compare them as 1301 // extensions. 1302 if (field1.constructor === Object) { 1303 return jspb.Message.compareExtensions(field1, field2); 1304 } 1305 1306 throw new Error('Invalid type in JSPB array'); 1307}; 1308 1309 1310/** 1311 * Static clone function. NOTE: A type-safe method called "cloneMessage" exists 1312 * on each generated JsPb class. Do not call this function directly. 1313 * @param {!jspb.Message} msg A message to clone. 1314 * @return {!jspb.Message} A deep clone of the given message. 1315 */ 1316jspb.Message.clone = function(msg) { 1317 // Although we could include the wrappers, we leave them out here. 1318 return jspb.Message.cloneMessage(msg); 1319}; 1320 1321 1322/** 1323 * @param {!jspb.Message} msg A message to clone. 1324 * @return {!jspb.Message} A deep clone of the given message. 1325 * @protected 1326 */ 1327jspb.Message.cloneMessage = function(msg) { 1328 // Although we could include the wrappers, we leave them out here. 1329 return new msg.constructor(jspb.Message.clone_(msg.toArray())); 1330}; 1331 1332 1333/** 1334 * Takes 2 messages of the same type and copies the contents of the first 1335 * message into the second. After this the 2 messages will equals in terms of 1336 * value semantics but share no state. All data in the destination message will 1337 * be overridden. 1338 * 1339 * @param {MESSAGE} fromMessage Message that will be copied into toMessage. 1340 * @param {MESSAGE} toMessage Message which will receive a copy of fromMessage 1341 * as its contents. 1342 * @template MESSAGE 1343 */ 1344jspb.Message.copyInto = function(fromMessage, toMessage) { 1345 goog.asserts.assertInstanceof(fromMessage, jspb.Message); 1346 goog.asserts.assertInstanceof(toMessage, jspb.Message); 1347 goog.asserts.assert(fromMessage.constructor == toMessage.constructor, 1348 'Copy source and target message should have the same type.'); 1349 var copyOfFrom = jspb.Message.clone(fromMessage); 1350 1351 var to = toMessage.toArray(); 1352 var from = copyOfFrom.toArray(); 1353 1354 // Empty destination in case it has more values at the end of the array. 1355 to.length = 0; 1356 // and then copy everything from the new to the existing message. 1357 for (var i = 0; i < from.length; i++) { 1358 to[i] = from[i]; 1359 } 1360 1361 // This is either null or empty for a fresh copy. 1362 toMessage.wrappers_ = copyOfFrom.wrappers_; 1363 // Just a reference into the shared array. 1364 toMessage.extensionObject_ = copyOfFrom.extensionObject_; 1365}; 1366 1367 1368/** 1369 * Helper for cloning an internal JsPb object. 1370 * @param {!Object} obj A JsPb object, eg, a field, to be cloned. 1371 * @return {!Object} A clone of the input object. 1372 * @private 1373 */ 1374jspb.Message.clone_ = function(obj) { 1375 var o; 1376 if (goog.isArray(obj)) { 1377 // Allocate array of correct size. 1378 var clonedArray = new Array(obj.length); 1379 // Use array iteration where possible because it is faster than for-in. 1380 for (var i = 0; i < obj.length; i++) { 1381 if ((o = obj[i]) != null) { 1382 clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o; 1383 } 1384 } 1385 return clonedArray; 1386 } 1387 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) { 1388 return new Uint8Array(obj); 1389 } 1390 var clone = {}; 1391 for (var key in obj) { 1392 if ((o = obj[key]) != null) { 1393 clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o; 1394 } 1395 } 1396 return clone; 1397}; 1398 1399 1400/** 1401 * Registers a JsPb message type id with its constructor. 1402 * @param {string} id The id for this type of message. 1403 * @param {Function} constructor The message constructor. 1404 */ 1405jspb.Message.registerMessageType = function(id, constructor) { 1406 jspb.Message.registry_[id] = constructor; 1407 // This is needed so we can later access messageId directly on the contructor, 1408 // otherwise it is not available due to 'property collapsing' by the compiler. 1409 constructor.messageId = id; 1410}; 1411 1412 1413/** 1414 * The registry of message ids to message constructors. 1415 * @private 1416 */ 1417jspb.Message.registry_ = {}; 1418 1419 1420/** 1421 * The extensions registered on MessageSet. This is a map of extension 1422 * field number to field info object. This should be considered as a 1423 * private API. 1424 * 1425 * This is similar to [jspb class name].extensions object for 1426 * non-MessageSet. We special case MessageSet so that we do not need 1427 * to goog.require MessageSet from classes that extends MessageSet. 1428 * 1429 * @type {!Object.<number, jspb.ExtensionFieldInfo>} 1430 */ 1431jspb.Message.messageSetExtensions = {}; 1432