| Index: third_party/protobuf/js/message.js
|
| diff --git a/third_party/protobuf/js/message.js b/third_party/protobuf/js/message.js
|
| index cef9aefd32365ad14cef1186660bbb0f49d3fee9..813e6b8cd1d2f02a083c89892211fe4b15d95b0a 100644
|
| --- a/third_party/protobuf/js/message.js
|
| +++ b/third_party/protobuf/js/message.js
|
| @@ -39,8 +39,8 @@ goog.provide('jspb.Message');
|
|
|
| goog.require('goog.array');
|
| goog.require('goog.asserts');
|
| +goog.require('goog.crypt.base64');
|
| goog.require('goog.json');
|
| -goog.require('goog.object');
|
|
|
| // Not needed in compilation units that have no protos with xids.
|
| goog.forwardDeclare('xid.String');
|
| @@ -120,6 +120,14 @@ jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn,
|
|
|
|
|
| /**
|
| + * @return {boolean} Does this field represent a sub Message?
|
| + */
|
| +jspb.ExtensionFieldInfo.prototype.isMessageType = function() {
|
| + return !!this.ctor;
|
| +};
|
| +
|
| +
|
| +/**
|
| * Base class for all JsPb messages.
|
| * @constructor
|
| * @struct
|
| @@ -166,6 +174,14 @@ goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED);
|
|
|
|
|
| /**
|
| + * Does this browser support Uint8Aray typed arrays?
|
| + * @type {boolean}
|
| + * @private
|
| + */
|
| +jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function');
|
| +
|
| +
|
| +/**
|
| * The internal data array.
|
| * @type {!Array}
|
| * @protected
|
| @@ -211,6 +227,14 @@ jspb.Message.prototype.messageId_;
|
|
|
|
|
| /**
|
| + * Repeated float or double fields which have been converted to include only
|
| + * numbers and not strings holding "NaN", "Infinity" and "-Infinity".
|
| + * @private {!Object<number,boolean>|undefined}
|
| + */
|
| +jspb.Message.prototype.convertedFloatingPointFields_;
|
| +
|
| +
|
| +/**
|
| * The xid of this proto type (The same for all instances of a proto). Provides
|
| * a way to identify a proto by stable obfuscated name.
|
| * @see {xid}.
|
| @@ -284,6 +308,8 @@ jspb.Message.initialize = function(
|
| msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
|
| msg.array = data;
|
| jspb.Message.materializeExtensionObject_(msg, suggestedPivot);
|
| + msg.convertedFloatingPointFields_ = {};
|
| +
|
| if (repeatedFields) {
|
| for (var i = 0; i < repeatedFields.length; i++) {
|
| var fieldNumber = repeatedFields[i];
|
| @@ -419,8 +445,9 @@ jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) {
|
| * @param {!jspb.Message} proto The proto whose extensions to convert.
|
| * @param {!Object} obj The Soy object to add converted extension data to.
|
| * @param {!Object} extensions The proto class' registered extensions.
|
| - * @param {function(jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
|
| - * class' getExtension function. Passed for effective dead code removal.
|
| + * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn
|
| + * The proto class' getExtension function. Passed for effective dead code
|
| + * removal.
|
| * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
|
| * for transitional soy proto support: http://goto/soy-param-migration
|
| */
|
| @@ -472,7 +499,7 @@ jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
|
| }
|
| var value = getExtensionFn.call(proto, fieldInfo);
|
| if (value) {
|
| - if (fieldInfo.ctor) { // is this a message type?
|
| + 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
|
| // we can't know when we codegen the extending message that the extended
|
| @@ -517,8 +544,7 @@ jspb.Message.readBinaryExtension = function(msg, reader, extensions,
|
| }
|
|
|
| var value;
|
| - if (fieldInfo.ctor) {
|
| - // Message type.
|
| + if (fieldInfo.isMessageType()) {
|
| value = new fieldInfo.ctor();
|
| fieldInfo.binaryReaderFn.call(
|
| reader, value, fieldInfo.binaryMessageDeserializeFn);
|
| @@ -567,6 +593,130 @@ jspb.Message.getField = function(msg, fieldNumber) {
|
|
|
|
|
| /**
|
| + * Gets the value of an optional float or double field.
|
| + * @param {!jspb.Message} msg A jspb proto.
|
| + * @param {number} fieldNumber The field number.
|
| + * @return {?number|undefined} The field's value.
|
| + * @protected
|
| + */
|
| +jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) {
|
| + var value = jspb.Message.getField(msg, fieldNumber);
|
| + // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers.
|
| + return value == null ? value : +value;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Gets the value of a repeated float or double field.
|
| + * @param {!jspb.Message} msg A jspb proto.
|
| + * @param {number} fieldNumber The field number.
|
| + * @return {!Array<number>} The field's value.
|
| + * @protected
|
| + */
|
| +jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
|
| + var values = jspb.Message.getField(msg, fieldNumber);
|
| + if (!msg.convertedFloatingPointFields_) {
|
| + msg.convertedFloatingPointFields_ = {};
|
| + }
|
| + if (!msg.convertedFloatingPointFields_[fieldNumber]) {
|
| + for (var i = 0; i < values.length; i++) {
|
| + // Converts "NaN", "Infinity" and "-Infinity" to their corresponding
|
| + // numbers.
|
| + values[i] = +values[i];
|
| + }
|
| + msg.convertedFloatingPointFields_[fieldNumber] = true;
|
| + }
|
| + return /** @type {!Array<number>} */ (values);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Coerce a 'bytes' field to a base 64 string.
|
| + * @param {string|Uint8Array|null} value
|
| + * @return {?string} The field's coerced value.
|
| + */
|
| +jspb.Message.bytesAsB64 = function(value) {
|
| + if (value == null || goog.isString(value)) {
|
| + return value;
|
| + }
|
| + if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) {
|
| + return goog.crypt.base64.encodeByteArray(value);
|
| + }
|
| + goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value));
|
| + return null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Coerce a 'bytes' field to a Uint8Array byte buffer.
|
| + * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
|
| + * Mini. @see http://caniuse.com/Uint8Array
|
| + * @param {string|Uint8Array|null} value
|
| + * @return {?Uint8Array} The field's coerced value.
|
| + */
|
| +jspb.Message.bytesAsU8 = function(value) {
|
| + if (value == null || value instanceof Uint8Array) {
|
| + return value;
|
| + }
|
| + if (goog.isString(value)) {
|
| + return goog.crypt.base64.decodeStringToUint8Array(value);
|
| + }
|
| + goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value));
|
| + return null;
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Coerce a repeated 'bytes' field to an array of base 64 strings.
|
| + * Note: the returned array should be treated as immutable.
|
| + * @param {!Array<string>|!Array<!Uint8Array>} value
|
| + * @return {!Array<string?>} The field's coerced value.
|
| + */
|
| +jspb.Message.bytesListAsB64 = function(value) {
|
| + jspb.Message.assertConsistentTypes_(value);
|
| + if (!value.length || goog.isString(value[0])) {
|
| + return /** @type {!Array<string>} */ (value);
|
| + }
|
| + return goog.array.map(value, jspb.Message.bytesAsB64);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers.
|
| + * Note: the returned array should be treated as immutable.
|
| + * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
|
| + * Mini. @see http://caniuse.com/Uint8Array
|
| + * @param {!Array<string>|!Array<!Uint8Array>} value
|
| + * @return {!Array<Uint8Array?>} The field's coerced value.
|
| + */
|
| +jspb.Message.bytesListAsU8 = function(value) {
|
| + jspb.Message.assertConsistentTypes_(value);
|
| + if (!value.length || value[0] instanceof Uint8Array) {
|
| + return /** @type {!Array<!Uint8Array>} */ (value);
|
| + }
|
| + return goog.array.map(value, jspb.Message.bytesAsU8);
|
| +};
|
| +
|
| +
|
| +/**
|
| + * Asserts that all elements of an array are of the same type.
|
| + * @param {Array?} array The array to test.
|
| + * @private
|
| + */
|
| +jspb.Message.assertConsistentTypes_ = function(array) {
|
| + if (goog.DEBUG && array && array.length > 1) {
|
| + var expected = goog.typeOf(array[0]);
|
| + goog.array.forEach(array, function(e) {
|
| + if (goog.typeOf(e) != expected) {
|
| + goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' +
|
| + 'Got ' + goog.typeOf(e) + ' expected ' + expected);
|
| + }
|
| + });
|
| + }
|
| +};
|
| +
|
| +
|
| +/**
|
| * Gets the value of a non-extension primitive field, with proto3 (non-nullable
|
| * primitives) semantics. Returns `defaultValue` if the field is not otherwise
|
| * set.
|
| @@ -841,7 +991,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
| }
|
| var fieldNumber = fieldInfo.fieldIndex;
|
| if (fieldInfo.isRepeated) {
|
| - if (fieldInfo.ctor) {
|
| + if (fieldInfo.isMessageType()) {
|
| if (!this.wrappers_[fieldNumber]) {
|
| this.wrappers_[fieldNumber] =
|
| goog.array.map(this.extensionObject_[fieldNumber] || [],
|
| @@ -854,7 +1004,7 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
| return this.extensionObject_[fieldNumber];
|
| }
|
| } else {
|
| - if (fieldInfo.ctor) {
|
| + if (fieldInfo.isMessageType()) {
|
| if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) {
|
| this.wrappers_[fieldNumber] = new fieldInfo.ctor(
|
| /** @type {Array|undefined} */ (
|
| @@ -871,7 +1021,8 @@ jspb.Message.prototype.getExtension = function(fieldInfo) {
|
| /**
|
| * Sets the value of the extension field in the extended object.
|
| * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set.
|
| - * @param {jspb.Message|string|number|boolean|Array} value The value to set.
|
| + * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value
|
| + * to set.
|
| */
|
| jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
| if (!this.wrappers_) {
|
| @@ -881,7 +1032,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
| var fieldNumber = fieldInfo.fieldIndex;
|
| if (fieldInfo.isRepeated) {
|
| value = value || [];
|
| - if (fieldInfo.ctor) {
|
| + if (fieldInfo.isMessageType()) {
|
| this.wrappers_[fieldNumber] = value;
|
| this.extensionObject_[fieldNumber] = goog.array.map(
|
| /** @type {Array<jspb.Message>} */ (value), function(msg) {
|
| @@ -891,7 +1042,7 @@ jspb.Message.prototype.setExtension = function(fieldInfo, value) {
|
| this.extensionObject_[fieldNumber] = value;
|
| }
|
| } else {
|
| - if (fieldInfo.ctor) {
|
| + if (fieldInfo.isMessageType()) {
|
| this.wrappers_[fieldNumber] = value;
|
| this.extensionObject_[fieldNumber] = value ? value.toArray() : value;
|
| } else {
|
| @@ -958,49 +1109,110 @@ jspb.Message.equals = function(m1, m2) {
|
|
|
|
|
| /**
|
| + * Compares two message extension fields recursively.
|
| + * @param {!Object} extension1 The first field.
|
| + * @param {!Object} extension2 The second field.
|
| + * @return {boolean} true if the extensions are null/undefined, or otherwise
|
| + * equal.
|
| + */
|
| +jspb.Message.compareExtensions = function(extension1, extension2) {
|
| + extension1 = extension1 || {};
|
| + extension2 = extension2 || {};
|
| +
|
| + var keys = {};
|
| + for (var name in extension1) {
|
| + keys[name] = 0;
|
| + }
|
| + for (var name in extension2) {
|
| + keys[name] = 0;
|
| + }
|
| + for (name in keys) {
|
| + if (!jspb.Message.compareFields(extension1[name], extension2[name])) {
|
| + return false;
|
| + }
|
| + }
|
| + return true;
|
| +};
|
| +
|
| +
|
| +/**
|
| * Compares two message fields recursively.
|
| * @param {*} field1 The first field.
|
| * @param {*} field2 The second field.
|
| * @return {boolean} true if the fields are null/undefined, or otherwise equal.
|
| */
|
| jspb.Message.compareFields = function(field1, field2) {
|
| - if (goog.isObject(field1) && goog.isObject(field2)) {
|
| - var keys = {}, name, extensionObject1, extensionObject2;
|
| - for (name in field1) {
|
| - field1.hasOwnProperty(name) && (keys[name] = 0);
|
| - }
|
| - for (name in field2) {
|
| - field2.hasOwnProperty(name) && (keys[name] = 0);
|
| + // 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)) {
|
| + return false;
|
| + }
|
| +
|
| + // We have two objects. If they're different types, they're not equal.
|
| + field1 = /** @type {!Object} */(field1);
|
| + field2 = /** @type {!Object} */(field2);
|
| + if (field1.constructor != field2.constructor) return false;
|
| +
|
| + // If both are Uint8Arrays, compare them element-by-element.
|
| + if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) {
|
| + var bytes1 = /** @type {!Uint8Array} */(field1);
|
| + var bytes2 = /** @type {!Uint8Array} */(field2);
|
| + if (bytes1.length != bytes2.length) return false;
|
| + for (var i = 0; i < bytes1.length; i++) {
|
| + if (bytes1[i] != bytes2[i]) return false;
|
| }
|
| - for (name in keys) {
|
| - var val1 = field1[name], val2 = field2[name];
|
| - if (goog.isObject(val1) && !goog.isArray(val1)) {
|
| - if (extensionObject1 !== undefined) {
|
| - throw new Error('invalid jspb state');
|
| - }
|
| - extensionObject1 = goog.object.isEmpty(val1) ? undefined : val1;
|
| + return true;
|
| + }
|
| +
|
| + // 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 extension1 = undefined;
|
| + var extension2 = undefined;
|
| +
|
| + var length = Math.max(field1.length, field2.length);
|
| + for (var i = 0; i < length; i++) {
|
| + var val1 = field1[i];
|
| + var val2 = field2[i];
|
| +
|
| + if (val1 && (val1.constructor == Object)) {
|
| + goog.asserts.assert(extension1 === undefined);
|
| + goog.asserts.assert(i === field1.length - 1);
|
| + extension1 = val1;
|
| val1 = undefined;
|
| }
|
| - if (goog.isObject(val2) && !goog.isArray(val2)) {
|
| - if (extensionObject2 !== undefined) {
|
| - throw new Error('invalid jspb state');
|
| - }
|
| - extensionObject2 = goog.object.isEmpty(val2) ? undefined : val2;
|
| +
|
| + if (val2 && (val2.constructor == Object)) {
|
| + goog.asserts.assert(extension2 === undefined);
|
| + goog.asserts.assert(i === field2.length - 1);
|
| + extension2 = val2;
|
| val2 = undefined;
|
| }
|
| +
|
| if (!jspb.Message.compareFields(val1, val2)) {
|
| return false;
|
| }
|
| }
|
| - if (extensionObject1 || extensionObject2) {
|
| - return jspb.Message.compareFields(extensionObject1, extensionObject2);
|
| +
|
| + if (extension1 || extension2) {
|
| + extension1 = extension1 || {};
|
| + extension2 = extension2 || {};
|
| + return jspb.Message.compareExtensions(extension1, extension2);
|
| }
|
| +
|
| return true;
|
| }
|
| - // Primitive fields, null and undefined compare as equal.
|
| - // This also forces booleans and 0/1 to compare as equal to ensure
|
| - // compatibility with the jspb serializer.
|
| - return field1 == field2;
|
| +
|
| + // If they're both plain Objects (i.e. extensions), compare them as
|
| + // extensions.
|
| + if (field1.constructor === Object) {
|
| + return jspb.Message.compareExtensions(field1, field2);
|
| + }
|
| +
|
| + throw new Error('Invalid type in JSPB array');
|
| };
|
|
|
|
|
|
|