Skip to content

"TypeError: out-of-bound numeric index" on typed arrays in strict mode #645

@TooTallNate

Description

@TooTallNate

QuickJS has an unexpected behavior (at least when compared with other JS engines), where it throws when a typed array is attempting to be set at a index that is out of bounds:

'use strict';
const a = new Uint8Array(1);
console.log(a[0] = 1);
console.log(a[1] = 2);
console.log(a);
$ qjs o.mjs 
1
TypeError: out-of-bound numeric index
    at <anonymous> (o.mjs:4:20)

Compared to other runtimes:

$ node o.mjs 
1
2
Uint8Array(1) [ 1 ]
$ bun o.mjs 
1
2
Uint8Array(1) [ 1 ]
$ deno run o.mjs 
1
2
Uint8Array(1) [ 1 ]

I believe the relevant part of the spec is here: TypedArraySetElement

It's not super clear to be if this is technically incorrect behavior or not (that is, I see that the function may result in a throw completion, but I don't see where that would happen based on the steps described).

I also found this 10 year old mailing list thread which seems relevant (but never seemed to reach a conclusion): https://esdiscuss.org/topic/should-assigning-to-out-of-bounds-elements-of-a-typed-array-throw-in-strict-mode-code

In practice, this is making the __toBinary() function defined by esbuild throw this out of bounds error in certain cases,
For reference:

var __toBinary = /* @__PURE__ */ (() => {
  var table = new Uint8Array(128);
  for (var i = 0; i < 64; i++)
    table[i < 26 ? i + 65 : i < 52 ? i + 71 : i < 62 ? i - 4 : i * 4 - 205] = i;
  return (base64) => {
    var n = base64.length, bytes = new Uint8Array((n - (base64[n - 1] == "=") - (base64[n - 2] == "=")) * 3 / 4 | 0);
    for (var i2 = 0, j = 0; i2 < n; ) {
      var c0 = table[base64.charCodeAt(i2++)], c1 = table[base64.charCodeAt(i2++)];
      var c2 = table[base64.charCodeAt(i2++)], c3 = table[base64.charCodeAt(i2++)];
      bytes[j++] = c0 << 2 | c1 >> 4;
      bytes[j++] = c1 << 4 | c2 >> 2;
      bytes[j++] = c2 << 6 | c3;
    }
    return bytes;
  };
})();

My vote would be to remove this behavior from QuickJS and make the behavior match other JS engines.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions