mirror of
https://github.com/lightpanda-io/browser.git
synced 2025-10-29 15:13:28 +00:00
Add more tests
Remove index and named setters, since they aren't working, and they aren't currently needed.
This commit is contained in:
@@ -1,16 +1,20 @@
|
||||
// Copyright 2023-2024 Lightpanda (Selecy SAS)
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
@@ -548,12 +552,11 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
}
|
||||
|
||||
fn generateIndexer(_: *Self, comptime Struct: type, template_proto: v8.ObjectTemplate) void {
|
||||
var has_one = false;
|
||||
var configuration = v8.IndexedPropertyHandlerConfiguration{};
|
||||
|
||||
if (@hasDecl(Struct, "indexed_get")) {
|
||||
has_one = true;
|
||||
configuration.getter = struct {
|
||||
if (@hasDecl(Struct, "indexed_get") == false) {
|
||||
return;
|
||||
}
|
||||
const configuration = v8.IndexedPropertyHandlerConfiguration{
|
||||
.getter = struct {
|
||||
fn callback(idx: u32, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
var caller = Caller(Self).init(info);
|
||||
@@ -564,44 +567,25 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
caller.handleError(named_function, err, info);
|
||||
};
|
||||
}
|
||||
}.callback;
|
||||
}
|
||||
}.callback,
|
||||
};
|
||||
|
||||
if (@hasDecl(Struct, "indexed_set")) {
|
||||
has_one = true;
|
||||
configuration.setter = struct {
|
||||
fn callback(idx: u32, raw_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
var caller = Caller(Self).init(info);
|
||||
defer caller.deinit();
|
||||
|
||||
const js_value = v8.Value{ .handle = raw_value.? };
|
||||
const named_function = NamedFunction(Struct, Struct.indexed_set, "indexed_set"){};
|
||||
caller.setIndex(named_function, idx, js_value, info) catch |err| {
|
||||
caller.handleError(named_function, err, info);
|
||||
};
|
||||
}
|
||||
}.callback;
|
||||
}
|
||||
|
||||
if (has_one) {
|
||||
template_proto.setIndexedProperty(configuration, null);
|
||||
}
|
||||
// If you're trying to implement setter, read:
|
||||
// https://groups.google.com/g/v8-users/c/8tahYBsHpgY/m/IteS7Wn2AAAJ
|
||||
// The issue I had was
|
||||
// (a) where to attache it: does it go ont he instance_template
|
||||
// instead of the prototype?
|
||||
// (b) defining the getter or query to respond with the
|
||||
// PropertyAttribute to indicate if the property can be set
|
||||
template_proto.setIndexedProperty(configuration, null);
|
||||
}
|
||||
|
||||
fn generateNamedIndexer(_: *Self, comptime Struct: type, template_proto: v8.ObjectTemplate) void {
|
||||
var has_one = false;
|
||||
var configuration = v8.NamedPropertyHandlerConfiguration{
|
||||
// This is really cool. Without this, we'd intercept _all_ properties
|
||||
// even those explicitly set. So, node.length for example would get routed
|
||||
// to our `named_get`, rather than a `get_length`. This might be
|
||||
// useful if we run into a type that we can't model properly in Zig.
|
||||
.flags = v8.PropertyHandlerFlags.OnlyInterceptStrings | v8.PropertyHandlerFlags.NonMasking,
|
||||
};
|
||||
|
||||
if (@hasDecl(Struct, "named_get")) {
|
||||
has_one = true;
|
||||
configuration.getter = struct {
|
||||
if (@hasDecl(Struct, "named_get") == false) {
|
||||
return;
|
||||
}
|
||||
const configuration = v8.NamedPropertyHandlerConfiguration{
|
||||
.getter = struct {
|
||||
fn callback(c_name: ?*const v8.C_Name, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
var caller = Caller(Self).init(info);
|
||||
@@ -612,29 +596,23 @@ pub fn Env(comptime S: type, comptime types: anytype) type {
|
||||
caller.handleError(named_function, err, info);
|
||||
};
|
||||
}
|
||||
}.callback;
|
||||
}
|
||||
}.callback,
|
||||
|
||||
if (@hasDecl(Struct, "named_set")) {
|
||||
has_one = true;
|
||||
configuration.setter = struct {
|
||||
fn callback(c_name: ?*const v8.C_Name, raw_value: ?*const v8.C_Value, raw_info: ?*const v8.C_PropertyCallbackInfo) callconv(.c) void {
|
||||
const info = v8.PropertyCallbackInfo.initFromV8(raw_info);
|
||||
var caller = Caller(Self).init(info);
|
||||
defer caller.deinit();
|
||||
// This is really cool. Without this, we'd intercept _all_ properties
|
||||
// even those explicitly set. So, node.length for example would get routed
|
||||
// to our `named_get`, rather than a `get_length`. This might be
|
||||
// useful if we run into a type that we can't model properly in Zig.
|
||||
.flags = v8.PropertyHandlerFlags.OnlyInterceptStrings | v8.PropertyHandlerFlags.NonMasking,
|
||||
};
|
||||
|
||||
const js_value = v8.Value{ .handle = raw_value.? };
|
||||
const named_function = NamedFunction(Struct, Struct.named_set, "named_set"){};
|
||||
caller.setNamedIndex(named_function, .{ .handle = c_name.? }, js_value, info) catch |err| {
|
||||
caller.handleError(named_function, err, info);
|
||||
};
|
||||
}
|
||||
}.callback;
|
||||
}
|
||||
|
||||
if (has_one) {
|
||||
template_proto.setNamedProperty(configuration, null);
|
||||
}
|
||||
// If you're trying to implement setter, read:
|
||||
// https://groups.google.com/g/v8-users/c/8tahYBsHpgY/m/IteS7Wn2AAAJ
|
||||
// The issue I had was
|
||||
// (a) where to attache it: does it go ont he instance_template
|
||||
// instead of the prototype?
|
||||
// (b) defining the getter or query to respond with the
|
||||
// PropertyAttribute to indicate if the property can be set
|
||||
template_proto.setNamedProperty(configuration, null);
|
||||
}
|
||||
|
||||
// Turns a Zig value into a JS one.
|
||||
@@ -1508,41 +1486,6 @@ fn Caller(comptime E: type) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn setIndex(self: *Self, comptime named_function: anytype, idx: u32, js_value: v8.Value, info: v8.PropertyCallbackInfo) !void {
|
||||
const S = named_function.S;
|
||||
comptime assertSelfReceiver(named_function);
|
||||
|
||||
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis());
|
||||
|
||||
const IndexedSet = @TypeOf(named_function.func);
|
||||
var args: ParamterTypes(IndexedSet) = undefined;
|
||||
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
|
||||
switch (arg_fields.len) {
|
||||
0, 1, 2 => @compileError(named_function.full_name ++ " must take at least a u32 parameter and a value"),
|
||||
3, 4 => {
|
||||
@field(args, "0") = zig_instance;
|
||||
@field(args, "1") = idx;
|
||||
@field(args, "2") = try self.jsValueToZig(named_function, arg_fields[2].type, js_value);
|
||||
if (comptime arg_fields.len == 4) {
|
||||
comptime assertIsStateArg(named_function, 3);
|
||||
@field(args, "3") = self.executor.state;
|
||||
}
|
||||
},
|
||||
else => @compileError(named_function.full_name ++ " has too many parmaters"),
|
||||
}
|
||||
|
||||
switch (@typeInfo(@typeInfo(IndexedSet).@"fn".return_type.?)) {
|
||||
.error_union => |eu| {
|
||||
if (eu.payload == void) {
|
||||
return @call(.auto, S.indexed_set, args);
|
||||
}
|
||||
},
|
||||
.void => return @call(.auto, S.indexed_set, args),
|
||||
else => {},
|
||||
}
|
||||
@compileError(named_function.full_name ++ " cannot have a return type");
|
||||
}
|
||||
|
||||
fn getNamedIndex(self: *Self, comptime named_function: anytype, name: v8.Name, info: v8.PropertyCallbackInfo) !void {
|
||||
const S = named_function.S;
|
||||
const NamedGet = @TypeOf(named_function.func);
|
||||
@@ -1579,41 +1522,6 @@ fn Caller(comptime E: type) type {
|
||||
}
|
||||
}
|
||||
|
||||
fn setNamedIndex(self: *Self, comptime named_function: anytype, name: v8.Name, js_value: v8.Value, info: v8.PropertyCallbackInfo) !void {
|
||||
const S = named_function.S;
|
||||
comptime assertSelfReceiver(named_function);
|
||||
|
||||
const zig_instance = try E.typeTaggedAnyOpaque(named_function, *Receiver(S), info.getThis());
|
||||
|
||||
const IndexedSet = @TypeOf(named_function.func);
|
||||
var args: ParamterTypes(IndexedSet) = undefined;
|
||||
const arg_fields = @typeInfo(@TypeOf(args)).@"struct".fields;
|
||||
switch (arg_fields.len) {
|
||||
0, 1, 2 => @compileError(named_function.full_name ++ " must take at least an u32 parameter and a value"),
|
||||
3, 4 => {
|
||||
@field(args, "0") = zig_instance;
|
||||
@field(args, "1") = try self.nameToString(name);
|
||||
@field(args, "2") = try self.jsValueToZig(named_function, arg_fields[2].type, js_value);
|
||||
if (comptime arg_fields.len == 4) {
|
||||
comptime assertIsStateArg(named_function, 3);
|
||||
@field(args, "3") = self.executor.state;
|
||||
}
|
||||
},
|
||||
else => @compileError(named_function.full_name ++ " has too many parmaters"),
|
||||
}
|
||||
|
||||
switch (@typeInfo(@typeInfo(IndexedSet).@"fn".return_type.?)) {
|
||||
.error_union => |eu| {
|
||||
if (eu.payload == void) {
|
||||
return @call(.auto, S.named_set, args);
|
||||
}
|
||||
},
|
||||
.void => return @call(.auto, S.named_set, args),
|
||||
else => {},
|
||||
}
|
||||
@compileError(named_function.full_name ++ " cannot have a return type");
|
||||
}
|
||||
|
||||
fn nameToString(self: *Self, name: v8.Name) ![]const u8 {
|
||||
return valueToString(self.call_allocator, .{ .handle = name.handle }, self.isolate, self.context);
|
||||
}
|
||||
@@ -2295,4 +2203,5 @@ fn getTaggedAnyOpaque(value: v8.Value) ?*TaggedAnyOpaque {
|
||||
test {
|
||||
std.testing.refAllDecls(@import("test_primitive_types.zig"));
|
||||
std.testing.refAllDecls(@import("test_complex_types.zig"));
|
||||
std.testing.refAllDecls(@import("test_object_types.zig"));
|
||||
}
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright 2023-2024 Lightpanda (Selecy SAS)
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const MemoryPool = std.heap.MemoryPool;
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
// Copyright 2023-2024 Lightpanda (Selecy SAS)
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
121
src/runtime/test_object_types.zig
Normal file
121
src/runtime/test_object_types.zig
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const Other = struct {
|
||||
val: u8,
|
||||
|
||||
fn init(val: u8) Other {
|
||||
return .{ .val = val };
|
||||
}
|
||||
|
||||
pub fn _val(self: *const Other) u8 {
|
||||
return self.val;
|
||||
}
|
||||
};
|
||||
|
||||
pub const OtherUnion = union(enum) {
|
||||
Other: Other,
|
||||
Bool: bool,
|
||||
};
|
||||
|
||||
pub const MyObject = struct {
|
||||
val: bool,
|
||||
|
||||
pub fn constructor(do_set: bool) MyObject {
|
||||
return .{.val = do_set,};
|
||||
}
|
||||
|
||||
pub fn named_get(_: *const MyObject, name: []const u8, has_value: *bool) ?OtherUnion {
|
||||
if (std.mem.eql(u8, name, "a")) {
|
||||
has_value.* = true;
|
||||
return .{.Other = .{.val = 4}};
|
||||
}
|
||||
if (std.mem.eql(u8, name, "c")) {
|
||||
has_value.* = true;
|
||||
return .{.Bool = true};
|
||||
}
|
||||
has_value.* = false;
|
||||
return null;
|
||||
}
|
||||
|
||||
pub fn get_val(self: *const MyObject) bool {
|
||||
return self.val;
|
||||
}
|
||||
|
||||
pub fn set_val(self: *MyObject, val: bool) void {
|
||||
self.val = val;
|
||||
}
|
||||
};
|
||||
|
||||
pub const MyAPI = struct {
|
||||
pub fn constructor() MyAPI {
|
||||
return .{};
|
||||
}
|
||||
|
||||
pub fn _obj(_: *const MyAPI) !MyObject {
|
||||
return MyObject.constructor(true);
|
||||
}
|
||||
};
|
||||
|
||||
const State = struct {
|
||||
arena: Allocator,
|
||||
};
|
||||
|
||||
const testing = @import("testing.zig");
|
||||
test "JS: object types" {
|
||||
var arena = std.heap.ArenaAllocator.init(testing.allocator);
|
||||
defer arena.deinit();
|
||||
|
||||
var runner = try testing.Runner(State, void, .{
|
||||
Other,
|
||||
MyObject,
|
||||
MyAPI,
|
||||
}).init(.{ .arena = arena.allocator() }, {});
|
||||
|
||||
defer runner.deinit();
|
||||
|
||||
// v8 has 5 default "own" properties
|
||||
const own_base = "5";
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "Object.getOwnPropertyNames(MyObject).length;", own_base },
|
||||
.{ "let myObj = new MyObject(true);", "undefined" },
|
||||
// check object property
|
||||
.{ "myObj.a.val()", "4" },
|
||||
.{ "myObj.b", "undefined" },
|
||||
.{ "Object.getOwnPropertyNames(myObj).length;", "0" },
|
||||
|
||||
// check if setter (pointer) still works
|
||||
.{ "myObj.val", "true" },
|
||||
.{ "myObj.val = false", "false" },
|
||||
.{ "myObj.val", "false" },
|
||||
|
||||
.{ "let myObj2 = new MyObject(false);", "undefined" },
|
||||
.{ "myObj2.c", "true" },
|
||||
}, .{});
|
||||
|
||||
try runner.testCases(&.{
|
||||
.{ "let myAPI = new MyAPI();", "undefined" },
|
||||
.{ "let myObjIndirect = myAPI.obj();", "undefined" },
|
||||
// check object property
|
||||
.{ "myObjIndirect.a.val()", "4" },
|
||||
}, .{});
|
||||
}
|
||||
@@ -1,3 +1,21 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
|
||||
// TODO: use functions instead of "fake" struct once we handle function API generation
|
||||
|
||||
@@ -1,3 +1,21 @@
|
||||
// Copyright (C) 2023-2024 Lightpanda (Selecy SAS)
|
||||
//
|
||||
// Francis Bouvier <francis@lightpanda.io>
|
||||
// Pierre Tachoire <pierre@lightpanda.io>
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public License as
|
||||
// published by the Free Software Foundation, either version 3 of the
|
||||
// License, or (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
const std = @import("std");
|
||||
const js = @import("js.zig");
|
||||
const generate = @import("generate.zig");
|
||||
|
||||
Reference in New Issue
Block a user