Stage 2 Draft / December 15, 2017

BigInt

Introduction

This proposal adds arbitrary-precision integers to ECMAScript. For motivation and a high-level introduction, see the explainer document.

I attempted to write this specification draft with an eye towards intelligibility and clarifying issues, rather than looking exactly like the final specification. In particular, the translation of the operations on the Number type into the new form are omitted, as they are identical to the previous definitions and would be expressed only as a lengthy refactoring to skip when reading this text.

If you find any part of this specification unclear or confusing, please file an issue.

1Numeric Types

ECMAScript has two built-in numeric types: Number and BigInt. In this specification, every numeric type T contains a multiplicative identity value denoted T::unit. The specification types also have the following abstract operations, likewise denoted T::op for a given operation with specification name op. Unless noted otherwise, argument and result types are all T.

Table 1: Numeric Type Operations
Invocation Synopsis Value and Purpose
T::unaryMinus(x) A specification function invoked when applying the unary minus operator. Called by the semantics of the unary - operator.
T::bitwiseNOT(x) A specification function invoked when applying the bitwise NOT operator. Called by the semantics of the bitwise NOT operator for ~x.
T::exponentiate(x, y) A specification function invoked when applying the exponentiation operator. Called by the semantics of the exponentiation operator for x ** y.
T::multiply(x, y) A specification function invoked when applying the multiplication operator. Called by the semantics of the * operator for x * y.
T::divide(x, y) A specification function invoked when applying the division operator. Called by the semantics of the / operator for x / y.
T::remainder(x, y) A specification function invoked when applying the truncating remainder ("mod") operator. Called by the semantics of the % operator for x % y. Revisit this name, as Number::remainder explicitly doesn't do the IEEE 754 remainder operation. However, modulo also seems problematic. (issue)
T::add(x, y) A specification function invoked when applying the addition operator. Called by the semantics of the + operator for x + y.
T::subtract(x, y) A specification function invoked when applying the subtraction operator. Called by the semantics of the - operator for x - y.
T::leftShift(x, y) A specification function invoked when applying the left shift operator to two operands, both of type T. Called by the semantics of the << operator for x << y.
T::signedRightShift(x, y) A specification function invoked when applying the right shift operator to two operands, both of type T. Called by the semantics of the >> operator for x >> y.
T::unsignedRightShift(x, y) A specification function invoked when applying the right shift operator to two operands, both of type T. Called by the semantics of the >>> operator for x >>> y.
T::lessThan(x, y) A specification function invoked when applying one of the four partial-order relational operators. The return value must be false, true, or undefined (for unordered inputs). Called by the Abstract Relational Comparison algorithm for x < y, x > y, x <= y, and x >= y.
T::equal(x, y) A specification function invoked when applying equality operators. The return value must be false or true. Called by the Strict Equality Comparison algorithm for x == y, x != y, x === y, and x !== y.
T::sameValue(x, y) A specification function invoked when applying abstract operation SameValue. The return value must be false or true. Called from Object internal methods to test exact value equality. May not throw an exception.
T::sameValueZero(x, y) A specification function invoked when applying abstract operation SameValueZero. The return value must be false or true. Called from Array, Map, and Set methods to test value equality ignoring differences among members of the zero cohort (e.g., -0 and +0). May not throw an exception.
T::bitwiseAND(x, y) A specification function invoked when applying binary bitwise AND operator. Called by the Binary Bitwise Operators algorithm for x & y.
T::bitwiseXOR(x, y) A specification function invoked when applying binary bitwise XOR operator. Called by the Binary Bitwise Operators algorithm for x ^ y.
T::bitwiseOR(x, y) A specification function invoked when applying binary bitwise OR operator. Called by the Binary Bitwise Operators algorithm for x | y.

The T::unit value and T::op operations are not a part of the ECMAScript language; they are defined here solely to aid the specification of the semantics of the ECMAScript language. Other abstract operations are defined throughout this specification.

Because the numeric types are in general not convertible without loss of precision or truncation, the ECMAScript language provides no implicit conversion among these types. Programmers must explicitly call Number and BigInt functions to convert among types when calling a function which requires another type.

Note

The first and subsequent editions of ECMAScript have provided, for certain operators, implicit numeric conversions that could lose precision or truncate. These legacy implicit conversions are maintained for backward compatibility, but not provided for BigInt in order to minimize opportunity for programmer error, and to leave open the option of generalized value types in a future edition.

1.1The BigInt Type

The BigInt type represents a mathematical integer value. The value may be any size and is not limited to a particular bit-width. Generally, where not otherwise noted, operations are designed to return exact mathematically-based answers. For binary operations, BigInts act as two's complement binary strings, with negative numbers treated as having bits set infinitely to the left.

The BigInt::unit value is 1n.

1.1.1BigInt::unaryMinus (x)

The abstract operation BigInt::unaryMinus with an argument x of BigInt type returns the result of negating x.

Note
There is only one 0n value; -0n is the same as 0n.

1.1.2BigInt::bitwiseNOT (x)

The abstract operation BigInt::bitwiseNOT with an argument x of BigInt type returns the one's complement of x; that is, -x - 1.

1.1.3BigInt::exponentiate (base, exponent)

  1. If exponent < 0, throw a RangeError exception.
  2. If base is 0n and exponent is 0n, return 1n.
  3. Return a BigInt representing the mathematical value of base raised to the power exponent.

1.1.4BigInt::multiply (x, y)

The abstract operation BigInt::multiply with two arguments x and y of BigInt type returns a BigInt representing the result of multiplying x and y.

Note
Even if the result has a much larger bit width than the input, the exact mathematical answer is given.

1.1.5BigInt::divide (x, y)

  1. If y is 0n, throw a RangeError exception.
  2. Let quotient be the mathematical value of x divided by y.
  3. Return a BigInt representing quotient rounded towards 0 to the next integral value.

1.1.6BigInt::remainder (n, d)

  1. If d is 0n, throw a RangeError exception.
  2. If n is 0n, return 0n.
  3. Let r be the BigInt defined by the mathematical relation r = n - (d × q) where q is a BigInt that is negative only if n/d is negative and positive only if n/d is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.
  4. Return r.
Note
The sign of the result equals the sign of the dividend.

1.1.7BigInt::add (x, y)

The abstract operation BigInt::add with two arguments x and y of BigInt type returns a BigInt representing the sum of x and y.

1.1.8BigInt::subtract (x, y)

The abstract operation BigInt::subtract with two arguments x and y of BigInt type returns the BigInt representing the difference x minus y.

1.1.9BigInt::leftShift (x, y)

The abstract operation BigInt::leftShift with two arguments x and y of BigInt:

  1. If y < 0,
    1. Return a BigInt representing x divided by 2-y, rounding down to the nearest integer, including for negative numbers.
  2. Return a BigInt representing x multiplied by 2y.
Note
Semantics here should be equivalent to a bitwise shift, treating the BigInt as an infinite length string of binary two's complement digits.

1.1.10BigInt::signedRightShift (x, y)

The abstract operation BigInt::signedRightShift with arguments x and y of type BigInt:

  1. Return BigInt::leftShift(x, -y).

1.1.11BigInt::unsignedRightShift (x, y)

The abstract operation BigInt::unsignedRightShift with two arguments x and y of type BigInt:

  1. Throw a TypeError exception.

1.1.12BigInt::lessThan (x, y)

The abstract operation BigInt::lessThan with two arguments x and y of BigInt type returns true if x is less than y and false otherwise.

1.1.13BigInt::equal (x, y)

The abstract operation BigInt::equal with two arguments x and y of BigInt type returns true if x and y have the same mathematical integer value and false otherwise.

1.1.14BigInt::sameValue (x, y)

The abstract operation BigInt::sameValue with two arguments x and y of BigInt type:

  1. Return BigInt::equal(x, y).

1.1.15BigInt::sameValueZero (x, y)

The abstract operation BigInt::sameValueZero with two arguments x and y of BigInt type:

  1. Return BigInt::equal(x, y).

1.1.16BitwiseOp(op, x, y)

  1. Let result be 0.
  2. Let shift be 0.
  3. Repeat, until (x = 0 or x = -1) and (y = 0 or y = -1),
    1. Let xDigit be x modulo 2.
    2. Let yDigit be y modulo 2.
    3. Let result be result + 2shift * op(xDigit, yDigit)
    4. Let shift be shift + 1.
    5. Let x be (x - xDigit) / 2.
    6. Let y be (y - yDigit) / 2.
  4. If op(x modulo 2, y modulo 2) ≠ 0,
    1. Let result be result - 2shift. NOTE: This extends the sign.
  5. Return result.

1.1.17BigInt::bitwiseAND (x, y)

  1. Return BitwiseOp(&, x, y).

1.1.18BigInt::bitwiseXOR (x, y)

  1. Return BitwiseOp(^, x, y).

