Skip to content

Commit 3fcd9b8

Browse files
Merge pull request #239 from lightpanda-io/js_exec
Refacto Env JS execution and result
2 parents 04ce2a2 + 049460a commit 3fcd9b8

18 files changed

+243
-310
lines changed

src/api.zig

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,17 @@ pub const test_utils = @import("tests/test_utils.zig");
4141
// JS types
4242
// --------
4343

44+
pub const JSTypes = enum {
45+
object,
46+
function,
47+
string,
48+
number,
49+
boolean,
50+
bigint,
51+
null,
52+
undefined,
53+
};
54+
4455
const types = @import("types.zig");
4556
pub const i64Num = types.i64Num;
4657
pub const u64Num = types.u64Num;
@@ -59,7 +70,7 @@ pub const UserContext = @import("user_context.zig").UserContext;
5970

6071
const Engine = @import("private_api.zig").Engine;
6172

62-
pub const JSResult = Engine.JSResult;
73+
pub const JSValue = Engine.JSValue;
6374
pub const JSObject = Engine.JSObject;
6475
pub const JSObjectID = Engine.JSObjectID;
6576

src/engines/v8/callback.zig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@ pub const Func = struct {
367367
// execute function
368368
const result = js_func.call(js_ctx, this, args);
369369
if (result == null) {
370-
return error.JSCallback;
370+
return error.JSExecCallback;
371371
}
372372
}
373373
};

src/engines/v8/v8.zig

Lines changed: 88 additions & 171 deletions
Original file line numberDiff line numberDiff line change
@@ -165,7 +165,7 @@ pub const Env = struct {
165165
}
166166

167167
// start a Javascript context
168-
pub fn start(self: *Env, alloc: std.mem.Allocator) anyerror!void {
168+
pub fn start(self: *Env) anyerror!void {
169169

170170
// context
171171
self.js_ctx = v8.Context.init(self.isolate, self.globals.getInstanceTemplate(), null);
@@ -197,13 +197,10 @@ pub const Env = struct {
197197
// TODO: this is an horrible hack
198198
if (comptime T_refl.isException()) {
199199
const script = T_refl.name ++ ".prototype.__proto__ = Error.prototype";
200-
const res = try self.execTryCatch(
201-
alloc,
202-
script,
203-
"errorSubclass",
204-
);
205-
defer res.deinit(alloc);
206-
if (!res.success) return error.errorSubClass;
200+
_ = self.exec(script, "errorSubclass") catch {
201+
// TODO: is there a reason to override the error?
202+
return error.errorSubClass;
203+
};
207204
}
208205
}
209206
}
@@ -293,119 +290,48 @@ pub const Env = struct {
293290
}
294291
}
295292

296-
// compile and run a Javascript script
297-
// if no error you need to call deinit on the returned result
293+
// compile and run a JS script
294+
// It doesn't wait for callbacks execution
298295
pub fn exec(
299296
self: Env,
300-
alloc: std.mem.Allocator,
301297
script: []const u8,
302298
name: ?[]const u8,
303-
try_catch: TryCatch,
304-
) !JSResult {
299+
) anyerror!JSValue {
305300
if (self.js_ctx == null) {
306301
return error.EnvNotStarted;
307302
}
308-
309-
var res = JSResult.init();
310-
try res.exec(alloc, script, name, self.isolate, self.js_ctx.?, try_catch.try_catch.*);
311-
return res;
303+
return try jsExec(script, name, self.isolate, self.js_ctx.?);
312304
}
313305

