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

test(node): get test-assert.js working #15698

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/bun.js/base.zig
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ pub fn toJS(globalObject: *JSC.JSGlobalObject, comptime ValueType: type, value:

var array = JSC.JSValue.createEmptyArray(globalObject, value.len);
for (value, 0..) |*item, i| {
const res = toJS(globalObject, *Child, item, lifetime);
const res = toJS(globalObject, *const Child, item, lifetime);
if (res == .zero) return .zero;
array.putIndex(
globalObject,
Expand Down
7 changes: 5 additions & 2 deletions src/bun.js/bindings/ErrorCode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,11 @@ export default [
["ERR_INVALID_ARG_VALUE", TypeError],
["ERR_INVALID_PROTOCOL", TypeError],
["ERR_INVALID_THIS", TypeError],
["ERR_INVALID_RETURN_VALUE", TypeError],
["ERR_IPC_CHANNEL_CLOSED", Error],
["ERR_IPC_DISCONNECTED", Error],
["ERR_MISSING_ARGS", TypeError],
["ERR_AMBIGUOUS_ARGUMENT", TypeError],
["ERR_OUT_OF_RANGE", RangeError],
["ERR_PARSE_ARGS_INVALID_OPTION_VALUE", TypeError],
["ERR_PARSE_ARGS_UNEXPECTED_POSITIONAL", TypeError],
Expand All @@ -47,8 +49,9 @@ export default [
["ERR_UNKNOWN_SIGNAL", TypeError],
["ERR_SOCKET_BAD_PORT", RangeError],
["ERR_STREAM_RELEASE_LOCK", Error, "AbortError"],
["ERR_INCOMPATIBLE_OPTION_PAIR", TypeError, "TypeError"],
["ERR_INVALID_URI", URIError, "URIError"],
["ERR_INCOMPATIBLE_OPTION_PAIR", TypeError],
["ERR_UNAVAILABLE_DURING_EXIT", Error],
["ERR_INVALID_URI", URIError],

// Bun-specific
["ERR_FORMDATA_PARSE_ERROR", TypeError],
Expand Down
2 changes: 2 additions & 0 deletions src/bun.js/bindings/bindings.zig
Original file line number Diff line number Diff line change
Expand Up @@ -3076,6 +3076,7 @@ pub const JSGlobalObject = opaque {
return this.throwValue(this.createInvalidArgumentType(name_, field, typename));
}

/// "The <argname> argument must be of type <typename>. Received <value>"
pub fn throwInvalidArgumentTypeValue(
this: *JSGlobalObject,
argname: []const u8,
Expand Down Expand Up @@ -3115,6 +3116,7 @@ pub const JSGlobalObject = opaque {
return JSC.toTypeError(.ERR_MISSING_ARGS, "Not enough arguments to '" ++ name_ ++ "'. Expected {d}, got {d}.", .{ expected, got }, this);
}

/// Not enough arguments passed to function named `name_`
pub fn throwNotEnoughArguments(
this: *JSGlobalObject,
comptime name_: []const u8,
Expand Down
15 changes: 15 additions & 0 deletions src/bun.js/node/node_assert.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
const std = @import("std");
const Allocator = std.mem.Allocator;

const DiffMatchPatch = @import("../../deps/diffz/DiffMatchPatch.zig");
const DiffError = DiffMatchPatch.DiffError;
pub const Diff = DiffMatchPatch.Diff;

const dmp = DiffMatchPatch{
.diff_timeout = 200, // ms
};

pub fn myersDiff(allocator: Allocator, actual: []const u8, expected: []const u8) DiffError!std.ArrayListUnmanaged(Diff) {
// TODO: this DMP impl allocates to much and needs improvement.
return dmp.diff(allocator, expected, actual, false);
}
81 changes: 81 additions & 0 deletions src/bun.js/node/node_assert_binding.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const std = @import("std");
const bun = @import("root").bun;
const JSC = bun.JSC;
const assert = @import("./node_assert.zig");
const Diff = assert.Diff;
const Allocator = std.mem.Allocator;

/// ```ts
/// const enum DiffType {
/// Insert = 0,
/// Delete = 1,
/// Equal = 2,
/// }
/// type Diff = { operation: DiffType, text: string };
/// declare function myersDiff(actual: string, expected: string): Diff[];
/// ```
pub fn myersDiff(global: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSC.JSValue {
if (callframe.argumentsCount() < 2) {
return global.throwNotEnoughArguments("printMyersDiff", 2, callframe.argumentsCount());
}

var stack_fallback = std.heap.stackFallback(1024 * 2, bun.default_allocator);
var arena = std.heap.ArenaAllocator.init(stack_fallback.get());
defer arena.deinit();
const alloc = arena.allocator();

const actual = try safeToString(global, alloc, callframe.argument(0), "actual");
const expected = try safeToString(global, alloc, callframe.argument(1), "expected");

const diff = assert.myersDiff(arena.allocator(), actual.slice(), expected.slice()) catch |e| {
return switch (e) {
error.OutOfMemory => return global.throwOutOfMemory(),
};
};

// todo: replace with toJS
var array = JSC.JSValue.createEmptyArray(global, diff.items.len);
for (diff.items, 0..) |*line, i| {
var obj = JSC.JSValue.createEmptyObjectWithNullPrototype(global);
if (obj == .zero) return global.throwOutOfMemory();
obj.put(global, bun.String.static("operation"), JSC.JSValue.jsNumber(@as(u32, @intFromEnum(line.operation))));
obj.put(global, bun.String.static("text"), JSC.toJS(global, []const u8, line.text, .allocated));
array.putIndex(global, @truncate(i), obj);
}
return array;
}

fn safeToString(global: *JSC.JSGlobalObject, arena: Allocator, argument: JSC.JSValue, comptime argname: []const u8) bun.JSError!bun.JSC.ZigString.Slice {
if (argument.isString()) {
const bunstring = argument.toBunString2(global) catch @panic("argument is string-like but could not be converted into a bun.String. This is a bug.");
return bunstring.toUTF8WithoutRef(arena);
} else if (argument.isObject()) {
const to_string: JSC.JSValue = (try argument.getFunction(global, "toString")) orelse {
return global.throwInvalidArgumentTypeValue(argname, "string or object with .toString()", argument);
};
const js_string = try to_string.call(
global,
argument,
&[0]JSC.JSValue{},
);
const bun_string = bun.String.fromJS(js_string, global);
// TODO: does bunstring own its memory or does it increment a reference
// count? IF the former, it's saved in the arena and this won't leak,
// otherwise this will cause a UAF.
return bun_string.toUTF8WithoutRef(arena);
} else {
return global.throwInvalidArgumentTypeValue(argname, "string or object with .toString()", argument);
}
}

pub fn generate(global: *JSC.JSGlobalObject) JSC.JSValue {
const exports = JSC.JSValue.createEmptyObject(global, 1);

exports.put(
global,
bun.String.static("myersDiff"),
JSC.JSFunction.create(global, "myersDiff", myersDiff, 2, .{}),
);

return exports;
}
1 change: 1 addition & 0 deletions src/codegen/bundle-modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,7 @@ writeIfNotChanged(
(() => {
let dts = `
// GENERATED TEMP FILE - DO NOT EDIT
// generated by ${import.meta.path}
`;

for (let i = 0; i < ErrorCode.length; i++) {
Expand Down
Loading
Loading