blob: 1f6bf16b6e768dc9f2465e384dd0128652616ccb [file] [log] [blame]
Feng Xiaoe841bac2015-12-11 17:09:20 -08001// 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
Josh Haberman7429b912016-07-18 15:58:58 -070037goog.provide('jspb.ExtensionFieldBinaryInfo');
Josh Habermane0e73772016-07-18 20:49:11 -070038goog.provide('jspb.ExtensionFieldInfo');
Feng Xiaoe841bac2015-12-11 17:09:20 -080039goog.provide('jspb.Message');
40
41goog.require('goog.array');
42goog.require('goog.asserts');
Jisi Liu3b3c8ab2016-03-30 11:39:59 -070043goog.require('goog.crypt.base64');
Feng Xiaoe841bac2015-12-11 17:09:20 -080044goog.require('goog.json');
Adam Cozzetted64a2d92016-06-29 15:23:27 -070045goog.require('jspb.Map');
Feng Xiaoe841bac2015-12-11 17:09:20 -080046
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
Feng Xiaoe841bac2015-12-11 17:09:20 -080088 * @constructor
89 * @struct
90 * @template T
91 */
92jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn,
Josh Haberman7429b912016-07-18 15:58:58 -070093 isRepeated) {
Feng Xiaoe841bac2015-12-11 17:09:20 -080094 /** @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 */
Feng Xiaoe841bac2015-12-11 17:09:20 -0800103 this.isRepeated = isRepeated;
Feng Xiaoe841bac2015-12-11 17:09:20 -0800104};
105
Josh Haberman7429b912016-07-18 15:58:58 -0700106/**
107 * Stores binary-related information for a single extension field.
108 * @param {!jspb.ExtensionFieldInfo<T>} fieldInfo
Feng Xiaod36c0c52017-03-29 14:32:48 -0700109 * @param {function(this:jspb.BinaryReader,number,?)} binaryReaderFn
110 * @param {function(this:jspb.BinaryWriter,number,?)
111 * |function(this:jspb.BinaryWriter,number,?,?,?,?,?)} binaryWriterFn
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700112 * @param {function(?,?)=} opt_binaryMessageSerializeFn
113 * @param {function(?,?)=} opt_binaryMessageDeserializeFn
114 * @param {boolean=} opt_isPacked
Josh Haberman7429b912016-07-18 15:58:58 -0700115 * @constructor
116 * @struct
117 * @template T
118 */
119jspb.ExtensionFieldBinaryInfo = function(fieldInfo, binaryReaderFn, binaryWriterFn,
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700120 opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn, opt_isPacked) {
Josh Haberman7429b912016-07-18 15:58:58 -0700121 /** @const */
122 this.fieldInfo = fieldInfo;
123 /** @const */
124 this.binaryReaderFn = binaryReaderFn;
125 /** @const */
126 this.binaryWriterFn = binaryWriterFn;
127 /** @const */
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700128 this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn;
Josh Haberman7429b912016-07-18 15:58:58 -0700129 /** @const */
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700130 this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn;
Josh Haberman7429b912016-07-18 15:58:58 -0700131 /** @const */
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700132 this.isPacked = opt_isPacked;
Josh Haberman7429b912016-07-18 15:58:58 -0700133};
Feng Xiaoe841bac2015-12-11 17:09:20 -0800134
135/**
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700136 * @return {boolean} Does this field represent a sub Message?
137 */
138jspb.ExtensionFieldInfo.prototype.isMessageType = function() {
139 return !!this.ctor;
140};
141
142
143/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800144 * Base class for all JsPb messages.
Feng Xiaod36c0c52017-03-29 14:32:48 -0700145 *
146 * Several common methods (toObject, serializeBinary, in particular) are not
147 * defined on the prototype to encourage code patterns that minimize code bloat
148 * due to otherwise unused code on all protos contained in the project.
149 *
150 * If you want to call these methods on a generic message, either
151 * pass in your instance of method as a parameter:
152 * someFunction(instanceOfKnownProto,
153 * KnownProtoClass.prototype.serializeBinary);
154 * or use a lambda that knows the type:
155 * someFunction(()=>instanceOfKnownProto.serializeBinary());
156 * or, if you don't care about code size, just suppress the
157 * WARNING - Property serializeBinary never defined on jspb.Message
158 * and call it the intuitive way.
159 *
Feng Xiaoe841bac2015-12-11 17:09:20 -0800160 * @constructor
161 * @struct
162 */
163jspb.Message = function() {
164};
165
166
167/**
168 * @define {boolean} Whether to generate toObject methods for objects. Turn
169 * this off, if you do not want toObject to be ever used in your project.
170 * When turning off this flag, consider adding a conformance test that bans
171 * calling toObject. Enabling this will disable the JSCompiler's ability to
172 * dead code eliminate fields used in protocol buffers that are never used
173 * in an application.
174 */
175goog.define('jspb.Message.GENERATE_TO_OBJECT', true);
176
177
178/**
179 * @define {boolean} Whether to generate fromObject methods for objects. Turn
180 * this off, if you do not want fromObject to be ever used in your project.
181 * When turning off this flag, consider adding a conformance test that bans
182 * calling fromObject. Enabling this might disable the JSCompiler's ability
183 * to dead code eliminate fields used in protocol buffers that are never
184 * used in an application.
185 * NOTE: By default no protos actually have a fromObject method. You need to
186 * add the jspb.generate_from_object options to the proto definition to
187 * activate the feature.
188 * By default this is enabled for test code only.
189 */
190goog.define('jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE);
191
192
193/**
Paul Yang7f3e2372017-01-31 09:17:32 -0800194 * @define {boolean} Whether to generate toString methods for objects. Turn
195 * this off if you do use toString in your project and want to trim it from
196 * compiled JS.
197 */
198goog.define('jspb.Message.GENERATE_TO_STRING', true);
199
200
201/**
202 * @define {boolean} Whether arrays passed to initialize() can be assumed to be
203 * local (e.g. not from another iframe) and thus safely classified with
204 * instanceof Array.
205 */
206goog.define('jspb.Message.ASSUME_LOCAL_ARRAYS', false);
207
208
209/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800210 * @define {boolean} Turning on this flag does NOT change the behavior of JSPB
211 * and only affects private internal state. It may, however, break some
212 * tests that use naive deeply-equals algorithms, because using a proto
213 * mutates its internal state.
214 * Projects are advised to turn this flag always on.
215 */
216goog.define('jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS', COMPILED);
217// TODO(b/19419436) Turn this on by default.
218
219
220/**
Josh Habermanf873d322016-06-08 12:38:15 -0700221 * Does this JavaScript environment support Uint8Aray typed arrays?
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700222 * @type {boolean}
223 * @private
224 */
225jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function');
226
227
228/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800229 * The internal data array.
230 * @type {!Array}
231 * @protected
232 */
233jspb.Message.prototype.array;
234
235
236/**
237 * Wrappers are the constructed instances of message-type fields. They are built
238 * on demand from the raw array data. Includes message fields, repeated message
239 * fields and extension message fields. Indexed by field number.
240 * @type {Object}
241 * @private
242 */
243jspb.Message.prototype.wrappers_;
244
245
246/**
247 * The object that contains extension fields, if any. This is an object that
248 * maps from a proto field number to the field's value.
249 * @type {Object}
250 * @private
251 */
252jspb.Message.prototype.extensionObject_;
253
254
255/**
256 * Non-extension fields with a field number at or above the pivot are
257 * stored in the extension object (in addition to all extension fields).
258 * @type {number}
259 * @private
260 */
261jspb.Message.prototype.pivot_;
262
263
264/**
265 * The JsPb message_id of this proto.
266 * @type {string|undefined} the message id or undefined if this message
267 * has no id.
268 * @private
269 */
270jspb.Message.prototype.messageId_;
271
272
273/**
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700274 * Repeated float or double fields which have been converted to include only
275 * numbers and not strings holding "NaN", "Infinity" and "-Infinity".
276 * @private {!Object<number,boolean>|undefined}
277 */
278jspb.Message.prototype.convertedFloatingPointFields_;
279
280
281/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800282 * The xid of this proto type (The same for all instances of a proto). Provides
283 * a way to identify a proto by stable obfuscated name.
284 * @see {xid}.
285 * Available if {@link jspb.generate_xid} is added as a Message option to
286 * a protocol buffer.
287 * @const {!xid.String|undefined} The xid or undefined if message is
288 * annotated to generate the xid.
289 */
290jspb.Message.prototype.messageXid;
291
292
293
294/**
295 * Returns the JsPb message_id of this proto.
296 * @return {string|undefined} the message id or undefined if this message
297 * has no id.
298 */
299jspb.Message.prototype.getJsPbMessageId = function() {
300 return this.messageId_;
301};
302
303
304/**
305 * An offset applied to lookups into this.array to account for the presence or
306 * absence of a messageId at position 0. For response messages, this will be 0.
307 * Otherwise, it will be -1 so that the first array position is not wasted.
308 * @type {number}
309 * @private
310 */
311jspb.Message.prototype.arrayIndexOffset_;
312
313
314/**
315 * Returns the index into msg.array at which the proto field with tag number
316 * fieldNumber will be located.
317 * @param {!jspb.Message} msg Message for which we're calculating an index.
318 * @param {number} fieldNumber The field number.
319 * @return {number} The index.
320 * @private
321 */
322jspb.Message.getIndex_ = function(msg, fieldNumber) {
323 return fieldNumber + msg.arrayIndexOffset_;
324};
325
326
327/**
328 * Initializes a JsPb Message.
329 * @param {!jspb.Message} msg The JsPb proto to modify.
330 * @param {Array|undefined} data An initial data array.
331 * @param {string|number} messageId For response messages, the message id or ''
332 * if no message id is specified. For non-response messages, 0.
333 * @param {number} suggestedPivot The field number at which to start putting
334 * fields into the extension object. This is only used if data does not
335 * contain an extension object already. -1 if no extension object is
336 * required for this message type.
337 * @param {Array<number>} repeatedFields The message's repeated fields.
338 * @param {Array<!Array<number>>=} opt_oneofFields The fields belonging to
339 * each of the message's oneof unions.
340 * @protected
341 */
342jspb.Message.initialize = function(
343 msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) {
344 msg.wrappers_ = jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ? null : {};
345 if (!data) {
346 data = messageId ? [messageId] : [];
347 }
348 msg.messageId_ = messageId ? String(messageId) : undefined;
349 // If the messageId is 0, this message is not a response message, so we shift
350 // array indices down by 1 so as not to waste the first position in the array,
351 // which would otherwise go unused.
352 msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
353 msg.array = data;
354 jspb.Message.materializeExtensionObject_(msg, suggestedPivot);
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700355 msg.convertedFloatingPointFields_ = {};
356
Feng Xiaoe841bac2015-12-11 17:09:20 -0800357 if (repeatedFields) {
358 for (var i = 0; i < repeatedFields.length; i++) {
359 var fieldNumber = repeatedFields[i];
360 if (fieldNumber < msg.pivot_) {
361 var index = jspb.Message.getIndex_(msg, fieldNumber);
362 msg.array[index] = msg.array[index] ||
363 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
364 jspb.Message.EMPTY_LIST_SENTINEL_ :
365 []);
366 } else {
367 msg.extensionObject_[fieldNumber] =
368 msg.extensionObject_[fieldNumber] ||
369 (jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS ?
370 jspb.Message.EMPTY_LIST_SENTINEL_ :
371 []);
372 }
373 }
374 }
375
376 if (opt_oneofFields && opt_oneofFields.length) {
377 // Compute the oneof case for each union. This ensures only one value is
378 // set in the union.
379 goog.array.forEach(
380 opt_oneofFields, goog.partial(jspb.Message.computeOneofCase, msg));
381 }
382};
383
384
385/**
386 * Used to mark empty repeated fields. Serializes to null when serialized
387 * to JSON.
388 * When reading a repeated field readers must check the return value against
389 * this value and return and replace it with a new empty array if it is
390 * present.
391 * @private @const {!Object}
392 */
393jspb.Message.EMPTY_LIST_SENTINEL_ = goog.DEBUG && Object.freeze ?
394 Object.freeze([]) :
395 [];
396
397
398/**
Paul Yang7f3e2372017-01-31 09:17:32 -0800399 * Returns true if the provided argument is an array.
400 * @param {*} o The object to classify as array or not.
401 * @return {boolean} True if the provided object is an array.
402 * @private
403 */
404jspb.Message.isArray_ = function(o) {
405 return jspb.Message.ASSUME_LOCAL_ARRAYS ? o instanceof Array :
406 goog.isArray(o);
407};
408
409
410/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800411 * Ensures that the array contains an extension object if necessary.
412 * If the array contains an extension object in its last position, then the
413 * object is kept in place and its position is used as the pivot. If not, then
414 * create an extension object using suggestedPivot. If suggestedPivot is -1,
415 * we don't have an extension object at all, in which case all fields are stored
416 * in the array.
417 * @param {!jspb.Message} msg The JsPb proto to modify.
418 * @param {number} suggestedPivot See description for initialize().
419 * @private
420 */
421jspb.Message.materializeExtensionObject_ = function(msg, suggestedPivot) {
422 if (msg.array.length) {
423 var foundIndex = msg.array.length - 1;
424 var obj = msg.array[foundIndex];
425 // Normal fields are never objects, so we can be sure that if we find an
426 // object here, then it's the extension object. However, we must ensure that
427 // the object is not an array, since arrays are valid field values.
428 // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
429 // in Safari on iOS 8. See the description of CL/86511464 for details.
Paul Yang7f3e2372017-01-31 09:17:32 -0800430 if (obj && typeof obj == 'object' && !jspb.Message.isArray_(obj) &&
431 !(jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array)) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800432 msg.pivot_ = foundIndex - msg.arrayIndexOffset_;
433 msg.extensionObject_ = obj;
434 return;
435 }
436 }
437 // This complexity exists because we keep all extension fields in the
438 // extensionObject_ regardless of proto field number. Changing this would
439 // simplify the code here, but it would require changing the serialization
440 // format from the server, which is not backwards compatible.
441 // TODO(jshneier): Should we just treat extension fields the same as
442 // non-extension fields, and select whether they appear in the object or in
443 // the array purely based on tag number? This would allow simplifying all the
444 // get/setExtension logic, but it would require the breaking change described
445 // above.
446 if (suggestedPivot > -1) {
447 msg.pivot_ = suggestedPivot;
448 var pivotIndex = jspb.Message.getIndex_(msg, suggestedPivot);
449 if (!jspb.Message.MINIMIZE_MEMORY_ALLOCATIONS) {
450 msg.extensionObject_ = msg.array[pivotIndex] = {};
451 } else {
452 // Initialize to null to avoid changing the shape of the proto when it
453 // gets eventually set.
454 msg.extensionObject_ = null;
455 }
456 } else {
457 msg.pivot_ = Number.MAX_VALUE;
458 }
459};
460
461
462/**
463 * Creates an empty extensionObject_ if non exists.
464 * @param {!jspb.Message} msg The JsPb proto to modify.
465 * @private
466 */
467jspb.Message.maybeInitEmptyExtensionObject_ = function(msg) {
468 var pivotIndex = jspb.Message.getIndex_(msg, msg.pivot_);
469 if (!msg.array[pivotIndex]) {
470 msg.extensionObject_ = msg.array[pivotIndex] = {};
471 }
472};
473
474
475/**
476 * Converts a JsPb repeated message field into an object list.
477 * @param {!Array<T>} field The repeated message field to be
478 * converted.
479 * @param {?function(boolean=): Object|
480 * function((boolean|undefined),T): Object} toObjectFn The toObject
481 * function for this field. We need to pass this for effective dead code
482 * removal.
483 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
484 * for transitional soy proto support: http://goto/soy-param-migration
485 * @return {!Array<Object>} An array of converted message objects.
486 * @template T
487 */
488jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) {
489 // Not using goog.array.map in the generated code to keep it small.
490 // And not using it here to avoid a function call.
491 var result = [];
492 for (var i = 0; i < field.length; i++) {
493 result[i] = toObjectFn.call(field[i], opt_includeInstance,
494 /** @type {!jspb.Message} */ (field[i]));
495 }
496 return result;
497};
498
499
500/**
501 * Adds a proto's extension data to a Soy rendering object.
502 * @param {!jspb.Message} proto The proto whose extensions to convert.
503 * @param {!Object} obj The Soy object to add converted extension data to.
504 * @param {!Object} extensions The proto class' registered extensions.
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700505 * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn
506 * The proto class' getExtension function. Passed for effective dead code
507 * removal.
Feng Xiaoe841bac2015-12-11 17:09:20 -0800508 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
509 * for transitional soy proto support: http://goto/soy-param-migration
510 */
511jspb.Message.toObjectExtension = function(proto, obj, extensions,
512 getExtensionFn, opt_includeInstance) {
513 for (var fieldNumber in extensions) {
514 var fieldInfo = extensions[fieldNumber];
515 var value = getExtensionFn.call(proto, fieldInfo);
Adam Cozzette651ba622017-03-06 15:12:19 -0800516 if (goog.isDefAndNotNull(value)) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800517 for (var name in fieldInfo.fieldName) {
518 if (fieldInfo.fieldName.hasOwnProperty(name)) {
519 break; // the compiled field name
520 }
521 }
522 if (!fieldInfo.toObjectFn) {
523 obj[name] = value;
524 } else {
525 if (fieldInfo.isRepeated) {
526 obj[name] = jspb.Message.toObjectList(
527 /** @type {!Array<jspb.Message>} */ (value),
528 fieldInfo.toObjectFn, opt_includeInstance);
529 } else {
530 obj[name] = fieldInfo.toObjectFn(opt_includeInstance, value);
531 }
532 }
533 }
534 }
535};
536
537
538/**
539 * Writes a proto's extension data to a binary-format output stream.
540 * @param {!jspb.Message} proto The proto whose extensions to convert.
541 * @param {*} writer The binary-format writer to write to.
542 * @param {!Object} extensions The proto class' registered extensions.
Feng Xiaod36c0c52017-03-29 14:32:48 -0700543 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
Feng Xiaoe841bac2015-12-11 17:09:20 -0800544 * class' getExtension function. Passed for effective dead code removal.
545 */
546jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
547 getExtensionFn) {
548 for (var fieldNumber in extensions) {
Josh Haberman7429b912016-07-18 15:58:58 -0700549 var binaryFieldInfo = extensions[fieldNumber];
550 var fieldInfo = binaryFieldInfo.fieldInfo;
551
Feng Xiaoe841bac2015-12-11 17:09:20 -0800552 // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we
553 // need to gracefully error-out here rather than produce a null dereference
554 // below.
Josh Haberman7429b912016-07-18 15:58:58 -0700555 if (!binaryFieldInfo.binaryWriterFn) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800556 throw new Error('Message extension present that was generated ' +
557 'without binary serialization support');
558 }
559 var value = getExtensionFn.call(proto, fieldInfo);
Adam Cozzette651ba622017-03-06 15:12:19 -0800560 if (goog.isDefAndNotNull(value)) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700561 if (fieldInfo.isMessageType()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800562 // If the message type of the extension was generated without binary
563 // support, there may not be a binary message serializer function, and
564 // we can't know when we codegen the extending message that the extended
565 // message may require binary support, so we can *only* catch this error
566 // here, at runtime (and this decoupled codegen is the whole point of
567 // extensions!).
Josh Haberman7429b912016-07-18 15:58:58 -0700568 if (binaryFieldInfo.binaryMessageSerializeFn) {
569 binaryFieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex,
570 value, binaryFieldInfo.binaryMessageSerializeFn);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800571 } else {
572 throw new Error('Message extension present holding submessage ' +
573 'without binary support enabled, and message is ' +
574 'being serialized to binary format');
575 }
576 } else {
Josh Haberman7429b912016-07-18 15:58:58 -0700577 binaryFieldInfo.binaryWriterFn.call(
578 writer, fieldInfo.fieldIndex, value);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800579 }
580 }
581 }
582};
583
584
585/**
586 * Reads an extension field from the given reader and, if a valid extension,
587 * sets the extension value.
588 * @param {!jspb.Message} msg A jspb proto.
Feng Xiaod36c0c52017-03-29 14:32:48 -0700589 * @param {{
590 * skipField:function(this:jspb.BinaryReader),
591 * getFieldNumber:function(this:jspb.BinaryReader):number
592 * }} reader
Feng Xiaoe841bac2015-12-11 17:09:20 -0800593 * @param {!Object} extensions The extensions object.
Feng Xiaod36c0c52017-03-29 14:32:48 -0700594 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo)} getExtensionFn
595 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo, ?)} setExtensionFn
Feng Xiaoe841bac2015-12-11 17:09:20 -0800596 */
597jspb.Message.readBinaryExtension = function(msg, reader, extensions,
598 getExtensionFn, setExtensionFn) {
Josh Haberman7429b912016-07-18 15:58:58 -0700599 var binaryFieldInfo = extensions[reader.getFieldNumber()];
Josh Haberman7429b912016-07-18 15:58:58 -0700600 if (!binaryFieldInfo) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800601 reader.skipField();
602 return;
603 }
Adam Cozzette5a76e632016-11-17 16:48:38 -0800604 var fieldInfo = binaryFieldInfo.fieldInfo;
Josh Haberman7429b912016-07-18 15:58:58 -0700605 if (!binaryFieldInfo.binaryReaderFn) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800606 throw new Error('Deserializing extension whose generated code does not ' +
607 'support binary format');
608 }
609
610 var value;
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700611 if (fieldInfo.isMessageType()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800612 value = new fieldInfo.ctor();
Josh Haberman7429b912016-07-18 15:58:58 -0700613 binaryFieldInfo.binaryReaderFn.call(
614 reader, value, binaryFieldInfo.binaryMessageDeserializeFn);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800615 } else {
616 // All other types.
Josh Haberman7429b912016-07-18 15:58:58 -0700617 value = binaryFieldInfo.binaryReaderFn.call(reader);
Feng Xiaoe841bac2015-12-11 17:09:20 -0800618 }
619
Josh Haberman7429b912016-07-18 15:58:58 -0700620 if (fieldInfo.isRepeated && !binaryFieldInfo.isPacked) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800621 var currentList = getExtensionFn.call(msg, fieldInfo);
622 if (!currentList) {
623 setExtensionFn.call(msg, fieldInfo, [value]);
624 } else {
625 currentList.push(value);
626 }
627 } else {
628 setExtensionFn.call(msg, fieldInfo, value);
629 }
630};
631
632
633/**
634 * Gets the value of a non-extension field.
635 * @param {!jspb.Message} msg A jspb proto.
636 * @param {number} fieldNumber The field number.
637 * @return {string|number|boolean|Uint8Array|Array|null|undefined}
638 * The field's value.
639 * @protected
640 */
641jspb.Message.getField = function(msg, fieldNumber) {
642 if (fieldNumber < msg.pivot_) {
643 var index = jspb.Message.getIndex_(msg, fieldNumber);
644 var val = msg.array[index];
645 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
646 return msg.array[index] = [];
647 }
648 return val;
649 } else {
650 var val = msg.extensionObject_[fieldNumber];
651 if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
652 return msg.extensionObject_[fieldNumber] = [];
653 }
654 return val;
655 }
656};
657
658
659/**
Jisi Liu3b3c8ab2016-03-30 11:39:59 -0700660 * Gets the value of an optional float or double field.
661 * @param {!jspb.Message} msg A jspb proto.
662 * @param {number} fieldNumber The field number.
663 * @return {?number|undefined} The field's value.
664 * @protected
665 */
666jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) {
667 var value = jspb.Message.getField(msg, fieldNumber);
668 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers.
669 return value == null ? value : +value;
670};
671
672
673/**
674 * Gets the value of a repeated float or double field.
675 * @param {!jspb.Message} msg A jspb proto.
676 * @param {number} fieldNumber The field number.
677 * @return {!Array<number>} The field's value.
678 * @protected
679 */
680jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
681 var values = jspb.Message.getField(msg, fieldNumber);
682 if (!msg.convertedFloatingPointFields_) {
683 msg.convertedFloatingPointFields_ = {};
684 }
685 if (!msg.convertedFloatingPointFields_[fieldNumber]) {
686 for (var i = 0; i < values.length; i++) {
687 // Converts "NaN", "Infinity" and "-Infinity" to their corresponding
688 // numbers.
689 values[i] = +values[i];
690 }
691 msg.convertedFloatingPointFields_[fieldNumber] = true;
692 }
693 return /** @type {!Array<number>} */ (values);
694};
695
696
697/**
698 * Coerce a 'bytes' field to a base 64 string.
699 * @param {string|Uint8Array|null} value
700 * @return {?string} The field's coerced value.
701 */
702jspb.Message.bytesAsB64 = function(value) {
703 if (value == null || goog.isString(value)) {
704 return value;
705 }
706 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) {
707 return goog.crypt.base64.encodeByteArray(value);
708 }
709 goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value));
710 return null;
711};
712
713
714/**
715 * Coerce a 'bytes' field to a Uint8Array byte buffer.
716 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
717 * Mini. @see http://caniuse.com/Uint8Array
718 * @param {string|Uint8Array|null} value
719 * @return {?Uint8Array} The field's coerced value.
720 */
721jspb.Message.bytesAsU8 = function(value) {
722 if (value == null || value instanceof Uint8Array) {
723 return value;
724 }
725 if (goog.isString(value)) {
726 return goog.crypt.base64.decodeStringToUint8Array(value);
727 }
728 goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value));
729 return null;
730};
731
732
733/**
734 * Coerce a repeated 'bytes' field to an array of base 64 strings.
735 * Note: the returned array should be treated as immutable.
736 * @param {!Array<string>|!Array<!Uint8Array>} value
737 * @return {!Array<string?>} The field's coerced value.
738 */
739jspb.Message.bytesListAsB64 = function(value) {
740 jspb.Message.assertConsistentTypes_(value);
741 if (!value.length || goog.isString(value[0])) {
742 return /** @type {!Array<string>} */ (value);
743 }
744 return goog.array.map(value, jspb.Message.bytesAsB64);
745};
746
747
748/**
749 * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers.
750 * Note: the returned array should be treated as immutable.
751 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
752 * Mini. @see http://caniuse.com/Uint8Array
753 * @param {!Array<string>|!Array<!Uint8Array>} value
754 * @return {!Array<Uint8Array?>} The field's coerced value.
755 */
756jspb.Message.bytesListAsU8 = function(value) {
757 jspb.Message.assertConsistentTypes_(value);
758 if (!value.length || value[0] instanceof Uint8Array) {
759 return /** @type {!Array<!Uint8Array>} */ (value);
760 }
761 return goog.array.map(value, jspb.Message.bytesAsU8);
762};
763
764
765/**
766 * Asserts that all elements of an array are of the same type.
767 * @param {Array?} array The array to test.
768 * @private
769 */
770jspb.Message.assertConsistentTypes_ = function(array) {
771 if (goog.DEBUG && array && array.length > 1) {
772 var expected = goog.typeOf(array[0]);
773 goog.array.forEach(array, function(e) {
774 if (goog.typeOf(e) != expected) {
775 goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' +
776 'Got ' + goog.typeOf(e) + ' expected ' + expected);
777 }
778 });
779 }
780};
781
782
783/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800784 * Gets the value of a non-extension primitive field, with proto3 (non-nullable
785 * primitives) semantics. Returns `defaultValue` if the field is not otherwise
786 * set.
787 * @template T
788 * @param {!jspb.Message} msg A jspb proto.
789 * @param {number} fieldNumber The field number.
790 * @param {T} defaultValue The default value.
791 * @return {T} The field's value.
792 * @protected
793 */
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700794jspb.Message.getFieldWithDefault = function(msg, fieldNumber, defaultValue) {
Feng Xiaoe841bac2015-12-11 17:09:20 -0800795 var value = jspb.Message.getField(msg, fieldNumber);
796 if (value == null) {
797 return defaultValue;
798 } else {
799 return value;
800 }
801};
802
803
804/**
Josh Habermanf2eeaf72016-09-28 10:52:32 -0700805 * Alias for getFieldWithDefault used by older generated code.
806 * @template T
807 * @param {!jspb.Message} msg A jspb proto.
808 * @param {number} fieldNumber The field number.
809 * @param {T} defaultValue The default value.
810 * @return {T} The field's value.
811 * @protected
812 */
813jspb.Message.getFieldProto3 = jspb.Message.getFieldWithDefault;
814
815
816/**
Adam Cozzetted64a2d92016-06-29 15:23:27 -0700817 * Gets the value of a map field, lazily creating the map container if
818 * necessary.
819 *
820 * This should only be called from generated code, because it requires knowledge
821 * of serialization/parsing callbacks (which are required by the map at
822 * construction time, and the map may be constructed here).
823 *
Adam Cozzetted64a2d92016-06-29 15:23:27 -0700824 * @template K, V
825 * @param {!jspb.Message} msg
826 * @param {number} fieldNumber
827 * @param {boolean|undefined} noLazyCreate
828 * @param {?=} opt_valueCtor
Adam Cozzetted64a2d92016-06-29 15:23:27 -0700829 * @return {!jspb.Map<K, V>|undefined}
830 * @protected
831 */
832jspb.Message.getMapField = function(msg, fieldNumber, noLazyCreate,
Josh Haberman923eae82016-07-18 14:46:12 -0700833 opt_valueCtor) {
Adam Cozzetted64a2d92016-06-29 15:23:27 -0700834 if (!msg.wrappers_) {
835 msg.wrappers_ = {};
836 }
837 // If we already have a map in the map wrappers, return that.
838 if (fieldNumber in msg.wrappers_) {
839 return msg.wrappers_[fieldNumber];
840 } else if (noLazyCreate) {
841 return undefined;
842 } else {
843 // Wrap the underlying elements array with a Map.
844 var arr = jspb.Message.getField(msg, fieldNumber);
845 if (!arr) {
846 arr = [];
847 jspb.Message.setField(msg, fieldNumber, arr);
848 }
849 return msg.wrappers_[fieldNumber] =
850 new jspb.Map(
Josh Haberman923eae82016-07-18 14:46:12 -0700851 /** @type {!Array<!Array<!Object>>} */ (arr), opt_valueCtor);
Adam Cozzetted64a2d92016-06-29 15:23:27 -0700852 }
853};
854
855
856/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800857 * Sets the value of a non-extension field.
858 * @param {!jspb.Message} msg A jspb proto.
859 * @param {number} fieldNumber The field number.
860 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
861 * @protected
862 */
863jspb.Message.setField = function(msg, fieldNumber, value) {
864 if (fieldNumber < msg.pivot_) {
865 msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value;
866 } else {
867 msg.extensionObject_[fieldNumber] = value;
868 }
869};
870
871
872/**
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700873 * Adds a value to a repeated, primitive field.
874 * @param {!jspb.Message} msg A jspb proto.
875 * @param {number} fieldNumber The field number.
876 * @param {string|number|boolean|!Uint8Array} value New value
877 * @param {number=} opt_index Index where to put new value.
878 * @protected
879 */
880jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) {
881 var arr = jspb.Message.getField(msg, fieldNumber);
882 if (opt_index != undefined) {
883 arr.splice(opt_index, 0, value);
884 } else {
885 arr.push(value);
886 }
887};
888
889
890/**
Feng Xiaoe841bac2015-12-11 17:09:20 -0800891 * Sets the value of a field in a oneof union and clears all other fields in
892 * the union.
893 * @param {!jspb.Message} msg A jspb proto.
894 * @param {number} fieldNumber The field number.
895 * @param {!Array<number>} oneof The fields belonging to the union.
896 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
897 * @protected
898 */
899jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) {
900 var currentCase = jspb.Message.computeOneofCase(msg, oneof);
901 if (currentCase && currentCase !== fieldNumber && value !== undefined) {
902 if (msg.wrappers_ && currentCase in msg.wrappers_) {
903 msg.wrappers_[currentCase] = undefined;
904 }
905 jspb.Message.setField(msg, currentCase, undefined);
906 }
907 jspb.Message.setField(msg, fieldNumber, value);
908};
909
910
911/**
912 * Computes the selection in a oneof group for the given message, ensuring
913 * only one field is set in the process.
914 *
915 * According to the protobuf language guide (
916 * https://developers.google.com/protocol-buffers/docs/proto#oneof), "if the
917 * parser encounters multiple members of the same oneof on the wire, only the
918 * last member seen is used in the parsed message." Since JSPB serializes
919 * messages to a JSON array, the "last member seen" will always be the field
920 * with the greatest field number (directly corresponding to the greatest
921 * array index).
922 *
923 * @param {!jspb.Message} msg A jspb proto.
924 * @param {!Array<number>} oneof The field numbers belonging to the union.
925 * @return {number} The field number currently set in the union, or 0 if none.
926 * @protected
927 */
928jspb.Message.computeOneofCase = function(msg, oneof) {
929 var oneofField;
930 var oneofValue;
931
932 goog.array.forEach(oneof, function(fieldNumber) {
933 var value = jspb.Message.getField(msg, fieldNumber);
934 if (goog.isDefAndNotNull(value)) {
935 oneofField = fieldNumber;
936 oneofValue = value;
937 jspb.Message.setField(msg, fieldNumber, undefined);
938 }
939 });
940
941 if (oneofField) {
942 // NB: We know the value is unique, so we can call jspb.Message.setField
943 // directly instead of jpsb.Message.setOneofField. Also, setOneofField
944 // calls this function.
945 jspb.Message.setField(msg, oneofField, oneofValue);
946 return oneofField;
947 }
948
949 return 0;
950};
951
952
953/**
954 * Gets and wraps a proto field on access.
955 * @param {!jspb.Message} msg A jspb proto.
956 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
957 * @param {number} fieldNumber The field number.
958 * @param {number=} opt_required True (1) if this is a required field.
959 * @return {jspb.Message} The field as a jspb proto.
960 * @protected
961 */
962jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) {
963 // TODO(mwr): Consider copying data and/or arrays.
964 if (!msg.wrappers_) {
965 msg.wrappers_ = {};
966 }
967 if (!msg.wrappers_[fieldNumber]) {
968 var data = /** @type {Array} */ (jspb.Message.getField(msg, fieldNumber));
969 if (opt_required || data) {
970 // TODO(mwr): Remove existence test for always valid default protos.
971 msg.wrappers_[fieldNumber] = new ctor(data);
972 }
973 }
974 return /** @type {jspb.Message} */ (msg.wrappers_[fieldNumber]);
975};
976
977
978/**
979 * Gets and wraps a repeated proto field on access.
980 * @param {!jspb.Message} msg A jspb proto.
981 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
982 * @param {number} fieldNumber The field number.
983 * @return {Array<!jspb.Message>} The repeated field as an array of protos.
984 * @protected
985 */
986jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) {
Bo Yangcc8ca5b2016-09-19 13:45:07 -0700987 jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber);
988 var val = msg.wrappers_[fieldNumber];
989 if (val == jspb.Message.EMPTY_LIST_SENTINEL_) {
990 val = msg.wrappers_[fieldNumber] = [];
991 }
992 return /** @type {!Array<!jspb.Message>} */ (val);
993};
994
995
996/**
997 * Wraps underlying array into proto message representation if it wasn't done
998 * before.
999 * @param {!jspb.Message} msg A jspb proto.
1000 * @param {function(new:jspb.Message, ?Array)} ctor Constructor for the field.
1001 * @param {number} fieldNumber The field number.
1002 * @private
1003 */
1004jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001005 if (!msg.wrappers_) {
1006 msg.wrappers_ = {};
1007 }
1008 if (!msg.wrappers_[fieldNumber]) {
1009 var data = jspb.Message.getField(msg, fieldNumber);
1010 for (var wrappers = [], i = 0; i < data.length; i++) {
1011 wrappers[i] = new ctor(data[i]);
1012 }
1013 msg.wrappers_[fieldNumber] = wrappers;
1014 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001015};
1016
1017
1018/**
1019 * Sets a proto field and syncs it to the backing array.
1020 * @param {!jspb.Message} msg A jspb proto.
1021 * @param {number} fieldNumber The field number.
Adam Cozzette5a76e632016-11-17 16:48:38 -08001022 * @param {?jspb.Message|?jspb.Map|undefined} value A new value for this proto
1023 * field.
Feng Xiaoe841bac2015-12-11 17:09:20 -08001024 * @protected
1025 */
1026jspb.Message.setWrapperField = function(msg, fieldNumber, value) {
1027 if (!msg.wrappers_) {
1028 msg.wrappers_ = {};
1029 }
1030 var data = value ? value.toArray() : value;
1031 msg.wrappers_[fieldNumber] = value;
1032 jspb.Message.setField(msg, fieldNumber, data);
1033};
1034
1035
1036/**
1037 * Sets a proto field in a oneof union and syncs it to the backing array.
1038 * @param {!jspb.Message} msg A jspb proto.
1039 * @param {number} fieldNumber The field number.
1040 * @param {!Array<number>} oneof The fields belonging to the union.
1041 * @param {jspb.Message|undefined} value A new value for this proto field.
1042 * @protected
1043 */
1044jspb.Message.setOneofWrapperField = function(msg, fieldNumber, oneof, value) {
1045 if (!msg.wrappers_) {
1046 msg.wrappers_ = {};
1047 }
1048 var data = value ? value.toArray() : value;
1049 msg.wrappers_[fieldNumber] = value;
1050 jspb.Message.setOneofField(msg, fieldNumber, oneof, data);
1051};
1052
1053
1054/**
1055 * Sets a repeated proto field and syncs it to the backing array.
1056 * @param {!jspb.Message} msg A jspb proto.
1057 * @param {number} fieldNumber The field number.
1058 * @param {Array<!jspb.Message>|undefined} value An array of protos.
1059 * @protected
1060 */
1061jspb.Message.setRepeatedWrapperField = function(msg, fieldNumber, value) {
1062 if (!msg.wrappers_) {
1063 msg.wrappers_ = {};
1064 }
1065 value = value || [];
1066 for (var data = [], i = 0; i < value.length; i++) {
1067 data[i] = value[i].toArray();
1068 }
1069 msg.wrappers_[fieldNumber] = value;
1070 jspb.Message.setField(msg, fieldNumber, data);
1071};
1072
1073
1074/**
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001075 * Add a message to a repeated proto field.
1076 * @param {!jspb.Message} msg A jspb proto.
1077 * @param {number} fieldNumber The field number.
1078 * @param {T_CHILD|undefined} value Proto that will be added to the
1079 * repeated field.
1080 * @param {function(new:T_CHILD, ?Array=)} ctor The constructor of the
1081 * message type.
1082 * @param {number|undefined} index Index at which to insert the value.
1083 * @return {T_CHILD_NOT_UNDEFINED} proto that was inserted to the repeated field
1084 * @template MessageType
1085 * Use go/closure-ttl to declare a non-undefined version of T_CHILD. Replace the
1086 * undefined in blah|undefined with none. This is necessary because the compiler
1087 * will infer T_CHILD to be |undefined.
1088 * @template T_CHILD
1089 * @template T_CHILD_NOT_UNDEFINED :=
1090 * cond(isUnknown(T_CHILD), unknown(),
1091 * mapunion(T_CHILD, (X) =>
1092 * cond(eq(X, 'undefined'), none(), X)))
1093 * =:
1094 * @protected
1095 */
1096jspb.Message.addToRepeatedWrapperField = function(
1097 msg, fieldNumber, value, ctor, index) {
1098 jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber);
1099 var wrapperArray = msg.wrappers_[fieldNumber];
1100 if (!wrapperArray) {
1101 wrapperArray = msg.wrappers_[fieldNumber] = [];
1102 }
1103 var insertedValue = value ? value : new ctor();
1104 var array = jspb.Message.getField(msg, fieldNumber);
1105 if (index != undefined) {
1106 wrapperArray.splice(index, 0, insertedValue);
1107 array.splice(index, 0, insertedValue.toArray());
1108 } else {
1109 wrapperArray.push(insertedValue);
1110 array.push(insertedValue.toArray());
1111 }
1112 return insertedValue;
1113};
1114
1115
1116/**
Feng Xiaoe841bac2015-12-11 17:09:20 -08001117 * Converts a JsPb repeated message field into a map. The map will contain
1118 * protos unless an optional toObject function is given, in which case it will
1119 * contain objects suitable for Soy rendering.
1120 * @param {!Array<T>} field The repeated message field to be
1121 * converted.
1122 * @param {function() : string?} mapKeyGetterFn The function to get the key of
1123 * the map.
1124 * @param {?function(boolean=): Object|
1125 * function((boolean|undefined),T): Object} opt_toObjectFn The
1126 * toObject function for this field. We need to pass this for effective
1127 * dead code removal.
1128 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
1129 * for transitional soy proto support: http://goto/soy-param-migration
1130 * @return {!Object.<string, Object>} A map of proto or Soy objects.
1131 * @template T
1132 */
1133jspb.Message.toMap = function(
1134 field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) {
1135 var result = {};
1136 for (var i = 0; i < field.length; i++) {
1137 result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ?
1138 opt_toObjectFn.call(field[i], opt_includeInstance,
1139 /** @type {!jspb.Message} */ (field[i])) : field[i];
1140 }
1141 return result;
1142};
1143
1144
1145/**
Adam Cozzetted64a2d92016-06-29 15:23:27 -07001146 * Syncs all map fields' contents back to their underlying arrays.
1147 * @private
1148 */
1149jspb.Message.prototype.syncMapFields_ = function() {
1150 // This iterates over submessage, map, and repeated fields, which is intended.
1151 // Submessages can contain maps which also need to be synced.
1152 //
1153 // There is a lot of opportunity for optimization here. For example we could
1154 // statically determine that some messages have no submessages with maps and
1155 // optimize this method away for those just by generating one extra static
1156 // boolean per message type.
1157 if (this.wrappers_) {
1158 for (var fieldNumber in this.wrappers_) {
1159 var val = this.wrappers_[fieldNumber];
1160 if (goog.isArray(val)) {
1161 for (var i = 0; i < val.length; i++) {
1162 if (val[i]) {
1163 val[i].toArray();
1164 }
1165 }
1166 } else {
1167 // Works for submessages and maps.
1168 if (val) {
1169 val.toArray();
1170 }
1171 }
1172 }
1173 }
1174};
1175
1176
1177/**
Feng Xiaoe841bac2015-12-11 17:09:20 -08001178 * Returns the internal array of this proto.
1179 * <p>Note: If you use this array to construct a second proto, the content
1180 * would then be partially shared between the two protos.
1181 * @return {!Array} The proto represented as an array.
1182 */
1183jspb.Message.prototype.toArray = function() {
Adam Cozzetted64a2d92016-06-29 15:23:27 -07001184 this.syncMapFields_();
Feng Xiaoe841bac2015-12-11 17:09:20 -08001185 return this.array;
1186};
1187
1188
1189
Paul Yang7f3e2372017-01-31 09:17:32 -08001190if (jspb.Message.GENERATE_TO_STRING) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001191
1192/**
1193 * Creates a string representation of the internal data array of this proto.
1194 * <p>NOTE: This string is *not* suitable for use in server requests.
1195 * @return {string} A string representation of this proto.
1196 * @override
1197 */
1198jspb.Message.prototype.toString = function() {
Adam Cozzetted64a2d92016-06-29 15:23:27 -07001199 this.syncMapFields_();
Feng Xiaoe841bac2015-12-11 17:09:20 -08001200 return this.array.toString();
1201};
1202
Paul Yang7f3e2372017-01-31 09:17:32 -08001203}
Feng Xiaoe841bac2015-12-11 17:09:20 -08001204
1205/**
1206 * Gets the value of the extension field from the extended object.
1207 * @param {jspb.ExtensionFieldInfo.<T>} fieldInfo Specifies the field to get.
1208 * @return {T} The value of the field.
1209 * @template T
1210 */
1211jspb.Message.prototype.getExtension = function(fieldInfo) {
1212 if (!this.extensionObject_) {
1213 return undefined;
1214 }
1215 if (!this.wrappers_) {
1216 this.wrappers_ = {};
1217 }
1218 var fieldNumber = fieldInfo.fieldIndex;
1219 if (fieldInfo.isRepeated) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001220 if (fieldInfo.isMessageType()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001221 if (!this.wrappers_[fieldNumber]) {
1222 this.wrappers_[fieldNumber] =
1223 goog.array.map(this.extensionObject_[fieldNumber] || [],
1224 function(arr) {
1225 return new fieldInfo.ctor(arr);
1226 });
1227 }
1228 return this.wrappers_[fieldNumber];
1229 } else {
1230 return this.extensionObject_[fieldNumber];
1231 }
1232 } else {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001233 if (fieldInfo.isMessageType()) {
Feng Xiaoe841bac2015-12-11 17:09:20 -08001234 if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) {
1235 this.wrappers_[fieldNumber] = new fieldInfo.ctor(
1236 /** @type {Array|undefined} */ (
1237 this.extensionObject_[fieldNumber]));
1238 }
1239 return this.wrappers_[fieldNumber];
1240 } else {
1241 return this.extensionObject_[fieldNumber];
1242 }
1243 }
1244};
1245
1246
1247/**
1248 * Sets the value of the extension field in the extended object.
1249 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set.
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001250 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value
1251 * to set.
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001252 * @return {THIS} For chaining
1253 * @this {THIS}
1254 * @template THIS
Feng Xiaoe841bac2015-12-11 17:09:20 -08001255 */
1256jspb.Message.prototype.setExtension = function(fieldInfo, value) {
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001257 // Cast self, since the inferred THIS is unknown inside the function body.
1258 // https://github.com/google/closure-compiler/issues/1411#issuecomment-232442220
1259 var self = /** @type {!jspb.Message} */ (this);
1260 if (!self.wrappers_) {
1261 self.wrappers_ = {};
Feng Xiaoe841bac2015-12-11 17:09:20 -08001262 }
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001263 jspb.Message.maybeInitEmptyExtensionObject_(self);
Feng Xiaoe841bac2015-12-11 17:09:20 -08001264 var fieldNumber = fieldInfo.fieldIndex;
1265 if (fieldInfo.isRepeated) {
1266 value = value || [];
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001267 if (fieldInfo.isMessageType()) {
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001268 self.wrappers_[fieldNumber] = value;
1269 self.extensionObject_[fieldNumber] = goog.array.map(
Feng Xiaoe841bac2015-12-11 17:09:20 -08001270 /** @type {Array<jspb.Message>} */ (value), function(msg) {
1271 return msg.toArray();
1272 });
1273 } else {
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001274 self.extensionObject_[fieldNumber] = value;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001275 }
1276 } else {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001277 if (fieldInfo.isMessageType()) {
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001278 self.wrappers_[fieldNumber] = value;
1279 self.extensionObject_[fieldNumber] = value ? value.toArray() : value;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001280 } else {
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001281 self.extensionObject_[fieldNumber] = value;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001282 }
1283 }
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001284 return self;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001285};
1286
1287
1288/**
1289 * Creates a difference object between two messages.
1290 *
1291 * The result will contain the top-level fields of m2 that differ from those of
1292 * m1 at any level of nesting. No data is cloned, the result object will
1293 * share its top-level elements with m2 (but not with m1).
1294 *
1295 * Note that repeated fields should not have null/undefined elements, but if
1296 * they do, this operation will treat repeated fields of different length as
1297 * the same if the only difference between them is due to trailing
1298 * null/undefined values.
1299 *
1300 * @param {!jspb.Message} m1 The first message object.
1301 * @param {!jspb.Message} m2 The second message object.
1302 * @return {!jspb.Message} The difference returned as a proto message.
1303 * Note that the returned message may be missing required fields. This is
1304 * currently tolerated in Js, but would cause an error if you tried to
1305 * send such a proto to the server. You can access the raw difference
1306 * array with result.toArray().
1307 * @throws {Error} If the messages are responses with different types.
1308 */
1309jspb.Message.difference = function(m1, m2) {
1310 if (!(m1 instanceof m2.constructor)) {
1311 throw new Error('Messages have different types.');
1312 }
1313 var arr1 = m1.toArray();
1314 var arr2 = m2.toArray();
1315 var res = [];
1316 var start = 0;
1317 var length = arr1.length > arr2.length ? arr1.length : arr2.length;
1318 if (m1.getJsPbMessageId()) {
1319 res[0] = m1.getJsPbMessageId();
1320 start = 1;
1321 }
1322 for (var i = start; i < length; i++) {
1323 if (!jspb.Message.compareFields(arr1[i], arr2[i])) {
1324 res[i] = arr2[i];
1325 }
1326 }
1327 return new m1.constructor(res);
1328};
1329
1330
1331/**
1332 * Tests whether two messages are equal.
1333 * @param {jspb.Message|undefined} m1 The first message object.
1334 * @param {jspb.Message|undefined} m2 The second message object.
1335 * @return {boolean} true if both messages are null/undefined, or if both are
1336 * of the same type and have the same field values.
1337 */
1338jspb.Message.equals = function(m1, m2) {
1339 return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) &&
1340 jspb.Message.compareFields(m1.toArray(), m2.toArray()));
1341};
1342
1343
1344/**
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001345 * Compares two message extension fields recursively.
1346 * @param {!Object} extension1 The first field.
1347 * @param {!Object} extension2 The second field.
1348 * @return {boolean} true if the extensions are null/undefined, or otherwise
1349 * equal.
1350 */
1351jspb.Message.compareExtensions = function(extension1, extension2) {
1352 extension1 = extension1 || {};
1353 extension2 = extension2 || {};
1354
1355 var keys = {};
1356 for (var name in extension1) {
1357 keys[name] = 0;
1358 }
1359 for (var name in extension2) {
1360 keys[name] = 0;
1361 }
1362 for (name in keys) {
1363 if (!jspb.Message.compareFields(extension1[name], extension2[name])) {
1364 return false;
1365 }
1366 }
1367 return true;
1368};
1369
1370
1371/**
Feng Xiaoe841bac2015-12-11 17:09:20 -08001372 * Compares two message fields recursively.
1373 * @param {*} field1 The first field.
1374 * @param {*} field2 The second field.
1375 * @return {boolean} true if the fields are null/undefined, or otherwise equal.
1376 */
1377jspb.Message.compareFields = function(field1, field2) {
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001378 // If the fields are trivially equal, they're equal.
1379 if (field1 == field2) return true;
1380
1381 // If the fields aren't trivially equal and one of them isn't an object,
1382 // they can't possibly be equal.
1383 if (!goog.isObject(field1) || !goog.isObject(field2)) {
1384 return false;
1385 }
1386
1387 // We have two objects. If they're different types, they're not equal.
1388 field1 = /** @type {!Object} */(field1);
1389 field2 = /** @type {!Object} */(field2);
1390 if (field1.constructor != field2.constructor) return false;
1391
1392 // If both are Uint8Arrays, compare them element-by-element.
1393 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) {
1394 var bytes1 = /** @type {!Uint8Array} */(field1);
1395 var bytes2 = /** @type {!Uint8Array} */(field2);
1396 if (bytes1.length != bytes2.length) return false;
1397 for (var i = 0; i < bytes1.length; i++) {
1398 if (bytes1[i] != bytes2[i]) return false;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001399 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001400 return true;
1401 }
1402
1403 // If they're both Arrays, compare them element by element except for the
1404 // optional extension objects at the end, which we compare separately.
1405 if (field1.constructor === Array) {
1406 var extension1 = undefined;
1407 var extension2 = undefined;
1408
1409 var length = Math.max(field1.length, field2.length);
1410 for (var i = 0; i < length; i++) {
1411 var val1 = field1[i];
1412 var val2 = field2[i];
1413
1414 if (val1 && (val1.constructor == Object)) {
1415 goog.asserts.assert(extension1 === undefined);
1416 goog.asserts.assert(i === field1.length - 1);
1417 extension1 = val1;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001418 val1 = undefined;
1419 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001420
1421 if (val2 && (val2.constructor == Object)) {
1422 goog.asserts.assert(extension2 === undefined);
1423 goog.asserts.assert(i === field2.length - 1);
1424 extension2 = val2;
Feng Xiaoe841bac2015-12-11 17:09:20 -08001425 val2 = undefined;
1426 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001427
Feng Xiaoe841bac2015-12-11 17:09:20 -08001428 if (!jspb.Message.compareFields(val1, val2)) {
1429 return false;
1430 }
1431 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001432
1433 if (extension1 || extension2) {
1434 extension1 = extension1 || {};
1435 extension2 = extension2 || {};
1436 return jspb.Message.compareExtensions(extension1, extension2);
Feng Xiaoe841bac2015-12-11 17:09:20 -08001437 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001438
Feng Xiaoe841bac2015-12-11 17:09:20 -08001439 return true;
1440 }
Jisi Liu3b3c8ab2016-03-30 11:39:59 -07001441
1442 // If they're both plain Objects (i.e. extensions), compare them as
1443 // extensions.
1444 if (field1.constructor === Object) {
1445 return jspb.Message.compareExtensions(field1, field2);
1446 }
1447
1448 throw new Error('Invalid type in JSPB array');
Feng Xiaoe841bac2015-12-11 17:09:20 -08001449};
1450
1451
1452/**
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001453 * Templated, type-safe cloneMessage definition.
1454 * @return {THIS}
1455 * @this {THIS}
1456 * @template THIS
1457 */
1458jspb.Message.prototype.cloneMessage = function() {
1459 return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this));
1460};
1461
1462/**
1463 * Alias clone to cloneMessage. goog.object.unsafeClone uses clone to
1464 * efficiently copy objects. Without this alias, copying jspb messages comes
1465 * with a large performance penalty.
1466 * @return {THIS}
1467 * @this {THIS}
1468 * @template THIS
1469 */
1470jspb.Message.prototype.clone = function() {
1471 return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this));
1472};
1473
1474/**
1475 * Static clone function. NOTE: A type-safe method called "cloneMessage"
1476 * exists
Feng Xiaoe841bac2015-12-11 17:09:20 -08001477 * on each generated JsPb class. Do not call this function directly.
1478 * @param {!jspb.Message} msg A message to clone.
1479 * @return {!jspb.Message} A deep clone of the given message.
1480 */
1481jspb.Message.clone = function(msg) {
1482 // Although we could include the wrappers, we leave them out here.
1483 return jspb.Message.cloneMessage(msg);
1484};
1485
1486
1487/**
1488 * @param {!jspb.Message} msg A message to clone.
1489 * @return {!jspb.Message} A deep clone of the given message.
1490 * @protected
1491 */
1492jspb.Message.cloneMessage = function(msg) {
1493 // Although we could include the wrappers, we leave them out here.
1494 return new msg.constructor(jspb.Message.clone_(msg.toArray()));
1495};
1496
1497
1498/**
1499 * Takes 2 messages of the same type and copies the contents of the first
1500 * message into the second. After this the 2 messages will equals in terms of
1501 * value semantics but share no state. All data in the destination message will
1502 * be overridden.
1503 *
1504 * @param {MESSAGE} fromMessage Message that will be copied into toMessage.
1505 * @param {MESSAGE} toMessage Message which will receive a copy of fromMessage
1506 * as its contents.
1507 * @template MESSAGE
1508 */
1509jspb.Message.copyInto = function(fromMessage, toMessage) {
1510 goog.asserts.assertInstanceof(fromMessage, jspb.Message);
1511 goog.asserts.assertInstanceof(toMessage, jspb.Message);
1512 goog.asserts.assert(fromMessage.constructor == toMessage.constructor,
1513 'Copy source and target message should have the same type.');
1514 var copyOfFrom = jspb.Message.clone(fromMessage);
1515
1516 var to = toMessage.toArray();
1517 var from = copyOfFrom.toArray();
1518
1519 // Empty destination in case it has more values at the end of the array.
1520 to.length = 0;
1521 // and then copy everything from the new to the existing message.
1522 for (var i = 0; i < from.length; i++) {
1523 to[i] = from[i];
1524 }
1525
1526 // This is either null or empty for a fresh copy.
1527 toMessage.wrappers_ = copyOfFrom.wrappers_;
1528 // Just a reference into the shared array.
1529 toMessage.extensionObject_ = copyOfFrom.extensionObject_;
1530};
1531
1532
1533/**
1534 * Helper for cloning an internal JsPb object.
1535 * @param {!Object} obj A JsPb object, eg, a field, to be cloned.
1536 * @return {!Object} A clone of the input object.
1537 * @private
1538 */
1539jspb.Message.clone_ = function(obj) {
1540 var o;
1541 if (goog.isArray(obj)) {
1542 // Allocate array of correct size.
1543 var clonedArray = new Array(obj.length);
1544 // Use array iteration where possible because it is faster than for-in.
1545 for (var i = 0; i < obj.length; i++) {
1546 if ((o = obj[i]) != null) {
1547 clonedArray[i] = typeof o == 'object' ? jspb.Message.clone_(o) : o;
1548 }
1549 }
1550 return clonedArray;
1551 }
Adam Cozzetted64a2d92016-06-29 15:23:27 -07001552 if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) {
1553 return new Uint8Array(obj);
1554 }
Feng Xiaoe841bac2015-12-11 17:09:20 -08001555 var clone = {};
1556 for (var key in obj) {
1557 if ((o = obj[key]) != null) {
1558 clone[key] = typeof o == 'object' ? jspb.Message.clone_(o) : o;
1559 }
1560 }
1561 return clone;
1562};
1563
1564
1565/**
1566 * Registers a JsPb message type id with its constructor.
1567 * @param {string} id The id for this type of message.
1568 * @param {Function} constructor The message constructor.
1569 */
1570jspb.Message.registerMessageType = function(id, constructor) {
1571 jspb.Message.registry_[id] = constructor;
1572 // This is needed so we can later access messageId directly on the contructor,
1573 // otherwise it is not available due to 'property collapsing' by the compiler.
1574 constructor.messageId = id;
1575};
1576
1577
1578/**
1579 * The registry of message ids to message constructors.
1580 * @private
1581 */
1582jspb.Message.registry_ = {};
1583
1584
1585/**
1586 * The extensions registered on MessageSet. This is a map of extension
1587 * field number to field info object. This should be considered as a
1588 * private API.
1589 *
1590 * This is similar to [jspb class name].extensions object for
1591 * non-MessageSet. We special case MessageSet so that we do not need
1592 * to goog.require MessageSet from classes that extends MessageSet.
1593 *
1594 * @type {!Object.<number, jspb.ExtensionFieldInfo>}
1595 */
1596jspb.Message.messageSetExtensions = {};
Bo Yangcc8ca5b2016-09-19 13:45:07 -07001597jspb.Message.messageSetExtensionsBinary = {};