From 0400cca3236de1ca303af38bf81eab332d042b7c Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Tue, 13 Mar 2018 16:37:29 -0700 Subject: Integrated internal changes from Google --- js/binary/constants.js | 19 +++++++++-- js/binary/decoder.js | 2 +- js/binary/writer.js | 6 ++-- js/binary/writer_test.js | 12 +++++++ js/debug.js | 2 +- js/map.js | 16 ++++++---- js/message.js | 82 ++++++++++++++++++++++++++---------------------- js/message_test.js | 12 +++++++ 8 files changed, 100 insertions(+), 51 deletions(-) (limited to 'js') diff --git a/js/binary/constants.js b/js/binary/constants.js index 75a8a52c..21c5889c 100644 --- a/js/binary/constants.js +++ b/js/binary/constants.js @@ -60,14 +60,25 @@ goog.forwardDeclare('jsproto.BinaryExtension'); /** - * Base interface class for all const messages. Does __not__ define any - * methods, as doing so on a widely-used interface defeats dead-code - * elimination. + * Base interface class for all const messages. * @interface */ jspb.ConstBinaryMessage = function() {}; +/** + * Generate a debug string for this proto that is in proto2 text format. + * @return {string} The debug string. + */ +jspb.ConstBinaryMessage.prototype.toDebugString; +/** + * Helper to generate a debug string for this proto at some indent level. The + * first line is not indented. + * @param {number} indentLevel The number of spaces by which to indent lines. + * @return {string} The debug string. + * @protected + */ +jspb.ConstBinaryMessage.prototype.toDebugStringInternal; /** * Base interface class for all messages. Does __not__ define any methods, as @@ -97,6 +108,7 @@ jspb.ScalarFieldType; * A repeated field in jspb is an array of scalars, blobs, or messages. * @typedef {!Array| !Array| + !Array| !Array} */ jspb.RepeatedFieldType; @@ -108,6 +120,7 @@ jspb.RepeatedFieldType; * @typedef {jspb.ScalarFieldType| jspb.RepeatedFieldType| !Uint8Array| + !jspb.ConstBinaryMessage| !jspb.BinaryMessage| !jsproto.BinaryExtension} */ diff --git a/js/binary/decoder.js b/js/binary/decoder.js index d0c0bc17..e33bf1be 100644 --- a/js/binary/decoder.js +++ b/js/binary/decoder.js @@ -986,7 +986,7 @@ jspb.BinaryDecoder.prototype.readString = function(length) { codeUnits.push(high, low); } - // Avoid exceeding the maximum stack size when calling {@code apply}. + // Avoid exceeding the maximum stack size when calling `apply`. if (codeUnits.length >= 8192) { result += String.fromCharCode.apply(null, codeUnits); codeUnits.length = 0; diff --git a/js/binary/writer.js b/js/binary/writer.js index 8a018058..287d29c3 100644 --- a/js/binary/writer.js +++ b/js/binary/writer.js @@ -236,10 +236,12 @@ jspb.BinaryWriter.prototype.getResultBuffer = function() { /** * Converts the encoded data into a base64-encoded string. + * @param {boolean=} opt_webSafe True indicates we should use a websafe + * alphabet, which does not require escaping for use in URLs. * @return {string} */ -jspb.BinaryWriter.prototype.getResultBase64String = function() { - return goog.crypt.base64.encodeByteArray(this.getResultBuffer()); +jspb.BinaryWriter.prototype.getResultBase64String = function(opt_webSafe) { + return goog.crypt.base64.encodeByteArray(this.getResultBuffer(), opt_webSafe); }; diff --git a/js/binary/writer_test.js b/js/binary/writer_test.js index 118eecfc..8a9a1bb0 100644 --- a/js/binary/writer_test.js +++ b/js/binary/writer_test.js @@ -118,4 +118,16 @@ describe('binaryWriterTest', function() { var buffer = writer.getResultBuffer(); assertEquals(expected, goog.crypt.byteArrayToHex(buffer)); }); + + + /** + * Tests websafe encodings for base64 strings. + */ + it('testWebSafeOption', function() { + var writer = new jspb.BinaryWriter(); + writer.writeBytes(1, new Uint8Array([127])); + assertEquals('CgF/', writer.getResultBase64String()); + assertEquals('CgF/', writer.getResultBase64String(false)); + assertEquals('CgF_', writer.getResultBase64String(true)); + }); }); diff --git a/js/debug.js b/js/debug.js index 4ae3c2a2..ba51bbe0 100644 --- a/js/debug.js +++ b/js/debug.js @@ -42,7 +42,7 @@ goog.require('jspb.Message'); /** * Turns a proto into a human readable object that can i.e. be written to the - * console: {@code console.log(jspb.debug.dump(myProto))}. + * console: `console.log(jspb.debug.dump(myProto))`. * This function makes a best effort and may not work in all cases. It will not * work in obfuscated and or optimized code. * Use this in environments where {@see jspb.Message.prototype.toObject} is diff --git a/js/map.js b/js/map.js index d423499f..7b5b2c38 100644 --- a/js/map.js +++ b/js/map.js @@ -48,9 +48,9 @@ goog.forwardDeclare('jspb.BinaryWriter'); * * @template K, V * - * @param {!Array>} arr + * @param {!Array>} arr * - * @param {?function(new:V)|function(new:V,?)=} opt_valueCtor + * @param {?function(new:V, ?=)=} opt_valueCtor * The constructor for type V, if type V is a message type. * * @constructor @@ -118,7 +118,7 @@ jspb.Map.prototype.toArray = function() { strKeys.sort(); for (var i = 0; i < strKeys.length; i++) { var entry = this.map_[strKeys[i]]; - var valueWrapper = /** @type {!Object} */ (entry.valueWrapper); + var valueWrapper = /** @type {?jspb.Message} */ (entry.valueWrapper); if (valueWrapper) { valueWrapper.toArray(); } @@ -165,7 +165,7 @@ jspb.Map.prototype.toObject = function(includeInstance, valueToObject) { * * @template K, V * @param {!Array>} entries - * @param {!function(new:V)|function(new:V,?)} valueCtor + * @param {!function(new:V,?=)} valueCtor * The constructor for type V. * @param {!function(!Object):V} valueFromObject * The fromObject function for type V. @@ -432,7 +432,8 @@ jspb.Map.prototype.serializeBinary = function( valueWriterFn.call(writer, 2, this.wrapEntry_(entry), opt_valueWriterCallback); } else { - valueWriterFn.call(writer, 2, entry.value); + /** @type {function(this:jspb.BinaryWriter,number,?)} */ (valueWriterFn) + .call(writer, 2, entry.value); } writer.endSubMessage(); } @@ -475,10 +476,13 @@ jspb.Map.deserializeBinary = function(map, reader, keyReaderFn, valueReaderFn, } else if (field == 2) { // Value. if (map.valueCtor_) { + goog.asserts.assert(opt_valueReaderCallback); value = new map.valueCtor_(); valueReaderFn.call(reader, value, opt_valueReaderCallback); } else { - value = valueReaderFn.call(reader); + value = + (/** @type {function(this:jspb.BinaryReader):?} */ (valueReaderFn)) + .call(reader); } } } diff --git a/js/message.js b/js/message.js index 8f68cbb4..04d43339 100644 --- a/js/message.js +++ b/js/message.js @@ -215,17 +215,6 @@ goog.define('jspb.Message.ASSUME_LOCAL_ARRAYS', false); goog.define('jspb.Message.SERIALIZE_EMPTY_TRAILING_FIELDS', true); -/** - * @define {boolean} Turning on this flag does NOT change the behavior of JSPB - * and only affects private internal state. It may, however, break some - * tests that use naive deeply-equals algorithms, because using a proto - * mutates its internal state. - * Projects are advised to turn this flag always on. - */ -goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', true); -// TODO(b/19419436): Delete this flag. - - /** * Does this JavaScript environment support Uint8Aray typed arrays? * @type {boolean} @@ -369,7 +358,7 @@ jspb.Message.getFieldNumber_ = function(msg, index) { */ jspb.Message.initialize = function( msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) { - msg.wrappers_ = jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? null : {}; + msg.wrappers_ = null; if (!data) { data = messageId ? [messageId] : []; } @@ -394,17 +383,12 @@ jspb.Message.initialize = function( var fieldNumber = repeatedFields[i]; if (fieldNumber < msg.pivot_) { var index = jspb.Message.getIndex_(msg, fieldNumber); - msg.array[index] = msg.array[index] || - (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? - jspb.Message.EMPTY_LIST_SENTINEL_ : - []); + msg.array[index] = + msg.array[index] || jspb.Message.EMPTY_LIST_SENTINEL_; } else { jspb.Message.maybeInitEmptyExtensionObject_(msg); - msg.extensionObject_[fieldNumber] = - msg.extensionObject_[fieldNumber] || - (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? - jspb.Message.EMPTY_LIST_SENTINEL_ : - []); + msg.extensionObject_[fieldNumber] = msg.extensionObject_[fieldNumber] || + jspb.Message.EMPTY_LIST_SENTINEL_; } } } @@ -517,8 +501,7 @@ jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) { // And not using it here to avoid a function call. var result = []; for (var i = 0; i < field.length; i++) { - result[i] = toObjectFn.call(field[i], opt_includeInstance, - /** @type {!jspb.Message} */ (field[i])); + result[i] = toObjectFn.call(field[i], opt_includeInstance, field[i]); } return result; }; @@ -551,10 +534,11 @@ jspb.Message.toObjectExtension = function(proto, obj, extensions, } else { if (fieldInfo.isRepeated) { obj[name] = jspb.Message.toObjectList( - /** @type {!Array} */ (value), + /** @type {!Array} */ (value), fieldInfo.toObjectFn, opt_includeInstance); } else { - obj[name] = fieldInfo.toObjectFn(opt_includeInstance, value); + obj[name] = fieldInfo.toObjectFn( + opt_includeInstance, /** @type {!jspb.Message} */ (value)); } } } @@ -1419,7 +1403,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) { if (fieldInfo.isMessageType()) { self.wrappers_[fieldNumber] = value; self.extensionObject_[fieldNumber] = goog.array.map( - /** @type {Array} */ (value), function(msg) { + /** @type {!Array} */ (value), function(msg) { return msg.toArray(); }); } else { @@ -1428,7 +1412,8 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) { } else { if (fieldInfo.isMessageType()) { self.wrappers_[fieldNumber] = value; - self.extensionObject_[fieldNumber] = value ? value.toArray() : value; + self.extensionObject_[fieldNumber] = + value ? /** @type {!jspb.Message} */ (value).toArray() : value; } else { self.extensionObject_[fieldNumber] = value; } @@ -1530,9 +1515,15 @@ jspb.Message.compareFields = function(field1, field2) { // If the fields are trivially equal, they're equal. if (field1 == field2) return true; - // If the fields aren't trivially equal and one of them isn't an object, - // they can't possibly be equal. if (!goog.isObject(field1) || !goog.isObject(field2)) { + // NaN != NaN so we cover this case. + if ((goog.isNumber(field1) && isNaN(field1)) || + (goog.isNumber(field2) && isNaN(field2))) { + // One of the fields might be a string 'NaN'. + return String(field1) == String(field2); + } + // If the fields aren't trivially equal and one of them isn't an object, + // they can't possibly be equal. return false; } @@ -1555,24 +1546,26 @@ jspb.Message.compareFields = function(field1, field2) { // If they're both Arrays, compare them element by element except for the // optional extension objects at the end, which we compare separately. if (field1.constructor === Array) { + var typedField1 = /** @type {!Array} */ (field1); + var typedField2 = /** @type {!Array} */ (field2); var extension1 = undefined; var extension2 = undefined; - var length = Math.max(field1.length, field2.length); + var length = Math.max(typedField1.length, typedField2.length); for (var i = 0; i < length; i++) { - var val1 = field1[i]; - var val2 = field2[i]; + var val1 = typedField1[i]; + var val2 = typedField2[i]; if (val1 && (val1.constructor == Object)) { goog.asserts.assert(extension1 === undefined); - goog.asserts.assert(i === field1.length - 1); + goog.asserts.assert(i === typedField1.length - 1); extension1 = val1; val1 = undefined; } if (val2 && (val2.constructor == Object)) { goog.asserts.assert(extension2 === undefined); - goog.asserts.assert(i === field2.length - 1); + goog.asserts.assert(i === typedField2.length - 1); extension2 = val2; val2 = undefined; } @@ -1695,8 +1688,13 @@ jspb.Message.clone_ = function(obj) { var clonedArray = new Array(obj.length); // Use array iteration where possible because it is faster than for-in. for (var i = 0; i < obj.length; i++) { - if ((o = obj[i]) != null) { - clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o; + o = obj[i]; + if (o != null) { + // NOTE:redundant null check existing for NTI compatibility. + // see b/70515949 + clonedArray[i] = (typeof o == 'object') ? + jspb.Message.clone_(goog.asserts.assert(o)) : + o; } } return clonedArray; @@ -1706,8 +1704,13 @@ jspb.Message.clone_ = function(obj) { } var clone = {}; for (var key in obj) { - if ((o = obj[key]) != null) { - clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o; + o = obj[key]; + if (o != null) { + // NOTE:redundant null check existing for NTI compatibility. + // see b/70515949 + clone[key] = (typeof o == 'object') ? + jspb.Message.clone_(goog.asserts.assert(o)) : + o; } } return clone; @@ -1723,6 +1726,9 @@ jspb.Message.registerMessageType = function(id, constructor) { jspb.Message.registry_[id] = constructor; // This is needed so we can later access messageId directly on the contructor, // otherwise it is not available due to 'property collapsing' by the compiler. + /** + * @suppress {strictMissingProperties} messageId is not defined on Function + */ constructor.messageId = id; }; diff --git a/js/message_test.js b/js/message_test.js index 0acebb75..1be41093 100644 --- a/js/message_test.js +++ b/js/message_test.js @@ -418,6 +418,18 @@ describe('Message test suite', function() { ['hi',,, {100: [{200: 'a'}]}], ['hi', {100: [{200: 'a'}]}])); }); + it('testEqualsNonFinite', function() { + assertTrue(jspb.Message.compareFields(NaN, NaN)); + assertTrue(jspb.Message.compareFields(NaN, 'NaN')); + assertTrue(jspb.Message.compareFields('NaN', NaN)); + assertTrue(jspb.Message.compareFields(Infinity, Infinity)); + assertTrue(jspb.Message.compareFields(Infinity, 'Infinity')); + assertTrue(jspb.Message.compareFields('-Infinity', -Infinity)); + assertTrue(jspb.Message.compareFields([NaN], ['NaN'])); + assertFalse(jspb.Message.compareFields(undefined, NaN)); + assertFalse(jspb.Message.compareFields(NaN, undefined)); + }); + it('testToMap', function() { var p1 = new proto.jspb.test.Simple1(['k', ['v']]); var p2 = new proto.jspb.test.Simple1(['k1', ['v1', 'v2']]); -- cgit v1.2.3