diff options
author | 2023-07-20 17:50:54 -0400 | |
---|---|---|
committer | 2023-07-20 14:50:54 -0700 | |
commit | c383c6cd810a80c6080096bbad737f9fa17f2e7b (patch) | |
tree | 21bb087d724facae5dba31666ead3abd03528a53 | |
parent | 68b4a64569039f39c7bb661570bf65b80028cf92 (diff) | |
download | bun-c383c6cd810a80c6080096bbad737f9fa17f2e7b.tar.gz bun-c383c6cd810a80c6080096bbad737f9fa17f2e7b.tar.zst bun-c383c6cd810a80c6080096bbad737f9fa17f2e7b.zip |
Pass constructor arguments to TextDecoder (#3692)
* Make TextDecoder constructor use options parameter
The constructor now actually sets TextDecoder properties using the
options parameter.
* Defer decoder allocation to end of constructor
* Verify types of TextDecoder options
* TextDecoder throw TypeError on failure
* Tidying
-rw-r--r-- | src/bun.js/webcore/encoding.zig | 65 | ||||
-rw-r--r-- | test/js/web/encoding/text-decoder.test.js | 19 |
2 files changed, 69 insertions, 15 deletions
diff --git a/src/bun.js/webcore/encoding.zig b/src/bun.js/webcore/encoding.zig index 42256a9ca..41a27ccd4 100644 --- a/src/bun.js/webcore/encoding.zig +++ b/src/bun.js/webcore/encoding.zig @@ -630,11 +630,13 @@ pub const TextDecoder = struct { } else |err| { switch (err) { error.InvalidByteSequence => { - globalThis.throw("Invalid byte sequence", .{}); + globalThis.throwValue( + globalThis.createTypeErrorInstance("Invalid byte sequence", .{}), + ); return JSValue.zero; }, error.OutOfMemory => { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return JSValue.zero; }, } @@ -647,7 +649,7 @@ pub const TextDecoder = struct { } else |err| { switch (err) { error.OutOfMemory => { - globalThis.throw("Out of memory", .{}); + globalThis.throwOutOfMemory(); return JSValue.zero; }, } @@ -676,26 +678,59 @@ pub const TextDecoder = struct { globalThis: *JSC.JSGlobalObject, callframe: *JSC.CallFrame, ) callconv(.C) ?*TextDecoder { - var args_ = callframe.arguments(1); + var args_ = callframe.arguments(2); var arguments: []const JSC.JSValue = args_.ptr[0..args_.len]; - var encoding = EncodingLabel.@"UTF-8"; + var decoder = TextDecoder{}; + if (arguments.len > 0) { - if (!arguments[0].isString()) { + // encoding + if (arguments[0].isString()) { + var str = arguments[0].toSlice(globalThis, default_allocator); + defer if (str.isAllocated()) str.deinit(); + + if (EncodingLabel.which(str.slice())) |label| { + decoder.encoding = label; + } else { + globalThis.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()}); + return null; + } + } else { globalThis.throwInvalidArguments("TextDecoder(encoding) label is invalid", .{}); return null; } - var str = arguments[0].toSlice(globalThis, default_allocator); - defer if (str.isAllocated()) str.deinit(); - encoding = EncodingLabel.which(str.slice()) orelse { - globalThis.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()}); - return null; - }; + if (arguments.len >= 2) { + const options = arguments[1]; + + if (!options.isObject()) { + globalThis.throwInvalidArguments("TextDecoder(options) is invalid", .{}); + return null; + } + + if (options.get(globalThis, "fatal")) |fatal| { + if (fatal.isBoolean()) { + decoder.fatal = fatal.asBoolean(); + } else { + globalThis.throwInvalidArguments("TextDecoder(options) fatal is invalid. Expected boolean value", .{}); + return null; + } + } + + if (options.get(globalThis, "ignoreBOM")) |ignoreBOM| { + if (ignoreBOM.isBoolean()) { + decoder.ignore_bom = ignoreBOM.asBoolean(); + } else { + globalThis.throwInvalidArguments("TextDecoder(options) ignoreBOM is invalid. Expected boolean value", .{}); + return null; + } + } + } } - var decoder = getAllocator(globalThis).create(TextDecoder) catch unreachable; - decoder.* = TextDecoder{ .encoding = encoding }; - return decoder; + + var result = getAllocator(globalThis).create(TextDecoder) catch unreachable; + result.* = decoder; + return result; } }; diff --git a/test/js/web/encoding/text-decoder.test.js b/test/js/web/encoding/text-decoder.test.js index abd4c2a72..d8038e628 100644 --- a/test/js/web/encoding/text-decoder.test.js +++ b/test/js/web/encoding/text-decoder.test.js @@ -225,6 +225,25 @@ describe("TextDecoder", () => { expect(decoder.decode(bytes.subarray(0, amount.written))).toBe(text); gcTrace(true); }); + + it("should respect fatal when encountering invalid data", () => { + const decoder = new TextDecoder("utf-8", { fatal: true }); + expect(() => { + decoder.decode(new Uint8Array([0xC0])) // Invalid UTF8 + }).toThrow(TypeError); + }); + + it("constructor should set values", () => { + const decoder = new TextDecoder("utf-8", { fatal: true, ignoreBOM: false }); + expect(decoder.fatal).toBe(true); + // expect(decoder.ignoreBOM).toBe(false); // currently the getter for ignoreBOM doesn't work and always returns undefined + }); + + it("should throw on invalid input", () => { + expect(() => { + const decoder = new TextDecoder("utf-8", { fatal: 10, ignoreBOM: {} }); + }).toThrow(); + }); }); it("truncated sequences", () => { |