From c64d86ed4f3bf2c61b4a9b23719966b7a5dc9bf5 Mon Sep 17 00:00:00 2001 From: Adam Cozzette Date: Wed, 6 Jul 2016 12:04:49 -0700 Subject: [PATCH] Fixed failing JS tests A couple of small tweaks were needed to fix test failures that were inadvertently introduced in the last down integration. I also had to add the map.js file which was missing. --- js/map.js | 482 ++++++++++++++++++++++++++++++++++++++++++++ js/test.proto | 4 + js/testbinary.proto | 2 - 3 files changed, 486 insertions(+), 2 deletions(-) create mode 100644 js/map.js diff --git a/js/map.js b/js/map.js new file mode 100644 index 0000000000..821765ecf9 --- /dev/null +++ b/js/map.js @@ -0,0 +1,482 @@ +// Protocol Buffers - Google's data interchange format +// Copyright 2008 Google Inc. All rights reserved. +// https://developers.google.com/protocol-buffers/ +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +goog.provide('jspb.Map'); + +goog.require('goog.asserts'); + +goog.forwardDeclare('jspb.BinaryReader'); +goog.forwardDeclare('jspb.BinaryWriter'); + + + +/** + * Constructs a new Map. A Map is a container that is used to implement map + * fields on message objects. It closely follows the ES6 Map API; however, + * it is distinct because we do not want to depend on external polyfills or + * on ES6 itself. + * + * This constructor should only be called from generated message code. It is not + * intended for general use by library consumers. The callback function + * arguments are references to methods in `BinaryReader` and `BinaryWriter`, as + * well as constructors and reader/writer methods in submessage types if + * appropriate, that are used for binary serialization and parsing. + * + * @template K, V + * + * @param {!Array>} arr + * + * @param {function(this:jspb.BinaryWriter,number,K)=} opt_keyWriterFn + * The method on BinaryWriter that writes type K to the stream. + * + * @param {function(this:jspb.BinaryReader):K=} opt_keyReaderFn + * The method on BinaryReader that reads type K from the stream. + * + * @param {function(this:jspb.BinaryWriter,number,V)| + * function(this:jspb.BinaryReader,V,?)=} opt_valueWriterFn + * The method on BinaryWriter that writes type V to the stream. May be + * writeMessage, in which case the second callback arg form is used. + * + * @param {function(this:jspb.BinaryReader):V| + * function(this:jspb.BinaryReader,V, + * function(V,!jspb.BinaryReader))=} opt_valueReaderFn + * The method on BinaryReader that reads type V from the stream. May be + * readMessage, in which case the second callback arg form is used. + * + * @param {?function(new:V)|function(new:V,?)=} opt_valueCtor + * The constructor for type V, if type V is a message type. + * + * @param {?function(V,!jspb.BinaryWriter)=} opt_valueWriterCallback + * The BinaryWriter serialization callback for type V, if V is a message + * type. + * + * @param {?function(V,!jspb.BinaryReader)=} opt_valueReaderCallback + * The BinaryReader parsing callback for type V, if V is a message type. + * + * @constructor + * @struct + */ +jspb.Map = function( + arr, opt_keyWriterFn, opt_keyReaderFn, opt_valueWriterFn, opt_valueReaderFn, + opt_valueCtor, opt_valueWriterCallback, opt_valueReaderCallback) { + + /** @const @private */ + this.arr_ = arr; + /** @const @private */ + this.keyWriterFn_ = opt_keyWriterFn; + /** @const @private */ + this.keyReaderFn_ = opt_keyReaderFn; + /** @const @private */ + this.valueWriterFn_ = opt_valueWriterFn; + /** @const @private */ + this.valueReaderFn_ = opt_valueReaderFn; + /** @const @private */ + this.valueCtor_ = opt_valueCtor; + /** @const @private */ + this.valueWriterCallback_ = opt_valueWriterCallback; + /** @const @private */ + this.valueReaderCallback_ = opt_valueReaderCallback; + + /** @type {!Object>} @private */ + this.map_ = {}; + + /** + * Is `this.arr_ updated with respect to `this.map_`? + * @type {boolean} + */ + this.arrClean = true; + + if (this.arr_.length > 0) { + this.loadFromArray_(); + } +}; + + +/** + * Load initial content from underlying array. + * @private + */ +jspb.Map.prototype.loadFromArray_ = function() { + for (var i = 0; i < this.arr_.length; i++) { + var record = this.arr_[i]; + var key = record[0]; + var value = record[1]; + this.map_[key.toString()] = new jspb.Map.Entry_(key, value); + } + this.arrClean = true; +}; + + +/** + * Synchronize content to underlying array, if needed, and return it. + * @return {!Array>} + */ +jspb.Map.prototype.toArray = function() { + if (this.arrClean) { + if (this.valueCtor_) { + // We need to recursively sync maps in submessages to their arrays. + var m = this.map_; + for (var p in m) { + if (Object.prototype.hasOwnProperty.call(m, p)) { + m[p].valueWrapper.toArray(); + } + } + } + } else { + // Delete all elements. + this.arr_.length = 0; + var strKeys = this.stringKeys_(); + // Output keys in deterministic (sorted) order. + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + var valueWrapper = /** @type {!Object} */ (entry.valueWrapper); + if (valueWrapper) { + valueWrapper.toArray(); + } + this.arr_.push([entry.key, entry.value]); + } + this.arrClean = true; + } + return this.arr_; +}; + + +/** + * Helper: return an iterator over an array. + * @template T + * @param {!Array} arr the array + * @return {!Iterator} an iterator + * @private + */ +jspb.Map.arrayIterator_ = function(arr) { + var idx = 0; + return /** @type {!Iterator} */ ({ + next: function() { + if (idx < arr.length) { + return { done: false, value: arr[idx++] }; + } else { + return { done: true }; + } + } + }); +}; + + +/** + * Returns the map's length (number of key/value pairs). + * @return {number} + */ +jspb.Map.prototype.getLength = function() { + return this.stringKeys_().length; +}; + + +/** + * Clears the map. + */ +jspb.Map.prototype.clear = function() { + this.map_ = {}; + this.arrClean = false; +}; + + +/** + * Deletes a particular key from the map. + * N.B.: differs in name from ES6 Map's `delete` because IE8 does not support + * reserved words as property names. + * @this {jspb.Map} + * @param {K} key + * @return {boolean} Whether any entry with this key was deleted. + */ +jspb.Map.prototype.del = function(key) { + var keyValue = key.toString(); + var hadKey = this.map_.hasOwnProperty(keyValue); + delete this.map_[keyValue]; + this.arrClean = false; + return hadKey; +}; + + +/** + * Returns an array of [key, value] pairs in the map. + * + * This is redundant compared to the plain entries() method, but we provide this + * to help out Angular 1.x users. Still evaluating whether this is the best + * option. + * + * @return {!Array} + */ +jspb.Map.prototype.getEntryList = function() { + var entries = []; + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + entries.push([entry.key, entry.value]); + } + return entries; +}; + + +/** + * Returns an iterator over [key, value] pairs in the map. + * Closure compiler sadly doesn't support tuples, ie. Iterator<[K,V]>. + * @return {!Iterator>} + * The iterator + */ +jspb.Map.prototype.entries = function() { + var entries = []; + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + entries.push([entry.key, this.wrapEntry_(entry)]); + } + return jspb.Map.arrayIterator_(entries); +}; + + +/** + * Returns an iterator over keys in the map. + * @return {!Iterator} The iterator + */ +jspb.Map.prototype.keys = function() { + var keys = []; + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + keys.push(entry.key); + } + return jspb.Map.arrayIterator_(keys); +}; + + +/** + * Returns an iterator over values in the map. + * @return {!Iterator} The iterator + */ +jspb.Map.prototype.values = function() { + var values = []; + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + values.push(this.wrapEntry_(entry)); + } + return jspb.Map.arrayIterator_(values); +}; + + +/** + * Iterates over entries in the map, calling a function on each. + * @template T + * @param {function(this:T, V, K, ?jspb.Map)} cb + * @param {T=} opt_thisArg + */ +jspb.Map.prototype.forEach = function(cb, opt_thisArg) { + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + cb.call(opt_thisArg, this.wrapEntry_(entry), entry.key, this); + } +}; + + +/** + * Sets a key in the map to the given value. + * @param {K} key The key + * @param {V} value The value + * @return {!jspb.Map} + */ +jspb.Map.prototype.set = function(key, value) { + var entry = new jspb.Map.Entry_(key); + if (this.valueCtor_) { + entry.valueWrapper = value; + // .toArray() on a message returns a reference to the underlying array + // rather than a copy. + entry.value = value.toArray(); + } else { + entry.value = value; + } + this.map_[key.toString()] = entry; + this.arrClean = false; + return this; +}; + + +/** + * Helper: lazily construct a wrapper around an entry, if needed, and return the + * user-visible type. + * @param {!jspb.Map.Entry_} entry + * @return {V} + * @private + */ +jspb.Map.prototype.wrapEntry_ = function(entry) { + if (this.valueCtor_) { + if (!entry.valueWrapper) { + entry.valueWrapper = new this.valueCtor_(entry.value); + } + return /** @type {V} */ (entry.valueWrapper); + } else { + return entry.value; + } +}; + + +/** + * Gets the value corresponding to a key in the map. + * @param {K} key + * @return {V|undefined} The value, or `undefined` if key not present + */ +jspb.Map.prototype.get = function(key) { + var keyValue = key.toString(); + var entry = this.map_[keyValue]; + if (entry) { + return this.wrapEntry_(entry); + } else { + return undefined; + } +}; + + +/** + * Determines whether the given key is present in the map. + * @param {K} key + * @return {boolean} `true` if the key is present + */ +jspb.Map.prototype.has = function(key) { + var keyValue = key.toString(); + return (keyValue in this.map_); +}; + + +/** + * Write this Map field in wire format to a BinaryWriter, using the given field + * number. + * @param {number} fieldNumber + * @param {!jspb.BinaryWriter} writer + */ +jspb.Map.prototype.serializeBinary = function(fieldNumber, writer) { + var strKeys = this.stringKeys_(); + strKeys.sort(); + for (var i = 0; i < strKeys.length; i++) { + var entry = this.map_[strKeys[i]]; + writer.beginSubMessage(fieldNumber); + this.keyWriterFn_.call(writer, 1, entry.key); + if (this.valueCtor_) { + this.valueWriterFn_.call(writer, 2, this.wrapEntry_(entry), + this.valueWriterCallback_); + } else { + this.valueWriterFn_.call(writer, 2, entry.value); + } + writer.endSubMessage(); + } +}; + + +/** + * Read one key/value message from the given BinaryReader. Compatible as the + * `reader` callback parameter to jspb.BinaryReader.readMessage, to be called + * when a key/value pair submessage is encountered. + * @param {!jspb.Map} map + * @param {!jspb.BinaryReader} reader + */ +jspb.Map.deserializeBinary = function(map, reader) { + var key = undefined; + var value = undefined; + + while (reader.nextField()) { + if (reader.isEndGroup()) { + break; + } + var field = reader.getFieldNumber(); + if (field == 1) { + // Key. + key = map.keyReaderFn_.call(reader); + } else if (field == 2) { + // Value. + if (map.valueCtor_) { + value = new map.valueCtor_(); + map.valueReaderFn_.call(reader, value, map.valueReaderCallback_); + } else { + value = map.valueReaderFn_.call(reader); + } + } + } + + goog.asserts.assert(key != undefined); + goog.asserts.assert(value != undefined); + map.set(key, value); +}; + + +/** + * Helper: compute the list of all stringified keys in the underlying Object + * map. + * @return {!Array} + * @private + */ +jspb.Map.prototype.stringKeys_ = function() { + var m = this.map_; + var ret = []; + for (var p in m) { + if (Object.prototype.hasOwnProperty.call(m, p)) { + ret.push(p); + } + } + return ret; +}; + + + +/** + * @param {!K} key The entry's key. + * @param {V=} opt_value The entry's value wrapper. + * @constructor + * @struct + * @template K, V + * @private + */ +jspb.Map.Entry_ = function(key, opt_value) { + /** @const {K} */ + this.key = key; + + // The JSPB-serializable value. For primitive types this will be of type V. + // For message types it will be an array. + /** @type {V} */ + this.value = opt_value; + + // Only used for submessage values. + /** @type {V} */ + this.valueWrapper = undefined; +}; diff --git a/js/test.proto b/js/test.proto index 06eb79af58..cf2eafef6e 100644 --- a/js/test.proto +++ b/js/test.proto @@ -229,3 +229,7 @@ message TestMessageWithOneof { } } +message TestEndsWithBytes { + optional int32 value = 1; + optional bytes data = 2; +} diff --git a/js/testbinary.proto b/js/testbinary.proto index a3fcb5f12c..116f17fb50 100644 --- a/js/testbinary.proto +++ b/js/testbinary.proto @@ -185,8 +185,6 @@ extend TestExtendable { } message TestMapFields { - option (jspb.generate_from_object) = true; - map map_string_string = 1; map map_string_int32 = 2; map map_string_int64 = 3;