diff --git a/src/browser/js/Context.zig b/src/browser/js/Context.zig index 9180223c..367a235a 100644 --- a/src/browser/js/Context.zig +++ b/src/browser/js/Context.zig @@ -336,28 +336,18 @@ pub fn toLocal(self: *Context, global: anytype) js.Local.ToLocalReturnType(@Type return l.toLocal(global); } -// This isn't expected to be called often. It's for converting attributes into -// function calls, e.g. will turn that "doSomething" -// string into a js.Function which looks like: function(e) { doSomething(e) } -// There might be more efficient ways to do this, but doing it this way means -// our code only has to worry about js.Funtion, not some union of a js.Function -// or a string. -pub fn stringToPersistedFunction(self: *Context, str: []const u8) !js.Function.Global { +pub fn stringToPersistedFunction( + self: *Context, + function_body: []const u8, + comptime parameter_names: []const []const u8, + extensions: []const v8.Object, +) !js.Function.Global { var ls: js.Local.Scope = undefined; self.localScope(&ls); defer ls.deinit(); - var extra: []const u8 = ""; - const normalized = std.mem.trim(u8, str, &std.ascii.whitespace); - if (normalized.len > 0 and normalized[normalized.len - 1] != ')') { - extra = "(e)"; - } - const full = try std.fmt.allocPrintSentinel(self.call_arena, "(function(e) {{ {s}{s} }})", .{ normalized, extra }, 0); - const js_val = try ls.local.compileAndRun(full, null); - if (!js_val.isFunction()) { - return error.StringFunctionError; - } - return try (js.Function{ .local = &ls.local, .handle = @ptrCast(js_val.handle) }).persist(); + const js_function = try ls.local.compileFunction(function_body, parameter_names, extensions); + return js_function.persist(); } pub fn module(self: *Context, comptime want_result: bool, local: *const js.Local, src: []const u8, url: []const u8, cacheable: bool) !(if (want_result) ModuleEntry else void) { diff --git a/src/browser/js/Local.zig b/src/browser/js/Local.zig index 4b38f3f8..c6b8e880 100644 --- a/src/browser/js/Local.zig +++ b/src/browser/js/Local.zig @@ -118,16 +118,27 @@ pub fn exec(self: *const Local, src: []const u8, name: ?[]const u8) !js.Value { /// Compiles a function body as function. /// /// https://v8.github.io/api/head/classv8_1_1ScriptCompiler.html#a3a15bb5a7dfc3f998e6ac789e6b4646a -pub fn compileFunction(self: *const Local, function_body: []const u8) !js.Function { +pub fn compileFunction( + self: *const Local, + function_body: []const u8, + /// We tend to know how many params we'll pass; can remove the comptime if necessary. + comptime parameter_names: []const []const u8, + extensions: []const v8.Object, +) !js.Function { // TODO: Make configurable. const script_name = self.isolate.initStringHandle("anonymous"); const script_source = self.isolate.initStringHandle(function_body); - // Create ScriptOrigin. + var parameter_list: [parameter_names.len]*const v8.String = undefined; + inline for (0..parameter_names.len) |i| { + parameter_list[i] = self.isolate.initStringHandle(parameter_names[i]); + } + + // Create `ScriptOrigin`. var origin: v8.ScriptOrigin = undefined; v8.v8__ScriptOrigin__CONSTRUCT(&origin, script_name); - // Create ScriptCompilerSource. + // Create `ScriptCompilerSource`. var script_compiler_source: v8.ScriptCompilerSource = undefined; v8.v8__ScriptCompiler__Source__CONSTRUCT2(script_source, &origin, null, &script_compiler_source); defer v8.v8__ScriptCompiler__Source__DESTRUCT(&script_compiler_source); @@ -136,10 +147,10 @@ pub fn compileFunction(self: *const Local, function_body: []const u8) !js.Functi const result = v8.v8__ScriptCompiler__CompileFunction( self.handle, &script_compiler_source, - 0, - null, - 0, - null, + parameter_list.len, + ¶meter_list, + extensions.len, + @ptrCast(&extensions), v8.kNoCompileOptions, v8.kNoCacheNoReason, ) orelse return error.CompilationError;