Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass constructor arguments to TextDecoder #3692

Merged
merged 5 commits into from Jul 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 42 additions & 11 deletions src/bun.js/webcore/encoding.zig
Expand Up @@ -676,25 +676,56 @@ 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";
if (arguments.len > 0) {
if (!arguments[0].isString()) {
globalThis.throwInvalidArguments("TextDecoder(encoding) label is invalid", .{});
return null;
}
var decoder = getAllocator(globalThis).create(TextDecoder) catch unreachable;
Parzival-3141 marked this conversation as resolved.
Show resolved Hide resolved
decoder.* = TextDecoder{}; // Is this necessary? Does allocation initialize variables?

var ctor_failed = false; // using errdefer would be much nicer but would require another fn
defer if (ctor_failed) getAllocator(globalThis).destroy(decoder);

if (arguments.len == 0) {
return decoder;
}

if (!arguments[0].isString()) {
globalThis.throwInvalidArguments("TextDecoder(encoding) label is invalid", .{});
ctor_failed = true;
return null;
}

// encoding
{
var str = arguments[0].toSlice(globalThis, default_allocator);
defer if (str.isAllocated()) str.deinit();
encoding = EncodingLabel.which(str.slice()) orelse {
if (EncodingLabel.which(str.slice())) |label| {
decoder.encoding = label;
} else {
globalThis.throwInvalidArguments("Unsupported encoding label \"{s}\"", .{str.slice()});
ctor_failed = true;
return null;
};
}
}
var decoder = getAllocator(globalThis).create(TextDecoder) catch unreachable;
decoder.* = TextDecoder{ .encoding = encoding };

if (arguments.len >= 2) {
const options = arguments[1];

if (!options.isObject()) {
globalThis.throwInvalidArguments("TextDecoder(options) is invalid", .{});
ctor_failed = true;
return null;
}

if (options.get(globalThis, "fatal")) |fatal| {
decoder.fatal = fatal.asBoolean();
Parzival-3141 marked this conversation as resolved.
Show resolved Hide resolved
}

if (options.get(globalThis, "ignoreBOM")) |ignoreBOM| {
decoder.ignore_bom = ignoreBOM.asBoolean();
}
}

return decoder;
}
};
Expand Down
13 changes: 13 additions & 0 deletions test/js/web/encoding/text-decoder.test.js
Expand Up @@ -225,6 +225,19 @@ 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(); // should throw TypeError according to WHATWG Encoding Standard
});

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("truncated sequences", () => {
Expand Down