Deno: TextDecoder.decode() uncaught error when passed null

Created on 24 Aug 2020  路  3Comments  路  Source: denoland/deno

Noticed while upgrading a module to use [email protected] and Deno 1.3.1 (REF) that the TextDecoder decode() method is missing a null check resulting in the following error:

$ deno

Deno 1.3.1
exit using ctrl+d or close()
> let d = new TextDecoder()
undefined
> d.decode(null)
Uncaught TypeError: Cannot use 'in' operator to search for 'buffer' in null
    at TextDecoder.decode (/Users/runner/work/deno/deno/op_crates/web/08_text_encoding.js:445:18)
    at <unknown>:3:3
    at evaluate (rt/40_repl.js:60:36)
    at replLoop (rt/40_repl.js:160:15)

(response.arrayBuffer() from a fetch now returns null for a null response body in Deno 1.3.1, whereas it returned an empty string before in Deno 1.3.0, hence how saw this error)

Assuming we want to keep the fall-through behaviour currently implemented (anything invalid passed is overwritten with new Uint8Array(0)), but without the error thrown, just need to add a one-liner and test for passing null to the decode method.

REF: https://github.com/denoland/deno/blob/master/op_crates/web/08_text_encoding.js#L445

      let bytes;
      if (input instanceof Uint8Array) {
        bytes = input;
      } else if (isEitherArrayBuffer(input)) {
        bytes = new Uint8Array(input);
      } else if (
        typeof input === "object" &&
        input !== null && // <-- Add this here?
        "buffer" in input &&
        isEitherArrayBuffer(input.buffer)
      ) {
        bytes = new Uint8Array(
          input.buffer,
          input.byteOffset,
          input.byteLength,
        );
      } else {
        bytes = new Uint8Array(0);
      }

It's a small thing, and expect most folks will be protected via the type defs, but simple enough to trip into / reproduce with a Deno server of your choice, e.g. using an express like server:

import { opine } from "https://deno.land/x/[email protected]/mod.ts";

const app = opine();
app.get("/", (_req, res) => {
  res.setStatus(304).send();
});
const server = app.listen();
const address = server.listener.addr as Deno.NetAddr;
const url = `http://localhost:${address.port}`;

const decoder = new TextDecoder();
const res = await fetch(url);
// Response {
//   _bodySource: null,
//   _stream: null,
//   url: "http://localhost:51780",
//   statusText: "Not Modified",
//   status: 304,
//   headers: Headers { content-length: 0, x-powered-by: Opine },
//   redirected: false,
//   type: "default"
// }
const buf = await res.arrayBuffer(); // returns null
const decoded = decoder.decode(buf); // BOOM
// error: Uncaught TypeError: Cannot use 'in' operator to search for 'buffer' in null
//     at TextDecoder.decode (/Users/runner/work/deno/deno/op_crates/web/08_text_encoding.js:445:18)
//     at asd.ts:18:19
console.log(decoded);
server.close();

Happy to put in a small PR 馃檭

bug web

All 3 comments

We should follow what browsers do here, which is throw a TypeError if the input is not a ArrayBuffer or ArrayBufferView. The error message could definitely be improved though. We should not default to Uint8Array(0) ever - that entire fallback should be removed, and replaced by a error. You can see this bad defaulting when passing a string.

Chrome:

const decoder = new TextDecoder()
decoder.decode(null)

Uncaught TypeError: Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'

decoder.decode("")

Uncaught TypeError: Failed to execute 'decode' on 'TextDecoder': The provided value is not of type '(ArrayBuffer or ArrayBufferView)'

Firefox:

const decoder = new TextDecoder()
decoder.decode(null)

Uncaught TypeError: TextDecoder.decode: Argument 1 could not be converted to any of: ArrayBufferView, ArrayBuffer.

decoder.decode("")

Uncaught TypeError: TextDecoder.decode: Argument 1 could not be converted to any of: ArrayBufferView, ArrayBuffer.

Deno:
```
const decoder = new TextDecoder()
decoder.decode(null)

Uncaught TypeError: Cannot use 'in' operator to search for 'buffer' in null

decoder.decode("")

@lucacasonato fair! It struck me as odd that didn't throw as per spec (REF: https://encoding.spec.whatwg.org/#dom-textdecoder-decode) / current browser implementations.

~Will close my PR as it follows the incorrect precedent opposed to spec.~

Update: will re-open and update actually, thinking on it... throwing a TypeError in the else statement won't be much work! (assuming that is all that is needed).

In addition to above and https://github.com/denoland/deno/issues/7181 should also consider the undefined case - in both Chrome and Firefox this is supported with a return value of an empty string "".

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ry picture ry  路  3Comments

kyeotic picture kyeotic  路  3Comments

metakeule picture metakeule  路  3Comments

ry picture ry  路  3Comments

somombo picture somombo  路  3Comments