Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 29 additions & 16 deletions src/engines/v8/generate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ const TPL = @import("v8.zig").TPL;
// Utils functions
// ---------------

fn throwError(msg: []const u8, js_res: v8.ReturnValue, isolate: v8.Isolate) void {
const err = v8.String.initUtf8(isolate, msg);
const exception = v8.Exception.initError(err);
js_res.set(isolate.throwException(exception));
}

fn throwTypeError(msg: []const u8, js_res: v8.ReturnValue, isolate: v8.Isolate) void {
const err = v8.String.initUtf8(isolate, msg);
const exception = v8.Exception.initTypeError(err);
Expand Down Expand Up @@ -290,35 +296,35 @@ fn setReturnType(
ctx: v8.Context,
isolate: v8.Isolate,
) !v8.Value {
const under_opt = comptime ret.underOpt();
const val_T = if (under_opt) |T| T else ret.T;
var val: val_T = undefined;
const info = @typeInfo(@TypeOf(res));

// Error Union
if (info == .ErrorUnion) {
const unwrapped = try res;
return setReturnType(alloc, all_T, ret, unwrapped, ctx, isolate);
}

// Optional
if (under_opt != null) {
if (info == .Optional) {
if (res == null) {
// if null just return JS null
return isolate.initNull().toValue();
} else {
// otherwise replace with the underlying type
val = res.?;
}
} else {
val = res;
return setReturnType(alloc, all_T, ret, res.?, ctx, isolate);
}

// Union type
if (comptime ret.union_T) |union_types| {
// retrieve the active field and setReturntype accordingly
const activeTag = @tagName(std.meta.activeTag(val));
const activeTag = @tagName(std.meta.activeTag(res));
// TODO: better algorythm?
inline for (union_types) |tt| {
if (std.mem.eql(u8, activeTag, tt.name.?)) {
return setReturnType(
alloc,
all_T,
tt,
@field(val, tt.name.?),
@field(res, tt.name.?),
ctx,
isolate,
);
Expand All @@ -340,7 +346,7 @@ fn setReturnType(
alloc,
all_T,
field,
@field(val, name),
@field(res, name),
ctx,
isolate,
);
Expand All @@ -363,7 +369,7 @@ fn setReturnType(
alloc,
all_T[index],
ret,
val,
res,
js_obj,
isolate,
) catch unreachable;
Expand All @@ -374,7 +380,7 @@ fn setReturnType(

const js_val = nativeToJS(
ret.underT(),
val,
res,
isolate,
) catch unreachable; // NOTE: should not happen has types have been checked at reflect
return js_val;
Expand Down Expand Up @@ -466,6 +472,7 @@ fn generateConstructor(
info.getThis(),
isolate,
) catch unreachable;
// TODO: we choose for now not throw user error
}
}.constructor;
}
Expand Down Expand Up @@ -512,7 +519,10 @@ fn generateGetter(
res,
isolate.getCurrentContext(),
isolate,
) catch unreachable;
) catch |err| {
// TODO: how to handle internal errors vs user errors
return throwError(@errorName(err), info.getReturnValue(), isolate);
};
info.getReturnValue().setValueHandle(js_val.handle);
}
}.getter;
Expand Down Expand Up @@ -629,7 +639,10 @@ fn generateMethod(
res,
ctx,
isolate,
) catch unreachable;
) catch |err| {
// TODO: how to handle internal errors vs user errors
return throwError(@errorName(err), info.getReturnValue(), isolate);
};
info.getReturnValue().setValueHandle(js_val.handle);

// sync callback
Expand Down
49 changes: 45 additions & 4 deletions src/reflect.zig
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ const internal_types = [_]type{
};

