diff options
author | 2022-04-12 22:59:25 -0700 | |
---|---|---|
committer | 2022-04-12 22:59:52 -0700 | |
commit | f6d73cb06ec5fe217a4d2d03808f68ecbc2d3461 (patch) | |
tree | 38ad0f487fe9a46fb4a1179967b8aee9c0510b06 /src/javascript | |
parent | b3522b2febaae5caf218a264fc5277974be8d57b (diff) | |
download | bun-f6d73cb06ec5fe217a4d2d03808f68ecbc2d3461.tar.gz bun-f6d73cb06ec5fe217a4d2d03808f68ecbc2d3461.tar.zst bun-f6d73cb06ec5fe217a4d2d03808f68ecbc2d3461.zip |
[bun.js] Implement Bun.sha1, Bun.sha256, Bun.sha384, Bun.sha512, Bun.sha512_384
Diffstat (limited to 'src/javascript')
-rw-r--r-- | src/javascript/jsc/api/bun.zig | 214 | ||||
-rw-r--r-- | src/javascript/jsc/base.zig | 136 |
2 files changed, 321 insertions, 29 deletions
diff --git a/src/javascript/jsc/api/bun.zig b/src/javascript/jsc/api/bun.zig index f0d483e1f..011314644 100644 --- a/src/javascript/jsc/api/bun.zig +++ b/src/javascript/jsc/api/bun.zig @@ -1118,6 +1118,21 @@ pub const Class = NewClass( .rfn = JSC.WebCore.Blob.writeFile, .ts = d.ts{}, }, + .sha1 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA1, "hash", false, false), + }, + .sha256 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA256, "hash", false, false), + }, + .sha384 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA384, "hash", false, false), + }, + .sha512 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA512, "hash", false, false), + }, + .sha512_256 = .{ + .rfn = JSC.wrapWithHasContainer(Crypto.SHA512_256, "hash", false, false), + }, }, .{ .main = .{ @@ -1174,9 +1189,208 @@ pub const Class = NewClass( .unsafe = .{ .get = getUnsafe, }, + .SHA1 = .{ + .get = Crypto.SHA1.getter, + }, + .SHA256 = .{ + .get = Crypto.SHA256.getter, + }, + .SHA384 = .{ + .get = Crypto.SHA384.getter, + }, + .SHA512 = .{ + .get = Crypto.SHA512.getter, + }, + .SHA512_256 = .{ + .get = Crypto.SHA512_256.getter, + }, }, ); +pub const Crypto = struct { + const Hashers = @import("../../../sha.zig"); + + fn CryptoHasher(comptime Hasher: type, comptime name: [:0]const u8, cached_constructor_name: []const u8) type { + return struct { + hashing: Hasher = Hasher{}, + + pub fn byteLength( + _: void, + _: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); + } + + pub fn byteLength2( + _: *@This(), + _: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + return JSC.JSValue.jsNumber(@as(u16, Hasher.digest)).asObjectRef(); + } + + pub const Constructor = JSC.NewConstructor( + @This(), + .{ + .hash = .{ + .rfn = JSC.wrapSync(@This(), "hash"), + }, + .constructor = .{ .rfn = constructor }, + }, + .{ + .byteLength = .{ + .get = byteLength, + }, + }, + ); + + pub const Class = JSC.NewClass( + @This(), + .{ + .name = name, + }, + .{ + .update = .{ + .rfn = JSC.wrapSync(@This(), "update"), + }, + .final = .{ + .rfn = JSC.wrapSync(@This(), "final"), + }, + .finalize = finalize, + }, + .{ + .byteLength = .{ + .get = byteLength2, + }, + }, + ); + + pub fn hash( + globalThis: *JSGlobalObject, + input: JSC.Node.StringOrBuffer, + output: ?JSC.ArrayBuffer, + exception: JSC.C.ExceptionRef, + ) JSC.JSValue { + var output_digest_buf: Hasher.Digest = undefined; + var output_digest_slice: *Hasher.Digest = &output_digest_buf; + if (output) |output_buf| { + var bytes = output_buf.byteSlice(); + if (bytes.len < Hasher.digest) { + JSC.JSError( + bun.default_allocator, + comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{Hasher.digest}), + .{}, + globalThis.ref(), + exception, + ); + return JSC.JSValue.zero; + } + output_digest_slice = bytes[0..Hasher.digest]; + } + + Hasher.hash(input.slice(), output_digest_slice); + + if (output) |output_buf| { + return output_buf.value; + } else { + var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, output_digest_slice) catch unreachable, .Uint8Array); + return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); + } + } + + pub fn constructor( + ctx: js.JSContextRef, + _: js.JSObjectRef, + _: []const js.JSValueRef, + exception: js.ExceptionRef, + ) js.JSObjectRef { + var this = bun.default_allocator.create(@This()) catch { + JSC.JSError(bun.default_allocator, "Failed to create new object", .{}, ctx, exception); + return null; + }; + + this.* = .{ .hashing = Hasher.init() }; + return @This().Class.make(ctx, this); + } + + pub fn getter( + _: void, + ctx: js.JSContextRef, + _: js.JSValueRef, + _: js.JSStringRef, + _: js.ExceptionRef, + ) js.JSValueRef { + var existing = ctx.ptr().getCachedObject(&ZigString.init(cached_constructor_name)); + if (existing.isEmpty()) { + return ctx.ptr().putCachedObject( + &ZigString.init(cached_constructor_name), + JSC.JSValue.fromRef(@This().Constructor.constructor(ctx)), + ).asObjectRef(); + } + + return existing.asObjectRef(); + } + + pub fn update(this: *@This(), buffer: JSC.Node.StringOrBuffer) JSC.JSValue { + this.hashing.update(buffer.slice()); + return JSC.JSValue.jsUndefined(); + } + + pub fn final(this: *@This(), globalThis: *JSGlobalObject, exception: JSC.C.ExceptionRef, output: ?JSC.ArrayBuffer) JSC.JSValue { + var output_digest_buf: Hasher.Digest = undefined; + var output_digest_slice: *Hasher.Digest = &output_digest_buf; + if (output) |output_buf| { + var bytes = output_buf.byteSlice(); + if (bytes.len < Hasher.digest) { + JSC.JSError( + bun.default_allocator, + comptime std.fmt.comptimePrint("TypedArray must be at least {d} bytes", .{@as(usize, Hasher.digest)}), + .{}, + globalThis.ref(), + exception, + ); + return JSC.JSValue.zero; + } + output_digest_slice = bytes[0..Hasher.digest]; + } else { + output_digest_buf = comptime brk: { + var bytes: Hasher.Digest = undefined; + var i: usize = 0; + while (i < Hasher.digest) { + bytes[i] = 0; + i += 1; + } + break :brk bytes; + }; + } + + this.hashing.final(output_digest_slice); + if (output) |output_buf| { + return output_buf.value; + } else { + var array_buffer_out = JSC.ArrayBuffer.fromBytes(bun.default_allocator.dupe(u8, &output_digest_buf) catch unreachable, .Uint8Array); + return array_buffer_out.toJSUnchecked(globalThis.ref(), exception); + } + } + + pub fn finalize(this: *@This()) void { + VirtualMachine.vm.allocator.destroy(this); + } + }; + } + + pub const SHA1 = CryptoHasher(Hashers.SHA1, "SHA1", "Bun_SHA1"); + pub const SHA256 = CryptoHasher(Hashers.SHA256, "SHA256", "Bun_SHA256"); + pub const SHA384 = CryptoHasher(Hashers.SHA384, "SHA384", "Bun_SHA384"); + pub const SHA512 = CryptoHasher(Hashers.SHA512, "SHA512", "Bun_SHA512"); + pub const SHA512_256 = CryptoHasher(Hashers.SHA512_256, "SHA512_256", "Bun_SHA512_256"); +}; + pub fn serve( _: void, ctx: js.JSContextRef, diff --git a/src/javascript/jsc/base.zig b/src/javascript/jsc/base.zig index 70feecb76..9c07e9b37 100644 --- a/src/javascript/jsc/base.zig +++ b/src/javascript/jsc/base.zig @@ -2248,6 +2248,29 @@ pub const ArrayBuffer = extern struct { return ArrayBuffer{ .offset = 0, .len = @intCast(u32, bytes.len), .byte_len = @intCast(u32, bytes.len), .typed_array_type = typed_array_type, .ptr = bytes.ptr }; } + pub fn toJSUnchecked(this: ArrayBuffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.JSValue { + if (this.typed_array_type == .ArrayBuffer) { + return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArrayBufferWithBytesNoCopy( + ctx, + this.ptr, + this.byte_len, + MarkedArrayBuffer_deallocator, + @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), + exception, + )); + } + + return JSC.JSValue.fromRef(JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy( + ctx, + this.typed_array_type.toC(), + this.ptr, + this.byte_len, + MarkedArrayBuffer_deallocator, + @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), + exception, + )); + } + pub fn toJS(this: ArrayBuffer, ctx: JSC.C.JSContextRef, exception: JSC.C.ExceptionRef) JSC.JSValue { if (!this.value.isEmpty()) { return this.value; @@ -2277,26 +2300,7 @@ pub const ArrayBuffer = extern struct { )); } - if (this.typed_array_type == .ArrayBuffer) { - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeArrayBufferWithBytesNoCopy( - ctx, - this.ptr, - this.byte_len, - MarkedArrayBuffer_deallocator, - @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), - exception, - )); - } - - return JSC.JSValue.fromRef(JSC.C.JSObjectMakeTypedArrayWithBytesNoCopy( - ctx, - this.typed_array_type.toC(), - this.ptr, - this.byte_len, - MarkedArrayBuffer_deallocator, - @intToPtr(*anyopaque, @ptrToInt(&bun.default_allocator)), - exception, - )); + return this.toJSUnchecked(ctx, exception); } pub fn toJSWithContext( @@ -2552,6 +2556,11 @@ const Server = JSC.API.Server; const SSLServer = JSC.API.SSLServer; const DebugServer = JSC.API.DebugServer; const DebugSSLServer = JSC.API.DebugSSLServer; +const SHA1 = JSC.API.Bun.Crypto.SHA1; +const SHA256 = JSC.API.Bun.Crypto.SHA256; +const SHA384 = JSC.API.Bun.Crypto.SHA384; +const SHA512 = JSC.API.Bun.Crypto.SHA512; +const SHA512_256 = JSC.API.Bun.Crypto.SHA512_256; pub const JSPrivateDataPtr = TaggedPointerUnion(.{ AttributeIterator, @@ -2589,6 +2598,11 @@ pub const JSPrivateDataPtr = TaggedPointerUnion(.{ TextEncoder, TimeoutTask, Transpiler, + SHA1, + SHA256, + SHA384, + SHA512, + SHA512_256, }); pub inline fn GetJSPrivateData(comptime Type: type, ref: js.JSObjectRef) ?*Type { @@ -2678,9 +2692,9 @@ fn SetterType(comptime Container: type) type { ) bool; } -fn MethodType(comptime Container: type) type { +fn MethodType(comptime Container: type, comptime has_container: bool) type { return fn ( - this: *Container, + this: if (has_container) *Container else void, ctx: js.JSContextRef, thisObject: js.JSObjectRef, target: js.JSObjectRef, @@ -2692,14 +2706,14 @@ fn MethodType(comptime Container: type) type { pub fn wrapSync( comptime Container: type, comptime name: string, -) MethodType(Container) { +) MethodType(Container, true) { return wrap(Container, name, false); } pub fn wrapAsync( comptime Container: type, comptime name: string, -) MethodType(Container) { +) MethodType(Container, true) { return wrap(Container, name, true); } @@ -2707,13 +2721,23 @@ pub fn wrap( comptime Container: type, comptime name: string, comptime maybe_async: bool, -) MethodType(Container) { +) MethodType(Container, true) { + return wrapWithHasContainer(Container, name, maybe_async, true); +} + +pub fn wrapWithHasContainer( + comptime Container: type, + comptime name: string, + comptime maybe_async: bool, + comptime has_container: bool, +) MethodType(Container, has_container) { return struct { const FunctionType = @TypeOf(@field(Container, name)); const FunctionTypeInfo: std.builtin.TypeInfo.Fn = @typeInfo(FunctionType).Fn; + const Args = std.meta.ArgsTuple(FunctionType); pub fn callback( - this: *Container, + this: if (has_container) *Container else void, ctx: js.JSContextRef, _: js.JSObjectRef, thisObject: js.JSObjectRef, @@ -2721,12 +2745,11 @@ pub fn wrap( exception: js.ExceptionRef, ) js.JSObjectRef { var iter = JSC.Node.ArgumentsSlice.from(arguments); - var args: std.meta.ArgsTuple(FunctionType) = undefined; + var args: Args = undefined; comptime var i: usize = 0; inline while (i < FunctionTypeInfo.args.len) : (i += 1) { const ArgType = comptime FunctionTypeInfo.args[i].arg_type.?; - switch (comptime ArgType) { *Container => { args[i] = this; @@ -2734,14 +2757,63 @@ pub fn wrap( *JSC.JSGlobalObject => { args[i] = ctx.ptr(); }, + JSC.Node.StringOrBuffer => { + const arg = iter.nextEat() orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + args[i] = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), iter.arena.allocator(), arg, exception) orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + }, + ?JSC.Node.StringOrBuffer => { + if (iter.nextEat()) |arg| { + args[i] = JSC.Node.StringOrBuffer.fromJS(ctx.ptr(), iter.arena.allocator(), arg, exception) orelse { + exception.* = JSC.toInvalidArguments("expected string or buffer", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + args[i] = null; + } + }, + JSC.ArrayBuffer => { + if (iter.nextEat()) |arg| { + args[i] = arg.asArrayBuffer(ctx.ptr()) orelse { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + } + }, + ?JSC.ArrayBuffer => { + if (iter.nextEat()) |arg| { + args[i] = arg.asArrayBuffer(ctx.ptr()) orelse { + exception.* = JSC.toInvalidArguments("expected TypedArray", .{}, ctx).asObjectRef(); + iter.deinit(); + return null; + }; + } else { + args[i] = null; + } + }, ZigString => { var string_value = iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing argument", .{}, ctx, exception); + iter.deinit(); return null; }; if (string_value.isUndefinedOrNull()) { JSC.throwInvalidArguments("Expected string", .{}, ctx, exception); + iter.deinit(); return null; } @@ -2759,18 +2831,22 @@ pub fn wrap( *Response => { args[i] = (iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing Response object", .{}, ctx, exception); + iter.deinit(); return null; }).as(Response) orelse { JSC.throwInvalidArguments("Expected Response object", .{}, ctx, exception); + iter.deinit(); return null; }; }, *Request => { args[i] = (iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing Request object", .{}, ctx, exception); + iter.deinit(); return null; }).as(Request) orelse { JSC.throwInvalidArguments("Expected Request object", .{}, ctx, exception); + iter.deinit(); return null; }; }, @@ -2778,6 +2854,7 @@ pub fn wrap( args[i] = thisObject; if (!JSValue.fromRef(thisObject).isCell() or !JSValue.fromRef(thisObject).isObject()) { JSC.throwInvalidArguments("Expected object", .{}, ctx, exception); + iter.deinit(); return null; } }, @@ -2787,6 +2864,7 @@ pub fn wrap( JSValue => { const val = iter.protectEatNext() orelse { JSC.throwInvalidArguments("Missing argument", .{}, ctx, exception); + iter.deinit(); return null; }; args[i] = val; @@ -2796,7 +2874,7 @@ pub fn wrap( } var result: JSValue = @call(.{}, @field(Container, name), args); - if (result.isError()) { + if (!result.isEmptyOrUndefinedOrNull() and result.isError()) { exception.* = result.asObjectRef(); iter.deinit(); return null; |