314-
// compile and run a Javascript script with try/catch
315-
// if no error you need to call deinit on the returned result
316-
pub fn execTryCatch(
317-
self: Env,
318-
alloc: std.mem.Allocator,
319-
script: []const u8,
320-
name: ?[]const u8,
321-
) anyerror!JSResult {
322-
323-
// JS try cache
324-
var try_catch = TryCatch.init(self);
325-
defer try_catch.deinit();
326-
327-
return self.exec(alloc, script, name, try_catch);
328-
}
329-
330-
// wait I/O loop until all JS callbacks are executed
306+
// wait I/O Loop until all JS callbacks are executed
331307
// This is a blocking operation.
332-
pub fn wait(
333-
self: Env,
334-
alloc: std.mem.Allocator,
335-
try_catch: v8.TryCatch,
336-
cbk_res: ?*JSResult,
337-
) !void {
308+
// Errors can be either:
309+
// - an error of the Loop (eg. IO kernel)
310+
// - an error of one of the JS callbacks
311+
// NOTE: the Loop does not stop when a JS callback throw an error
312+
// ie. all JS callbacks are executed
313+
// TODO: return at first error on a JS callback and let the caller
314+
// decide whether going forward or not
315+
pub fn wait(self: Env) anyerror!void {
338316
if (self.js_ctx == null) {
339317
return error.EnvNotStarted;
340318
}
341319

342320
// run loop
343-
self.nat_ctx.loop.run() catch |err| {
344-
if (try_catch.hasCaught()) {
345-
if (cbk_res) |res| {
346-
res.success = false;
347-
return res.setError(alloc, self.isolate, self.js_ctx.?, try_catch);
348-
}
349-
// otherwise ignore JS errors
350-
} else {
351-
// IO kernel error
352-
return err;
353-
}
354-
};
355-
356-
if (cbk_res) |res| res.success = true;
321+
return self.nat_ctx.loop.run();
357322
}
358323

359-
pub fn waitTryCatch(
360-
self: Env,
361-
alloc: std.mem.Allocator,
362-
) anyerror!JSResult {
363-
364-
// JS try cache
365-
var try_catch: v8.TryCatch = undefined;
366-
try_catch.init(self.isolate);
367-
defer try_catch.deinit();
368-
369-
var res = JSResult{};
370-
try self.wait(alloc, try_catch, &res);
371-
return res;
372-
}
373-
374-
// run a JS script and wait for all callbacks
375-
// try_catch + exec + wait
376-
pub fn run(
377-
self: Env,
378-
alloc: std.mem.Allocator,
379-
script: []const u8,
380-
name: ?[]const u8,
381-
res: *JSResult,
382-
cbk_res: ?*JSResult,
383-
) anyerror!void {
384-
if (self.js_ctx == null) {
385-
return error.EnvNotStarted;
386-
}
387-
388-
// JS try cache
389-
var try_catch: v8.TryCatch = undefined;
390-
try_catch.init(self.isolate);
391-
defer try_catch.deinit();
324+
// compile and run a JS script and wait for all callbacks (exec + wait)
325+
// This is a blocking operation.
326+
pub fn execWait(self: Env, script: []const u8, name: ?[]const u8) anyerror!JSValue {
392327

393328
// exec script
394-
try res.exec(alloc, script, name, self.isolate, self.js_ctx.?, try_catch);
329+
const res = try self.exec(script, name);
395330

396-
// run loop
397-
self.nat_ctx.loop.run() catch |err| {
398-
if (try_catch.hasCaught()) {
399-
if (cbk_res) |r| {
400-
r.success = false;
401-
return r.setError(alloc, self.isolate, self.js_ctx.?, try_catch);
402-
}
403-
// otherwise ignore JS errors
404-
} else {
405-
// IO kernel error
406-
return err;
407-
}
408-
};
331+
// wait
332+
try self.wait();
333+
334+
return res;
409335
}
410336
};
411337

@@ -493,91 +419,82 @@ pub const JSObject = struct {
493419
}
494420
};
495421

