Skip to content

Commit

Permalink
Merge pull request #130 from Browsercore/custom_exceptions
Browse files Browse the repository at this point in the history
Handle custom Exceptions
  • Loading branch information
francisbouvier authored Nov 24, 2023
2 parents 4b0aee3 + 6483329 commit ed78daa
Show file tree
Hide file tree
Showing 11 changed files with 366 additions and 34 deletions.
81 changes: 72 additions & 9 deletions src/engines/v8/generate.zig
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,60 @@ 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);
fn throwBasicError(
msg: []const u8,
js_res: v8.ReturnValue,
isolate: v8.Isolate,
) void {
const except_msg = v8.String.initUtf8(isolate, msg);
const exception = v8.Exception.initError(except_msg);
js_res.set(isolate.throwException(exception));
}

fn throwError(
alloc: std.mem.Allocator,
comptime T_refl: refl.Struct,
comptime all_T: []refl.Struct,
comptime func: refl.Func,
err: anytype,
js_res: v8.ReturnValue,
isolate: v8.Isolate,
) void {
const ret = func.return_type;

// Is the returned Type a custom Exception error?
// conditions:
// - the return type must be an ErrorUnion
// - the API must define a custom Exception
// - the ErrorSet of the return type must be an error of Exception
const except = comptime T_refl.exception(all_T);
if (comptime ret.errorSet() == null or except == null) {
return throwBasicError(@errorName(err), js_res, isolate);
}
if (!ret.isErrorException(except.?, err)) {
return throwBasicError(@errorName(err), js_res, isolate);
}

// create custom error instance
const obj = except.?.T.init(alloc, err, func.js_name) catch unreachable; // TODO
const ctx = isolate.getCurrentContext();
const js_obj = gen.getTpl(except.?.index).tpl.getInstanceTemplate().initInstance(ctx);
_ = setNativeObject(
alloc,
except.?,
@TypeOf(obj),
obj,
js_obj,
isolate,
) catch unreachable;

// throw exeption
js_res.set(isolate.throwException(js_obj));

// TODO: v8 does not throw a stack trace as Exception is not a prototype of Error
// There is no way to change this with the current v8 public API
}

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 @@ -240,12 +288,11 @@ fn getArgs(
pub fn setNativeObject(
alloc: std.mem.Allocator,
comptime T_refl: refl.Struct,
comptime obj_T: refl.Type,
comptime T: type,
obj: anytype,
js_obj: v8.Object,
isolate: v8.Isolate,
) !void {
const T = obj_T.underT();

// assign and bind native obj to JS obj
var obj_ptr: *T = undefined;
Expand Down Expand Up @@ -368,7 +415,7 @@ fn setReturnType(
_ = setNativeObject(
alloc,
all_T[index],
ret,
ret.underT(),
res,
js_obj,
isolate,
Expand Down Expand Up @@ -467,7 +514,7 @@ fn generateConstructor(
setNativeObject(
utils.allocator,
T_refl,
func.return_type,
func.return_type.underT(),
obj,
info.getThis(),
isolate,
Expand Down Expand Up @@ -527,7 +574,15 @@ fn generateGetter(
isolate,
) catch |err| {
// TODO: how to handle internal errors vs user errors
return throwError(@errorName(err), info.getReturnValue(), isolate);
return throwError(
utils.allocator,
T_refl,
all_T,
func,
err,
info.getReturnValue(),
isolate,
);
};
info.getReturnValue().setValueHandle(js_val.handle);
}
Expand Down Expand Up @@ -661,7 +716,15 @@ fn generateMethod(
isolate,
) catch |err| {
// TODO: how to handle internal errors vs user errors
return throwError(@errorName(err), info.getReturnValue(), isolate);
return throwError(
utils.allocator,
T_refl,
all_T,
func,
err,
info.getReturnValue(),
isolate,
);
};
info.getReturnValue().setValueHandle(js_val.handle);

Expand Down
42 changes: 30 additions & 12 deletions src/engines/v8/v8.zig
Original file line number Diff line number Diff line change
Expand Up @@ -156,29 +156,46 @@ pub const Env = struct {
}

// start a Javascript context
pub fn start(self: *Env, comptime apis: []API) void {
pub fn start(self: *Env, alloc: std.mem.Allocator, comptime apis: []API) anyerror!void {

// context
self.context = v8.Context.init(self.isolate, self.globals, null);
const ctx = self.context.?;
ctx.enter();

// APIs prototype
// set the prototype of each corresponding constructor Function
// NOTE: this is required to inherit attributes at the Type level,
// ie. static class attributes.
// For static instance attributes we set them
// on FunctionTemplate.PrototypeTemplate
// TODO: is there a better way to do it at the Template level?
// see https://github.com/Browsercore/jsruntime-lib/issues/128
// TODO: ideally all this should disapear,
// we shouldn't do anything at context startup time
inline for (apis, 0..) |api, i| {

// APIs prototype
// set the prototype of each corresponding constructor Function
// NOTE: this is required to inherit attributes at the Type level,
// ie. static class attributes.
// For static instance attributes we set them
// on FunctionTemplate.PrototypeTemplate
// TODO: is there a better way to do it at the Template level?
// see https://github.com/Browsercore/jsruntime-lib/issues/128
if (api.T_refl.proto_index) |proto_index| {
const cstr_tpl = gen.getTpl(i).tpl;
const proto_tpl = gen.getTpl(proto_index).tpl;
const cstr_obj = cstr_tpl.getFunction(ctx).toObject();
const proto_obj = proto_tpl.getFunction(ctx).toObject();
_ = cstr_obj.setPrototype(ctx, proto_obj);
}

// Custom exception
// NOTE: there is no way in v8 to subclass the Error built-in type
// TODO: this is an horrible hack
if (comptime api.T_refl.isException()) {
const script = api.T_refl.name ++ ".prototype.__proto__ = Error.prototype";
const res = try self.execTryCatch(
alloc,
script,
"errorSubclass",
);
defer res.deinit(alloc);
if (!res.success) return error.errorSubClass;
}
}
}

Expand Down Expand Up @@ -363,7 +380,7 @@ fn createJSObject(
try setNativeObject(
utils.allocator,
T_refl,
T_refl.value,
T_refl.value.underT(),
obj,
js_obj,
isolate,
Expand Down Expand Up @@ -453,7 +470,8 @@ pub const JSResult = struct {
if (self.stack != null) {
return;
}
const stack = try_catch.getStackTrace(context).?;
self.stack = try valueToUtf8(alloc, stack, isolate, context);
if (try_catch.getStackTrace(context)) |stack| {
self.stack = try valueToUtf8(alloc, stack, isolate, context);
}
}
};
3 changes: 2 additions & 1 deletion src/interfaces.zig
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ pub fn Env(
// start()
assertDecl(T, "start", fn (
self: *T,
alloc: std.mem.Allocator,
comptime apis: []API_T,
) void);
) anyerror!void);

// stop()
assertDecl(T, "stop", fn (self: *T) void);
Expand Down
Loading

0 comments on commit ed78daa

Please sign in to comment.