// Type describes the reflect information of an individual value, either:
// - an input paramater of a function
// - an input parameter of a function
// - or a return value of a function
pub const Type = struct {
T: type, // could be pointer or concrete
Expand Down Expand Up @@ -73,9 +73,24 @@ pub const Type = struct {
// - the underlying type, ie. the concrete one
// - and all the successive stages

// !Type
fn _underErr(comptime T: type) ?type {
const info = @typeInfo(T);
if (info != .ErrorUnion) {
return null;
}
return info.ErrorUnion.payload;
}
pub inline fn underErr(comptime self: Type) ?type {
std.debug.assert(@inComptime());
return Type._underErr(self.T);
}

// !?Type (from underErr)
// ?Type
fn _underOpt(comptime T: type) ?type {
const info = @typeInfo(T);
const TT = Type._underErr(T) orelse T;
const info = @typeInfo(TT);
if (info != .Optional) {
return null;
}
Expand All @@ -86,10 +101,18 @@ pub const Type = struct {
return Type._underOpt(self.T);
}

// ?*Type
// !?*Type, ?*Type (from underOpt)
// !*Type (from underErr)
// *Type
fn _underPtr(comptime T: type) ?type {
const TT = Type._underOpt(T) orelse T;
var TT: type = undefined;
if (Type._underOpt(T)) |t| {
TT = t;
} else if (Type._underErr(T)) |t| {
TT = t;
} else {
TT = T;
}
const info = @typeInfo(TT);
if (info == .Pointer and info.Pointer.size != .Slice) {
// NOTE: slice are pointers but we handle them as single value
Expand All @@ -102,9 +125,14 @@ pub const Type = struct {
return Type._underPtr(self.T);
}

// !?*Type, ?*Type, !*Type, *Type (from underPtr)
// !?Type, ?Type (from underOpt)
// !Type (from underErr)
// Type
fn _underT(comptime T: type) type {
if (Type._underPtr(T)) |TT| return TT;
if (Type._underOpt(T)) |TT| return TT;
if (Type._underErr(T)) |TT| return TT;
return T;
}
pub inline fn underT(comptime self: Type) type {
Expand Down Expand Up @@ -482,6 +510,11 @@ pub const Func = struct {
if (Type._is_variadic(args_types[i].underT()) and i < (args.len - 1)) {
return error.FuncVariadicNotLastOne;
}

// error union prohibited for args
if (args_types[i].underErr() != null) {
return error.FuncErrorUnionArg;
}
}

// first optional arg
Expand Down Expand Up @@ -1258,6 +1291,7 @@ const Error = error{
FuncMultiCbk,
FuncVariadicNotLastOne,
FuncReturnTypeVariadic,
FuncErrorUnionArg,

// type errors
TypeTaggedUnion,
Expand Down Expand Up @@ -1367,6 +1401,9 @@ const TestFuncVariadicNotLastOne = struct {
const TestFuncReturnTypeVariadic = struct {
pub fn _example(_: TestFuncReturnTypeVariadic) ?VariadicBool {}
};
const TestFuncErrorUnionArg = struct {
pub fn _example(_: TestFuncErrorUnionArg, _: anyerror!void) void {}
};

// types tests
const TestTaggedUnion = union {
Expand Down Expand Up @@ -1477,6 +1514,10 @@ pub fn tests() !void {
.{TestFuncReturnTypeVariadic},
error.FuncReturnTypeVariadic,
);
try ensureErr(
.{TestFuncErrorUnionArg},
error.FuncErrorUnionArg,
);

// types checks
try ensureErr(
Expand Down
34 changes: 32 additions & 2 deletions src/tests/types_complex_test.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,33 @@ const MyVariadic = struct {
}
};

const MyErrorUnion = struct {
pub fn constructor() MyErrorUnion {
return .{};
}

pub fn get_withoutError(_: MyErrorUnion) !u8 {
return 0;
}

pub fn get_withError(_: MyErrorUnion) !u8 {
return error.MyError;
}

pub fn _funcWithoutError(_: MyErrorUnion) !void {}

pub fn _funcWithError(_: MyErrorUnion) !void {
return error.MyError;
}
};

// generate API, comptime
pub fn generate() ![]public.API {
return try public.compile(.{
pub fn generate() []public.API {
return public.compile(.{
MyIterable,
MyList,
MyVariadic,
MyErrorUnion,
});
}

Expand Down Expand Up @@ -93,4 +114,13 @@ pub fn exec(
.{ .src = "myVariadic.empty()", .ex = "true" },
};
try tests.checkCases(js_env, &variadic);

var error_union = [_]tests.Case{
.{ .src = "let myErrorUnion = new MyErrorUnion();", .ex = "undefined" },
.{ .src = "myErrorUnion.withoutError", .ex = "0" },
.{ .src = "var myErrorGetter = ''; try {myErrorUnion.withError} catch (error) {myErrorGetter = error}; myErrorGetter", .ex = "Error: MyError" },
.{ .src = "myErrorUnion.funcWithoutError()", .ex = "undefined" },
.{ .src = "var myErrorFunc = ''; try {myErrorUnion.funcWithError()} catch (error) {myErrorFunc = error}; myErrorFunc", .ex = "Error: MyError" },
};
try tests.checkCases(js_env, &error_union);
}