From 09354db1434859a31a3c81abebcc4018d42f2715 Mon Sep 17 00:00:00 2001 From: Jisi Liu Date: Tue, 18 Jul 2017 15:38:30 -0700 Subject: Merge from Google internal for 3.4 release --- js/message.js | 86 ++++++++++++++++++++++++++++++++++++----------------------- 1 file changed, 53 insertions(+), 33 deletions(-) (limited to 'js/message.js') diff --git a/js/message.js b/js/message.js index 1f6bf16b..f7dcfa96 100644 --- a/js/message.js +++ b/js/message.js @@ -41,7 +41,6 @@ goog.provide('jspb.Message'); goog.require('goog.array'); goog.require('goog.asserts'); goog.require('goog.crypt.base64'); -goog.require('goog.json'); goog.require('jspb.Map'); // Not needed in compilation units that have no protos with xids. @@ -192,8 +191,8 @@ goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE); /** * @define {boolean} Whether to generate toString methods for objects. Turn - * this off if you do use toString in your project and want to trim it from - * compiled JS. + * this off if you do not use toString in your project and want to trim it + * from the compiled JS. */ goog.define('jspb.Message.GENERATE_TO_STRING', true); @@ -351,7 +350,7 @@ jspb.Message.initialize = function( // which would otherwise go unused. msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0; msg.array = data; - jspb.Message.materializeExtensionObject_(msg, suggestedPivot); + jspb.Message.initPivotAndExtensionObject_(msg, suggestedPivot); msg.convertedFloatingPointFields_ = {}; if (repeatedFields) { @@ -364,6 +363,7 @@ jspb.Message.initialize = function( jspb.Message.EMPTY_LIST_SENTINEL_ : []); } else { + jspb.Message.maybeInitEmptyExtensionObject_(msg); msg.extensionObject_[fieldNumber] = msg.extensionObject_[fieldNumber] || (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? @@ -408,17 +408,16 @@ jspb.Message.isArray_ = function(o) { /** - * Ensures that the array contains an extension object if necessary. * If the array contains an extension object in its last position, then the - * object is kept in place and its position is used as the pivot. If not, then - * create an extension object using suggestedPivot. If suggestedPivot is -1, - * we don't have an extension object at all, in which case all fields are stored - * in the array. + * object is kept in place and its position is used as the pivot. If not, + * decides the pivot of the message based on suggestedPivot without + * materializing the extension object. + * * @param {!jspb.Message} msg The JsPb proto to modify. * @param {number} suggestedPivot See description for initialize(). * @private */ -jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { +jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) { if (msg.array.length) { var foundIndex = msg.array.length - 1; var obj = msg.array[foundIndex]; @@ -434,26 +433,17 @@ jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) { return; } } - // This complexity exists because we keep all extension fields in the - // extensionObject_ regardless of proto field number. Changing this would - // simplify the code here, but it would require changing the serialization - // format from the server, which is not backwards compatible. - // TODO(jshneier): Should we just treat extension fields the same as - // non-extension fields, and select whether they appear in the object or in - // the array purely based on tag number? This would allow simplifying all the - // get/setExtension logic, but it would require the breaking change described - // above. + if (suggestedPivot > -1) { msg.pivot_ = suggestedPivot; - var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot); - if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) { - msg.extensionObject_ = msg.array[pivotIndex] = {}; - } else { - // Initialize to null to avoid changing the shape of the proto when it - // gets eventually set. - msg.extensionObject_ = null; - } + // Avoid changing the shape of the proto with an empty extension object by + // deferring the materialization of the extension object until the first + // time a field set into it (may be due to getting a repeated proto field + // from it, in which case a new empty array is set into it at first). + msg.extensionObject_ = null; } else { + // suggestedPivot is -1, which means that we don't have an extension object + // at all, in which case all fields are stored in the array. msg.pivot_ = Number.MAX_VALUE; } }; @@ -513,7 +503,7 @@ jspb.Message.toObjectExtension = function(proto, obj, extensions, for (var fieldNumber in extensions) { var fieldInfo = extensions[fieldNumber]; var value = getExtensionFn.call(proto, fieldInfo); - if (goog.isDefAndNotNull(value)) { + if (value != null) { for (var name in fieldInfo.fieldName) { if (fieldInfo.fieldName.hasOwnProperty(name)) { break; // the compiled field name @@ -557,7 +547,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions, 'without binary serialization support'); } var value = getExtensionFn.call(proto, fieldInfo); - if (goog.isDefAndNotNull(value)) { + if (value != null) { if (fieldInfo.isMessageType()) { // If the message type of the extension was generated without binary // support, there may not be a binary message serializer function, and @@ -647,6 +637,9 @@ jspb.Message.getField = function(msg, fieldNumber) { } return val; } else { + if (!msg.extensionObject_) { + return undefined; + } var val = msg.extensionObject_[fieldNumber]; if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { return msg.extensionObject_[fieldNumber] = []; @@ -656,6 +649,32 @@ jspb.Message.getField = function(msg, fieldNumber) { }; +/** + * Gets the value of a non-extension repeated field. + * @param {!jspb.Message} msg A jspb proto. + * @param {number} fieldNumber The field number. + * @return {!Array} + * The field's value. + * @protected + */ +jspb.Message.getRepeatedField = function(msg, fieldNumber) { + if (fieldNumber < msg.pivot_) { + var index = jspb.Message.getIndex_(msg, fieldNumber); + var val = msg.array[index]; + if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { + return msg.array[index] = []; + } + return val; + } + + var val = msg.extensionObject_[fieldNumber]; + if (val === jspb.Message.EMPTY_LIST_SENTINEL_) { + return msg.extensionObject_[fieldNumber] = []; + } + return val; +}; + + /** * Gets the value of an optional float or double field. * @param {!jspb.Message} msg A jspb proto. @@ -678,7 +697,7 @@ jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) { * @protected */ jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) { - var values = jspb.Message.getField(msg, fieldNumber); + var values = jspb.Message.getRepeatedField(msg, fieldNumber); if (!msg.convertedFloatingPointFields_) { msg.convertedFloatingPointFields_ = {}; } @@ -864,6 +883,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) { if (fieldNumber < msg.pivot_) { msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value; } else { + jspb.Message.maybeInitEmptyExtensionObject_(msg); msg.extensionObject_[fieldNumber] = value; } }; @@ -878,7 +898,7 @@ jspb.Message.setField = function(msg, fieldNumber, value) { * @protected */ jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) { - var arr = jspb.Message.getField(msg, fieldNumber); + var arr = jspb.Message.getRepeatedField(msg, fieldNumber); if (opt_index != undefined) { arr.splice(opt_index, 0, value); } else { @@ -1006,7 +1026,7 @@ jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) { msg.wrappers_ = {}; } if (!msg.wrappers_[fieldNumber]) { - var data = jspb.Message.getField(msg, fieldNumber); + var data = jspb.Message.getRepeatedField(msg, fieldNumber); for (var wrappers = [], i = 0; i < data.length; i++) { wrappers[i] = new ctor(data[i]); } @@ -1101,7 +1121,7 @@ jspb.Message.addToRepeatedWrapperField = function( wrapperArray = msg.wrappers_[fieldNumber] = []; } var insertedValue = value ? value : new ctor(); - var array = jspb.Message.getField(msg, fieldNumber); + var array = jspb.Message.getRepeatedField(msg, fieldNumber); if (index != undefined) { wrapperArray.splice(index, 0, insertedValue); array.splice(index, 0, insertedValue.toArray()); -- cgit v1.2.3