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 */ 18goog.module('protobuf.internal.checks'); 19 20const ByteString = goog.require('protobuf.ByteString'); 21const Int64 = goog.require('protobuf.Int64'); 22const 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} */ 29const CHECK_LEVEL_DEFINE = goog.define('protobuf.defines.CHECK_LEVEL', ''); 30 31/** @define{boolean} */ 32const POLYFILL_TEXT_ENCODING = 33 goog.define('protobuf.defines.POLYFILL_TEXT_ENCODING', true); 34 35/** 36 * @const {number} 37 */ 38const MAX_FIELD_NUMBER = Math.pow(2, 29) - 1; 39 40/** 41 * The largest finite float32 value. 42 * @const {number} 43 */ 44const FLOAT32_MAX = 3.4028234663852886e+38; 45 46/** @enum {number} */ 47const CheckLevel = { 48 DEBUG: 0, 49 CRITICAL: 1, 50 OFF: 2 51}; 52 53 54/** @return {!CheckLevel} */ 55function 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 77const /** !CheckLevel */ CHECK_LEVEL = calculateCheckLevel(); 78 79const /** boolean */ CHECK_STATE = CHECK_LEVEL === CheckLevel.DEBUG; 80 81const /** boolean */ CHECK_CRITICAL_STATE = 82 CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG; 83 84const /** boolean */ CHECK_BOUNDS = CHECK_LEVEL === CheckLevel.DEBUG; 85 86const /** boolean */ CHECK_CRITICAL_BOUNDS = 87 CHECK_LEVEL === CheckLevel.CRITICAL || CHECK_LEVEL === CheckLevel.DEBUG; 88 89const /** boolean */ CHECK_TYPE = CHECK_LEVEL === CheckLevel.DEBUG; 90 91const /** 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 */ 104function 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 */ 121function 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 */ 135function 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 */ 150function 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 */ 167function 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 */ 182function 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 */ 199function 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 */ 214function 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 */ 233function 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 */ 256function 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 */ 271function 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 */ 286function 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 */ 300function 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 */ 312function 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 */ 328function 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 */ 342function 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 */ 353function 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 */ 363function 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 */ 372function 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 */ 387function 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 */ 396function 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 */ 412function 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 */ 424function 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 */ 433function 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 */ 450function 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 */ 463function 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 */ 478function 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 */ 495function 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 */ 508function 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 */ 522function 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 */ 538function 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 */ 550function 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 */ 566function 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 */ 581function 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 */ 590function 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 */ 606function 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 */ 619function 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 */ 635function 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 */ 650function 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 662exports = { 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