aboutsummaryrefslogtreecommitdiff
path: root/src/javascript
diff options
context:
space:
mode:
authorGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-04-12 22:59:25 -0700
committerGravatar Jarred Sumner <jarred@jarredsumner.com> 2022-04-12 22:59:52 -0700
commitf6d73cb06ec5fe217a4d2d03808f68ecbc2d3461 (patch)
tree38ad0f487fe9a46fb4a1179967b8aee9c0510b06 /src/javascript
parentb3522b2febaae5caf218a264fc5277974be8d57b (diff)
downloadbun-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.zig214
-rw-r--r--src/javascript/jsc/base.zig136
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;