mirror of
https://github.com/lightpanda-io/browser.git
synced 2026-03-22 04:34:44 +00:00
Merge pull request #1771 from lightpanda-io/nikneym/compile-function
Prefer `ScriptCompiler::CompileFunction` to compile attribute listeners
This commit is contained in:
@@ -251,28 +251,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. <body onload="doSomething"> 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) {
|
||||
|
||||
@@ -116,6 +116,49 @@ pub fn exec(self: *const Local, src: []const u8, name: ?[]const u8) !js.Value {
|
||||
return self.compileAndRun(src, name);
|
||||
}
|
||||
|
||||
/// 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,
|
||||
/// 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);
|
||||
|
||||
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`.
|
||||
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);
|
||||
|
||||
// Compile the function.
|
||||
const result = v8.v8__ScriptCompiler__CompileFunction(
|
||||
self.handle,
|
||||
&script_compiler_source,
|
||||
parameter_list.len,
|
||||
¶meter_list,
|
||||
extensions.len,
|
||||
@ptrCast(&extensions),
|
||||
v8.kNoCompileOptions,
|
||||
v8.kNoCacheNoReason,
|
||||
) orelse return error.CompilationError;
|
||||
|
||||
return .{ .local = self, .handle = result };
|
||||
}
|
||||
|
||||
pub fn compileAndRun(self: *const Local, src: []const u8, name: ?[]const u8) !js.Value {
|
||||
const script_name = self.isolate.initStringHandle(name orelse "anonymous");
|
||||
const script_source = self.isolate.initStringHandle(src);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<body onload=func1></body>
|
||||
<body onload="func1(event)"></body>
|
||||
<script src="../testing.js"></script>
|
||||
|
||||
<script id=bodyOnLoad1>
|
||||
@@ -14,4 +14,3 @@
|
||||
testing.expectEqual(1, called);
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
@@ -387,22 +387,17 @@ pub fn getAttributeFunction(
|
||||
}
|
||||
|
||||
const attr = element.getAttributeSafe(.wrap(@tagName(listener_type))) orelse return null;
|
||||
const callback = page.js.stringToPersistedFunction(attr) catch |err| switch (err) {
|
||||
error.OutOfMemory => return err,
|
||||
else => {
|
||||
// Not a valid expression; log this to find out if its something we should be supporting.
|
||||
log.warn(.js, "Html.getAttributeFunction", .{
|
||||
.expression = attr,
|
||||
.err = err,
|
||||
});
|
||||
|
||||
return null;
|
||||
},
|
||||
const function = page.js.stringToPersistedFunction(attr, &.{"event"}, &.{}) catch |err| {
|
||||
// Not a valid expression; log this to find out if its something we should be supporting.
|
||||
log.warn(.js, "Html.getAttributeFunction", .{
|
||||
.expression = attr,
|
||||
.err = err,
|
||||
});
|
||||
return null;
|
||||
};
|
||||
|
||||
try self.setAttributeListener(listener_type, callback, page);
|
||||
|
||||
return callback;
|
||||
try self.setAttributeListener(listener_type, function, page);
|
||||
return function;
|
||||
}
|
||||
|
||||
pub fn hasAttributeFunction(self: *HtmlElement, listener_type: GlobalEventHandler, page: *const Page) bool {
|
||||
|
||||
@@ -50,7 +50,7 @@ pub const Build = struct {
|
||||
pub fn complete(node: *Node, page: *Page) !void {
|
||||
const el = node.as(Element);
|
||||
const on_load = el.getAttributeSafe(comptime .wrap("onload")) orelse return;
|
||||
if (page.js.stringToPersistedFunction(on_load)) |func| {
|
||||
if (page.js.stringToPersistedFunction(on_load, &.{"event"}, &.{})) |func| {
|
||||
page.window._on_load = func;
|
||||
} else |err| {
|
||||
log.err(.js, "body.onload", .{ .err = err, .str = on_load });
|
||||
|
||||
Reference in New Issue
Block a user