1.1.19BigInt::bitwiseOR (x, y)

  1. Return BitwiseOp(|, x, y).
As part of the integration with the main specification, the Number type will have a similar definition of operations, derived from the current operator definitions. Because the semantics are not proposed to change, for ease of review, the refactoring is omitted from this spec draft.

2Modifications to the Number grammar

NumericLiteral::DecimalLiteral DecimalIntegerLiteralBigIntLiteralSuffix BinaryIntegerLiteral OctalIntegerLiteral HexIntegerLiteral NumericLiteralBase NumericLiteralBaseBigIntLiteralSuffix LegacyOctalIntegerLiteral NumericLiteralBase::BinaryIntegerLiteral OctalIntegerLiteral HexIntegerLiteral BigIntLiteralSuffix::n

2.1Static Semantics: BigInt Value

NumericLiteral::NumericLiteralBaseBigIntLiteralSuffix NumericLiteral::DecimalIntegerLiteralBigIntLiteralSuffix

2.2Static Semantics: Number Value

NumericLiteral::NumericLiteralBase

The MV is rounded to a value of the Number type.

Rounding to the nearest Number will be moved from the MV calculation to a Number Value Static Semantics section so that it doesn't apply to BigInts.

3Abstract Operations

3.1Type Conversion

The BigInt type has no implicit conversions in the ECMAScript language; programmers must call BigInt explicitly to convert values from other types.

3.1.1ToPrimitive ( input [ , PreferredType ] )

Table 2: ToPrimitive Conversions
Input Type Result
BigInt Return input.

3.1.2ToBoolean ( argument )

The abstract operation ToBoolean converts argument to a value of type Boolean according to Table 3:

Table 3: ToBoolean Conversions
Argument Type Result
BigInt Return false if argument is 0n; otherwise return true.

3.1.3ToNumber ( argument )

The abstract operation ToNumber converts argument to a value of type Number according to Table 4:

Table 4: ToNumber Conversions
Argument Type Result
BigInt Throw a TypeError exception
Although it would be possible to define a conversion here, to find the nearest Number for a BigInt, such a conversion may lose precision. ToNumber is called implicitly from so many places, but BigInts would not add any value if they lost precision all the time; you might as well just use Numbers instead in the first place. A key design decision of this specification is to disallow implicit conversions, and force programmers to use explicit conversions themselves instead.

3.1.3.1ToNumber Applied to the String Type

Note

