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

More spec compliant Blob.prototype.type #2340

Merged
merged 12 commits into from
Mar 8, 2023
20 changes: 19 additions & 1 deletion packages/bun-types/bun.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -620,8 +620,26 @@ declare module "bun" {
*
* @param begin - start offset in bytes
* @param end - absolute offset in bytes (relative to 0)
* @param contentType - MIME type for the new FileBlob
*/
slice(begin?: number, end?: number): FileBlob;
slice(begin?: number, end?: number, contentType?: string): FileBlob;

/**
* Offset any operation on the file starting at `begin`
*
* Similar to [`TypedArray.subarray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypedArray/subarray). Does not copy the file, open the file, or modify the file.
*
* If `begin` > 0, {@link Bun.write()} will be slower on macOS
*
* @param begin - start offset in bytes
* @param contentType - MIME type for the new FileBlob
*/
slice(begin?: number, contentType?: string): FileBlob;

/**
* @param contentType - MIME type for the new FileBlob
*/
slice(contentType?: string): FileBlob;

/**
* Incremental writer for files and pipes.
Expand Down
24 changes: 23 additions & 1 deletion packages/bun-types/globals.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -565,7 +565,29 @@ declare class Blob implements BlobInterface {
* @param end The index that sets the end of the view.
*
*/
slice(begin?: number, end?: number): Blob;
slice(begin?: number, end?: number, contentType?: string): Blob;

/**
* Create a new view **without 🚫 copying** the underlying data.
*
* Similar to [`BufferSource.subarray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BufferSource/subarray)
*
* @param begin The index that sets the beginning of the view.
* @param end The index that sets the end of the view.
*
*/
slice(begin?: number, contentType?: string): Blob;

/**
* Create a new view **without 🚫 copying** the underlying data.
*
* Similar to [`BufferSource.subarray`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BufferSource/subarray)
*
* @param begin The index that sets the beginning of the view.
* @param end The index that sets the end of the view.
*
*/
slice(contentType?: string): Blob;

/**
* Read the data from the blob as a string. It will be decoded from UTF-8.
Expand Down
4 changes: 1 addition & 3 deletions packages/bun-types/tests/array.test-d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ async function* listReleases() {
}
}

const releases = await Array.fromAsync(listReleases());

export {};
export const releases = await Array.fromAsync(listReleases());
4 changes: 3 additions & 1 deletion src/bun.js/api/server.zig
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,9 @@ pub const ServerConfig = struct {
}

if (arg.getTruthy(global, "maxRequestBodySize")) |max_request_body_size| {
args.max_request_body_size = @intCast(u64, @max(0, max_request_body_size.toInt64()));
if (max_request_body_size.isNumber()) {
args.max_request_body_size = @intCast(u64, @max(0, max_request_body_size.toInt64()));
}
}

if (arg.getTruthy(global, "error")) |onError| {
Expand Down
2 changes: 2 additions & 0 deletions src/bun.js/base.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1773,6 +1773,8 @@ pub const ArrayBuffer = extern struct {
}

pub fn createEmpty(globalThis: *JSC.JSGlobalObject, comptime kind: JSC.JSValue.JSType) JSValue {
JSC.markBinding(@src());

return switch (comptime kind) {
.Uint8Array => Bun__createUint8ArrayForCopy(globalThis, null, 0, false),
.ArrayBuffer => Bun__createArrayBufferForCopy(globalThis, null, 0),
Expand Down
4 changes: 0 additions & 4 deletions src/bun.js/bindings/bindings.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2455,10 +2455,6 @@ bool JSC__JSValue__isPrimitive(JSC__JSValue JSValue0)
{
return JSC::JSValue::decode(JSValue0).isPrimitive();
}
bool JSC__JSValue__isString(JSC__JSValue JSValue0)
{
return JSC::JSValue::decode(JSValue0).isString();
}
bool JSC__JSValue__isSymbol(JSC__JSValue JSValue0)
{
return JSC::JSValue::decode(JSValue0).isSymbol();
Expand Down
15 changes: 12 additions & 3 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ pub const ZigString = extern struct {
}

pub fn eql(this: ZigString, other: ZigString) bool {
if (this.len == 0 or other.len == 0)
return this.len == other.len;

const left_utf16 = this.is16Bit();
const right_utf16 = other.is16Bit();

Expand Down Expand Up @@ -2276,6 +2279,10 @@ pub const JSGlobalObject = extern struct {
return this.bunVM().allocator;
}

pub fn throwOutOfMemory(this: *JSGlobalObject) void {
this.throwValue(this.createErrorInstance("Out of memory", .{}));
}

pub fn throwInvalidArguments(
this: *JSGlobalObject,
comptime fmt: string,
Expand Down Expand Up @@ -3454,8 +3461,11 @@ pub const JSValue = enum(JSValueReprInt) {
return res;
}

pub fn isString(this: JSValue) bool {
return cppFn("isString", .{this});
pub inline fn isString(this: JSValue) bool {
if (!this.isCell())
return false;

return jsType(this).isStringLike();
}
pub fn isBigInt(this: JSValue) bool {
return cppFn("isBigInt", .{this});
Expand Down Expand Up @@ -3968,7 +3978,6 @@ pub const JSValue = enum(JSValueReprInt) {
"isObject",
"isPrimitive",
"isSameValue",
"isString",
"isSymbol",
"isTerminationException",
"isUInt32AsAnyInt",
Expand Down
2 changes: 1 addition & 1 deletion src/bun.js/bindings/headers-cpp.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
//-- AUTOGENERATED FILE -- 1677776166
//-- AUTOGENERATED FILE -- 1678254453
// clang-format off
#pragma once

Expand Down
3 changes: 1 addition & 2 deletions src/bun.js/bindings/headers.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// clang-format off
//-- AUTOGENERATED FILE -- 1677776166
//-- AUTOGENERATED FILE -- 1678254453
#pragma once

#include <stddef.h>
Expand Down Expand Up @@ -331,7 +331,6 @@ CPP_DECL bool JSC__JSValue__isNumber(JSC__JSValue JSValue0);
CPP_DECL bool JSC__JSValue__isObject(JSC__JSValue JSValue0);
CPP_DECL bool JSC__JSValue__isPrimitive(JSC__JSValue JSValue0);
CPP_DECL bool JSC__JSValue__isSameValue(JSC__JSValue JSValue0, JSC__JSValue JSValue1, JSC__JSGlobalObject* arg2);
CPP_DECL bool JSC__JSValue__isString(JSC__JSValue JSValue0);
CPP_DECL bool JSC__JSValue__isSymbol(JSC__JSValue JSValue0);
CPP_DECL bool JSC__JSValue__isTerminationException(JSC__JSValue JSValue0, JSC__VM* arg1);
CPP_DECL bool JSC__JSValue__isUInt32AsAnyInt(JSC__JSValue JSValue0);
Expand Down
1 change: 0 additions & 1 deletion src/bun.js/bindings/headers.zig
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,6 @@ pub extern fn JSC__JSValue__isNumber(JSValue0: JSC__JSValue) bool;
pub extern fn JSC__JSValue__isObject(JSValue0: JSC__JSValue) bool;
pub extern fn JSC__JSValue__isPrimitive(JSValue0: JSC__JSValue) bool;
pub extern fn JSC__JSValue__isSameValue(JSValue0: JSC__JSValue, JSValue1: JSC__JSValue, arg2: *bindings.JSGlobalObject) bool;
pub extern fn JSC__JSValue__isString(JSValue0: JSC__JSValue) bool;
pub extern fn JSC__JSValue__isSymbol(JSValue0: JSC__JSValue) bool;
pub extern fn JSC__JSValue__isTerminationException(JSValue0: JSC__JSValue, arg1: *bindings.VM) bool;
pub extern fn JSC__JSValue__isUInt32AsAnyInt(JSValue0: JSC__JSValue) bool;
Expand Down
4 changes: 4 additions & 0 deletions src/bun.js/javascript.zig
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,10 @@ pub const VirtualMachine = struct {
return VMHolder.vm.?;
}

pub fn mimeType(this: *VirtualMachine, str: []const u8) ?bun.HTTP.MimeType {
return this.rareData().mimeTypeFromString(this.allocator, str);
}

pub const GCLevel = enum(u3) {
none = 0,
mild = 1,
Expand Down
12 changes: 12 additions & 0 deletions src/bun.js/rare_data.zig
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,18 @@ file_polls_: ?*JSC.FilePoll.HiveArray = null,

global_dns_data: ?*JSC.DNS.GlobalData = null,

mime_types: ?bun.HTTP.MimeType.Map = null,

pub fn mimeTypeFromString(this: *RareData, allocator: std.mem.Allocator, str: []const u8) ?bun.HTTP.MimeType {
if (this.mime_types == null) {
this.mime_types = bun.HTTP.MimeType.createHashTable(
allocator,
) catch @panic("Out of memory");
}

return this.mime_types.?.get(str);
}

pub fn filePolls(this: *RareData, vm: *JSC.VirtualMachine) *JSC.FilePoll.HiveArray {
return this.file_polls_ orelse {
this.file_polls_ = vm.allocator.create(JSC.FilePoll.HiveArray) catch unreachable;
Expand Down
138 changes: 98 additions & 40 deletions src/bun.js/webcore/blob.zig
Original file line number Diff line number Diff line change
Expand Up @@ -2391,7 +2391,7 @@ pub const Blob = struct {
callframe: *JSC.CallFrame,
) callconv(.C) JSC.JSValue {
var allocator = globalThis.allocator();
var arguments_ = callframe.arguments(2);
var arguments_ = callframe.arguments(3);
var args = arguments_.ptr[0..arguments_.len];

if (this.size == 0) {
Expand All @@ -2410,51 +2410,84 @@ pub const Blob = struct {
// If the optional end parameter is not used as a parameter when making this call, let relativeEnd be size.
var relativeEnd: i64 = @intCast(i64, this.size);

if (args.ptr[0].isString()) {
Jarred-Sumner marked this conversation as resolved.
Show resolved Hide resolved
args.ptr[2] = args.ptr[0];
args.ptr[1] = .zero;
args.ptr[0] = .zero;
args.len = 3;
} else if (args.ptr[1].isString()) {
args.ptr[2] = args.ptr[1];
args.ptr[1] = .zero;
args.len = 3;
}

var args_iter = JSC.Node.ArgumentsSlice.init(globalThis.bunVM(), args);
if (args_iter.nextEat()) |start_| {
const start = start_.toInt64();
if (start < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeStart = @intCast(i64, @max(start + @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeStart = @min(@intCast(i64, start), @intCast(i64, this.size));
if (start_.isNumber()) {
const start = start_.toInt64();
if (start < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeStart = @intCast(i64, @max(start +% @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeStart = @min(@intCast(i64, start), @intCast(i64, this.size));
}
}
}

if (args_iter.nextEat()) |end_| {
const end = end_.toInt64();
// If end is negative, let relativeEnd be max((size + end), 0).
if (end < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeEnd = @intCast(i64, @max(end + @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeEnd = @min(@intCast(i64, end), @intCast(i64, this.size));
if (end_.isNumber()) {
const end = end_.toInt64();
// If end is negative, let relativeEnd be max((size + end), 0).
if (end < 0) {
// If the optional start parameter is negative, let relativeStart be start + size.
relativeEnd = @intCast(i64, @max(end +% @intCast(i64, this.size), 0));
} else {
// Otherwise, let relativeStart be start.
relativeEnd = @min(@intCast(i64, end), @intCast(i64, this.size));
}
}
}

var content_type: string = "";
var content_type_was_allocated = false;
if (args_iter.nextEat()) |content_type_| {
if (content_type_.isString()) {
var zig_str = content_type_.getZigString(globalThis);
var slicer = zig_str.toSlice(bun.default_allocator);
defer slicer.deinit();
var slice = slicer.slice();
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
content_type = strings.copyLowercase(slice, content_type_buf);
inner: {
if (content_type_.isString()) {
var zig_str = content_type_.getZigString(globalThis);
var slicer = zig_str.toSlice(bun.default_allocator);
defer slicer.deinit();
var slice = slicer.slice();
if (!strings.isAllASCII(slice)) {
break :inner;
}

if (globalThis.bunVM().mimeType(slice)) |mime| {
content_type = mime.value;
break :inner;
}

content_type_was_allocated = slice.len > 0;
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
content_type = strings.copyLowercase(slice, content_type_buf);
}
}
}

const len = @intCast(SizeType, @max(relativeEnd - relativeStart, 0));
const len = @intCast(SizeType, @max(relativeEnd -| relativeStart, 0));

// This copies over the is_all_ascii flag
// which is okay because this will only be a <= slice
var blob = this.dupe();
blob.offset = @intCast(SizeType, relativeStart);
blob.size = len;

// infer the content type if it was not specified
if (content_type.len == 0 and this.content_type.len > 0 and !this.content_type_allocated)
content_type = this.content_type;

blob.content_type = content_type;
blob.content_type_allocated = content_type.len > 0;
blob.content_type_allocated = content_type_was_allocated;

var blob_ = allocator.create(Blob) catch unreachable;
blob_.* = blob;
Expand All @@ -2474,19 +2507,35 @@ pub const Blob = struct {
globalThis: *JSC.JSGlobalObject,
value: JSC.JSValue,
) callconv(.C) bool {
var zig_str = value.getZigString(globalThis);
if (zig_str.is16Bit())
return false;
var zig_str = if (value.isString())
value.getZigString(globalThis)
else
ZigString.Empty;

if (!zig_str.isAllASCII()) {
zig_str = ZigString.Empty;
}

var slice = zig_str.trimmedSlice();
if (strings.eql(slice, this.content_type))
if (zig_str.eql(ZigString.init(this.content_type))) {
return true;
}

const prev_content_type = this.content_type;
{
defer if (this.content_type_allocated) bun.default_allocator.free(prev_content_type);
var content_type_buf = globalThis.allocator().alloc(u8, slice.len) catch unreachable;
this.content_type = strings.copyLowercase(slice, content_type_buf);
var slicer = zig_str.toSlice(bun.default_allocator);
defer slicer.deinit();
const allocated = this.content_type_allocated;
defer if (allocated) bun.default_allocator.free(prev_content_type);
if (globalThis.bunVM().mimeType(slicer.slice())) |mime| {
this.content_type = mime.value;
this.content_type_allocated = false;
return true;
}
var content_type_buf = globalThis.allocator().alloc(u8, slicer.len) catch {
globalThis.throwOutOfMemory();
return false;
};
this.content_type = strings.copyLowercase(slicer.slice(), content_type_buf);
}

this.content_type_allocated = true;
Expand Down Expand Up @@ -2602,13 +2651,22 @@ pub const Blob = struct {
// Normative conditions for this member are provided
// in the § 3.1 Constructors.
if (options.get(globalThis, "type")) |content_type| {
if (content_type.isString()) {
var content_type_str = content_type.toSlice(globalThis, bun.default_allocator);
defer content_type_str.deinit();
var slice = content_type_str.slice();
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
blob.content_type = strings.copyLowercase(slice, content_type_buf);
blob.content_type_allocated = true;
inner: {
if (content_type.isString()) {
var content_type_str = content_type.toSlice(globalThis, bun.default_allocator);
defer content_type_str.deinit();
var slice = content_type_str.slice();
if (!strings.isAllASCII(slice)) {
break :inner;
}
if (globalThis.bunVM().mimeType(slice)) |mime| {
blob.content_type = mime.value;
break :inner;
}
var content_type_buf = allocator.alloc(u8, slice.len) catch unreachable;
blob.content_type = strings.copyLowercase(slice, content_type_buf);
blob.content_type_allocated = true;
}
}
}
}
Expand Down
Loading