• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * @fileoverview Proto internal runtime checks.
3  *
4  * Checks are grouped into different severity, see:
5  * http://g3doc/third_party/protobuf/javascript/README.md#configurable-check-support-in-protocol-buffers
6  *
7  * Checks are also grouped into different sections:
8  *   - CHECK_BOUNDS:
9  *       Checks that ensure that indexed access is within bounds
10  *       (e.g. an array being accessed past its size).
11  *   - CHECK_STATE
12  *       Checks related to the state of an object
13  *       (e.g. a parser hitting an invalid case).
14  *   - CHECK_TYPE:
15  *       Checks that relate to type errors (e.g. code receives a number instead
16  *       of a string).
17  */
18 goog.module('protobuf.internal.checks');
19 
20 const ByteString = goog.require('protobuf.ByteString');
21 const Int64 = goog.require('protobuf.Int64');
22 const WireType = goog.require('protobuf.binary.WireType');
23 
24 //
25 // See
26 // http://g3doc/third_party/protobuf/javascript/README.md#configurable-check-support-in-protocol-buffers
27 //
28 /** @define{string} */
29 const CHECK_LEVEL_DEFINE = goog.define('protobuf.defines.CHECK_LEVEL', '');
30 
31 /** @define{boolean} */
32 const POLYFILL_TEXT_ENCODING =
33     goog.define('protobuf.defines.POLYFILL_TEXT_ENCODING', true);
34 
35 /**
36  * @const {number}
37  */
38 const MAX_FIELD_NUMBER = Math.pow(2, 29) - 1;
39 
40 /**
41  * The largest finite float32 value.
42  * @const {number}
43  */
44 const FLOAT32_MAX = 3.4028234663852886e+38;
45 
46 /** @enum {number} */
47 const CheckLevel = {
48   DEBUG: 0,
49   CRITICAL: 1,
50   OFF: 2
51 };
52 
53 
54 /** @return {!CheckLevel} */
55 function calculateCheckLevel() {
56   const definedLevel = CHECK_LEVEL_DEFINE.toUpperCase();
57   if (definedLevel === '') {
58     // user did not set a value, value now just depends on goog.DEBUG
59     return goog.DEBUG ? CheckLevel.DEBUG : CheckLevel.CRITICAL;
60   }
61 
62   if (definedLevel === 'CRITICAL') {
63     return CheckLevel.CRITICAL;
64   }
65 
66   if (definedLevel === 'OFF') {
67     return CheckLevel.OFF;
68   }
69 
70   if (definedLevel === 'DEBUG') {
71     return CheckLevel.DEBUG;
72   }
73 
74   throw new Error(`Unknown value for CHECK_LEVEL: ${CHECK_LEVEL_DEFINE}`);
75 }
76 
77 const /** !CheckLevel */ CHECK_LEVEL = calculateCheckLevel();
78 
79 const /** boolean */ CHECK_STATE = CHECK_LEVEL === CheckLevel.DEBUG;
80 
81 const /** boolean */ CHECK_CRITICAL_STATE =
82     CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG;
83 
84 const /** boolean */ CHECK_BOUNDS = CHECK_LEVEL === CheckLevel.DEBUG;
85 
86 const /** boolean */ CHECK_CRITICAL_BOUNDS =
87     CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG;
88 
89 const /** boolean */ CHECK_TYPE = CHECK_LEVEL === CheckLevel.DEBUG;
90 
91 const /** boolean */ CHECK_CRITICAL_TYPE =
92     CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG;
93 
94 /**
95  * Ensures the truth of an expression involving the state of the calling
96  * instance, but not involving any parameters to the calling method.
97  *
98  * For cases where failing fast is pretty important and not failing early could
99  * cause bugs that are much harder to debug.
100  * @param {boolean} state
101  * @param {string=} message
102  * @throws {!Error} If the state is false and the check state is critical.
103  */
104 function checkCriticalState(state, message = '') {
105   if (!CHECK_CRITICAL_STATE) {
106     return;
107   }
108   if (!state) {
109     throw new Error(message);
110   }
111 }
112 
113 /**
114  * Ensures the truth of an expression involving the state of the calling
115  * instance, but not involving any parameters to the calling method.
116  *
117  * @param {boolean} state
118  * @param {string=} message
119  * @throws {!Error} If the state is false and the check state is debug.
120  */
121 function checkState(state, message = '') {
122   if (!CHECK_STATE) {
123     return;
124   }
125   checkCriticalState(state, message);
126 }
127 
128 /**
129  * Ensures that `index` specifies a valid position in an indexable object of
130  * size `size`. A position index may range from zero to size, inclusive.
131  * @param {number} index
132  * @param {number} size
133  * @throws {!Error} If the index is out of range and the check state is debug.
134  */
135 function checkPositionIndex(index, size) {
136   if (!CHECK_BOUNDS) {
137     return;
138   }
139   checkCriticalPositionIndex(index, size);
140 }
141 
142 /**
143  * Ensures that `index` specifies a valid position in an indexable object of
144  * size `size`. A position index may range from zero to size, inclusive.
145  * @param {number} index
146  * @param {number} size
147  * @throws {!Error} If the index is out of range and the check state is
148  * critical.
149  */
150 function checkCriticalPositionIndex(index, size) {
151   if (!CHECK_CRITICAL_BOUNDS) {
152     return;
153   }
154   if (index < 0 || index > size) {
155     throw new Error(`Index out of bounds: index: ${index} size: ${size}`);
156   }
157 }
158 
159 /**
160  * Ensures that `index` specifies a valid element in an indexable object of
161  * size `size`. A element index may range from zero to size, exclusive.
162  * @param {number} index
163  * @param {number} size
164  * @throws {!Error} If the index is out of range and the check state is
165  * debug.
166  */
167 function checkElementIndex(index, size) {
168   if (!CHECK_BOUNDS) {
169     return;
170   }
171   checkCriticalElementIndex(index, size);
172 }
173 
174 /**
175  * Ensures that `index` specifies a valid element in an indexable object of
176  * size `size`. A element index may range from zero to size, exclusive.
177  * @param {number} index
178  * @param {number} size
179  * @throws {!Error} If the index is out of range and the check state is
180  * critical.
181  */
182 function checkCriticalElementIndex(index, size) {
183   if (!CHECK_CRITICAL_BOUNDS) {
184     return;
185   }
186   if (index < 0 || index >= size) {
187     throw new Error(`Index out of bounds: index: ${index} size: ${size}`);
188   }
189 }
190 
191 /**
192  * Ensures the range of [start, end) is with the range of [0, size).
193  * @param {number} start
194  * @param {number} end
195  * @param {number} size
196  * @throws {!Error} If start and end are out of range and the check state is
197  * debug.
198  */
199 function checkRange(start, end, size) {
200   if (!CHECK_BOUNDS) {
201     return;
202   }
203   checkCriticalRange(start, end, size);
204 }
205 
206 /**
207  * Ensures the range of [start, end) is with the range of [0, size).
208  * @param {number} start
209  * @param {number} end
210  * @param {number} size
211  * @throws {!Error} If start and end are out of range and the check state is
212  * critical.
213  */
214 function checkCriticalRange(start, end, size) {
215   if (!CHECK_CRITICAL_BOUNDS) {
216     return;
217   }
218   if (start < 0 || end < 0 || start > size || end > size) {
219     throw new Error(`Range error: start: ${start} end: ${end} size: ${size}`);
220   }
221   if (start > end) {
222     throw new Error(`Start > end: ${start} > ${end}`);
223   }
224 }
225 
226 /**
227  * Ensures that field number is an integer and within the range of
228  * [1, MAX_FIELD_NUMBER].
229  * @param {number} fieldNumber
230  * @throws {!Error} If the field number is out of range and the check state is
231  * debug.
232  */
233 function checkFieldNumber(fieldNumber) {
234   if (!CHECK_TYPE) {
235     return;
236   }
237   checkCriticalFieldNumber(fieldNumber);
238 }
239 
240 /**
241  * Ensures that the value is neither null nor undefined.
242  *
243  * @param {T} value
244  * @return {R}
245  *
246  * @template T
247  * @template R :=
248  *     mapunion(T, (V) =>
249  *         cond(eq(V, 'null'),
250  *             none(),
251  *             cond(eq(V, 'undefined'),
252  *                 none(),
253  *                 V)))
254  *  =:
255  */
256 function checkDefAndNotNull(value) {
257   if (CHECK_TYPE) {
258     // Note that undefined == null.
259     if (value == null) {
260       throw new Error(`Value can't be null`);
261     }
262   }
263   return value;
264 }
265 
266 /**
267  * Ensures that the value exists and is a function.
268  *
269  * @param {function(?): ?} func
270  */
271 function checkFunctionExists(func) {
272   if (CHECK_TYPE) {
273     if (typeof func !== 'function') {
274       throw new Error(`${func} is not a function`);
275     }
276   }
277 }
278 
279 /**
280  * Ensures that field number is an integer and within the range of
281  * [1, MAX_FIELD_NUMBER].
282  * @param {number} fieldNumber
283  * @throws {!Error} If the field number is out of range and the check state is
284  * critical.
285  */
286 function checkCriticalFieldNumber(fieldNumber) {
287   if (!CHECK_CRITICAL_TYPE) {
288     return;
289   }
290   if (fieldNumber <= 0 || fieldNumber > MAX_FIELD_NUMBER) {
291     throw new Error(`Field number is out of range: ${fieldNumber}`);
292   }
293 }
294 
295 /**
296  * Ensures that wire type is valid.
297  * @param {!WireType} wireType
298  * @throws {!Error} If the wire type is invalid and the check state is debug.
299  */
300 function checkWireType(wireType) {
301   if (!CHECK_TYPE) {
302     return;
303   }
304   checkCriticalWireType(wireType);
305 }
306 
307 /**
308  * Ensures that wire type is valid.
309  * @param {!WireType} wireType
310  * @throws {!Error} If the wire type is invalid and the check state is critical.
311  */
312 function checkCriticalWireType(wireType) {
313   if (!CHECK_CRITICAL_TYPE) {
314     return;
315   }
316   if (wireType < WireType.VARINT || wireType > WireType.FIXED32) {
317     throw new Error(`Invalid wire type: ${wireType}`);
318   }
319 }
320 
321 /**
322  * Ensures the given value has the correct type.
323  * @param {boolean} expression
324  * @param {string} errorMsg
325  * @throws {!Error} If the value has the wrong type and the check state is
326  * critical.
327  */
328 function checkCriticalType(expression, errorMsg) {
329   if (!CHECK_CRITICAL_TYPE) {
330     return;
331   }
332   if (!expression) {
333     throw new Error(errorMsg);
334   }
335 }
336 
337 /**
338  * Checks whether a given object is an array.
339  * @param {*} value
340  * @return {!Array<*>}
341  */
342 function checkCriticalTypeArray(value) {
343   checkCriticalType(
344       Array.isArray(value), `Must be an array, but got: ${value}`);
345   return /** @type {!Array<*>} */ (value);
346 }
347 
348 /**
349  * Checks whether a given object is an iterable.
350  * @param {*} value
351  * @return {!Iterable<*>}
352  */
353 function checkCriticalTypeIterable(value) {
354   checkCriticalType(
355       !!value[Symbol.iterator], `Must be an iterable, but got: ${value}`);
356   return /** @type {!Iterable<*>} */ (value);
357 }
358 
359 /**
360  * Checks whether a given object is a boolean.
361  * @param {*} value
362  */
363 function checkCriticalTypeBool(value) {
364   checkCriticalType(
365       typeof value === 'boolean', `Must be a boolean, but got: ${value}`);
366 }
367 
368 /**
369  * Checks whether a given object is an array of boolean.
370  * @param {*} values
371  */
372 function checkCriticalTypeBoolArray(values) {
373   // TODO(b/134765672)
374   if (!CHECK_CRITICAL_TYPE) {
375     return;
376   }
377   const array = checkCriticalTypeArray(values);
378   for (const value of array) {
379     checkCriticalTypeBool(value);
380   }
381 }
382 
383 /**
384  * Checks whether a given object is a ByteString.
385  * @param {*} value
386  */
387 function checkCriticalTypeByteString(value) {
388   checkCriticalType(
389       value instanceof ByteString, `Must be a ByteString, but got: ${value}`);
390 }
391 
392 /**
393  * Checks whether a given object is an array of ByteString.
394  * @param {*} values
395  */
396 function checkCriticalTypeByteStringArray(values) {
397   // TODO(b/134765672)
398   if (!CHECK_CRITICAL_TYPE) {
399     return;
400   }
401   const array = checkCriticalTypeArray(values);
402   for (const value of array) {
403     checkCriticalTypeByteString(value);
404   }
405 }
406 
407 /**
408  * Checks whether a given object is a number.
409  * @param {*} value
410  * @throws {!Error} If the value is not float and the check state is debug.
411  */
412 function checkTypeDouble(value) {
413   if (!CHECK_TYPE) {
414     return;
415   }
416   checkCriticalTypeDouble(value);
417 }
418 
419 /**
420  * Checks whether a given object is a number.
421  * @param {*} value
422  * @throws {!Error} If the value is not float and the check state is critical.
423  */
424 function checkCriticalTypeDouble(value) {
425   checkCriticalType(
426       typeof value === 'number', `Must be a number, but got: ${value}`);
427 }
428 
429 /**
430  * Checks whether a given object is an array of double.
431  * @param {*} values
432  */
433 function checkCriticalTypeDoubleArray(values) {
434   // TODO(b/134765672)
435   if (!CHECK_CRITICAL_TYPE) {
436     return;
437   }
438   const array = checkCriticalTypeArray(values);
439   for (const value of array) {
440     checkCriticalTypeDouble(value);
441   }
442 }
443 
444 /**
445  * Checks whether a given object is a number.
446  * @param {*} value
447  * @throws {!Error} If the value is not signed int32 and the check state is
448  *     debug.
449  */
450 function checkTypeSignedInt32(value) {
451   if (!CHECK_TYPE) {
452     return;
453   }
454   checkCriticalTypeSignedInt32(value);
455 }
456 
457 /**
458  * Checks whether a given object is a number.
459  * @param {*} value
460  * @throws {!Error} If the value is not signed int32 and the check state is
461  *     critical.
462  */
463 function checkCriticalTypeSignedInt32(value) {
464   checkCriticalTypeDouble(value);
465   const valueAsNumber = /** @type {number} */ (value);
466   if (CHECK_CRITICAL_TYPE) {
467     if (valueAsNumber < -Math.pow(2, 31) || valueAsNumber > Math.pow(2, 31) ||
468         !Number.isInteger(valueAsNumber)) {
469       throw new Error(`Must be int32, but got: ${valueAsNumber}`);
470     }
471   }
472 }
473 
474 /**
475  * Checks whether a given object is an array of numbers.
476  * @param {*} values
477  */
478 function checkCriticalTypeSignedInt32Array(values) {
479   // TODO(b/134765672)
480   if (!CHECK_CRITICAL_TYPE) {
481     return;
482   }
483   const array = checkCriticalTypeArray(values);
484   for (const value of array) {
485     checkCriticalTypeSignedInt32(value);
486   }
487 }
488 
489 /**
490  * Ensures that value is a long instance.
491  * @param {*} value
492  * @throws {!Error} If the value is not a long instance and check state is
493  *     debug.
494  */
495 function checkTypeSignedInt64(value) {
496   if (!CHECK_TYPE) {
497     return;
498   }
499   checkCriticalTypeSignedInt64(value);
500 }
501 
502 /**
503  * Ensures that value is a long instance.
504  * @param {*} value
505  * @throws {!Error} If the value is not a long instance and check state is
506  *     critical.
507  */
508 function checkCriticalTypeSignedInt64(value) {
509   if (!CHECK_CRITICAL_TYPE) {
510     return;
511   }
512   if (!(value instanceof Int64)) {
513     throw new Error(`Must be Int64 instance, but got: ${value}`);
514   }
515 }
516 
517 /**
518  * Checks whether a given object is an array of long instances.
519  * @param {*} values
520  * @throws {!Error} If values is not an array of long instances.
521  */
522 function checkCriticalTypeSignedInt64Array(values) {
523   // TODO(b/134765672)
524   if (!CHECK_CRITICAL_TYPE) {
525     return;
526   }
527   const array = checkCriticalTypeArray(values);
528   for (const value of array) {
529     checkCriticalTypeSignedInt64(value);
530   }
531 }
532 
533 /**
534  * Checks whether a given object is a number and within float32 precision.
535  * @param {*} value
536  * @throws {!Error} If the value is not float and the check state is debug.
537  */
538 function checkTypeFloat(value) {
539   if (!CHECK_TYPE) {
540     return;
541   }
542   checkCriticalTypeFloat(value);
543 }
544 
545 /**
546  * Checks whether a given object is a number and within float32 precision.
547  * @param {*} value
548  * @throws {!Error} If the value is not float and the check state is critical.
549  */
550 function checkCriticalTypeFloat(value) {
551   checkCriticalTypeDouble(value);
552   if (CHECK_CRITICAL_TYPE) {
553     const valueAsNumber = /** @type {number} */ (value);
554     if (Number.isFinite(valueAsNumber) &&
555         (valueAsNumber > FLOAT32_MAX || valueAsNumber < -FLOAT32_MAX)) {
556       throw new Error(
557           `Given number does not fit into float precision: ${value}`);
558     }
559   }
560 }
561 
562 /**
563  * Checks whether a given object is an iterable of floats.
564  * @param {*} values
565  */
566 function checkCriticalTypeFloatIterable(values) {
567   // TODO(b/134765672)
568   if (!CHECK_CRITICAL_TYPE) {
569     return;
570   }
571   const iterable = checkCriticalTypeIterable(values);
572   for (const value of iterable) {
573     checkCriticalTypeFloat(value);
574   }
575 }
576 
577 /**
578  * Checks whether a given object is a string.
579  * @param {*} value
580  */
581 function checkCriticalTypeString(value) {
582   checkCriticalType(
583       typeof value === 'string', `Must be string, but got: ${value}`);
584 }
585 
586 /**
587  * Checks whether a given object is an array of string.
588  * @param {*} values
589  */
590 function checkCriticalTypeStringArray(values) {
591   // TODO(b/134765672)
592   if (!CHECK_CRITICAL_TYPE) {
593     return;
594   }
595   const array = checkCriticalTypeArray(values);
596   for (const value of array) {
597     checkCriticalTypeString(value);
598   }
599 }
600 
601 /**
602  * Ensures that value is a valid unsigned int32.
603  * @param {*} value
604  * @throws {!Error} If the value is out of range and the check state is debug.
605  */
606 function checkTypeUnsignedInt32(value) {
607   if (!CHECK_TYPE) {
608     return;
609   }
610   checkCriticalTypeUnsignedInt32(value);
611 }
612 
613 /**
614  * Ensures that value is a valid unsigned int32.
615  * @param {*} value
616  * @throws {!Error} If the value is out of range and the check state
617  * is critical.
618  */
619 function checkCriticalTypeUnsignedInt32(value) {
620   if (!CHECK_CRITICAL_TYPE) {
621     return;
622   }
623   checkCriticalTypeDouble(value);
624   const valueAsNumber = /** @type {number} */ (value);
625   if (valueAsNumber < 0 || valueAsNumber > Math.pow(2, 32) - 1 ||
626       !Number.isInteger(valueAsNumber)) {
627     throw new Error(`Must be uint32, but got: ${value}`);
628   }
629 }
630 
631 /**
632  * Checks whether a given object is an array of unsigned int32.
633  * @param {*} values
634  */
635 function checkCriticalTypeUnsignedInt32Array(values) {
636   // TODO(b/134765672)
637   if (!CHECK_CRITICAL_TYPE) {
638     return;
639   }
640   const array = checkCriticalTypeArray(values);
641   for (const value of array) {
642     checkCriticalTypeUnsignedInt32(value);
643   }
644 }
645 
646 /**
647  * Checks whether a given object is an array of message.
648  * @param {*} values
649  */
650 function checkCriticalTypeMessageArray(values) {
651   // TODO(b/134765672)
652   if (!CHECK_CRITICAL_TYPE) {
653     return;
654   }
655   const array = checkCriticalTypeArray(values);
656   for (const value of array) {
657     checkCriticalType(
658         value !== null, 'Given value is not a message instance: null');
659   }
660 }
661 
662 exports = {
663   checkDefAndNotNull,
664   checkCriticalElementIndex,
665   checkCriticalFieldNumber,
666   checkCriticalPositionIndex,
667   checkCriticalRange,
668   checkCriticalState,
669   checkCriticalTypeBool,
670   checkCriticalTypeBoolArray,
671   checkCriticalTypeByteString,
672   checkCriticalTypeByteStringArray,
673   checkCriticalTypeDouble,
674   checkTypeDouble,
675   checkCriticalTypeDoubleArray,
676   checkTypeFloat,
677   checkCriticalTypeFloat,
678   checkCriticalTypeFloatIterable,
679   checkCriticalTypeMessageArray,
680   checkCriticalTypeSignedInt32,
681   checkCriticalTypeSignedInt32Array,
682   checkCriticalTypeSignedInt64,
683   checkTypeSignedInt64,
684   checkCriticalTypeSignedInt64Array,
685   checkCriticalTypeString,
686   checkCriticalTypeStringArray,
687   checkCriticalTypeUnsignedInt32,
688   checkCriticalTypeUnsignedInt32Array,
689   checkCriticalType,
690   checkCriticalWireType,
691   checkElementIndex,
692   checkFieldNumber,
693   checkFunctionExists,
694   checkPositionIndex,
695   checkRange,
696   checkState,
697   checkTypeUnsignedInt32,
698   checkTypeSignedInt32,
699   checkWireType,
700   CHECK_BOUNDS,
701   CHECK_CRITICAL_BOUNDS,
702   CHECK_STATE,
703   CHECK_CRITICAL_STATE,
704   CHECK_TYPE,
705   CHECK_CRITICAL_TYPE,
706   MAX_FIELD_NUMBER,
707   POLYFILL_TEXT_ENCODING,
708 };
709