diff options
-rw-r--r-- | src/javascript/jsc/JavascriptCore.zig | 2 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 40 | ||||
-rw-r--r-- | src/javascript/jsc/javascript.zig | 72 |
3 files changed, 113 insertions, 1 deletions
diff --git a/src/javascript/jsc/JavascriptCore.zig b/src/javascript/jsc/JavascriptCore.zig index 612009a1f..f443799c1 100644 --- a/src/javascript/jsc/JavascriptCore.zig +++ b/src/javascript/jsc/JavascriptCore.zig @@ -18,7 +18,7 @@ pub const struct_OpaqueJSPropertyNameArray = generic; pub const JSPropertyNameArrayRef = ?*struct_OpaqueJSPropertyNameArray; pub const struct_OpaqueJSPropertyNameAccumulator = generic; pub const JSPropertyNameAccumulatorRef = ?*struct_OpaqueJSPropertyNameAccumulator; -pub const JSTypedArrayBytesDeallocator = ?fn (?*c_void, ?*c_void) callconv(.C) void; +pub const JSTypedArrayBytesDeallocator = ?fn (*c_void, *c_void) callconv(.C) void; pub const OpaqueJSValue = generic; pub const JSValueRef = ?*OpaqueJSValue; pub const JSObjectRef = ?*OpaqueJSValue; diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 4acd688c1..65300f07c 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -1494,8 +1494,48 @@ pub const ArrayBuffer = struct { byte_len: u32, typed_array_type: js.JSTypedArrayType, + + pub inline fn slice(this: *const ArrayBuffer) []u8 { + return this.ptr[this.offset .. this.offset + this.byte_len]; + } }; +pub const MarkedArrayBuffer = struct { + buffer: ArrayBuffer, + allocator: *std.mem.Allocator, + + pub fn fromBytes(bytes: []u8, allocator: *std.mem.Allocator, typed_array_type: js.JSTypedArrayType) MarkedArrayBuffer { + return MarkedArrayBuffer{ + .buffer = ArrayBuffer{ .offset = 0, .len = @intCast(u32, bytes.len), .byte_len = @intCast(u32, bytes.len), .typed_array_type = typed_array_type, .ptr = bytes.ptr }, + .allocator = allocator, + }; + } + + pub fn destroy(this: *MarkedArrayBuffer) void { + const content = this.*; + content.allocator.free(content.buffer.slice()); + content.allocator.destroy(this); + } + + pub fn init(allocator: *std.mem.Allocator, size: u32, typed_array_type: js.JSTypedArrayType) !*MarkedArrayBuffer { + const bytes = try allocator.alloc(u8, size); + var container = try allocator.create(MarkedArrayBuffer); + container.* = MarkedArrayBuffer.fromBytes(bytes, allocator, typed_array_type); + return container; + } + + pub fn toJSObjectRef(this: *MarkedArrayBuffer, ctx: js.JSContextRef, exception: js.ExceptionRef) js.JSObjectRef { + return js.JSObjectMakeTypedArrayWithBytesNoCopy(ctx, this.buffer.typed_array_type, this.buffer.ptr, this.buffer.byte_len, MarkedArrayBuffer_deallocator, this, exception); + } +}; + +export fn MarkedArrayBuffer_deallocator(bytes_: *c_void, ctx_: *c_void) void { + var ctx = @ptrCast(*MarkedArrayBuffer, @alignCast(@alignOf(*MarkedArrayBuffer), ctx_)); + + if (comptime isDebug) std.debug.assert(ctx.buffer.ptr == @ptrCast([*]u8, bytes_)); + ctx.destroy(); +} + pub fn castObj(obj: js.JSObjectRef, comptime Type: type) *Type { return JSPrivateDataPtr.from(js.JSObjectGetPrivate(obj)).as(Type); } diff --git a/src/javascript/jsc/javascript.zig b/src/javascript/jsc/javascript.zig index e4ccd62e5..50585bf91 100644 --- a/src/javascript/jsc/javascript.zig +++ b/src/javascript/jsc/javascript.zig @@ -181,6 +181,71 @@ pub const Bun = struct { return ref; } + pub fn readFileAsBytes( + this: void, + ctx: js.JSContextRef, + function: js.JSObjectRef, + thisObject: js.JSObjectRef, + arguments: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSValueRef { + if (arguments.len != 1 or !JSValue.isString(JSValue.fromRef(arguments[0]))) { + JSError(getAllocator(ctx), "readFileBytes expects a file path as a string. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + var out = ZigString.Empty; + JSValue.toZigString(JSValue.fromRef(arguments[0]), &out, VirtualMachine.vm.global); + var out_slice = out.slice(); + + // The dots are kind of unnecessary. They'll be normalized. + if (out.len == 0 or @ptrToInt(out.ptr) == 0 or std.mem.eql(u8, out_slice, ".") or std.mem.eql(u8, out_slice, "..") or std.mem.eql(u8, out_slice, "../")) { + JSError(getAllocator(ctx), "readFileBytes expects a valid file path. e.g. Bun.readFile(\"public/index.html\")", .{}, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + var buf: [std.fs.MAX_PATH_BYTES]u8 = undefined; + + var parts = [_]string{out_slice}; + // This does the equivalent of Node's path.normalize(path.join(cwd, out_slice)) + var path = VirtualMachine.vm.bundler.fs.absBuf(&parts, &buf); + buf[path.len] = 0; + + const buf_z: [:0]const u8 = buf[0..path.len :0]; + var file = std.fs.cwd().openFileZ(buf_z, .{ .read = true, .write = false }) catch |err| { + JSError(getAllocator(ctx), "Opening file {s} for path: \"{s}\"", .{ @errorName(err), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + }; + + defer file.close(); + + const stat = file.stat() catch |err| { + JSError(getAllocator(ctx), "Getting file size {s} for \"{s}\"", .{ @errorName(err), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + }; + + if (stat.kind != .File) { + JSError(getAllocator(ctx), "Can't read a {s} as a string (\"{s}\")", .{ @tagName(stat.kind), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + } + + var contents_buf = VirtualMachine.vm.allocator.alloc(u8, stat.size + 2) catch unreachable; // OOM + errdefer VirtualMachine.vm.allocator.free(contents_buf); + const contents_len = file.readAll(contents_buf) catch |err| { + JSError(getAllocator(ctx), "{s} reading file (\"{s}\")", .{ @errorName(err), path }, ctx, exception); + return js.JSValueMakeUndefined(ctx); + }; + + contents_buf[contents_len] = 0; + + var marked_array_buffer = VirtualMachine.vm.allocator.create(MarkedArrayBuffer) catch unreachable; + marked_array_buffer.* = MarkedArrayBuffer.fromBytes( + contents_buf[0..contents_len], + VirtualMachine.vm.allocator, + js.JSTypedArrayType.kJSTypedArrayTypeUint8Array, + ); + + return marked_array_buffer.toJSObjectRef(ctx, exception); + } pub fn readFileAsString( this: void, @@ -324,6 +389,13 @@ pub const Bun = struct { .@"return" = "string", }, }, + .readFileBytes = .{ + .rfn = Bun.readFileAsBytes, + .ts = d.ts{ + .name = "readFile", + .@"return" = "Uint8Array", + }, + }, .getPublicPath = .{ .rfn = Bun.getPublicPathJS, .ts = d.ts{ |