Some differences should be noted between the syntax of a StringNumericLiteral and a NumericLiteral:

  • A StringNumericLiteral may not include a BigIntLiteralSuffix.
  • 3.1.4ToString ( argument )

    The abstract operation ToString converts argument to a value of type String according to Table 5:

    Table 5: ToString Conversions
    Argument Type Result
    BigInt See 3.1.4.1.

    3.1.4.1ToString Applied to the BigInt Type

    The abstract operation ToString converts a BigInt i to String format as follows:

    1. If i is less than zero, return the String concatenation of the String "-" and ToString(-i).
    2. Return the String consisting of the code units of the digits of the decimal representation of i.

    3.1.5ToObject ( argument )

    The abstract operation ToObject converts argument to a value of type Object according to Table 6:

    Table 6: ToObject Conversions
    Argument Type Result
    BigInt Return a new BigInt object whose [[BigIntData]] internal slot is set to argument. See BigInt Objects for a description of BigInt objects.

    3.1.6ToNumeric ( value )

    The abstract operation ToNumeric returns value converted to a numeric value of type Number or BigInt. This abstract operation functions as follows:

    1. Let primValue be ? ToPrimitive(value, hint Number).
    2. If Type(primValue) is BigInt, return primValue.
    3. Return ToNumber(primValue).

    3.1.7RequireObjectCoercible ( argument )

    The abstract operation RequireObjectCoercible throws an error if argument is a value that cannot be converted to an Object using ToObject. It is defined by Table 7:

    Table 7: RequireObjectCoercible Results
    Argument Type Result
    BigInt Return argument.

    3.2Testing and Comparison Operations

    3.2.1SameValue ( x, y )

    The internal comparison abstract operation SameValue(x, y), where x and y are ECMAScript language values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is different from Type(y), return false.
    2. If Type(x) is Number or BigInt, then
      1. If x is NaN and y is NaN, return true.
      2. If x is +0 and y is -0, return false.
      3. If x is -0 and y is +0, return false.
      4. If x is the same Number value as y, return true.
      5. Return false.
      6. Return ! Type(x)::sameValue(x, y).
    3. Return SameValueNonNumberNumeric(x, y).
    The previous Number-related contents of this algorithm will be moved into Number::sameValue.

    3.2.2SameValueZero ( x, y )

    The internal comparison abstract operation SameValueZero(x, y), where x and y are ECMAScript language values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is different from Type(y), return false.
    2. If Type(x) is Number or BigInt, then
      1. If x is NaN and y is NaN, return true.
      2. If x is +0 and y is -0, return true.
      3. If x is -0 and y is +0, return true.
      4. If x is the same Number value as y, return true.
      5. Return false.
      6. Return ! Type(x)::sameValueZero(x, y).
    3. Return SameValueNonNumberNumeric(x, y).
    The previous Number-related contents of this algorithm will be moved into Number::sameValueZero.

    3.2.3SameValueNonNumberNumeric ( x, y )

    The internal comparison abstract operation SameValueNonNumeric(x, y), where neither x nor y are numeric type values, produces true or false. Such a comparison is performed as follows:

    1. Assert: Type(x) is not Number or BigInt.
    2. Assert: Type(x) is the same as Type(y).
    3. If Type(x) is Undefined, return true.
    4. If Type(x) is Null, return true.
    5. If Type(x) is String, then
      1. If x and y are exactly the same sequence of code units (same length and same code units at corresponding indices), return true; otherwise, return false.
    6. If Type(x) is Boolean, then
      1. If x and y are both true or both false, return true; otherwise, return false.
    7. If Type(x) is Symbol, then
      1. If x and y are both the same Symbol value, return true; otherwise, return false.
    8. Return true if x and y are the same Object value. Otherwise, return false.

    3.2.4Abstract Relational Comparison

    The comparison x < y, where x and y are values, produces true, false, or undefined (which indicates that at least one operand is NaN). In addition to x and y the algorithm takes a Boolean flag named LeftFirst as a parameter. The flag is used to control the order in which operations with potentially visible side-effects are performed upon x and y. It is necessary because ECMAScript specifies left to right evaluation of expressions. The default value of LeftFirst is true and indicates that the x parameter corresponds to an expression that occurs to the left of the y parameter's corresponding expression. If LeftFirst is false, the reverse is the case and operations must be performed upon y before x. Such a comparison is performed as follows:

    1. If the LeftFirst flag is true, then
      1. Let px be ? ToPrimitive(x, hint Number).
      2. Let py be ? ToPrimitive(y, hint Number).
    2. Else the order of evaluation needs to be reversed to preserve left to right evaluation,
      1. Let py be ? ToPrimitive(y, hint Number).
      2. Let px be ? ToPrimitive(x, hint Number).
    3. If both px and py are Strings, then
      1. If py is a prefix of px, return false. (A String value p is a prefix of String value q if q can be the result of concatenating p and some other String r. Note that any String is a prefix of itself, because r may be the empty String.)
      2. If px is a prefix of py, return true.
      3. Let k be the smallest nonnegative integer such that the code unit at index k within px is different from the code unit at index k within py. (There must be such a k, for neither String is a prefix of the other.)
      4. Let m be the integer that is the code unit value at index k within px.
      5. Let n be the integer that is the code unit value at index k within py.
      6. If m < n, return true. Otherwise, return false.
    4. Else,
      1. Let nx be ? ToNumeric(px). Because px and py are primitive values evaluation order is not important.
      2. Let nx be ? ToNumber(px). Because px and py are primitive values evaluation order is not important.
      3. Let ny be ? ToNumber(py).
      4. If nx is NaN, return undefined.
      5. If ny is NaN, return undefined.
      6. If nx and ny are the same Number value, return false.
      7. If nx is +0 and ny is -0, return false.
      8. If nx is -0 and ny is +0, return false.
      9. If nx is +∞, return false.
      10. If ny is +∞, return true.
      11. If ny is -∞, return false.
      12. If nx is -∞, return true.
      13. If the mathematical value of nx is less than the mathematical value of ny —note that these mathematical values are both finite and not both zero—return true. Otherwise, return false.
      14. Let ny be ? ToNumeric(py).
      15. If Type(nx) is Type(ny), return ? Type(nx)::lessThan(nx, ny).
      16. Assert: Type(nx) is BigInt and Type(ny) is Number, or Type(nx) is Number and Type(ny) is BigInt.
      17. If x or y are any of NaN, return undefined.
      18. If x is -∞, or y is +∞, return true.
      19. If x is +∞, or y is -∞, return false.
      20. If the mathematical value of nx is less than the mathematical value of ny, return true, otherwise return false.
    The previous Number-related contents of this algorithm will be moved into Number::lessThan.

    3.2.5Abstract Equality Comparison

    The comparison x == y, where x and y are values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is the same as Type(y), then
      1. Return the result of performing Strict Equality Comparison x === y.
    2. If x is null and y is undefined, return true.
    3. If x is undefined and y is null, return true.
    4. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ToNumber(y).
    5. If Type(x) is String and Type(y) is Number, return the result of the comparison ToNumber(x) == y.
    6. If Type(x) is BigInt and Type(y) is String,
      1. Let n be StringToBigInt(y).
      2. If n is NaN, return false.
      3. Return the result of x == n.
    7. If Type(x) is String and Type(y) is BigInt, return the result of y == x.
    8. If Type(x) is Boolean, return the result of the comparison ToNumber(x) == y.
    9. If Type(y) is Boolean, return the result of the comparison x == ToNumber(y).
    10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result of the comparison x == ? ToPrimitive(y).
    11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result of the comparison ? ToPrimitive(x) == y.
    12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt,
      1. If x or y are any of NaN, +∞, or -∞, return false.
      2. If the mathematical value of x is equal to the mathematical value of y, return true, otherwise return false.
    13. Return false.

    3.2.6Strict Equality Comparison

    The comparison x === y, where x and y are values, produces true or false. Such a comparison is performed as follows:

    1. If Type(x) is different from Type(y), return false.
    2. If Type(x) is Number or BigInt, then
      1. If x is NaN, return false.
      2. If y is NaN, return false.
      3. If x is the same Number value as y, return true.
      4. If x is +0 and y is -0, return true.
      5. If x is -0 and y is +0, return true.
      6. Return false.
      7. Return ! Type(x)::equal(x, y).
    3. Return SameValueNonNumberNumeric(x, y).
    The previous Number-related contents of this algorithm will be moved into Number::equal.

    4ECMAScript Language: Expressions

    4.1Update Expressions

    4.1.1Postfix Increment Operator

    4.1.1.1Runtime Semantics: Evaluation

    UpdateExpression:LeftHandSideExpression++
    1. Let lhs be the result of evaluating LeftHandSideExpression.
    2. Let oldValue be ? ToNumber(? GetValue(lhs)).
    3. Let newValue be the result of adding the value 1 to oldValue, using the same rules as for the + operator (see 12.8.5).
    4. Let oldValue be ? ToNumeric(? GetValue(lhs)).
    5. Let newValue be ? Type(oldvalue)::add(oldValue, Type(oldValue)::unit).
    6. Perform ? PutValue(lhs, newValue).
    7. Return oldValue.

    4.1.2Postfix Decrement Operator

    4.1.2.1Runtime Semantics: Evaluation

    UpdateExpression:LeftHandSideExpression--
    1. Let lhs be the result of evaluating LeftHandSideExpression.
    2. Let oldValue be ? ToNumber(? GetValue(lhs)).
    3. Let newValue be the result of subtracting the value 1 from oldValue, using the same rules as for the - operator (see 12.8.5).
    4. Let oldValue be ? ToNumeric(? GetValue(lhs)).
    5. Let newValue be ? Type(oldvalue)::subtract(oldValue, Type(oldValue)::unit).
    6. Perform ? PutValue(lhs, newValue).
    7. Return oldValue.

    4.1.3Prefix Increment Operator

    4.1.3.1Runtime Semantics: Evaluation

    UpdateExpression:++UnaryExpression
    1. Let expr be the result of evaluating UnaryExpression.
    2. Let oldValue be ? ToNumber(? GetValue(expr)).
    3. Let newValue be the result of adding the value 1 to oldValue, using the same rules as for the + operator (see 12.8.5).
    4. Let oldValue be ? ToNumeric(? GetValue(expr)).
    5. Let newValue be ? Type(oldvalue)::add(oldValue, Type(oldValue)::unit).
    6. Perform ? PutValue(expr, newValue).
    7. Return newValue.

    4.1.4Prefix Decrement Operator

    4.1.4.1Runtime Semantics: Evaluation

    UpdateExpression:--UnaryExpression
    1. Let expr be the result of evaluating UnaryExpression.
    2. Let oldValue be ? ToNumber(? GetValue(expr)).
    3. Let newValue be the result of subtracting the value 1 from oldValue, using the same rules as for the - operator (see 12.8.5).
    4. Let oldValue be ? ToNumeric(? GetValue(expr)).
    5. Let newValue be ? Type(oldvalue)::subtract(oldValue, Type(oldValue)::unit).
    6. Perform ? PutValue(expr, newValue).
    7. Return newValue.

    4.2Unary Operators

    4.2.1The typeof Operator

    4.2.1.1Runtime Semantics: Evaluation

    UnaryExpression:typeofUnaryExpression
    Table 8: typeof Operator Results
    Type of val Result
    BigInt "bigint"

    4.2.2Unary + Operator

    Note

    The unary + operator converts its operand to Number type.

    4.2.2.1Runtime Semantics: Evaluation

    UnaryExpression:+UnaryExpression
    1. Let expr be the result of evaluating UnaryExpression.
    2. Return ? ToNumber(? GetValue(expr)).
    The definition here is unchanged, and still uses ToNumber rather than ToNumeric. This means that + will throw on BigInts. The semantics here are designed to allow expressions of the form +x to always return Numbers, which is necessary to preserve assumptions made by asm.js.

    4.2.3Unary - Operator

    Note

    The unary - operator converts its operand to Number type and then negates it. Negating +0 produces -0, and negating -0 produces +0.

    4.2.3.1Runtime Semantics: Evaluation

    UnaryExpression:-UnaryExpression
    1. Let expr be the result of evaluating UnaryExpression.
    2. Let oldValue be ? ToNumberToNumeric(? GetValue(expr)).
    3. If oldValue is NaN, return NaN.
    4. Return the result of negating oldValue; that is, compute a Number with the same magnitude but opposite sign.
    5. Let T be Type(oldValue).
    6. Return ? T::unaryMinus(oldValue).

    4.2.4Bitwise NOT Operator ( ~ )

    4.2.4.1Runtime Semantics: Evaluation

    UnaryExpression:~UnaryExpression
    1. Let expr be the result of evaluating UnaryExpression.
    2. Let oldValue be ? ToInt32ToNumeric(? GetValue(expr)).
    3. Return the result of applying bitwise complement to oldValue. The result is a signed 32-bit integer.
    4. Let T be Type(oldValue).
    5. Return ? T::bitwiseNOT(oldValue).

    4.3Exponentiation Operator

    4.3.1Runtime Semantics: Evaluation

    ExponentiationExpression:UpdateExpression**ExponentiationExpression
    1. Let left be the result of evaluating UpdateExpression.
    2. Let leftValue be ? GetValue(left).
    3. Let right be the result of evaluating ExponentiationExpression.
    4. Let rightValue be ? GetValue(right).
    5. Let base be ? ToNumberToNumeric(leftValue).
    6. Let exponent be ? ToNumberToNumeric(rightValue).
    7. Return the result of Applying the ** operator with base and exponent as specified in 12.7.3.4.
    8. If Type(base) does not equal Type(exponent), throw a TypeError exception.
    9. Return ? Type(base)::exponentiate(base, exponent).

    4.4Multiplicative Operators

    4.4.1Runtime Semantics: Evaluation

    MultiplicativeExpression:MultiplicativeExpressionMultiplicativeOperatorExponentiationExpression
    1. Let left be the result of evaluating MultiplicativeExpression.
    2. Let leftValue be ? GetValue(left).
    3. Let right be the result of evaluating ExponentiationExpression.
    4. Let rightValue be ? GetValue(right).
    5. Let lnum be ? ToNumberToNumeric(leftValue).
    6. Let rnum be ? ToNumberToNumeric(rightValue).
    7. Return the result of applying the MultiplicativeOperator (*, /, or %) to lnum and rnum as specified in 12.7.3.1, 12.7.3.2, or 12.7.3.3.
    8. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    9. Let T be Type(lnum).
    10. If MultiplicativeOperator is *, return T::multiply(lnum, rnum).
    11. If MultiplicativeOperator is /, return T::divide(lnum, rnum).
    12. Otherwise, MultiplicativeOperator is %; return T::remainder(lnum, rnum).

    4.5Additive Operators

    4.5.1The Addition Operator ( + )

    Note

    The addition operator either performs string concatenation or numeric addition.

    4.5.1.1Runtime Semantics: Evaluation

    AdditiveExpression:AdditiveExpression+MultiplicativeExpression
    1. Let lref be the result of evaluating AdditiveExpression.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating MultiplicativeExpression.
    4. Let rval be ? GetValue(rref).
    5. Let lprim be ? ToPrimitive(lval).
    6. Let rprim be ? ToPrimitive(rval).
    7. If Type(lprim) is String or Type(rprim) is String, then
      1. Let lstr be ? ToString(lprim).
      2. Let rstr be ? ToString(rprim).
      3. Return the String that is the result of concatenating lstr and rstr.
    8. Let lnum be ? ToNumberToNumeric(lprim).
    9. Let rnum be ? ToNumberToNumeric(rprim).
    10. Return the result of applying the addition operation to lnum and rnum. See the Note below 12.8.5.
    11. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    12. Let T be Type(lnum).
    13. Return T::add(lnum, rnum).
    Note 1

    No hint is provided in the calls to ToPrimitive in steps 5 and 6. All standard objects except Date objects handle the absence of a hint as if the hint Number were given; Date objects handle the absence of a hint as if the hint String were given. Exotic objects may handle the absence of a hint in some other manner.

    Note 2

    Step 7 differs from step 5 of the Abstract Relational Comparison algorithm, by using the logical-or operation instead of the logical-and operation.

    4.5.2The Subtraction Operator ( - )

    4.5.2.1Runtime Semantics: Evaluation

    AdditiveExpression:AdditiveExpression-MultiplicativeExpression
    1. Let lref be the result of evaluating AdditiveExpression.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating MultiplicativeExpression.
    4. Let rval be ? GetValue(rref).
    5. Let lnum be ? ToNumberToNumeric(lval).
    6. Let rnum be ? ToNumberToNumeric(rval).
    7. Return the result of applying the subtraction operation to lnum and rnum. See the note below 12.8.5.
    8. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    9. Let T be Type(lnum).
    10. Return T::subtract(lnum, rnum).

    4.6Bitwise Shift Operators

    4.6.1The Left Shift Operator ( << )

    Note

    Performs a bitwise left shift operation on the left operand by the amount specified by the right operand.

    4.6.1.1Runtime Semantics: Evaluation

    ShiftExpression:ShiftExpression<<AdditiveExpression
    1. Let lref be the result of evaluating ShiftExpression.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating AdditiveExpression.
    4. Let rval be ? GetValue(rref).
    5. Let lnum be ? ToInt32ToNumeric(lval).
    6. Let rnum be ? ToUint32ToNumeric(rval).
    7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
    8. Return the result of left shifting lnum by shiftCount bits. The result is a signed 32-bit integer.
    9. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    10. Let T be Type(lnum).
    11. Return T::leftShift(lnum, rnum).

    4.6.2The Signed Right Shift Operator ( >> )

    Note

    Performs a sign-filling bitwise right shift operation on the left operand by the amount specified by the right operand.

    4.6.2.1Runtime Semantics: Evaluation

    ShiftExpression:ShiftExpression>>AdditiveExpression
    1. Let lref be the result of evaluating ShiftExpression.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating AdditiveExpression.
    4. Let rval be ? GetValue(rref).
    5. Let lnum be ? ToInt32ToNumeric(lval).
    6. Let rnum be ? ToUint32ToNumeric(rval).
    7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
    8. Return the result of performing a sign-extending right shift of lnum by shiftCount bits. The most significant bit is propagated. The result is a signed 32-bit integer.
    9. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    10. Let T be Type(lnum).
    11. Return T::signedRightShift(lnum, rnum).

    4.6.3The Unsigned Right Shift Operator ( >>> )

    Note

    Performs a zero-filling bitwise right shift operation on the left operand by the amount specified by the right operand.

    4.6.3.1Runtime Semantics: Evaluation

    ShiftExpression:ShiftExpression>>>AdditiveExpression
    1. Let lref be the result of evaluating ShiftExpression.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating AdditiveExpression.
    4. Let rval be ? GetValue(rref).
    5. Let lnum be ? ToInt32ToNumeric(lval).
    6. Let rnum be ? ToUint32ToNumeric(rval).
    7. Let shiftCount be the result of masking out all but the least significant 5 bits of rnum, that is, compute rnum & 0x1F.
    8. Return the result of performing a zero-filling right shift of lnum by shiftCount bits. Vacated bits are filled with zero. The result is an unsigned 32-bit integer.
    9. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    10. Let T be Type(lnum).
    11. Return T::unsignedRightShift(lnum, rnum).

    4.7Binary Bitwise Operators

    4.7.1Runtime Semantics: Evaluation

    The production A:A@B , where @ is one of the bitwise operators in the productions above, is evaluated as follows:

    1. Let lref be the result of evaluating A.
    2. Let lval be ? GetValue(lref).
    3. Let rref be the result of evaluating B.
    4. Let rval be ? GetValue(rref).
    5. Let lnum be ? ToInt32ToNumeric(lval).
    6. Let rnum be ? ToUint32ToNumeric(rval).
    7. If Type(lnum) does not equal Type(rnum), throw a TypeError exception.
    8. Let T be Type(lnum).
    9. If @ is &, return T::bitwiseAND(lnum, rnum).
    10. If @ is |, return T::bitwiseOR(lnum, rnum).
    11. Otherwise, @ is ^; return T::bitwiseXOR(lnum, rnum).

    5BigInt Objects

    5.1The BigInt Constructor

    The BigInt constructor is the %BigInt% intrinsic object and the initial value of the BigInt property of the global object. When BigInt is called as a function, it performs a type conversion.

    5.1.1IsSafeInteger ( number )

    1. Assert: Type(number) is Number.
    2. If number is NaN, +∞, or -∞, return false.
    3. Let integer be ! ToInteger(number).
    4. If ! SameValueZero(integer, number) is false, return false.
    5. If abs(integer) ≤ 253-1, return true.
    6. Otherwise, return false.
    Number.isSafeInteger will be refactored to call this abstract algorithm.

    5.1.2NumberToBigInt ( number )

    1. Assert: Type(number) is Number.
    2. If IsSafeInteger(number) is false, throw a RangeError exception.
    3. Return a BigInt value representing the mathematical value of number.

    5.1.3BigInt ( value )

    When BigInt is called with argument value, the following steps are taken:

    1. If NewTarget is not undefined, throw a TypeError exception.
    2. Let prim be ? ToPrimitive(value, hint Number).
    3. If Type(prim) is Number, return ? NumberToBigInt(prim).
    4. Otherwise, return ? ToBigInt(value).

    5.2Properties of the BigInt Constructor

    The value of the [[Prototype]] internal slot of the BigInt constructor is the intrinsic object %FunctionPrototype%.

    The BigInt constructor has the following properties:

    5.2.1BigInt.asUintN ( bits, bigint )

    When the BigInt.asUintN function is called with two argument bits and bigint, the following steps are taken:

    1. Let bits be ? ToIndex(bits).
    2. Let bigintToBigInt(bigint).
    3. Return a BigInt representing bigint modulo 2bits.

    5.2.2BigInt.asIntN ( bits, bigint )

    When the BigInt.asIntN is called with two argument bits and bigint, the following steps are taken:

    1. Let bits be ? ToIndex(bits).
    2. Let bigintToBigInt(bigint).
    3. Let mod be a BigInt representing bigint modulo 2bits.
    4. If mod ≥ 2bits - 1, return mod - 2bits; otherwise, return mod.

    5.2.3BigInt.prototype

    The initial value of BigInt.prototype is the intrinsic object %BigIntPrototype%.

    This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }.

    5.3Properties of the BigInt Prototype Object

    The BigInt prototype object is the intrinsic object %BigIntPrototype%. The BigInt prototype object is an ordinary object. The BigInt prototype is not a BigInt object; it does not have a [[BigIntData]] internal slot.

    The value of the [[Prototype]] internal slot of the BigInt prototype object is the intrinsic object %ObjectPrototype%.

    The abstract operation thisBigIntValue(value) performs the following steps:

    1. If Type(value) is BigInt, return value.
    2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
      1. Assert: value.[[BigIntData]] is a BigInt value.
      2. Return value.[[BigIntData]].
    3. Throw a TypeError exception.

    The phrase “this BigInt value” within the specification of a method refers to the result returned by calling the abstract operation thisBigIntValue with the this value of the method invocation passed as the argument.

    5.3.1BigInt.prototype.constructor

    The initial value of BigInt.prototype.constructor is the intrinsic object %BigInt%.

    5.3.2BigInt.prototype.toLocaleString ( [ reserved1 [ , reserved2 ] ] )

    An ECMAScript implementation that includes the ECMA-402 Internationalization API must implement the BigInt.prototype.toLocaleString method as specified in the ECMA-402 specification. If an ECMAScript implementation does not include the ECMA-402 API the following specification of the toLocaleString method is used.

    Produces a String value that represents this BigInt value formatted according to the conventions of the host environment's current locale. This function is implementation-dependent, and it is permissible, but not encouraged, for it to return the same thing as toString.

    The meanings of the optional parameters to this method are defined in the ECMA-402 specification; implementations that do not include ECMA-402 support must not use those parameter positions for anything else.

    5.3.3BigInt.prototype.toString ( [ radix ] )

    Note

    The optional radix should be an integer value in the inclusive range 2 to 36. If radix not present or is undefined the Number 10 is used as the value of radix.

    The following steps are performed:

    1. Let x be ? thisBigIntValue(this value).
    2. If radix is not present, let radixNumber be 10.
    3. Else if radix is undefined, let radixNumber be 10.
    4. Else, let radixNumber be ? ToInteger(radix).
    5. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception.
    6. If radixNumber = 10, return ! ToString(x).
    7. Return the String representation of this Number value using the radix specified by radixNumber. Letters a-z are used for digits with values 10 through 35. The precise algorithm is implementation-dependent, however the algorithm should be a generalization of that specified in 3.1.4.1.

    The toString function is not generic; it throws a TypeError exception if its this value is not a BigInt or a BigInt object. Therefore, it cannot be transferred to other kinds of objects for use as a method.

    5.3.4BigInt.prototype.valueOf ( )

    1. Return ? thisBigIntValue(this value).

    5.3.5BigInt.prototype [ @@toStringTag ]

    The initial value of the @@toStringTag property is the String value "BigInt".

    This property has the attributes { [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }.

    6Modified algorithms

    6.1Runtime Semantics: SerializeJSONProperty ( key, holder )

    The abstract operation SerializeJSONProperty with arguments key, and holder has access to ReplacerFunction from the invocation of the stringify method. Its algorithm is as follows:

    1. Let value be ? Get(holder, key).
    2. If Type(value) is Object or BigInt, then
      1. Let toJSON be ? GetGetV(value, "toJSON").
      2. If IsCallable(toJSON) is true, then
        1. Set value to ? Call(toJSON, value, « key »).
    3. If ReplacerFunction is not undefined, then
      1. Set value to ? Call(ReplacerFunction, holder, « key, value »).
    4. If Type(value) is Object, then
      1. If value has a [[NumberData]] internal slot, then
        1. Set value to ? ToNumber(value).
      2. Else if value has a [[StringData]] internal slot, then
        1. Set value to ? ToString(value).
      3. Else if value has a [[BooleanData]] internal slot, then
        1. Set value to value.[[BooleanData]].
      4. Else if value has a [[BigIntData]] internal slot, then
        1. Set value to value.[[BigIntData]].
    5. If value is null, return "null".
    6. If value is true, return "true".
    7. If value is false, return "false".
    8. If Type(value) is String, return QuoteJSONString(value).
    9. If Type(value) is Number, then
      1. If value is finite, return ! ToString(value).
      2. Else, return "null".
    10. If Type(value) is BigInt, throw a TypeError exception
    11. If Type(value) is Object and IsCallable(value) is false, then
      1. Let isArray be ? IsArray(value).
      2. If isArray is true, return ? SerializeJSONArray(value).
      3. Else, return ? SerializeJSONObject(value).
    12. Return undefined.

    6.2Number ( value )

    When Number is called with argument number, the following steps are taken:

    1. If no arguments were passed to this function invocation, let n be +0.
    2. Else,
      1. Let prim be ? ToNumeric(value).
      2. If Type(prim) is BigInt, let n be the Number value for prim.
      3. Otherwise, let n be prim.
    3. If NewTarget is undefined, return n.
    4. Let O be ? OrdinaryCreateFromConstructor(NewTarget, "%NumberPrototype%", « [[NumberData]] »).
    5. Set O.[[NumberData]] to n.
    6. Return O.
    Note
    See 6.1.6 in the second-to-last paragraph for the definition of the phrase "The Number value for prim".
    That paragraph should possibly be refactored into a separate abstract operation; see this bug for more discussion about the integration of different numeric types and casting operations between them.

    6.3Math.pow ( base, exponent )

    1. Let base be ? ToNumber(base).
    2. Let exponent be ? ToNumber(exponent).
    3. Return Number::exponentiate(base, exponent).

    7TypedArrays and DataViews

    BigInt is integrated into TypedArray, DataView, SharedArrayBuffer and Atomics by providing Int64 and Uint64 access as represented by BigInts on the ECMAScript side.

    7.1TypedArray Objects

    Table 9: The TypedArray Constructors
    Constructor Name and Intrinsic Element Type Element Size Conversion Operation Description Equivalent C Type
    BigInt64Array
    %BigInt64Array%
    BigInt64 8 ToBigInt64 64-bit 2's complement signed integer signed long long
    BigUint64Array
    %BigUint64Array%
    BigUint64 8 ToBigUint64 64-bit unsigned integer unsigned long long

    7.2StringToBigInt ( argument )

    Apply the algorithm in 3.1.3.1 with the following changes:
    • Replace the StrUnsignedDecimalLiteral production with DecimalDigits to not allow decimal points or exponents.
    • If the MV is NaN, return NaN, otherwise return the BigInt which exactly corresponds to the MV, rather than rounding to a Number.
    Editor's Note
    StringToBigInt("") is 0n according to the logic in 3.1.3.1.

    7.3ToBigInt ( argument )

    The abstract operation ToBigInt converts its argument argument to a BigInt value, or throws if an implicit conversion from Number would be required.

    1. Let prim be ? ToPrimitive(argument, hint Number).
    2. Return the value that prim corresponds to in Table 10.
    Table 10: BigInt Conversions
    Argument Type Result
    Undefined Throw a TypeError exception.
    Null Throw a TypeError exception.
    Boolean Return 1n if prim is true and 0n if prim is false.
    BigInt Return prim.
    Number Throw a TypeError exception.
    String
    1. Let n be StringToBigInt(prim).
    2. If n is NaN, throw a SyntaxError.
    3. Return n.
    Symbol Throw a TypeError exception.

    7.4ToBigInt64 ( argument )

    The abstract operation ToBigInt64 converts argument to one of 264 integer values in the range -263 through 263-1, inclusive. This abstract operation functions as follows:

    1. Let nToBigInt(argument).
    2. Let int64bit be n modulo 264.
    3. If int64bit ≥ 264, return int64bit - 263; otherwise return int64bit.

    7.5ToBigUint64 ( argument )

    The abstract operation ToBigUint64 converts argument to one of 264 integer values in the range 0 through 264-1, inclusive. This abstract operation functions as follows:

    1. Let nToBigInt(argument).
    2. Let int64bit be n modulo 264.
    3. Return int64bit.

    7.6RawBytesToNumber( type, rawBytes, isLittleEndian )

    The abstract operation RawBytesToNumber takes three parameters, a String type, a List rawBytes, and a Boolean isLittleEndian. This operation performs the following steps:

    1. Let elementSize be the Number value of the Element Size value specified in Table 9 for Element Type type.
    2. If isLittleEndian is false, reverse the order of the elements of rawBytes.
    3. If type is "Float32", then
      1. Let value be the byte elements of rawBytes concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2008 binary32 value.
      2. If value is an IEEE 754-2008 binary32 NaN value, return the NaN Number value.
      3. Return the Number value that corresponds to value.
    4. If type is "Float64", then
      1. Let value be the byte elements of rawBytes concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2008 binary64 value.
      2. If value is an IEEE 754-2008 binary64 NaN value, return the NaN Number value.
      3. Return the Number value that corresponds to value.
    5. If the first code unit of type is "U" or type is "BigUint64Array", then
      1. Let intValue be the byte elements of rawBytes concatenated and interpreted as a bit string encoding of an unsigned little-endian binary number.
    6. Else,
      1. Let intValue be the byte elements of rawBytes concatenated and interpreted as a bit string encoding of a binary little-endian 2's complement number of bit length elementSize × 8.
    7. If type is "BigUint64" or "BigInt64", return the BigInt value that corresponds to intValue.
    8. Otherwise, return the Number value that corresponds to intValue.

    7.7NumberToRawBytes( type, value, isLittleEndian )

    The abstract operation NumberToRawBytes takes three parameters, a String type, a BigInt or Number value, and a Boolean isLittleEndian. This operation performs the following steps:

    1. If type is "Float32", then
      1. Set rawBytes to a List containing the 4 bytes that are the result of converting value to IEEE 754-2008 binary32 format using “Round to nearest, ties to even” rounding mode. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawValue may be set to any implementation chosen IEEE 754-2008 binary32 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.
    2. Else if type is "Float64", then
      1. Set rawBytes to a List containing the 8 bytes that are the IEEE 754-2008 binary64 format encoding of value. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawValue may be set to any implementation chosen IEEE 754-2008 binary64 format Not-a-Number encoding. An implementation must always choose the same encoding for each implementation distinguishable NaN value.
    3. Else,
      1. Let n be the Number value of the Element Size specified in Table 9 for Element Type type.
      2. Let convOp be the abstract operation named in the Conversion Operation column in Table 9 for Element Type type.
      3. Let intValue be convOp(value) treated as a mathematical value, whether the result is a BigInt or Number.
      4. If intValue ≥ 0, then
        1. Let rawBytes be a List containing the n-byte binary encoding of intValue. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
      5. Else,
        1. Let rawBytes be a List containing the n-byte binary 2's complement encoding of intValue. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
    4. Return rawBytes.

    7.8IntegerIndexedElementSet ( O, index, value )

    The abstract operation IntegerIndexedElementSet with arguments O, index, and value performs the following steps:

    1. Assert: Type(index) is Number.
    2. Assert: O is an Object that has [[ViewedArrayBuffer]], [[ArrayLength]], [[ByteOffset]], and [[TypedArrayName]] internal slots.
    3. Let arrayTypeName be the String value of O.[[TypedArrayName]].
    4. Let elementType be the String value of the Element Type value in Table 9 for arrayTypeName.
    5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let numValue be ? ToBigInt(v).
    6. Otherwise, let numValue be ? ToNumber(value).
    7. Let buffer be O.[[ViewedArrayBuffer]].
    8. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
    9. If IsInteger(index) is false, return false.
    10. If index = -0, return false.
    11. Let length be O.[[ArrayLength]].
    12. If index < 0 or indexlength, return false.
    13. Let offset be O.[[ByteOffset]].
    14. Let elementSize be the Number value of the Element Size value specified in Table 9 for arrayTypeName.
    15. Let indexedPosition be (index × elementSize) + offset.
    16. Perform SetValueInBuffer(buffer, indexedPosition, elementType, numValue, true, "Unordered").
    17. Return true.

    7.9SetValueInBuffer ( arrayBuffer, byteIndex, type, value, isTypedArray, order [ , isLittleEndian ] )

    The abstract operation SetValueInBuffer takes seven parameters, an ArrayBuffer or SharedArrayBuffer arrayBuffer, an integer byteIndex, a String type, a Number value, a Boolean isTypedArray, a String order, and optionally a Boolean isLittleEndian. This operation performs the following steps:

    1. Assert: IsDetachedBuffer(arrayBuffer) is false.
    2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
    3. Assert: byteIndex is an integer value ≥ 0.
    4. Assert: Type(value) is Number if type is not "BigInt64" or "BigUint64"; otherwise, Type(value) is BigInt.
    5. Let block be arrayBuffer.[[ArrayBufferData]].
    6. Let elementSize be the Number value of the Element Size value specified in Table 9 for Element Type type.
    7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
    8. Let rawBytes be NumberToRawBytes(type, value, isLittleEndian).
    9. If IsSharedArrayBuffer(arrayBuffer) is true, then
      1. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
      2. Let eventList be the [[EventList]] field of the element in execution.[[EventLists]] whose [[AgentSignifier]] is AgentSignifier().
      3. If isTypedArray is true and type is "Int8", "Uint8", "Int16", "Uint16", "Int32", or "Uint32", let noTear be true; otherwise let noTear be false.
      4. Append WriteSharedMemory{ [[Order]]: order, [[NoTear]]: noTear, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes } to eventList.
    10. Else, store the individual bytes of rawBytes into block, in order, starting at block[byteIndex].
    11. Return NormalCompletion(undefined).
    Editor's Note
    BigInt64 and BigUint64, like Float64, are excluded from the list of types which experience noTear writes. That is, non-atomic writes may be observed in a partially completed state.

    7.10GetModifySetValueInBuffer( arrayBuffer, byteIndex, type, value, op [ , isLittleEndian ] )

    The abstract operation GetModifySetValueInBuffer takes six parameters, a SharedArrayBuffer arrayBuffer, a nonnegative integer byteIndex, a String type, a Number value, a semantic function op, and optionally a Boolean isLittleEndian. This operation performs the following steps:

    1. Assert: IsSharedArrayBuffer(arrayBuffer) is true.
    2. Assert: There are sufficient bytes in arrayBuffer starting at byteIndex to represent a value of type.
    3. Assert: byteIndex is an integer value ≥ 0.
    4. Assert: Type(value) is Number if type is not "BigInt64" or "BigUint64"; otherwise, Type(value) is BigInt.
    5. Let block be arrayBuffer.[[ArrayBufferData]].
    6. Let elementSize be the Number value of the Element Size value specified in Table 9 for Element Type type.
    7. If isLittleEndian is not present, set isLittleEndian to the value of the [[LittleEndian]] field of the surrounding agent's Agent Record.
    8. Let rawBytes be NumberToRawBytes(type, value, isLittleEndian).
    9. Let execution be the [[CandidateExecution]] field of the surrounding agent's Agent Record.
    10. Let eventList be the [[EventList]] field of the element in execution.[[EventLists]] whose [[AgentSignifier]] is AgentSignifier().
    11. Let rawBytesRead be a List of length elementSize of nondeterministically chosen byte values.
    12. NOTE: In implementations, rawBytesRead is the result of a load-link, of a load-exclusive, or of an operand of a read-modify-write instruction on the underlying hardware. The nondeterminism is a semantic prescription of the memory model to describe observable behaviour of hardware with weak consistency.
    13. Let rmwEvent be ReadModifyWriteSharedMemory{ [[Order]]: "SeqCst", [[NoTear]]: true, [[Block]]: block, [[ByteIndex]]: byteIndex, [[ElementSize]]: elementSize, [[Payload]]: rawBytes, [[ModifyOp]]: op }.
    14. Append rmwEvent to eventList.
    15. Append Chosen Value Record { [[Event]]: rmwEvent, [[ChosenValue]]: rawBytesRead } to execution.[[ChosenValues]].
    16. Return RawBytesToNumber(type, rawBytesRead, isLittleEndian).

    7.11ValidateSharedIntegerTypedArray(typedArray [ , waitable ] )

    The abstract operation ValidateSharedIntegerTypedArray takes one argument typedArray and an optional Boolean waitable. It performs the following steps:

    1. If the waitable argument was not provided, set waitable to false.
    2. If Type(typedArray) is not Object, throw a TypeError exception.
    3. If typedArray does not have a [[TypedArrayName]] internal slot, throw a TypeError exception.
    4. Let typeName be typedArray.[[TypedArrayName]].
    5. If waitable is true, then
      1. If typeName is not "Int32Array" or "BigInt64Array", throw a TypeError exception.
    6. Else,
      1. If typeName is not "Int8Array", "Uint8Array", "Int16Array", "Uint16Array", "Int32Array", "Uint32Array", "BigUint64Array", or "BigInt64Array" throw a TypeError exception.
    7. Assert: typedArray has a [[ViewedArrayBuffer]] internal slot.
    8. Let buffer be typedArray.[[ViewedArrayBuffer]].
    9. If IsSharedArrayBuffer(buffer) is false, throw a TypeError exception.
    10. Return buffer.

    7.12AtomicReadModifyWrite( typedArray, index, value, op )

    The abstract operation AtomicReadModifyWrite takes four arguments, typedArray, index, value, and a pure combining operation op. The pure combining operation op takes two List of byte values arguments and returns a List of byte values. The operation atomically loads a value, combines it with another value, and stores the result of the combination. It returns the loaded value. It performs the following steps:

    1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray).
    2. Let i be ? ValidateAtomicAccess(typedArray, index).
    3. Let arrayTypeName be typedArray.[[TypedArrayName]].
    4. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let v be ? ToBigInt(v).
    5. Otherwise, let v be ? ToInteger(value).
    6. Let elementSize be the Number value of the Element Size value specified in Table 9 for arrayTypeName.
    7. Let elementType be the String value of the Element Type value in Table 9 for arrayTypeName.
    8. Let offset be typedArray.[[ByteOffset]].
    9. Let indexedPosition be (i × elementSize) + offset.
    10. Return GetModifySetValueInBuffer(buffer, indexedPosition, elementType, v, op).

    7.13Atomics.wait( typedArray, index, value, timeout )

    Atomics.wait puts the calling agent in a wait queue and puts it to sleep until it is awoken or the sleep times out. The following steps are taken:

    1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray, true).
    2. Let i be ? ValidateAtomicAccess(typedArray, index).
    3. If typedArray.[[TypedArrayName]] is "BigInt64Array", let v be ? ToBigInt64(value)
    4. Otherwise, let v be ? ToInt32(value).
    5. Let q be ? ToNumber(timeout).
    6. If q is NaN, let t be +∞, else let t be max(q, 0).
    7. Let B be AgentCanSuspend().
    8. If B is false, throw a TypeError exception.
    9. Let block be buffer.[[ArrayBufferData]].
    10. Let offset be typedArray.[[ByteOffset]].
    11. Let elementSize be the Number value of the Element Size value specified in Table 9 for arrayTypeName.
    12. Let indexedPosition be (i × 4elementSize) + offset.
    13. Let WL be GetWaiterList(block, indexedPosition).
    14. Perform EnterCriticalSection(WL).
    15. Let w be ! AtomicLoad(typedArray, i).
    16. If v is not equal to w, then
      1. Perform LeaveCriticalSection(WL).
      2. Return the String "not-equal".
    17. Let W be AgentSignifier().
    18. Perform AddWaiter(WL, W).
    19. Let awoken be Suspend(WL, W, t).
    20. If awoken is true, then
      1. Assert: W is not on the list of waiters in WL.
    21. Else,
      1. Perform RemoveWaiter(WL, W).
    22. Perform LeaveCriticalSection(WL).
    23. If awoken is true, return the String "ok".
    24. Return the String "timed-out".

    7.14Atomics.wake( typedArray, index, count )

    Atomics.wake wakes up some agents that are sleeping in the wait queue. The following steps are taken:

    1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray, true).
    2. Let i be ? ValidateAtomicAccess(typedArray, index).
    3. If count is undefined, let c be +∞.
    4. Else,
      1. Let intCount be ? ToInteger(count).
      2. Let c be max(intCount, 0).
    5. Let block be buffer.[[ArrayBufferData]].
    6. Let offset be typedArray.[[ByteOffset]].
    7. Let elementSize be the Number value of the Element Size value specified in Table 9 for arrayTypeName.
    8. Let indexedPosition be (i × 4elementSize) + offset.
    9. Let WL be GetWaiterList(block, indexedPosition).
    10. Let n be 0.
    11. Perform EnterCriticalSection(WL).
    12. Let S be RemoveWaiters(WL, c).
    13. Repeat, while S is not an empty List,
      1. Let W be the first agent in S.
      2. Remove W from the front of S.
      3. Perform WakeWaiter(WL, W).
      4. Add 1 to n.
    14. Perform LeaveCriticalSection(WL).
    15. Return n.

    7.15Atomics.store( typedArray, index, value )

    The following steps are taken:

    1. Let buffer be ? ValidateSharedIntegerTypedArray(typedArray).
    2. Let i be ? ValidateAtomicAccess(typedArray, index).
    3. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let v be ? ToBigInt(value).
    4. Otherwise, let v be ? ToInteger(value).
    5. Let arrayTypeName be typedArray.[[TypedArrayName]].
    6. Let elementSize be the Number value of the Element Size value specified in Table 9 for arrayTypeName.
    7. Let elementType be the String value of the Element Type value in Table 9 for arrayTypeName.
    8. Let offset be typedArray.[[ByteOffset]].
    9. Let indexedPosition be (i × elementSize) + offset.
    10. Perform SetValueInBuffer(buffer, indexedPosition, elementType, v, true, "SeqCst").
    11. Return v.

    7.16%TypedArray%.prototype.sort ( comparefn )

    %TypedArray%.prototype.sort is a distinct function that, except as described below, implements the same requirements as those of Array.prototype.sort as defined in 22.1.3.25. The implementation of the %TypedArray%.prototype.sort specification may be optimized with the knowledge that the this value is an object that has a fixed length and whose integer indexed properties are not sparse. The only internal methods of the this object that the algorithm may call are [[Get]] and [[Set]].

    This function is not generic. The this value must be an object with a [[TypedArrayName]] internal slot.

    Upon entry, the following steps are performed to initialize evaluation of the sort function. These steps are used instead of the entry steps in 22.1.3.25:

    1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception.
    2. Let obj be the this value.
    3. Let buffer be ? ValidateTypedArray(obj).
    4. Let len be obj.[[ArrayLength]].

    The implementation-defined sort order condition for exotic objects is not applied by %TypedArray%.prototype.sort.

    The following version of SortCompare is used by %TypedArray%.prototype.sort. It performs a numeric comparison rather than the string comparison used in 22.1.3.25. SortCompare has access to the comparefn and buffer values of the current invocation of the sort method.

    When the TypedArray SortCompare abstract operation is called with two arguments x and y, the following steps are taken:

    1. Assert: Both Type(x) and Type(y) is Number or both are BigInt.
    2. If comparefn is not undefined, then
      1. Let v be ? ToNumber(? Call(comparefn, undefined, « x, y »)).
      2. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
      3. If v is NaN, return +0.
      4. Return v.
    3. If x and y are both NaN, return +0.
    4. If x is NaN, return 1.
    5. If y is NaN, return -1.
    6. If x < y, return -1.
    7. If x > y, return 1.
    8. If x is -0 and y is +0, return -1.
    9. If x is +0 and y is -0, return 1.
    10. Return +0.
    Note

    Because NaN always compares greater than any other value, NaN property values always sort to the end of the result when comparefn is not provided.

    7.17%TypedArray%.prototype.fill ( value [ , start [ , end ] ] )

    The interpretation and use of the arguments of %TypedArray%.prototype.fill are the same as for Array.prototype.fill as defined in 22.1.3.6.

    The following steps are taken:

    1. Let O be the this value.
    2. Perform ? ValidateTypedArray(O).
    3. Let len be O.[[ArrayLength]].
    4. If O.[[TypedArrayName]] is "BigUint64Array" or "BigInt64Array", let value be ? ToBigInt(value)
    5. Otherwise, let value be ? ToNumber(value).
    6. Let relativeStart be ? ToInteger(start).
    7. If relativeStart < 0, let k be max((len + relativeStart), 0); else let k be min(relativeStart, len).
    8. If end is undefined, let relativeEnd be len; else let relativeEnd be ? ToInteger(end).
    9. If relativeEnd < 0, let final be max((len + relativeEnd), 0); else let final be min(relativeEnd, len).
    10. If IsDetachedBuffer(O.[[ViewedArrayBuffer]]) is true, throw a TypeError exception.
    11. Repeat, while k < final
      1. Let Pk be ! ToString(k).
      2. Perform ! Set(O, Pk, value, true).
      3. Increase k by 1.
    12. Return O.

    7.18%TypedArray%.prototype.set ( array [ , offset ] )

    Sets multiple values in this TypedArray, reading the values from the object array. The optional offset value indicates the first element index in this TypedArray where values are written. If omitted, it is assumed to be 0.

    1. Assert: array is any ECMAScript language value other than an Object with a [[TypedArrayName]] internal slot. If it is such an Object, the definition in 7.19 applies.
    2. Let target be the this value.
    3. If Type(target) is not Object, throw a TypeError exception.
    4. If target does not have a [[TypedArrayName]] internal slot, throw a TypeError exception.
    5. Assert: target has a [[ViewedArrayBuffer]] internal slot.
    6. Let targetOffset be ? ToInteger(offset).
    7. If targetOffset < 0, throw a RangeError exception.
    8. Let targetBuffer be target.[[ViewedArrayBuffer]].
    9. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
    10. Let targetLength be target.[[ArrayLength]].
    11. Let targetName be the String value of target.[[TypedArrayName]].
    12. Let targetElementSize be the Number value of the Element Size value specified in Table 9 for targetName.
    13. Let targetType be the String value of the Element Type value in Table 9 for targetName.
    14. Let targetByteOffset be target.[[ByteOffset]].
    15. Let src be ? ToObject(array).
    16. Let srcLength be ? ToLength(? Get(src, "length")).
    17. If srcLength + targetOffset > targetLength, throw a RangeError exception.
    18. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
    19. Let k be 0.
    20. Let limit be targetByteIndex + targetElementSize × srcLength.
    21. Repeat, while targetByteIndex < limit
      1. Let Pk be ! ToString(k).
      2. Let kNumber be ? ToNumber(? Get(src, Pk)).
      3. Let value be ? Get(src, Pk)
      4. If target.[[TypedArrayName]] is "BigUint64Array" or "BigInt64Array", let value be ? ToBigInt(value)
      5. Otherwise, let value be ? ToNumber(value).
      6. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
      7. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, kNumbervalue, true, "Unordered").
      8. Set k to k + 1.
      9. Set targetByteIndex to targetByteIndex + targetElementSize.
    22. Return undefined.

    7.19%TypedArray%.prototype.set( typedArray [ , offset ] )

    Sets multiple values in this TypedArray, reading the values from the typedArray argument object. The optional offset value indicates the first element index in this TypedArray where values are written. If omitted, it is assumed to be 0.

    1. Assert: typedArray has a [[TypedArrayName]] internal slot. If it does not, the definition in 7.18 applies.
    2. Let target be the this value.
    3. If Type(target) is not Object, throw a TypeError exception.
    4. If target does not have a [[TypedArrayName]] internal slot, throw a TypeError exception.
    5. Assert: target has a [[ViewedArrayBuffer]] internal slot.
    6. Let targetOffset be ? ToInteger(offset).
    7. If targetOffset < 0, throw a RangeError exception.
    8. Let targetBuffer be target.[[ViewedArrayBuffer]].
    9. If IsDetachedBuffer(targetBuffer) is true, throw a TypeError exception.
    10. Let targetLength be target.[[ArrayLength]].
    11. Let srcBuffer be typedArray.[[ViewedArrayBuffer]].
    12. If IsDetachedBuffer(srcBuffer) is true, throw a TypeError exception.
    13. Let targetName be the String value of target.[[TypedArrayName]].
    14. Let targetType be the String value of the Element Type value in Table 9 for targetName.
    15. Let targetElementSize be the Number value of the Element Size value specified in Table 9 for targetName.
    16. Let targetByteOffset be target.[[ByteOffset]].
    17. Let srcName be the String value of typedArray.[[TypedArrayName]].
    18. Let srcType be the String value of the Element Type value in Table 9 for srcName.
    19. Let srcElementSize be the Number value of the Element Size value specified in Table 9 for srcName.
    20. Let srcLength be typedArray.[[ArrayLength]].
    21. Let srcByteOffset be typedArray.[[ByteOffset]].
    22. If srcLength + targetOffset > targetLength, throw a RangeError exception.
    23. If one of srcType and targetType contains the substring "Big" and the other does not, throw a TypeError exception.
    24. If both IsSharedArrayBuffer(srcBuffer) and IsSharedArrayBuffer(targetBuffer) are true, then
      1. If srcBuffer.[[ArrayBufferData]] and targetBuffer.[[ArrayBufferData]] are the same Shared Data Block values, let same be true; else let same be false.
    25. Else, let same be SameValue(srcBuffer, targetBuffer).
    26. If same is true, then
      1. Let srcByteLength be typedArray.[[ByteLength]].
      2. Let srcBuffer be ? CloneArrayBuffer(srcBuffer, srcByteOffset, srcByteLength, %ArrayBuffer%).
      3. NOTE: %ArrayBuffer% is used to clone srcBuffer because is it known to not have any observable side-effects.
      4. Let srcByteIndex be 0.
    27. Else, let srcByteIndex be srcByteOffset.
    28. Let targetByteIndex be targetOffset × targetElementSize + targetByteOffset.
    29. Let limit be targetByteIndex + targetElementSize × srcLength.
    30. If SameValue(srcType, targetType) is true, then
      1. NOTE: If srcType and targetType are the same, the transfer must be performed in a manner that preserves the bit-level encoding of the source data.
      2. Repeat, while targetByteIndex < limit
        1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, "Uint8", true, "Unordered").
        2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, "Uint8", value, true, "Unordered").
        3. Set srcByteIndex to srcByteIndex + 1.
        4. Set targetByteIndex to targetByteIndex + 1.
    31. Else,
      1. Repeat, while targetByteIndex < limit
        1. Let value be GetValueFromBuffer(srcBuffer, srcByteIndex, srcType, true, "Unordered").
        2. Perform SetValueInBuffer(targetBuffer, targetByteIndex, targetType, value, true, "Unordered").
        3. Set srcByteIndex to srcByteIndex + srcElementSize.
        4. Set targetByteIndex to targetByteIndex + targetElementSize.
    32. Return undefined.

    7.20TypedArray ( typedArray )

    This description applies only if the TypedArray function is called with at least one argument and the Type of the first argument is Object and that object has a [[TypedArrayName]] internal slot.

    TypedArray called with argument typedArray performs the following steps:

    1. Assert: Type(typedArray) is Object and typedArray has a [[TypedArrayName]] internal slot.
    2. If NewTarget is undefined, throw a TypeError exception.
    3. Let constructorName be the String value of the Constructor Name value specified in Table 9 for this TypedArray constructor.
    4. Let O be ? AllocateTypedArray(constructorName, NewTarget, "%TypedArrayPrototype%").
    5. Let srcArray be typedArray.
    6. Let srcData be srcArray.[[ViewedArrayBuffer]].
    7. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
    8. Let elementType be the String value of the Element Type value in Table 9 for constructorName.
    9. Let elementLength be srcArray.[[ArrayLength]].
    10. Let srcName be the String value of srcArray.[[TypedArrayName]].
    11. Let srcType be the String value of the Element Type value in Table 9 for srcName.
    12. Let srcElementSize be the Element Size value in Table 9 for srcName.
    13. Let srcByteOffset be srcArray.[[ByteOffset]].
    14. Let elementSize be the Element Size value in Table 9 for constructorName.
    15. Let byteLength be elementSize × elementLength.
    16. If IsSharedArrayBuffer(srcData) is false, then
      1. Let bufferConstructor be ? SpeciesConstructor(srcData, %ArrayBuffer%).
    17. Else,
      1. Let bufferConstructor be %ArrayBuffer%.
    18. If SameValue(elementType, srcType) is true, then
      1. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
      2. Let data be ? CloneArrayBuffer(srcData, srcByteOffset, byteLength, bufferConstructor).
    19. Else,
      1. Let data be ? AllocateArrayBuffer(bufferConstructor, byteLength).
      2. If IsDetachedBuffer(srcData) is true, throw a TypeError exception.
      3. If one of srcType and targetType contains the substring "Big" and the other does not, throw a TypeError exception.
      4. Let srcByteIndex be srcByteOffset.
      5. Let targetByteIndex be 0.
      6. Let count be elementLength.
      7. Repeat, while count > 0
        1. Let value be GetValueFromBuffer(srcData, srcByteIndex, srcType, true, "Unordered").
        2. Perform SetValueInBuffer(data, targetByteIndex, elementType, value, true, "Unordered").
        3. Set srcByteIndex to srcByteIndex + srcElementSize.
        4. Set targetByteIndex to targetByteIndex + elementSize.
        5. Decrement count by 1.
    20. Set O.[[ViewedArrayBuffer]] to data.
    21. Set O.[[ByteLength]] to byteLength.
    22. Set O.[[ByteOffset]] to 0.
    23. Set O.[[ArrayLength]] to elementLength.
    24. Return O.

    7.21SetViewValue ( view, requestIndex, isLittleEndian, type, value )

    The abstract operation SetViewValue with arguments view, requestIndex, isLittleEndian, type, and value is used by functions on DataView instances to store values into the view's buffer. It performs the following steps:

    1. If Type(view) is not Object, throw a TypeError exception.
    2. If view does not have a [[DataView]] internal slot, throw a TypeError exception.
    3. Assert: view has a [[ViewedArrayBuffer]] internal slot.
    4. Let getIndex be ? ToIndex(requestIndex).
    5. If arrayTypeName is "BigUint64Array" or "BigInt64Array", let v be ? ToBigInt(value).
    6. Otherwise, let v be ? ToInteger(value).
    7. Set isLittleEndian to ToBoolean(isLittleEndian).
    8. Let buffer be view.[[ViewedArrayBuffer]].
    9. If IsDetachedBuffer(buffer) is true, throw a TypeError exception.
    10. Let viewOffset be view.[[ByteOffset]].
    11. Let viewSize be view.[[ByteLength]].
    12. Let elementSize be the Number value of the Element Size value specified in Table 9 for Element Type type.
    13. If getIndex + elementSize > viewSize, throw a RangeError exception.
    14. Let bufferIndex be getIndex + viewOffset.
    15. Return SetValueInBuffer(buffer, bufferIndex, type, v, false, "Unordered", isLittleEndian).

    7.22DataView.prototype.getBigInt64 ( byteOffset [ , littleEndian ] )

    When the getBigInt64 method is called with argument byteOffset and optional argument littleEndian, the following steps are taken:

    1. Let v be the this value.
    2. If littleEndian is not present, let littleEndian be undefined.
    3. Return ? GetViewValue(v, byteOffset, littleEndian, "Int64").

    7.23DataView.prototype.getBigUint64 ( byteOffset [ , littleEndian ] )

    When the getBigUint64 method is called with argument byteOffset and optional argument littleEndian, the following steps are taken:

    1. Let v be the this value.
    2. If littleEndian is not present, let littleEndian be undefined.
    3. Return ? GetViewValue(v, byteOffset, littleEndian, "Uint64").

    7.24DataView.prototype.setBigInt64 ( byteOffset, value [ , littleEndian ] )

    When the setBigInt64 method is called with arguments byteOffset and value, the following steps are taken:

    1. Let v be the this value.
    2. If littleEndian is not present, let littleEndian be undefined.
    3. Return ? SetViewValue(v, byteOffset, littleEndian, "Int64", value).

    7.25DataView.prototype.setBigUint64 ( byteOffset, value [ , littleEndian ] )

    When the setBigUint64 method is called with arguments byteOffset and value, the following steps are taken:

    1. Let v be the this value.
    2. If littleEndian is not present, let littleEndian be undefined.
    3. Return ? SetViewValue(v, byteOffset, littleEndian, "Uint64", value).