496-
pub const TryCatch = struct {
497-
try_catch: *v8.TryCatch,
422+
pub const JSValue = struct {
423+
value: v8.Value,
498424

499-
pub inline fn init(env: Env) TryCatch {
500-
var try_catch: v8.TryCatch = undefined;
501-
try_catch.init(env.isolate);
502-
return .{ .try_catch = &try_catch };
425+
// the caller needs to deinit the string returned
426+
pub fn toString(self: JSValue, alloc: std.mem.Allocator, env: Env) anyerror![]const u8 {
427+
return valueToUtf8(alloc, self.value, env.isolate, env.js_ctx.?);
503428
}
504429

505-
pub inline fn exception(self: TryCatch, alloc: std.mem.Allocator, env: Env) anyerror!?[]const u8 {
506-
if (self.try_catch.getException()) |msg| {
507-
return try valueToUtf8(alloc, msg, env.isolate, env.js_ctx.?);
508-
}
509-
return null;
510-
}
511-
512-
pub inline fn deinit(self: *TryCatch) void {
513-
self.try_catch.deinit();
430+
pub fn typeOf(self: JSValue, env: Env) anyerror!public.JSTypes {
431+
var buf: [20]u8 = undefined;
432+
const str = try self.value.typeOf(env.isolate);
433+
const len = str.lenUtf8(env.isolate);
434+
const s = buf[0..len];
435+
_ = str.writeUtf8(env.isolate, s);
436+
return std.meta.stringToEnum(public.JSTypes, s) orelse {
437+
std.log.err("JSValueTypeNotHandled: {s}", .{s});
438+
return error.JSValueTypeNotHandled;
439+
};
514440
}
515441
};
516442

517-
pub const JSResult = struct {
518-
success: bool = false,
519-
result: []const u8 = undefined,
520-
stack: ?[]const u8 = null,
443+
pub const TryCatch = struct {
444+
inner: v8.TryCatch,
521445

522-
pub fn init() JSResult {
523-
return .{};
446+
pub fn init(self: *TryCatch, env: Env) void {
447+
self.inner.init(env.isolate);
524448
}
525449

526-
pub fn deinit(self: JSResult, alloc: std.mem.Allocator) void {
527-
alloc.free(self.result);
528-
if (self.stack) |stack| {
529-
alloc.free(stack);
530-
}
450+
pub fn hasCaught(self: TryCatch) bool {
451+
return self.inner.hasCaught();
531452
}
532453

533-
pub fn exec(
534-
self: *JSResult,
535-
alloc: std.mem.Allocator,
536-
script: []const u8,
537-
name: ?[]const u8,
538-
isolate: v8.Isolate,
539-
js_ctx: v8.Context,
540-
try_catch: v8.TryCatch,
541-
) !void {
542-
543-
// compile
544-
var origin: ?v8.ScriptOrigin = undefined;
545-
if (name) |n| {
546-
const scr_name = v8.String.initUtf8(isolate, n);
547-
origin = v8.ScriptOrigin.initDefault(isolate, scr_name.toValue());
454+
// the caller needs to deinit the string returned
455+
pub fn exception(self: TryCatch, alloc: std.mem.Allocator, env: Env) anyerror!?[]const u8 {
456+
if (self.inner.getException()) |msg| {
457+
return try valueToUtf8(alloc, msg, env.isolate, env.js_ctx.?);
548458
}
549-
const scr_js = v8.String.initUtf8(isolate, script);
550-
const scr = v8.Script.compile(js_ctx, scr_js, origin) catch {
551-
return self.setError(alloc, isolate, js_ctx, try_catch);
552-
};
459+
return null;
460+
}
553461

554-
// run
555-
const res = scr.run(js_ctx) catch {
556-
return self.setError(alloc, isolate, js_ctx, try_catch);
557-
};
558-
self.success = true;
559-
self.result = try valueToUtf8(alloc, res, isolate, js_ctx);
462+
// the caller needs to deinit the string returned
463+
pub fn stack(self: TryCatch, alloc: std.mem.Allocator, env: Env) anyerror!?[]const u8 {
464+
const stck = self.inner.getStackTrace(env.js_ctx.?);
465+
if (stck) |s| return try valueToUtf8(alloc, s, env.isolate, env.js_ctx.?);
466+
return null;
560467
}
561468

562-
pub fn setError(
563-
self: *JSResult,
564-
alloc: std.mem.Allocator,
565-
isolate: v8.Isolate,
566-
js_ctx: v8.Context,
567-
try_catch: v8.TryCatch,
568-
) !void {
569-
570-
// exception
571-
const except = try_catch.getException().?;
572-
self.success = false;
573-
self.result = try valueToUtf8(alloc, except, isolate, js_ctx);
574-
575-
// stack
576-
if (self.stack != null) {
577-
return;
578-
}
579-
if (try_catch.getStackTrace(js_ctx)) |stack| {
580-
self.stack = try valueToUtf8(alloc, stack, isolate, js_ctx);
469+
// a shorthand method to return either the entire stack message
470+
// or just the exception message
471+
// - in Debug mode return the stack if available
472+
// - otherwhise return the exception if available
473+
// the caller needs to deinit the string returned
474+
pub fn err(self: TryCatch, alloc: std.mem.Allocator, env: Env) anyerror!?[]const u8 {
475+
if (builtin.mode == .Debug) {
476+
if (try self.stack(alloc, env)) |msg| return msg;
581477
}
478+
return try self.exception(alloc, env);
479+
}
480+
481+
pub fn deinit(self: *TryCatch) void {
482+
self.inner.deinit();
582483
}
583484
};
485+
486+
pub fn jsExec(script: []const u8, name: ?[]const u8, isolate: v8.Isolate, js_ctx: v8.Context) !JSValue {
487+
488+
// compile
489+
var origin: ?v8.ScriptOrigin = undefined;
490+
if (name) |n| {
491+
const scr_name = v8.String.initUtf8(isolate, n);
492+
origin = v8.ScriptOrigin.initDefault(isolate, scr_name.toValue());
493+
}
494+
const scr_js = v8.String.initUtf8(isolate, script);
495+
const scr = v8.Script.compile(js_ctx, scr_js, origin) catch return error.JSCompile;
496+
497+
// run
498+
const value = scr.run(js_ctx) catch return error.JSExec;
499+
return .{ .value = value };
500+
}

0 commit comments

Comments
 (0)