• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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 * @suppress {missingRequire} TODO(b/152540451): this shouldn't be needed
35 * @author mwr@google.com (Mark Rawling)
36 */
37
38goog.provide('jspb.ExtensionFieldBinaryInfo');
39goog.provide('jspb.ExtensionFieldInfo');
40goog.provide('jspb.Message');
41
42goog.require('goog.array');
43goog.require('goog.asserts');
44goog.require('goog.crypt.base64');
45goog.require('jspb.BinaryReader');
46goog.require('jspb.Map');
47
48
49
50
51/**
52 * Stores information for a single extension field.
53 *
54 * For example, an extension field defined like so:
55 *
56 *     extend BaseMessage {
57 *       optional MyMessage my_field = 123;
58 *     }
59 *
60 * will result in an ExtensionFieldInfo object with these properties:
61 *
62 *     {
63 *       fieldIndex: 123,
64 *       fieldName: {my_field_renamed: 0},
65 *       ctor: proto.example.MyMessage,
66 *       toObjectFn: proto.example.MyMessage.toObject,
67 *       isRepeated: 0
68 *     }
69 *
70 * We include `toObjectFn` to allow the JSCompiler to perform dead-code removal
71 * on unused toObject() methods.
72 *
73 * If an extension field is primitive, ctor and toObjectFn will be null.
74 * isRepeated should be 0 or 1.
75 *
76 * binary{Reader,Writer}Fn and (if message type) binaryMessageSerializeFn are
77 * always provided. binaryReaderFn and binaryWriterFn are references to the
78 * appropriate methods on BinaryReader/BinaryWriter to read/write the value of
79 * this extension, and binaryMessageSerializeFn is a reference to the message
80 * class's .serializeBinary method, if available.
81 *
82 * @param {number} fieldNumber
83 * @param {Object} fieldName This has the extension field name as a property.
84 * @param {?function(new: jspb.Message, Array=)} ctor
85 * @param {?function((boolean|undefined),!jspb.Message):!Object} toObjectFn
86 * @param {number} isRepeated
87 * @constructor
88 * @struct
89 * @template T
90 */
91jspb.ExtensionFieldInfo = function(fieldNumber, fieldName, ctor, toObjectFn,
92    isRepeated) {
93  /** @const */
94  this.fieldIndex = fieldNumber;
95  /** @const */
96  this.fieldName = fieldName;
97  /** @const */
98  this.ctor = ctor;
99  /** @const */
100  this.toObjectFn = toObjectFn;
101  /** @const */
102  this.isRepeated = isRepeated;
103};
104
105/**
106 * Stores binary-related information for a single extension field.
107 * @param {!jspb.ExtensionFieldInfo<T>} fieldInfo
108 * @param {function(this:jspb.BinaryReader,number,?,?)} binaryReaderFn
109 * @param {function(this:jspb.BinaryWriter,number,?)
110 *        |function(this:jspb.BinaryWriter,number,?,?,?,?,?)} binaryWriterFn
111 * @param {function(?,?)=} opt_binaryMessageSerializeFn
112 * @param {function(?,?)=} opt_binaryMessageDeserializeFn
113 * @param {boolean=} opt_isPacked
114 * @constructor
115 * @struct
116 * @template T
117 */
118jspb.ExtensionFieldBinaryInfo = function(fieldInfo, binaryReaderFn, binaryWriterFn,
119    opt_binaryMessageSerializeFn, opt_binaryMessageDeserializeFn, opt_isPacked) {
120  /** @const */
121  this.fieldInfo = fieldInfo;
122  /** @const */
123  this.binaryReaderFn = binaryReaderFn;
124  /** @const */
125  this.binaryWriterFn = binaryWriterFn;
126  /** @const */
127  this.binaryMessageSerializeFn = opt_binaryMessageSerializeFn;
128  /** @const */
129  this.binaryMessageDeserializeFn = opt_binaryMessageDeserializeFn;
130  /** @const */
131  this.isPacked = opt_isPacked;
132};
133
134/**
135 * @return {boolean} Does this field represent a sub Message?
136 */
137jspb.ExtensionFieldInfo.prototype.isMessageType = function() {
138  return !!this.ctor;
139};
140
141
142/**
143 * Base class for all JsPb messages.
144 *
145 * Several common methods (toObject, serializeBinary, in particular) are not
146 * defined on the prototype to encourage code patterns that minimize code bloat
147 * due to otherwise unused code on all protos contained in the project.
148 *
149 * If you want to call these methods on a generic message, either
150 * pass in your instance of method as a parameter:
151 *     someFunction(instanceOfKnownProto,
152 *                  KnownProtoClass.prototype.serializeBinary);
153 * or use a lambda that knows the type:
154 *     someFunction(()=>instanceOfKnownProto.serializeBinary());
155 * or, if you don't care about code size, just suppress the
156 *     WARNING - Property serializeBinary never defined on jspb.Message
157 * and call it the intuitive way.
158 *
159 * @constructor
160 * @struct
161 */
162jspb.Message = function() {
163};
164
165
166/**
167 * @define {boolean} Whether to generate toObject methods for objects. Turn
168 *     this off, if you do not want toObject to be ever used in your project.
169 *     When turning off this flag, consider adding a conformance test that bans
170 *     calling toObject. Enabling this will disable the JSCompiler's ability to
171 *     dead code eliminate fields used in protocol buffers that are never used
172 *     in an application.
173 */
174jspb.Message.GENERATE_TO_OBJECT =
175    goog.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 *     By default this is enabled for test code only.
186 */
187jspb.Message.GENERATE_FROM_OBJECT = goog.define(
188    'jspb.Message.GENERATE_FROM_OBJECT', !goog.DISALLOW_TEST_ONLY_CODE);
189
190
191/**
192 * @define {boolean} Whether to generate toString methods for objects. Turn
193 *     this off if you do not use toString in your project and want to trim it
194 *     from the compiled JS.
195 */
196jspb.Message.GENERATE_TO_STRING =
197    goog.define('jspb.Message.GENERATE_TO_STRING', true);
198
199
200/**
201 * @define {boolean} Whether arrays passed to initialize() can be assumed to be
202 *     local (e.g. not from another iframe) and thus safely classified with
203 *     instanceof Array.
204 */
205jspb.Message.ASSUME_LOCAL_ARRAYS =
206    goog.define('jspb.Message.ASSUME_LOCAL_ARRAYS', false);
207
208
209// TODO(jakubvrana): Turn this off by default.
210/**
211 * @define {boolean} Disabling the serialization of empty trailing fields
212 *     reduces the size of serialized protos. The price is an extra iteration of
213 *     the proto before serialization. This is enabled by default to be
214 *     backwards compatible. Projects are advised to turn this flag always off.
215 */
216jspb.Message.SERIALIZE_EMPTY_TRAILING_FIELDS =
217    goog.define('jspb.Message.SERIALIZE_EMPTY_TRAILING_FIELDS', true);
218
219
220/**
221 * Does this JavaScript environment support Uint8Aray typed arrays?
222 * @type {boolean}
223 * @private
224 */
225jspb.Message.SUPPORTS_UINT8ARRAY_ = (typeof Uint8Array == 'function');
226
227
228/**
229 * 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/**
274 * Repeated fields that have been converted to their proper type. This is used
275 * for numbers stored as strings (typically "NaN", "Infinity" and "-Infinity")
276 * and for booleans stored as numbers (0 or 1).
277 * @private {!Object<number,boolean>|undefined}
278 */
279jspb.Message.prototype.convertedPrimitiveFields_;
280
281/**
282 * Repeated fields numbers.
283 * @protected {?Array<number>|undefined}
284 */
285jspb.Message.prototype.repeatedFields;
286
287
288
289/**
290 * Returns the JsPb message_id of this proto.
291 * @return {string|undefined} the message id or undefined if this message
292 *     has no id.
293 */
294jspb.Message.prototype.getJsPbMessageId = function() {
295  return this.messageId_;
296};
297
298
299/**
300 * An offset applied to lookups into this.array to account for the presence or
301 * absence of a messageId at position 0. For response messages, this will be 0.
302 * Otherwise, it will be -1 so that the first array position is not wasted.
303 * @type {number}
304 * @private
305 */
306jspb.Message.prototype.arrayIndexOffset_;
307
308
309/**
310 * Returns the index into msg.array at which the proto field with tag number
311 * fieldNumber will be located.
312 * @param {!jspb.Message} msg Message for which we're calculating an index.
313 * @param {number} fieldNumber The field number.
314 * @return {number} The index.
315 * @private
316 */
317jspb.Message.getIndex_ = function(msg, fieldNumber) {
318  return fieldNumber + msg.arrayIndexOffset_;
319};
320
321// This is only here to ensure we are not back sliding on ES6 requirements for
322// protos in g3.
323jspb.Message.hiddenES6Property_ = class {};
324
325
326/**
327 * Returns the tag number based on the index in msg.array.
328 * @param {!jspb.Message} msg Message for which we're calculating an index.
329 * @param {number} index The tag number.
330 * @return {number} The field number.
331 * @private
332 */
333jspb.Message.getFieldNumber_ = function(msg, index) {
334  return index - msg.arrayIndexOffset_;
335};
336
337
338/**
339 * Initializes a JsPb Message.
340 * @param {!jspb.Message} msg The JsPb proto to modify.
341 * @param {Array|undefined} data An initial data array.
342 * @param {string|number} messageId For response messages, the message id or ''
343 *     if no message id is specified. For non-response messages, 0.
344 * @param {number} suggestedPivot The field number at which to start putting
345 *     fields into the extension object. This is only used if data does not
346 *     contain an extension object already. -1 if no extension object is
347 *     required for this message type.
348 * @param {Array<number>} repeatedFields The message's repeated fields.
349 * @param {Array<!Array<number>>=} opt_oneofFields The fields belonging to
350 *     each of the message's oneof unions.
351 * @protected
352 */
353jspb.Message.initialize = function(
354    msg, data, messageId, suggestedPivot, repeatedFields, opt_oneofFields) {
355  msg.wrappers_ = null;
356  if (!data) {
357    data = messageId ? [messageId] : [];
358  }
359  msg.messageId_ = messageId ? String(messageId) : undefined;
360  // If the messageId is 0, this message is not a response message, so we shift
361  // array indices down by 1 so as not to waste the first position in the array,
362  // which would otherwise go unused.
363  msg.arrayIndexOffset_ = messageId === 0 ? -1 : 0;
364  msg.array = data;
365  jspb.Message.initPivotAndExtensionObject_(msg, suggestedPivot);
366  msg.convertedPrimitiveFields_ = {};
367
368  if (!jspb.Message.SERIALIZE_EMPTY_TRAILING_FIELDS) {
369    // TODO(jakubvrana): This is same for all instances, move to prototype.
370    // TODO(jakubvrana): There are indexOf calls on this in serialization,
371    // consider switching to a set.
372    msg.repeatedFields = repeatedFields;
373  }
374
375  if (repeatedFields) {
376    for (var i = 0; i < repeatedFields.length; i++) {
377      var fieldNumber = repeatedFields[i];
378      if (fieldNumber < msg.pivot_) {
379        var index = jspb.Message.getIndex_(msg, fieldNumber);
380        msg.array[index] =
381            msg.array[index] || jspb.Message.EMPTY_LIST_SENTINEL_;
382      } else {
383        jspb.Message.maybeInitEmptyExtensionObject_(msg);
384        msg.extensionObject_[fieldNumber] = msg.extensionObject_[fieldNumber] ||
385            jspb.Message.EMPTY_LIST_SENTINEL_;
386      }
387    }
388  }
389
390  if (opt_oneofFields && opt_oneofFields.length) {
391    // Compute the oneof case for each union. This ensures only one value is
392    // set in the union.
393    for (var i = 0; i < opt_oneofFields.length; i++) {
394      jspb.Message.computeOneofCase(msg, opt_oneofFields[i]);
395    }
396  }
397};
398
399
400/**
401 * Used to mark empty repeated fields. Serializes to null when serialized
402 * to JSON.
403 * When reading a repeated field readers must check the return value against
404 * this value and return and replace it with a new empty array if it is
405 * present.
406 * @private @const {!Object}
407 */
408jspb.Message.EMPTY_LIST_SENTINEL_ = goog.DEBUG && Object.freeze ?
409    Object.freeze([]) :
410    [];
411
412
413/**
414 * Returns true if the provided argument is an array.
415 * @param {*} o The object to classify as array or not.
416 * @return {boolean} True if the provided object is an array.
417 * @private
418 */
419jspb.Message.isArray_ = function(o) {
420  return jspb.Message.ASSUME_LOCAL_ARRAYS ? o instanceof Array :
421                                            Array.isArray(o);
422};
423
424/**
425 * Returns true if the provided argument is an extension object.
426 * @param {*} o The object to classify as array or not.
427 * @return {boolean} True if the provided object is an extension object.
428 * @private
429 */
430jspb.Message.isExtensionObject_ = function(o) {
431  // Normal fields are never objects, so we can be sure that if we find an
432  // object here, then it's the extension object. However, we must ensure that
433  // the object is not an array, since arrays are valid field values (bytes
434  // fields can also be array).
435  // NOTE(lukestebbing): We avoid looking at .length to avoid a JIT bug
436  // in Safari on iOS 8. See the description of CL/86511464 for details.
437  return (o !== null && typeof o == 'object' &&
438      !jspb.Message.isArray_(o) &&
439      !(jspb.Message.SUPPORTS_UINT8ARRAY_ && o instanceof Uint8Array));
440};
441
442
443/**
444 * If the array contains an extension object in its last position, then the
445 * object is kept in place and its position is used as the pivot.  If not,
446 * decides the pivot of the message based on suggestedPivot without
447 * materializing the extension object.
448 *
449 * @param {!jspb.Message} msg The JsPb proto to modify.
450 * @param {number} suggestedPivot See description for initialize().
451 * @private
452 */
453jspb.Message.initPivotAndExtensionObject_ = function(msg, suggestedPivot) {
454  // There are 3 variants that need to be dealt with which are the
455  // combination of whether there exists an extension object (EO) and
456  // whether there is a suggested pivot (SP).
457  //
458  // EO,    ?    : pivot is the index of the EO
459  // no-EO, no-SP: pivot is MAX_INT
460  // no-EO, SP   : pivot is the max(lastindex + 1, SP)
461
462  var msgLength = msg.array.length;
463  var lastIndex = -1;
464  if (msgLength) {
465    lastIndex = msgLength - 1;
466    var obj = msg.array[lastIndex];
467    if (jspb.Message.isExtensionObject_(obj)) {
468      msg.pivot_ = jspb.Message.getFieldNumber_(msg, lastIndex);
469      msg.extensionObject_ = obj;
470      return;
471    }
472  }
473
474  if (suggestedPivot > -1) {
475    // If a extension object is not present, set the pivot value as being
476    // after the last value in the array to avoid overwriting values, etc.
477    msg.pivot_ = Math.max(
478        suggestedPivot, jspb.Message.getFieldNumber_(msg, lastIndex + 1));
479    // Avoid changing the shape of the proto with an empty extension object by
480    // deferring the materialization of the extension object until the first
481    // time a field set into it (may be due to getting a repeated proto field
482    // from it, in which case a new empty array is set into it at first).
483    msg.extensionObject_ = null;
484  } else {
485    // suggestedPivot is -1, which means that we don't have an extension object
486    // at all, in which case all fields are stored in the array.
487    msg.pivot_ = Number.MAX_VALUE;
488  }
489};
490
491
492/**
493 * Creates an empty extensionObject_ if non exists.
494 * @param {!jspb.Message} msg The JsPb proto to modify.
495 * @private
496 */
497jspb.Message.maybeInitEmptyExtensionObject_ = function(msg) {
498  var pivotIndex = jspb.Message.getIndex_(msg, msg.pivot_);
499  if (!msg.array[pivotIndex]) {
500    msg.extensionObject_ = msg.array[pivotIndex] = {};
501  }
502};
503
504
505/**
506 * Converts a JsPb repeated message field into an object list.
507 * @param {!Array<T>} field The repeated message field to be
508 *     converted.
509 * @param {?function(boolean=): Object|
510 *     function((boolean|undefined),T): Object} toObjectFn The toObject
511 *     function for this field.  We need to pass this for effective dead code
512 *     removal.
513 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
514 *     for transitional soy proto support: http://goto/soy-param-migration
515 * @return {!Array<Object>} An array of converted message objects.
516 * @template T
517 */
518jspb.Message.toObjectList = function(field, toObjectFn, opt_includeInstance) {
519  // Not using goog.array.map in the generated code to keep it small.
520  // And not using it here to avoid a function call.
521  var result = [];
522  for (var i = 0; i < field.length; i++) {
523    result[i] = toObjectFn.call(field[i], opt_includeInstance, field[i]);
524  }
525  return result;
526};
527
528
529/**
530 * Adds a proto's extension data to a Soy rendering object.
531 * @param {!jspb.Message} proto The proto whose extensions to convert.
532 * @param {!Object} obj The Soy object to add converted extension data to.
533 * @param {!Object} extensions The proto class' registered extensions.
534 * @param {function(this:?, jspb.ExtensionFieldInfo) : *} getExtensionFn
535 *     The proto class' getExtension function. Passed for effective dead code
536 *     removal.
537 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
538 *     for transitional soy proto support: http://goto/soy-param-migration
539 */
540jspb.Message.toObjectExtension = function(proto, obj, extensions,
541    getExtensionFn, opt_includeInstance) {
542  for (var fieldNumber in extensions) {
543    var fieldInfo = extensions[fieldNumber];
544    var value = getExtensionFn.call(proto, fieldInfo);
545    if (value != null) {
546      for (var name in fieldInfo.fieldName) {
547        if (fieldInfo.fieldName.hasOwnProperty(name)) {
548          break; // the compiled field name
549        }
550      }
551      if (!fieldInfo.toObjectFn) {
552        obj[name] = value;
553      } else {
554        if (fieldInfo.isRepeated) {
555          obj[name] = jspb.Message.toObjectList(
556              /** @type {!Array<!jspb.Message>} */ (value),
557              fieldInfo.toObjectFn, opt_includeInstance);
558        } else {
559          obj[name] = fieldInfo.toObjectFn(
560              opt_includeInstance, /** @type {!jspb.Message} */ (value));
561        }
562      }
563    }
564  }
565};
566
567
568/**
569 * Writes a proto's extension data to a binary-format output stream.
570 * @param {!jspb.Message} proto The proto whose extensions to convert.
571 * @param {*} writer The binary-format writer to write to.
572 * @param {!Object} extensions The proto class' registered extensions.
573 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo) : *} getExtensionFn The proto
574 *     class' getExtension function. Passed for effective dead code removal.
575 */
576jspb.Message.serializeBinaryExtensions = function(proto, writer, extensions,
577    getExtensionFn) {
578  for (var fieldNumber in extensions) {
579    var binaryFieldInfo = extensions[fieldNumber];
580    var fieldInfo = binaryFieldInfo.fieldInfo;
581
582    // The old codegen doesn't add the extra fields to ExtensionFieldInfo, so we
583    // need to gracefully error-out here rather than produce a null dereference
584    // below.
585    if (!binaryFieldInfo.binaryWriterFn) {
586      throw new Error('Message extension present that was generated ' +
587                      'without binary serialization support');
588    }
589    var value = getExtensionFn.call(proto, fieldInfo);
590    if (value != null) {
591      if (fieldInfo.isMessageType()) {
592        // If the message type of the extension was generated without binary
593        // support, there may not be a binary message serializer function, and
594        // we can't know when we codegen the extending message that the extended
595        // message may require binary support, so we can *only* catch this error
596        // here, at runtime (and this decoupled codegen is the whole point of
597        // extensions!).
598        if (binaryFieldInfo.binaryMessageSerializeFn) {
599          binaryFieldInfo.binaryWriterFn.call(writer, fieldInfo.fieldIndex,
600              value, binaryFieldInfo.binaryMessageSerializeFn);
601        } else {
602          throw new Error('Message extension present holding submessage ' +
603                          'without binary support enabled, and message is ' +
604                          'being serialized to binary format');
605        }
606      } else {
607        binaryFieldInfo.binaryWriterFn.call(
608            writer, fieldInfo.fieldIndex, value);
609      }
610    }
611  }
612};
613
614
615/**
616 * Reads an extension field from the given reader and, if a valid extension,
617 * sets the extension value.
618 * @param {!jspb.Message} msg A jspb proto.
619 * @param {!jspb.BinaryReader} reader
620 * @param {!Object} extensions The extensions object.
621 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo)} getExtensionFn
622 * @param {function(this:jspb.Message,!jspb.ExtensionFieldInfo, ?)} setExtensionFn
623 */
624jspb.Message.readBinaryExtension = function(msg, reader, extensions,
625    getExtensionFn, setExtensionFn) {
626  var binaryFieldInfo = extensions[reader.getFieldNumber()];
627  if (!binaryFieldInfo) {
628    reader.skipField();
629    return;
630  }
631  var fieldInfo = binaryFieldInfo.fieldInfo;
632  if (!binaryFieldInfo.binaryReaderFn) {
633    throw new Error('Deserializing extension whose generated code does not ' +
634                    'support binary format');
635  }
636
637  var value;
638  if (fieldInfo.isMessageType()) {
639    value = new fieldInfo.ctor();
640    binaryFieldInfo.binaryReaderFn.call(
641        reader, value, binaryFieldInfo.binaryMessageDeserializeFn);
642  } else {
643    // All other types.
644    value = binaryFieldInfo.binaryReaderFn.call(reader);
645  }
646
647  if (fieldInfo.isRepeated && !binaryFieldInfo.isPacked) {
648    var currentList = getExtensionFn.call(msg, fieldInfo);
649    if (!currentList) {
650      setExtensionFn.call(msg, fieldInfo, [value]);
651    } else {
652      currentList.push(value);
653    }
654  } else {
655    setExtensionFn.call(msg, fieldInfo, value);
656  }
657};
658
659
660/**
661 * Gets the value of a non-extension field.
662 * @param {!jspb.Message} msg A jspb proto.
663 * @param {number} fieldNumber The field number.
664 * @return {string|number|boolean|Uint8Array|Array|null|undefined}
665 * The field's value.
666 * @protected
667 */
668jspb.Message.getField = function(msg, fieldNumber) {
669  if (fieldNumber < msg.pivot_) {
670    var index = jspb.Message.getIndex_(msg, fieldNumber);
671    var val = msg.array[index];
672    if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
673      return msg.array[index] = [];
674    }
675    return val;
676  } else {
677    if (!msg.extensionObject_) {
678      return undefined;
679    }
680    var val = msg.extensionObject_[fieldNumber];
681    if (val === jspb.Message.EMPTY_LIST_SENTINEL_) {
682      return msg.extensionObject_[fieldNumber] = [];
683    }
684    return val;
685  }
686};
687
688
689/**
690 * Gets the value of a non-extension repeated field.
691 * @param {!jspb.Message} msg A jspb proto.
692 * @param {number} fieldNumber The field number.
693 * @return {!Array}
694 * The field's value.
695 * @protected
696 */
697jspb.Message.getRepeatedField = function(msg, fieldNumber) {
698  return /** @type {!Array} */ (jspb.Message.getField(msg, fieldNumber));
699};
700
701
702/**
703 * Gets the value of an optional float or double field.
704 * @param {!jspb.Message} msg A jspb proto.
705 * @param {number} fieldNumber The field number.
706 * @return {?number|undefined} The field's value.
707 * @protected
708 */
709jspb.Message.getOptionalFloatingPointField = function(msg, fieldNumber) {
710  var value = jspb.Message.getField(msg, fieldNumber);
711  // Converts "NaN", "Infinity" and "-Infinity" to their corresponding numbers.
712  return value == null ? value : +value;
713};
714
715
716/**
717 * Gets the value of an optional boolean field.
718 * @param {!jspb.Message} msg A jspb proto.
719 * @param {number} fieldNumber The field number.
720 * @return {?boolean|undefined} The field's value.
721 * @protected
722 */
723jspb.Message.getBooleanField = function(msg, fieldNumber) {
724  var value = jspb.Message.getField(msg, fieldNumber);
725  // TODO(b/122673075): always return null when the value is null-ish.
726  return value == null ? (value) : !!value;
727};
728
729
730/**
731 * Gets the value of a repeated float or double field.
732 * @param {!jspb.Message} msg A jspb proto.
733 * @param {number} fieldNumber The field number.
734 * @return {!Array<number>} The field's value.
735 * @protected
736 */
737jspb.Message.getRepeatedFloatingPointField = function(msg, fieldNumber) {
738  var values = jspb.Message.getRepeatedField(msg, fieldNumber);
739  if (!msg.convertedPrimitiveFields_) {
740    msg.convertedPrimitiveFields_ = {};
741  }
742  if (!msg.convertedPrimitiveFields_[fieldNumber]) {
743    for (var i = 0; i < values.length; i++) {
744      // Converts "NaN", "Infinity" and "-Infinity" to their corresponding
745      // numbers.
746      values[i] = +values[i];
747    }
748    msg.convertedPrimitiveFields_[fieldNumber] = true;
749  }
750  return /** @type {!Array<number>} */ (values);
751};
752
753/**
754 * Gets the value of a repeated boolean field.
755 * @param {!jspb.Message} msg A jspb proto.
756 * @param {number} fieldNumber The field number.
757 * @return {!Array<boolean>} The field's value.
758 * @protected
759 */
760jspb.Message.getRepeatedBooleanField = function(msg, fieldNumber) {
761  var values = jspb.Message.getRepeatedField(msg, fieldNumber);
762  if (!msg.convertedPrimitiveFields_) {
763    msg.convertedPrimitiveFields_ = {};
764  }
765  if (!msg.convertedPrimitiveFields_[fieldNumber]) {
766    for (var i = 0; i < values.length; i++) {
767      // Converts 0 and 1 to their corresponding booleans.
768      values[i] = !!values[i];
769    }
770    msg.convertedPrimitiveFields_[fieldNumber] = true;
771  }
772  return /** @type {!Array<boolean>} */ (values);
773};
774
775
776/**
777 * Coerce a 'bytes' field to a base 64 string.
778 * @param {string|Uint8Array|null} value
779 * @return {?string} The field's coerced value.
780 */
781jspb.Message.bytesAsB64 = function(value) {
782  if (value == null || typeof value === 'string') {
783    return value;
784  }
785  if (jspb.Message.SUPPORTS_UINT8ARRAY_ && value instanceof Uint8Array) {
786    return goog.crypt.base64.encodeByteArray(value);
787  }
788  goog.asserts.fail('Cannot coerce to b64 string: ' + goog.typeOf(value));
789  return null;
790};
791
792
793/**
794 * Coerce a 'bytes' field to a Uint8Array byte buffer.
795 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
796 * Mini. @see http://caniuse.com/Uint8Array
797 * @param {string|Uint8Array|null} value
798 * @return {?Uint8Array} The field's coerced value.
799 */
800jspb.Message.bytesAsU8 = function(value) {
801  if (value == null || value instanceof Uint8Array) {
802    return value;
803  }
804  if (typeof value === 'string') {
805    return goog.crypt.base64.decodeStringToUint8Array(value);
806  }
807  goog.asserts.fail('Cannot coerce to Uint8Array: ' + goog.typeOf(value));
808  return null;
809};
810
811
812/**
813 * Coerce a repeated 'bytes' field to an array of base 64 strings.
814 * Note: the returned array should be treated as immutable.
815 * @param {!Array<string>|!Array<!Uint8Array>} value
816 * @return {!Array<string?>} The field's coerced value.
817 */
818jspb.Message.bytesListAsB64 = function(value) {
819  jspb.Message.assertConsistentTypes_(value);
820  if (!value.length || typeof value[0] === 'string') {
821    return /** @type {!Array<string>} */ (value);
822  }
823  return goog.array.map(value, jspb.Message.bytesAsB64);
824};
825
826
827/**
828 * Coerce a repeated 'bytes' field to an array of Uint8Array byte buffers.
829 * Note: the returned array should be treated as immutable.
830 * Note that Uint8Array is not supported on IE versions before 10 nor on Opera
831 * Mini. @see http://caniuse.com/Uint8Array
832 * @param {!Array<string>|!Array<!Uint8Array>} value
833 * @return {!Array<Uint8Array?>} The field's coerced value.
834 */
835jspb.Message.bytesListAsU8 = function(value) {
836  jspb.Message.assertConsistentTypes_(value);
837  if (!value.length || value[0] instanceof Uint8Array) {
838    return /** @type {!Array<!Uint8Array>} */ (value);
839  }
840  return goog.array.map(value, jspb.Message.bytesAsU8);
841};
842
843
844/**
845 * Asserts that all elements of an array are of the same type.
846 * @param {Array?} array The array to test.
847 * @private
848 */
849jspb.Message.assertConsistentTypes_ = function(array) {
850  if (goog.DEBUG && array && array.length > 1) {
851    var expected = goog.typeOf(array[0]);
852    goog.array.forEach(array, function(e) {
853      if (goog.typeOf(e) != expected) {
854        goog.asserts.fail('Inconsistent type in JSPB repeated field array. ' +
855            'Got ' + goog.typeOf(e) + ' expected ' + expected);
856      }
857    });
858  }
859};
860
861
862/**
863 * Gets the value of a non-extension primitive field, with proto3 (non-nullable
864 * primitives) semantics. Returns `defaultValue` if the field is not otherwise
865 * set.
866 * @template T
867 * @param {!jspb.Message} msg A jspb proto.
868 * @param {number} fieldNumber The field number.
869 * @param {T} defaultValue The default value.
870 * @return {T} The field's value.
871 * @protected
872 */
873jspb.Message.getFieldWithDefault = function(msg, fieldNumber, defaultValue) {
874  var value = jspb.Message.getField(msg, fieldNumber);
875  if (value == null) {
876    return defaultValue;
877  } else {
878    return value;
879  }
880};
881
882
883/**
884 * Gets the value of a boolean field, with proto3 (non-nullable primitives)
885 * semantics. Returns `defaultValue` if the field is not otherwise set.
886 * @template T
887 * @param {!jspb.Message} msg A jspb proto.
888 * @param {number} fieldNumber The field number.
889 * @param {boolean} defaultValue The default value.
890 * @return {boolean} The field's value.
891 * @protected
892 */
893jspb.Message.getBooleanFieldWithDefault = function(
894    msg, fieldNumber, defaultValue) {
895  var value = jspb.Message.getBooleanField(msg, fieldNumber);
896  if (value == null) {
897    return defaultValue;
898  } else {
899    return value;
900  }
901};
902
903
904/**
905 * Gets the value of a floating point field, with proto3 (non-nullable
906 * primitives) semantics. Returns `defaultValue` if the field is not otherwise
907 * set.
908 * @template T
909 * @param {!jspb.Message} msg A jspb proto.
910 * @param {number} fieldNumber The field number.
911 * @param {number} defaultValue The default value.
912 * @return {number} The field's value.
913 * @protected
914 */
915jspb.Message.getFloatingPointFieldWithDefault = function(
916    msg, fieldNumber, defaultValue) {
917  var value = jspb.Message.getOptionalFloatingPointField(msg, fieldNumber);
918  if (value == null) {
919    return defaultValue;
920  } else {
921    return value;
922  }
923};
924
925
926/**
927 * Alias for getFieldWithDefault used by older generated code.
928 * @template T
929 * @param {!jspb.Message} msg A jspb proto.
930 * @param {number} fieldNumber The field number.
931 * @param {T} defaultValue The default value.
932 * @return {T} The field's value.
933 * @protected
934 */
935jspb.Message.getFieldProto3 = jspb.Message.getFieldWithDefault;
936
937
938/**
939 * Gets the value of a map field, lazily creating the map container if
940 * necessary.
941 *
942 * This should only be called from generated code, because it requires knowledge
943 * of serialization/parsing callbacks (which are required by the map at
944 * construction time, and the map may be constructed here).
945 *
946 * @template K, V
947 * @param {!jspb.Message} msg
948 * @param {number} fieldNumber
949 * @param {boolean|undefined} noLazyCreate
950 * @param {?=} opt_valueCtor
951 * @return {!jspb.Map<K, V>|undefined}
952 * @protected
953 */
954jspb.Message.getMapField = function(msg, fieldNumber, noLazyCreate,
955    opt_valueCtor) {
956  if (!msg.wrappers_) {
957    msg.wrappers_ = {};
958  }
959  // If we already have a map in the map wrappers, return that.
960  if (fieldNumber in msg.wrappers_) {
961    return msg.wrappers_[fieldNumber];
962  }
963  var arr = jspb.Message.getField(msg, fieldNumber);
964  // Wrap the underlying elements array with a Map.
965  if (!arr) {
966    if (noLazyCreate) {
967      return undefined;
968    }
969    arr = [];
970    jspb.Message.setField(msg, fieldNumber, arr);
971  }
972  return msg.wrappers_[fieldNumber] =
973      new jspb.Map(
974          /** @type {!Array<!Array<!Object>>} */ (arr), opt_valueCtor);
975};
976
977
978/**
979 * Sets the value of a non-extension field.
980 * @param {T} msg A jspb proto.
981 * @param {number} fieldNumber The field number.
982 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
983 * @return {T} return msg
984 * @template T
985 * @protected
986 */
987jspb.Message.setField = function(msg, fieldNumber, value) {
988  // TODO(b/35241823): replace this with a bounded generic when available
989  goog.asserts.assertInstanceof(msg, jspb.Message);
990  if (fieldNumber < msg.pivot_) {
991    msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = value;
992  } else {
993    jspb.Message.maybeInitEmptyExtensionObject_(msg);
994    msg.extensionObject_[fieldNumber] = value;
995  }
996  return msg;
997};
998
999
1000/**
1001 * Sets the value of a non-extension integer field of a proto3
1002 * @param {T} msg A jspb proto.
1003 * @param {number} fieldNumber The field number.
1004 * @param {number} value New value
1005 * @return {T} return msg
1006 * @template T
1007 * @protected
1008 */
1009jspb.Message.setProto3IntField = function(msg, fieldNumber, value) {
1010  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0);
1011};
1012
1013
1014/**
1015 * Sets the value of a non-extension floating point field of a proto3
1016 * @param {T} msg A jspb proto.
1017 * @param {number} fieldNumber The field number.
1018 * @param {number} value New value
1019 * @return {T} return msg
1020 * @template T
1021 * @protected
1022 */
1023jspb.Message.setProto3FloatField = function(msg, fieldNumber, value) {
1024  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0.0);
1025};
1026
1027
1028/**
1029 * Sets the value of a non-extension boolean field of a proto3
1030 * @param {T} msg A jspb proto.
1031 * @param {number} fieldNumber The field number.
1032 * @param {boolean} value New value
1033 * @return {T} return msg
1034 * @template T
1035 * @protected
1036 */
1037jspb.Message.setProto3BooleanField = function(msg, fieldNumber, value) {
1038  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, false);
1039};
1040
1041
1042/**
1043 * Sets the value of a non-extension String field of a proto3
1044 * @param {T} msg A jspb proto.
1045 * @param {number} fieldNumber The field number.
1046 * @param {string} value New value
1047 * @return {T} return msg
1048 * @template T
1049 * @protected
1050 */
1051jspb.Message.setProto3StringField = function(msg, fieldNumber, value) {
1052  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, '');
1053};
1054
1055
1056/**
1057 * Sets the value of a non-extension Bytes field of a proto3
1058 * @param {T} msg A jspb proto.
1059 * @param {number} fieldNumber The field number.
1060 * @param {!Uint8Array|string} value New value
1061 * @return {T} return msg
1062 * @template T
1063 * @protected
1064 */
1065jspb.Message.setProto3BytesField = function(msg, fieldNumber, value) {
1066  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, '');
1067};
1068
1069
1070/**
1071 * Sets the value of a non-extension enum field of a proto3
1072 * @param {T} msg A jspb proto.
1073 * @param {number} fieldNumber The field number.
1074 * @param {number} value New value
1075 * @return {T} return msg
1076 * @template T
1077 * @protected
1078 */
1079jspb.Message.setProto3EnumField = function(msg, fieldNumber, value) {
1080  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, 0);
1081};
1082
1083
1084/**
1085 * Sets the value of a non-extension int field of a proto3 that has jstype set
1086 * to String.
1087 * @param {T} msg A jspb proto.
1088 * @param {number} fieldNumber The field number.
1089 * @param {string} value New value
1090 * @return {T} return msg
1091 * @template T
1092 * @protected
1093 */
1094jspb.Message.setProto3StringIntField = function(msg, fieldNumber, value) {
1095  return jspb.Message.setFieldIgnoringDefault_(msg, fieldNumber, value, '0');
1096};
1097
1098/**
1099 * Sets the value of a non-extension primitive field, with proto3 (non-nullable
1100 * primitives) semantics of ignoring values that are equal to the type's
1101 * default.
1102 * @param {T} msg A jspb proto.
1103 * @param {number} fieldNumber The field number.
1104 * @param {!Uint8Array|string|number|boolean|undefined} value New value
1105 * @param {!Uint8Array|string|number|boolean} defaultValue The default value.
1106 * @return {T} return msg
1107 * @template T
1108 * @private
1109 */
1110jspb.Message.setFieldIgnoringDefault_ = function(
1111    msg, fieldNumber, value, defaultValue) {
1112  // TODO(b/35241823): replace this with a bounded generic when available
1113  goog.asserts.assertInstanceof(msg, jspb.Message);
1114  if (value !== defaultValue) {
1115    jspb.Message.setField(msg, fieldNumber, value);
1116  } else if (fieldNumber < msg.pivot_) {
1117    msg.array[jspb.Message.getIndex_(msg, fieldNumber)] = null;
1118  } else {
1119    jspb.Message.maybeInitEmptyExtensionObject_(msg);
1120    delete msg.extensionObject_[fieldNumber];
1121  }
1122  return msg;
1123};
1124
1125
1126/**
1127 * Adds a value to a repeated, primitive field.
1128 * @param {T} msg A jspb proto.
1129 * @param {number} fieldNumber The field number.
1130 * @param {string|number|boolean|!Uint8Array} value New value
1131 * @param {number=} opt_index Index where to put new value.
1132 * @return {T} return msg
1133 * @template T
1134 * @protected
1135 */
1136jspb.Message.addToRepeatedField = function(msg, fieldNumber, value, opt_index) {
1137  // TODO(b/35241823): replace this with a bounded generic when available
1138  goog.asserts.assertInstanceof(msg, jspb.Message);
1139  var arr = jspb.Message.getRepeatedField(msg, fieldNumber);
1140  if (opt_index != undefined) {
1141    arr.splice(opt_index, 0, value);
1142  } else {
1143    arr.push(value);
1144  }
1145  return msg;
1146};
1147
1148
1149/**
1150 * Sets the value of a field in a oneof union and clears all other fields in
1151 * the union.
1152 * @param {T} msg A jspb proto.
1153 * @param {number} fieldNumber The field number.
1154 * @param {!Array<number>} oneof The fields belonging to the union.
1155 * @param {string|number|boolean|Uint8Array|Array|undefined} value New value
1156 * @return {T} return msg
1157 * @template T
1158 * @protected
1159 */
1160jspb.Message.setOneofField = function(msg, fieldNumber, oneof, value) {
1161  // TODO(b/35241823): replace this with a bounded generic when available
1162  goog.asserts.assertInstanceof(msg, jspb.Message);
1163  var currentCase = jspb.Message.computeOneofCase(msg, oneof);
1164  if (currentCase && currentCase !== fieldNumber && value !== undefined) {
1165    if (msg.wrappers_ && currentCase in msg.wrappers_) {
1166      msg.wrappers_[currentCase] = undefined;
1167    }
1168    jspb.Message.setField(msg, currentCase, undefined);
1169  }
1170  return jspb.Message.setField(msg, fieldNumber, value);
1171};
1172
1173
1174/**
1175 * Computes the selection in a oneof group for the given message, ensuring
1176 * only one field is set in the process.
1177 *
1178 * According to the protobuf language guide (
1179 * https://developers.google.com/protocol-buffers/docs/proto#oneof), "if the
1180 * parser encounters multiple members of the same oneof on the wire, only the
1181 * last member seen is used in the parsed message." Since JSPB serializes
1182 * messages to a JSON array, the "last member seen" will always be the field
1183 * with the greatest field number (directly corresponding to the greatest
1184 * array index).
1185 *
1186 * @param {!jspb.Message} msg A jspb proto.
1187 * @param {!Array<number>} oneof The field numbers belonging to the union.
1188 * @return {number} The field number currently set in the union, or 0 if none.
1189 * @protected
1190 */
1191jspb.Message.computeOneofCase = function(msg, oneof) {
1192  var oneofField;
1193  var oneofValue;
1194
1195  for (var i = 0; i < oneof.length; i++) {
1196    var fieldNumber = oneof[i];
1197    var value = jspb.Message.getField(msg, fieldNumber);
1198    if (value != null) {
1199      oneofField = fieldNumber;
1200      oneofValue = value;
1201      jspb.Message.setField(msg, fieldNumber, undefined);
1202    }
1203  }
1204
1205  if (oneofField) {
1206    // NB: We know the value is unique, so we can call jspb.Message.setField
1207    // directly instead of jpsb.Message.setOneofField. Also, setOneofField
1208    // calls this function.
1209    jspb.Message.setField(msg, oneofField, oneofValue);
1210    return oneofField;
1211  }
1212
1213  return 0;
1214};
1215
1216
1217/**
1218 * Gets and wraps a proto field on access.
1219 * @param {!jspb.Message} msg A jspb proto.
1220 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
1221 * @param {number} fieldNumber The field number.
1222 * @param {number=} opt_required True (1) if this is a required field.
1223 * @return {jspb.Message} The field as a jspb proto.
1224 * @protected
1225 */
1226jspb.Message.getWrapperField = function(msg, ctor, fieldNumber, opt_required) {
1227  // TODO(mwr): Consider copying data and/or arrays.
1228  if (!msg.wrappers_) {
1229    msg.wrappers_ = {};
1230  }
1231  if (!msg.wrappers_[fieldNumber]) {
1232    var data = /** @type {Array} */ (jspb.Message.getField(msg, fieldNumber));
1233    if (opt_required || data) {
1234      // TODO(mwr): Remove existence test for always valid default protos.
1235      msg.wrappers_[fieldNumber] = new ctor(data);
1236    }
1237  }
1238  return /** @type {jspb.Message} */ (msg.wrappers_[fieldNumber]);
1239};
1240
1241
1242/**
1243 * Gets and wraps a repeated proto field on access.
1244 * @param {!jspb.Message} msg A jspb proto.
1245 * @param {function(new:jspb.Message, Array)} ctor Constructor for the field.
1246 * @param {number} fieldNumber The field number.
1247 * @return {!Array<!jspb.Message>} The repeated field as an array of protos.
1248 * @protected
1249 */
1250jspb.Message.getRepeatedWrapperField = function(msg, ctor, fieldNumber) {
1251  jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber);
1252  var val = msg.wrappers_[fieldNumber];
1253  if (val == jspb.Message.EMPTY_LIST_SENTINEL_) {
1254    val = msg.wrappers_[fieldNumber] = [];
1255  }
1256  return /** @type {!Array<!jspb.Message>} */ (val);
1257};
1258
1259
1260/**
1261 * Wraps underlying array into proto message representation if it wasn't done
1262 * before.
1263 * @param {!jspb.Message} msg A jspb proto.
1264 * @param {function(new:jspb.Message, ?Array)} ctor Constructor for the field.
1265 * @param {number} fieldNumber The field number.
1266 * @private
1267 */
1268jspb.Message.wrapRepeatedField_ = function(msg, ctor, fieldNumber) {
1269  if (!msg.wrappers_) {
1270    msg.wrappers_ = {};
1271  }
1272  if (!msg.wrappers_[fieldNumber]) {
1273    var data = jspb.Message.getRepeatedField(msg, fieldNumber);
1274    for (var wrappers = [], i = 0; i < data.length; i++) {
1275      wrappers[i] = new ctor(data[i]);
1276    }
1277    msg.wrappers_[fieldNumber] = wrappers;
1278  }
1279};
1280
1281
1282/**
1283 * Sets a proto field and syncs it to the backing array.
1284 * @param {T} msg A jspb proto.
1285 * @param {number} fieldNumber The field number.
1286 * @param {?jspb.Message|?jspb.Map|undefined} value A new value for this proto
1287 * field.
1288 * @return {T} the msg
1289 * @template T
1290 * @protected
1291 */
1292jspb.Message.setWrapperField = function(msg, fieldNumber, value) {
1293  // TODO(b/35241823): replace this with a bounded generic when available
1294  goog.asserts.assertInstanceof(msg, jspb.Message);
1295  if (!msg.wrappers_) {
1296    msg.wrappers_ = {};
1297  }
1298  var data = value ? value.toArray() : value;
1299  msg.wrappers_[fieldNumber] = value;
1300  return jspb.Message.setField(msg, fieldNumber, data);
1301};
1302
1303
1304
1305/**
1306 * Sets a proto field in a oneof union and syncs it to the backing array.
1307 * @param {T} msg A jspb proto.
1308 * @param {number} fieldNumber The field number.
1309 * @param {!Array<number>} oneof The fields belonging to the union.
1310 * @param {jspb.Message|undefined} value A new value for this proto field.
1311 * @return {T} the msg
1312 * @template T
1313 * @protected
1314 */
1315jspb.Message.setOneofWrapperField = function(msg, fieldNumber, oneof, value) {
1316  // TODO(b/35241823): replace this with a bounded generic when available
1317  goog.asserts.assertInstanceof(msg, jspb.Message);
1318  if (!msg.wrappers_) {
1319    msg.wrappers_ = {};
1320  }
1321  var data = value ? value.toArray() : value;
1322  msg.wrappers_[fieldNumber] = value;
1323  return jspb.Message.setOneofField(msg, fieldNumber, oneof, data);
1324};
1325
1326
1327/**
1328 * Sets a repeated proto field and syncs it to the backing array.
1329 * @param {T} msg A jspb proto.
1330 * @param {number} fieldNumber The field number.
1331 * @param {Array<!jspb.Message>|undefined} value An array of protos.
1332 * @return {T} the msg
1333 * @template T
1334 * @protected
1335 */
1336jspb.Message.setRepeatedWrapperField = function(msg, fieldNumber, value) {
1337  // TODO(b/35241823): replace this with a bounded generic when available
1338  goog.asserts.assertInstanceof(msg, jspb.Message);
1339  if (!msg.wrappers_) {
1340    msg.wrappers_ = {};
1341  }
1342  value = value || [];
1343  for (var data = [], i = 0; i < value.length; i++) {
1344    data[i] = value[i].toArray();
1345  }
1346  msg.wrappers_[fieldNumber] = value;
1347  return jspb.Message.setField(msg, fieldNumber, data);
1348};
1349
1350
1351/**
1352 * Add a message to a repeated proto field.
1353 * @param {!jspb.Message} msg A jspb proto.
1354 * @param {number} fieldNumber The field number.
1355 * @param {T_CHILD|undefined} value Proto that will be added to the
1356 *     repeated field.
1357 * @param {function(new:T_CHILD, ?Array=)} ctor The constructor of the
1358 *     message type.
1359 * @param {number|undefined} index Index at which to insert the value.
1360 * @return {T_CHILD_NOT_UNDEFINED} proto that was inserted to the repeated field
1361 * @template MessageType
1362 * Use go/closure-ttl to declare a non-undefined version of T_CHILD. Replace the
1363 * undefined in blah|undefined with none. This is necessary because the compiler
1364 * will infer T_CHILD to be |undefined.
1365 * @template T_CHILD
1366 * @template T_CHILD_NOT_UNDEFINED :=
1367 *     cond(isUnknown(T_CHILD), unknown(),
1368 *       mapunion(T_CHILD, (X) =>
1369 *         cond(eq(X, 'undefined'), none(), X)))
1370 * =:
1371 * @protected
1372 */
1373jspb.Message.addToRepeatedWrapperField = function(
1374    msg, fieldNumber, value, ctor, index) {
1375  jspb.Message.wrapRepeatedField_(msg, ctor, fieldNumber);
1376  var wrapperArray = msg.wrappers_[fieldNumber];
1377  if (!wrapperArray) {
1378    wrapperArray = msg.wrappers_[fieldNumber] = [];
1379  }
1380  var insertedValue = value ? value : new ctor();
1381  var array = jspb.Message.getRepeatedField(msg, fieldNumber);
1382  if (index != undefined) {
1383    wrapperArray.splice(index, 0, insertedValue);
1384    array.splice(index, 0, insertedValue.toArray());
1385  } else {
1386    wrapperArray.push(insertedValue);
1387    array.push(insertedValue.toArray());
1388  }
1389  return insertedValue;
1390};
1391
1392
1393/**
1394 * Converts a JsPb repeated message field into a map. The map will contain
1395 * protos unless an optional toObject function is given, in which case it will
1396 * contain objects suitable for Soy rendering.
1397 * @param {!Array<T>} field The repeated message field to be
1398 *     converted.
1399 * @param {function() : string?} mapKeyGetterFn The function to get the key of
1400 *     the map.
1401 * @param {?function(boolean=): Object|
1402 *     function((boolean|undefined),T): Object} opt_toObjectFn The
1403 *     toObject function for this field. We need to pass this for effective
1404 *     dead code removal.
1405 * @param {boolean=} opt_includeInstance Whether to include the JSPB instance
1406 *     for transitional soy proto support: http://goto/soy-param-migration
1407 * @return {!Object<string, Object>} A map of proto or Soy objects.
1408 * @template T
1409 */
1410jspb.Message.toMap = function(
1411    field, mapKeyGetterFn, opt_toObjectFn, opt_includeInstance) {
1412  var result = {};
1413  for (var i = 0; i < field.length; i++) {
1414    result[mapKeyGetterFn.call(field[i])] = opt_toObjectFn ?
1415        opt_toObjectFn.call(field[i], opt_includeInstance,
1416            /** @type {!jspb.Message} */ (field[i])) : field[i];
1417  }
1418  return result;
1419};
1420
1421
1422/**
1423 * Syncs all map fields' contents back to their underlying arrays.
1424 * @private
1425 */
1426jspb.Message.prototype.syncMapFields_ = function() {
1427  // This iterates over submessage, map, and repeated fields, which is intended.
1428  // Submessages can contain maps which also need to be synced.
1429  //
1430  // There is a lot of opportunity for optimization here.  For example we could
1431  // statically determine that some messages have no submessages with maps and
1432  // optimize this method away for those just by generating one extra static
1433  // boolean per message type.
1434  if (this.wrappers_) {
1435    for (var fieldNumber in this.wrappers_) {
1436      var val = this.wrappers_[fieldNumber];
1437      if (Array.isArray(val)) {
1438        for (var i = 0; i < val.length; i++) {
1439          if (val[i]) {
1440            val[i].toArray();
1441          }
1442        }
1443      } else {
1444        // Works for submessages and maps.
1445        if (val) {
1446          val.toArray();
1447        }
1448      }
1449    }
1450  }
1451};
1452
1453
1454/**
1455 * Returns the internal array of this proto.
1456 * <p>Note: If you use this array to construct a second proto, the content
1457 * would then be partially shared between the two protos.
1458 * @return {!Array} The proto represented as an array.
1459 */
1460jspb.Message.prototype.toArray = function() {
1461  this.syncMapFields_();
1462  return this.array;
1463};
1464
1465
1466
1467if (jspb.Message.GENERATE_TO_STRING) {
1468
1469/**
1470 * Creates a string representation of the internal data array of this proto.
1471 * <p>NOTE: This string is *not* suitable for use in server requests.
1472 * @return {string} A string representation of this proto.
1473 * @override
1474 */
1475jspb.Message.prototype.toString = function() {
1476  this.syncMapFields_();
1477  return this.array.toString();
1478};
1479
1480}
1481
1482/**
1483 * Gets the value of the extension field from the extended object.
1484 * @param {jspb.ExtensionFieldInfo<T>} fieldInfo Specifies the field to get.
1485 * @return {T} The value of the field.
1486 * @template T
1487 */
1488jspb.Message.prototype.getExtension = function(fieldInfo) {
1489  if (!this.extensionObject_) {
1490    return undefined;
1491  }
1492  if (!this.wrappers_) {
1493    this.wrappers_ = {};
1494  }
1495  var fieldNumber = fieldInfo.fieldIndex;
1496  if (fieldInfo.isRepeated) {
1497    if (fieldInfo.isMessageType()) {
1498      if (!this.wrappers_[fieldNumber]) {
1499        this.wrappers_[fieldNumber] =
1500            goog.array.map(this.extensionObject_[fieldNumber] || [],
1501                function(arr) {
1502                  return new fieldInfo.ctor(arr);
1503                });
1504      }
1505      return this.wrappers_[fieldNumber];
1506    } else {
1507      return this.extensionObject_[fieldNumber];
1508    }
1509  } else {
1510    if (fieldInfo.isMessageType()) {
1511      if (!this.wrappers_[fieldNumber] && this.extensionObject_[fieldNumber]) {
1512        this.wrappers_[fieldNumber] = new fieldInfo.ctor(
1513            /** @type {Array|undefined} */ (
1514                this.extensionObject_[fieldNumber]));
1515      }
1516      return this.wrappers_[fieldNumber];
1517    } else {
1518      return this.extensionObject_[fieldNumber];
1519    }
1520  }
1521};
1522
1523
1524/**
1525 * Sets the value of the extension field in the extended object.
1526 * @param {jspb.ExtensionFieldInfo} fieldInfo Specifies the field to set.
1527 * @param {jspb.Message|string|Uint8Array|number|boolean|Array?} value The value
1528 *     to set.
1529 * @return {THIS} For chaining
1530 * @this {THIS}
1531 * @template THIS
1532 */
1533jspb.Message.prototype.setExtension = function(fieldInfo, value) {
1534  // Cast self, since the inferred THIS is unknown inside the function body.
1535  // https://github.com/google/closure-compiler/issues/1411#issuecomment-232442220
1536  var self = /** @type {!jspb.Message} */ (this);
1537  if (!self.wrappers_) {
1538    self.wrappers_ = {};
1539  }
1540  jspb.Message.maybeInitEmptyExtensionObject_(self);
1541  var fieldNumber = fieldInfo.fieldIndex;
1542  if (fieldInfo.isRepeated) {
1543    value = value || [];
1544    if (fieldInfo.isMessageType()) {
1545      self.wrappers_[fieldNumber] = value;
1546      self.extensionObject_[fieldNumber] = goog.array.map(
1547          /** @type {!Array<!jspb.Message>} */ (value), function(msg) {
1548        return msg.toArray();
1549      });
1550    } else {
1551      self.extensionObject_[fieldNumber] = value;
1552    }
1553  } else {
1554    if (fieldInfo.isMessageType()) {
1555      self.wrappers_[fieldNumber] = value;
1556      self.extensionObject_[fieldNumber] =
1557          value ? /** @type {!jspb.Message} */ (value).toArray() : value;
1558    } else {
1559      self.extensionObject_[fieldNumber] = value;
1560    }
1561  }
1562  return self;
1563};
1564
1565
1566/**
1567 * Creates a difference object between two messages.
1568 *
1569 * The result will contain the top-level fields of m2 that differ from those of
1570 * m1 at any level of nesting. No data is cloned, the result object will
1571 * share its top-level elements with m2 (but not with m1).
1572 *
1573 * Note that repeated fields should not have null/undefined elements, but if
1574 * they do, this operation will treat repeated fields of different length as
1575 * the same if the only difference between them is due to trailing
1576 * null/undefined values.
1577 *
1578 * @param {!jspb.Message} m1 The first message object.
1579 * @param {!jspb.Message} m2 The second message object.
1580 * @return {!jspb.Message} The difference returned as a proto message.
1581 *     Note that the returned message may be missing required fields. This is
1582 *     currently tolerated in Js, but would cause an error if you tried to
1583 *     send such a proto to the server. You can access the raw difference
1584 *     array with result.toArray().
1585 * @throws {Error} If the messages are responses with different types.
1586 */
1587jspb.Message.difference = function(m1, m2) {
1588  if (!(m1 instanceof m2.constructor)) {
1589    throw new Error('Messages have different types.');
1590  }
1591  var arr1 = m1.toArray();
1592  var arr2 = m2.toArray();
1593  var res = [];
1594  var start = 0;
1595  var length = arr1.length > arr2.length ? arr1.length : arr2.length;
1596  if (m1.getJsPbMessageId()) {
1597    res[0] = m1.getJsPbMessageId();
1598    start = 1;
1599  }
1600  for (var i = start; i < length; i++) {
1601    if (!jspb.Message.compareFields(arr1[i], arr2[i])) {
1602      res[i] = arr2[i];
1603    }
1604  }
1605  return new m1.constructor(res);
1606};
1607
1608
1609/**
1610 * Tests whether two messages are equal.
1611 * @param {jspb.Message|undefined} m1 The first message object.
1612 * @param {jspb.Message|undefined} m2 The second message object.
1613 * @return {boolean} true if both messages are null/undefined, or if both are
1614 *     of the same type and have the same field values.
1615 */
1616jspb.Message.equals = function(m1, m2) {
1617  return m1 == m2 || (!!(m1 && m2) && (m1 instanceof m2.constructor) &&
1618      jspb.Message.compareFields(m1.toArray(), m2.toArray()));
1619};
1620
1621
1622/**
1623 * Compares two message extension fields recursively.
1624 * @param {!Object} extension1 The first field.
1625 * @param {!Object} extension2 The second field.
1626 * @return {boolean} true if the extensions are null/undefined, or otherwise
1627 *     equal.
1628 */
1629jspb.Message.compareExtensions = function(extension1, extension2) {
1630  extension1 = extension1 || {};
1631  extension2 = extension2 || {};
1632
1633  var keys = {};
1634  for (var name in extension1) {
1635    keys[name] = 0;
1636  }
1637  for (var name in extension2) {
1638    keys[name] = 0;
1639  }
1640  for (name in keys) {
1641    if (!jspb.Message.compareFields(extension1[name], extension2[name])) {
1642      return false;
1643    }
1644  }
1645  return true;
1646};
1647
1648
1649/**
1650 * Compares two message fields recursively.
1651 * @param {*} field1 The first field.
1652 * @param {*} field2 The second field.
1653 * @return {boolean} true if the fields are null/undefined, or otherwise equal.
1654 */
1655jspb.Message.compareFields = function(field1, field2) {
1656  // If the fields are trivially equal, they're equal.
1657  if (field1 == field2) return true;
1658
1659  if (!goog.isObject(field1) || !goog.isObject(field2)) {
1660    // NaN != NaN so we cover this case.
1661    if ((typeof field1 === 'number' && isNaN(field1)) ||
1662        (typeof field2 === 'number' && isNaN(field2))) {
1663      // One of the fields might be a string 'NaN'.
1664      return String(field1) == String(field2);
1665    }
1666    // If the fields aren't trivially equal and one of them isn't an object,
1667    // they can't possibly be equal.
1668    return false;
1669  }
1670
1671  // We have two objects. If they're different types, they're not equal.
1672  field1 = /** @type {!Object} */(field1);
1673  field2 = /** @type {!Object} */(field2);
1674  if (field1.constructor != field2.constructor) return false;
1675
1676  // If both are Uint8Arrays, compare them element-by-element.
1677  if (jspb.Message.SUPPORTS_UINT8ARRAY_ && field1.constructor === Uint8Array) {
1678    var bytes1 = /** @type {!Uint8Array} */(field1);
1679    var bytes2 = /** @type {!Uint8Array} */(field2);
1680    if (bytes1.length != bytes2.length) return false;
1681    for (var i = 0; i < bytes1.length; i++) {
1682      if (bytes1[i] != bytes2[i]) return false;
1683    }
1684    return true;
1685  }
1686
1687  // If they're both Arrays, compare them element by element except for the
1688  // optional extension objects at the end, which we compare separately.
1689  if (field1.constructor === Array) {
1690    var typedField1 = /** @type {!Array<?>} */ (field1);
1691    var typedField2 = /** @type {!Array<?>} */ (field2);
1692    var extension1 = undefined;
1693    var extension2 = undefined;
1694
1695    var length = Math.max(typedField1.length, typedField2.length);
1696    for (var i = 0; i < length; i++) {
1697      var val1 = typedField1[i];
1698      var val2 = typedField2[i];
1699
1700      if (val1 && (val1.constructor == Object)) {
1701        goog.asserts.assert(extension1 === undefined);
1702        goog.asserts.assert(i === typedField1.length - 1);
1703        extension1 = val1;
1704        val1 = undefined;
1705      }
1706
1707      if (val2 && (val2.constructor == Object)) {
1708        goog.asserts.assert(extension2 === undefined);
1709        goog.asserts.assert(i === typedField2.length - 1);
1710        extension2 = val2;
1711        val2 = undefined;
1712      }
1713
1714      if (!jspb.Message.compareFields(val1, val2)) {
1715        return false;
1716      }
1717    }
1718
1719    if (extension1 || extension2) {
1720      extension1 = extension1 || {};
1721      extension2 = extension2 || {};
1722      return jspb.Message.compareExtensions(extension1, extension2);
1723    }
1724
1725    return true;
1726  }
1727
1728  // If they're both plain Objects (i.e. extensions), compare them as
1729  // extensions.
1730  if (field1.constructor === Object) {
1731    return jspb.Message.compareExtensions(field1, field2);
1732  }
1733
1734  throw new Error('Invalid type in JSPB array');
1735};
1736
1737
1738/**
1739 * Templated, type-safe cloneMessage definition.
1740 * @return {THIS}
1741 * @this {THIS}
1742 * @template THIS
1743 */
1744jspb.Message.prototype.cloneMessage = function() {
1745  return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this));
1746};
1747
1748/**
1749 * Alias clone to cloneMessage. goog.object.unsafeClone uses clone to
1750 * efficiently copy objects. Without this alias, copying jspb messages comes
1751 * with a large performance penalty.
1752 * @return {THIS}
1753 * @this {THIS}
1754 * @template THIS
1755 */
1756jspb.Message.prototype.clone = function() {
1757  return jspb.Message.cloneMessage(/** @type {!jspb.Message} */ (this));
1758};
1759
1760/**
1761 * Static clone function. NOTE: A type-safe method called "cloneMessage"
1762 * exists
1763 * on each generated JsPb class. Do not call this function directly.
1764 * @param {!jspb.Message} msg A message to clone.
1765 * @return {!jspb.Message} A deep clone of the given message.
1766 */
1767jspb.Message.clone = function(msg) {
1768  // Although we could include the wrappers, we leave them out here.
1769  return jspb.Message.cloneMessage(msg);
1770};
1771
1772
1773/**
1774 * @param {!jspb.Message} msg A message to clone.
1775 * @return {!jspb.Message} A deep clone of the given message.
1776 * @protected
1777 */
1778jspb.Message.cloneMessage = function(msg) {
1779  // Although we could include the wrappers, we leave them out here.
1780  return new msg.constructor(jspb.Message.clone_(msg.toArray()));
1781};
1782
1783
1784/**
1785 * Takes 2 messages of the same type and copies the contents of the first
1786 * message into the second. After this the 2 messages will equals in terms of
1787 * value semantics but share no state. All data in the destination message will
1788 * be overridden.
1789 *
1790 * @param {MESSAGE} fromMessage Message that will be copied into toMessage.
1791 * @param {MESSAGE} toMessage Message which will receive a copy of fromMessage
1792 *     as its contents.
1793 * @template MESSAGE
1794 */
1795jspb.Message.copyInto = function(fromMessage, toMessage) {
1796  goog.asserts.assertInstanceof(fromMessage, jspb.Message);
1797  goog.asserts.assertInstanceof(toMessage, jspb.Message);
1798  goog.asserts.assert(fromMessage.constructor == toMessage.constructor,
1799      'Copy source and target message should have the same type.');
1800  var copyOfFrom = jspb.Message.clone(fromMessage);
1801
1802  var to = toMessage.toArray();
1803  var from = copyOfFrom.toArray();
1804
1805  // Empty destination in case it has more values at the end of the array.
1806  to.length = 0;
1807  // and then copy everything from the new to the existing message.
1808  for (var i = 0; i < from.length; i++) {
1809    to[i] = from[i];
1810  }
1811
1812  // This is either null or empty for a fresh copy.
1813  toMessage.wrappers_ = copyOfFrom.wrappers_;
1814  // Just a reference into the shared array.
1815  toMessage.extensionObject_ = copyOfFrom.extensionObject_;
1816};
1817
1818
1819/**
1820 * Helper for cloning an internal JsPb object.
1821 * @param {!Object} obj A JsPb object, eg, a field, to be cloned.
1822 * @return {!Object} A clone of the input object.
1823 * @private
1824 */
1825jspb.Message.clone_ = function(obj) {
1826  var o;
1827  if (Array.isArray(obj)) {
1828    // Allocate array of correct size.
1829    var clonedArray = new Array(obj.length);
1830    // Use array iteration where possible because it is faster than for-in.
1831    for (var i = 0; i < obj.length; i++) {
1832      o = obj[i];
1833      if (o != null) {
1834        // NOTE:redundant null check existing for NTI compatibility.
1835        // see b/70515949
1836        clonedArray[i] = (typeof o == 'object') ?
1837            jspb.Message.clone_(goog.asserts.assert(o)) :
1838            o;
1839      }
1840    }
1841    return clonedArray;
1842  }
1843  if (jspb.Message.SUPPORTS_UINT8ARRAY_ && obj instanceof Uint8Array) {
1844    return new Uint8Array(obj);
1845  }
1846  var clone = {};
1847  for (var key in obj) {
1848    o = obj[key];
1849    if (o != null) {
1850      // NOTE:redundant null check existing for NTI compatibility.
1851      // see b/70515949
1852      clone[key] = (typeof o == 'object') ?
1853          jspb.Message.clone_(goog.asserts.assert(o)) :
1854          o;
1855    }
1856  }
1857  return clone;
1858};
1859
1860
1861/**
1862 * Registers a JsPb message type id with its constructor.
1863 * @param {string} id The id for this type of message.
1864 * @param {Function} constructor The message constructor.
1865 */
1866jspb.Message.registerMessageType = function(id, constructor) {
1867  // This is needed so we can later access messageId directly on the constructor,
1868  // otherwise it is not available due to 'property collapsing' by the compiler.
1869  /**
1870   * @suppress {strictMissingProperties} messageId is not defined on Function
1871   */
1872  constructor.messageId = id;
1873};
1874/**
1875 * The extensions registered on MessageSet. This is a map of extension
1876 * field number to field info object. This should be considered as a
1877 * private API.
1878 *
1879 * This is similar to [jspb class name].extensions object for
1880 * non-MessageSet. We special case MessageSet so that we do not need
1881 * to goog.require MessageSet from classes that extends MessageSet.
1882 *
1883 * @type {!Object<number, jspb.ExtensionFieldInfo>}
1884 */
1885jspb.Message.messageSetExtensions = {};
1886
1887/**
1888 * @type {!Object<number, jspb.ExtensionFieldBinaryInfo>}
1889 */
1890jspb.Message.messageSetExtensionsBinary = {};
1891