diff --git a/src/browser/js/Value.zig b/src/browser/js/Value.zig index bf1696e7..b7e8b4ca 100644 --- a/src/browser/js/Value.zig +++ b/src/browser/js/Value.zig @@ -163,6 +163,53 @@ pub fn isFloat64Array(self: Value) bool { return v8.v8__Value__IsFloat64Array(self.handle); } +// A few places in the code take various types, but want a string. This is a +// type-aware version of toString(). If you do: +// (new ArrayBuffer(100)).toString() +// You'll get "[object ArrayBuffer]". But this `toStringSmart()` knows about +// buffers, and Blobs, etc and will try to return the real underlying string +// value. It _does_ ultimately fallback to toString() - callers should check +// for types they _don't_ want before calling this. For example, `Response` +// checks for null or undefined before calling this to apply specific handling +// to those cases. +pub fn toStringSmart(self: Value) ![]const u8 { + if (self.isString()) |js_str| { + return try js_str.toSlice(); + } + + const Blob = @import("../webapi/Blob.zig"); + if (self.local.jsValueToZig(*Blob, self)) |blob_obj| { + return blob_obj._slice; + } else |_| {} + + var byte_offset: usize = 0; + var byte_len: usize = undefined; + var array_buffer: ?*const v8.ArrayBuffer = null; + + if (self.isTypedArray() or self.isArrayBufferView()) { + const buffer_handle: *const v8.ArrayBufferView = @ptrCast(self.handle); + byte_len = v8.v8__ArrayBufferView__ByteLength(buffer_handle); + byte_offset = v8.v8__ArrayBufferView__ByteOffset(buffer_handle); + array_buffer = v8.v8__ArrayBufferView__Buffer(buffer_handle); + } else if (self.isArrayBuffer()) { + array_buffer = @ptrCast(self.handle); + byte_len = v8.v8__ArrayBuffer__ByteLength(array_buffer); + } else { + return self.toStringSlice(); + } + + const backing_store_ptr = v8.v8__ArrayBuffer__GetBackingStore(array_buffer orelse return ""); + if (byte_len == 0) { + return &[_]u8{}; + } + + const backing_store_handle = v8.std__shared_ptr__v8__BackingStore__get(&backing_store_ptr) orelse return ""; + const data = v8.v8__BackingStore__Data(backing_store_handle) orelse return ""; + const base = @as([*]const u8, @ptrCast(data)) + byte_offset; + + return base[0..byte_len]; +} + pub fn isPromise(self: Value) bool { return v8.v8__Value__IsPromise(self.handle); } diff --git a/src/browser/tests/blob.html b/src/browser/tests/blob.html index 0cbf8ea5..5f59ade7 100644 --- a/src/browser/tests/blob.html +++ b/src/browser/tests/blob.html @@ -79,6 +79,100 @@ } + +