From 15a4d308db9f6981dd67967f9e34f11ae5177ddc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Mar 2022 17:57:04 +0100 Subject: [PATCH 001/527] Initial version of LuaEngine, custom math lib and vector lib --- Cargo.toml | 19 ++- src/engine.rs | 111 ++++++++++++++ src/ext.rs | 111 ++++++++++++++ src/lib.rs | 14 +- src/math.rs | 84 +++++++++++ src/number.rs | 128 ++++++++++++++++ src/vector.rs | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 855 insertions(+), 7 deletions(-) create mode 100644 src/engine.rs create mode 100644 src/ext.rs create mode 100644 src/math.rs create mode 100644 src/number.rs create mode 100644 src/vector.rs diff --git a/Cargo.toml b/Cargo.toml index 35fff1b..88f3aec 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,25 @@ [package] -name = "test" +name = "bp3d-lua" version = "0.1.0" authors = ["Yuri Edward "] -edition = "2018" -description = "" +edition = "2021" +description = "Lua base library and tools for BP3D." license = "BSD-3-Clause" -repository = "https://gitlab.com/bp3d/test" +repository = "https://gitlab.com/bp3d/lua" readme = "./README.MD" -keywords = [] +keywords = ["lua"] categories = [] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +nalgebra = { version = "0.30.1", optional = true } +rlua = "0.19.1" [features] - +math = [] +vector = ["nalgebra"] +point = ["nalgebra"] +scale = ["nalgebra"] +quaternion = ["nalgebra"] +matrix = ["nalgebra"] diff --git a/src/engine.rs b/src/engine.rs new file mode 100644 index 0000000..28fc27a --- /dev/null +++ b/src/engine.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use rlua::{Context, FromLuaMulti, Lua, StdLib, Table, ToLua, ToLuaMulti, Value}; + +macro_rules! auto_lib { + ($lua: ident ($lib: ident, $self_callable: expr) { $($name: ident: $fn: ident,)* }) => { + $lua.create_library($lib, $self_callable, |ctx| { + $( + ctx.function(stringify!($name), $fn)?; + )* + Ok(()) + }) + }; +} + +pub(crate) use auto_lib; +use crate::TableExt; + +pub struct LibContext<'a> { + ctx: Context<'a>, + table: Table<'a> +} + +impl<'a> LibContext<'a> { + pub fn function, R: ToLuaMulti<'a>, F: 'static + Send + Fn(Context, A) -> rlua::Result>(&self, name: &str, function: F) -> rlua::Result<()> { + let func = self.ctx.create_function(function)?; + self.table.raw_set(name, func)?; + Ok(()) + } + + pub fn constant>(&self, name: &str, val: T) -> rlua::Result<()> { + self.table.raw_set(name, val) + } +} + +pub struct LuaEngine { + state: Lua +} + +fn strip_potentially_dangerous(state: &Lua) -> rlua::Result<()> { + state.context(|ctx| { + let globals = ctx.globals(); + //Basically all these functions allows arbitrary lua code injection without source checking. + globals.raw_set("dofile", Value::Nil)?; + globals.raw_set("load", Value::Nil)?; + globals.raw_set("loadfile", Value::Nil)?; + globals.raw_set("loadstring", Value::Nil)?; + Ok(()) + }) +} + +impl LuaEngine { + pub fn new() -> rlua::Result { + let state = Lua::new_with(StdLib::BASE | StdLib::UTF8 | StdLib::STRING | StdLib::TABLE | StdLib::COROUTINE); + strip_potentially_dangerous(&state)?; + Ok(LuaEngine { + state + }) + } + + pub fn create_library rlua::Result<()>>(&self, name: &str, self_callable: bool, function: F) -> rlua::Result<()> { + self.state.context(|ctx| { + let table = ctx.create_table()?; + let libctx = LibContext { + ctx, + table + }; + function(&libctx)?; + if self_callable { + libctx.table.enable_self_callable()?; + } + let globals = libctx.ctx.globals(); + globals.raw_set(name, libctx.table)?; + Ok(()) + }) + } + + pub fn context rlua::Result>(&self, function: F) -> rlua::Result { + self.state.context(function) + } + + //TODO: quaternion library + //TODO: matrix library + //TODO: vector library +} diff --git a/src/ext.rs b/src/ext.rs new file mode 100644 index 0000000..9fb86d3 --- /dev/null +++ b/src/ext.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This module contains lua engine extensions, some of which existed in the C API. + +// These functions are non standard but used to optimize 3D engines where access to numbers +// MUST be VERY fast. + +use rlua::{Integer, Number, Table, Value, Result, Error}; + +pub trait ValueExt<'a> { + fn check_number(self) -> Result; + fn check_integer(self) -> Result; + fn check_table(self) -> Result>; + fn check_bool(self) -> Result; + fn check_string(&self) -> Result<&str>; +} + +impl<'a> ValueExt<'a> for Value<'a> { + fn check_number(self) -> Result { + match self { + Value::Number(v) => Ok(v), + Value::Integer(v) => Ok(v as Number), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "Number", + message: Some("expected number".to_string()), + }) + } + } + + fn check_integer(self) -> Result { + match self { + Value::Integer(v) => Ok(v), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "Integer", + message: Some("expected integer".to_string()), + }) + } + } + + fn check_table(self) -> Result> { + match self { + Value::Table(v) => Ok(v), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "Table", + message: Some("expected table".to_string()), + }) + } + } + + fn check_bool(self) -> Result { + match self { + Value::Boolean(v) => Ok(v), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "Boolean", + message: Some("expected boolean".to_string()), + }) + } + } + + fn check_string(&self) -> Result<&str> { + match self { + Value::String(v) => v.to_str(), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "String", + message: Some("expected string".to_string()), + }) + } + } +} + +pub trait TableExt { + fn enable_self_callable(&self) -> rlua::Result<()>; +} + +impl<'a> TableExt for Table<'a> { + fn enable_self_callable(&self) -> rlua::Result<()> { + let selfcopy = self.clone(); + self.raw_set("__index", selfcopy) + } +} diff --git a/src/lib.rs b/src/lib.rs index 250e249..8c26c58 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2021, BlockProject 3D +// Copyright (c) 2022, BlockProject 3D // // All rights reserved. // @@ -26,3 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod engine; +mod ext; +pub mod number; + +#[cfg(feature = "math")] +pub mod math; + +#[cfg(feature = "vector")] +pub mod vector; + +pub use engine::*; +pub use ext::*; diff --git a/src/math.rs b/src/math.rs new file mode 100644 index 0000000..dc4833a --- /dev/null +++ b/src/math.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use rlua::{Context, Number}; +use crate::LuaEngine; +use crate::number::{Int, Num}; + +fn math_clamp(_: Context, (x, min, max): (Num, Num, Num)) -> rlua::Result { + if x.0 > max.0 { + Ok(max) + } else if x.0 < min.0 { + Ok(min) + } else { + Ok(x) + } +} + +fn math_round(_: Context, (x, decimals): (Num, Int)) -> rlua::Result { + let power = u32::pow(10, decimals.0 as _) as Number; + Ok((x.0 * power).round() / power) +} + +fn math_gaussian(_: Context, (sigma, x): (Num, Num)) -> rlua::Result { + let term1 = 1.0 / 2.0 * std::f64::consts::PI * (sigma.0 * sigma.0); + let term2 = (-(x.0 / (2.0 * sigma.0 * sigma.0))).exp(); + Ok(term1 * term2) +} + +// The reason why we provide a custom math lib is to have on par implementation with rust +// and nalgebra, required for accurate rendering with the engine. +pub trait Lib { + fn load_math(&self) -> rlua::Result<()>; +} + +impl Lib for LuaEngine { + fn load_math(&self) -> rlua::Result<()> { + self.create_library("math", false, |ctx| { + ctx.constant("PI", std::f64::consts::PI)?; + ctx.constant("E", std::f64::consts::E)?; + ctx.function("cos", |_, x: Num| Ok(x.0.cos()))?; + ctx.function("sin", |_, x: Num| Ok(x.0.sin()))?; + ctx.function("tan", |_, x: Num| Ok(x.0.tan()))?; + ctx.function("acos", |_, x: Num| Ok(x.0.acos()))?; + ctx.function("asin", |_, x: Num| Ok(x.0.asin()))?; + ctx.function("atan", |_, x: Num| Ok(x.0.atan()))?; + ctx.function("atan2", |_, (x, y): (Num, Num)| Ok(x.0.atan2(y.0)))?; + ctx.function("degrees", |_, x: Num| Ok(x.0.to_degrees()))?; + ctx.function("radians", |_, x: Num| Ok(x.0.to_radians()))?; + ctx.function("floor", |_, x: Num| Ok(x.0.floor()))?; + ctx.function("ceil", |_, x: Num| Ok(x.0.ceil()))?; + ctx.function("round", |_, x: Num| Ok(x.0.round()))?; + ctx.function("round2", math_round)?; + ctx.function("pow", |_, (x, n): (Num, Num)| Ok(x.0.powf(n.0)))?; + ctx.function("clamp", math_clamp)?; + ctx.function("gaussian2d", math_gaussian)?; + Ok(()) + }) + } +} diff --git a/src/number.rs b/src/number.rs new file mode 100644 index 0000000..4e4e67c --- /dev/null +++ b/src/number.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! Fast implementation of numbers and integers. + +use rlua::{Context, FromLua, Integer, Number, ToLua, Value}; +use crate::ValueExt; + +#[derive(Copy, Clone, Debug, Default)] +pub struct Num(pub Number); + +// Well I've looked at the implementation and extra cpu time pumped at useless ifs do not +// look good in 3D engines! +// So here we go let's get rid of these useless ifs... +// The reason why they're useless is because of the fact we're directly reading lua numbers (f64). +impl<'lua> FromLua<'lua> for Num { + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + lua_value.check_number().map(Num) + } +} + +// Here the implementation is the same. This means that for sending to lua using with Number +// or FastNumber is fine. +impl<'lua> ToLua<'lua> for Num { + fn to_lua(self, _: Context<'lua>) -> rlua::Result> { + Ok(Value::Number(self.0)) + } +} + +#[derive(Copy, Clone, Debug, Default)] +pub struct Int(pub Integer); + +// Well I've looked at the implementation and extra cpu time pumped at useless ifs do not +// look good in 3D engines! +// So here we go let's get rid of these useless ifs... +// The reason why they're useless is because of the fact we're directly reading lua numbers (f64). +impl<'lua> FromLua<'lua> for Int { + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + lua_value.check_integer().map(Int) + } +} + +// Again here we're skipping quite a lot of if blocks, which are not needed again because we're +// using native lua types. +impl<'lua> ToLua<'lua> for Int { + fn to_lua(self, _: Context<'lua>) -> rlua::Result> { + Ok(Value::Integer(self.0)) + } +} + +// These implementation are possibly truncating numbers; the use case being rendering engines, +// precision doesn't matter that much. + +pub trait NumToLua<'a> { + fn num_to_lua(self) -> Value<'a>; +} + +pub trait NumFromLua<'a> where Self: Sized { + fn num_from_lua(val: Value<'a>) -> rlua::Result; +} + +macro_rules! impl_num_float { + ($($target: ty)*) => { + $( + impl<'a> NumToLua<'a> for $target { + fn num_to_lua(self) -> Value<'a> { + Value::Number(self as Number) + } + } + + impl<'a> NumFromLua<'a> for $target { + fn num_from_lua(val: Value<'a>) -> rlua::Result { + val.check_number().map(|v| v as $target) + } + } + )* + }; +} + +macro_rules! impl_num_int { + ($($target: ty)*) => { + $( + impl<'a> NumToLua<'a> for $target { + fn num_to_lua(self) -> Value<'a> { + Value::Integer(self as Integer) + } + } + + impl<'a> NumFromLua<'a> for $target { + fn num_from_lua(val: Value<'a>) -> rlua::Result { + val.check_integer().map(|v| v as $target) + } + } + )* + }; +} + +impl_num_float!(f32 f64); + +impl_num_int!( + i8 i16 i32 i64 + u8 u16 u32 u64 +); diff --git a/src/vector.rs b/src/vector.rs new file mode 100644 index 0000000..0eaa75c --- /dev/null +++ b/src/vector.rs @@ -0,0 +1,395 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use nalgebra::{Vector2, Vector3, Vector4}; +use rlua::{Context, FromLua, Function, Number, ToLua, Value}; +use crate::{LuaEngine, ValueExt}; +use crate::number::{Num, Int, NumFromLua, NumToLua}; + +pub trait Lib { + fn load_vec2(&self) -> rlua::Result<()>; + fn load_vec3(&self) -> rlua::Result<()>; + fn load_vec4(&self) -> rlua::Result<()>; +} + +const VEC2_LIB: &str = "vec2"; +const VEC3_LIB: &str = "vec3"; +const VEC4_LIB: &str = "vec4"; + +const VEC2_NEW: &str = "Vec2"; +const VEC3_NEW: &str = "Vec3"; +const VEC4_NEW: &str = "Vec4"; + +pub struct LuaVec2(Vector2); + +impl From> for LuaVec2 { + fn from(v: Vector2) -> Self { + Self(v) + } +} + +impl From> for Vector2 { + fn from(v: LuaVec2) -> Self { + v.0 + } +} + +impl LuaVec2 { + pub fn new(v: Vector2) -> LuaVec2 { + Self(v) + } + + pub fn into_inner(self) -> Vector2 { + self.0 + } +} + +impl<'lua, T> ToLua<'lua> for LuaVec2 + where T: NumToLua<'lua> +{ + fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { + let func: Function = lua.globals().raw_get(VEC2_NEW)?; + let [[x, y]] = self.0.data.0; + func.call((x.num_to_lua(), y.num_to_lua())) + } +} + +impl<'lua, T> FromLua<'lua> for LuaVec2 + where T: NumFromLua<'lua> +{ + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + let table = lua_value.check_table()?; + Ok(LuaVec2(nalgebra::Vector2::new( + T::num_from_lua(table.raw_get("x")?)?, + T::num_from_lua(table.raw_get("y")?)? + ))) + } +} + +pub struct LuaVec3(Vector3); + +impl From> for LuaVec3 { + fn from(v: Vector3) -> Self { + Self(v) + } +} + +impl From> for Vector3 { + fn from(v: LuaVec3) -> Self { + v.0 + } +} + +impl LuaVec3 { + pub fn new(v: Vector3) -> LuaVec3 { + Self(v) + } + + pub fn into_inner(self) -> Vector3 { + self.0 + } +} + +impl<'lua, T> ToLua<'lua> for LuaVec3 + where T: NumToLua<'lua> +{ + fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { + let func: Function = lua.globals().raw_get(VEC3_NEW)?; + let [[x, y, z]] = self.0.data.0; + func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua())) + } +} + +impl<'lua, T> FromLua<'lua> for LuaVec3 + where T: NumFromLua<'lua> +{ + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + let table = lua_value.check_table()?; + Ok(LuaVec3(nalgebra::Vector3::new( + T::num_from_lua(table.raw_get("x")?)?, + T::num_from_lua(table.raw_get("y")?)?, + T::num_from_lua(table.raw_get("z")?)? + ))) + } +} + +pub struct LuaVec4(Vector4); + +impl From> for LuaVec4 { + fn from(v: Vector4) -> Self { + Self(v) + } +} + +impl From> for Vector4 { + fn from(v: LuaVec4) -> Self { + v.0 + } +} + +impl LuaVec4 { + pub fn new(v: Vector4) -> LuaVec4 { + Self(v) + } + + pub fn into_inner(self) -> Vector4 { + self.0 + } +} + +impl<'lua, T> ToLua<'lua> for LuaVec4 + where T: NumToLua<'lua> +{ + fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { + let func: Function = lua.globals().raw_get(VEC4_NEW)?; + let [[x, y, z, w]] = self.0.data.0; + func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua(), w.num_to_lua())) + } +} + +impl<'lua, T> FromLua<'lua> for LuaVec4 + where T: NumFromLua<'lua> +{ + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + let table = lua_value.check_table()?; + Ok(LuaVec4(nalgebra::Vector4::new( + T::num_from_lua(table.raw_get("x")?)?, + T::num_from_lua(table.raw_get("y")?)?, + T::num_from_lua(table.raw_get("z")?)?, + T::num_from_lua(table.raw_get("w")?)? + ))) + } +} + +type Vec2 = LuaVec2; +type Vec3 = LuaVec3; +type Vec4 = LuaVec4; + +macro_rules! vec_wrapper { + ($name: ident ($this: ident $other: ident): $in: ty => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, other): ($in, $in)) -> rlua::Result<$out> { + let $this = this.into_inner(); + let $other = other.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_unary { + ($name: ident ($this: ident): $in: ty => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, this: $in) -> rlua::Result<$out> { + let $this = this.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_lerp { + ($name: ident (($this: ident $other: ident): $in: ty, $val_name: ident: $val: ty) => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, other, $val_name): ($in, $in, $val)) -> rlua::Result<$out> { + let $this = this.into_inner(); + let $other = other.into_inner(); + Ok($code) + } + }; +} + +fn argminmax_to_lua((id, val): (usize, Number)) -> (Int, Num) { + (Int(id as _), Num(val)) +} + +vec_wrapper!(vec2_add (a b): Vec2 => Vec2 {(a + b).into()}); +vec_wrapper!(vec2_sub (a b): Vec2 => Vec2 {(a - b).into()}); +vec_wrapper!(vec2_mul (a b): Vec2 => Vec2 {a.component_mul(&b).into()}); +vec_wrapper!(vec2_div (a b): Vec2 => Vec2 {a.component_div(&b).into()}); +vec_wrapper!(vec2_le (a b): Vec2 => bool {a <= b}); +vec_wrapper!(vec2_lt (a b): Vec2 => bool {a < b}); +vec_wrapper!(vec2_eq (a b): Vec2 => bool {a == b}); +vec_wrapper_unary!(vec2_unm (a): Vec2 => Vec2 {(-a).into()}); +vec_wrapper!(vec2_dot (a b): Vec2 => Number {a.dot(&b)}); +vec_wrapper!(vec2_cross (a b): Vec2 => Vec2 {a.cross(&b).into()}); +vec_wrapper_unary!(vec2_norm (a): Vec2 => Number {a.norm()}); +vec_wrapper_unary!(vec2_norm_squared (a): Vec2 => Number {a.norm_squared()}); +vec_wrapper_unary!(vec2_argmin (a): Vec2 => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_unary!(vec2_argmax (a): Vec2 => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_lerp!(vec2_lerp ((a b): Vec2, f: Num) => Vec2 {a.lerp(&b, f.0).into()}); +vec_wrapper_lerp!(vec2_slerp ((a b): Vec2, f: Num) => Vec2 {a.slerp(&b, f.0).into()}); + +vec_wrapper!(vec3_add (a b): Vec3 => Vec3 {(a + b).into()}); +vec_wrapper!(vec3_sub (a b): Vec3 => Vec3 {(a - b).into()}); +vec_wrapper!(vec3_mul (a b): Vec3 => Vec3 {a.component_mul(&b).into()}); +vec_wrapper!(vec3_div (a b): Vec3 => Vec3 {a.component_div(&b).into()}); +vec_wrapper!(vec3_le (a b): Vec3 => bool {a <= b}); +vec_wrapper!(vec3_lt (a b): Vec3 => bool {a < b}); +vec_wrapper!(vec3_eq (a b): Vec3 => bool {a == b}); +vec_wrapper_unary!(vec3_unm (a): Vec3 => Vec3 {(-a).into()}); +vec_wrapper!(vec3_dot (a b): Vec3 => Number {a.dot(&b)}); +vec_wrapper!(vec3_cross (a b): Vec3 => Vec3 {a.cross(&b).into()}); +vec_wrapper_unary!(vec3_norm (a): Vec3 => Number {a.norm()}); +vec_wrapper_unary!(vec3_norm_squared (a): Vec3 => Number {a.norm_squared()}); +vec_wrapper_unary!(vec3_argmin (a): Vec3 => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_unary!(vec3_argmax (a): Vec3 => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_lerp!(vec3_lerp ((a b): Vec3, f: Num) => Vec3 {a.lerp(&b, f.0).into()}); +vec_wrapper_lerp!(vec3_slerp ((a b): Vec3, f: Num) => Vec3 {a.slerp(&b, f.0).into()}); + +vec_wrapper!(vec4_add (a b): Vec4 => Vec4 {(a + b).into()}); +vec_wrapper!(vec4_sub (a b): Vec4 => Vec4 {(a - b).into()}); +vec_wrapper!(vec4_mul (a b): Vec4 => Vec4 {a.component_mul(&b).into()}); +vec_wrapper!(vec4_div (a b): Vec4 => Vec4 {a.component_div(&b).into()}); +vec_wrapper!(vec4_le (a b): Vec4 => bool {a <= b}); +vec_wrapper!(vec4_lt (a b): Vec4 => bool {a < b}); +vec_wrapper!(vec4_eq (a b): Vec4 => bool {a == b}); +vec_wrapper_unary!(vec4_unm (a): Vec4 => Vec4 {(-a).into()}); +vec_wrapper!(vec4_dot (a b): Vec4 => Number {a.dot(&b)}); +vec_wrapper!(vec4_cross (a b): Vec4 => Vec4 {a.cross(&b).into()}); +vec_wrapper_unary!(vec4_norm (a): Vec4 => Number {a.norm()}); +vec_wrapper_unary!(vec4_norm_squared (a): Vec4 => Number {a.norm_squared()}); +vec_wrapper_unary!(vec4_argmin (a): Vec4 => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_unary!(vec4_argmax (a): Vec4 => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_lerp!(vec4_lerp ((a b): Vec4, f: Num) => Vec4 {a.lerp(&b, f.0).into()}); +vec_wrapper_lerp!(vec4_slerp ((a b): Vec4, f: Num) => Vec4 {a.slerp(&b, f.0).into()}); + +impl Lib for LuaEngine { + fn load_vec2(&self) -> rlua::Result<()> { + //Create the metatable. + crate::engine::auto_lib!(self (VEC2_LIB, true) { + __add: vec2_add, __sub: vec2_sub, __mul: vec2_mul, __div: vec2_div, + __le: vec2_le, __lt: vec2_lt, __eq: vec2_eq, __unm: vec2_unm, + dot: vec2_dot, cross: vec2_cross, norm: vec2_norm, normSquared: vec2_norm_squared, + argmin: vec2_argmin, argmax: vec2_argmax, lerp: vec2_lerp, slerp: vec2_slerp, + })?; + //Create constructor function. + self.context(|ctx| { + let function = ctx.create_function(|ctx, (x, y): (Num, Num)| { + let globals = ctx.globals(); + let table = ctx.create_table()?; + table.raw_set("x", x)?; + table.raw_set("y", y)?; + table.set_metatable(globals.raw_get(VEC2_LIB)?); + Ok(table) + })?; + let globals = ctx.globals(); + globals.raw_set(VEC2_NEW, function)?; + Ok(()) + }) + } + + fn load_vec3(&self) -> rlua::Result<()> { + //Create the metatable. + crate::engine::auto_lib!(self (VEC3_LIB, true) { + __add: vec3_add, __sub: vec3_sub, __mul: vec3_mul, __div: vec3_div, + __le: vec3_le, __lt: vec3_lt, __eq: vec3_eq, __unm: vec3_unm, + dot: vec3_dot, cross: vec3_cross, norm: vec3_norm, normSquared: vec3_norm_squared, + argmin: vec3_argmin, argmax: vec3_argmax, lerp: vec3_lerp, slerp: vec3_slerp, + })?; + //Create constructor function. + self.context(|ctx| { + let function = ctx.create_function(|ctx, (x, y, z): (Num, Num, Num)| { + let globals = ctx.globals(); + let table = ctx.create_table()?; + table.raw_set("x", x)?; + table.raw_set("y", y)?; + table.raw_set("z", z)?; + table.set_metatable(globals.raw_get(VEC3_LIB)?); + Ok(table) + })?; + let globals = ctx.globals(); + globals.raw_set(VEC3_NEW, function)?; + Ok(()) + }) + } + + fn load_vec4(&self) -> rlua::Result<()> { + //Create the metatable. + crate::engine::auto_lib!(self (VEC4_LIB, true) { + __add: vec4_add, __sub: vec4_sub, __mul: vec4_mul, __div: vec4_div, + __le: vec4_le, __lt: vec4_lt, __eq: vec4_eq, __unm: vec4_unm, + dot: vec4_dot, cross: vec4_cross, norm: vec4_norm, normSquared: vec4_norm_squared, + argmin: vec4_argmin, argmax: vec4_argmax, lerp: vec4_lerp, slerp: vec4_slerp, + })?; + //Create constructor function. + self.context(|ctx| { + let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Num, Num, Num)| { + let globals = ctx.globals(); + let table = ctx.create_table()?; + table.raw_set("x", x)?; + table.raw_set("y", y)?; + table.raw_set("z", z)?; + table.raw_set("w", w)?; + table.set_metatable(globals.raw_get(VEC4_LIB)?); + Ok(table) + })?; + let globals = ctx.globals(); + globals.raw_set(VEC4_NEW, function)?; + Ok(()) + }) + } +} + +#[cfg(test)] +mod tests { + use crate::LuaEngine; + use crate::vector::Lib; + + #[test] + fn basic() { + let engine = LuaEngine::new().unwrap(); + engine.load_vec2().unwrap(); + engine.context(|ctx| { + ctx.load(r#" + local v = Vec2(0, 0) + local v1 = Vec2(2.5, 2.5) + local add = v + v1 + local mul = v * v1 + local sub = v - v1 + local div = v1 / v + print(add.x, add.y) + print(mul.x, mul.y) + print(sub.x, sub.y) + print(div.x, div.y) + print((-div):norm()) + "#).exec()?; + Ok(()) + }).unwrap(); + } + + #[test] + fn mutate() { + let engine = LuaEngine::new().unwrap(); + engine.load_vec2().unwrap(); + engine.context(|ctx| { + ctx.load(r#" + local v = Vec2(0.1, 0.1) + v.x = 0 + v.y = 5 + print(v:argmax()) + "#).exec()?; + Ok(()) + }).unwrap(); + } +} From b480957015d51750d518f02e4e05a95a0cc89224 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Mar 2022 22:04:33 +0100 Subject: [PATCH 002/527] Added hyperbolic variants of trigonometric functions --- src/math.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/math.rs b/src/math.rs index dc4833a..4fbb9c9 100644 --- a/src/math.rs +++ b/src/math.rs @@ -68,6 +68,12 @@ impl Lib for LuaEngine { ctx.function("acos", |_, x: Num| Ok(x.0.acos()))?; ctx.function("asin", |_, x: Num| Ok(x.0.asin()))?; ctx.function("atan", |_, x: Num| Ok(x.0.atan()))?; + ctx.function("cosh", |_, x: Num| Ok(x.0.cosh()))?; + ctx.function("sinh", |_, x: Num| Ok(x.0.sinh()))?; + ctx.function("tanh", |_, x: Num| Ok(x.0.tanh()))?; + ctx.function("acosh", |_, x: Num| Ok(x.0.acosh()))?; + ctx.function("asinh", |_, x: Num| Ok(x.0.asinh()))?; + ctx.function("atanh", |_, x: Num| Ok(x.0.atanh()))?; ctx.function("atan2", |_, (x, y): (Num, Num)| Ok(x.0.atan2(y.0)))?; ctx.function("degrees", |_, x: Num| Ok(x.0.to_degrees()))?; ctx.function("radians", |_, x: Num| Ok(x.0.to_radians()))?; From c8eddbf7813199a57a9762453be67e849c87a96a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Mar 2022 22:24:25 +0100 Subject: [PATCH 003/527] Refactor of macros + Added initial version of quaternion lib --- Cargo.toml | 2 +- src/engine.rs | 14 ---- src/lib.rs | 4 ++ src/macros.rs | 91 +++++++++++++++++++++++++ src/quaternion.rs | 166 ++++++++++++++++++++++++++++++++++++++++++++++ src/vector.rs | 151 ++++++++++++++++++----------------------- 6 files changed, 328 insertions(+), 100 deletions(-) create mode 100644 src/macros.rs create mode 100644 src/quaternion.rs diff --git a/Cargo.toml b/Cargo.toml index 88f3aec..1f3535d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,5 +21,5 @@ math = [] vector = ["nalgebra"] point = ["nalgebra"] scale = ["nalgebra"] -quaternion = ["nalgebra"] +quaternion = ["vector"] matrix = ["nalgebra"] diff --git a/src/engine.rs b/src/engine.rs index 28fc27a..29e34cb 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -27,19 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use rlua::{Context, FromLuaMulti, Lua, StdLib, Table, ToLua, ToLuaMulti, Value}; - -macro_rules! auto_lib { - ($lua: ident ($lib: ident, $self_callable: expr) { $($name: ident: $fn: ident,)* }) => { - $lua.create_library($lib, $self_callable, |ctx| { - $( - ctx.function(stringify!($name), $fn)?; - )* - Ok(()) - }) - }; -} - -pub(crate) use auto_lib; use crate::TableExt; pub struct LibContext<'a> { @@ -107,5 +94,4 @@ impl LuaEngine { //TODO: quaternion library //TODO: matrix library - //TODO: vector library } diff --git a/src/lib.rs b/src/lib.rs index 8c26c58..65565d7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,6 +28,7 @@ mod engine; mod ext; +mod macros; pub mod number; #[cfg(feature = "math")] @@ -36,5 +37,8 @@ pub mod math; #[cfg(feature = "vector")] pub mod vector; +#[cfg(feature = "quaternion")] +pub mod quaternion; + pub use engine::*; pub use ext::*; diff --git a/src/macros.rs b/src/macros.rs new file mode 100644 index 0000000..6b6cd02 --- /dev/null +++ b/src/macros.rs @@ -0,0 +1,91 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +macro_rules! auto_lib { + ($lua: ident ($lib: ident, $self_callable: expr) { $($name: ident: $fn: ident,)* }) => { + $lua.create_library($lib, $self_callable, |ctx| { + $( + ctx.function(stringify!($name), $fn)?; + )* + Ok(()) + }) + }; +} + +macro_rules! vec_wrapper_2 { + ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty) => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, $other): ($in, $in2)) -> rlua::Result<$out> { + let $this = this.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_3 { + ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty) => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, $other, $other2): ($in, $in2, $in3)) -> rlua::Result<$out> { + let $this = this.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_4 { + ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty, $other3: ident: $in4: ty) => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, $other, $other2, $other3): ($in, $in2, $in3, $in4)) -> rlua::Result<$out> { + let $this = this.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_1 { + ($name: ident ($this: ident: $in: ty) => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, this: $in) -> rlua::Result<$out> { + let $this = this.into_inner(); + Ok($code) + } + }; +} + +macro_rules! vec_wrapper_2_uniform { + ($name: ident ($this: ident, $other: ident): $in: ty => $out: ty { $code: expr }) => { + fn $name<'a>(_: Context<'a>, (this, other): ($in, $in)) -> rlua::Result<$out> { + let $this = this.into_inner(); + let $other = other.into_inner(); + Ok($code) + } + }; +} + +pub(crate) use auto_lib; +pub(crate) use vec_wrapper_1; +pub(crate) use vec_wrapper_2; +pub(crate) use vec_wrapper_3; +pub(crate) use vec_wrapper_4; +pub(crate) use vec_wrapper_2_uniform; diff --git a/src/quaternion.rs b/src/quaternion.rs new file mode 100644 index 0000000..cbdce0e --- /dev/null +++ b/src/quaternion.rs @@ -0,0 +1,166 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use nalgebra::{Quaternion, UnitQuaternion}; +use rlua::{Context, FromLua, Function, Number, ToLua, UserData, Value}; +use crate::{LuaEngine, ValueExt}; +use crate::number::{NumFromLua, NumToLua, Num}; +use crate::macros::auto_lib; +use crate::macros::vec_wrapper_2; +use crate::macros::vec_wrapper_2_uniform; +use crate::macros::vec_wrapper_1; +use crate::macros::vec_wrapper_3; + +pub trait Lib { + fn load_quat(&self) -> rlua::Result<()>; +} + +const QUAT_LIB: &str = "quat"; + +const QUAT_NEW: &str = "Quat"; + +pub struct LuaQuat(Quaternion); + +impl From> for LuaQuat { + fn from(v: Quaternion) -> Self { + Self(v) + } +} + +impl From> for Quaternion { + fn from(v: LuaQuat) -> Self { + v.0 + } +} + +impl LuaQuat { + pub fn new(v: Quaternion) -> LuaQuat { + Self(v) + } + + pub fn into_inner(self) -> Quaternion { + self.0 + } +} + +impl<'lua, T> ToLua<'lua> for LuaQuat + where T: NumToLua<'lua> +{ + fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { + let func: Function = lua.globals().raw_get(QUAT_NEW)?; + let [[x, y, z, w]] = self.0.coords.data.0; + func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua(), w.num_to_lua())) + } +} + +impl<'lua, T> FromLua<'lua> for LuaQuat + where T: NumFromLua<'lua> +{ + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + let table = lua_value.check_table()?; + Ok(LuaQuat(Quaternion::new( + T::num_from_lua(table.raw_get("w")?)?, + T::num_from_lua(table.raw_get("x")?)?, + T::num_from_lua(table.raw_get("y")?)?, + T::num_from_lua(table.raw_get("z")?)? + ))) + } +} + +pub struct LuaUnitQuat(UnitQuaternion); + +pub(crate) type Quat = LuaQuat; + +vec_wrapper_2_uniform!(quat_eq (a, b): Quat => bool {a == b}); +vec_wrapper_2_uniform!(quat_add (a, b): Quat => Quat {(a + b).into()}); +vec_wrapper_2_uniform!(quat_sub (a, b): Quat => Quat {(a - b).into()}); +vec_wrapper_2_uniform!(quat_mul (a, b): Quat => Quat {(a * b).into()}); +vec_wrapper_2_uniform!(quat_dot (a, b): Quat => Number {a.dot(&b)}); +vec_wrapper_2_uniform!(quat_inner (a, b): Quat => Quat {a.inner(&b).into()}); +vec_wrapper_2_uniform!(quat_outer (a, b): Quat => Quat {a.outer(&b).into()}); +vec_wrapper_2_uniform!(quat_project (a, b): Quat => Option {a.project(&b).map(|v| v.into())}); +vec_wrapper_2_uniform!(quat_reject (a, b): Quat => Option {a.reject(&b).map(|v| v.into())}); +vec_wrapper_1!(quat_conjugate (a: Quat) => Quat {a.conjugate().into()}); +vec_wrapper_1!(quat_normalize (a: Quat) => Quat {a.normalize().into()}); +vec_wrapper_1!(quat_ln (a: Quat) => Quat {a.ln().into()}); +vec_wrapper_1!(quat_exp (a: Quat) => Quat {a.exp().into()}); +vec_wrapper_1!(quat_squared (a: Quat) => Quat {a.squared().into()}); +vec_wrapper_1!(quat_half (a: Quat) => Quat {a.half().into()}); +vec_wrapper_1!(quat_sqrt (a: Quat) => Quat {a.sqrt().into()}); +vec_wrapper_1!(quat_ispure (a: Quat) => bool {a.is_pure()}); +vec_wrapper_1!(quat_cos (a: Quat) => Quat {a.cos().into()}); +vec_wrapper_1!(quat_acos (a: Quat) => Quat {a.acos().into()}); +vec_wrapper_1!(quat_sin (a: Quat) => Quat {a.sin().into()}); +vec_wrapper_1!(quat_asin (a: Quat) => Quat {a.asin().into()}); +vec_wrapper_1!(quat_tan (a: Quat) => Quat {a.tan().into()}); +vec_wrapper_1!(quat_atan (a: Quat) => Quat {a.atan().into()}); +vec_wrapper_1!(quat_sinh (a: Quat) => Quat {a.sinh().into()}); +vec_wrapper_1!(quat_asinh (a: Quat) => Quat {a.asinh().into()}); +vec_wrapper_1!(quat_cosh (a: Quat) => Quat {a.cosh().into()}); +vec_wrapper_1!(quat_acosh (a: Quat) => Quat {a.acosh().into()}); +vec_wrapper_1!(quat_tanh (a: Quat) => Quat {a.tanh().into()}); +vec_wrapper_1!(quat_atanh (a: Quat) => Quat {a.atanh().into()}); +vec_wrapper_2!(quat_pow (a: Quat, n: Num) => Quat {a.powf(n.0).into()}); +vec_wrapper_3!(quat_lerp (a: Quat, b: Quat, f: Num) => Quat {a.lerp(&b.into_inner(), f.0).into()}); +vec_wrapper_1!(quat_imag (a: Quat) => crate::vector::Vec3 {a.imag().into()}); +vec_wrapper_1!(quat_scalar (a: Quat) => Number {a.scalar()}); +vec_wrapper_1!(quat_norm (a: Quat) => Number {a.norm()}); +vec_wrapper_1!(quat_norm_squared (a: Quat) => Number {a.norm_squared()}); +vec_wrapper_1!(quat_inverse (a: Quat) => Option {a.try_inverse().map(|v| v.into())}); + +impl Lib for LuaEngine { + fn load_quat(&self) -> rlua::Result<()> { + auto_lib!(self (QUAT_LIB, true) { + __add: quat_add, __sub: quat_sub, __mul: quat_mul, __eq: quat_eq, + dot: quat_dot, inner: quat_inner, outer: quat_outer, project: quat_project, + reject: quat_reject, conjugate: quat_conjugate, normalize: quat_normalize, ln: quat_ln, + exp: quat_exp, squared: quat_squared, half: quat_half, sqrt: quat_sqrt, + ispure: quat_ispure, cos: quat_cos, acos: quat_acos, sin: quat_sin, asin: quat_asin, + tan: quat_tan, atan: quat_atan, sinh: quat_sinh, asinh: quat_asinh, + cosh: quat_cosh, acosh: quat_acosh, tanh: quat_tanh, atanh: quat_atanh, + pow: quat_pow, lerp: quat_lerp, imag: quat_imag, scalar: quat_scalar, + norm: quat_norm, normSquared: quat_norm_squared, inverse: quat_inverse, + })?; + //Create constructor function. + self.context(|ctx| { + let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Num, Num, Num)| { + let globals = ctx.globals(); + let table = ctx.create_table()?; + table.raw_set("x", x)?; + table.raw_set("y", y)?; + table.raw_set("z", z)?; + table.raw_set("w", w)?; + table.set_metatable(globals.raw_get(QUAT_LIB)?); + Ok(table) + })?; + let globals = ctx.globals(); + globals.raw_set(QUAT_NEW, function)?; + Ok(()) + }) + } +} diff --git a/src/vector.rs b/src/vector.rs index 0eaa75c..2e6eb3b 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -30,6 +30,10 @@ use nalgebra::{Vector2, Vector3, Vector4}; use rlua::{Context, FromLua, Function, Number, ToLua, Value}; use crate::{LuaEngine, ValueExt}; use crate::number::{Num, Int, NumFromLua, NumToLua}; +use crate::macros::vec_wrapper_3; +use crate::macros::vec_wrapper_1; +use crate::macros::vec_wrapper_2_uniform; +use crate::macros::auto_lib; pub trait Lib { fn load_vec2(&self) -> rlua::Result<()>; @@ -186,102 +190,77 @@ impl<'lua, T> FromLua<'lua> for LuaVec4 } } -type Vec2 = LuaVec2; -type Vec3 = LuaVec3; -type Vec4 = LuaVec4; - -macro_rules! vec_wrapper { - ($name: ident ($this: ident $other: ident): $in: ty => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, other): ($in, $in)) -> rlua::Result<$out> { - let $this = this.into_inner(); - let $other = other.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_unary { - ($name: ident ($this: ident): $in: ty => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, this: $in) -> rlua::Result<$out> { - let $this = this.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_lerp { - ($name: ident (($this: ident $other: ident): $in: ty, $val_name: ident: $val: ty) => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, other, $val_name): ($in, $in, $val)) -> rlua::Result<$out> { - let $this = this.into_inner(); - let $other = other.into_inner(); - Ok($code) - } - }; -} +pub(crate) type Vec2 = LuaVec2; +pub(crate) type Vec3 = LuaVec3; +pub(crate) type Vec4 = LuaVec4; fn argminmax_to_lua((id, val): (usize, Number)) -> (Int, Num) { (Int(id as _), Num(val)) } -vec_wrapper!(vec2_add (a b): Vec2 => Vec2 {(a + b).into()}); -vec_wrapper!(vec2_sub (a b): Vec2 => Vec2 {(a - b).into()}); -vec_wrapper!(vec2_mul (a b): Vec2 => Vec2 {a.component_mul(&b).into()}); -vec_wrapper!(vec2_div (a b): Vec2 => Vec2 {a.component_div(&b).into()}); -vec_wrapper!(vec2_le (a b): Vec2 => bool {a <= b}); -vec_wrapper!(vec2_lt (a b): Vec2 => bool {a < b}); -vec_wrapper!(vec2_eq (a b): Vec2 => bool {a == b}); -vec_wrapper_unary!(vec2_unm (a): Vec2 => Vec2 {(-a).into()}); -vec_wrapper!(vec2_dot (a b): Vec2 => Number {a.dot(&b)}); -vec_wrapper!(vec2_cross (a b): Vec2 => Vec2 {a.cross(&b).into()}); -vec_wrapper_unary!(vec2_norm (a): Vec2 => Number {a.norm()}); -vec_wrapper_unary!(vec2_norm_squared (a): Vec2 => Number {a.norm_squared()}); -vec_wrapper_unary!(vec2_argmin (a): Vec2 => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_unary!(vec2_argmax (a): Vec2 => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_lerp!(vec2_lerp ((a b): Vec2, f: Num) => Vec2 {a.lerp(&b, f.0).into()}); -vec_wrapper_lerp!(vec2_slerp ((a b): Vec2, f: Num) => Vec2 {a.slerp(&b, f.0).into()}); - -vec_wrapper!(vec3_add (a b): Vec3 => Vec3 {(a + b).into()}); -vec_wrapper!(vec3_sub (a b): Vec3 => Vec3 {(a - b).into()}); -vec_wrapper!(vec3_mul (a b): Vec3 => Vec3 {a.component_mul(&b).into()}); -vec_wrapper!(vec3_div (a b): Vec3 => Vec3 {a.component_div(&b).into()}); -vec_wrapper!(vec3_le (a b): Vec3 => bool {a <= b}); -vec_wrapper!(vec3_lt (a b): Vec3 => bool {a < b}); -vec_wrapper!(vec3_eq (a b): Vec3 => bool {a == b}); -vec_wrapper_unary!(vec3_unm (a): Vec3 => Vec3 {(-a).into()}); -vec_wrapper!(vec3_dot (a b): Vec3 => Number {a.dot(&b)}); -vec_wrapper!(vec3_cross (a b): Vec3 => Vec3 {a.cross(&b).into()}); -vec_wrapper_unary!(vec3_norm (a): Vec3 => Number {a.norm()}); -vec_wrapper_unary!(vec3_norm_squared (a): Vec3 => Number {a.norm_squared()}); -vec_wrapper_unary!(vec3_argmin (a): Vec3 => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_unary!(vec3_argmax (a): Vec3 => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_lerp!(vec3_lerp ((a b): Vec3, f: Num) => Vec3 {a.lerp(&b, f.0).into()}); -vec_wrapper_lerp!(vec3_slerp ((a b): Vec3, f: Num) => Vec3 {a.slerp(&b, f.0).into()}); - -vec_wrapper!(vec4_add (a b): Vec4 => Vec4 {(a + b).into()}); -vec_wrapper!(vec4_sub (a b): Vec4 => Vec4 {(a - b).into()}); -vec_wrapper!(vec4_mul (a b): Vec4 => Vec4 {a.component_mul(&b).into()}); -vec_wrapper!(vec4_div (a b): Vec4 => Vec4 {a.component_div(&b).into()}); -vec_wrapper!(vec4_le (a b): Vec4 => bool {a <= b}); -vec_wrapper!(vec4_lt (a b): Vec4 => bool {a < b}); -vec_wrapper!(vec4_eq (a b): Vec4 => bool {a == b}); -vec_wrapper_unary!(vec4_unm (a): Vec4 => Vec4 {(-a).into()}); -vec_wrapper!(vec4_dot (a b): Vec4 => Number {a.dot(&b)}); -vec_wrapper!(vec4_cross (a b): Vec4 => Vec4 {a.cross(&b).into()}); -vec_wrapper_unary!(vec4_norm (a): Vec4 => Number {a.norm()}); -vec_wrapper_unary!(vec4_norm_squared (a): Vec4 => Number {a.norm_squared()}); -vec_wrapper_unary!(vec4_argmin (a): Vec4 => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_unary!(vec4_argmax (a): Vec4 => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_lerp!(vec4_lerp ((a b): Vec4, f: Num) => Vec4 {a.lerp(&b, f.0).into()}); -vec_wrapper_lerp!(vec4_slerp ((a b): Vec4, f: Num) => Vec4 {a.slerp(&b, f.0).into()}); +vec_wrapper_2_uniform!(vec2_add (a, b): Vec2 => Vec2 {(a + b).into()}); +vec_wrapper_2_uniform!(vec2_sub (a, b): Vec2 => Vec2 {(a - b).into()}); +vec_wrapper_2_uniform!(vec2_mul (a, b): Vec2 => Vec2 {a.component_mul(&b).into()}); +vec_wrapper_2_uniform!(vec2_div (a, b): Vec2 => Vec2 {a.component_div(&b).into()}); +vec_wrapper_2_uniform!(vec2_le (a, b): Vec2 => bool {a <= b}); +vec_wrapper_2_uniform!(vec2_lt (a, b): Vec2 => bool {a < b}); +vec_wrapper_2_uniform!(vec2_eq (a, b): Vec2 => bool {a == b}); +vec_wrapper_1!(vec2_unm (a: Vec2) => Vec2 {(-a).into()}); +vec_wrapper_2_uniform!(vec2_dot (a, b): Vec2 => Number {a.dot(&b)}); +vec_wrapper_2_uniform!(vec2_cross (a, b): Vec2 => Vec2 {a.cross(&b).into()}); +vec_wrapper_1!(vec2_norm (a: Vec2) => Number {a.norm()}); +vec_wrapper_1!(vec2_norm_squared (a: Vec2) => Number {a.norm_squared()}); +vec_wrapper_1!(vec2_argmin (a: Vec2) => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_1!(vec2_argmax (a: Vec2) => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_1!(vec2_normalize (a: Vec2) => Vec2 {a.normalize().into()}); +vec_wrapper_3!(vec2_lerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.lerp(&b.into_inner(), f.0).into()}); +vec_wrapper_3!(vec2_slerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.slerp(&b.into_inner(), f.0).into()}); + +vec_wrapper_2_uniform!(vec3_add (a, b): Vec3 => Vec3 {(a + b).into()}); +vec_wrapper_2_uniform!(vec3_sub (a, b): Vec3 => Vec3 {(a - b).into()}); +vec_wrapper_2_uniform!(vec3_mul (a, b): Vec3 => Vec3 {a.component_mul(&b).into()}); +vec_wrapper_2_uniform!(vec3_div (a, b): Vec3 => Vec3 {a.component_div(&b).into()}); +vec_wrapper_2_uniform!(vec3_le (a, b): Vec3 => bool {a <= b}); +vec_wrapper_2_uniform!(vec3_lt (a, b): Vec3 => bool {a < b}); +vec_wrapper_2_uniform!(vec3_eq (a, b): Vec3 => bool {a == b}); +vec_wrapper_1!(vec3_unm (a: Vec3) => Vec3 {(-a).into()}); +vec_wrapper_2_uniform!(vec3_dot (a, b): Vec3 => Number {a.dot(&b)}); +vec_wrapper_2_uniform!(vec3_cross (a, b): Vec3 => Vec3 {a.cross(&b).into()}); +vec_wrapper_1!(vec3_norm (a: Vec3) => Number {a.norm()}); +vec_wrapper_1!(vec3_norm_squared (a: Vec3) => Number {a.norm_squared()}); +vec_wrapper_1!(vec3_argmin (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_1!(vec3_argmax (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_1!(vec3_normalize (a: Vec3) => Vec3 {a.normalize().into()}); +vec_wrapper_3!(vec3_lerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.lerp(&b.into_inner(), f.0).into()}); +vec_wrapper_3!(vec3_slerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.slerp(&b.into_inner(), f.0).into()}); + +vec_wrapper_2_uniform!(vec4_add (a, b): Vec4 => Vec4 {(a + b).into()}); +vec_wrapper_2_uniform!(vec4_sub (a, b): Vec4 => Vec4 {(a - b).into()}); +vec_wrapper_2_uniform!(vec4_mul (a, b): Vec4 => Vec4 {a.component_mul(&b).into()}); +vec_wrapper_2_uniform!(vec4_div (a, b): Vec4 => Vec4 {a.component_div(&b).into()}); +vec_wrapper_2_uniform!(vec4_le (a, b): Vec4 => bool {a <= b}); +vec_wrapper_2_uniform!(vec4_lt (a, b): Vec4 => bool {a < b}); +vec_wrapper_2_uniform!(vec4_eq (a, b): Vec4 => bool {a == b}); +vec_wrapper_1!(vec4_unm (a: Vec4) => Vec4 {(-a).into()}); +vec_wrapper_2_uniform!(vec4_dot (a, b): Vec4 => Number {a.dot(&b)}); +vec_wrapper_2_uniform!(vec4_cross (a, b): Vec4 => Vec4 {a.cross(&b).into()}); +vec_wrapper_1!(vec4_norm (a: Vec4) => Number {a.norm()}); +vec_wrapper_1!(vec4_norm_squared (a: Vec4) => Number {a.norm_squared()}); +vec_wrapper_1!(vec4_argmin (a: Vec4) => (Int, Num) {argminmax_to_lua(a.argmin())}); +vec_wrapper_1!(vec4_argmax (a: Vec4) => (Int, Num) {argminmax_to_lua(a.argmax())}); +vec_wrapper_1!(vec4_normalize (a: Vec4) => Vec4 {a.normalize().into()}); +vec_wrapper_3!(vec4_lerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.lerp(&b.into_inner(), f.0).into()}); +vec_wrapper_3!(vec4_slerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.slerp(&b.into_inner(), f.0).into()}); impl Lib for LuaEngine { fn load_vec2(&self) -> rlua::Result<()> { //Create the metatable. - crate::engine::auto_lib!(self (VEC2_LIB, true) { + auto_lib!(self (VEC2_LIB, true) { __add: vec2_add, __sub: vec2_sub, __mul: vec2_mul, __div: vec2_div, __le: vec2_le, __lt: vec2_lt, __eq: vec2_eq, __unm: vec2_unm, dot: vec2_dot, cross: vec2_cross, norm: vec2_norm, normSquared: vec2_norm_squared, argmin: vec2_argmin, argmax: vec2_argmax, lerp: vec2_lerp, slerp: vec2_slerp, + normalize: vec2_normalize, })?; //Create constructor function. self.context(|ctx| { @@ -301,11 +280,12 @@ impl Lib for LuaEngine { fn load_vec3(&self) -> rlua::Result<()> { //Create the metatable. - crate::engine::auto_lib!(self (VEC3_LIB, true) { + auto_lib!(self (VEC3_LIB, true) { __add: vec3_add, __sub: vec3_sub, __mul: vec3_mul, __div: vec3_div, __le: vec3_le, __lt: vec3_lt, __eq: vec3_eq, __unm: vec3_unm, dot: vec3_dot, cross: vec3_cross, norm: vec3_norm, normSquared: vec3_norm_squared, argmin: vec3_argmin, argmax: vec3_argmax, lerp: vec3_lerp, slerp: vec3_slerp, + normalize: vec3_normalize, })?; //Create constructor function. self.context(|ctx| { @@ -326,11 +306,12 @@ impl Lib for LuaEngine { fn load_vec4(&self) -> rlua::Result<()> { //Create the metatable. - crate::engine::auto_lib!(self (VEC4_LIB, true) { + auto_lib!(self (VEC4_LIB, true) { __add: vec4_add, __sub: vec4_sub, __mul: vec4_mul, __div: vec4_div, __le: vec4_le, __lt: vec4_lt, __eq: vec4_eq, __unm: vec4_unm, dot: vec4_dot, cross: vec4_cross, norm: vec4_norm, normSquared: vec4_norm_squared, argmin: vec4_argmin, argmax: vec4_argmax, lerp: vec4_lerp, slerp: vec4_slerp, + normalize: vec4_normalize, })?; //Create constructor function. self.context(|ctx| { From 2b280a0c36f3e8d8a9f0767df3eba0eb240bf4e7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Mar 2022 11:13:44 +0100 Subject: [PATCH 004/527] Refactored number utilities + Finished support for quaternion lib --- src/number.rs | 24 +++++++-------- src/quaternion.rs | 77 ++++++++++++++++++++++++++++++++++++----------- src/vector.rs | 71 +++++++++++++++++++++++++++++-------------- 3 files changed, 120 insertions(+), 52 deletions(-) diff --git a/src/number.rs b/src/number.rs index 4e4e67c..0fad954 100644 --- a/src/number.rs +++ b/src/number.rs @@ -76,25 +76,25 @@ impl<'lua> ToLua<'lua> for Int { // These implementation are possibly truncating numbers; the use case being rendering engines, // precision doesn't matter that much. -pub trait NumToLua<'a> { - fn num_to_lua(self) -> Value<'a>; +pub trait NumToLua { + fn num_to_lua<'a>(self) -> Value<'a>; } -pub trait NumFromLua<'a> where Self: Sized { - fn num_from_lua(val: Value<'a>) -> rlua::Result; +pub trait NumFromLua where Self: Sized { + fn num_from_lua(val: Value) -> rlua::Result; } macro_rules! impl_num_float { ($($target: ty)*) => { $( - impl<'a> NumToLua<'a> for $target { - fn num_to_lua(self) -> Value<'a> { + impl NumToLua for $target { + fn num_to_lua<'a>(self) -> Value<'a> { Value::Number(self as Number) } } - impl<'a> NumFromLua<'a> for $target { - fn num_from_lua(val: Value<'a>) -> rlua::Result { + impl NumFromLua for $target { + fn num_from_lua(val: Value) -> rlua::Result { val.check_number().map(|v| v as $target) } } @@ -105,14 +105,14 @@ macro_rules! impl_num_float { macro_rules! impl_num_int { ($($target: ty)*) => { $( - impl<'a> NumToLua<'a> for $target { - fn num_to_lua(self) -> Value<'a> { + impl NumToLua for $target { + fn num_to_lua<'a>(self) -> Value<'a> { Value::Integer(self as Integer) } } - impl<'a> NumFromLua<'a> for $target { - fn num_from_lua(val: Value<'a>) -> rlua::Result { + impl NumFromLua for $target { + fn num_from_lua(val: Value) -> rlua::Result { val.check_integer().map(|v| v as $target) } } diff --git a/src/quaternion.rs b/src/quaternion.rs index cbdce0e..750b031 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use nalgebra::{Quaternion, UnitQuaternion}; -use rlua::{Context, FromLua, Function, Number, ToLua, UserData, Value}; +use nalgebra::{Quaternion, Unit, UnitQuaternion, Vector3}; +use rlua::{Context, FromLua, Function, Number, Table, ToLua, Value}; use crate::{LuaEngine, ValueExt}; use crate::number::{NumFromLua, NumToLua, Num}; use crate::macros::auto_lib; @@ -35,6 +35,7 @@ use crate::macros::vec_wrapper_2; use crate::macros::vec_wrapper_2_uniform; use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_3; +use crate::macros::vec_wrapper_4; pub trait Lib { fn load_quat(&self) -> rlua::Result<()>; @@ -69,31 +70,29 @@ impl LuaQuat { } impl<'lua, T> ToLua<'lua> for LuaQuat - where T: NumToLua<'lua> + where T: NumToLua { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(QUAT_NEW)?; - let [[x, y, z, w]] = self.0.coords.data.0; - func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua(), w.num_to_lua())) + let [[i, j, k, w]] = self.0.coords.data.0; + func.call((w.num_to_lua(), i.num_to_lua(), j.num_to_lua(), k.num_to_lua())) } } impl<'lua, T> FromLua<'lua> for LuaQuat - where T: NumFromLua<'lua> + where T: NumFromLua { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; Ok(LuaQuat(Quaternion::new( T::num_from_lua(table.raw_get("w")?)?, - T::num_from_lua(table.raw_get("x")?)?, - T::num_from_lua(table.raw_get("y")?)?, - T::num_from_lua(table.raw_get("z")?)? + T::num_from_lua(table.raw_get("i")?)?, + T::num_from_lua(table.raw_get("j")?)?, + T::num_from_lua(table.raw_get("k")?)? ))) } } -pub struct LuaUnitQuat(UnitQuaternion); - pub(crate) type Quat = LuaQuat; vec_wrapper_2_uniform!(quat_eq (a, b): Quat => bool {a == b}); @@ -133,6 +132,22 @@ vec_wrapper_1!(quat_norm (a: Quat) => Number {a.norm()}); vec_wrapper_1!(quat_norm_squared (a: Quat) => Number {a.norm_squared()}); vec_wrapper_1!(quat_inverse (a: Quat) => Option {a.try_inverse().map(|v| v.into())}); +//Unit quaternions +vec_wrapper_1!(quat_angle (a: Quat) => Number {Unit::new_unchecked(a).angle()}); +vec_wrapper_2_uniform!(quat_angle_to (a, b): Quat => Number { + Unit::new_unchecked(a).angle_to(&Unit::new_unchecked(b)) +}); +vec_wrapper_2_uniform!(quat_rotation_to (a, b): Quat => Quat { + Unit::new_unchecked(a).rotation_to(&Unit::new_unchecked(b)).into_inner().into() +}); +vec_wrapper_4!(quat_slerp (a: Quat, b: Quat, f: Num, e: Num) => Option { + Unit::new_unchecked(a).try_slerp(&Unit::new_unchecked(b.into_inner()), f.0, e.0) + .map(|v| v.into_inner().into()) +}); +vec_wrapper_1!(quat_euler_angles (a: Quat) => (Number, Number, Number) { + Unit::new_unchecked(a).euler_angles() +}); + impl Lib for LuaEngine { fn load_quat(&self) -> rlua::Result<()> { auto_lib!(self (QUAT_LIB, true) { @@ -145,16 +160,44 @@ impl Lib for LuaEngine { cosh: quat_cosh, acosh: quat_acosh, tanh: quat_tanh, atanh: quat_atanh, pow: quat_pow, lerp: quat_lerp, imag: quat_imag, scalar: quat_scalar, norm: quat_norm, normSquared: quat_norm_squared, inverse: quat_inverse, + angle: quat_angle, angleTo: quat_angle_to, rotationTo: quat_rotation_to, + slerp: quat_slerp, eulerAngles: quat_euler_angles, + })?; + //Create identity constant. + self.context(|ctx| { + let tbl: Table = ctx.globals().raw_get(QUAT_LIB)?; + tbl.raw_set("IDENTITY", Quat::new(Quaternion::identity()))?; + Ok(()) })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Num, Num, Num)| { - let globals = ctx.globals(); + let function = ctx.create_function(|ctx, (v1, v2, v3, z): (Value, Option, Option, Option)| { + let v = match (v2, v3, z) { + (Some(i), Some(j), Some(k)) => { + let w = v1.check_number()?; + Quaternion::new(w, i.0, j.0, k.0) + }, // wijk constructor + (Some(pitch), Some(yaw), None) => { + let roll = v1.check_number()?; + UnitQuaternion::from_euler_angles(roll, pitch.0, yaw.0) + .into_inner() + }, // roll, pitch, yaw constructor + (Some(angle), None, None) => { + let axis = v1.check_table()?; + let vec = Unit::new_unchecked(Vector3::new(axis.raw_get("x")?, axis.raw_get("y")?, axis.raw_get("z")?)); + UnitQuaternion::from_axis_angle(&vec, angle.0).into_inner() + }, // axis, angle constructor + _ => { + let real = v1.check_number()?; + Quaternion::from_real(real) + } + }; let table = ctx.create_table()?; - table.raw_set("x", x)?; - table.raw_set("y", y)?; - table.raw_set("z", z)?; - table.raw_set("w", w)?; + table.raw_set("i", v.i)?; + table.raw_set("j", v.j)?; + table.raw_set("k", v.k)?; + table.raw_set("w", v.w)?; + let globals = ctx.globals(); table.set_metatable(globals.raw_get(QUAT_LIB)?); Ok(table) })?; diff --git a/src/vector.rs b/src/vector.rs index 2e6eb3b..87a76b1 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -31,6 +31,7 @@ use rlua::{Context, FromLua, Function, Number, ToLua, Value}; use crate::{LuaEngine, ValueExt}; use crate::number::{Num, Int, NumFromLua, NumToLua}; use crate::macros::vec_wrapper_3; +use crate::macros::vec_wrapper_2; use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_2_uniform; use crate::macros::auto_lib; @@ -74,7 +75,7 @@ impl LuaVec2 { } impl<'lua, T> ToLua<'lua> for LuaVec2 - where T: NumToLua<'lua> + where T: NumToLua { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC2_NEW)?; @@ -84,7 +85,7 @@ impl<'lua, T> ToLua<'lua> for LuaVec2 } impl<'lua, T> FromLua<'lua> for LuaVec2 - where T: NumFromLua<'lua> + where T: NumFromLua { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; @@ -120,7 +121,7 @@ impl LuaVec3 { } impl<'lua, T> ToLua<'lua> for LuaVec3 - where T: NumToLua<'lua> + where T: NumToLua { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC3_NEW)?; @@ -130,7 +131,7 @@ impl<'lua, T> ToLua<'lua> for LuaVec3 } impl<'lua, T> FromLua<'lua> for LuaVec3 - where T: NumFromLua<'lua> + where T: NumFromLua { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; @@ -167,7 +168,7 @@ impl LuaVec4 { } impl<'lua, T> ToLua<'lua> for LuaVec4 - where T: NumToLua<'lua> + where T: NumToLua { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC4_NEW)?; @@ -177,7 +178,7 @@ impl<'lua, T> ToLua<'lua> for LuaVec4 } impl<'lua, T> FromLua<'lua> for LuaVec4 - where T: NumFromLua<'lua> + where T: NumFromLua { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; @@ -215,6 +216,7 @@ vec_wrapper_1!(vec2_argmax (a: Vec2) => (Int, Num) {argminmax_to_lua(a.argmax()) vec_wrapper_1!(vec2_normalize (a: Vec2) => Vec2 {a.normalize().into()}); vec_wrapper_3!(vec2_lerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.lerp(&b.into_inner(), f.0).into()}); vec_wrapper_3!(vec2_slerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.slerp(&b.into_inner(), f.0).into()}); +vec_wrapper_2!(vec2_push (a: Vec2, b: Num) => Vec3 {a.push(b.0).into()}); vec_wrapper_2_uniform!(vec3_add (a, b): Vec3 => Vec3 {(a + b).into()}); vec_wrapper_2_uniform!(vec3_sub (a, b): Vec3 => Vec3 {(a - b).into()}); @@ -231,8 +233,13 @@ vec_wrapper_1!(vec3_norm_squared (a: Vec3) => Number {a.norm_squared()}); vec_wrapper_1!(vec3_argmin (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmin())}); vec_wrapper_1!(vec3_argmax (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmax())}); vec_wrapper_1!(vec3_normalize (a: Vec3) => Vec3 {a.normalize().into()}); +#[cfg(feature = "quaternion")] +vec_wrapper_2!(vec3_rotate (a: Vec3, b: crate::quaternion::Quat) => Vec3 { + (nalgebra::Unit::new_unchecked(b.into_inner()) * a).into() +}); vec_wrapper_3!(vec3_lerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.lerp(&b.into_inner(), f.0).into()}); vec_wrapper_3!(vec3_slerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.slerp(&b.into_inner(), f.0).into()}); +vec_wrapper_2!(vec3_push (a: Vec3, b: Num) => Vec4 {a.push(b.0).into()}); vec_wrapper_2_uniform!(vec4_add (a, b): Vec4 => Vec4 {(a + b).into()}); vec_wrapper_2_uniform!(vec4_sub (a, b): Vec4 => Vec4 {(a - b).into()}); @@ -260,15 +267,19 @@ impl Lib for LuaEngine { __le: vec2_le, __lt: vec2_lt, __eq: vec2_eq, __unm: vec2_unm, dot: vec2_dot, cross: vec2_cross, norm: vec2_norm, normSquared: vec2_norm_squared, argmin: vec2_argmin, argmax: vec2_argmax, lerp: vec2_lerp, slerp: vec2_slerp, - normalize: vec2_normalize, + normalize: vec2_normalize, push: vec2_push, })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y): (Num, Num)| { - let globals = ctx.globals(); + let function = ctx.create_function(|ctx, (x, y): (Num, Option)| { + let val = match y { + Some(y) => Vector2::new(x.0, y.0), + None => Vector2::from_element(x.0) + }; let table = ctx.create_table()?; - table.raw_set("x", x)?; - table.raw_set("y", y)?; + table.raw_set("x", val.x)?; + table.raw_set("y", val.y)?; + let globals = ctx.globals(); table.set_metatable(globals.raw_get(VEC2_LIB)?); Ok(table) })?; @@ -285,16 +296,26 @@ impl Lib for LuaEngine { __le: vec3_le, __lt: vec3_lt, __eq: vec3_eq, __unm: vec3_unm, dot: vec3_dot, cross: vec3_cross, norm: vec3_norm, normSquared: vec3_norm_squared, argmin: vec3_argmin, argmax: vec3_argmax, lerp: vec3_lerp, slerp: vec3_slerp, - normalize: vec3_normalize, + normalize: vec3_normalize, push: vec3_push, + })?; + #[cfg(feature = "quaternion")] + self.context(|ctx| { + let tbl: rlua::Table = ctx.globals().raw_get(VEC3_LIB)?; + tbl.raw_set("rotate", ctx.create_function(vec3_rotate)?)?; + Ok(()) })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y, z): (Num, Num, Num)| { - let globals = ctx.globals(); + let function = ctx.create_function(|ctx, (x, y, z): (Num, Option, Option)| { + let val = match (y, z) { + (Some(y), Some(z)) => Vector3::new(x.0, y.0, z.0), + _ => Vector3::from_element(x.0) + }; let table = ctx.create_table()?; - table.raw_set("x", x)?; - table.raw_set("y", y)?; - table.raw_set("z", z)?; + table.raw_set("x", val.x)?; + table.raw_set("y", val.y)?; + table.raw_set("z", val.z)?; + let globals = ctx.globals(); table.set_metatable(globals.raw_get(VEC3_LIB)?); Ok(table) })?; @@ -315,13 +336,17 @@ impl Lib for LuaEngine { })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Num, Num, Num)| { - let globals = ctx.globals(); + let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Option, Option, Option)| { + let val = match (y, z, w) { + (Some(y), Some(z), Some(w)) => Vector4::new(x.0, y.0, z.0, w.0), + _ => Vector4::from_element(x.0) + }; let table = ctx.create_table()?; - table.raw_set("x", x)?; - table.raw_set("y", y)?; - table.raw_set("z", z)?; - table.raw_set("w", w)?; + table.raw_set("x", val.x)?; + table.raw_set("y", val.y)?; + table.raw_set("z", val.z)?; + table.raw_set("w", val.w)?; + let globals = ctx.globals(); table.set_metatable(globals.raw_get(VEC4_LIB)?); Ok(table) })?; From 1dc7c5907565429dda8828451d21b90f522cad49 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Mar 2022 11:20:09 +0100 Subject: [PATCH 005/527] Removed unneeded constant --- src/engine.rs | 3 --- src/quaternion.rs | 6 ------ 2 files changed, 9 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index 29e34cb..e6aa5f0 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -91,7 +91,4 @@ impl LuaEngine { pub fn context rlua::Result>(&self, function: F) -> rlua::Result { self.state.context(function) } - - //TODO: quaternion library - //TODO: matrix library } diff --git a/src/quaternion.rs b/src/quaternion.rs index 750b031..d126b5a 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -163,12 +163,6 @@ impl Lib for LuaEngine { angle: quat_angle, angleTo: quat_angle_to, rotationTo: quat_rotation_to, slerp: quat_slerp, eulerAngles: quat_euler_angles, })?; - //Create identity constant. - self.context(|ctx| { - let tbl: Table = ctx.globals().raw_get(QUAT_LIB)?; - tbl.raw_set("IDENTITY", Quat::new(Quaternion::identity()))?; - Ok(()) - })?; //Create constructor function. self.context(|ctx| { let function = ctx.create_function(|ctx, (v1, v2, v3, z): (Value, Option, Option, Option)| { From 2ac5c2c44d318a999f28be3fe4175cc6e1416fc5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Mar 2022 11:22:44 +0100 Subject: [PATCH 006/527] Removed placeholder features --- .gitlab-ci.yml | 4 ++-- Cargo.toml | 3 --- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ddb034f..62b7e5a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -3,5 +3,5 @@ include: file: lib-template.yml variables: - CRATE_NAME: test - PROJECT_NAME: Test + CRATE_NAME: bp3d-lua + PROJECT_NAME: Lua diff --git a/Cargo.toml b/Cargo.toml index 1f3535d..9c04b58 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -19,7 +19,4 @@ rlua = "0.19.1" [features] math = [] vector = ["nalgebra"] -point = ["nalgebra"] -scale = ["nalgebra"] quaternion = ["vector"] -matrix = ["nalgebra"] From b6754f549d0d43ad1a63e2c8f4d9f2467558cfb4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Mar 2022 11:26:25 +0100 Subject: [PATCH 007/527] Added categories --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 9c04b58..6cab8bf 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ license = "BSD-3-Clause" repository = "https://gitlab.com/bp3d/lua" readme = "./README.MD" keywords = ["lua"] -categories = [] +categories = ["graphics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html From 18eed898ab245ec1ea3b21e99f80c48b33030cf8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Mar 2022 11:46:54 +0100 Subject: [PATCH 008/527] Disabled rust warnings about internal macros used to generate vector and quaternion API --- src/macros.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/macros.rs b/src/macros.rs index 6b6cd02..d73da6f 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -26,6 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Some macros are only used with vector feature and most macros are used in both quaternion and +// features; so just disable the warnings. +#![allow(unused_macros)] +#![allow(unused_imports)] + macro_rules! auto_lib { ($lua: ident ($lib: ident, $self_callable: expr) { $($name: ident: $fn: ident,)* }) => { $lua.create_library($lib, $self_callable, |ctx| { From cc5c00592c4fa4cf65fbec2e19c547b896c127ce Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 27 Mar 2022 14:21:18 +0200 Subject: [PATCH 009/527] Renamed traits to avoid conflicts --- src/math.rs | 4 ++-- src/quaternion.rs | 8 ++++---- src/vector.rs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/math.rs b/src/math.rs index 4fbb9c9..905d882 100644 --- a/src/math.rs +++ b/src/math.rs @@ -53,11 +53,11 @@ fn math_gaussian(_: Context, (sigma, x): (Num, Num)) -> rlua::Result { // The reason why we provide a custom math lib is to have on par implementation with rust // and nalgebra, required for accurate rendering with the engine. -pub trait Lib { +pub trait LibMath { fn load_math(&self) -> rlua::Result<()>; } -impl Lib for LuaEngine { +impl LibMath for LuaEngine { fn load_math(&self) -> rlua::Result<()> { self.create_library("math", false, |ctx| { ctx.constant("PI", std::f64::consts::PI)?; diff --git a/src/quaternion.rs b/src/quaternion.rs index d126b5a..64a4995 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -37,8 +37,8 @@ use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_3; use crate::macros::vec_wrapper_4; -pub trait Lib { - fn load_quat(&self) -> rlua::Result<()>; +pub trait LibQuaternion { + fn load_quaternion(&self) -> rlua::Result<()>; } const QUAT_LIB: &str = "quat"; @@ -148,8 +148,8 @@ vec_wrapper_1!(quat_euler_angles (a: Quat) => (Number, Number, Number) { Unit::new_unchecked(a).euler_angles() }); -impl Lib for LuaEngine { - fn load_quat(&self) -> rlua::Result<()> { +impl LibQuaternion for LuaEngine { + fn load_quaternion(&self) -> rlua::Result<()> { auto_lib!(self (QUAT_LIB, true) { __add: quat_add, __sub: quat_sub, __mul: quat_mul, __eq: quat_eq, dot: quat_dot, inner: quat_inner, outer: quat_outer, project: quat_project, diff --git a/src/vector.rs b/src/vector.rs index 87a76b1..f3bac0d 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -36,7 +36,7 @@ use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_2_uniform; use crate::macros::auto_lib; -pub trait Lib { +pub trait LibVector { fn load_vec2(&self) -> rlua::Result<()>; fn load_vec3(&self) -> rlua::Result<()>; fn load_vec4(&self) -> rlua::Result<()>; @@ -259,7 +259,7 @@ vec_wrapper_1!(vec4_normalize (a: Vec4) => Vec4 {a.normalize().into()}); vec_wrapper_3!(vec4_lerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.lerp(&b.into_inner(), f.0).into()}); vec_wrapper_3!(vec4_slerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.slerp(&b.into_inner(), f.0).into()}); -impl Lib for LuaEngine { +impl LibVector for LuaEngine { fn load_vec2(&self) -> rlua::Result<()> { //Create the metatable. auto_lib!(self (VEC2_LIB, true) { @@ -360,7 +360,7 @@ impl Lib for LuaEngine { #[cfg(test)] mod tests { use crate::LuaEngine; - use crate::vector::Lib; + use crate::vector::LibVector; #[test] fn basic() { From 878447143f46e9d41c3d660b4aeef93f3c1a54e0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 27 Mar 2022 17:41:59 +0200 Subject: [PATCH 010/527] Added Checked type of number to allow range checking without number coercion --- Cargo.toml | 1 + src/ext.rs | 1 + src/number.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 43 insertions(+) diff --git a/Cargo.toml b/Cargo.toml index 6cab8bf..d68cf60 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["graphics"] [dependencies] nalgebra = { version = "0.30.1", optional = true } +num-traits = "0.2.14" rlua = "0.19.1" [features] diff --git a/src/ext.rs b/src/ext.rs index 9fb86d3..5213d2f 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -57,6 +57,7 @@ impl<'a> ValueExt<'a> for Value<'a> { fn check_integer(self) -> Result { match self { Value::Integer(v) => Ok(v), + Value::Number(v) => Ok(v as Integer), _ => Err(Error::FromLuaConversionError { from: self.type_name(), to: "Integer", diff --git a/src/number.rs b/src/number.rs index 0fad954..428cdfe 100644 --- a/src/number.rs +++ b/src/number.rs @@ -28,9 +28,12 @@ //! Fast implementation of numbers and integers. +use num_traits::{cast, NumCast}; +use rlua::Error; use rlua::{Context, FromLua, Integer, Number, ToLua, Value}; use crate::ValueExt; +/// Fast encodable/decodable float type with no coercion and no range checking. #[derive(Copy, Clone, Debug, Default)] pub struct Num(pub Number); @@ -52,6 +55,7 @@ impl<'lua> ToLua<'lua> for Num { } } +/// Fast encodable/decodable integer type with no coercion and no range checking. #[derive(Copy, Clone, Debug, Default)] pub struct Int(pub Integer); @@ -81,6 +85,7 @@ pub trait NumToLua { } pub trait NumFromLua where Self: Sized { + fn type_name() -> &'static str; fn num_from_lua(val: Value) -> rlua::Result; } @@ -97,6 +102,10 @@ macro_rules! impl_num_float { fn num_from_lua(val: Value) -> rlua::Result { val.check_number().map(|v| v as $target) } + + fn type_name() -> &'static str { + stringify!($target) + } } )* }; @@ -115,6 +124,10 @@ macro_rules! impl_num_int { fn num_from_lua(val: Value) -> rlua::Result { val.check_integer().map(|v| v as $target) } + + fn type_name() -> &'static str { + stringify!($target) + } } )* }; @@ -126,3 +139,31 @@ impl_num_int!( i8 i16 i32 i64 u8 u16 u32 u64 ); + +/// A range checked number which does not implement number coercion for better performance. +pub struct Checked(pub T); + +impl<'lua, T: NumFromLua + NumCast> FromLua<'lua> for Checked { + fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { + match lua_value { + Value::Integer(v) => { + cast(v).map(Checked).ok_or_else(|| Error::FromLuaConversionError { + from: lua_value.type_name(), + to: T::type_name(), + message: Some("out of range".to_string()), + }) + }, + _ => Err(Error::FromLuaConversionError { + from: lua_value.type_name(), + to: T::type_name(), + message: Some("expected integer".to_string()), + }) + } + } +} + +impl<'lua, T: NumToLua> ToLua<'lua> for Checked { + fn to_lua(self, _: Context<'lua>) -> rlua::Result> { + Ok(self.0.num_to_lua()) + } +} From 06f4df9a6b9d100d86b9fa16aca67d15579d8f6b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 30 Mar 2022 16:12:45 +0200 Subject: [PATCH 011/527] Added check_userdata --- src/ext.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ext.rs b/src/ext.rs index 5213d2f..3057090 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -31,7 +31,9 @@ // These functions are non standard but used to optimize 3D engines where access to numbers // MUST be VERY fast. +use std::cell::Ref; use rlua::{Integer, Number, Table, Value, Result, Error}; +use rlua::prelude::LuaUserData; pub trait ValueExt<'a> { fn check_number(self) -> Result; @@ -39,6 +41,7 @@ pub trait ValueExt<'a> { fn check_table(self) -> Result>; fn check_bool(self) -> Result; fn check_string(&self) -> Result<&str>; + fn check_userdata(&self) -> Result>; } impl<'a> ValueExt<'a> for Value<'a> { @@ -98,6 +101,17 @@ impl<'a> ValueExt<'a> for Value<'a> { }) } } + + fn check_userdata(&self) -> Result> { + match self { + Value::UserData(v) => v.borrow(), + _ => Err(Error::FromLuaConversionError { + from: self.type_name(), + to: "Userdata", + message: Some("expected userdata".to_string()), + }) + } + } } pub trait TableExt { From aab7b26d55d066bcf92d359d3b5bd9590b46b5a8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 30 Mar 2022 21:03:25 +0200 Subject: [PATCH 012/527] Added a lot of useful math functions --- src/math.rs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/math.rs b/src/math.rs index 905d882..29698df 100644 --- a/src/math.rs +++ b/src/math.rs @@ -60,8 +60,8 @@ pub trait LibMath { impl LibMath for LuaEngine { fn load_math(&self) -> rlua::Result<()> { self.create_library("math", false, |ctx| { - ctx.constant("PI", std::f64::consts::PI)?; - ctx.constant("E", std::f64::consts::E)?; + ctx.constant("pi", std::f64::consts::PI)?; + ctx.constant("e", std::f64::consts::E)?; ctx.function("cos", |_, x: Num| Ok(x.0.cos()))?; ctx.function("sin", |_, x: Num| Ok(x.0.sin()))?; ctx.function("tan", |_, x: Num| Ok(x.0.tan()))?; @@ -77,9 +77,17 @@ impl LibMath for LuaEngine { ctx.function("atan2", |_, (x, y): (Num, Num)| Ok(x.0.atan2(y.0)))?; ctx.function("degrees", |_, x: Num| Ok(x.0.to_degrees()))?; ctx.function("radians", |_, x: Num| Ok(x.0.to_radians()))?; - ctx.function("floor", |_, x: Num| Ok(x.0.floor()))?; - ctx.function("ceil", |_, x: Num| Ok(x.0.ceil()))?; - ctx.function("round", |_, x: Num| Ok(x.0.round()))?; + ctx.function("abs", |_, x: Num| Ok(x.0.abs()))?; + ctx.function("exp", |_, x: Num| Ok(x.0.exp()))?; + ctx.function("log", |_, (x, base): (Num, Num)| Ok(x.0.log(base.0)))?; + ctx.function("ln", |_, x: Num| Ok(x.0.ln()))?; + ctx.function("log2", |_, x: Num| Ok(x.0.log2()))?; + ctx.function("log10", |_, x: Num| Ok(x.0.log10()))?; + ctx.function("sqrt", |_, x: Num| Ok(x.0.sqrt()))?; + ctx.function("floor", |_, x: Num| Ok(Int(x.0.floor() as _)))?; + ctx.function("ceil", |_, x: Num| Ok(Int(x.0.ceil() as _)))?; + ctx.function("round", |_, x: Num| Ok(Int(x.0.round() as _)))?; + ctx.function("int", |_, x: Num| Ok(Int(x.0 as _)))?; ctx.function("round2", math_round)?; ctx.function("pow", |_, (x, n): (Num, Num)| Ok(x.0.powf(n.0)))?; ctx.function("clamp", math_clamp)?; From 4fa48689846b9239a21fd5da831e1de007e93eea Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 31 Mar 2022 18:56:41 +0200 Subject: [PATCH 013/527] Fixed warning in quaternion library + Added noise library --- Cargo.toml | 3 ++ src/lib.rs | 3 ++ src/noise.rs | 102 ++++++++++++++++++++++++++++++++++++++++++++++ src/quaternion.rs | 2 +- 4 files changed, 109 insertions(+), 1 deletion(-) create mode 100644 src/noise.rs diff --git a/Cargo.toml b/Cargo.toml index d68cf60..4416885 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,8 +16,11 @@ categories = ["graphics"] nalgebra = { version = "0.30.1", optional = true } num-traits = "0.2.14" rlua = "0.19.1" +noise = { version = "0.7.0", optional = true } +rand = {version = "0.8.5", optional = true } [features] math = [] vector = ["nalgebra"] quaternion = ["vector"] +random = ["rand"] diff --git a/src/lib.rs b/src/lib.rs index 65565d7..a3acac3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -40,5 +40,8 @@ pub mod vector; #[cfg(feature = "quaternion")] pub mod quaternion; +#[cfg(feature = "noise")] +pub mod noise; + pub use engine::*; pub use ext::*; diff --git a/src/noise.rs b/src/noise.rs new file mode 100644 index 0000000..e49bd49 --- /dev/null +++ b/src/noise.rs @@ -0,0 +1,102 @@ +// Copyright (c) 2022, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::LuaEngine; +use crate::number::{Checked, Int, Num}; +use rlua::UserData; +use rlua::UserDataMethods; +use noise::Perlin; +use noise::NoiseFn; +use noise::Billow; +use noise::Seedable; + +pub trait LibNoise { + fn load_noise(&self) -> rlua::Result<()>; +} + +struct LuaPerlin(Perlin); + +impl UserData for LuaPerlin { + fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { + methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); + methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| Ok(this.0.get([x.0, y.0]))); + methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0]))); + methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0, w.0]))); + } +} + +struct LuaBillow(Billow); + +impl UserData for LuaBillow { + fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { + methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); + methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| Ok(this.0.get([x.0, y.0]))); + methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0]))); + methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0, w.0]))); + methods.add_method("getOctaves", |_, this, ()| Ok(Int(this.0.octaves as _))); + methods.add_method("getFrequency", |_, this, ()| Ok(this.0.frequency)); + methods.add_method("getLacunarity", |_, this, ()| Ok(this.0.lacunarity)); + methods.add_method("getPersistence", |_, this, ()| Ok(this.0.persistence)); + methods.add_method_mut("setOctaves", |_, this, x: Int| { + this.0.octaves = x.0 as _; + Ok(()) + }); + methods.add_method_mut("setFrequency", |_, this, x: Num| { + this.0.frequency = x.0; + Ok(()) + }); + methods.add_method_mut("setLacunarity", |_, this, x: Num| { + this.0.lacunarity = x.0; + Ok(()) + }); + methods.add_method_mut("setPersistence", |_, this, x: Num| { + this.0.persistence = x.0; + Ok(()) + }); + } +} + +impl LibNoise for LuaEngine { + fn load_noise(&self) -> rlua::Result<()> { + self.create_library("noise", false, |ctx| { + ctx.function("perlin", |_, seed: Option>| { + Ok(LuaPerlin(match seed { + None => Perlin::new(), + Some(seed) => Perlin::new().set_seed(seed.0) + })) + })?; + ctx.function("billow", |_, seed: Option>| { + Ok(LuaBillow(match seed { + None => Billow::new(), + Some(seed) => Billow::new().set_seed(seed.0) + })) + })?; + Ok(()) + }) + } +} diff --git a/src/quaternion.rs b/src/quaternion.rs index 64a4995..75cd948 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use nalgebra::{Quaternion, Unit, UnitQuaternion, Vector3}; -use rlua::{Context, FromLua, Function, Number, Table, ToLua, Value}; +use rlua::{Context, FromLua, Function, Number, ToLua, Value}; use crate::{LuaEngine, ValueExt}; use crate::number::{NumFromLua, NumToLua, Num}; use crate::macros::auto_lib; From bcbd17b7aadc7b2891670fca5f100a77539578e7 Mon Sep 17 00:00:00 2001 From: Yuri6037 Date: Sun, 30 Oct 2022 12:05:56 +0100 Subject: [PATCH 014/527] Added support for GitHub Actions --- .github/workflows/development.yml | 141 ++++++++++++++++++++++++++++++ .github/workflows/release.yml | 48 ++++++++++ 2 files changed, 189 insertions(+) create mode 100644 .github/workflows/development.yml create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml new file mode 100644 index 0000000..64e8000 --- /dev/null +++ b/.github/workflows/development.yml @@ -0,0 +1,141 @@ +name: Development + +on: + - push + - workflow_dispatch + +jobs: + test-build: + name: Build & Test + strategy: + matrix: + os: + - ubuntu-20.04 + - macos-11 + - windows-2019 + runs-on: ${{ matrix.os }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Build + run: cargo build --all-features + - name: Test + uses: bp3d-actions/cargo@main + with: + check-name: cargo test (${{ matrix.os }}) + command: test + args: --all-features --no-fail-fast + token: ${{ secrets.GITHUB_TOKEN }} + + clippy: + name: Check | Clippy + if: ${{ always() }} + needs: test-build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: clippy + - name: Run check + uses: bp3d-actions/clippy-check@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + args: --all-features + + audit: + name: Check | Audit + if: ${{ always() }} + needs: test-build + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Install Audit Tool + run: cargo install cargo-audit + - name: Run check + uses: bp3d-actions/audit-check@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + + fmt: + name: Format Code + if: ${{ always() && github.ref != 'refs/heads/master' }} + needs: + - clippy + - audit + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt + - name: Run code formatter + uses: bp3d-actions/rustfmt-check@main + with: + token: ${{ secrets.GITHUB_TOKEN }} + + version: + name: Get Version + needs: test-build + runs-on: ubuntu-latest + outputs: + name: ${{ steps.version.outputs.name }} + version: ${{ steps.version.outputs.version }} + isnew: ${{ steps.version.outputs.isnew }} + ispre: ${{ steps.version.outputs.ispre }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Get Version + id: version + uses: bp3d-actions/cargo-version@main + with: + mode: get + token: ${{ secrets.GITHUB_TOKEN }} + + create-pre-release: + name: Create Pre Release + needs: version + if: github.ref == 'refs/heads/develop' && needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'true' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Setup cargo + run: cargo login ${{ secrets.RELEASE_TOKEN }} + - name: Publish + run: cargo publish + - name: Create + uses: ncipollo/release-action@main + with: + tag: ${{ needs.version.outputs.version }} + commit: ${{ github.ref }} + prerelease: true + name: ${{ needs.version.outputs.name }} release ${{ needs.version.outputs.version }} + body: "[Link to crates.io](https://crates.io/crates/${{ needs.version.outputs.name }})" + + create-release-pr: + name: Create Release Pull Request + needs: version + if: needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'false' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Create Pull Request + uses: repo-sync/pull-request@master + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + destination_branch: master + pr_title: Release ${{ needs.version.outputs.version }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..cf2e432 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,48 @@ +name: Release + +on: + push: + branches: + - master + +jobs: + version: + name: Get Version + runs-on: ubuntu-latest + outputs: + name: ${{ steps.version.outputs.name }} + version: ${{ steps.version.outputs.version }} + isnew: ${{ steps.version.outputs.isnew }} + ispre: ${{ steps.version.outputs.ispre }} + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Get Version + id: version + uses: bp3d-actions/cargo-version@main + with: + mode: get + token: ${{ secrets.GITHUB_TOKEN }} + + create-release: + name: Release + needs: version + if: needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'false' + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v3 + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + - name: Setup cargo + run: cargo login ${{ secrets.RELEASE_TOKEN }} + - name: Publish + run: cargo publish + - name: Create + uses: ncipollo/release-action@main + with: + tag: ${{ needs.version.outputs.version }} + commit: ${{ github.ref }} + prerelease: false + name: ${{ needs.version.outputs.name }} release ${{ needs.version.outputs.version }} + body: "[Link to crates.io](https://crates.io/crates/${{ needs.version.outputs.name }})" From 744622da3ecda1b7d38cdac48844eb5c1f7b413a Mon Sep 17 00:00:00 2001 From: Yuri6037 Date: Sun, 30 Oct 2022 12:05:56 +0100 Subject: [PATCH 015/527] Removed support for GitLab CI --- .gitlab-ci.yml | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 .gitlab-ci.yml diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index 62b7e5a..0000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,7 +0,0 @@ -include: - - project: bp3d/cargo-gitlab - file: lib-template.yml - -variables: - CRATE_NAME: bp3d-lua - PROJECT_NAME: Lua From d5d55c0bab5f5568b64e76a6d4ecbb902ceb031a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 30 Oct 2022 11:12:14 +0000 Subject: [PATCH 016/527] Format Rust code using rustfmt --- src/engine.rs | 41 +++++++++----- src/ext.rs | 16 +++--- src/macros.rs | 12 +++- src/math.rs | 4 +- src/noise.rs | 38 ++++++++----- src/number.rs | 23 +++++--- src/quaternion.rs | 90 +++++++++++++++++------------- src/vector.rs | 136 +++++++++++++++++++++++++++------------------- 8 files changed, 216 insertions(+), 144 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index e6aa5f0..9a0a45d 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -26,16 +26,24 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rlua::{Context, FromLuaMulti, Lua, StdLib, Table, ToLua, ToLuaMulti, Value}; use crate::TableExt; +use rlua::{Context, FromLuaMulti, Lua, StdLib, Table, ToLua, ToLuaMulti, Value}; pub struct LibContext<'a> { ctx: Context<'a>, - table: Table<'a> + table: Table<'a>, } impl<'a> LibContext<'a> { - pub fn function, R: ToLuaMulti<'a>, F: 'static + Send + Fn(Context, A) -> rlua::Result>(&self, name: &str, function: F) -> rlua::Result<()> { + pub fn function< + A: FromLuaMulti<'a>, + R: ToLuaMulti<'a>, + F: 'static + Send + Fn(Context, A) -> rlua::Result, + >( + &self, + name: &str, + function: F, + ) -> rlua::Result<()> { let func = self.ctx.create_function(function)?; self.table.raw_set(name, func)?; Ok(()) @@ -47,7 +55,7 @@ impl<'a> LibContext<'a> { } pub struct LuaEngine { - state: Lua + state: Lua, } fn strip_potentially_dangerous(state: &Lua) -> rlua::Result<()> { @@ -64,20 +72,22 @@ fn strip_potentially_dangerous(state: &Lua) -> rlua::Result<()> { impl LuaEngine { pub fn new() -> rlua::Result { - let state = Lua::new_with(StdLib::BASE | StdLib::UTF8 | StdLib::STRING | StdLib::TABLE | StdLib::COROUTINE); + let state = Lua::new_with( + StdLib::BASE | StdLib::UTF8 | StdLib::STRING | StdLib::TABLE | StdLib::COROUTINE, + ); strip_potentially_dangerous(&state)?; - Ok(LuaEngine { - state - }) + Ok(LuaEngine { state }) } - pub fn create_library rlua::Result<()>>(&self, name: &str, self_callable: bool, function: F) -> rlua::Result<()> { + pub fn create_library rlua::Result<()>>( + &self, + name: &str, + self_callable: bool, + function: F, + ) -> rlua::Result<()> { self.state.context(|ctx| { let table = ctx.create_table()?; - let libctx = LibContext { - ctx, - table - }; + let libctx = LibContext { ctx, table }; function(&libctx)?; if self_callable { libctx.table.enable_self_callable()?; @@ -88,7 +98,10 @@ impl LuaEngine { }) } - pub fn context rlua::Result>(&self, function: F) -> rlua::Result { + pub fn context rlua::Result>( + &self, + function: F, + ) -> rlua::Result { self.state.context(function) } } diff --git a/src/ext.rs b/src/ext.rs index 3057090..ddd7af5 100644 --- a/src/ext.rs +++ b/src/ext.rs @@ -31,9 +31,9 @@ // These functions are non standard but used to optimize 3D engines where access to numbers // MUST be VERY fast. -use std::cell::Ref; -use rlua::{Integer, Number, Table, Value, Result, Error}; use rlua::prelude::LuaUserData; +use rlua::{Error, Integer, Number, Result, Table, Value}; +use std::cell::Ref; pub trait ValueExt<'a> { fn check_number(self) -> Result; @@ -53,7 +53,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "Number", message: Some("expected number".to_string()), - }) + }), } } @@ -65,7 +65,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "Integer", message: Some("expected integer".to_string()), - }) + }), } } @@ -76,7 +76,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "Table", message: Some("expected table".to_string()), - }) + }), } } @@ -87,7 +87,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "Boolean", message: Some("expected boolean".to_string()), - }) + }), } } @@ -98,7 +98,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "String", message: Some("expected string".to_string()), - }) + }), } } @@ -109,7 +109,7 @@ impl<'a> ValueExt<'a> for Value<'a> { from: self.type_name(), to: "Userdata", message: Some("expected userdata".to_string()), - }) + }), } } } diff --git a/src/macros.rs b/src/macros.rs index d73da6f..018fb1c 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -53,7 +53,10 @@ macro_rules! vec_wrapper_2 { macro_rules! vec_wrapper_3 { ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty) => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, $other, $other2): ($in, $in2, $in3)) -> rlua::Result<$out> { + fn $name<'a>( + _: Context<'a>, + (this, $other, $other2): ($in, $in2, $in3), + ) -> rlua::Result<$out> { let $this = this.into_inner(); Ok($code) } @@ -62,7 +65,10 @@ macro_rules! vec_wrapper_3 { macro_rules! vec_wrapper_4 { ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty, $other3: ident: $in4: ty) => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, $other, $other2, $other3): ($in, $in2, $in3, $in4)) -> rlua::Result<$out> { + fn $name<'a>( + _: Context<'a>, + (this, $other, $other2, $other3): ($in, $in2, $in3, $in4), + ) -> rlua::Result<$out> { let $this = this.into_inner(); Ok($code) } @@ -91,6 +97,6 @@ macro_rules! vec_wrapper_2_uniform { pub(crate) use auto_lib; pub(crate) use vec_wrapper_1; pub(crate) use vec_wrapper_2; +pub(crate) use vec_wrapper_2_uniform; pub(crate) use vec_wrapper_3; pub(crate) use vec_wrapper_4; -pub(crate) use vec_wrapper_2_uniform; diff --git a/src/math.rs b/src/math.rs index 29698df..5466ba2 100644 --- a/src/math.rs +++ b/src/math.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use rlua::{Context, Number}; -use crate::LuaEngine; use crate::number::{Int, Num}; +use crate::LuaEngine; +use rlua::{Context, Number}; fn math_clamp(_: Context, (x, min, max): (Num, Num, Num)) -> rlua::Result { if x.0 > max.0 { diff --git a/src/noise.rs b/src/noise.rs index e49bd49..94e57e2 100644 --- a/src/noise.rs +++ b/src/noise.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::LuaEngine; use crate::number::{Checked, Int, Num}; -use rlua::UserData; -use rlua::UserDataMethods; -use noise::Perlin; -use noise::NoiseFn; +use crate::LuaEngine; use noise::Billow; +use noise::NoiseFn; +use noise::Perlin; use noise::Seedable; +use rlua::UserData; +use rlua::UserDataMethods; pub trait LibNoise { fn load_noise(&self) -> rlua::Result<()>; @@ -44,9 +44,15 @@ struct LuaPerlin(Perlin); impl UserData for LuaPerlin { fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); - methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| Ok(this.0.get([x.0, y.0]))); - methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0]))); - methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0, w.0]))); + methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| { + Ok(this.0.get([x.0, y.0])) + }); + methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| { + Ok(this.0.get([x.0, y.0, z.0])) + }); + methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| { + Ok(this.0.get([x.0, y.0, z.0, w.0])) + }); } } @@ -55,9 +61,15 @@ struct LuaBillow(Billow); impl UserData for LuaBillow { fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); - methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| Ok(this.0.get([x.0, y.0]))); - methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0]))); - methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| Ok(this.0.get([x.0, y.0, z.0, w.0]))); + methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| { + Ok(this.0.get([x.0, y.0])) + }); + methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| { + Ok(this.0.get([x.0, y.0, z.0])) + }); + methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| { + Ok(this.0.get([x.0, y.0, z.0, w.0])) + }); methods.add_method("getOctaves", |_, this, ()| Ok(Int(this.0.octaves as _))); methods.add_method("getFrequency", |_, this, ()| Ok(this.0.frequency)); methods.add_method("getLacunarity", |_, this, ()| Ok(this.0.lacunarity)); @@ -87,13 +99,13 @@ impl LibNoise for LuaEngine { ctx.function("perlin", |_, seed: Option>| { Ok(LuaPerlin(match seed { None => Perlin::new(), - Some(seed) => Perlin::new().set_seed(seed.0) + Some(seed) => Perlin::new().set_seed(seed.0), })) })?; ctx.function("billow", |_, seed: Option>| { Ok(LuaBillow(match seed { None => Billow::new(), - Some(seed) => Billow::new().set_seed(seed.0) + Some(seed) => Billow::new().set_seed(seed.0), })) })?; Ok(()) diff --git a/src/number.rs b/src/number.rs index 428cdfe..b67ca90 100644 --- a/src/number.rs +++ b/src/number.rs @@ -28,10 +28,10 @@ //! Fast implementation of numbers and integers. +use crate::ValueExt; use num_traits::{cast, NumCast}; use rlua::Error; use rlua::{Context, FromLua, Integer, Number, ToLua, Value}; -use crate::ValueExt; /// Fast encodable/decodable float type with no coercion and no range checking. #[derive(Copy, Clone, Debug, Default)] @@ -84,7 +84,10 @@ pub trait NumToLua { fn num_to_lua<'a>(self) -> Value<'a>; } -pub trait NumFromLua where Self: Sized { +pub trait NumFromLua +where + Self: Sized, +{ fn type_name() -> &'static str; fn num_from_lua(val: Value) -> rlua::Result; } @@ -147,17 +150,19 @@ impl<'lua, T: NumFromLua + NumCast> FromLua<'lua> for Checked { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { match lua_value { Value::Integer(v) => { - cast(v).map(Checked).ok_or_else(|| Error::FromLuaConversionError { - from: lua_value.type_name(), - to: T::type_name(), - message: Some("out of range".to_string()), - }) - }, + cast(v) + .map(Checked) + .ok_or_else(|| Error::FromLuaConversionError { + from: lua_value.type_name(), + to: T::type_name(), + message: Some("out of range".to_string()), + }) + } _ => Err(Error::FromLuaConversionError { from: lua_value.type_name(), to: T::type_name(), message: Some("expected integer".to_string()), - }) + }), } } } diff --git a/src/quaternion.rs b/src/quaternion.rs index 75cd948..12cb3fb 100644 --- a/src/quaternion.rs +++ b/src/quaternion.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use nalgebra::{Quaternion, Unit, UnitQuaternion, Vector3}; -use rlua::{Context, FromLua, Function, Number, ToLua, Value}; -use crate::{LuaEngine, ValueExt}; -use crate::number::{NumFromLua, NumToLua, Num}; use crate::macros::auto_lib; +use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_2; use crate::macros::vec_wrapper_2_uniform; -use crate::macros::vec_wrapper_1; use crate::macros::vec_wrapper_3; use crate::macros::vec_wrapper_4; +use crate::number::{Num, NumFromLua, NumToLua}; +use crate::{LuaEngine, ValueExt}; +use nalgebra::{Quaternion, Unit, UnitQuaternion, Vector3}; +use rlua::{Context, FromLua, Function, Number, ToLua, Value}; pub trait LibQuaternion { fn load_quaternion(&self) -> rlua::Result<()>; @@ -70,17 +70,24 @@ impl LuaQuat { } impl<'lua, T> ToLua<'lua> for LuaQuat - where T: NumToLua +where + T: NumToLua, { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(QUAT_NEW)?; let [[i, j, k, w]] = self.0.coords.data.0; - func.call((w.num_to_lua(), i.num_to_lua(), j.num_to_lua(), k.num_to_lua())) + func.call(( + w.num_to_lua(), + i.num_to_lua(), + j.num_to_lua(), + k.num_to_lua(), + )) } } impl<'lua, T> FromLua<'lua> for LuaQuat - where T: NumFromLua +where + T: NumFromLua, { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; @@ -88,7 +95,7 @@ impl<'lua, T> FromLua<'lua> for LuaQuat T::num_from_lua(table.raw_get("w")?)?, T::num_from_lua(table.raw_get("i")?)?, T::num_from_lua(table.raw_get("j")?)?, - T::num_from_lua(table.raw_get("k")?)? + T::num_from_lua(table.raw_get("k")?)?, ))) } } @@ -165,36 +172,41 @@ impl LibQuaternion for LuaEngine { })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (v1, v2, v3, z): (Value, Option, Option, Option)| { - let v = match (v2, v3, z) { - (Some(i), Some(j), Some(k)) => { - let w = v1.check_number()?; - Quaternion::new(w, i.0, j.0, k.0) - }, // wijk constructor - (Some(pitch), Some(yaw), None) => { - let roll = v1.check_number()?; - UnitQuaternion::from_euler_angles(roll, pitch.0, yaw.0) - .into_inner() - }, // roll, pitch, yaw constructor - (Some(angle), None, None) => { - let axis = v1.check_table()?; - let vec = Unit::new_unchecked(Vector3::new(axis.raw_get("x")?, axis.raw_get("y")?, axis.raw_get("z")?)); - UnitQuaternion::from_axis_angle(&vec, angle.0).into_inner() - }, // axis, angle constructor - _ => { - let real = v1.check_number()?; - Quaternion::from_real(real) - } - }; - let table = ctx.create_table()?; - table.raw_set("i", v.i)?; - table.raw_set("j", v.j)?; - table.raw_set("k", v.k)?; - table.raw_set("w", v.w)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(QUAT_LIB)?); - Ok(table) - })?; + let function = ctx.create_function( + |ctx, (v1, v2, v3, z): (Value, Option, Option, Option)| { + let v = match (v2, v3, z) { + (Some(i), Some(j), Some(k)) => { + let w = v1.check_number()?; + Quaternion::new(w, i.0, j.0, k.0) + } // wijk constructor + (Some(pitch), Some(yaw), None) => { + let roll = v1.check_number()?; + UnitQuaternion::from_euler_angles(roll, pitch.0, yaw.0).into_inner() + } // roll, pitch, yaw constructor + (Some(angle), None, None) => { + let axis = v1.check_table()?; + let vec = Unit::new_unchecked(Vector3::new( + axis.raw_get("x")?, + axis.raw_get("y")?, + axis.raw_get("z")?, + )); + UnitQuaternion::from_axis_angle(&vec, angle.0).into_inner() + } // axis, angle constructor + _ => { + let real = v1.check_number()?; + Quaternion::from_real(real) + } + }; + let table = ctx.create_table()?; + table.raw_set("i", v.i)?; + table.raw_set("j", v.j)?; + table.raw_set("k", v.k)?; + table.raw_set("w", v.w)?; + let globals = ctx.globals(); + table.set_metatable(globals.raw_get(QUAT_LIB)?); + Ok(table) + }, + )?; let globals = ctx.globals(); globals.raw_set(QUAT_NEW, function)?; Ok(()) diff --git a/src/vector.rs b/src/vector.rs index f3bac0d..04c358d 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use nalgebra::{Vector2, Vector3, Vector4}; -use rlua::{Context, FromLua, Function, Number, ToLua, Value}; -use crate::{LuaEngine, ValueExt}; -use crate::number::{Num, Int, NumFromLua, NumToLua}; -use crate::macros::vec_wrapper_3; -use crate::macros::vec_wrapper_2; +use crate::macros::auto_lib; use crate::macros::vec_wrapper_1; +use crate::macros::vec_wrapper_2; use crate::macros::vec_wrapper_2_uniform; -use crate::macros::auto_lib; +use crate::macros::vec_wrapper_3; +use crate::number::{Int, Num, NumFromLua, NumToLua}; +use crate::{LuaEngine, ValueExt}; +use nalgebra::{Vector2, Vector3, Vector4}; +use rlua::{Context, FromLua, Function, Number, ToLua, Value}; pub trait LibVector { fn load_vec2(&self) -> rlua::Result<()>; @@ -75,7 +75,8 @@ impl LuaVec2 { } impl<'lua, T> ToLua<'lua> for LuaVec2 - where T: NumToLua +where + T: NumToLua, { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC2_NEW)?; @@ -85,13 +86,14 @@ impl<'lua, T> ToLua<'lua> for LuaVec2 } impl<'lua, T> FromLua<'lua> for LuaVec2 - where T: NumFromLua +where + T: NumFromLua, { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; Ok(LuaVec2(nalgebra::Vector2::new( T::num_from_lua(table.raw_get("x")?)?, - T::num_from_lua(table.raw_get("y")?)? + T::num_from_lua(table.raw_get("y")?)?, ))) } } @@ -121,7 +123,8 @@ impl LuaVec3 { } impl<'lua, T> ToLua<'lua> for LuaVec3 - where T: NumToLua +where + T: NumToLua, { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC3_NEW)?; @@ -131,14 +134,15 @@ impl<'lua, T> ToLua<'lua> for LuaVec3 } impl<'lua, T> FromLua<'lua> for LuaVec3 - where T: NumFromLua +where + T: NumFromLua, { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; Ok(LuaVec3(nalgebra::Vector3::new( T::num_from_lua(table.raw_get("x")?)?, T::num_from_lua(table.raw_get("y")?)?, - T::num_from_lua(table.raw_get("z")?)? + T::num_from_lua(table.raw_get("z")?)?, ))) } } @@ -168,17 +172,24 @@ impl LuaVec4 { } impl<'lua, T> ToLua<'lua> for LuaVec4 - where T: NumToLua +where + T: NumToLua, { fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { let func: Function = lua.globals().raw_get(VEC4_NEW)?; let [[x, y, z, w]] = self.0.data.0; - func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua(), w.num_to_lua())) + func.call(( + x.num_to_lua(), + y.num_to_lua(), + z.num_to_lua(), + w.num_to_lua(), + )) } } impl<'lua, T> FromLua<'lua> for LuaVec4 - where T: NumFromLua +where + T: NumFromLua, { fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { let table = lua_value.check_table()?; @@ -186,7 +197,7 @@ impl<'lua, T> FromLua<'lua> for LuaVec4 T::num_from_lua(table.raw_get("x")?)?, T::num_from_lua(table.raw_get("y")?)?, T::num_from_lua(table.raw_get("z")?)?, - T::num_from_lua(table.raw_get("w")?)? + T::num_from_lua(table.raw_get("w")?)?, ))) } } @@ -274,7 +285,7 @@ impl LibVector for LuaEngine { let function = ctx.create_function(|ctx, (x, y): (Num, Option)| { let val = match y { Some(y) => Vector2::new(x.0, y.0), - None => Vector2::from_element(x.0) + None => Vector2::from_element(x.0), }; let table = ctx.create_table()?; table.raw_set("x", val.x)?; @@ -306,19 +317,20 @@ impl LibVector for LuaEngine { })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y, z): (Num, Option, Option)| { - let val = match (y, z) { - (Some(y), Some(z)) => Vector3::new(x.0, y.0, z.0), - _ => Vector3::from_element(x.0) - }; - let table = ctx.create_table()?; - table.raw_set("x", val.x)?; - table.raw_set("y", val.y)?; - table.raw_set("z", val.z)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(VEC3_LIB)?); - Ok(table) - })?; + let function = + ctx.create_function(|ctx, (x, y, z): (Num, Option, Option)| { + let val = match (y, z) { + (Some(y), Some(z)) => Vector3::new(x.0, y.0, z.0), + _ => Vector3::from_element(x.0), + }; + let table = ctx.create_table()?; + table.raw_set("x", val.x)?; + table.raw_set("y", val.y)?; + table.raw_set("z", val.z)?; + let globals = ctx.globals(); + table.set_metatable(globals.raw_get(VEC3_LIB)?); + Ok(table) + })?; let globals = ctx.globals(); globals.raw_set(VEC3_NEW, function)?; Ok(()) @@ -336,20 +348,22 @@ impl LibVector for LuaEngine { })?; //Create constructor function. self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y, z, w): (Num, Option, Option, Option)| { - let val = match (y, z, w) { - (Some(y), Some(z), Some(w)) => Vector4::new(x.0, y.0, z.0, w.0), - _ => Vector4::from_element(x.0) - }; - let table = ctx.create_table()?; - table.raw_set("x", val.x)?; - table.raw_set("y", val.y)?; - table.raw_set("z", val.z)?; - table.raw_set("w", val.w)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(VEC4_LIB)?); - Ok(table) - })?; + let function = ctx.create_function( + |ctx, (x, y, z, w): (Num, Option, Option, Option)| { + let val = match (y, z, w) { + (Some(y), Some(z), Some(w)) => Vector4::new(x.0, y.0, z.0, w.0), + _ => Vector4::from_element(x.0), + }; + let table = ctx.create_table()?; + table.raw_set("x", val.x)?; + table.raw_set("y", val.y)?; + table.raw_set("z", val.z)?; + table.raw_set("w", val.w)?; + let globals = ctx.globals(); + table.set_metatable(globals.raw_get(VEC4_LIB)?); + Ok(table) + }, + )?; let globals = ctx.globals(); globals.raw_set(VEC4_NEW, function)?; Ok(()) @@ -359,15 +373,17 @@ impl LibVector for LuaEngine { #[cfg(test)] mod tests { - use crate::LuaEngine; use crate::vector::LibVector; + use crate::LuaEngine; #[test] fn basic() { let engine = LuaEngine::new().unwrap(); engine.load_vec2().unwrap(); - engine.context(|ctx| { - ctx.load(r#" + engine + .context(|ctx| { + ctx.load( + r#" local v = Vec2(0, 0) local v1 = Vec2(2.5, 2.5) local add = v + v1 @@ -379,23 +395,31 @@ mod tests { print(sub.x, sub.y) print(div.x, div.y) print((-div):norm()) - "#).exec()?; - Ok(()) - }).unwrap(); + "#, + ) + .exec()?; + Ok(()) + }) + .unwrap(); } #[test] fn mutate() { let engine = LuaEngine::new().unwrap(); engine.load_vec2().unwrap(); - engine.context(|ctx| { - ctx.load(r#" + engine + .context(|ctx| { + ctx.load( + r#" local v = Vec2(0.1, 0.1) v.x = 0 v.y = 5 print(v:argmax()) - "#).exec()?; - Ok(()) - }).unwrap(); + "#, + ) + .exec()?; + Ok(()) + }) + .unwrap(); } } From 84d419a973486aa460021e882f2d6cb143c69c3c Mon Sep 17 00:00:00 2001 From: Yuri6037 Date: Thu, 10 Nov 2022 23:36:14 +0100 Subject: [PATCH 017/527] Updated rlua to disable the OS lib --- Cargo.toml | 2 +- src/vector.rs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 4416885..46d831f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,7 +15,7 @@ categories = ["graphics"] [dependencies] nalgebra = { version = "0.30.1", optional = true } num-traits = "0.2.14" -rlua = "0.19.1" +rlua = { version = "0.19.4", features = ["lua-no-oslib"] } noise = { version = "0.7.0", optional = true } rand = {version = "0.8.5", optional = true } diff --git a/src/vector.rs b/src/vector.rs index f3bac0d..85448ee 100644 --- a/src/vector.rs +++ b/src/vector.rs @@ -199,6 +199,8 @@ fn argminmax_to_lua((id, val): (usize, Number)) -> (Int, Num) { (Int(id as _), Num(val)) } +//TODO: create special wrappers for operators to allow simple operations with scalars. + vec_wrapper_2_uniform!(vec2_add (a, b): Vec2 => Vec2 {(a + b).into()}); vec_wrapper_2_uniform!(vec2_sub (a, b): Vec2 => Vec2 {(a - b).into()}); vec_wrapper_2_uniform!(vec2_mul (a, b): Vec2 => Vec2 {a.component_mul(&b).into()}); From 0b6d26c8fd6c07b5a945d2f2d1aba15524040ea4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Feb 2025 20:29:17 +0100 Subject: [PATCH 018/527] Initialize new branch --- Cargo.toml | 13 +- src/engine.rs | 107 ------------ src/ext.rs | 126 -------------- src/ffi/lua.rs | 28 +++ src/ffi/mod.rs | 29 ++++ src/lib.rs | 22 +-- src/macros.rs | 102 ----------- src/math.rs | 98 ----------- src/noise.rs | 114 ------------- src/number.rs | 174 ------------------- src/quaternion.rs | 215 ----------------------- src/vector.rs | 427 ---------------------------------------------- 12 files changed, 61 insertions(+), 1394 deletions(-) delete mode 100644 src/engine.rs delete mode 100644 src/ext.rs create mode 100644 src/ffi/lua.rs create mode 100644 src/ffi/mod.rs delete mode 100644 src/macros.rs delete mode 100644 src/math.rs delete mode 100644 src/noise.rs delete mode 100644 src/number.rs delete mode 100644 src/quaternion.rs delete mode 100644 src/vector.rs diff --git a/Cargo.toml b/Cargo.toml index 46d831f..c888701 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,9 @@ [package] name = "bp3d-lua" -version = "0.1.0" +version = "0.2.0" authors = ["Yuri Edward "] edition = "2021" -description = "Lua base library and tools for BP3D." +description = "Lua wrapper and base library for BP3D." license = "BSD-3-Clause" repository = "https://gitlab.com/bp3d/lua" readme = "./README.MD" @@ -13,14 +13,5 @@ categories = ["graphics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -nalgebra = { version = "0.30.1", optional = true } -num-traits = "0.2.14" -rlua = { version = "0.19.4", features = ["lua-no-oslib"] } -noise = { version = "0.7.0", optional = true } -rand = {version = "0.8.5", optional = true } [features] -math = [] -vector = ["nalgebra"] -quaternion = ["vector"] -random = ["rand"] diff --git a/src/engine.rs b/src/engine.rs deleted file mode 100644 index 9a0a45d..0000000 --- a/src/engine.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::TableExt; -use rlua::{Context, FromLuaMulti, Lua, StdLib, Table, ToLua, ToLuaMulti, Value}; - -pub struct LibContext<'a> { - ctx: Context<'a>, - table: Table<'a>, -} - -impl<'a> LibContext<'a> { - pub fn function< - A: FromLuaMulti<'a>, - R: ToLuaMulti<'a>, - F: 'static + Send + Fn(Context, A) -> rlua::Result, - >( - &self, - name: &str, - function: F, - ) -> rlua::Result<()> { - let func = self.ctx.create_function(function)?; - self.table.raw_set(name, func)?; - Ok(()) - } - - pub fn constant>(&self, name: &str, val: T) -> rlua::Result<()> { - self.table.raw_set(name, val) - } -} - -pub struct LuaEngine { - state: Lua, -} - -fn strip_potentially_dangerous(state: &Lua) -> rlua::Result<()> { - state.context(|ctx| { - let globals = ctx.globals(); - //Basically all these functions allows arbitrary lua code injection without source checking. - globals.raw_set("dofile", Value::Nil)?; - globals.raw_set("load", Value::Nil)?; - globals.raw_set("loadfile", Value::Nil)?; - globals.raw_set("loadstring", Value::Nil)?; - Ok(()) - }) -} - -impl LuaEngine { - pub fn new() -> rlua::Result { - let state = Lua::new_with( - StdLib::BASE | StdLib::UTF8 | StdLib::STRING | StdLib::TABLE | StdLib::COROUTINE, - ); - strip_potentially_dangerous(&state)?; - Ok(LuaEngine { state }) - } - - pub fn create_library rlua::Result<()>>( - &self, - name: &str, - self_callable: bool, - function: F, - ) -> rlua::Result<()> { - self.state.context(|ctx| { - let table = ctx.create_table()?; - let libctx = LibContext { ctx, table }; - function(&libctx)?; - if self_callable { - libctx.table.enable_self_callable()?; - } - let globals = libctx.ctx.globals(); - globals.raw_set(name, libctx.table)?; - Ok(()) - }) - } - - pub fn context rlua::Result>( - &self, - function: F, - ) -> rlua::Result { - self.state.context(function) - } -} diff --git a/src/ext.rs b/src/ext.rs deleted file mode 100644 index ddd7af5..0000000 --- a/src/ext.rs +++ /dev/null @@ -1,126 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// This module contains lua engine extensions, some of which existed in the C API. - -// These functions are non standard but used to optimize 3D engines where access to numbers -// MUST be VERY fast. - -use rlua::prelude::LuaUserData; -use rlua::{Error, Integer, Number, Result, Table, Value}; -use std::cell::Ref; - -pub trait ValueExt<'a> { - fn check_number(self) -> Result; - fn check_integer(self) -> Result; - fn check_table(self) -> Result>; - fn check_bool(self) -> Result; - fn check_string(&self) -> Result<&str>; - fn check_userdata(&self) -> Result>; -} - -impl<'a> ValueExt<'a> for Value<'a> { - fn check_number(self) -> Result { - match self { - Value::Number(v) => Ok(v), - Value::Integer(v) => Ok(v as Number), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "Number", - message: Some("expected number".to_string()), - }), - } - } - - fn check_integer(self) -> Result { - match self { - Value::Integer(v) => Ok(v), - Value::Number(v) => Ok(v as Integer), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "Integer", - message: Some("expected integer".to_string()), - }), - } - } - - fn check_table(self) -> Result> { - match self { - Value::Table(v) => Ok(v), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "Table", - message: Some("expected table".to_string()), - }), - } - } - - fn check_bool(self) -> Result { - match self { - Value::Boolean(v) => Ok(v), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "Boolean", - message: Some("expected boolean".to_string()), - }), - } - } - - fn check_string(&self) -> Result<&str> { - match self { - Value::String(v) => v.to_str(), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "String", - message: Some("expected string".to_string()), - }), - } - } - - fn check_userdata(&self) -> Result> { - match self { - Value::UserData(v) => v.borrow(), - _ => Err(Error::FromLuaConversionError { - from: self.type_name(), - to: "Userdata", - message: Some("expected userdata".to_string()), - }), - } - } -} - -pub trait TableExt { - fn enable_self_callable(&self) -> rlua::Result<()>; -} - -impl<'a> TableExt for Table<'a> { - fn enable_self_callable(&self) -> rlua::Result<()> { - let selfcopy = self.clone(); - self.raw_set("__index", selfcopy) - } -} diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs new file mode 100644 index 0000000..9ae0ae5 --- /dev/null +++ b/src/ffi/lua.rs @@ -0,0 +1,28 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs new file mode 100644 index 0000000..2543c7f --- /dev/null +++ b/src/ffi/mod.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod lua; diff --git a/src/lib.rs b/src/lib.rs index a3acac3..904633c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2022, BlockProject 3D +// Copyright (c) 2025, BlockProject 3D // // All rights reserved. // @@ -26,22 +26,4 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod engine; -mod ext; -mod macros; -pub mod number; - -#[cfg(feature = "math")] -pub mod math; - -#[cfg(feature = "vector")] -pub mod vector; - -#[cfg(feature = "quaternion")] -pub mod quaternion; - -#[cfg(feature = "noise")] -pub mod noise; - -pub use engine::*; -pub use ext::*; +pub mod ffi; diff --git a/src/macros.rs b/src/macros.rs deleted file mode 100644 index 018fb1c..0000000 --- a/src/macros.rs +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Some macros are only used with vector feature and most macros are used in both quaternion and -// features; so just disable the warnings. -#![allow(unused_macros)] -#![allow(unused_imports)] - -macro_rules! auto_lib { - ($lua: ident ($lib: ident, $self_callable: expr) { $($name: ident: $fn: ident,)* }) => { - $lua.create_library($lib, $self_callable, |ctx| { - $( - ctx.function(stringify!($name), $fn)?; - )* - Ok(()) - }) - }; -} - -macro_rules! vec_wrapper_2 { - ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty) => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, $other): ($in, $in2)) -> rlua::Result<$out> { - let $this = this.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_3 { - ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty) => $out: ty { $code: expr }) => { - fn $name<'a>( - _: Context<'a>, - (this, $other, $other2): ($in, $in2, $in3), - ) -> rlua::Result<$out> { - let $this = this.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_4 { - ($name: ident ($this: ident: $in: ty, $other: ident: $in2: ty, $other2: ident: $in3: ty, $other3: ident: $in4: ty) => $out: ty { $code: expr }) => { - fn $name<'a>( - _: Context<'a>, - (this, $other, $other2, $other3): ($in, $in2, $in3, $in4), - ) -> rlua::Result<$out> { - let $this = this.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_1 { - ($name: ident ($this: ident: $in: ty) => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, this: $in) -> rlua::Result<$out> { - let $this = this.into_inner(); - Ok($code) - } - }; -} - -macro_rules! vec_wrapper_2_uniform { - ($name: ident ($this: ident, $other: ident): $in: ty => $out: ty { $code: expr }) => { - fn $name<'a>(_: Context<'a>, (this, other): ($in, $in)) -> rlua::Result<$out> { - let $this = this.into_inner(); - let $other = other.into_inner(); - Ok($code) - } - }; -} - -pub(crate) use auto_lib; -pub(crate) use vec_wrapper_1; -pub(crate) use vec_wrapper_2; -pub(crate) use vec_wrapper_2_uniform; -pub(crate) use vec_wrapper_3; -pub(crate) use vec_wrapper_4; diff --git a/src/math.rs b/src/math.rs deleted file mode 100644 index 5466ba2..0000000 --- a/src/math.rs +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::number::{Int, Num}; -use crate::LuaEngine; -use rlua::{Context, Number}; - -fn math_clamp(_: Context, (x, min, max): (Num, Num, Num)) -> rlua::Result { - if x.0 > max.0 { - Ok(max) - } else if x.0 < min.0 { - Ok(min) - } else { - Ok(x) - } -} - -fn math_round(_: Context, (x, decimals): (Num, Int)) -> rlua::Result { - let power = u32::pow(10, decimals.0 as _) as Number; - Ok((x.0 * power).round() / power) -} - -fn math_gaussian(_: Context, (sigma, x): (Num, Num)) -> rlua::Result { - let term1 = 1.0 / 2.0 * std::f64::consts::PI * (sigma.0 * sigma.0); - let term2 = (-(x.0 / (2.0 * sigma.0 * sigma.0))).exp(); - Ok(term1 * term2) -} - -// The reason why we provide a custom math lib is to have on par implementation with rust -// and nalgebra, required for accurate rendering with the engine. -pub trait LibMath { - fn load_math(&self) -> rlua::Result<()>; -} - -impl LibMath for LuaEngine { - fn load_math(&self) -> rlua::Result<()> { - self.create_library("math", false, |ctx| { - ctx.constant("pi", std::f64::consts::PI)?; - ctx.constant("e", std::f64::consts::E)?; - ctx.function("cos", |_, x: Num| Ok(x.0.cos()))?; - ctx.function("sin", |_, x: Num| Ok(x.0.sin()))?; - ctx.function("tan", |_, x: Num| Ok(x.0.tan()))?; - ctx.function("acos", |_, x: Num| Ok(x.0.acos()))?; - ctx.function("asin", |_, x: Num| Ok(x.0.asin()))?; - ctx.function("atan", |_, x: Num| Ok(x.0.atan()))?; - ctx.function("cosh", |_, x: Num| Ok(x.0.cosh()))?; - ctx.function("sinh", |_, x: Num| Ok(x.0.sinh()))?; - ctx.function("tanh", |_, x: Num| Ok(x.0.tanh()))?; - ctx.function("acosh", |_, x: Num| Ok(x.0.acosh()))?; - ctx.function("asinh", |_, x: Num| Ok(x.0.asinh()))?; - ctx.function("atanh", |_, x: Num| Ok(x.0.atanh()))?; - ctx.function("atan2", |_, (x, y): (Num, Num)| Ok(x.0.atan2(y.0)))?; - ctx.function("degrees", |_, x: Num| Ok(x.0.to_degrees()))?; - ctx.function("radians", |_, x: Num| Ok(x.0.to_radians()))?; - ctx.function("abs", |_, x: Num| Ok(x.0.abs()))?; - ctx.function("exp", |_, x: Num| Ok(x.0.exp()))?; - ctx.function("log", |_, (x, base): (Num, Num)| Ok(x.0.log(base.0)))?; - ctx.function("ln", |_, x: Num| Ok(x.0.ln()))?; - ctx.function("log2", |_, x: Num| Ok(x.0.log2()))?; - ctx.function("log10", |_, x: Num| Ok(x.0.log10()))?; - ctx.function("sqrt", |_, x: Num| Ok(x.0.sqrt()))?; - ctx.function("floor", |_, x: Num| Ok(Int(x.0.floor() as _)))?; - ctx.function("ceil", |_, x: Num| Ok(Int(x.0.ceil() as _)))?; - ctx.function("round", |_, x: Num| Ok(Int(x.0.round() as _)))?; - ctx.function("int", |_, x: Num| Ok(Int(x.0 as _)))?; - ctx.function("round2", math_round)?; - ctx.function("pow", |_, (x, n): (Num, Num)| Ok(x.0.powf(n.0)))?; - ctx.function("clamp", math_clamp)?; - ctx.function("gaussian2d", math_gaussian)?; - Ok(()) - }) - } -} diff --git a/src/noise.rs b/src/noise.rs deleted file mode 100644 index 94e57e2..0000000 --- a/src/noise.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::number::{Checked, Int, Num}; -use crate::LuaEngine; -use noise::Billow; -use noise::NoiseFn; -use noise::Perlin; -use noise::Seedable; -use rlua::UserData; -use rlua::UserDataMethods; - -pub trait LibNoise { - fn load_noise(&self) -> rlua::Result<()>; -} - -struct LuaPerlin(Perlin); - -impl UserData for LuaPerlin { - fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { - methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); - methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| { - Ok(this.0.get([x.0, y.0])) - }); - methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| { - Ok(this.0.get([x.0, y.0, z.0])) - }); - methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| { - Ok(this.0.get([x.0, y.0, z.0, w.0])) - }); - } -} - -struct LuaBillow(Billow); - -impl UserData for LuaBillow { - fn add_methods<'lua, T: UserDataMethods<'lua, Self>>(methods: &mut T) { - methods.add_method("getSeed", |_, this, ()| Ok(Checked(this.0.seed()))); - methods.add_method("sample2d", |_, this, (x, y): (Num, Num)| { - Ok(this.0.get([x.0, y.0])) - }); - methods.add_method("sample3d", |_, this, (x, y, z): (Num, Num, Num)| { - Ok(this.0.get([x.0, y.0, z.0])) - }); - methods.add_method("sample4d", |_, this, (x, y, z, w): (Num, Num, Num, Num)| { - Ok(this.0.get([x.0, y.0, z.0, w.0])) - }); - methods.add_method("getOctaves", |_, this, ()| Ok(Int(this.0.octaves as _))); - methods.add_method("getFrequency", |_, this, ()| Ok(this.0.frequency)); - methods.add_method("getLacunarity", |_, this, ()| Ok(this.0.lacunarity)); - methods.add_method("getPersistence", |_, this, ()| Ok(this.0.persistence)); - methods.add_method_mut("setOctaves", |_, this, x: Int| { - this.0.octaves = x.0 as _; - Ok(()) - }); - methods.add_method_mut("setFrequency", |_, this, x: Num| { - this.0.frequency = x.0; - Ok(()) - }); - methods.add_method_mut("setLacunarity", |_, this, x: Num| { - this.0.lacunarity = x.0; - Ok(()) - }); - methods.add_method_mut("setPersistence", |_, this, x: Num| { - this.0.persistence = x.0; - Ok(()) - }); - } -} - -impl LibNoise for LuaEngine { - fn load_noise(&self) -> rlua::Result<()> { - self.create_library("noise", false, |ctx| { - ctx.function("perlin", |_, seed: Option>| { - Ok(LuaPerlin(match seed { - None => Perlin::new(), - Some(seed) => Perlin::new().set_seed(seed.0), - })) - })?; - ctx.function("billow", |_, seed: Option>| { - Ok(LuaBillow(match seed { - None => Billow::new(), - Some(seed) => Billow::new().set_seed(seed.0), - })) - })?; - Ok(()) - }) - } -} diff --git a/src/number.rs b/src/number.rs deleted file mode 100644 index b67ca90..0000000 --- a/src/number.rs +++ /dev/null @@ -1,174 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! Fast implementation of numbers and integers. - -use crate::ValueExt; -use num_traits::{cast, NumCast}; -use rlua::Error; -use rlua::{Context, FromLua, Integer, Number, ToLua, Value}; - -/// Fast encodable/decodable float type with no coercion and no range checking. -#[derive(Copy, Clone, Debug, Default)] -pub struct Num(pub Number); - -// Well I've looked at the implementation and extra cpu time pumped at useless ifs do not -// look good in 3D engines! -// So here we go let's get rid of these useless ifs... -// The reason why they're useless is because of the fact we're directly reading lua numbers (f64). -impl<'lua> FromLua<'lua> for Num { - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - lua_value.check_number().map(Num) - } -} - -// Here the implementation is the same. This means that for sending to lua using with Number -// or FastNumber is fine. -impl<'lua> ToLua<'lua> for Num { - fn to_lua(self, _: Context<'lua>) -> rlua::Result> { - Ok(Value::Number(self.0)) - } -} - -/// Fast encodable/decodable integer type with no coercion and no range checking. -#[derive(Copy, Clone, Debug, Default)] -pub struct Int(pub Integer); - -// Well I've looked at the implementation and extra cpu time pumped at useless ifs do not -// look good in 3D engines! -// So here we go let's get rid of these useless ifs... -// The reason why they're useless is because of the fact we're directly reading lua numbers (f64). -impl<'lua> FromLua<'lua> for Int { - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - lua_value.check_integer().map(Int) - } -} - -// Again here we're skipping quite a lot of if blocks, which are not needed again because we're -// using native lua types. -impl<'lua> ToLua<'lua> for Int { - fn to_lua(self, _: Context<'lua>) -> rlua::Result> { - Ok(Value::Integer(self.0)) - } -} - -// These implementation are possibly truncating numbers; the use case being rendering engines, -// precision doesn't matter that much. - -pub trait NumToLua { - fn num_to_lua<'a>(self) -> Value<'a>; -} - -pub trait NumFromLua -where - Self: Sized, -{ - fn type_name() -> &'static str; - fn num_from_lua(val: Value) -> rlua::Result; -} - -macro_rules! impl_num_float { - ($($target: ty)*) => { - $( - impl NumToLua for $target { - fn num_to_lua<'a>(self) -> Value<'a> { - Value::Number(self as Number) - } - } - - impl NumFromLua for $target { - fn num_from_lua(val: Value) -> rlua::Result { - val.check_number().map(|v| v as $target) - } - - fn type_name() -> &'static str { - stringify!($target) - } - } - )* - }; -} - -macro_rules! impl_num_int { - ($($target: ty)*) => { - $( - impl NumToLua for $target { - fn num_to_lua<'a>(self) -> Value<'a> { - Value::Integer(self as Integer) - } - } - - impl NumFromLua for $target { - fn num_from_lua(val: Value) -> rlua::Result { - val.check_integer().map(|v| v as $target) - } - - fn type_name() -> &'static str { - stringify!($target) - } - } - )* - }; -} - -impl_num_float!(f32 f64); - -impl_num_int!( - i8 i16 i32 i64 - u8 u16 u32 u64 -); - -/// A range checked number which does not implement number coercion for better performance. -pub struct Checked(pub T); - -impl<'lua, T: NumFromLua + NumCast> FromLua<'lua> for Checked { - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - match lua_value { - Value::Integer(v) => { - cast(v) - .map(Checked) - .ok_or_else(|| Error::FromLuaConversionError { - from: lua_value.type_name(), - to: T::type_name(), - message: Some("out of range".to_string()), - }) - } - _ => Err(Error::FromLuaConversionError { - from: lua_value.type_name(), - to: T::type_name(), - message: Some("expected integer".to_string()), - }), - } - } -} - -impl<'lua, T: NumToLua> ToLua<'lua> for Checked { - fn to_lua(self, _: Context<'lua>) -> rlua::Result> { - Ok(self.0.num_to_lua()) - } -} diff --git a/src/quaternion.rs b/src/quaternion.rs deleted file mode 100644 index 12cb3fb..0000000 --- a/src/quaternion.rs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::macros::auto_lib; -use crate::macros::vec_wrapper_1; -use crate::macros::vec_wrapper_2; -use crate::macros::vec_wrapper_2_uniform; -use crate::macros::vec_wrapper_3; -use crate::macros::vec_wrapper_4; -use crate::number::{Num, NumFromLua, NumToLua}; -use crate::{LuaEngine, ValueExt}; -use nalgebra::{Quaternion, Unit, UnitQuaternion, Vector3}; -use rlua::{Context, FromLua, Function, Number, ToLua, Value}; - -pub trait LibQuaternion { - fn load_quaternion(&self) -> rlua::Result<()>; -} - -const QUAT_LIB: &str = "quat"; - -const QUAT_NEW: &str = "Quat"; - -pub struct LuaQuat(Quaternion); - -impl From> for LuaQuat { - fn from(v: Quaternion) -> Self { - Self(v) - } -} - -impl From> for Quaternion { - fn from(v: LuaQuat) -> Self { - v.0 - } -} - -impl LuaQuat { - pub fn new(v: Quaternion) -> LuaQuat { - Self(v) - } - - pub fn into_inner(self) -> Quaternion { - self.0 - } -} - -impl<'lua, T> ToLua<'lua> for LuaQuat -where - T: NumToLua, -{ - fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { - let func: Function = lua.globals().raw_get(QUAT_NEW)?; - let [[i, j, k, w]] = self.0.coords.data.0; - func.call(( - w.num_to_lua(), - i.num_to_lua(), - j.num_to_lua(), - k.num_to_lua(), - )) - } -} - -impl<'lua, T> FromLua<'lua> for LuaQuat -where - T: NumFromLua, -{ - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - let table = lua_value.check_table()?; - Ok(LuaQuat(Quaternion::new( - T::num_from_lua(table.raw_get("w")?)?, - T::num_from_lua(table.raw_get("i")?)?, - T::num_from_lua(table.raw_get("j")?)?, - T::num_from_lua(table.raw_get("k")?)?, - ))) - } -} - -pub(crate) type Quat = LuaQuat; - -vec_wrapper_2_uniform!(quat_eq (a, b): Quat => bool {a == b}); -vec_wrapper_2_uniform!(quat_add (a, b): Quat => Quat {(a + b).into()}); -vec_wrapper_2_uniform!(quat_sub (a, b): Quat => Quat {(a - b).into()}); -vec_wrapper_2_uniform!(quat_mul (a, b): Quat => Quat {(a * b).into()}); -vec_wrapper_2_uniform!(quat_dot (a, b): Quat => Number {a.dot(&b)}); -vec_wrapper_2_uniform!(quat_inner (a, b): Quat => Quat {a.inner(&b).into()}); -vec_wrapper_2_uniform!(quat_outer (a, b): Quat => Quat {a.outer(&b).into()}); -vec_wrapper_2_uniform!(quat_project (a, b): Quat => Option {a.project(&b).map(|v| v.into())}); -vec_wrapper_2_uniform!(quat_reject (a, b): Quat => Option {a.reject(&b).map(|v| v.into())}); -vec_wrapper_1!(quat_conjugate (a: Quat) => Quat {a.conjugate().into()}); -vec_wrapper_1!(quat_normalize (a: Quat) => Quat {a.normalize().into()}); -vec_wrapper_1!(quat_ln (a: Quat) => Quat {a.ln().into()}); -vec_wrapper_1!(quat_exp (a: Quat) => Quat {a.exp().into()}); -vec_wrapper_1!(quat_squared (a: Quat) => Quat {a.squared().into()}); -vec_wrapper_1!(quat_half (a: Quat) => Quat {a.half().into()}); -vec_wrapper_1!(quat_sqrt (a: Quat) => Quat {a.sqrt().into()}); -vec_wrapper_1!(quat_ispure (a: Quat) => bool {a.is_pure()}); -vec_wrapper_1!(quat_cos (a: Quat) => Quat {a.cos().into()}); -vec_wrapper_1!(quat_acos (a: Quat) => Quat {a.acos().into()}); -vec_wrapper_1!(quat_sin (a: Quat) => Quat {a.sin().into()}); -vec_wrapper_1!(quat_asin (a: Quat) => Quat {a.asin().into()}); -vec_wrapper_1!(quat_tan (a: Quat) => Quat {a.tan().into()}); -vec_wrapper_1!(quat_atan (a: Quat) => Quat {a.atan().into()}); -vec_wrapper_1!(quat_sinh (a: Quat) => Quat {a.sinh().into()}); -vec_wrapper_1!(quat_asinh (a: Quat) => Quat {a.asinh().into()}); -vec_wrapper_1!(quat_cosh (a: Quat) => Quat {a.cosh().into()}); -vec_wrapper_1!(quat_acosh (a: Quat) => Quat {a.acosh().into()}); -vec_wrapper_1!(quat_tanh (a: Quat) => Quat {a.tanh().into()}); -vec_wrapper_1!(quat_atanh (a: Quat) => Quat {a.atanh().into()}); -vec_wrapper_2!(quat_pow (a: Quat, n: Num) => Quat {a.powf(n.0).into()}); -vec_wrapper_3!(quat_lerp (a: Quat, b: Quat, f: Num) => Quat {a.lerp(&b.into_inner(), f.0).into()}); -vec_wrapper_1!(quat_imag (a: Quat) => crate::vector::Vec3 {a.imag().into()}); -vec_wrapper_1!(quat_scalar (a: Quat) => Number {a.scalar()}); -vec_wrapper_1!(quat_norm (a: Quat) => Number {a.norm()}); -vec_wrapper_1!(quat_norm_squared (a: Quat) => Number {a.norm_squared()}); -vec_wrapper_1!(quat_inverse (a: Quat) => Option {a.try_inverse().map(|v| v.into())}); - -//Unit quaternions -vec_wrapper_1!(quat_angle (a: Quat) => Number {Unit::new_unchecked(a).angle()}); -vec_wrapper_2_uniform!(quat_angle_to (a, b): Quat => Number { - Unit::new_unchecked(a).angle_to(&Unit::new_unchecked(b)) -}); -vec_wrapper_2_uniform!(quat_rotation_to (a, b): Quat => Quat { - Unit::new_unchecked(a).rotation_to(&Unit::new_unchecked(b)).into_inner().into() -}); -vec_wrapper_4!(quat_slerp (a: Quat, b: Quat, f: Num, e: Num) => Option { - Unit::new_unchecked(a).try_slerp(&Unit::new_unchecked(b.into_inner()), f.0, e.0) - .map(|v| v.into_inner().into()) -}); -vec_wrapper_1!(quat_euler_angles (a: Quat) => (Number, Number, Number) { - Unit::new_unchecked(a).euler_angles() -}); - -impl LibQuaternion for LuaEngine { - fn load_quaternion(&self) -> rlua::Result<()> { - auto_lib!(self (QUAT_LIB, true) { - __add: quat_add, __sub: quat_sub, __mul: quat_mul, __eq: quat_eq, - dot: quat_dot, inner: quat_inner, outer: quat_outer, project: quat_project, - reject: quat_reject, conjugate: quat_conjugate, normalize: quat_normalize, ln: quat_ln, - exp: quat_exp, squared: quat_squared, half: quat_half, sqrt: quat_sqrt, - ispure: quat_ispure, cos: quat_cos, acos: quat_acos, sin: quat_sin, asin: quat_asin, - tan: quat_tan, atan: quat_atan, sinh: quat_sinh, asinh: quat_asinh, - cosh: quat_cosh, acosh: quat_acosh, tanh: quat_tanh, atanh: quat_atanh, - pow: quat_pow, lerp: quat_lerp, imag: quat_imag, scalar: quat_scalar, - norm: quat_norm, normSquared: quat_norm_squared, inverse: quat_inverse, - angle: quat_angle, angleTo: quat_angle_to, rotationTo: quat_rotation_to, - slerp: quat_slerp, eulerAngles: quat_euler_angles, - })?; - //Create constructor function. - self.context(|ctx| { - let function = ctx.create_function( - |ctx, (v1, v2, v3, z): (Value, Option, Option, Option)| { - let v = match (v2, v3, z) { - (Some(i), Some(j), Some(k)) => { - let w = v1.check_number()?; - Quaternion::new(w, i.0, j.0, k.0) - } // wijk constructor - (Some(pitch), Some(yaw), None) => { - let roll = v1.check_number()?; - UnitQuaternion::from_euler_angles(roll, pitch.0, yaw.0).into_inner() - } // roll, pitch, yaw constructor - (Some(angle), None, None) => { - let axis = v1.check_table()?; - let vec = Unit::new_unchecked(Vector3::new( - axis.raw_get("x")?, - axis.raw_get("y")?, - axis.raw_get("z")?, - )); - UnitQuaternion::from_axis_angle(&vec, angle.0).into_inner() - } // axis, angle constructor - _ => { - let real = v1.check_number()?; - Quaternion::from_real(real) - } - }; - let table = ctx.create_table()?; - table.raw_set("i", v.i)?; - table.raw_set("j", v.j)?; - table.raw_set("k", v.k)?; - table.raw_set("w", v.w)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(QUAT_LIB)?); - Ok(table) - }, - )?; - let globals = ctx.globals(); - globals.raw_set(QUAT_NEW, function)?; - Ok(()) - }) - } -} diff --git a/src/vector.rs b/src/vector.rs deleted file mode 100644 index 5a293a2..0000000 --- a/src/vector.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright (c) 2022, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::macros::auto_lib; -use crate::macros::vec_wrapper_1; -use crate::macros::vec_wrapper_2; -use crate::macros::vec_wrapper_2_uniform; -use crate::macros::vec_wrapper_3; -use crate::number::{Int, Num, NumFromLua, NumToLua}; -use crate::{LuaEngine, ValueExt}; -use nalgebra::{Vector2, Vector3, Vector4}; -use rlua::{Context, FromLua, Function, Number, ToLua, Value}; - -pub trait LibVector { - fn load_vec2(&self) -> rlua::Result<()>; - fn load_vec3(&self) -> rlua::Result<()>; - fn load_vec4(&self) -> rlua::Result<()>; -} - -const VEC2_LIB: &str = "vec2"; -const VEC3_LIB: &str = "vec3"; -const VEC4_LIB: &str = "vec4"; - -const VEC2_NEW: &str = "Vec2"; -const VEC3_NEW: &str = "Vec3"; -const VEC4_NEW: &str = "Vec4"; - -pub struct LuaVec2(Vector2); - -impl From> for LuaVec2 { - fn from(v: Vector2) -> Self { - Self(v) - } -} - -impl From> for Vector2 { - fn from(v: LuaVec2) -> Self { - v.0 - } -} - -impl LuaVec2 { - pub fn new(v: Vector2) -> LuaVec2 { - Self(v) - } - - pub fn into_inner(self) -> Vector2 { - self.0 - } -} - -impl<'lua, T> ToLua<'lua> for LuaVec2 -where - T: NumToLua, -{ - fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { - let func: Function = lua.globals().raw_get(VEC2_NEW)?; - let [[x, y]] = self.0.data.0; - func.call((x.num_to_lua(), y.num_to_lua())) - } -} - -impl<'lua, T> FromLua<'lua> for LuaVec2 -where - T: NumFromLua, -{ - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - let table = lua_value.check_table()?; - Ok(LuaVec2(nalgebra::Vector2::new( - T::num_from_lua(table.raw_get("x")?)?, - T::num_from_lua(table.raw_get("y")?)?, - ))) - } -} - -pub struct LuaVec3(Vector3); - -impl From> for LuaVec3 { - fn from(v: Vector3) -> Self { - Self(v) - } -} - -impl From> for Vector3 { - fn from(v: LuaVec3) -> Self { - v.0 - } -} - -impl LuaVec3 { - pub fn new(v: Vector3) -> LuaVec3 { - Self(v) - } - - pub fn into_inner(self) -> Vector3 { - self.0 - } -} - -impl<'lua, T> ToLua<'lua> for LuaVec3 -where - T: NumToLua, -{ - fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { - let func: Function = lua.globals().raw_get(VEC3_NEW)?; - let [[x, y, z]] = self.0.data.0; - func.call((x.num_to_lua(), y.num_to_lua(), z.num_to_lua())) - } -} - -impl<'lua, T> FromLua<'lua> for LuaVec3 -where - T: NumFromLua, -{ - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - let table = lua_value.check_table()?; - Ok(LuaVec3(nalgebra::Vector3::new( - T::num_from_lua(table.raw_get("x")?)?, - T::num_from_lua(table.raw_get("y")?)?, - T::num_from_lua(table.raw_get("z")?)?, - ))) - } -} - -pub struct LuaVec4(Vector4); - -impl From> for LuaVec4 { - fn from(v: Vector4) -> Self { - Self(v) - } -} - -impl From> for Vector4 { - fn from(v: LuaVec4) -> Self { - v.0 - } -} - -impl LuaVec4 { - pub fn new(v: Vector4) -> LuaVec4 { - Self(v) - } - - pub fn into_inner(self) -> Vector4 { - self.0 - } -} - -impl<'lua, T> ToLua<'lua> for LuaVec4 -where - T: NumToLua, -{ - fn to_lua(self, lua: Context<'lua>) -> rlua::Result> { - let func: Function = lua.globals().raw_get(VEC4_NEW)?; - let [[x, y, z, w]] = self.0.data.0; - func.call(( - x.num_to_lua(), - y.num_to_lua(), - z.num_to_lua(), - w.num_to_lua(), - )) - } -} - -impl<'lua, T> FromLua<'lua> for LuaVec4 -where - T: NumFromLua, -{ - fn from_lua(lua_value: Value<'lua>, _: Context<'lua>) -> rlua::Result { - let table = lua_value.check_table()?; - Ok(LuaVec4(nalgebra::Vector4::new( - T::num_from_lua(table.raw_get("x")?)?, - T::num_from_lua(table.raw_get("y")?)?, - T::num_from_lua(table.raw_get("z")?)?, - T::num_from_lua(table.raw_get("w")?)?, - ))) - } -} - -pub(crate) type Vec2 = LuaVec2; -pub(crate) type Vec3 = LuaVec3; -pub(crate) type Vec4 = LuaVec4; - -fn argminmax_to_lua((id, val): (usize, Number)) -> (Int, Num) { - (Int(id as _), Num(val)) -} - -//TODO: create special wrappers for operators to allow simple operations with scalars. - -vec_wrapper_2_uniform!(vec2_add (a, b): Vec2 => Vec2 {(a + b).into()}); -vec_wrapper_2_uniform!(vec2_sub (a, b): Vec2 => Vec2 {(a - b).into()}); -vec_wrapper_2_uniform!(vec2_mul (a, b): Vec2 => Vec2 {a.component_mul(&b).into()}); -vec_wrapper_2_uniform!(vec2_div (a, b): Vec2 => Vec2 {a.component_div(&b).into()}); -vec_wrapper_2_uniform!(vec2_le (a, b): Vec2 => bool {a <= b}); -vec_wrapper_2_uniform!(vec2_lt (a, b): Vec2 => bool {a < b}); -vec_wrapper_2_uniform!(vec2_eq (a, b): Vec2 => bool {a == b}); -vec_wrapper_1!(vec2_unm (a: Vec2) => Vec2 {(-a).into()}); -vec_wrapper_2_uniform!(vec2_dot (a, b): Vec2 => Number {a.dot(&b)}); -vec_wrapper_2_uniform!(vec2_cross (a, b): Vec2 => Vec2 {a.cross(&b).into()}); -vec_wrapper_1!(vec2_norm (a: Vec2) => Number {a.norm()}); -vec_wrapper_1!(vec2_norm_squared (a: Vec2) => Number {a.norm_squared()}); -vec_wrapper_1!(vec2_argmin (a: Vec2) => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_1!(vec2_argmax (a: Vec2) => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_1!(vec2_normalize (a: Vec2) => Vec2 {a.normalize().into()}); -vec_wrapper_3!(vec2_lerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.lerp(&b.into_inner(), f.0).into()}); -vec_wrapper_3!(vec2_slerp (a: Vec2, b: Vec2, f: Num) => Vec2 {a.slerp(&b.into_inner(), f.0).into()}); -vec_wrapper_2!(vec2_push (a: Vec2, b: Num) => Vec3 {a.push(b.0).into()}); - -vec_wrapper_2_uniform!(vec3_add (a, b): Vec3 => Vec3 {(a + b).into()}); -vec_wrapper_2_uniform!(vec3_sub (a, b): Vec3 => Vec3 {(a - b).into()}); -vec_wrapper_2_uniform!(vec3_mul (a, b): Vec3 => Vec3 {a.component_mul(&b).into()}); -vec_wrapper_2_uniform!(vec3_div (a, b): Vec3 => Vec3 {a.component_div(&b).into()}); -vec_wrapper_2_uniform!(vec3_le (a, b): Vec3 => bool {a <= b}); -vec_wrapper_2_uniform!(vec3_lt (a, b): Vec3 => bool {a < b}); -vec_wrapper_2_uniform!(vec3_eq (a, b): Vec3 => bool {a == b}); -vec_wrapper_1!(vec3_unm (a: Vec3) => Vec3 {(-a).into()}); -vec_wrapper_2_uniform!(vec3_dot (a, b): Vec3 => Number {a.dot(&b)}); -vec_wrapper_2_uniform!(vec3_cross (a, b): Vec3 => Vec3 {a.cross(&b).into()}); -vec_wrapper_1!(vec3_norm (a: Vec3) => Number {a.norm()}); -vec_wrapper_1!(vec3_norm_squared (a: Vec3) => Number {a.norm_squared()}); -vec_wrapper_1!(vec3_argmin (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_1!(vec3_argmax (a: Vec3) => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_1!(vec3_normalize (a: Vec3) => Vec3 {a.normalize().into()}); -#[cfg(feature = "quaternion")] -vec_wrapper_2!(vec3_rotate (a: Vec3, b: crate::quaternion::Quat) => Vec3 { - (nalgebra::Unit::new_unchecked(b.into_inner()) * a).into() -}); -vec_wrapper_3!(vec3_lerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.lerp(&b.into_inner(), f.0).into()}); -vec_wrapper_3!(vec3_slerp (a: Vec3, b: Vec3, f: Num) => Vec3 {a.slerp(&b.into_inner(), f.0).into()}); -vec_wrapper_2!(vec3_push (a: Vec3, b: Num) => Vec4 {a.push(b.0).into()}); - -vec_wrapper_2_uniform!(vec4_add (a, b): Vec4 => Vec4 {(a + b).into()}); -vec_wrapper_2_uniform!(vec4_sub (a, b): Vec4 => Vec4 {(a - b).into()}); -vec_wrapper_2_uniform!(vec4_mul (a, b): Vec4 => Vec4 {a.component_mul(&b).into()}); -vec_wrapper_2_uniform!(vec4_div (a, b): Vec4 => Vec4 {a.component_div(&b).into()}); -vec_wrapper_2_uniform!(vec4_le (a, b): Vec4 => bool {a <= b}); -vec_wrapper_2_uniform!(vec4_lt (a, b): Vec4 => bool {a < b}); -vec_wrapper_2_uniform!(vec4_eq (a, b): Vec4 => bool {a == b}); -vec_wrapper_1!(vec4_unm (a: Vec4) => Vec4 {(-a).into()}); -vec_wrapper_2_uniform!(vec4_dot (a, b): Vec4 => Number {a.dot(&b)}); -vec_wrapper_2_uniform!(vec4_cross (a, b): Vec4 => Vec4 {a.cross(&b).into()}); -vec_wrapper_1!(vec4_norm (a: Vec4) => Number {a.norm()}); -vec_wrapper_1!(vec4_norm_squared (a: Vec4) => Number {a.norm_squared()}); -vec_wrapper_1!(vec4_argmin (a: Vec4) => (Int, Num) {argminmax_to_lua(a.argmin())}); -vec_wrapper_1!(vec4_argmax (a: Vec4) => (Int, Num) {argminmax_to_lua(a.argmax())}); -vec_wrapper_1!(vec4_normalize (a: Vec4) => Vec4 {a.normalize().into()}); -vec_wrapper_3!(vec4_lerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.lerp(&b.into_inner(), f.0).into()}); -vec_wrapper_3!(vec4_slerp (a: Vec4, b: Vec4, f: Num) => Vec4 {a.slerp(&b.into_inner(), f.0).into()}); - -impl LibVector for LuaEngine { - fn load_vec2(&self) -> rlua::Result<()> { - //Create the metatable. - auto_lib!(self (VEC2_LIB, true) { - __add: vec2_add, __sub: vec2_sub, __mul: vec2_mul, __div: vec2_div, - __le: vec2_le, __lt: vec2_lt, __eq: vec2_eq, __unm: vec2_unm, - dot: vec2_dot, cross: vec2_cross, norm: vec2_norm, normSquared: vec2_norm_squared, - argmin: vec2_argmin, argmax: vec2_argmax, lerp: vec2_lerp, slerp: vec2_slerp, - normalize: vec2_normalize, push: vec2_push, - })?; - //Create constructor function. - self.context(|ctx| { - let function = ctx.create_function(|ctx, (x, y): (Num, Option)| { - let val = match y { - Some(y) => Vector2::new(x.0, y.0), - None => Vector2::from_element(x.0), - }; - let table = ctx.create_table()?; - table.raw_set("x", val.x)?; - table.raw_set("y", val.y)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(VEC2_LIB)?); - Ok(table) - })?; - let globals = ctx.globals(); - globals.raw_set(VEC2_NEW, function)?; - Ok(()) - }) - } - - fn load_vec3(&self) -> rlua::Result<()> { - //Create the metatable. - auto_lib!(self (VEC3_LIB, true) { - __add: vec3_add, __sub: vec3_sub, __mul: vec3_mul, __div: vec3_div, - __le: vec3_le, __lt: vec3_lt, __eq: vec3_eq, __unm: vec3_unm, - dot: vec3_dot, cross: vec3_cross, norm: vec3_norm, normSquared: vec3_norm_squared, - argmin: vec3_argmin, argmax: vec3_argmax, lerp: vec3_lerp, slerp: vec3_slerp, - normalize: vec3_normalize, push: vec3_push, - })?; - #[cfg(feature = "quaternion")] - self.context(|ctx| { - let tbl: rlua::Table = ctx.globals().raw_get(VEC3_LIB)?; - tbl.raw_set("rotate", ctx.create_function(vec3_rotate)?)?; - Ok(()) - })?; - //Create constructor function. - self.context(|ctx| { - let function = - ctx.create_function(|ctx, (x, y, z): (Num, Option, Option)| { - let val = match (y, z) { - (Some(y), Some(z)) => Vector3::new(x.0, y.0, z.0), - _ => Vector3::from_element(x.0), - }; - let table = ctx.create_table()?; - table.raw_set("x", val.x)?; - table.raw_set("y", val.y)?; - table.raw_set("z", val.z)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(VEC3_LIB)?); - Ok(table) - })?; - let globals = ctx.globals(); - globals.raw_set(VEC3_NEW, function)?; - Ok(()) - }) - } - - fn load_vec4(&self) -> rlua::Result<()> { - //Create the metatable. - auto_lib!(self (VEC4_LIB, true) { - __add: vec4_add, __sub: vec4_sub, __mul: vec4_mul, __div: vec4_div, - __le: vec4_le, __lt: vec4_lt, __eq: vec4_eq, __unm: vec4_unm, - dot: vec4_dot, cross: vec4_cross, norm: vec4_norm, normSquared: vec4_norm_squared, - argmin: vec4_argmin, argmax: vec4_argmax, lerp: vec4_lerp, slerp: vec4_slerp, - normalize: vec4_normalize, - })?; - //Create constructor function. - self.context(|ctx| { - let function = ctx.create_function( - |ctx, (x, y, z, w): (Num, Option, Option, Option)| { - let val = match (y, z, w) { - (Some(y), Some(z), Some(w)) => Vector4::new(x.0, y.0, z.0, w.0), - _ => Vector4::from_element(x.0), - }; - let table = ctx.create_table()?; - table.raw_set("x", val.x)?; - table.raw_set("y", val.y)?; - table.raw_set("z", val.z)?; - table.raw_set("w", val.w)?; - let globals = ctx.globals(); - table.set_metatable(globals.raw_get(VEC4_LIB)?); - Ok(table) - }, - )?; - let globals = ctx.globals(); - globals.raw_set(VEC4_NEW, function)?; - Ok(()) - }) - } -} - -#[cfg(test)] -mod tests { - use crate::vector::LibVector; - use crate::LuaEngine; - - #[test] - fn basic() { - let engine = LuaEngine::new().unwrap(); - engine.load_vec2().unwrap(); - engine - .context(|ctx| { - ctx.load( - r#" - local v = Vec2(0, 0) - local v1 = Vec2(2.5, 2.5) - local add = v + v1 - local mul = v * v1 - local sub = v - v1 - local div = v1 / v - print(add.x, add.y) - print(mul.x, mul.y) - print(sub.x, sub.y) - print(div.x, div.y) - print((-div):norm()) - "#, - ) - .exec()?; - Ok(()) - }) - .unwrap(); - } - - #[test] - fn mutate() { - let engine = LuaEngine::new().unwrap(); - engine.load_vec2().unwrap(); - engine - .context(|ctx| { - ctx.load( - r#" - local v = Vec2(0.1, 0.1) - v.x = 0 - v.y = 5 - print(v:argmax()) - "#, - ) - .exec()?; - Ok(()) - }) - .unwrap(); - } -} From 503f75a7feda0e2a23b4079d208ccd5f74cf6607 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Feb 2025 22:29:26 +0100 Subject: [PATCH 019/527] Added LuaJIT submodule --- .gitmodules | 3 +++ LuaJIT | 1 + src/ffi/lua.rs | 1 + 3 files changed, 5 insertions(+) create mode 100644 .gitmodules create mode 160000 LuaJIT diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..784cf44 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "LuaJIT"] + path = LuaJIT + url = https://github.com/LuaJIT/LuaJIT.git diff --git a/LuaJIT b/LuaJIT new file mode 160000 index 0000000..a4f56a4 --- /dev/null +++ b/LuaJIT @@ -0,0 +1 @@ +Subproject commit a4f56a459a588ae768801074b46ba0adcfb49eb1 diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 9ae0ae5..31ba077 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -26,3 +26,4 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + From ac4598f95cb25722c6a3a43eee88231fd79628dd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Feb 2025 22:31:22 +0100 Subject: [PATCH 020/527] Added initial version of LuaJIT patch and build script (does not yet build) --- Cargo.toml | 3 + build.rs | 59 +++++ luajit.patch | 646 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 708 insertions(+) create mode 100644 build.rs create mode 100644 luajit.patch diff --git a/Cargo.toml b/Cargo.toml index c888701..f34269d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,4 +14,7 @@ categories = ["graphics"] [dependencies] +[build-dependencies] +bp3d-os = { version = "1.0.0-rc.4.1.0", features = ["fs"] } + [features] diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..1a5e4ca --- /dev/null +++ b/build.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::OsStr; +use std::path::Path; +use std::process::ExitStatus; +use bp3d_os::fs::CopyOptions; + +fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator>) -> ExitStatus { + let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); + std::process::Command::new(cmd) + .args(args) + .current_dir(path.join("LuaJIT")).status().expect(text) +} + +fn main() { + // Create build directory. + let out = std::env::var_os("OUT_DIR").expect("Failed to acquire output directory"); + let out_path = Path::new(&out).join("luajit-build"); + std::fs::create_dir_all(&out_path).expect("Failed to create LuaJIT build directory"); + + // Apply patch to LuaJIT source code. + let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("luajit.patch").as_os_str()]); + if !result.success() { + panic!("Failed to patch LuaJIT"); + } + + // Copy the source directory to the build directory. + bp3d_os::fs::copy(&path.join("LuaJIT"), &out_path, CopyOptions::new()).expect("Failed to copy LuaJIT sources to build directory"); + + // Revert patch to LuaJIT source code and cleanup. + run_command_in_luajit("Failed to revert LuaJIT patch", "git", &["checkout", "."]); +} diff --git a/luajit.patch b/luajit.patch new file mode 100644 index 0000000..f2adb56 --- /dev/null +++ b/luajit.patch @@ -0,0 +1,646 @@ +diff --git a/src/lib_init.c b/src/lib_init.c +index 01cecf2f..0cfb3dbf 100644 +--- a/src/lib_init.c ++++ b/src/lib_init.c +@@ -19,23 +19,13 @@ static const luaL_Reg lj_lib_load[] = { + { "", luaopen_base }, + { LUA_LOADLIBNAME, luaopen_package }, + { LUA_TABLIBNAME, luaopen_table }, +- { LUA_IOLIBNAME, luaopen_io }, +- { LUA_OSLIBNAME, luaopen_os }, + { LUA_STRLIBNAME, luaopen_string }, + { LUA_MATHLIBNAME, luaopen_math }, +- { LUA_DBLIBNAME, luaopen_debug }, + { LUA_BITLIBNAME, luaopen_bit }, + { LUA_JITLIBNAME, luaopen_jit }, + { NULL, NULL } + }; + +-static const luaL_Reg lj_lib_preload[] = { +-#if LJ_HASFFI +- { LUA_FFILIBNAME, luaopen_ffi }, +-#endif +- { NULL, NULL } +-}; +- + LUALIB_API void luaL_openlibs(lua_State *L) + { + const luaL_Reg *lib; +@@ -44,12 +34,5 @@ LUALIB_API void luaL_openlibs(lua_State *L) + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +- luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", +- sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1); +- for (lib = lj_lib_preload; lib->func; lib++) { +- lua_pushcfunction(L, lib->func); +- lua_setfield(L, -2, lib->name); +- } +- lua_pop(L, 1); + } + +diff --git a/src/lib_jit.c b/src/lib_jit.c +index fd8e585b..3f232958 100644 +--- a/src/lib_jit.c ++++ b/src/lib_jit.c +@@ -40,480 +40,12 @@ + + #define LJLIB_MODULE_jit + +-static int setjitmode(lua_State *L, int mode) +-{ +- int idx = 0; +- if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */ +- mode |= LUAJIT_MODE_ENGINE; +- } else { +- /* jit.on/off/flush(func|proto, nil|true|false) */ +- if (tvisfunc(L->base) || tvisproto(L->base)) +- idx = 1; +- else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */ +- goto err; +- if (L->base+1 < L->top && tvisbool(L->base+1)) +- mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC; +- else +- mode |= LUAJIT_MODE_FUNC; +- } +- if (luaJIT_setmode(L, idx, mode) != 1) { +- if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE) +- lj_err_caller(L, LJ_ERR_NOJIT); +- err: +- lj_err_argt(L, 1, LUA_TFUNCTION); +- } +- return 0; +-} +- +-LJLIB_CF(jit_on) +-{ +- return setjitmode(L, LUAJIT_MODE_ON); +-} +- +-LJLIB_CF(jit_off) +-{ +- return setjitmode(L, LUAJIT_MODE_OFF); +-} +- +-LJLIB_CF(jit_flush) +-{ +-#if LJ_HASJIT +- if (L->base < L->top && tvisnumber(L->base)) { +- int traceno = lj_lib_checkint(L, 1); +- luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE); +- return 0; +- } +-#endif +- return setjitmode(L, LUAJIT_MODE_FLUSH); +-} +- +-#if LJ_HASJIT +-/* Push a string for every flag bit that is set. */ +-static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base, +- const char *str) +-{ +- for (; *str; base <<= 1, str += 1+*str) +- if (flags & base) +- setstrV(L, L->top++, lj_str_new(L, str+1, *(uint8_t *)str)); +-} +-#endif +- +-LJLIB_CF(jit_status) +-{ +-#if LJ_HASJIT +- jit_State *J = L2J(L); +- L->top = L->base; +- setboolV(L->top++, (J->flags & JIT_F_ON) ? 1 : 0); +- flagbits_to_strings(L, J->flags, JIT_F_CPU, JIT_F_CPUSTRING); +- flagbits_to_strings(L, J->flags, JIT_F_OPT, JIT_F_OPTSTRING); +- return (int)(L->top - L->base); +-#else +- setboolV(L->top++, 0); +- return 1; +-#endif +-} +- +-LJLIB_CF(jit_security) +-{ +- int idx = lj_lib_checkopt(L, 1, -1, LJ_SECURITY_MODESTRING); +- setintV(L->top++, ((LJ_SECURITY_MODE >> (2*idx)) & 3)); +- return 1; +-} +- +-LJLIB_CF(jit_attach) +-{ +-#ifdef LUAJIT_DISABLE_VMEVENT +- luaL_error(L, "vmevent API disabled"); +-#else +- GCfunc *fn = lj_lib_checkfunc(L, 1); +- GCstr *s = lj_lib_optstr(L, 2); +- luaL_findtable(L, LUA_REGISTRYINDEX, LJ_VMEVENTS_REGKEY, LJ_VMEVENTS_HSIZE); +- if (s) { /* Attach to given event. */ +- const uint8_t *p = (const uint8_t *)strdata(s); +- uint32_t h = s->len; +- while (*p) h = h ^ (lj_rol(h, 6) + *p++); +- lua_pushvalue(L, 1); +- lua_rawseti(L, -2, VMEVENT_HASHIDX(h)); +- G(L)->vmevmask = VMEVENT_NOCACHE; /* Invalidate cache. */ +- } else { /* Detach if no event given. */ +- setnilV(L->top++); +- while (lua_next(L, -2)) { +- L->top--; +- if (tvisfunc(L->top) && funcV(L->top) == fn) { +- setnilV(lj_tab_set(L, tabV(L->top-2), L->top-1)); +- } +- } +- } +-#endif +- return 0; +-} +- +-LJLIB_PUSH(top-5) LJLIB_SET(os) +-LJLIB_PUSH(top-4) LJLIB_SET(arch) +-LJLIB_PUSH(top-3) LJLIB_SET(version_num) +-LJLIB_PUSH(top-2) LJLIB_SET(version) +- +-#include "lj_libdef.h" +- +-/* -- jit.util.* functions ------------------------------------------------ */ +- +-#define LJLIB_MODULE_jit_util +- +-/* -- Reflection API for Lua functions ------------------------------------ */ +- +-static void setintfield(lua_State *L, GCtab *t, const char *name, int32_t val) +-{ +- setintV(lj_tab_setstr(L, t, lj_str_newz(L, name)), val); +-} +- +-/* local info = jit.util.funcinfo(func [,pc]) */ +-LJLIB_CF(jit_util_funcinfo) +-{ +- GCproto *pt = lj_lib_checkLproto(L, 1, 1); +- if (pt) { +- BCPos pc = (BCPos)lj_lib_optint(L, 2, 0); +- GCtab *t; +- lua_createtable(L, 0, 16); /* Increment hash size if fields are added. */ +- t = tabV(L->top-1); +- setintfield(L, t, "linedefined", pt->firstline); +- setintfield(L, t, "lastlinedefined", pt->firstline + pt->numline); +- setintfield(L, t, "stackslots", pt->framesize); +- setintfield(L, t, "params", pt->numparams); +- setintfield(L, t, "bytecodes", (int32_t)pt->sizebc); +- setintfield(L, t, "gcconsts", (int32_t)pt->sizekgc); +- setintfield(L, t, "nconsts", (int32_t)pt->sizekn); +- setintfield(L, t, "upvalues", (int32_t)pt->sizeuv); +- if (pc < pt->sizebc) +- setintfield(L, t, "currentline", lj_debug_line(pt, pc)); +- lua_pushboolean(L, (pt->flags & PROTO_VARARG)); +- lua_setfield(L, -2, "isvararg"); +- lua_pushboolean(L, (pt->flags & PROTO_CHILD)); +- lua_setfield(L, -2, "children"); +- setstrV(L, L->top++, proto_chunkname(pt)); +- lua_setfield(L, -2, "source"); +- lj_debug_pushloc(L, pt, pc); +- lua_setfield(L, -2, "loc"); +- setprotoV(L, lj_tab_setstr(L, t, lj_str_newlit(L, "proto")), pt); +- } else { +- GCfunc *fn = funcV(L->base); +- GCtab *t; +- lua_createtable(L, 0, 4); /* Increment hash size if fields are added. */ +- t = tabV(L->top-1); +- if (!iscfunc(fn)) +- setintfield(L, t, "ffid", fn->c.ffid); +- setintptrV(lj_tab_setstr(L, t, lj_str_newlit(L, "addr")), +- (intptr_t)(void *)fn->c.f); +- setintfield(L, t, "upvalues", fn->c.nupvalues); +- } +- return 1; +-} +- +-/* local ins, m = jit.util.funcbc(func, pc) */ +-LJLIB_CF(jit_util_funcbc) +-{ +- GCproto *pt = lj_lib_checkLproto(L, 1, 0); +- BCPos pc = (BCPos)lj_lib_checkint(L, 2); +- if (pc < pt->sizebc) { +- BCIns ins = proto_bc(pt)[pc]; +- BCOp op = bc_op(ins); +- lj_assertL(op < BC__MAX, "bad bytecode op %d", op); +- setintV(L->top, ins); +- setintV(L->top+1, lj_bc_mode[op]); +- L->top += 2; +- return 2; +- } +- return 0; +-} +- +-/* local k = jit.util.funck(func, idx) */ +-LJLIB_CF(jit_util_funck) +-{ +- GCproto *pt = lj_lib_checkLproto(L, 1, 0); +- ptrdiff_t idx = (ptrdiff_t)lj_lib_checkint(L, 2); +- if (idx >= 0) { +- if (idx < (ptrdiff_t)pt->sizekn) { +- copyTV(L, L->top-1, proto_knumtv(pt, idx)); +- return 1; +- } +- } else { +- if (~idx < (ptrdiff_t)pt->sizekgc) { +- GCobj *gc = proto_kgc(pt, idx); +- setgcV(L, L->top-1, gc, ~gc->gch.gct); +- return 1; +- } +- } +- return 0; +-} +- +-/* local name = jit.util.funcuvname(func, idx) */ +-LJLIB_CF(jit_util_funcuvname) +-{ +- GCproto *pt = lj_lib_checkLproto(L, 1, 0); +- uint32_t idx = (uint32_t)lj_lib_checkint(L, 2); +- if (idx < pt->sizeuv) { +- setstrV(L, L->top-1, lj_str_newz(L, lj_debug_uvname(pt, idx))); +- return 1; +- } +- return 0; +-} +- +-/* -- Reflection API for traces ------------------------------------------- */ +- +-#if LJ_HASJIT +- +-/* Check trace argument. Must not throw for non-existent trace numbers. */ +-static GCtrace *jit_checktrace(lua_State *L) +-{ +- TraceNo tr = (TraceNo)lj_lib_checkint(L, 1); +- jit_State *J = L2J(L); +- if (tr > 0 && tr < J->sizetrace) +- return traceref(J, tr); +- return NULL; +-} +- +-/* Names of link types. ORDER LJ_TRLINK */ +-static const char *const jit_trlinkname[] = { +- "none", "root", "loop", "tail-recursion", "up-recursion", "down-recursion", +- "interpreter", "return", "stitch" +-}; +- +-/* local info = jit.util.traceinfo(tr) */ +-LJLIB_CF(jit_util_traceinfo) +-{ +- GCtrace *T = jit_checktrace(L); +- if (T) { +- GCtab *t; +- lua_createtable(L, 0, 8); /* Increment hash size if fields are added. */ +- t = tabV(L->top-1); +- setintfield(L, t, "nins", (int32_t)T->nins - REF_BIAS - 1); +- setintfield(L, t, "nk", REF_BIAS - (int32_t)T->nk); +- setintfield(L, t, "link", T->link); +- setintfield(L, t, "nexit", T->nsnap); +- setstrV(L, L->top++, lj_str_newz(L, jit_trlinkname[T->linktype])); +- lua_setfield(L, -2, "linktype"); +- /* There are many more fields. Add them only when needed. */ +- return 1; +- } +- return 0; +-} +- +-/* local m, ot, op1, op2, prev = jit.util.traceir(tr, idx) */ +-LJLIB_CF(jit_util_traceir) +-{ +- GCtrace *T = jit_checktrace(L); +- IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; +- if (T && ref >= REF_BIAS && ref < T->nins) { +- IRIns *ir = &T->ir[ref]; +- int32_t m = lj_ir_mode[ir->o]; +- setintV(L->top-2, m); +- setintV(L->top-1, ir->ot); +- setintV(L->top++, (int32_t)ir->op1 - (irm_op1(m)==IRMref ? REF_BIAS : 0)); +- setintV(L->top++, (int32_t)ir->op2 - (irm_op2(m)==IRMref ? REF_BIAS : 0)); +- setintV(L->top++, ir->prev); +- return 5; +- } +- return 0; +-} +- +-/* local k, t [, slot] = jit.util.tracek(tr, idx) */ +-LJLIB_CF(jit_util_tracek) +-{ +- GCtrace *T = jit_checktrace(L); +- IRRef ref = (IRRef)lj_lib_checkint(L, 2) + REF_BIAS; +- if (T && ref >= T->nk && ref < REF_BIAS) { +- IRIns *ir = &T->ir[ref]; +- int32_t slot = -1; +- if (ir->o == IR_KSLOT) { +- slot = ir->op2; +- ir = &T->ir[ir->op1]; +- } +-#if LJ_HASFFI +- if (ir->o == IR_KINT64) ctype_loadffi(L); +-#endif +- lj_ir_kvalue(L, L->top-2, ir); +- setintV(L->top-1, (int32_t)irt_type(ir->t)); +- if (slot == -1) +- return 2; +- setintV(L->top++, slot); +- return 3; +- } +- return 0; +-} +- +-/* local snap = jit.util.tracesnap(tr, sn) */ +-LJLIB_CF(jit_util_tracesnap) +-{ +- GCtrace *T = jit_checktrace(L); +- SnapNo sn = (SnapNo)lj_lib_checkint(L, 2); +- if (T && sn < T->nsnap) { +- SnapShot *snap = &T->snap[sn]; +- SnapEntry *map = &T->snapmap[snap->mapofs]; +- MSize n, nent = snap->nent; +- GCtab *t; +- lua_createtable(L, nent+2, 0); +- t = tabV(L->top-1); +- setintV(lj_tab_setint(L, t, 0), (int32_t)snap->ref - REF_BIAS); +- setintV(lj_tab_setint(L, t, 1), (int32_t)snap->nslots); +- for (n = 0; n < nent; n++) +- setintV(lj_tab_setint(L, t, (int32_t)(n+2)), (int32_t)map[n]); +- setintV(lj_tab_setint(L, t, (int32_t)(nent+2)), (int32_t)SNAP(255, 0, 0)); +- return 1; +- } +- return 0; +-} +- +-/* local mcode, addr, loop = jit.util.tracemc(tr) */ +-LJLIB_CF(jit_util_tracemc) +-{ +- GCtrace *T = jit_checktrace(L); +- if (T && T->mcode != NULL) { +- setstrV(L, L->top-1, lj_str_new(L, (const char *)T->mcode, T->szmcode)); +- setintptrV(L->top++, (intptr_t)(void *)T->mcode); +- setintV(L->top++, T->mcloop); +- return 3; +- } +- return 0; +-} +- +-/* local addr = jit.util.traceexitstub([tr,] exitno) */ +-LJLIB_CF(jit_util_traceexitstub) +-{ +-#ifdef EXITSTUBS_PER_GROUP +- ExitNo exitno = (ExitNo)lj_lib_checkint(L, 1); +- jit_State *J = L2J(L); +- if (exitno < EXITSTUBS_PER_GROUP*LJ_MAX_EXITSTUBGR) { +- setintptrV(L->top-1, (intptr_t)(void *)exitstub_addr(J, exitno)); +- return 1; +- } +-#else +- if (L->top > L->base+1) { /* Don't throw for one-argument variant. */ +- GCtrace *T = jit_checktrace(L); +- ExitNo exitno = (ExitNo)lj_lib_checkint(L, 2); +- ExitNo maxexit = T->root ? T->nsnap+1 : T->nsnap; +- if (T && T->mcode != NULL && exitno < maxexit) { +- setintptrV(L->top-1, (intptr_t)(void *)exitstub_trace_addr(T, exitno)); +- return 1; +- } +- } +-#endif +- return 0; +-} +- +-/* local addr = jit.util.ircalladdr(idx) */ +-LJLIB_CF(jit_util_ircalladdr) +-{ +- uint32_t idx = (uint32_t)lj_lib_checkint(L, 1); +- if (idx < IRCALL__MAX) { +- ASMFunction func = lj_ir_callinfo[idx].func; +- setintptrV(L->top-1, (intptr_t)(void *)lj_ptr_strip(func)); +- return 1; +- } +- return 0; +-} +- +-#endif +- +-#include "lj_libdef.h" +- +-static int luaopen_jit_util(lua_State *L) +-{ +- LJ_LIB_REG(L, NULL, jit_util); +- return 1; +-} +- + /* -- jit.opt module ------------------------------------------------------ */ + + #if LJ_HASJIT + + #define LJLIB_MODULE_jit_opt + +-/* Parse optimization level. */ +-static int jitopt_level(jit_State *J, const char *str) +-{ +- if (str[0] >= '0' && str[0] <= '9' && str[1] == '\0') { +- uint32_t flags; +- if (str[0] == '0') flags = JIT_F_OPT_0; +- else if (str[0] == '1') flags = JIT_F_OPT_1; +- else if (str[0] == '2') flags = JIT_F_OPT_2; +- else flags = JIT_F_OPT_3; +- J->flags = (J->flags & ~JIT_F_OPT_MASK) | flags; +- return 1; /* Ok. */ +- } +- return 0; /* No match. */ +-} +- +-/* Parse optimization flag. */ +-static int jitopt_flag(jit_State *J, const char *str) +-{ +- const char *lst = JIT_F_OPTSTRING; +- uint32_t opt; +- int set = 1; +- if (str[0] == '+') { +- str++; +- } else if (str[0] == '-') { +- str++; +- set = 0; +- } else if (str[0] == 'n' && str[1] == 'o') { +- str += str[2] == '-' ? 3 : 2; +- set = 0; +- } +- for (opt = JIT_F_OPT; ; opt <<= 1) { +- size_t len = *(const uint8_t *)lst; +- if (len == 0) +- break; +- if (strncmp(str, lst+1, len) == 0 && str[len] == '\0') { +- if (set) J->flags |= opt; else J->flags &= ~opt; +- return 1; /* Ok. */ +- } +- lst += 1+len; +- } +- return 0; /* No match. */ +-} +- +-/* Parse optimization parameter. */ +-static int jitopt_param(jit_State *J, const char *str) +-{ +- const char *lst = JIT_P_STRING; +- int i; +- for (i = 0; i < JIT_P__MAX; i++) { +- size_t len = *(const uint8_t *)lst; +- lj_assertJ(len != 0, "bad JIT_P_STRING"); +- if (strncmp(str, lst+1, len) == 0 && str[len] == '=') { +- int32_t n = 0; +- const char *p = &str[len+1]; +- while (*p >= '0' && *p <= '9') +- n = n*10 + (*p++ - '0'); +- if (*p) return 0; /* Malformed number. */ +- J->param[i] = n; +- if (i == JIT_P_hotloop) +- lj_dispatch_init_hotcount(J2G(J)); +- return 1; /* Ok. */ +- } +- lst += 1+len; +- } +- return 0; /* No match. */ +-} +- +-/* jit.opt.start(flags...) */ +-LJLIB_CF(jit_opt_start) +-{ +- jit_State *J = L2J(L); +- int nargs = (int)(L->top - L->base); +- if (nargs == 0) { +- J->flags = (J->flags & ~JIT_F_OPT_MASK) | JIT_F_OPT_DEFAULT; +- } else { +- int i; +- for (i = 1; i <= nargs; i++) { +- const char *str = strdata(lj_lib_checkstr(L, i)); +- if (!jitopt_level(J, str) && +- !jitopt_flag(J, str) && +- !jitopt_param(J, str)) +- lj_err_callerv(L, LJ_ERR_JITOPT, str); +- } +- } +- return 0; +-} +- + #include "lj_libdef.h" + + #endif +@@ -524,96 +56,8 @@ LJLIB_CF(jit_opt_start) + + #define LJLIB_MODULE_jit_profile + +-/* Not loaded by default, use: local profile = require("jit.profile") */ +- +-#define KEY_PROFILE_THREAD (U64x(80000000,00000000)|'t') +-#define KEY_PROFILE_FUNC (U64x(80000000,00000000)|'f') +- +-static void jit_profile_callback(lua_State *L2, lua_State *L, int samples, +- int vmstate) +-{ +- TValue key; +- cTValue *tv; +- key.u64 = KEY_PROFILE_FUNC; +- tv = lj_tab_get(L, tabV(registry(L)), &key); +- if (tvisfunc(tv)) { +- char vmst = (char)vmstate; +- int status; +- setfuncV(L2, L2->top++, funcV(tv)); +- setthreadV(L2, L2->top++, L); +- setintV(L2->top++, samples); +- setstrV(L2, L2->top++, lj_str_new(L2, &vmst, 1)); +- status = lua_pcall(L2, 3, 0, 0); /* callback(thread, samples, vmstate) */ +- if (status) { +- if (G(L2)->panic) G(L2)->panic(L2); +- exit(EXIT_FAILURE); +- } +- lj_trace_abort(G(L2)); +- } +-} +- +-/* profile.start(mode, cb) */ +-LJLIB_CF(jit_profile_start) +-{ +- GCtab *registry = tabV(registry(L)); +- GCstr *mode = lj_lib_optstr(L, 1); +- GCfunc *func = lj_lib_checkfunc(L, 2); +- lua_State *L2 = lua_newthread(L); /* Thread that runs profiler callback. */ +- TValue key; +- /* Anchor thread and function in registry. */ +- key.u64 = KEY_PROFILE_THREAD; +- setthreadV(L, lj_tab_set(L, registry, &key), L2); +- key.u64 = KEY_PROFILE_FUNC; +- setfuncV(L, lj_tab_set(L, registry, &key), func); +- lj_gc_anybarriert(L, registry); +- luaJIT_profile_start(L, mode ? strdata(mode) : "", +- (luaJIT_profile_callback)jit_profile_callback, L2); +- return 0; +-} +- +-/* profile.stop() */ +-LJLIB_CF(jit_profile_stop) +-{ +- GCtab *registry; +- TValue key; +- luaJIT_profile_stop(L); +- registry = tabV(registry(L)); +- key.u64 = KEY_PROFILE_THREAD; +- setnilV(lj_tab_set(L, registry, &key)); +- key.u64 = KEY_PROFILE_FUNC; +- setnilV(lj_tab_set(L, registry, &key)); +- lj_gc_anybarriert(L, registry); +- return 0; +-} +- +-/* dump = profile.dumpstack([thread,] fmt, depth) */ +-LJLIB_CF(jit_profile_dumpstack) +-{ +- lua_State *L2 = L; +- int arg = 0; +- size_t len; +- int depth; +- GCstr *fmt; +- const char *p; +- if (L->top > L->base && tvisthread(L->base)) { +- L2 = threadV(L->base); +- arg = 1; +- } +- fmt = lj_lib_checkstr(L, arg+1); +- depth = lj_lib_checkint(L, arg+2); +- p = luaJIT_profile_dumpstack(L2, strdata(fmt), depth, &len); +- lua_pushlstring(L, p, len); +- return 1; +-} +- + #include "lj_libdef.h" + +-static int luaopen_jit_profile(lua_State *L) +-{ +- LJ_LIB_REG(L, NULL, jit_profile); +- return 1; +-} +- + #endif + + /* -- JIT compiler initialization ----------------------------------------- */ +@@ -723,22 +167,6 @@ LUALIB_API int luaopen_jit(lua_State *L) + #if LJ_HASJIT + jit_init(L); + #endif +- lua_pushliteral(L, LJ_OS_NAME); +- lua_pushliteral(L, LJ_ARCH_NAME); +- lua_pushinteger(L, LUAJIT_VERSION_NUM); /* Deprecated. */ +- lua_pushliteral(L, LUAJIT_VERSION); +- LJ_LIB_REG(L, LUA_JITLIBNAME, jit); +-#if LJ_HASPROFILE +- lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile, +- tabref(L->env)); +-#endif +-#ifndef LUAJIT_DISABLE_JITUTIL +- lj_lib_prereg(L, LUA_JITLIBNAME ".util", luaopen_jit_util, tabref(L->env)); +-#endif +-#if LJ_HASJIT +- LJ_LIB_REG(L, "jit.opt", jit_opt); +-#endif +- L->top -= 2; + return 1; + } + From 08c5103779f95d6bb012240c33bc804eb6132dd0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 28 Feb 2025 23:29:25 +0100 Subject: [PATCH 021/527] Added new patches and fixed build script --- Cargo.toml | 4 +- build.rs | 73 ++++++++++- patch/lib_init.patch | 41 ++++++ luajit.patch => patch/lj_disable_jit.patch | 139 +++++++++++---------- 4 files changed, 189 insertions(+), 68 deletions(-) create mode 100644 patch/lib_init.patch rename luajit.patch => patch/lj_disable_jit.patch (89%) diff --git a/Cargo.toml b/Cargo.toml index f34269d..130e6e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -15,6 +15,8 @@ categories = ["graphics"] [dependencies] [build-dependencies] -bp3d-os = { version = "1.0.0-rc.4.1.0", features = ["fs"] } +bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } +phf = { version = "0.11.3", features = ["macros"] } +cc = "1.2.15" [features] diff --git a/build.rs b/build.rs index 1a5e4ca..4cff88d 100644 --- a/build.rs +++ b/build.rs @@ -28,8 +28,26 @@ use std::ffi::OsStr; use std::path::Path; -use std::process::ExitStatus; +use std::process::{Command, ExitStatus}; use bp3d_os::fs::CopyOptions; +use phf::phf_map; + +pub enum Target { + MacAmd64, + MacAarch64, + Linux, + Windows, + Unsupported +} + +static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { + "aarch64-apple-darwin" => Target::MacAarch64, + "aarch64-unknown-linux-gnu" => Target::Linux, + "i686-pc-windows-msvc" => Target::Windows, + "x86_64-pc-windows-msvc" => Target::Windows, + "x86_64-apple-darwin" => Target::MacAmd64, + "x86_64-unknown-linux-gnu" => Target::Linux +}; fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator>) -> ExitStatus { let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); @@ -38,22 +56,69 @@ fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "10.11") + .current_dir(build_dir).status(), + Target::MacAarch64 => Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "11.0") + .current_dir(build_dir).status(), + Target::Linux => Command::new("make") + .current_dir(build_dir).status(), + Target::Windows => { + let mut cmd = Command::new("msvcbuild.bat"); + let cl = cc::windows_registry::find_tool(&target_name, "cl.exe").expect("failed to find cl"); + for (k, v) in cl.env() { + cmd.env(k, v); + } + cmd.current_dir(build_dir.join("src")).status() + }, + Target::Unsupported => panic!("Unsupported build target {}", target_name) + }.expect("Failed to run build command"); + if !cmd.success() { + panic!("Failed to build LuaJIT"); + } +} + fn main() { + // Rerun this script if any of the patch files changed. + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-changed=patch"); + + // Revert patch to LuaJIT source code and cleanup. + run_command_in_luajit("Failed to revert LuaJIT patch", "git", &["checkout", "."]); + // Create build directory. let out = std::env::var_os("OUT_DIR").expect("Failed to acquire output directory"); let out_path = Path::new(&out).join("luajit-build"); - std::fs::create_dir_all(&out_path).expect("Failed to create LuaJIT build directory"); // Apply patch to LuaJIT source code. let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("luajit.patch").as_os_str()]); + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lib_init.patch").as_os_str()]); + if !result.success() { + panic!("Failed to patch LuaJIT"); + } + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lj_disable_jit.patch").as_os_str()]); if !result.success() { panic!("Failed to patch LuaJIT"); } // Copy the source directory to the build directory. - bp3d_os::fs::copy(&path.join("LuaJIT"), &out_path, CopyOptions::new()).expect("Failed to copy LuaJIT sources to build directory"); + println!("{}", out_path.display()); + if !out_path.is_dir() { + bp3d_os::fs::copy(&path.join("LuaJIT"), &out_path, CopyOptions::new().exclude(OsStr::new(".git"))).expect("Failed to copy LuaJIT sources to build directory"); + } // Revert patch to LuaJIT source code and cleanup. run_command_in_luajit("Failed to revert LuaJIT patch", "git", &["checkout", "."]); + + // Build LuaJIT. + build_luajit(&out_path); + + // Attempt to setup linkage. + println!("cargo:rustc-link-search=native={}", out_path.display()); + println!("cargo:rustc-link-lib=static=luajit"); } diff --git a/patch/lib_init.patch b/patch/lib_init.patch new file mode 100644 index 0000000..88fae6a --- /dev/null +++ b/patch/lib_init.patch @@ -0,0 +1,41 @@ +diff --git a/src/lib_init.c b/src/lib_init.c +index 01cecf2f..0cfb3dbf 100644 +--- a/src/lib_init.c ++++ b/src/lib_init.c +@@ -19,23 +19,13 @@ static const luaL_Reg lj_lib_load[] = { + { "", luaopen_base }, + { LUA_LOADLIBNAME, luaopen_package }, + { LUA_TABLIBNAME, luaopen_table }, +- { LUA_IOLIBNAME, luaopen_io }, +- { LUA_OSLIBNAME, luaopen_os }, + { LUA_STRLIBNAME, luaopen_string }, + { LUA_MATHLIBNAME, luaopen_math }, +- { LUA_DBLIBNAME, luaopen_debug }, + { LUA_BITLIBNAME, luaopen_bit }, + { LUA_JITLIBNAME, luaopen_jit }, + { NULL, NULL } + }; + +-static const luaL_Reg lj_lib_preload[] = { +-#if LJ_HASFFI +- { LUA_FFILIBNAME, luaopen_ffi }, +-#endif +- { NULL, NULL } +-}; +- + LUALIB_API void luaL_openlibs(lua_State *L) + { + const luaL_Reg *lib; +@@ -44,12 +34,5 @@ LUALIB_API void luaL_openlibs(lua_State *L) + lua_pushstring(L, lib->name); + lua_call(L, 1, 0); + } +- luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", +- sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1); +- for (lib = lj_lib_preload; lib->func; lib++) { +- lua_pushcfunction(L, lib->func); +- lua_setfield(L, -2, lib->name); +- } +- lua_pop(L, 1); + } + diff --git a/luajit.patch b/patch/lj_disable_jit.patch similarity index 89% rename from luajit.patch rename to patch/lj_disable_jit.patch index f2adb56..ec3fe2e 100644 --- a/luajit.patch +++ b/patch/lj_disable_jit.patch @@ -1,49 +1,8 @@ -diff --git a/src/lib_init.c b/src/lib_init.c -index 01cecf2f..0cfb3dbf 100644 ---- a/src/lib_init.c -+++ b/src/lib_init.c -@@ -19,23 +19,13 @@ static const luaL_Reg lj_lib_load[] = { - { "", luaopen_base }, - { LUA_LOADLIBNAME, luaopen_package }, - { LUA_TABLIBNAME, luaopen_table }, -- { LUA_IOLIBNAME, luaopen_io }, -- { LUA_OSLIBNAME, luaopen_os }, - { LUA_STRLIBNAME, luaopen_string }, - { LUA_MATHLIBNAME, luaopen_math }, -- { LUA_DBLIBNAME, luaopen_debug }, - { LUA_BITLIBNAME, luaopen_bit }, - { LUA_JITLIBNAME, luaopen_jit }, - { NULL, NULL } - }; - --static const luaL_Reg lj_lib_preload[] = { --#if LJ_HASFFI -- { LUA_FFILIBNAME, luaopen_ffi }, --#endif -- { NULL, NULL } --}; -- - LUALIB_API void luaL_openlibs(lua_State *L) - { - const luaL_Reg *lib; -@@ -44,12 +34,5 @@ LUALIB_API void luaL_openlibs(lua_State *L) - lua_pushstring(L, lib->name); - lua_call(L, 1, 0); - } -- luaL_findtable(L, LUA_REGISTRYINDEX, "_PRELOAD", -- sizeof(lj_lib_preload)/sizeof(lj_lib_preload[0])-1); -- for (lib = lj_lib_preload; lib->func; lib++) { -- lua_pushcfunction(L, lib->func); -- lua_setfield(L, -2, lib->name); -- } -- lua_pop(L, 1); - } - diff --git a/src/lib_jit.c b/src/lib_jit.c -index fd8e585b..3f232958 100644 +index fd8e585b..d22b8332 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c -@@ -40,480 +40,12 @@ +@@ -40,582 +40,8 @@ #define LJLIB_MODULE_jit @@ -420,20 +379,20 @@ index fd8e585b..3f232958 100644 - -#endif - --#include "lj_libdef.h" -- + #include "lj_libdef.h" + -static int luaopen_jit_util(lua_State *L) -{ - LJ_LIB_REG(L, NULL, jit_util); - return 1; -} - - /* -- jit.opt module ------------------------------------------------------ */ - - #if LJ_HASJIT - - #define LJLIB_MODULE_jit_opt - +-/* -- jit.opt module ------------------------------------------------------ */ +- +-#if LJ_HASJIT +- +-#define LJLIB_MODULE_jit_opt +- -/* Parse optimization level. */ -static int jitopt_level(jit_State *J, const char *str) -{ @@ -521,13 +480,16 @@ index fd8e585b..3f232958 100644 - return 0; -} - - #include "lj_libdef.h" - - #endif -@@ -524,96 +56,8 @@ LJLIB_CF(jit_opt_start) - - #define LJLIB_MODULE_jit_profile - +-#include "lj_libdef.h" +- +-#endif +- +-/* -- jit.profile module -------------------------------------------------- */ +- +-#if LJ_HASPROFILE +- +-#define LJLIB_MODULE_jit_profile +- -/* Not loaded by default, use: local profile = require("jit.profile") */ - -#define KEY_PROFILE_THREAD (U64x(80000000,00000000)|'t') @@ -610,18 +572,20 @@ index fd8e585b..3f232958 100644 - return 1; -} - - #include "lj_libdef.h" - +-#include "lj_libdef.h" +- -static int luaopen_jit_profile(lua_State *L) -{ - LJ_LIB_REG(L, NULL, jit_profile); - return 1; -} - - #endif - +-#endif +- /* -- JIT compiler initialization ----------------------------------------- */ -@@ -723,22 +167,6 @@ LUALIB_API int luaopen_jit(lua_State *L) + + #if LJ_HASJIT +@@ -723,22 +149,6 @@ LUALIB_API int luaopen_jit(lua_State *L) #if LJ_HASJIT jit_init(L); #endif @@ -644,3 +608,52 @@ index fd8e585b..3f232958 100644 return 1; } +diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c +index 9ea81e54..292e27dc 100644 +--- a/src/lj_ffrecord.c ++++ b/src/lj_ffrecord.c +@@ -173,7 +173,7 @@ static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd) + switch (J->fn->c.ffid) { + case FF_error: + case FF_debug_sethook: +- case FF_jit_flush: ++ //case FF_jit_flush: + break; /* Don't stitch across special builtins. */ + default: + recff_stitch(J); /* Use trace stitching. */ +diff --git a/src/luajit.c b/src/luajit.c +index a725db1c..5095ae36 100644 +--- a/src/luajit.c ++++ b/src/luajit.c +@@ -134,24 +134,15 @@ static void print_version(void) + fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout); + } + ++#include "lj_dispatch.h" ++#include "lj_jit.h" ++ + static void print_jit_status(lua_State *L) + { +- int n; +- const char *s; +- lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); +- lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ +- lua_remove(L, -2); +- lua_getfield(L, -1, "status"); +- lua_remove(L, -2); +- n = lua_gettop(L); +- lua_call(L, 0, LUA_MULTRET); +- fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout); +- for (n++; (s = lua_tostring(L, n)); n++) { +- putc(' ', stdout); +- fputs(s, stdout); +- } +- putc('\n', stdout); +- lua_settop(L, 0); /* clear stack */ ++ jit_State *J = L2J(L); ++ fputs(J->flags & JIT_F_ON ? "JIT: ON" : "JIT: OFF", stdout); ++ fputs(J->flags & JIT_F_OPT_DEFAULT ? " OPT: DEFAULT" : " OPT: OFF", stdout); ++ fprintf(stdout, "\n"); + } + + static void createargtable(lua_State *L, char **argv, int argc, int argf) From bf8591fbee165f7c44afb2e51eee7e509d164417 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 1 Mar 2025 14:10:00 +0100 Subject: [PATCH 022/527] Added initial FFI mappings for lua.h --- src/ffi/lua.rs | 188 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 188 insertions(+) diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 31ba077..6c00355 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -26,4 +26,192 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::{c_char, c_double, c_int, c_void}; +pub const REGISTRYINDEX: c_int = -10000; +pub const ENVIRONINDEX: c_int = -10001; +pub const GLOBALSINDEX: c_int = -10002; + +pub const fn upvalueindex(i: c_int) -> c_int { + GLOBALSINDEX - i +} + +#[repr(i32)] +pub enum ThreadStatus { + Ok = 0, + Yield = 1, + ErrRun = 2, + ErrSyntax = 3, + ErrMem = 4, + ErrErr = 5 +} + +#[repr(transparent)] +pub struct State(*mut c_void); + +pub type CFunction = extern "C" fn(l: State) -> i32; + +pub type Reader = extern "C" fn(l: State, ud: *mut c_void, sz: usize) -> *const c_char; + +pub type Writer = extern "C" fn(l: State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; + +#[repr(i32)] +pub enum Type { + None = -1, + Nil = 0, + Boolean = 1, + LightUserdata = 2, + Number = 3, + String = 4, + Table = 5, + Function = 6, + Userdata = 7, + Thread = 8 +} + +pub type Number = c_double; +pub type Integer = isize; + + +//-------------------- +// State manipulation +//-------------------- +extern "C" { + pub fn lua_close(l: State); + + pub fn lua_atpanic(l: State, panicf: CFunction) -> CFunction; +} + +//-------------------------- +// Basic stack manipulation +//-------------------------- +extern "C" { + pub fn lua_gettop(l: State) -> c_int; + pub fn lua_settop(l: State, idx: c_int); + pub fn lua_pushvalue(l: State, idx: c_int); + pub fn lua_remove(l: State, idx: c_int); + pub fn lua_insert(l: State, idx: c_int); + pub fn lua_replace(l: State, idx: c_int); + pub fn lua_checkstack(l: State, sz: c_int) -> c_int; + + pub fn lua_xmove(from: State, to: State, n: c_int); +} + +//------------------------------- +// Access functions (stack -> C) +//------------------------------- +extern "C" { + pub fn lua_isnumber(l: State, idx: c_int) -> c_int; + pub fn lua_isstring(l: State, idx: c_int) -> c_int; + pub fn lua_iscfunction(l: State, idx: c_int) -> c_int; + pub fn lua_isuserdata(l: State, idx: c_int) -> c_int; + pub fn lua_type(l: State, idx: c_int) -> Type; + pub fn lua_typename(l: State, tp: c_int) -> *const c_char; + + pub fn lua_equal(l: State, idx1: c_int, idx2: c_int) -> c_int; + pub fn lua_rawequal(l: State, idx1: c_int, idx2: c_int) -> c_int; + pub fn lua_lessthan(l: State, idx1: c_int, idx2: c_int) -> c_int; + + pub fn lua_tonumber(l: State, idx: c_int) -> Number; + pub fn lua_tointeger(l: State, idx: c_int) -> Integer; + pub fn lua_toboolean(l: State, idx: c_int) -> c_int; + pub fn lua_tolstring(l: State, idx: c_int, len: *mut usize) -> *const c_char; + pub fn lua_objlen(l: State, idx: c_int) -> usize; + pub fn lua_tocfunction(l: State, idx: c_int) -> CFunction; + pub fn lua_touserdata(l: State, idx: c_int) -> *mut c_void; + pub fn lua_tothread(l: State, idx: c_int) -> State; + pub fn lua_topointer(l: State, idx: c_int) -> *const c_void; +} + +//------------------------------- +// Push functions (C -> stack) +//------------------------------- +extern "C" { + pub fn lua_pushnil(l: State); + pub fn lua_pushnumber(l: State, n: Number); + pub fn lua_pushinteger(l: State, n: Integer); + pub fn lua_pushlstring(l: State, s: *const c_char, len: usize); + pub fn lua_pushstring(l: State, s: *const c_char); + //LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); + pub fn lua_pushfstring(l: State, fmt: *const c_char, ...) -> *const c_char; + pub fn lua_pushcclosure(l: State, fun: CFunction, n: c_int); + pub fn lua_pushboolean(l: State, b: c_int); + pub fn lua_pushlightuserdata(l: State, p: *mut c_void); + pub fn lua_pushthread(l: State); +} + +//------------------------------- +// Get functions (Lua -> stack) +//------------------------------- +extern "C" { + pub fn lua_gettable(l: State, idx: c_int); + pub fn lua_getfield(l: State, idx: c_int, k: *const c_char); + pub fn lua_rawget(l: State, idx: c_int); + pub fn lua_rawgeti(l: State, idx: c_int, n: c_int); + pub fn lua_createtable(l: State, narr: c_int, nrec: c_int); + pub fn lua_newuserdata(l: State, sz: usize) -> *mut c_void; + pub fn lua_getmetatable(l: State, objindex: c_int) -> c_int; + pub fn lua_getfenv(l: State, idx: c_int); +} + +//------------------------------- +// Set functions (stack -> Lua) +//------------------------------- +extern "C" { + pub fn lua_settable(l: State, idx: c_int); + pub fn lua_setfield(l: State, idx: c_int, k: *const c_char); + pub fn lua_rawset(l: State, idx: c_int); + pub fn lua_rawseti(l: State, idx: c_int, n: c_int); + pub fn lua_setmetatable(l: State, objindex: c_int) -> c_int; + pub fn lua_setfenv(l: State, idx: c_int) -> c_int; +} + +//----------------------------------------------------- +// `load' and `call' functions (load and run Lua code) +//----------------------------------------------------- +extern "C" { + pub fn lua_call(l: State, nargs: c_int, nresults: c_int); + pub fn lua_pcall(l: State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int; + pub fn lua_cpcall(l: State, func: CFunction, ud: *mut c_void) -> c_int; + pub fn lua_load(l: State, reader: Reader, dt: *mut c_void, chunkname: *const c_char) -> c_int; + + pub fn lua_dump(l: State, writer: Writer, data: *mut c_void) -> c_int; +} + +//--------------------- +// Coroutine functions +//--------------------- +extern "C" { + pub fn lua_yield(l: State, nresults: c_int) -> c_int; + pub fn lua_resume(l: State, narg: c_int) -> c_int; + pub fn lua_status(l: State) -> c_int; +} + +//----------------------------------------- +// Garbage-collection function and options +//----------------------------------------- +#[repr(i32)] +pub enum Gc { + Stop = 0, + Restart = 1, + Collect = 2, + Count = 3, + Countb = 4, + Step = 5, + SetPause = 6, + SetStepMul = 7, + IsRunning = 9, +} + +extern "C" { + pub fn lua_gc(l: State, what: Gc, data: c_int) -> c_int; +} + +//------------------------- +// Miscellaneous functions +//------------------------- +extern "C" { + pub fn lua_error(l: State) -> c_int; + pub fn lua_next(l: State, idx: c_int) -> c_int; + pub fn lua_concat(l: State, n: c_int); +} From b32840c6b86458738dac82ae9812235172707b15 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 1 Mar 2025 23:20:13 +0100 Subject: [PATCH 023/527] Fixed broken lib path in build script --- build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.rs b/build.rs index 4cff88d..cbfcc24 100644 --- a/build.rs +++ b/build.rs @@ -119,6 +119,6 @@ fn main() { build_luajit(&out_path); // Attempt to setup linkage. - println!("cargo:rustc-link-search=native={}", out_path.display()); + println!("cargo:rustc-link-search=native={}", out_path.join("src").display()); println!("cargo:rustc-link-lib=static=luajit"); } From c47a10d60833dc48e2b4a95b3ab90beb749de03a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 2 Mar 2025 17:16:52 +0100 Subject: [PATCH 024/527] Added laux lib and signature for precompiled code --- src/ffi/laux.rs | 105 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ffi/lua.rs | 3 ++ src/ffi/mod.rs | 1 + 3 files changed, 109 insertions(+) create mode 100644 src/ffi/laux.rs diff --git a/src/ffi/laux.rs b/src/ffi/laux.rs new file mode 100644 index 0000000..3c12d7f --- /dev/null +++ b/src/ffi/laux.rs @@ -0,0 +1,105 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::{c_char, c_int, c_void}; +use crate::ffi::lua::{Integer, Number, State, Type}; + +//-------------------- +// State manipulation +//-------------------- +extern "C" { + pub fn luaL_newstate() -> State; + pub fn luaL_openlibs(l: State); +} + +//---------------- +// Error handling +//---------------- +extern "C" { + pub fn luaL_error(l: State, fmt: *const c_char, ...) -> c_int; + pub fn luaL_typerror(l: State, narg: c_int, tname: *const c_char) -> c_int; + pub fn luaL_argerror(l: State, numarg: c_int, extramsg: *const c_char) -> c_int; +} + +//--------------- +// Value reading +//--------------- +extern "C" { + pub fn luaL_checklstring(l: State, numarg: c_int, len: *mut usize) -> *const c_char; + pub fn luaL_optlstring(l: State, numarg: c_int, def: *const c_char, len: *mut usize) -> *const c_char; + + pub fn luaL_checknumber(l: State, numarg: c_int) -> Number; + pub fn luaL_optnumber(l: State, narg: c_int, def: Number) -> Number; + + pub fn luaL_checkinteger(l: State, numarg: c_int) -> Integer; + pub fn luaL_optinteger(l: State, narg: c_int, def: Integer) -> Integer; + + pub fn luaL_checkstack(l: State, sz: c_int, msg: *const c_char); + pub fn luaL_checktype(l: State, narg: c_int, t: Type); + pub fn luaL_checkany(l: State, narg: c_int); + + pub fn luaL_checkudata(l: State, ud: c_int, tname: *const c_char) -> *mut c_void; +} + +//------------------------ +// Metatable manipulation +//------------------------ +extern "C" { + pub fn luaL_newmetatable(l: State, tname: *const c_char) -> c_int; + pub fn luaL_getmetafield(l: State, obj: c_int, e: *const c_char) -> c_int; + pub fn luaL_callmeta(l: State, obj: c_int, e: *const c_char) -> c_int; +} + +//------------------------- +// Miscellaneous functions +//------------------------- +extern "C" { + pub fn luaL_where(l: State, lvl: c_int); + + pub fn luaL_checkoption(l: State, narg: c_int, def: *const c_char, lst: *const *const c_char) -> c_int; +} + +//---------- +// Registry +//---------- +pub const NOREF: c_int = -2; +pub const REFNIL: c_int = -1; + +extern "C" { + pub fn luaL_ref(l: State, t: c_int) -> c_int; + pub fn luaL_unref(l: State, t: c_int, r: c_int); +} + +//------------------ +// Loading lua code +//------------------ +extern "C" { + pub fn luaL_loadfile(l: State, filename: *const c_char) -> c_int; + pub fn luaL_loadbuffer(l: State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int; + pub fn luaL_loadstring(l: State, s: *const c_char) -> c_int; +} diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 6c00355..007b5dc 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -28,6 +28,9 @@ use std::ffi::{c_char, c_double, c_int, c_void}; +/* mark for precompiled code (`Lua') */ +pub const SIGNATURE: &[u8] = b"\033Lua"; + pub const REGISTRYINDEX: c_int = -10000; pub const ENVIRONINDEX: c_int = -10001; pub const GLOBALSINDEX: c_int = -10002; diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 2543c7f..86e4b2a 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -27,3 +27,4 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod lua; +mod laux; From e90136640e76ee2ced3bf83f43eeb7caa27bdf11 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 2 Mar 2025 17:18:06 +0100 Subject: [PATCH 025/527] Added disable_lua_load patch --- build.rs | 6 +- patch/disable_lua_load.patch | 108 +++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 patch/disable_lua_load.patch diff --git a/build.rs b/build.rs index cbfcc24..6f568cd 100644 --- a/build.rs +++ b/build.rs @@ -95,7 +95,7 @@ fn main() { let out = std::env::var_os("OUT_DIR").expect("Failed to acquire output directory"); let out_path = Path::new(&out).join("luajit-build"); - // Apply patch to LuaJIT source code. + // Apply patches to LuaJIT source code. let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lib_init.patch").as_os_str()]); if !result.success() { @@ -105,6 +105,10 @@ fn main() { if !result.success() { panic!("Failed to patch LuaJIT"); } + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/disable_lua_load.patch").as_os_str()]); + if !result.success() { + panic!("Failed to patch LuaJIT"); + } // Copy the source directory to the build directory. println!("{}", out_path.display()); diff --git a/patch/disable_lua_load.patch b/patch/disable_lua_load.patch new file mode 100644 index 0000000..73d1598 --- /dev/null +++ b/patch/disable_lua_load.patch @@ -0,0 +1,108 @@ +diff --git a/src/lib_base.c b/src/lib_base.c +index 5d1b88a9..f8419349 100644 +--- a/src/lib_base.c ++++ b/src/lib_base.c +@@ -361,103 +361,6 @@ LJLIB_ASM_(xpcall) LJLIB_REC(.) + + /* -- Base library: load Lua code ----------------------------------------- */ + +-static int load_aux(lua_State *L, int status, int envarg) +-{ +- if (status == LUA_OK) { +- /* +- ** Set environment table for top-level function. +- ** Don't do this for non-native bytecode, which returns a prototype. +- */ +- if (tvistab(L->base+envarg-1) && tvisfunc(L->top-1)) { +- GCfunc *fn = funcV(L->top-1); +- GCtab *t = tabV(L->base+envarg-1); +- setgcref(fn->c.env, obj2gco(t)); +- lj_gc_objbarrier(L, fn, t); +- } +- return 1; +- } else { +- setnilV(L->top-2); +- return 2; +- } +-} +- +-LJLIB_CF(loadfile) +-{ +- GCstr *fname = lj_lib_optstr(L, 1); +- GCstr *mode = lj_lib_optstr(L, 2); +- int status; +- lua_settop(L, 3); /* Ensure env arg exists. */ +- status = luaL_loadfilex(L, fname ? strdata(fname) : NULL, +- mode ? strdata(mode) : NULL); +- return load_aux(L, status, 3); +-} +- +-static const char *reader_func(lua_State *L, void *ud, size_t *size) +-{ +- UNUSED(ud); +- luaL_checkstack(L, 2, "too many nested functions"); +- copyTV(L, L->top++, L->base); +- lua_call(L, 0, 1); /* Call user-supplied function. */ +- L->top--; +- if (tvisnil(L->top)) { +- *size = 0; +- return NULL; +- } else if (tvisstr(L->top) || tvisnumber(L->top)) { +- copyTV(L, L->base+4, L->top); /* Anchor string in reserved stack slot. */ +- return lua_tolstring(L, 5, size); +- } else { +- lj_err_caller(L, LJ_ERR_RDRSTR); +- return NULL; +- } +-} +- +-LJLIB_CF(load) +-{ +- GCstr *name = lj_lib_optstr(L, 2); +- GCstr *mode = lj_lib_optstr(L, 3); +- int status; +- if (L->base < L->top && +- (tvisstr(L->base) || tvisnumber(L->base) || tvisbuf(L->base))) { +- const char *s; +- MSize len; +- if (tvisbuf(L->base)) { +- SBufExt *sbx = bufV(L->base); +- s = sbx->r; +- len = sbufxlen(sbx); +- if (!name) name = &G(L)->strempty; /* Buffers are not NUL-terminated. */ +- } else { +- GCstr *str = lj_lib_checkstr(L, 1); +- s = strdata(str); +- len = str->len; +- } +- lua_settop(L, 4); /* Ensure env arg exists. */ +- status = luaL_loadbufferx(L, s, len, name ? strdata(name) : s, +- mode ? strdata(mode) : NULL); +- } else { +- lj_lib_checkfunc(L, 1); +- lua_settop(L, 5); /* Reserve a slot for the string from the reader. */ +- status = lua_loadx(L, reader_func, NULL, name ? strdata(name) : "=(load)", +- mode ? strdata(mode) : NULL); +- } +- return load_aux(L, status, 4); +-} +- +-LJLIB_CF(loadstring) +-{ +- return lj_cf_load(L); +-} +- +-LJLIB_CF(dofile) +-{ +- GCstr *fname = lj_lib_optstr(L, 1); +- setnilV(L->top); +- L->top = L->base+1; +- if (luaL_loadfile(L, fname ? strdata(fname) : NULL) != LUA_OK) +- lua_error(L); +- lua_call(L, 0, LUA_MULTRET); +- return (int)(L->top - L->base) - 1; +-} +- + /* -- Base library: GC control -------------------------------------------- */ + + LJLIB_CF(gcinfo) From 63c9843cfe6dceaaa0570775db0a0a34e6d058ae Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 2 Mar 2025 17:20:26 +0100 Subject: [PATCH 026/527] Made laux lib public --- src/ffi/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ffi/mod.rs b/src/ffi/mod.rs index 86e4b2a..0f4c2cb 100644 --- a/src/ffi/mod.rs +++ b/src/ffi/mod.rs @@ -27,4 +27,4 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod lua; -mod laux; +pub mod laux; From be5ae4a820bf34b89378811c4a9be60f8399f934 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 07:54:06 +0100 Subject: [PATCH 027/527] Fixed crash in lua FFI --- src/ffi/lua.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index 007b5dc..b0cf418 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -50,15 +50,17 @@ pub enum ThreadStatus { } #[repr(transparent)] +#[derive(Clone, Copy)] pub struct State(*mut c_void); -pub type CFunction = extern "C" fn(l: State) -> i32; +pub type CFunction = extern "C-unwind" fn(l: State) -> i32; pub type Reader = extern "C" fn(l: State, ud: *mut c_void, sz: usize) -> *const c_char; pub type Writer = extern "C" fn(l: State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; #[repr(i32)] +#[derive(Clone, Copy, Debug)] pub enum Type { None = -1, Nil = 0, From 22dcd4c71d317c8e29010c8d511485b527b486d5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 07:54:50 +0100 Subject: [PATCH 028/527] Added first Lua VM test using value readers. --- src/lib.rs | 31 +++++++++++ src/util.rs | 48 +++++++++++++++++ src/value.rs | 103 ++++++++++++++++++++++++++++++++++++ src/vm.rs | 73 +++++++++++++++++++++++++ tests/test_vm_destructor.rs | 84 +++++++++++++++++++++++++++++ 5 files changed, 339 insertions(+) create mode 100644 src/util.rs create mode 100644 src/value.rs create mode 100644 src/vm.rs create mode 100644 tests/test_vm_destructor.rs diff --git a/src/lib.rs b/src/lib.rs index 904633c..c93dda2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,3 +27,34 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod ffi; +pub mod vm; +pub mod value; +pub mod util; + +macro_rules! declare_lib { + ( + pub lib $($namespace: ident)*.$name: ident { + $( + pub fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + )* + } + ) => { + const LIB_NAME: &str = stringify!($($namespace.)*$name); + fn register_lib() { + $( + { + const FN_NAME: &str = stringify!($fn_name); + let args = &[$(std::any::TypeId::of::<$arg_ty>()),*]; + } + )* + } + }; +} + +declare_lib! { + pub lib bp3d.math { + pub fn test(a: f32, b: f32) -> f32 { + + } + } +} diff --git a/src/util.rs b/src/util.rs new file mode 100644 index 0000000..6b76230 --- /dev/null +++ b/src/util.rs @@ -0,0 +1,48 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::error::Error; +use crate::ffi::lua::{lua_error, lua_pushlstring, State}; + +pub unsafe trait SimpleDrop {} + +unsafe impl SimpleDrop for T {} + +pub unsafe fn lua_rust_error(l: State, error: E) -> ! { + // At this point the function is assumed to be a non-POF (error and String). + let s = format!("rust error: {}", error); + lua_pushlstring(l, s.as_ptr() as _, s.len()); + // Drop both the error and the error string. + // Very important as lua_error does not return. + drop(error); + drop(s); + // Now the function should be back what Rust calls a POF. + lua_error(l); + // If this is reached, then lua_error has silently failed. + std::unreachable!() +} diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..2acdd42 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,103 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::slice; +use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; +use crate::util::{lua_rust_error, SimpleDrop}; +use crate::vm::Stack; + +/// This trait represents a function parameter. +pub trait FromParam: Sized + SimpleDrop { + fn from_lua(stack: &Stack) -> Self; +} + +impl FromParam for &str { + fn from_lua(stack: &Stack) -> Self { + unsafe { + let mut len: usize = 0; + let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); + let slice = slice::from_raw_parts(str as *const u8, len); + match std::str::from_utf8(slice){ + Ok(v) => v, + Err(e) => { + lua_rust_error(stack.as_ptr(), e); + } + } + } + } +} + +macro_rules! impl_integer { + ($($t: ty),*) => { + $( + impl FromParam for $t { + fn from_lua(stack: &Stack) -> Self { + unsafe { + luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ + } + } + } + )* + }; +} + +#[cfg(target_pointer_width = "64")] +impl FromParam for i64 { + fn from_lua(stack: &Stack) -> Self { + unsafe { + luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ + } + } +} + +#[cfg(target_pointer_width = "64")] +impl FromParam for u64 { + fn from_lua(stack: &Stack) -> Self { + unsafe { + luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ + } + } +} + +impl_integer!(i8, u8, i16, u16, i32, u32); + +impl FromParam for f64 { + fn from_lua(stack: &Stack) -> Self { + unsafe { + luaL_checknumber(stack.as_ptr(), stack.pop()) as _ + } + } +} + +impl FromParam for f32 { + fn from_lua(stack: &Stack) -> Self { + unsafe { + luaL_checknumber(stack.as_ptr(), stack.pop()) as _ + } + } +} diff --git a/src/vm.rs b/src/vm.rs new file mode 100644 index 0000000..b969213 --- /dev/null +++ b/src/vm.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::Cell; +use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; +use crate::ffi::lua::State; + +pub struct Stack { + l: State, + index: Cell +} + +impl Stack { + pub unsafe fn wrap(l: State) -> Stack { + Stack { + l, + index: Cell::new(1) + } + } + + pub fn as_ptr(&self) -> State { + self.l + } + + pub fn pop(&self) -> i32 { + let i = self.index.get(); + self.index.set(i + 1); + i + } +} + +pub struct Vm { + l: State +} + +impl Vm { + pub fn new() -> Vm { + let l = unsafe { luaL_newstate() }; + unsafe { luaL_openlibs(l) }; + Vm { + l + } + } + + pub fn as_ptr(&self) -> State { + self.l + } +} diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs new file mode 100644 index 0000000..fc7811b --- /dev/null +++ b/tests/test_vm_destructor.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::value::FromParam; +use bp3d_lua::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_error, luaL_loadstring}; +use bp3d_lua::ffi::lua::{lua_call, lua_error, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushfstring, lua_pushlstring, lua_pushnumber, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, Number, State, GLOBALSINDEX}; +use bp3d_lua::vm::{Stack, Vm}; + +struct ValueWithDrop; +impl ValueWithDrop { + pub fn print(&self) { + println!("ValueWithDrop") + } +} +impl Drop for ValueWithDrop { + fn drop(&mut self) { + println!("Dropping!"); + } +} + +fn safe_test_c_function(name: &str, value: f64) { + let drop = ValueWithDrop; + drop.print(); + println!("Hello {} ({})", name, value); +} + +extern "C-unwind" fn test_c_function(l: State) -> i32 { + let stack = unsafe { Stack::wrap(l) }; + let name: &str = FromParam::from_lua(&stack); + let value = f64::from_lua(&stack); + safe_test_c_function(name, value); + 1 +} + +extern "C" fn test_error_handler(l: State) -> i32 { + println!("An error has occured from lua VM"); + //luaL_traceback(L, L, lua_tostring(L, 1), 1); + 1 +} + +#[test] +fn test_vm_destructor() { + let vm = Vm::new(); + unsafe { + lua_pushcclosure(vm.as_ptr(), test_c_function, 0); + lua_setfield(vm.as_ptr(), GLOBALSINDEX, c"test_c_function".as_ptr()); + assert_eq!(luaL_loadstring(vm.as_ptr(), c"return test_c_function('this is a test', 0.42)".as_ptr()), 0); + let i = lua_pcall(vm.as_ptr(), 0, 0, 0); + if i != 0 { + println!("{:?}", lua_type(vm.as_ptr(), -1)); + if lua_isstring(vm.as_ptr(), -1) == 1 { + let mut len: usize = 0; + let s = lua_tolstring(vm.as_ptr(), -1, &mut len as _); + let slice = std::slice::from_raw_parts(s as *const u8, len); + println!("Error as string: {:?}", std::str::from_utf8(slice)); + } + } + } +} From 0899cd7568a112356d0a1059aba82eb10365db99 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 20:51:22 +0100 Subject: [PATCH 029/527] Added IntoParam and refactored Vm to its own sub folder --- src/lib.rs | 1 - src/{vm.rs => vm/core.rs} | 0 src/{value.rs => vm/function.rs} | 91 +++++++++++++++++++++----------- src/vm/mod.rs | 32 +++++++++++ tests/test_vm_destructor.rs | 20 +++---- 5 files changed, 101 insertions(+), 43 deletions(-) rename src/{vm.rs => vm/core.rs} (100%) rename src/{value.rs => vm/function.rs} (61%) create mode 100644 src/vm/mod.rs diff --git a/src/lib.rs b/src/lib.rs index c93dda2..f046e5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ pub mod ffi; pub mod vm; -pub mod value; pub mod util; macro_rules! declare_lib { diff --git a/src/vm.rs b/src/vm/core.rs similarity index 100% rename from src/vm.rs rename to src/vm/core.rs diff --git a/src/value.rs b/src/vm/function.rs similarity index 61% rename from src/value.rs rename to src/vm/function.rs index 2acdd42..763e538 100644 --- a/src/value.rs +++ b/src/vm/function.rs @@ -28,16 +28,31 @@ use std::slice; use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; +use crate::ffi::lua::{lua_pushinteger, lua_pushlstring, lua_pushnumber}; use crate::util::{lua_rust_error, SimpleDrop}; use crate::vm::Stack; +/// This trait represents a function return value. +pub trait IntoParam: Sized + SimpleDrop { + /// Turns self into a function return parameter. + /// + /// This function returns the number of parameters pushed onto the lua stack. + /// + /// # Arguments + /// + /// * `stack`: the stack to push this value to. + /// + /// returns: u16 + fn into_param(self, stack: &Stack) -> u16; +} + /// This trait represents a function parameter. pub trait FromParam: Sized + SimpleDrop { - fn from_lua(stack: &Stack) -> Self; + fn from_param(stack: &Stack) -> Self; } impl FromParam for &str { - fn from_lua(stack: &Stack) -> Self { + fn from_param(stack: &Stack) -> Self { unsafe { let mut len: usize = 0; let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); @@ -52,52 +67,64 @@ impl FromParam for &str { } } +impl IntoParam for &str { + fn into_param(self, stack: &Stack) -> u16 { + unsafe { + lua_pushlstring(stack.as_ptr(), self.as_ptr() as _, self.len()); + } + 1 + } +} + macro_rules! impl_integer { ($($t: ty),*) => { $( impl FromParam for $t { - fn from_lua(stack: &Stack) -> Self { + fn from_param(stack: &Stack) -> Self { unsafe { luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ } } } + + impl IntoParam for $t { + fn into_param(self, stack: &Stack) -> u16 { + unsafe { + lua_pushinteger(stack.as_ptr(), self as _); + 1 + } + } + } )* }; } #[cfg(target_pointer_width = "64")] -impl FromParam for i64 { - fn from_lua(stack: &Stack) -> Self { - unsafe { - luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ - } - } -} - -#[cfg(target_pointer_width = "64")] -impl FromParam for u64 { - fn from_lua(stack: &Stack) -> Self { - unsafe { - luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ - } - } -} +impl_integer!(i64, u64); impl_integer!(i8, u8, i16, u16, i32, u32); -impl FromParam for f64 { - fn from_lua(stack: &Stack) -> Self { - unsafe { - luaL_checknumber(stack.as_ptr(), stack.pop()) as _ - } - } -} +macro_rules! impl_float { + ($($t: ty),*) => { + $( + impl FromParam for $t { + fn from_param(stack: &Stack) -> Self { + unsafe { + luaL_checknumber(stack.as_ptr(), stack.pop()) as _ + } + } + } -impl FromParam for f32 { - fn from_lua(stack: &Stack) -> Self { - unsafe { - luaL_checknumber(stack.as_ptr(), stack.pop()) as _ - } - } + impl IntoParam for $t { + fn into_param(self, stack: &Stack) -> u16 { + unsafe { + lua_pushnumber(stack.as_ptr(), self as _); + 1 + } + } + } + )* + }; } + +impl_float!(f32, f64); diff --git a/src/vm/mod.rs b/src/vm/mod.rs new file mode 100644 index 0000000..fe967ef --- /dev/null +++ b/src/vm/mod.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod core; +pub mod function; + +pub use core::*; diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index fc7811b..4abcb26 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::value::FromParam; -use bp3d_lua::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_error, luaL_loadstring}; -use bp3d_lua::ffi::lua::{lua_call, lua_error, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushfstring, lua_pushlstring, lua_pushnumber, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, Number, State, GLOBALSINDEX}; +use bp3d_lua::vm::function::{FromParam, IntoParam}; +use bp3d_lua::ffi::laux::luaL_loadstring; +use bp3d_lua::ffi::lua::{lua_isstring, lua_pcall, lua_pushcclosure, lua_setfield, lua_tolstring, lua_type, State, GLOBALSINDEX}; use bp3d_lua::vm::{Stack, Vm}; struct ValueWithDrop; @@ -43,18 +43,18 @@ impl Drop for ValueWithDrop { } } -fn safe_test_c_function(name: &str, value: f64) { +fn safe_test_c_function(name: &str, value: f64) -> String { let drop = ValueWithDrop; drop.print(); - println!("Hello {} ({})", name, value); + format!("Hello {} ({})", name, value) } extern "C-unwind" fn test_c_function(l: State) -> i32 { let stack = unsafe { Stack::wrap(l) }; - let name: &str = FromParam::from_lua(&stack); - let value = f64::from_lua(&stack); - safe_test_c_function(name, value); - 1 + let name: &str = FromParam::from_param(&stack); + let value = f64::from_param(&stack); + let res = safe_test_c_function(name, value); + res.into_param(&stack) as _ } extern "C" fn test_error_handler(l: State) -> i32 { @@ -69,7 +69,7 @@ fn test_vm_destructor() { unsafe { lua_pushcclosure(vm.as_ptr(), test_c_function, 0); lua_setfield(vm.as_ptr(), GLOBALSINDEX, c"test_c_function".as_ptr()); - assert_eq!(luaL_loadstring(vm.as_ptr(), c"return test_c_function('this is a test', 0.42)".as_ptr()), 0); + assert_eq!(luaL_loadstring(vm.as_ptr(), c"return test_c_function('this is a test\\xFF', 0.42)".as_ptr()), 0); let i = lua_pcall(vm.as_ptr(), 0, 0, 0); if i != 0 { println!("{:?}", lua_type(vm.as_ptr(), -1)); From 4b1ea7748fe3c0402214e14cfafb79d02506b500 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 21:10:50 +0100 Subject: [PATCH 030/527] Added doc to Stack::wrap --- src/vm/core.rs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/vm/core.rs b/src/vm/core.rs index b969213..44dccb8 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -36,6 +36,18 @@ pub struct Stack { } impl Stack { + /// Creates a new [Stack] by wrapping an existing lua VM. + /// + /// # Arguments + /// + /// * `l`: the raw lua state to wrap. + /// + /// returns: Stack + /// + /// # Safety + /// + /// This struct SHALL only exist in a [CFunction](crate::ffi::lua::CFunction). Usage in any other + /// context is UB. pub unsafe fn wrap(l: State) -> Stack { Stack { l, From 39c22e70f8f5a0408cceca7e240eb0f6d1a18c32 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 21:45:09 +0100 Subject: [PATCH 031/527] Added checked from_lua and into_lua --- Cargo.toml | 1 + src/vm/function.rs | 9 ++- src/vm/mod.rs | 1 + src/vm/value.rs | 143 +++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 src/vm/value.rs diff --git a/Cargo.toml b/Cargo.toml index 130e6e3..617ab81 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,6 +13,7 @@ categories = ["graphics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] +bp3d-util = { version = "1.4.0", features = ["simple-error"] } [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } diff --git a/src/vm/function.rs b/src/vm/function.rs index 763e538..7fba337 100644 --- a/src/vm/function.rs +++ b/src/vm/function.rs @@ -28,7 +28,7 @@ use std::slice; use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; -use crate::ffi::lua::{lua_pushinteger, lua_pushlstring, lua_pushnumber}; +use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnumber}; use crate::util::{lua_rust_error, SimpleDrop}; use crate::vm::Stack; @@ -128,3 +128,10 @@ macro_rules! impl_float { } impl_float!(f32, f64); + +impl IntoParam for bool { + fn into_param(self, stack: &Stack) -> u16 { + unsafe { lua_pushboolean(stack.as_ptr(), if self { 1 } else { 0 }) }; + 1 + } +} diff --git a/src/vm/mod.rs b/src/vm/mod.rs index fe967ef..99ca607 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -28,5 +28,6 @@ mod core; pub mod function; +pub mod value; pub use core::*; diff --git a/src/vm/value.rs b/src/vm/value.rs new file mode 100644 index 0000000..ff53107 --- /dev/null +++ b/src/vm/value.rs @@ -0,0 +1,143 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Display, Formatter}; +use std::str::Utf8Error; +use bp3d_util::simple_error; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean}; +use crate::vm::function::IntoParam; +use crate::vm::{Stack, Vm}; + +#[derive(Debug, Copy, Clone)] +pub struct TypeError { + pub expected: Type, + pub actual: Type +} + +impl Display for TypeError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "expected {:?}, got {:?}", self.expected, self.actual) + } +} + +simple_error! { + pub Error { + InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", + TypeError(TypeError) => "type error: {}" + } +} + +pub trait FromLua: Sized { + /// Attempt to read the value at the specified index in the given [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to read from. + /// * `index`: the index at which to try reading the value from. + /// + /// returns: Result + fn from_lua(vm: &Vm, index: i32) -> Result; +} + +pub trait IntoLua: Sized { + /// Attempt to push self onto the top of the stack in the given [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to push into. + /// + /// returns: Result + fn into_lua(self, vm: &Vm) -> Result<(), Error>; +} + +impl FromLua for &str { + fn from_lua(vm: &Vm, index: i32) -> Result { + let l = vm.as_ptr(); + unsafe { + let ty = lua_type(l, index); + match ty { + Type::String => { + let mut len: usize = 0; + let s = lua_tolstring(l, index, &mut len as _); + let slice = std::slice::from_raw_parts(s as _, len); + std::str::from_utf8(slice).map_err(Error::InvalidUtf8) + }, + _ => Err(Error::TypeError(TypeError { + expected: Type::String, + actual: ty + })) + } + } + } +} + +macro_rules! impl_from_lua { + ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { + impl FromLua for $t { + fn from_lua(vm: &Vm, index: i32) -> Result { + let l = vm.as_ptr(); + unsafe { + let ty = lua_type(l, index); + match ty { + Type::$expected => Ok($func(l, index) $($ret)*), + _ => Err(Error::TypeError(TypeError { + expected: Type::$expected, + actual: ty + })) + } + } + } + } + }; +} + +#[cfg(target_pointer_width = "64")] +impl_from_lua!(i64, Number, lua_tointeger, as _); + +#[cfg(target_pointer_width = "64")] +impl_from_lua!(u64, Number, lua_tointeger, as _); + +impl_from_lua!(i8, Number, lua_tointeger, as _); +impl_from_lua!(u8, Number, lua_tointeger, as _); +impl_from_lua!(i16, Number, lua_tointeger, as _); +impl_from_lua!(u16, Number, lua_tointeger, as _); +impl_from_lua!(i32, Number, lua_tointeger, as _); +impl_from_lua!(u32, Number, lua_tointeger, as _); + +impl_from_lua!(f32, Number, lua_tonumber, as _); +impl_from_lua!(f64, Number, lua_tonumber, as _); + +impl_from_lua!(bool, Boolean, lua_toboolean, == 1); + +impl IntoLua for T { + fn into_lua(self, vm: &Vm) -> Result<(), Error> { + let stack = unsafe { Stack::wrap(vm.as_ptr()) }; + self.into_param(&stack); + Ok(()) + } +} From 7e05bf76bdb9d0c4cb586bd2a4692f000f5e5848 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 22:08:13 +0100 Subject: [PATCH 032/527] Added start index to Stack --- src/vm/core.rs | 5 +++-- src/vm/value.rs | 2 +- tests/test_vm_destructor.rs | 2 +- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/vm/core.rs b/src/vm/core.rs index 44dccb8..66b5f28 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -41,6 +41,7 @@ impl Stack { /// # Arguments /// /// * `l`: the raw lua state to wrap. + /// * `start`: the index at which to start reading values from the lua stack. /// /// returns: Stack /// @@ -48,10 +49,10 @@ impl Stack { /// /// This struct SHALL only exist in a [CFunction](crate::ffi::lua::CFunction). Usage in any other /// context is UB. - pub unsafe fn wrap(l: State) -> Stack { + pub unsafe fn wrap(l: State, start: i32) -> Stack { Stack { l, - index: Cell::new(1) + index: Cell::new(start) } } diff --git a/src/vm/value.rs b/src/vm/value.rs index ff53107..2b73435 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -136,7 +136,7 @@ impl_from_lua!(bool, Boolean, lua_toboolean, == 1); impl IntoLua for T { fn into_lua(self, vm: &Vm) -> Result<(), Error> { - let stack = unsafe { Stack::wrap(vm.as_ptr()) }; + let stack = unsafe { Stack::wrap(vm.as_ptr(), 0) }; self.into_param(&stack); Ok(()) } diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 4abcb26..87d9f0f 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -50,7 +50,7 @@ fn safe_test_c_function(name: &str, value: f64) -> String { } extern "C-unwind" fn test_c_function(l: State) -> i32 { - let stack = unsafe { Stack::wrap(l) }; + let stack = unsafe { Stack::wrap(l, 1) }; let name: &str = FromParam::from_param(&stack); let value = f64::from_param(&stack); let res = safe_test_c_function(name, value); From 90443bd8843a8cd59858ffc315874eca8a979336 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 23:03:44 +0100 Subject: [PATCH 033/527] Added initial version of run_code --- src/ffi/laux.rs | 8 +++--- src/ffi/lua.rs | 7 +++-- src/lib.rs | 1 - src/vm/core.rs | 42 +++++++++++++++++++++++++++- src/vm/error.rs | 56 +++++++++++++++++++++++++++++++++++++ src/vm/function.rs | 2 +- src/vm/mod.rs | 4 +++ src/{ => vm}/util.rs | 28 ++++++++++++++++++- src/vm/value.rs | 49 +++++++++++--------------------- tests/test_vm_destructor.rs | 20 ++++--------- 10 files changed, 160 insertions(+), 57 deletions(-) create mode 100644 src/vm/error.rs rename src/{ => vm}/util.rs (77%) diff --git a/src/ffi/laux.rs b/src/ffi/laux.rs index 3c12d7f..2658aa9 100644 --- a/src/ffi/laux.rs +++ b/src/ffi/laux.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::ffi::{c_char, c_int, c_void}; -use crate::ffi::lua::{Integer, Number, State, Type}; +use crate::ffi::lua::{Integer, Number, State, ThreadStatus, Type}; //-------------------- // State manipulation @@ -99,7 +99,7 @@ extern "C" { // Loading lua code //------------------ extern "C" { - pub fn luaL_loadfile(l: State, filename: *const c_char) -> c_int; - pub fn luaL_loadbuffer(l: State, buff: *const c_char, sz: usize, name: *const c_char) -> c_int; - pub fn luaL_loadstring(l: State, s: *const c_char) -> c_int; + pub fn luaL_loadfile(l: State, filename: *const c_char) -> ThreadStatus; + pub fn luaL_loadbuffer(l: State, buff: *const c_char, sz: usize, name: *const c_char) -> ThreadStatus; + pub fn luaL_loadstring(l: State, s: *const c_char) -> ThreadStatus; } diff --git a/src/ffi/lua.rs b/src/ffi/lua.rs index b0cf418..54fdbbd 100644 --- a/src/ffi/lua.rs +++ b/src/ffi/lua.rs @@ -40,6 +40,7 @@ pub const fn upvalueindex(i: c_int) -> c_int { } #[repr(i32)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum ThreadStatus { Ok = 0, Yield = 1, @@ -60,7 +61,7 @@ pub type Reader = extern "C" fn(l: State, ud: *mut c_void, sz: usize) -> *const pub type Writer = extern "C" fn(l: State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; #[repr(i32)] -#[derive(Clone, Copy, Debug)] +#[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Type { None = -1, Nil = 0, @@ -176,9 +177,9 @@ extern "C" { //----------------------------------------------------- extern "C" { pub fn lua_call(l: State, nargs: c_int, nresults: c_int); - pub fn lua_pcall(l: State, nargs: c_int, nresults: c_int, errfunc: c_int) -> c_int; + pub fn lua_pcall(l: State, nargs: c_int, nresults: c_int, errfunc: c_int) -> ThreadStatus; pub fn lua_cpcall(l: State, func: CFunction, ud: *mut c_void) -> c_int; - pub fn lua_load(l: State, reader: Reader, dt: *mut c_void, chunkname: *const c_char) -> c_int; + pub fn lua_load(l: State, reader: Reader, dt: *mut c_void, chunkname: *const c_char) -> ThreadStatus; pub fn lua_dump(l: State, writer: Writer, data: *mut c_void) -> c_int; } diff --git a/src/lib.rs b/src/lib.rs index f046e5b..a7ac84a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,7 +28,6 @@ pub mod ffi; pub mod vm; -pub mod util; macro_rules! declare_lib { ( diff --git a/src/vm/core.rs b/src/vm/core.rs index 66b5f28..509535a 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -28,7 +28,10 @@ use std::cell::Cell; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; -use crate::ffi::lua::State; +use crate::ffi::lua::{lua_close, lua_pcall, State, ThreadStatus}; +use crate::vm::error::Error; +use crate::vm::util::LoadCode; +use crate::vm::value::FromLua; pub struct Stack { l: State, @@ -83,4 +86,41 @@ impl Vm { pub fn as_ptr(&self) -> State { self.l } + + pub fn run_code<'a, R: FromLua<'a>>(&'a mut self, code: impl LoadCode) -> crate::vm::Result { + let l = self.as_ptr(); + let res = code.load_code(l); + match res { + ThreadStatus::Ok => (), + ThreadStatus::ErrSyntax => { + let str: &str = FromLua::from_lua(self, -1)?; + return Err(Error::Syntax(str.into())) + } + ThreadStatus::ErrMem => return Err(Error::Memory), + _ => return Err(Error::Unknown) + }; + unsafe { + let res = lua_pcall(l, 0, R::num_values() as _, 0); + match res { + ThreadStatus::Ok => (), + ThreadStatus::ErrRun => { + let str: &str = FromLua::from_lua(self, -1)?; + return Err(Error::Runtime(str.into())) + } + ThreadStatus::ErrMem => return Err(Error::Memory), + ThreadStatus::ErrErr => return Err(Error::Error), + _ => return Err(Error::Unknown) + }; + } + FromLua::from_lua(self, -1) + } +} + +impl Drop for Vm { + fn drop(&mut self) { + unsafe { + println!("Closing Lua VM..."); + lua_close(self.l); + } + } } diff --git a/src/vm/error.rs b/src/vm/error.rs new file mode 100644 index 0000000..767217a --- /dev/null +++ b/src/vm/error.rs @@ -0,0 +1,56 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Display, Formatter}; +use std::str::Utf8Error; +use bp3d_util::simple_error; +use crate::ffi::lua::Type; + +#[derive(Debug, Copy, Clone)] +pub struct TypeError { + pub expected: Type, + pub actual: Type +} + +impl Display for TypeError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "expected {:?}, got {:?}", self.expected, self.actual) + } +} + +simple_error! { + pub Error { + InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", + TypeError(TypeError) => "type error: {}", + Syntax(String) => "syntax error: {}", + Runtime(String) => "runtime error: {}", + Memory => "memory allocation error", + Unknown => "unknown error", + Error => "error in error handler" + } +} diff --git a/src/vm/function.rs b/src/vm/function.rs index 7fba337..96cfc3a 100644 --- a/src/vm/function.rs +++ b/src/vm/function.rs @@ -29,7 +29,7 @@ use std::slice; use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnumber}; -use crate::util::{lua_rust_error, SimpleDrop}; +use crate::vm::util::{lua_rust_error, SimpleDrop}; use crate::vm::Stack; /// This trait represents a function return value. diff --git a/src/vm/mod.rs b/src/vm/mod.rs index 99ca607..a3cd3e4 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -29,5 +29,9 @@ mod core; pub mod function; pub mod value; +pub mod error; +mod util; pub use core::*; + +pub type Result = std::result::Result; diff --git a/src/util.rs b/src/vm/util.rs similarity index 77% rename from src/util.rs rename to src/vm/util.rs index 6b76230..50d1936 100644 --- a/src/util.rs +++ b/src/vm/util.rs @@ -27,7 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::error::Error; -use crate::ffi::lua::{lua_error, lua_pushlstring, State}; +use std::ffi::{CStr, CString}; +use crate::ffi::laux::luaL_loadstring; +use crate::ffi::lua::{lua_error, lua_pushlstring, State, ThreadStatus}; pub unsafe trait SimpleDrop {} @@ -46,3 +48,27 @@ pub unsafe fn lua_rust_error(l: State, error: E) -> ! { // If this is reached, then lua_error has silently failed. std::unreachable!() } + +pub trait LoadCode { + fn load_code(&self, l: State) -> ThreadStatus; +} + +impl LoadCode for &CStr { + fn load_code(&self, l: State) -> ThreadStatus { + unsafe { + luaL_loadstring(l, self.as_ptr()) + } + } +} + +impl LoadCode for &str { + fn load_code(&self, l: State) -> ThreadStatus { + let s = CString::new(*self); + match s { + Ok(v) => { + (&*v).load_code(l) + } + Err(_) => ThreadStatus::ErrSyntax + } + } +} diff --git a/src/vm/value.rs b/src/vm/value.rs index 2b73435..292b18b 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -26,33 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Display, Formatter}; -use std::str::Utf8Error; -use bp3d_util::simple_error; use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean}; use crate::vm::function::IntoParam; use crate::vm::{Stack, Vm}; +use crate::vm::error::{Error, TypeError}; -#[derive(Debug, Copy, Clone)] -pub struct TypeError { - pub expected: Type, - pub actual: Type -} - -impl Display for TypeError { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "expected {:?}, got {:?}", self.expected, self.actual) - } -} - -simple_error! { - pub Error { - InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", - TypeError(TypeError) => "type error: {}" - } -} - -pub trait FromLua: Sized { +pub trait FromLua<'a>: Sized { /// Attempt to read the value at the specified index in the given [Vm]. /// /// # Arguments @@ -61,22 +40,29 @@ pub trait FromLua: Sized { /// * `index`: the index at which to try reading the value from. /// /// returns: Result - fn from_lua(vm: &Vm, index: i32) -> Result; + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result; + + /// Returns the number of values to be expected on the lua stack, after reading this value. + fn num_values() -> u16 { + 1 + } } pub trait IntoLua: Sized { /// Attempt to push self onto the top of the stack in the given [Vm]. /// + /// Returns the number values pushed into the lua stack. + /// /// # Arguments /// /// * `vm`: the [Vm] to push into. /// /// returns: Result - fn into_lua(self, vm: &Vm) -> Result<(), Error>; + fn into_lua(self, vm: &Vm) -> crate::vm::Result; } -impl FromLua for &str { - fn from_lua(vm: &Vm, index: i32) -> Result { +impl<'a> FromLua<'a> for &'a str { + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { let l = vm.as_ptr(); unsafe { let ty = lua_type(l, index); @@ -98,8 +84,8 @@ impl FromLua for &str { macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { - impl FromLua for $t { - fn from_lua(vm: &Vm, index: i32) -> Result { + impl FromLua<'static> for $t { + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { let l = vm.as_ptr(); unsafe { let ty = lua_type(l, index); @@ -135,9 +121,8 @@ impl_from_lua!(f64, Number, lua_tonumber, as _); impl_from_lua!(bool, Boolean, lua_toboolean, == 1); impl IntoLua for T { - fn into_lua(self, vm: &Vm) -> Result<(), Error> { + fn into_lua(self, vm: &Vm) -> Result { let stack = unsafe { Stack::wrap(vm.as_ptr(), 0) }; - self.into_param(&stack); - Ok(()) + Ok(self.into_param(&stack)) } } diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 87d9f0f..891e24a 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -27,8 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::vm::function::{FromParam, IntoParam}; -use bp3d_lua::ffi::laux::luaL_loadstring; -use bp3d_lua::ffi::lua::{lua_isstring, lua_pcall, lua_pushcclosure, lua_setfield, lua_tolstring, lua_type, State, GLOBALSINDEX}; +use bp3d_lua::ffi::lua::{lua_pushcclosure, lua_setfield, State, GLOBALSINDEX}; use bp3d_lua::vm::{Stack, Vm}; struct ValueWithDrop; @@ -65,20 +64,13 @@ extern "C" fn test_error_handler(l: State) -> i32 { #[test] fn test_vm_destructor() { - let vm = Vm::new(); + let mut vm = Vm::new(); unsafe { lua_pushcclosure(vm.as_ptr(), test_c_function, 0); lua_setfield(vm.as_ptr(), GLOBALSINDEX, c"test_c_function".as_ptr()); - assert_eq!(luaL_loadstring(vm.as_ptr(), c"return test_c_function('this is a test\\xFF', 0.42)".as_ptr()), 0); - let i = lua_pcall(vm.as_ptr(), 0, 0, 0); - if i != 0 { - println!("{:?}", lua_type(vm.as_ptr(), -1)); - if lua_isstring(vm.as_ptr(), -1) == 1 { - let mut len: usize = 0; - let s = lua_tolstring(vm.as_ptr(), -1, &mut len as _); - let slice = std::slice::from_raw_parts(s as *const u8, len); - println!("Error as string: {:?}", std::str::from_utf8(slice)); - } - } } + assert!(vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)").is_err()); + assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); + let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); + assert_eq!(s, "Hello this is a test (0.42)"); } From 12d2ec9fb243bbc4e1c1c7535da2381ebcbea3b1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Mar 2025 23:09:03 +0100 Subject: [PATCH 034/527] Fixed lifetime bug in FromLua --- src/vm/value.rs | 2 +- tests/test_vm_destructor.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/vm/value.rs b/src/vm/value.rs index 292b18b..21d5f0d 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -84,7 +84,7 @@ impl<'a> FromLua<'a> for &'a str { macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { - impl FromLua<'static> for $t { + impl FromLua<'_> for $t { fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { let l = vm.as_ptr(); unsafe { diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 891e24a..07313bb 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -73,4 +73,5 @@ fn test_vm_destructor() { assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); + assert!(vm.run_code::(c"return test_c_function('this is a test', 0.42)").is_err()); } From e3159592196175ba24b0979b58a9797505d2d41f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Mar 2025 20:57:38 +0100 Subject: [PATCH 035/527] Added error handling and trace back generation to run_code --- Cargo.toml | 1 + src/ffi/laux.rs | 2 ++ src/vm/core.rs | 63 +++++++++++++++++++++++++++++++++---- src/vm/error.rs | 46 ++++++++++++++++++++++++++- tests/test_vm_destructor.rs | 11 +++---- 5 files changed, 109 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 617ab81..14019a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -14,6 +14,7 @@ categories = ["graphics"] [dependencies] bp3d-util = { version = "1.4.0", features = ["simple-error"] } +log = "0.4.26" [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } diff --git a/src/ffi/laux.rs b/src/ffi/laux.rs index 2658aa9..ae6253e 100644 --- a/src/ffi/laux.rs +++ b/src/ffi/laux.rs @@ -44,6 +44,8 @@ extern "C" { pub fn luaL_error(l: State, fmt: *const c_char, ...) -> c_int; pub fn luaL_typerror(l: State, narg: c_int, tname: *const c_char) -> c_int; pub fn luaL_argerror(l: State, numarg: c_int, extramsg: *const c_char) -> c_int; + + pub fn luaL_traceback(l: State, l1: State, msg: *const c_char, level: c_int); } //--------------- diff --git a/src/vm/core.rs b/src/vm/core.rs index 509535a..6eefe73 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -27,9 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::cell::Cell; -use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; -use crate::ffi::lua::{lua_close, lua_pcall, State, ThreadStatus}; -use crate::vm::error::Error; +use std::ffi::c_int; +use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; +use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_tolstring, lua_type, State, ThreadStatus, Type}; +use crate::vm::error::{Error, RuntimeError}; use crate::vm::util::LoadCode; use crate::vm::value::FromLua; @@ -70,6 +71,36 @@ impl Stack { } } +const TRACEBACK_NONE: &[u8] = b"\n"; +extern "C-unwind" fn error_handler(l: State) -> c_int { + unsafe { + let ty = lua_type(l, 1); + if ty != Type::String { + // Non-string error object? Try metamethod. + if (ty == Type::Nil || ty == Type::None) || + luaL_callmeta(l, 1, c"__tostring".as_ptr()) != 1 || + lua_isstring(l, -1) != 1 { + // Object does not turn into a string remove it alongside the return value of + // __tostring. + lua_remove(l, 1); + lua_remove(l, 1); + // Push a place-holder string to avoid the rust code from crashing because the stack + // would be empty otherwise. + lua_pushlstring(l, TRACEBACK_NONE.as_ptr() as _, TRACEBACK_NONE.len()); + return 1; + } + // Remove the object from the stack so that error message becomes now index 1. + lua_remove(l, 1); + } + // Call traceback with the actual error message as a string which should push onto the stack + // the stacktrace as a string. + luaL_traceback(l, l, lua_tolstring(l, 1, std::ptr::null_mut()), 1); + // Remove the original error message string from the stack. + lua_remove(l, 1); + 1 + } +} + pub struct Vm { l: State } @@ -89,29 +120,49 @@ impl Vm { pub fn run_code<'a, R: FromLua<'a>>(&'a mut self, code: impl LoadCode) -> crate::vm::Result { let l = self.as_ptr(); + // Push error handler and the get the stack position of it. + let handler_pos = unsafe { + lua_pushcclosure(l, error_handler, 0); + lua_gettop(l) + }; + // Push the lua code. let res = code.load_code(l); + if res != ThreadStatus::Ok { + unsafe { lua_remove(l, handler_pos) }; + } match res { ThreadStatus::Ok => (), ThreadStatus::ErrSyntax => { + // If we've got an error, read it and clear the stack. let str: &str = FromLua::from_lua(self, -1)?; + unsafe { lua_remove(l, -1) }; return Err(Error::Syntax(str.into())) } ThreadStatus::ErrMem => return Err(Error::Memory), _ => return Err(Error::Unknown) }; unsafe { - let res = lua_pcall(l, 0, R::num_values() as _, 0); + // Call the function created by load_code. + let res = lua_pcall(l, 0, R::num_values() as _, handler_pos); + // At this point the stack should no longer have the function but still has the error + // handler and R::num_values results. + // First remove error handler as we no longer need it. + lua_remove(l, handler_pos); match res { ThreadStatus::Ok => (), ThreadStatus::ErrRun => { - let str: &str = FromLua::from_lua(self, -1)?; - return Err(Error::Runtime(str.into())) + // We've got a runtime error when executing the function so read the full stack + // trace produced by luaL_traceback and remove it from the stack. + let full_traceback: &str = FromLua::from_lua(self, -1)?; + lua_remove(l, -1); + return Err(Error::Runtime(RuntimeError::new(full_traceback.into()))); } ThreadStatus::ErrMem => return Err(Error::Memory), ThreadStatus::ErrErr => return Err(Error::Error), _ => return Err(Error::Unknown) }; } + // Read and return the result of the function from the stack. FromLua::from_lua(self, -1) } } diff --git a/src/vm/error.rs b/src/vm/error.rs index 767217a..aea4292 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -29,6 +29,7 @@ use std::fmt::{Display, Formatter}; use std::str::Utf8Error; use bp3d_util::simple_error; +use log::trace; use crate::ffi::lua::Type; #[derive(Debug, Copy, Clone)] @@ -43,14 +44,57 @@ impl Display for TypeError { } } +#[derive(Debug, Clone)] +pub struct RuntimeError { + traceback: String, + index: usize +} + +impl RuntimeError { + pub fn new(traceback: String) -> Self { + let id = traceback.find('\n').unwrap(); + Self { + traceback, + index: id + } + } + + pub fn msg(&self) -> &str { + &self.traceback[..self.index] + } + + pub fn stacktrace(&self) -> &str { + &self.traceback[self.index + 1..] + } + + pub fn traceback(&self) -> &str { + &self.traceback + } +} + +impl Display for RuntimeError { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(self.msg()) + } +} + simple_error! { pub Error { InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", TypeError(TypeError) => "type error: {}", Syntax(String) => "syntax error: {}", - Runtime(String) => "runtime error: {}", + Runtime(RuntimeError) => "runtime error: {}", Memory => "memory allocation error", Unknown => "unknown error", Error => "error in error handler" } } + +impl Error { + pub fn into_runtime(self) -> RuntimeError { + match self { + Error::Runtime(e) => e, + _ => panic!("error is not a runtime error") + } + } +} diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 07313bb..096804c 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -56,12 +56,6 @@ extern "C-unwind" fn test_c_function(l: State) -> i32 { res.into_param(&stack) as _ } -extern "C" fn test_error_handler(l: State) -> i32 { - println!("An error has occured from lua VM"); - //luaL_traceback(L, L, lua_tostring(L, 1), 1); - 1 -} - #[test] fn test_vm_destructor() { let mut vm = Vm::new(); @@ -69,7 +63,10 @@ fn test_vm_destructor() { lua_pushcclosure(vm.as_ptr(), test_c_function, 0); lua_setfield(vm.as_ptr(), GLOBALSINDEX, c"test_c_function".as_ptr()); } - assert!(vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)").is_err()); + let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); + assert!(res.is_err()); + let err = res.unwrap_err().into_runtime(); + assert_eq!(err.msg(), "rust error: invalid utf-8 sequence of 1 bytes from index 14"); assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); From b58776642ca1952f64d0b78ea318cf708795e865 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Mar 2025 22:09:04 +0100 Subject: [PATCH 036/527] Added RFunction and set_global functions --- src/vm/core.rs | 16 +++++++++++++--- src/vm/error.rs | 3 ++- src/vm/value.rs | 14 +++++++++++++- tests/test_vm_destructor.rs | 11 ++++++----- 4 files changed, 34 insertions(+), 10 deletions(-) diff --git a/src/vm/core.rs b/src/vm/core.rs index 6eefe73..cbf8422 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -26,13 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Borrow; use std::cell::Cell; -use std::ffi::c_int; +use std::ffi::{c_int, CString}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_tolstring, lua_type, State, ThreadStatus, Type}; +use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::util::LoadCode; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; pub struct Stack { l: State, @@ -118,6 +119,15 @@ impl Vm { self.l } + pub fn set_global(&mut self, name: impl Borrow, value: impl IntoLua) -> crate::vm::Result<()> { + value.into_lua(self)?; + let cstr = CString::new(name.borrow()).map_err(|_| Error::Null)?; + unsafe { + lua_setfield(self.as_ptr(), GLOBALSINDEX, cstr.as_ptr()); + } + Ok(()) + } + pub fn run_code<'a, R: FromLua<'a>>(&'a mut self, code: impl LoadCode) -> crate::vm::Result { let l = self.as_ptr(); // Push error handler and the get the stack position of it. diff --git a/src/vm/error.rs b/src/vm/error.rs index aea4292..a92fdbf 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -86,7 +86,8 @@ simple_error! { Runtime(RuntimeError) => "runtime error: {}", Memory => "memory allocation error", Unknown => "unknown error", - Error => "error in error handler" + Error => "error in error handler", + Null => "string contains a null character" } } diff --git a/src/vm/value.rs b/src/vm/value.rs index 21d5f0d..ad1a6f2 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean}; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, CFunction, lua_pushcclosure}; use crate::vm::function::IntoParam; use crate::vm::{Stack, Vm}; use crate::vm::error::{Error, TypeError}; @@ -126,3 +126,15 @@ impl IntoLua for T { Ok(self.into_param(&stack)) } } + +pub struct RFunction(pub CFunction); + +impl IntoLua for RFunction { + fn into_lua(self, vm: &Vm) -> crate::vm::Result { + let l = vm.as_ptr(); + unsafe { + lua_pushcclosure(l, self.0, 0); + } + Ok(1) + } +} diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 096804c..4ddac7d 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -27,8 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::vm::function::{FromParam, IntoParam}; -use bp3d_lua::ffi::lua::{lua_pushcclosure, lua_setfield, State, GLOBALSINDEX}; +use bp3d_lua::ffi::lua::State; use bp3d_lua::vm::{Stack, Vm}; +use bp3d_lua::vm::value::RFunction; struct ValueWithDrop; impl ValueWithDrop { @@ -59,10 +60,8 @@ extern "C-unwind" fn test_c_function(l: State) -> i32 { #[test] fn test_vm_destructor() { let mut vm = Vm::new(); - unsafe { - lua_pushcclosure(vm.as_ptr(), test_c_function, 0); - lua_setfield(vm.as_ptr(), GLOBALSINDEX, c"test_c_function".as_ptr()); - } + vm.set_global("test_c_function", RFunction(test_c_function)).unwrap(); + let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); let err = res.unwrap_err().into_runtime(); @@ -71,4 +70,6 @@ fn test_vm_destructor() { let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); assert!(vm.run_code::(c"return test_c_function('this is a test', 0.42)").is_err()); + let time = time.elapsed(); + println!("time: {:?}", time); } From 66085173b63e61c00875605648cca462859123a6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Mar 2025 22:28:29 +0100 Subject: [PATCH 037/527] Added initial version of decl_lib_func --- src/lib.rs | 29 +----------------------- src/macros/lib_func.rs | 44 +++++++++++++++++++++++++++++++++++++ src/macros/mod.rs | 29 ++++++++++++++++++++++++ src/vm/function.rs | 44 +++++++++++++++++++++---------------- tests/test_vm_destructor.rs | 23 +++++++------------ 5 files changed, 107 insertions(+), 62 deletions(-) create mode 100644 src/macros/lib_func.rs create mode 100644 src/macros/mod.rs diff --git a/src/lib.rs b/src/lib.rs index a7ac84a..c9cd540 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -28,31 +28,4 @@ pub mod ffi; pub mod vm; - -macro_rules! declare_lib { - ( - pub lib $($namespace: ident)*.$name: ident { - $( - pub fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block - )* - } - ) => { - const LIB_NAME: &str = stringify!($($namespace.)*$name); - fn register_lib() { - $( - { - const FN_NAME: &str = stringify!($fn_name); - let args = &[$(std::any::TypeId::of::<$arg_ty>()),*]; - } - )* - } - }; -} - -declare_lib! { - pub lib bp3d.math { - pub fn test(a: f32, b: f32) -> f32 { - - } - } -} +mod macros; diff --git a/src/macros/lib_func.rs b/src/macros/lib_func.rs new file mode 100644 index 0000000..8fdf6a4 --- /dev/null +++ b/src/macros/lib_func.rs @@ -0,0 +1,44 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! decl_lib_func { + ( + fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + ) => { + pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { + fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code + use $crate::vm::function::FromParam; + use $crate::vm::function::IntoParam; + let stack = unsafe { $crate::vm::Stack::wrap(l, 1) }; + $(let $arg_name: $arg_ty = unsafe { FromParam::from_param(&stack) };)* + let ret = _func($($arg_name),*); + ret.into_param(&stack) as _ + } + } +} diff --git a/src/macros/mod.rs b/src/macros/mod.rs new file mode 100644 index 0000000..d5dd9c2 --- /dev/null +++ b/src/macros/mod.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod lib_func; diff --git a/src/vm/function.rs b/src/vm/function.rs index 96cfc3a..de57b63 100644 --- a/src/vm/function.rs +++ b/src/vm/function.rs @@ -48,20 +48,30 @@ pub trait IntoParam: Sized + SimpleDrop { /// This trait represents a function parameter. pub trait FromParam: Sized + SimpleDrop { - fn from_param(stack: &Stack) -> Self; + /// Reads this value from the given lua stack. + /// + /// # Arguments + /// + /// * `stack`: the stack to read from. + /// + /// returns: Self + /// + /// # Safety + /// + /// Calling this function outside the body of a CFunction is UB. Calling this function in a + /// non-POF segment of that CFunction is also UB. + unsafe fn from_param(stack: &Stack) -> Self; } impl FromParam for &str { - fn from_param(stack: &Stack) -> Self { - unsafe { - let mut len: usize = 0; - let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); - let slice = slice::from_raw_parts(str as *const u8, len); - match std::str::from_utf8(slice){ - Ok(v) => v, - Err(e) => { - lua_rust_error(stack.as_ptr(), e); - } + unsafe fn from_param(stack: &Stack) -> Self { + let mut len: usize = 0; + let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); + let slice = slice::from_raw_parts(str as *const u8, len); + match std::str::from_utf8(slice){ + Ok(v) => v, + Err(e) => { + lua_rust_error(stack.as_ptr(), e); } } } @@ -80,10 +90,8 @@ macro_rules! impl_integer { ($($t: ty),*) => { $( impl FromParam for $t { - fn from_param(stack: &Stack) -> Self { - unsafe { - luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ - } + unsafe fn from_param(stack: &Stack) -> Self { + luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ } } @@ -108,10 +116,8 @@ macro_rules! impl_float { ($($t: ty),*) => { $( impl FromParam for $t { - fn from_param(stack: &Stack) -> Self { - unsafe { - luaL_checknumber(stack.as_ptr(), stack.pop()) as _ - } + unsafe fn from_param(stack: &Stack) -> Self { + luaL_checknumber(stack.as_ptr(), stack.pop()) as _ } } diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index 4ddac7d..a4e3ef3 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -26,9 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::function::{FromParam, IntoParam}; -use bp3d_lua::ffi::lua::State; -use bp3d_lua::vm::{Stack, Vm}; +use bp3d_lua::decl_lib_func; +use bp3d_lua::vm::Vm; use bp3d_lua::vm::value::RFunction; struct ValueWithDrop; @@ -43,18 +42,12 @@ impl Drop for ValueWithDrop { } } -fn safe_test_c_function(name: &str, value: f64) -> String { - let drop = ValueWithDrop; - drop.print(); - format!("Hello {} ({})", name, value) -} - -extern "C-unwind" fn test_c_function(l: State) -> i32 { - let stack = unsafe { Stack::wrap(l, 1) }; - let name: &str = FromParam::from_param(&stack); - let value = f64::from_param(&stack); - let res = safe_test_c_function(name, value); - res.into_param(&stack) as _ +decl_lib_func! { + fn test_c_function(name: &str, value: f64) -> String { + let drop = ValueWithDrop; + drop.print(); + format!("Hello {} ({})", name, value) + } } #[test] From 870d18a82128761e4f9ca79409e34261a65266be Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Mar 2025 23:36:17 +0100 Subject: [PATCH 038/527] Added initial support for reading tables in C functions --- src/vm/core.rs | 53 +++++++++++----- src/vm/error.rs | 1 - src/vm/{function.rs => function/core.rs} | 80 +++++++++++++---------- src/vm/function/interface.rs | 61 ++++++++++++++++++ src/vm/function/mod.rs | 34 ++++++++++ src/vm/function/table.rs | 81 ++++++++++++++++++++++++ src/vm/util.rs | 31 +++++++++ src/vm/value.rs | 30 ++++----- tests/test_vm_destructor.rs | 2 +- 9 files changed, 304 insertions(+), 69 deletions(-) rename src/vm/{function.rs => function/core.rs} (73%) create mode 100644 src/vm/function/interface.rs create mode 100644 src/vm/function/mod.rs create mode 100644 src/vm/function/table.rs diff --git a/src/vm/core.rs b/src/vm/core.rs index cbf8422..ad28fe0 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -26,17 +26,27 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Borrow; use std::cell::Cell; -use std::ffi::{c_int, CString}; +use std::ffi::c_int; +use std::ops::Deref; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; -use crate::vm::util::LoadCode; +use crate::vm::util::{AnyStr, LoadCode}; use crate::vm::value::{FromLua, IntoLua}; +pub struct LuaState(State); + +impl Deref for LuaState { + type Target = State; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + pub struct Stack { - l: State, + l: LuaState, index: Cell } @@ -56,13 +66,17 @@ impl Stack { /// context is UB. pub unsafe fn wrap(l: State, start: i32) -> Stack { Stack { - l, + l: LuaState(l), index: Cell::new(start) } } + pub fn as_state(&self) -> &LuaState { + &self.l + } + pub fn as_ptr(&self) -> State { - self.l + *self.l } pub fn pop(&self) -> i32 { @@ -103,7 +117,7 @@ extern "C-unwind" fn error_handler(l: State) -> c_int { } pub struct Vm { - l: State + l: LuaState } impl Vm { @@ -111,19 +125,24 @@ impl Vm { let l = unsafe { luaL_newstate() }; unsafe { luaL_openlibs(l) }; Vm { - l + l: LuaState(l) + } + } + + pub unsafe fn from_raw(l: State) -> Self { + Self { + l: LuaState(l) } } pub fn as_ptr(&self) -> State { - self.l + *self.l } - pub fn set_global(&mut self, name: impl Borrow, value: impl IntoLua) -> crate::vm::Result<()> { - value.into_lua(self)?; - let cstr = CString::new(name.borrow()).map_err(|_| Error::Null)?; + pub fn set_global(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { + value.into_lua(&self.l)?; unsafe { - lua_setfield(self.as_ptr(), GLOBALSINDEX, cstr.as_ptr()); + lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } Ok(()) } @@ -144,7 +163,7 @@ impl Vm { ThreadStatus::Ok => (), ThreadStatus::ErrSyntax => { // If we've got an error, read it and clear the stack. - let str: &str = FromLua::from_lua(self, -1)?; + let str: &str = FromLua::from_lua(&self.l, -1)?; unsafe { lua_remove(l, -1) }; return Err(Error::Syntax(str.into())) } @@ -163,7 +182,7 @@ impl Vm { ThreadStatus::ErrRun => { // We've got a runtime error when executing the function so read the full stack // trace produced by luaL_traceback and remove it from the stack. - let full_traceback: &str = FromLua::from_lua(self, -1)?; + let full_traceback: &str = FromLua::from_lua(&self.l, -1)?; lua_remove(l, -1); return Err(Error::Runtime(RuntimeError::new(full_traceback.into()))); } @@ -173,7 +192,7 @@ impl Vm { }; } // Read and return the result of the function from the stack. - FromLua::from_lua(self, -1) + FromLua::from_lua(&self.l, -1) } } @@ -181,7 +200,7 @@ impl Drop for Vm { fn drop(&mut self) { unsafe { println!("Closing Lua VM..."); - lua_close(self.l); + lua_close(*self.l); } } } diff --git a/src/vm/error.rs b/src/vm/error.rs index a92fdbf..d220442 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -29,7 +29,6 @@ use std::fmt::{Display, Formatter}; use std::str::Utf8Error; use bp3d_util::simple_error; -use log::trace; use crate::ffi::lua::Type; #[derive(Debug, Copy, Clone)] diff --git a/src/vm/function.rs b/src/vm/function/core.rs similarity index 73% rename from src/vm/function.rs rename to src/vm/function/core.rs index de57b63..ef52efc 100644 --- a/src/vm/function.rs +++ b/src/vm/function/core.rs @@ -26,45 +26,28 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::error::Error; use std::slice; use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; -use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnumber}; -use crate::vm::util::{lua_rust_error, SimpleDrop}; +use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Type}; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::util::{lua_rust_error}; use crate::vm::Stack; -/// This trait represents a function return value. -pub trait IntoParam: Sized + SimpleDrop { - /// Turns self into a function return parameter. - /// - /// This function returns the number of parameters pushed onto the lua stack. - /// - /// # Arguments - /// - /// * `stack`: the stack to push this value to. - /// - /// returns: u16 - fn into_param(self, stack: &Stack) -> u16; -} - -/// This trait represents a function parameter. -pub trait FromParam: Sized + SimpleDrop { - /// Reads this value from the given lua stack. - /// - /// # Arguments - /// - /// * `stack`: the stack to read from. - /// - /// returns: Self - /// - /// # Safety - /// - /// Calling this function outside the body of a CFunction is UB. Calling this function in a - /// non-POF segment of that CFunction is also UB. - unsafe fn from_param(stack: &Stack) -> Self; +impl<'a, T: FromParam<'a> + Copy> FromParam<'a> for Option { + unsafe fn from_param(stack: &'a Stack) -> Self { + let l = stack.as_ptr(); + let ty = lua_type(l, stack.pop()); + if ty == Type::Nil || ty == Type::None { + None + } else { + Some(T::from_param(stack)) + } + } } -impl FromParam for &str { - unsafe fn from_param(stack: &Stack) -> Self { +impl<'a> FromParam<'a> for &'a str { + unsafe fn from_param(stack: &'a Stack) -> Self { let mut len: usize = 0; let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); let slice = slice::from_raw_parts(str as *const u8, len); @@ -89,7 +72,7 @@ impl IntoParam for &str { macro_rules! impl_integer { ($($t: ty),*) => { $( - impl FromParam for $t { + impl FromParam<'_> for $t { unsafe fn from_param(stack: &Stack) -> Self { luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ } @@ -115,7 +98,7 @@ impl_integer!(i8, u8, i16, u16, i32, u32); macro_rules! impl_float { ($($t: ty),*) => { $( - impl FromParam for $t { + impl FromParam<'_> for $t { unsafe fn from_param(stack: &Stack) -> Self { luaL_checknumber(stack.as_ptr(), stack.pop()) as _ } @@ -141,3 +124,30 @@ impl IntoParam for bool { 1 } } + +impl IntoParam for Result { + fn into_param(self, stack: &Stack) -> u16 { + match self { + Ok(v) => v.into_param(stack), + Err(e) => { + unsafe { + lua_rust_error(stack.as_ptr(), e); + } + } + } + } +} + +impl IntoParam for Option { + fn into_param(self, stack: &Stack) -> u16 { + match self { + None => { + unsafe { + lua_pushnil(stack.as_ptr()); + 1 + } + } + Some(v) => v.into_param(stack) + } + } +} diff --git a/src/vm/function/interface.rs b/src/vm/function/interface.rs new file mode 100644 index 0000000..43c7acc --- /dev/null +++ b/src/vm/function/interface.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::Stack; +use crate::vm::util::SimpleDrop; + +/// This trait represents a function return value. +pub trait IntoParam: Sized { + /// Turns self into a function return parameter. + /// + /// This function returns the number of parameters pushed onto the lua stack. + /// + /// # Arguments + /// + /// * `stack`: the stack to push this value to. + /// + /// returns: u16 + fn into_param(self, stack: &Stack) -> u16; +} + +/// This trait represents a function parameter. +pub trait FromParam<'a>: Sized + SimpleDrop { + /// Reads this value from the given lua stack. + /// + /// # Arguments + /// + /// * `stack`: the stack to read from. + /// + /// returns: Self + /// + /// # Safety + /// + /// Calling this function outside the body of a CFunction is UB. Calling this function in a + /// non-POF segment of that CFunction is also UB. + unsafe fn from_param(stack: &'a Stack) -> Self; +} diff --git a/src/vm/function/mod.rs b/src/vm/function/mod.rs new file mode 100644 index 0000000..938f7bb --- /dev/null +++ b/src/vm/function/mod.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod table; +mod interface; +mod core; + +pub use interface::*; +pub use table::Table; diff --git a/src/vm/function/table.rs b/src/vm/function/table.rs new file mode 100644 index 0000000..9667257 --- /dev/null +++ b/src/vm/function/table.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::laux::luaL_checktype; +use crate::ffi::lua::{lua_getfield, lua_setfield, lua_settop, Type}; +use crate::vm::function::FromParam; +use crate::vm::{LuaState, Stack}; +use crate::vm::util::AnyStr; +use crate::vm::value::{FromLua, IntoLua}; + +#[derive(Copy, Clone)] +pub struct Table<'a> { + vm: &'a LuaState, + index: i32 +} + +impl<'a> Table<'a> { + pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm)?; + if nums > 1 { + // Clear the stack. + lua_settop(**self.vm, -(nums as i32)-1); + //FIXME: Better error type + return Err(crate::vm::error::Error::Unknown) + } + lua_setfield(**self.vm, self.index, name.to_str()?.as_ptr()); + } + Ok(()) + } + + pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { + if T::num_values() > 1 { + //FIXME: Better error type + return Err(crate::vm::error::Error::Unknown) + } + unsafe { + lua_getfield(**self.vm, self.index, name.to_str()?.as_ptr()); + let ret = T::from_lua(self.vm, -1); + // Clear the value returned by lua_getfield. + lua_settop(**self.vm, -2); + ret + } + } +} + +impl<'a> FromParam<'a> for Table<'a> { + unsafe fn from_param(stack: &'a Stack) -> Self { + let index = stack.pop(); + luaL_checktype(stack.as_ptr(), index, Type::Table); + Table { + vm: stack.as_state(), + index + } + } +} diff --git a/src/vm/util.rs b/src/vm/util.rs index 50d1936..2038b91 100644 --- a/src/vm/util.rs +++ b/src/vm/util.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Cow; use std::error::Error; use std::ffi::{CStr, CString}; use crate::ffi::laux::luaL_loadstring; @@ -72,3 +73,33 @@ impl LoadCode for &str { } } } + +pub trait AnyStr { + fn to_str(&self) -> crate::vm::Result>; +} + +impl AnyStr for String { + fn to_str(&self) -> crate::vm::Result> { + let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; + Ok(Cow::Owned(cstr)) + } +} + +impl AnyStr for &str { + fn to_str(&self) -> crate::vm::Result> { + let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; + Ok(Cow::Owned(cstr)) + } +} + +impl AnyStr for CString { + fn to_str(&self) -> crate::vm::Result> { + Ok(Cow::Borrowed(&**self)) + } +} + +impl AnyStr for &CStr { + fn to_str(&self) -> crate::vm::Result> { + Ok(Cow::Borrowed(&**self)) + } +} diff --git a/src/vm/value.rs b/src/vm/value.rs index ad1a6f2..0e85bf3 100644 --- a/src/vm/value.rs +++ b/src/vm/value.rs @@ -28,19 +28,19 @@ use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, CFunction, lua_pushcclosure}; use crate::vm::function::IntoParam; -use crate::vm::{Stack, Vm}; +use crate::vm::{LuaState, Stack}; use crate::vm::error::{Error, TypeError}; pub trait FromLua<'a>: Sized { - /// Attempt to read the value at the specified index in the given [Vm]. + /// Attempt to read the value at the specified index in the given [LuaState]. /// /// # Arguments /// - /// * `vm`: the [Vm] to read from. + /// * `vm`: the [LuaState] to read from. /// * `index`: the index at which to try reading the value from. /// /// returns: Result - fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result; + fn from_lua(vm: &'a LuaState, index: i32) -> crate::vm::Result; /// Returns the number of values to be expected on the lua stack, after reading this value. fn num_values() -> u16 { @@ -49,21 +49,21 @@ pub trait FromLua<'a>: Sized { } pub trait IntoLua: Sized { - /// Attempt to push self onto the top of the stack in the given [Vm]. + /// Attempt to push self onto the top of the stack in the given [LuaState]. /// /// Returns the number values pushed into the lua stack. /// /// # Arguments /// - /// * `vm`: the [Vm] to push into. + /// * `vm`: the [LuaState] to push into. /// /// returns: Result - fn into_lua(self, vm: &Vm) -> crate::vm::Result; + fn into_lua(self, vm: &LuaState) -> crate::vm::Result; } impl<'a> FromLua<'a> for &'a str { - fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - let l = vm.as_ptr(); + fn from_lua(vm: &LuaState, index: i32) -> crate::vm::Result { + let l = **vm; unsafe { let ty = lua_type(l, index); match ty { @@ -85,8 +85,8 @@ impl<'a> FromLua<'a> for &'a str { macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { - fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - let l = vm.as_ptr(); + fn from_lua(vm: &LuaState, index: i32) -> crate::vm::Result { + let l = **vm; unsafe { let ty = lua_type(l, index); match ty { @@ -121,8 +121,8 @@ impl_from_lua!(f64, Number, lua_tonumber, as _); impl_from_lua!(bool, Boolean, lua_toboolean, == 1); impl IntoLua for T { - fn into_lua(self, vm: &Vm) -> Result { - let stack = unsafe { Stack::wrap(vm.as_ptr(), 0) }; + fn into_lua(self, vm: &LuaState) -> Result { + let stack = unsafe { Stack::wrap(**vm, 0) }; Ok(self.into_param(&stack)) } } @@ -130,8 +130,8 @@ impl IntoLua for T { pub struct RFunction(pub CFunction); impl IntoLua for RFunction { - fn into_lua(self, vm: &Vm) -> crate::vm::Result { - let l = vm.as_ptr(); + fn into_lua(self, vm: &LuaState) -> crate::vm::Result { + let l = **vm; unsafe { lua_pushcclosure(l, self.0, 0); } diff --git a/tests/test_vm_destructor.rs b/tests/test_vm_destructor.rs index a4e3ef3..56b80aa 100644 --- a/tests/test_vm_destructor.rs +++ b/tests/test_vm_destructor.rs @@ -53,7 +53,7 @@ decl_lib_func! { #[test] fn test_vm_destructor() { let mut vm = Vm::new(); - vm.set_global("test_c_function", RFunction(test_c_function)).unwrap(); + vm.set_global(c"test_c_function", RFunction(test_c_function)).unwrap(); let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); From 3268edf79e4d88fc4bfedb819583bdf8131d0db3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 6 Mar 2025 21:00:13 +0100 Subject: [PATCH 039/527] Refactored table engine --- src/vm/function/mod.rs | 2 - src/vm/{value.rs => value/core.rs} | 31 +-------------- src/vm/value/interface.rs | 59 +++++++++++++++++++++++++++++ src/vm/value/mod.rs | 33 ++++++++++++++++ src/vm/{function => value}/table.rs | 36 +++++++++++++++--- 5 files changed, 123 insertions(+), 38 deletions(-) rename src/vm/{value.rs => value/core.rs} (82%) create mode 100644 src/vm/value/interface.rs create mode 100644 src/vm/value/mod.rs rename src/vm/{function => value}/table.rs (78%) diff --git a/src/vm/function/mod.rs b/src/vm/function/mod.rs index 938f7bb..eb1d07f 100644 --- a/src/vm/function/mod.rs +++ b/src/vm/function/mod.rs @@ -26,9 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod table; mod interface; mod core; pub use interface::*; -pub use table::Table; diff --git a/src/vm/value.rs b/src/vm/value/core.rs similarity index 82% rename from src/vm/value.rs rename to src/vm/value/core.rs index 0e85bf3..411fc4d 100644 --- a/src/vm/value.rs +++ b/src/vm/value/core.rs @@ -30,36 +30,7 @@ use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type use crate::vm::function::IntoParam; use crate::vm::{LuaState, Stack}; use crate::vm::error::{Error, TypeError}; - -pub trait FromLua<'a>: Sized { - /// Attempt to read the value at the specified index in the given [LuaState]. - /// - /// # Arguments - /// - /// * `vm`: the [LuaState] to read from. - /// * `index`: the index at which to try reading the value from. - /// - /// returns: Result - fn from_lua(vm: &'a LuaState, index: i32) -> crate::vm::Result; - - /// Returns the number of values to be expected on the lua stack, after reading this value. - fn num_values() -> u16 { - 1 - } -} - -pub trait IntoLua: Sized { - /// Attempt to push self onto the top of the stack in the given [LuaState]. - /// - /// Returns the number values pushed into the lua stack. - /// - /// # Arguments - /// - /// * `vm`: the [LuaState] to push into. - /// - /// returns: Result - fn into_lua(self, vm: &LuaState) -> crate::vm::Result; -} +use crate::vm::value::{FromLua, IntoLua}; impl<'a> FromLua<'a> for &'a str { fn from_lua(vm: &LuaState, index: i32) -> crate::vm::Result { diff --git a/src/vm/value/interface.rs b/src/vm/value/interface.rs new file mode 100644 index 0000000..428e040 --- /dev/null +++ b/src/vm/value/interface.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::LuaState; + +pub trait FromLua<'a>: Sized { + /// Attempt to read the value at the specified index in the given [LuaState]. + /// + /// # Arguments + /// + /// * `vm`: the [LuaState] to read from. + /// * `index`: the index at which to try reading the value from. + /// + /// returns: Result + fn from_lua(vm: &'a LuaState, index: i32) -> crate::vm::Result; + + /// Returns the number of values to be expected on the lua stack, after reading this value. + fn num_values() -> u16 { + 1 + } +} + +pub trait IntoLua: Sized { + /// Attempt to push self onto the top of the stack in the given [LuaState]. + /// + /// Returns the number values pushed into the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [LuaState] to push into. + /// + /// returns: Result + fn into_lua(self, vm: &LuaState) -> crate::vm::Result; +} diff --git a/src/vm/value/mod.rs b/src/vm/value/mod.rs new file mode 100644 index 0000000..89c10cd --- /dev/null +++ b/src/vm/value/mod.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +mod core; +pub mod table; + +pub use interface::*; diff --git a/src/vm/function/table.rs b/src/vm/value/table.rs similarity index 78% rename from src/vm/function/table.rs rename to src/vm/value/table.rs index 9667257..bc8dac6 100644 --- a/src/vm/function/table.rs +++ b/src/vm/value/table.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_getfield, lua_setfield, lua_settop, Type}; +use crate::ffi::lua::{lua_getfield, lua_gettop, lua_setfield, lua_settop, Type}; use crate::vm::function::FromParam; use crate::vm::{LuaState, Stack}; use crate::vm::util::AnyStr; @@ -39,7 +39,18 @@ pub struct Table<'a> { index: i32 } -impl<'a> Table<'a> { +pub struct Scope<'a> { + vm: &'a LuaState, + index: i32, + initial_top: i32 +} + +impl<'a> Scope<'a> { + fn new(vm: &'a LuaState, index: i32) -> Self { + let initial_top = unsafe { lua_gettop(**vm) }; + Self { vm, index, initial_top } + } + pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let nums = value.into_lua(self.vm)?; @@ -61,14 +72,27 @@ impl<'a> Table<'a> { } unsafe { lua_getfield(**self.vm, self.index, name.to_str()?.as_ptr()); - let ret = T::from_lua(self.vm, -1); - // Clear the value returned by lua_getfield. - lua_settop(**self.vm, -2); - ret + T::from_lua(self.vm, -1) } } } +impl Drop for Scope<'_> { + fn drop(&mut self) { + let top = unsafe { lua_gettop(**self.vm) }; + let count = top - self.initial_top; + // Pop count values off the stack to ensure the stack is cleared after all table + // manipulations are finished. + unsafe { lua_settop(**self.vm, -count-1) }; + } +} + +impl<'a> Table<'a> { + pub fn lock(&mut self) -> Scope { + Scope::new(self.vm, self.index) + } +} + impl<'a> FromParam<'a> for Table<'a> { unsafe fn from_param(stack: &'a Stack) -> Self { let index = stack.pop(); From f85a732baa5efbdbb8c628f8373a8f51f81766b1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 7 Mar 2025 07:49:27 +0100 Subject: [PATCH 040/527] Large refactor of param system and table system --- src/macros/lib_func.rs | 27 ++++++++- src/vm/core.rs | 109 +++++++++++++---------------------- src/vm/error.rs | 2 +- src/vm/function/core.rs | 58 ++++++++++--------- src/vm/function/interface.rs | 11 ++-- src/vm/value/core.rs | 29 +++++----- src/vm/value/interface.rs | 17 +++--- src/vm/value/table.rs | 44 +++++++++----- 8 files changed, 154 insertions(+), 143 deletions(-) diff --git a/src/macros/lib_func.rs b/src/macros/lib_func.rs index 8fdf6a4..e12d3f9 100644 --- a/src/macros/lib_func.rs +++ b/src/macros/lib_func.rs @@ -35,10 +35,31 @@ macro_rules! decl_lib_func { fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; - let stack = unsafe { $crate::vm::Stack::wrap(l, 1) }; - $(let $arg_name: $arg_ty = unsafe { FromParam::from_param(&stack) };)* + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + let mut index = 1; + $( + let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; + index += 1; + )* let ret = _func($($arg_name),*); - ret.into_param(&stack) as _ + ret.into_param(&vm) as _ + } + }; + ( + fn $fn_name: ident (vm: &mut Vm, $($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + ) => { + pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { + fn _func(vm: &mut crate::vm::Vm, $($arg_name: $arg_ty),*) -> $ret_ty $code + use $crate::vm::function::FromParam; + use $crate::vm::function::IntoParam; + let mut vm = unsafe { $crate::vm::Vm::from_raw(l) }; + let mut index = 1; + $( + let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; + index += 1; + )* + let ret = _func(&mut vm, $($arg_name),*); + ret.into_param(&vm) as _ } } } diff --git a/src/vm/core.rs b/src/vm/core.rs index ad28fe0..fa64630 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -26,66 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; use std::ffi::c_int; -use std::ops::Deref; +use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::util::{AnyStr, LoadCode}; use crate::vm::value::{FromLua, IntoLua}; -pub struct LuaState(State); - -impl Deref for LuaState { - type Target = State; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -pub struct Stack { - l: LuaState, - index: Cell -} - -impl Stack { - /// Creates a new [Stack] by wrapping an existing lua VM. - /// - /// # Arguments - /// - /// * `l`: the raw lua state to wrap. - /// * `start`: the index at which to start reading values from the lua stack. - /// - /// returns: Stack - /// - /// # Safety - /// - /// This struct SHALL only exist in a [CFunction](crate::ffi::lua::CFunction). Usage in any other - /// context is UB. - pub unsafe fn wrap(l: State, start: i32) -> Stack { - Stack { - l: LuaState(l), - index: Cell::new(start) - } - } - - pub fn as_state(&self) -> &LuaState { - &self.l - } - - pub fn as_ptr(&self) -> State { - *self.l - } - - pub fn pop(&self) -> i32 { - let i = self.index.get(); - self.index.set(i + 1); - i - } -} - const TRACEBACK_NONE: &[u8] = b"\n"; extern "C-unwind" fn error_handler(l: State) -> c_int { unsafe { @@ -117,30 +65,23 @@ extern "C-unwind" fn error_handler(l: State) -> c_int { } pub struct Vm { - l: LuaState + l: State } impl Vm { - pub fn new() -> Vm { - let l = unsafe { luaL_newstate() }; - unsafe { luaL_openlibs(l) }; - Vm { - l: LuaState(l) - } - } - pub unsafe fn from_raw(l: State) -> Self { Self { - l: LuaState(l) + l } } + #[inline] pub fn as_ptr(&self) -> State { - *self.l + self.l } pub fn set_global(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { - value.into_lua(&self.l)?; + value.into_lua(self)?; unsafe { lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } @@ -163,7 +104,7 @@ impl Vm { ThreadStatus::Ok => (), ThreadStatus::ErrSyntax => { // If we've got an error, read it and clear the stack. - let str: &str = FromLua::from_lua(&self.l, -1)?; + let str: &str = FromLua::from_lua(self, -1)?; unsafe { lua_remove(l, -1) }; return Err(Error::Syntax(str.into())) } @@ -182,7 +123,7 @@ impl Vm { ThreadStatus::ErrRun => { // We've got a runtime error when executing the function so read the full stack // trace produced by luaL_traceback and remove it from the stack. - let full_traceback: &str = FromLua::from_lua(&self.l, -1)?; + let full_traceback: &str = FromLua::from_lua(self, -1)?; lua_remove(l, -1); return Err(Error::Runtime(RuntimeError::new(full_traceback.into()))); } @@ -192,15 +133,43 @@ impl Vm { }; } // Read and return the result of the function from the stack. - FromLua::from_lua(&self.l, -1) + FromLua::from_lua(self, -1) + } +} + +pub struct RootVm { + vm: Vm +} + +impl RootVm { + pub fn new() -> RootVm { + let l = unsafe { luaL_newstate() }; + unsafe { luaL_openlibs(l) }; + RootVm { + vm: unsafe { Vm::from_raw(l) } + } + } +} + +impl Deref for RootVm { + type Target = Vm; + + fn deref(&self) -> &Self::Target { + &self.vm + } +} + +impl DerefMut for RootVm { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vm } } -impl Drop for Vm { +impl Drop for RootVm { fn drop(&mut self) { unsafe { println!("Closing Lua VM..."); - lua_close(*self.l); + lua_close(self.vm.as_ptr()); } } } diff --git a/src/vm/error.rs b/src/vm/error.rs index d220442..a84ab6a 100644 --- a/src/vm/error.rs +++ b/src/vm/error.rs @@ -80,7 +80,7 @@ impl Display for RuntimeError { simple_error! { pub Error { InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", - TypeError(TypeError) => "type error: {}", + Type(TypeError) => "type error: {}", Syntax(String) => "syntax error: {}", Runtime(RuntimeError) => "runtime error: {}", Memory => "memory allocation error", diff --git a/src/vm/function/core.rs b/src/vm/function/core.rs index ef52efc..6834b0f 100644 --- a/src/vm/function/core.rs +++ b/src/vm/function/core.rs @@ -32,38 +32,38 @@ use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Type}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::util::{lua_rust_error}; -use crate::vm::Stack; +use crate::vm::Vm; impl<'a, T: FromParam<'a> + Copy> FromParam<'a> for Option { - unsafe fn from_param(stack: &'a Stack) -> Self { - let l = stack.as_ptr(); - let ty = lua_type(l, stack.pop()); + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + let l = vm.as_ptr(); + let ty = lua_type(l, index); if ty == Type::Nil || ty == Type::None { None } else { - Some(T::from_param(stack)) + Some(T::from_param(vm, index)) } } } impl<'a> FromParam<'a> for &'a str { - unsafe fn from_param(stack: &'a Stack) -> Self { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { let mut len: usize = 0; - let str = luaL_checklstring(stack.as_ptr(), stack.pop(), &mut len as _); + let str = luaL_checklstring(vm.as_ptr(), index, &mut len as _); let slice = slice::from_raw_parts(str as *const u8, len); match std::str::from_utf8(slice){ Ok(v) => v, Err(e) => { - lua_rust_error(stack.as_ptr(), e); + lua_rust_error(vm.as_ptr(), e); } } } } impl IntoParam for &str { - fn into_param(self, stack: &Stack) -> u16 { + fn into_param(self, vm: &Vm) -> u16 { unsafe { - lua_pushlstring(stack.as_ptr(), self.as_ptr() as _, self.len()); + lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } 1 } @@ -73,15 +73,15 @@ macro_rules! impl_integer { ($($t: ty),*) => { $( impl FromParam<'_> for $t { - unsafe fn from_param(stack: &Stack) -> Self { - luaL_checkinteger(stack.as_ptr(), stack.pop()) as _ + unsafe fn from_param(vm: &Vm, index: i32) -> Self { + luaL_checkinteger(vm.as_ptr(), index) as _ } } impl IntoParam for $t { - fn into_param(self, stack: &Stack) -> u16 { + fn into_param(self, vm: &Vm) -> u16 { unsafe { - lua_pushinteger(stack.as_ptr(), self as _); + lua_pushinteger(vm.as_ptr(), self as _); 1 } } @@ -99,15 +99,15 @@ macro_rules! impl_float { ($($t: ty),*) => { $( impl FromParam<'_> for $t { - unsafe fn from_param(stack: &Stack) -> Self { - luaL_checknumber(stack.as_ptr(), stack.pop()) as _ + unsafe fn from_param(vm: &Vm, index: i32) -> Self { + luaL_checknumber(vm.as_ptr(), index) as _ } } impl IntoParam for $t { - fn into_param(self, stack: &Stack) -> u16 { + fn into_param(self, vm: &Vm) -> u16 { unsafe { - lua_pushnumber(stack.as_ptr(), self as _); + lua_pushnumber(vm.as_ptr(), self as _); 1 } } @@ -119,19 +119,19 @@ macro_rules! impl_float { impl_float!(f32, f64); impl IntoParam for bool { - fn into_param(self, stack: &Stack) -> u16 { - unsafe { lua_pushboolean(stack.as_ptr(), if self { 1 } else { 0 }) }; + fn into_param(self, vm: &Vm) -> u16 { + unsafe { lua_pushboolean(vm.as_ptr(), if self { 1 } else { 0 }) }; 1 } } impl IntoParam for Result { - fn into_param(self, stack: &Stack) -> u16 { + fn into_param(self, vm: &Vm) -> u16 { match self { - Ok(v) => v.into_param(stack), + Ok(v) => v.into_param(vm), Err(e) => { unsafe { - lua_rust_error(stack.as_ptr(), e); + lua_rust_error(vm.as_ptr(), e); } } } @@ -139,15 +139,21 @@ impl IntoParam for Result { } impl IntoParam for Option { - fn into_param(self, stack: &Stack) -> u16 { + fn into_param(self, vm: &Vm) -> u16 { match self { None => { unsafe { - lua_pushnil(stack.as_ptr()); + lua_pushnil(vm.as_ptr()); 1 } } - Some(v) => v.into_param(stack) + Some(v) => v.into_param(vm) } } } + +impl IntoParam for () { + fn into_param(self, _: &Vm) -> u16 { + 0 + } +} diff --git a/src/vm/function/interface.rs b/src/vm/function/interface.rs index 43c7acc..0bd1b0f 100644 --- a/src/vm/function/interface.rs +++ b/src/vm/function/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::Stack; +use crate::vm::Vm; use crate::vm::util::SimpleDrop; /// This trait represents a function return value. @@ -37,10 +37,10 @@ pub trait IntoParam: Sized { /// /// # Arguments /// - /// * `stack`: the stack to push this value to. + /// * `vm`: the [Vm] to push this value to. /// /// returns: u16 - fn into_param(self, stack: &Stack) -> u16; + fn into_param(self, vm: &Vm) -> u16; } /// This trait represents a function parameter. @@ -49,7 +49,8 @@ pub trait FromParam<'a>: Sized + SimpleDrop { /// /// # Arguments /// - /// * `stack`: the stack to read from. + /// * `vm`: the [Vm] to read from. + /// * `index`: index of the parameter to read. /// /// returns: Self /// @@ -57,5 +58,5 @@ pub trait FromParam<'a>: Sized + SimpleDrop { /// /// Calling this function outside the body of a CFunction is UB. Calling this function in a /// non-POF segment of that CFunction is also UB. - unsafe fn from_param(stack: &'a Stack) -> Self; + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self; } diff --git a/src/vm/value/core.rs b/src/vm/value/core.rs index 411fc4d..5631f5c 100644 --- a/src/vm/value/core.rs +++ b/src/vm/value/core.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, CFunction, lua_pushcclosure}; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_pushcclosure}; use crate::vm::function::IntoParam; -use crate::vm::{LuaState, Stack}; +use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, IntoLua, RFunction}; impl<'a> FromLua<'a> for &'a str { - fn from_lua(vm: &LuaState, index: i32) -> crate::vm::Result { - let l = **vm; + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { + let l = vm.as_ptr(); unsafe { let ty = lua_type(l, index); match ty { @@ -44,7 +44,7 @@ impl<'a> FromLua<'a> for &'a str { let slice = std::slice::from_raw_parts(s as _, len); std::str::from_utf8(slice).map_err(Error::InvalidUtf8) }, - _ => Err(Error::TypeError(TypeError { + _ => Err(Error::Type(TypeError { expected: Type::String, actual: ty })) @@ -56,13 +56,13 @@ impl<'a> FromLua<'a> for &'a str { macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { - fn from_lua(vm: &LuaState, index: i32) -> crate::vm::Result { - let l = **vm; + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { + let l = vm.as_ptr(); unsafe { let ty = lua_type(l, index); match ty { Type::$expected => Ok($func(l, index) $($ret)*), - _ => Err(Error::TypeError(TypeError { + _ => Err(Error::Type(TypeError { expected: Type::$expected, actual: ty })) @@ -92,17 +92,14 @@ impl_from_lua!(f64, Number, lua_tonumber, as _); impl_from_lua!(bool, Boolean, lua_toboolean, == 1); impl IntoLua for T { - fn into_lua(self, vm: &LuaState) -> Result { - let stack = unsafe { Stack::wrap(**vm, 0) }; - Ok(self.into_param(&stack)) + fn into_lua(self, vm: &Vm) -> Result { + Ok(self.into_param(vm)) } } -pub struct RFunction(pub CFunction); - impl IntoLua for RFunction { - fn into_lua(self, vm: &LuaState) -> crate::vm::Result { - let l = **vm; + fn into_lua(self, vm: &Vm) -> crate::vm::Result { + let l = vm.as_ptr(); unsafe { lua_pushcclosure(l, self.0, 0); } diff --git a/src/vm/value/interface.rs b/src/vm/value/interface.rs index 428e040..129dc2c 100644 --- a/src/vm/value/interface.rs +++ b/src/vm/value/interface.rs @@ -26,18 +26,19 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::LuaState; +use crate::ffi::lua::CFunction; +use crate::vm::Vm; pub trait FromLua<'a>: Sized { - /// Attempt to read the value at the specified index in the given [LuaState]. + /// Attempt to read the value at the specified index in the given [Vm]. /// /// # Arguments /// - /// * `vm`: the [LuaState] to read from. + /// * `vm`: the [Vm] to read from. /// * `index`: the index at which to try reading the value from. /// /// returns: Result - fn from_lua(vm: &'a LuaState, index: i32) -> crate::vm::Result; + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result; /// Returns the number of values to be expected on the lua stack, after reading this value. fn num_values() -> u16 { @@ -46,14 +47,16 @@ pub trait FromLua<'a>: Sized { } pub trait IntoLua: Sized { - /// Attempt to push self onto the top of the stack in the given [LuaState]. + /// Attempt to push self onto the top of the stack in the given [Vm]. /// /// Returns the number values pushed into the lua stack. /// /// # Arguments /// - /// * `vm`: the [LuaState] to push into. + /// * `vm`: the [Vm] to push into. /// /// returns: Result - fn into_lua(self, vm: &LuaState) -> crate::vm::Result; + fn into_lua(self, vm: &Vm) -> crate::vm::Result; } + +pub struct RFunction(pub CFunction); diff --git a/src/vm/value/table.rs b/src/vm/value/table.rs index bc8dac6..5087a18 100644 --- a/src/vm/value/table.rs +++ b/src/vm/value/table.rs @@ -27,27 +27,28 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_getfield, lua_gettop, lua_setfield, lua_settop, Type}; +use crate::ffi::lua::{lua_getfield, lua_gettop, lua_setfield, lua_settop, lua_type, Type}; use crate::vm::function::FromParam; -use crate::vm::{LuaState, Stack}; +use crate::vm::Vm; +use crate::vm::error::TypeError; use crate::vm::util::AnyStr; use crate::vm::value::{FromLua, IntoLua}; #[derive(Copy, Clone)] pub struct Table<'a> { - vm: &'a LuaState, + vm: &'a Vm, index: i32 } pub struct Scope<'a> { - vm: &'a LuaState, + vm: &'a Vm, index: i32, initial_top: i32 } impl<'a> Scope<'a> { - fn new(vm: &'a LuaState, index: i32) -> Self { - let initial_top = unsafe { lua_gettop(**vm) }; + fn new(vm: &'a Vm, index: i32) -> Self { + let initial_top = unsafe { lua_gettop(vm.as_ptr()) }; Self { vm, index, initial_top } } @@ -56,11 +57,11 @@ impl<'a> Scope<'a> { let nums = value.into_lua(self.vm)?; if nums > 1 { // Clear the stack. - lua_settop(**self.vm, -(nums as i32)-1); + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); //FIXME: Better error type return Err(crate::vm::error::Error::Unknown) } - lua_setfield(**self.vm, self.index, name.to_str()?.as_ptr()); + lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); } Ok(()) } @@ -71,7 +72,7 @@ impl<'a> Scope<'a> { return Err(crate::vm::error::Error::Unknown) } unsafe { - lua_getfield(**self.vm, self.index, name.to_str()?.as_ptr()); + lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); T::from_lua(self.vm, -1) } } @@ -79,11 +80,11 @@ impl<'a> Scope<'a> { impl Drop for Scope<'_> { fn drop(&mut self) { - let top = unsafe { lua_gettop(**self.vm) }; + let top = unsafe { lua_gettop(self.vm.as_ptr()) }; let count = top - self.initial_top; // Pop count values off the stack to ensure the stack is cleared after all table // manipulations are finished. - unsafe { lua_settop(**self.vm, -count-1) }; + unsafe { lua_settop(self.vm.as_ptr(), -count-1) }; } } @@ -94,12 +95,25 @@ impl<'a> Table<'a> { } impl<'a> FromParam<'a> for Table<'a> { - unsafe fn from_param(stack: &'a Stack) -> Self { - let index = stack.pop(); - luaL_checktype(stack.as_ptr(), index, Type::Table); + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + luaL_checktype(vm.as_ptr(), index, Type::Table); Table { - vm: stack.as_state(), + vm, index } } } + +impl<'a> FromLua<'a> for Table<'a> { + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let ty = unsafe { lua_type(vm.as_ptr(), index) }; + if ty == Type::Table { + Ok(Table { vm, index }) + } else { + Err(crate::vm::error::Error::Type(TypeError { + expected: Type::Table, + actual: ty + })) + } + } +} From f5a2fdd6b8c01dad042e29e282898e0bc8177062 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 10:48:47 +0100 Subject: [PATCH 041/527] Improved support for tables --- src/vm/value/table.rs | 31 +++++++++++++++++++++++++++---- 1 file changed, 27 insertions(+), 4 deletions(-) diff --git a/src/vm/value/table.rs b/src/vm/value/table.rs index 5087a18..d708f9d 100644 --- a/src/vm/value/table.rs +++ b/src/vm/value/table.rs @@ -27,14 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_getfield, lua_gettop, lua_setfield, lua_settop, lua_type, Type}; -use crate::vm::function::FromParam; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_setfield, lua_settop, lua_type, Type}; +use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; use crate::vm::error::TypeError; -use crate::vm::util::AnyStr; +use crate::vm::util::{AnyStr, SimpleDrop}; use crate::vm::value::{FromLua, IntoLua}; -#[derive(Copy, Clone)] pub struct Table<'a> { vm: &'a Vm, index: i32 @@ -89,11 +88,25 @@ impl Drop for Scope<'_> { } impl<'a> Table<'a> { + pub fn new(vm: &'a Vm) -> Self { + unsafe { lua_createtable(vm.as_ptr(), 0, 0) }; + let index = unsafe { lua_gettop(vm.as_ptr()) }; + Self { vm, index } + } + + pub fn with_capacity(vm: &'a Vm, array_capacity: usize, non_array_capcity: usize) -> Self { + unsafe { lua_createtable(vm.as_ptr(), array_capacity as _, non_array_capcity as _) }; + let index = unsafe { lua_gettop(vm.as_ptr()) }; + Self { vm, index } + } + pub fn lock(&mut self) -> Scope { Scope::new(self.vm, self.index) } } +unsafe impl<'a> SimpleDrop for Table<'a> {} + impl<'a> FromParam<'a> for Table<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { luaL_checktype(vm.as_ptr(), index, Type::Table); @@ -117,3 +130,13 @@ impl<'a> FromLua<'a> for Table<'a> { } } } + +impl IntoParam for Table<'_> { + fn into_param(self, vm: &Vm) -> u16 { + let top = unsafe { lua_gettop(vm.as_ptr()) }; + if top != self.index { + unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; + } + 1 + } +} From 76df97cf6413c4bd188e5ea8d6178a4d7d24eb8e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 10:58:27 +0100 Subject: [PATCH 042/527] Added clear and top functions to Vm --- src/vm/core.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/vm/core.rs b/src/vm/core.rs index fa64630..00a9ea6 100644 --- a/src/vm/core.rs +++ b/src/vm/core.rs @@ -29,7 +29,7 @@ use std::ffi::c_int; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; +use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::util::{AnyStr, LoadCode}; use crate::vm::value::{FromLua, IntoLua}; @@ -75,12 +75,23 @@ impl Vm { } } + /// Returns the top of the lua stack. + #[inline] + pub fn top(&self) -> i32 { + unsafe { lua_gettop(self.l) } + } + + /// Clears the lua stack. + pub fn clear(&mut self) { + unsafe { lua_settop(self.l, 0); } + } + #[inline] pub fn as_ptr(&self) -> State { self.l } - pub fn set_global(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { + pub fn set_global(&self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { value.into_lua(self)?; unsafe { lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); From c33f460fdde8014e770faa30443a4ddeb3f9294a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 11:27:43 +0100 Subject: [PATCH 043/527] Large project re-organization to include a performance test with mlua-luajit --- Cargo.toml | 29 +----- core/Cargo.toml | 24 +++++ build.rs => core/build.rs | 4 +- {src => core/src}/ffi/laux.rs | 0 {src => core/src}/ffi/lua.rs | 0 {src => core/src}/ffi/mod.rs | 0 {src => core/src}/lib.rs | 0 {src => core/src}/macros/lib_func.rs | 0 {src => core/src}/macros/mod.rs | 0 {src => core/src}/vm/core.rs | 0 {src => core/src}/vm/error.rs | 0 {src => core/src}/vm/function/core.rs | 0 {src => core/src}/vm/function/interface.rs | 0 {src => core/src}/vm/function/mod.rs | 0 {src => core/src}/vm/mod.rs | 0 {src => core/src}/vm/util.rs | 0 {src => core/src}/vm/value/core.rs | 0 {src => core/src}/vm/value/interface.rs | 0 {src => core/src}/vm/value/mod.rs | 0 {src => core/src}/vm/value/table.rs | 0 {tests => core/tests}/test_vm_destructor.rs | 5 +- core/tests/test_vm_tables.rs | 67 +++++++++++++ testbin/Cargo.toml | 11 +++ testbin/src/main.rs | 103 ++++++++++++++++++++ 24 files changed, 215 insertions(+), 28 deletions(-) create mode 100644 core/Cargo.toml rename build.rs => core/build.rs (96%) rename {src => core/src}/ffi/laux.rs (100%) rename {src => core/src}/ffi/lua.rs (100%) rename {src => core/src}/ffi/mod.rs (100%) rename {src => core/src}/lib.rs (100%) rename {src => core/src}/macros/lib_func.rs (100%) rename {src => core/src}/macros/mod.rs (100%) rename {src => core/src}/vm/core.rs (100%) rename {src => core/src}/vm/error.rs (100%) rename {src => core/src}/vm/function/core.rs (100%) rename {src => core/src}/vm/function/interface.rs (100%) rename {src => core/src}/vm/function/mod.rs (100%) rename {src => core/src}/vm/mod.rs (100%) rename {src => core/src}/vm/util.rs (100%) rename {src => core/src}/vm/value/core.rs (100%) rename {src => core/src}/vm/value/interface.rs (100%) rename {src => core/src}/vm/value/mod.rs (100%) rename {src => core/src}/vm/value/table.rs (100%) rename {tests => core/tests}/test_vm_destructor.rs (97%) create mode 100644 core/tests/test_vm_tables.rs create mode 100644 testbin/Cargo.toml create mode 100644 testbin/src/main.rs diff --git a/Cargo.toml b/Cargo.toml index 14019a2..c66655d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,5 @@ -[package] -name = "bp3d-lua" -version = "0.2.0" -authors = ["Yuri Edward "] -edition = "2021" -description = "Lua wrapper and base library for BP3D." -license = "BSD-3-Clause" -repository = "https://gitlab.com/bp3d/lua" -readme = "./README.MD" -keywords = ["lua"] -categories = ["graphics"] - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - -[dependencies] -bp3d-util = { version = "1.4.0", features = ["simple-error"] } -log = "0.4.26" - -[build-dependencies] -bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } -phf = { version = "0.11.3", features = ["macros"] } -cc = "1.2.15" - -[features] +[workspace] +members = [ + "core", + "testbin" +] \ No newline at end of file diff --git a/core/Cargo.toml b/core/Cargo.toml new file mode 100644 index 0000000..14019a2 --- /dev/null +++ b/core/Cargo.toml @@ -0,0 +1,24 @@ +[package] +name = "bp3d-lua" +version = "0.2.0" +authors = ["Yuri Edward "] +edition = "2021" +description = "Lua wrapper and base library for BP3D." +license = "BSD-3-Clause" +repository = "https://gitlab.com/bp3d/lua" +readme = "./README.MD" +keywords = ["lua"] +categories = ["graphics"] + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +bp3d-util = { version = "1.4.0", features = ["simple-error"] } +log = "0.4.26" + +[build-dependencies] +bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } +phf = { version = "0.11.3", features = ["macros"] } +cc = "1.2.15" + +[features] diff --git a/build.rs b/core/build.rs similarity index 96% rename from build.rs rename to core/build.rs index 6f568cd..3b0ad80 100644 --- a/build.rs +++ b/core/build.rs @@ -50,7 +50,7 @@ static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { }; fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator>) -> ExitStatus { - let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); + let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); std::process::Command::new(cmd) .args(args) .current_dir(path.join("LuaJIT")).status().expect(text) @@ -96,7 +96,7 @@ fn main() { let out_path = Path::new(&out).join("luajit-build"); // Apply patches to LuaJIT source code. - let path = bp3d_os::fs::get_absolute_path("./").expect("Failed to acquire current path"); + let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lib_init.patch").as_os_str()]); if !result.success() { panic!("Failed to patch LuaJIT"); diff --git a/src/ffi/laux.rs b/core/src/ffi/laux.rs similarity index 100% rename from src/ffi/laux.rs rename to core/src/ffi/laux.rs diff --git a/src/ffi/lua.rs b/core/src/ffi/lua.rs similarity index 100% rename from src/ffi/lua.rs rename to core/src/ffi/lua.rs diff --git a/src/ffi/mod.rs b/core/src/ffi/mod.rs similarity index 100% rename from src/ffi/mod.rs rename to core/src/ffi/mod.rs diff --git a/src/lib.rs b/core/src/lib.rs similarity index 100% rename from src/lib.rs rename to core/src/lib.rs diff --git a/src/macros/lib_func.rs b/core/src/macros/lib_func.rs similarity index 100% rename from src/macros/lib_func.rs rename to core/src/macros/lib_func.rs diff --git a/src/macros/mod.rs b/core/src/macros/mod.rs similarity index 100% rename from src/macros/mod.rs rename to core/src/macros/mod.rs diff --git a/src/vm/core.rs b/core/src/vm/core.rs similarity index 100% rename from src/vm/core.rs rename to core/src/vm/core.rs diff --git a/src/vm/error.rs b/core/src/vm/error.rs similarity index 100% rename from src/vm/error.rs rename to core/src/vm/error.rs diff --git a/src/vm/function/core.rs b/core/src/vm/function/core.rs similarity index 100% rename from src/vm/function/core.rs rename to core/src/vm/function/core.rs diff --git a/src/vm/function/interface.rs b/core/src/vm/function/interface.rs similarity index 100% rename from src/vm/function/interface.rs rename to core/src/vm/function/interface.rs diff --git a/src/vm/function/mod.rs b/core/src/vm/function/mod.rs similarity index 100% rename from src/vm/function/mod.rs rename to core/src/vm/function/mod.rs diff --git a/src/vm/mod.rs b/core/src/vm/mod.rs similarity index 100% rename from src/vm/mod.rs rename to core/src/vm/mod.rs diff --git a/src/vm/util.rs b/core/src/vm/util.rs similarity index 100% rename from src/vm/util.rs rename to core/src/vm/util.rs diff --git a/src/vm/value/core.rs b/core/src/vm/value/core.rs similarity index 100% rename from src/vm/value/core.rs rename to core/src/vm/value/core.rs diff --git a/src/vm/value/interface.rs b/core/src/vm/value/interface.rs similarity index 100% rename from src/vm/value/interface.rs rename to core/src/vm/value/interface.rs diff --git a/src/vm/value/mod.rs b/core/src/vm/value/mod.rs similarity index 100% rename from src/vm/value/mod.rs rename to core/src/vm/value/mod.rs diff --git a/src/vm/value/table.rs b/core/src/vm/value/table.rs similarity index 100% rename from src/vm/value/table.rs rename to core/src/vm/value/table.rs diff --git a/tests/test_vm_destructor.rs b/core/tests/test_vm_destructor.rs similarity index 97% rename from tests/test_vm_destructor.rs rename to core/tests/test_vm_destructor.rs index 56b80aa..1c31fbe 100644 --- a/tests/test_vm_destructor.rs +++ b/core/tests/test_vm_destructor.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_lib_func; -use bp3d_lua::vm::Vm; +use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::RFunction; struct ValueWithDrop; @@ -52,7 +52,7 @@ decl_lib_func! { #[test] fn test_vm_destructor() { - let mut vm = Vm::new(); + let mut vm = RootVm::new(); vm.set_global(c"test_c_function", RFunction(test_c_function)).unwrap(); let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); @@ -63,6 +63,7 @@ fn test_vm_destructor() { let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); assert!(vm.run_code::(c"return test_c_function('this is a test', 0.42)").is_err()); + vm.clear(); let time = time.elapsed(); println!("time: {:?}", time); } diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs new file mode 100644 index 0000000..f7fd8ab --- /dev/null +++ b/core/tests/test_vm_tables.rs @@ -0,0 +1,67 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::{RootVm}; +use bp3d_lua::vm::value::table::Table; + +#[test] +fn tables() { + let mut vm = RootVm::new(); + let top = vm.top(); + let mut tbl = Table::new(&vm); + { + let mut scope = tbl.lock(); + scope.set_field(c"a", 0.42).unwrap(); + scope.set_field(c"b", "My great string").unwrap(); + let mut new_table = Table::new(&vm); + { + let mut scope = new_table.lock(); + scope.set_field(c"whatever", 42).unwrap(); + } + let s: &str = scope.get_field(c"b").unwrap(); + assert_eq!(s, "My great string"); + scope.set_field(c"sub", new_table).unwrap(); + } + vm.set_global(c"myTable", tbl).unwrap(); + let new_top = vm.top(); + assert_eq!(top, new_top); + let v = vm.run_code::(c"return myTable.c"); + assert!(v.is_err()); + let v = vm.run_code::(c"return myTable.a"); + assert!(v.is_ok()); + assert_eq!(v.unwrap(), 0.42); + let v = vm.run_code::<&str>(c"return myTable.b"); + assert!(v.is_ok()); + assert_eq!(v.unwrap(), "My great string"); + let v = vm.run_code::(c"return myTable.sub.whatever"); + assert!(v.is_ok()); + assert_eq!(v.unwrap(), 42); + vm.clear(); + let new_top_1 = vm.top(); + assert_eq!(new_top, new_top_1); +} diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml new file mode 100644 index 0000000..fe56e55 --- /dev/null +++ b/testbin/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "testbin" +version = "0.1.0" +edition = "2021" +publish = false + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +mlua = { version = "0.10.3", features = ["luajit", "vendored"] } +bp3d-lua = { version = "0.2.0", path = "../core" } diff --git a/testbin/src/main.rs b/testbin/src/main.rs new file mode 100644 index 0000000..06cd3d1 --- /dev/null +++ b/testbin/src/main.rs @@ -0,0 +1,103 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use mlua::Lua; +use bp3d_lua::decl_lib_func; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::RFunction; + +struct ValueWithDrop; +impl ValueWithDrop { + pub fn print(&self) { + } +} +impl Drop for ValueWithDrop { + fn drop(&mut self) { + } +} + +decl_lib_func! { + fn test_c_function(name: &str, value: f64) -> String { + let drop = ValueWithDrop; + drop.print(); + format!("Hello {} ({})", name, value) + } +} + +fn test_vm_destructor() -> Duration { + let mut vm = RootVm::new(); + vm.set_global(c"test_c_function", RFunction(test_c_function)).unwrap(); + let time = std::time::Instant::now(); + for _ in 0..20000 { + let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); + assert!(res.is_err()); + assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); + let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); + assert_eq!(s, "Hello this is a test (0.42)"); + vm.clear(); + } + let time = time.elapsed(); + time +} + +fn test_vm_mlua() -> Duration { + let lua = Lua::new(); + let f = lua.create_function(|_, (name, value): (String, f64)| { + let drop = ValueWithDrop; + drop.print(); + Ok(format!("Hello {} ({})", name, value)) + }).unwrap(); + lua.globals().set("test_c_function", f).unwrap(); + let time = std::time::Instant::now(); + for _ in 0..20000 { + let res: mlua::Result = lua.load("return test_c_function('this is a test\\xFF', 0.42)").call(()); + assert!(res.is_err()); + assert!(lua.load("return test_c_function('this is a test', 0.42)").call::(()).is_ok()); + let s: String = lua.load("return test_c_function('this is a test', 0.42)").call(()).unwrap(); + assert_eq!(s, "Hello this is a test (0.42)"); + } + let time = time.elapsed(); + time +} + +fn main() { + const RUNS: u32 = 10; + let mut lua = Duration::new(0, 0); + let mut mlua = Duration::new(0, 0); + for _ in 0..RUNS { + lua += test_vm_destructor(); + mlua += test_vm_mlua(); + } + lua = lua / RUNS; + mlua = mlua / RUNS; + println!("average tools.lua: {:?}", lua); + println!("average mlua: {:?}", mlua); + assert!(lua < mlua); + println!("average diff: {:?}", mlua - lua); +} From 324fa6ad5e85cf3afd5dc8b0b34729b70dbbd1b9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 11:28:14 +0100 Subject: [PATCH 044/527] Fixed path to README --- core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 14019a2..c88368d 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" description = "Lua wrapper and base library for BP3D." license = "BSD-3-Clause" repository = "https://gitlab.com/bp3d/lua" -readme = "./README.MD" +readme = "../README.MD" keywords = ["lua"] categories = ["graphics"] From 22f6d90960cb53344429be067aee1e56a2881b88 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 11:59:41 +0100 Subject: [PATCH 045/527] Fixed link error --- core/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.rs b/core/build.rs index 3b0ad80..dbefae3 100644 --- a/core/build.rs +++ b/core/build.rs @@ -124,5 +124,5 @@ fn main() { // Attempt to setup linkage. println!("cargo:rustc-link-search=native={}", out_path.join("src").display()); - println!("cargo:rustc-link-lib=static=luajit"); + println!("cargo:rustc-link-lib=static:-bundle,+whole-archive=luajit"); } From 2281427e105ed2e71584055223eaf2a893703e99 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:29:08 +0100 Subject: [PATCH 046/527] Added FromLua for empty value --- core/src/vm/value/core.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 5631f5c..43f1cc5 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -106,3 +106,13 @@ impl IntoLua for RFunction { Ok(1) } } + +impl FromLua<'_> for () { + fn from_lua(_vm: &Vm, _: i32) -> crate::vm::Result<()> { + Ok(()) + } + + fn num_values() -> u16 { + 0 + } +} From 9cd5f9010e24c9ef0de8e7cefad82df1a3b6bd3a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:29:38 +0100 Subject: [PATCH 047/527] Added absolute function to convert a relative stack index into an absolute stack index --- core/src/vm/core.rs | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 00a9ea6..5a74af5 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -29,7 +29,7 @@ use std::ffi::c_int; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; +use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::util::{AnyStr, LoadCode}; use crate::vm::value::{FromLua, IntoLua}; @@ -75,6 +75,16 @@ impl Vm { } } + /// Returns the absolute stack index for the given index. + #[inline] + pub fn absolute(&self, index: i32) -> i32 { + if index < 0 { + unsafe { lua_gettop(self.l) + index + 1 } + } else { + index + } + } + /// Returns the top of the lua stack. #[inline] pub fn top(&self) -> i32 { @@ -82,6 +92,7 @@ impl Vm { } /// Clears the lua stack. + #[inline] pub fn clear(&mut self) { unsafe { lua_settop(self.l, 0); } } @@ -99,7 +110,14 @@ impl Vm { Ok(()) } - pub fn run_code<'a, R: FromLua<'a>>(&'a mut self, code: impl LoadCode) -> crate::vm::Result { + pub fn get_global<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr) -> crate::vm::Result { + unsafe { + lua_getfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); + } + R::from_lua(self, -1) + } + + pub fn run_code<'a, R: FromLua<'a>>(&'a self, code: impl LoadCode) -> crate::vm::Result { let l = self.as_ptr(); // Push error handler and the get the stack position of it. let handler_pos = unsafe { From 401e4656ee0ef8502dac16655b14eb3d6a4db97b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:30:05 +0100 Subject: [PATCH 048/527] Fixed macro accepting an additional Vm parameter --- core/src/macros/lib_func.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/macros/lib_func.rs b/core/src/macros/lib_func.rs index e12d3f9..b003655 100644 --- a/core/src/macros/lib_func.rs +++ b/core/src/macros/lib_func.rs @@ -29,10 +29,10 @@ #[macro_export] macro_rules! decl_lib_func { ( - fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + fn $fn_name: ident ($name: ident: &Vm, $($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block ) => { pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code + fn _func($name: &$crate::vm::Vm, $($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; @@ -41,25 +41,25 @@ macro_rules! decl_lib_func { let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; index += 1; )* - let ret = _func($($arg_name),*); + let ret = _func(&vm, $($arg_name),*); ret.into_param(&vm) as _ } }; ( - fn $fn_name: ident (vm: &mut Vm, $($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block ) => { pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func(vm: &mut crate::vm::Vm, $($arg_name: $arg_ty),*) -> $ret_ty $code + fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; - let mut vm = unsafe { $crate::vm::Vm::from_raw(l) }; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; let mut index = 1; $( let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; index += 1; )* - let ret = _func(&mut vm, $($arg_name),*); + let ret = _func($($arg_name),*); ret.into_param(&vm) as _ } - } + }; } From 970da0724b99ec35bc6400454ae576ee069443c1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:30:46 +0100 Subject: [PATCH 049/527] Fixed bug in Table where the index is not absolute when read from FromLua --- core/src/vm/value/table.rs | 1 + core/tests/test_vm_tables.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index d708f9d..e28a9d7 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -120,6 +120,7 @@ impl<'a> FromParam<'a> for Table<'a> { impl<'a> FromLua<'a> for Table<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let ty = unsafe { lua_type(vm.as_ptr(), index) }; + let index = vm.absolute(index); if ty == Type::Table { Ok(Table { vm, index }) } else { diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index f7fd8ab..28ad4eb 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -64,4 +64,19 @@ fn tables() { vm.clear(); let new_top_1 = vm.top(); assert_eq!(new_top, new_top_1); + let mut tbl: Table = vm.get_global("myTable").unwrap(); + { + let scope = tbl.lock(); + let v: f64 = scope.get_field(c"a").unwrap(); + assert_eq!(v, 0.42); + } + let v = vm.run_code::<&str>(c"return myTable.b").unwrap(); + assert_eq!(v, "My great string"); + { + let scope = tbl.lock(); + let v: f64 = scope.get_field(c"a").unwrap(); + assert_eq!(v, 0.42); + } + assert_eq!(v, "My great string"); + assert_eq!(vm.top(), new_top + 2); } From 80fd41a08a72e755dc1c6a11c84d364cdace43ca Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:30:58 +0100 Subject: [PATCH 050/527] Added a test dostring function --- core/tests/test_vm_run_string.rs | 51 ++++++++++++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 core/tests/test_vm_run_string.rs diff --git a/core/tests/test_vm_run_string.rs b/core/tests/test_vm_run_string.rs new file mode 100644 index 0000000..6d8bf11 --- /dev/null +++ b/core/tests/test_vm_run_string.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::decl_lib_func; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::RFunction; + +decl_lib_func! { + fn dostring(vm: &Vm, code: &str) -> bp3d_lua::vm::Result<()> { + let ret = vm.run_code::<()>(code); + println!("Ran code: {}", code); + ret + } +} + +#[test] +fn test_run_string() { + let vm = RootVm::new(); + assert!(vm.run_code::<()>(c"dostring('test')").is_err()); + vm.set_global(c"dostring", RFunction(dostring)).unwrap(); + assert!(vm.run_code::<()>(c"dostring('test')").is_err()); + assert!(vm.run_code::<()>(c"dostring('print(\"whatever 123\")')").is_ok()); + assert!(vm.run_code::<()>(c"dostring('root = 42')").is_ok()); + let val: u32 = vm.get_global("root").unwrap(); + assert_eq!(val, 42); +} From 69b12ca46b0f4f16a4bf2faa957b2007e4642411 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:34:01 +0100 Subject: [PATCH 051/527] Added assert to ensure that dostring is indeed disabled in the base luajit sandboxed VM --- core/tests/test_vm_run_string.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/tests/test_vm_run_string.rs b/core/tests/test_vm_run_string.rs index 6d8bf11..13715ba 100644 --- a/core/tests/test_vm_run_string.rs +++ b/core/tests/test_vm_run_string.rs @@ -41,7 +41,9 @@ decl_lib_func! { #[test] fn test_run_string() { let vm = RootVm::new(); - assert!(vm.run_code::<()>(c"dostring('test')").is_err()); + let res = vm.run_code::<()>(c"dostring('test')"); + assert!(res.is_err()); + assert_eq!(res.unwrap_err().to_string(), "runtime error: [string \"dostring('test')\"]:1: attempt to call global 'dostring' (a nil value)"); vm.set_global(c"dostring", RFunction(dostring)).unwrap(); assert!(vm.run_code::<()>(c"dostring('test')").is_err()); assert!(vm.run_code::<()>(c"dostring('print(\"whatever 123\")')").is_ok()); From 7fcd71ec8f946235d0b8381fc024912ac5d640e0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 15:53:08 +0100 Subject: [PATCH 052/527] Added new error type and set/get functions to use integer keys with tables --- core/src/vm/error.rs | 3 ++- core/src/vm/value/table.rs | 32 +++++++++++++++++++++++++++----- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index a84ab6a..c42508f 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -86,7 +86,8 @@ simple_error! { Memory => "memory allocation error", Unknown => "unknown error", Error => "error in error handler", - Null => "string contains a null character" + Null => "string contains a null character", + MultiValue => "only one value is supported by this API" } } diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index e28a9d7..6d16734 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use log::__private_api::Value; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_setfield, lua_settop, lua_type, Type}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_type, Type}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; use crate::vm::error::TypeError; @@ -57,8 +58,7 @@ impl<'a> Scope<'a> { if nums > 1 { // Clear the stack. lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - //FIXME: Better error type - return Err(crate::vm::error::Error::Unknown) + return Err(crate::vm::error::Error::MultiValue); } lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); } @@ -67,14 +67,36 @@ impl<'a> Scope<'a> { pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { if T::num_values() > 1 { - //FIXME: Better error type - return Err(crate::vm::error::Error::Unknown) + return Err(crate::vm::error::Error::MultiValue); } unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); T::from_lua(self.vm, -1) } } + + pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm)?; + if nums > 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_rawseti(self.vm.as_ptr(), self.index, i); + } + Ok(()) + } + + pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { + if T::num_values() > 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + lua_rawgeti(self.vm.as_ptr(), self.index, i); + T::from_lua(self.vm, -1) + } + } } impl Drop for Scope<'_> { From 5c37365f094db9d5d675b3b435dc91d6501cc023 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 8 Mar 2025 21:17:12 +0100 Subject: [PATCH 053/527] Added table len function and new lua_ext patch --- core/build.rs | 4 +++ core/src/ffi/ext.rs | 47 ++++++++++++++++++++++++++++++++++ core/src/ffi/mod.rs | 1 + core/src/vm/function/core.rs | 7 +++--- core/src/vm/value/table.rs | 25 ++++++++++++++++-- core/tests/test_vm_tables.rs | 2 ++ patch/lua_ext.patch | 49 ++++++++++++++++++++++++++++++++++++ 7 files changed, 130 insertions(+), 5 deletions(-) create mode 100644 core/src/ffi/ext.rs create mode 100644 patch/lua_ext.patch diff --git a/core/build.rs b/core/build.rs index dbefae3..4de9d02 100644 --- a/core/build.rs +++ b/core/build.rs @@ -109,6 +109,10 @@ fn main() { if !result.success() { panic!("Failed to patch LuaJIT"); } + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lua_ext.patch").as_os_str()]); + if !result.success() { + panic!("Failed to patch LuaJIT"); + } // Copy the source directory to the build directory. println!("{}", out_path.display()); diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs new file mode 100644 index 0000000..1582a85 --- /dev/null +++ b/core/src/ffi/ext.rs @@ -0,0 +1,47 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::c_int; +use crate::ffi::lua::{Integer, Number, State}; + +pub type MSize = u32; + +//--------------- +// Value reading +//--------------- +extern "C" { + pub fn lua_ext_fast_checknumber(l: State, numarg: c_int) -> Number; + pub fn lua_ext_fast_checkinteger(l: State, numarg: c_int) -> Integer; +} + +//------- +// Other +//------- +extern "C" { + pub fn lua_ext_tab_len(l: State, idx: c_int, outsize: *mut MSize) -> c_int; +} diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index 0f4c2cb..80a5329 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -28,3 +28,4 @@ pub mod lua; pub mod laux; +pub mod ext; diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 6834b0f..34b1210 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -28,8 +28,9 @@ use std::error::Error; use std::slice; -use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber}; +use crate::ffi::laux::luaL_checklstring; use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Type}; +use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::util::{lua_rust_error}; use crate::vm::Vm; @@ -74,7 +75,7 @@ macro_rules! impl_integer { $( impl FromParam<'_> for $t { unsafe fn from_param(vm: &Vm, index: i32) -> Self { - luaL_checkinteger(vm.as_ptr(), index) as _ + lua_ext_fast_checkinteger(vm.as_ptr(), index) as _ } } @@ -100,7 +101,7 @@ macro_rules! impl_float { $( impl FromParam<'_> for $t { unsafe fn from_param(vm: &Vm, index: i32) -> Self { - luaL_checknumber(vm.as_ptr(), index) as _ + lua_ext_fast_checknumber(vm.as_ptr(), index) as _ } } diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index 6d16734..bf80b3e 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use log::__private_api::Value; +use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_type, Type}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_type, Type}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; use crate::vm::error::TypeError; @@ -125,6 +125,27 @@ impl<'a> Table<'a> { pub fn lock(&mut self) -> Scope { Scope::new(self.vm, self.index) } + + pub fn len(&self) -> usize { + let mut size: MSize = 0; + let ret = unsafe { lua_ext_tab_len(self.vm.as_ptr(), self.index, &mut size) }; + if ret == 0 { + return size as _; + } + let mut count = 0; + unsafe { + lua_pushnil(self.vm.as_ptr()); + while lua_next(self.vm.as_ptr(), self.index) != 0 { + lua_settop(self.vm.as_ptr(), -2); + count += 1; + } + } + count + } + + pub fn is_empty(&self) -> bool { + self.len() == 0 + } } unsafe impl<'a> SimpleDrop for Table<'a> {} diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index 28ad4eb..37c8501 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -47,6 +47,7 @@ fn tables() { assert_eq!(s, "My great string"); scope.set_field(c"sub", new_table).unwrap(); } + assert_eq!(tbl.len(), 3); vm.set_global(c"myTable", tbl).unwrap(); let new_top = vm.top(); assert_eq!(top, new_top); @@ -65,6 +66,7 @@ fn tables() { let new_top_1 = vm.top(); assert_eq!(new_top, new_top_1); let mut tbl: Table = vm.get_global("myTable").unwrap(); + assert_eq!(tbl.len(), 3); { let scope = tbl.lock(); let v: f64 = scope.get_field(c"a").unwrap(); diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch new file mode 100644 index 0000000..5cfa5bc --- /dev/null +++ b/patch/lua_ext.patch @@ -0,0 +1,49 @@ +diff --git a/src/lj_api.c b/src/lj_api.c +index e9fc25b4..7e21f135 100644 +--- a/src/lj_api.c ++++ b/src/lj_api.c +@@ -1317,3 +1317,44 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) + g->allocf = f; + } + ++/*------------*/ ++/* Extensions */ ++/*------------*/ ++ ++LUALIB_API int lua_ext_tab_len(lua_State* L, int idx, MSize* outsize) ++{ ++ TValue* o = index2adr(L, idx); ++ GCtab* tab = tabV(o); ++ if (LJ_LIKELY(tab->hmask == 0)) ++ return lj_tab_len(tab); ++ // Return an error as the table has a hash part. ++ return -1; ++} ++ ++LUALIB_API lua_Number lua_ext_fast_checknumber(lua_State *L, int idx) ++{ ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tvisnumber(o))) ++ return numberVnum(o); ++ else ++ lj_err_argt(L, idx, LUA_TNUMBER); ++ return 0; ++} ++ ++LUALIB_API lua_Integer lua_ext_fast_checkinteger(lua_State *L, int idx) ++{ ++ cTValue *o = index2adr(L, idx); ++ lua_Number n; ++ if (LJ_LIKELY(tvisint(o))) { ++ return intV(o); ++ } else if (LJ_LIKELY(tvisnum(o))) { ++ n = numV(o); ++ } else { ++ lj_err_argt(L, idx, LUA_TNUMBER); ++ } ++#if LJ_64 ++ return (lua_Integer)n; ++#else ++ return lj_num2int(n); ++#endif ++} From 037bd8c875a64a50e184f948b5425e058c4a4d26 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 9 Mar 2025 20:38:20 +0100 Subject: [PATCH 054/527] Fixed bug in decl_lib_func macro --- core/src/macros/lib_func.rs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/core/src/macros/lib_func.rs b/core/src/macros/lib_func.rs index b003655..a493105 100644 --- a/core/src/macros/lib_func.rs +++ b/core/src/macros/lib_func.rs @@ -29,26 +29,26 @@ #[macro_export] macro_rules! decl_lib_func { ( - fn $fn_name: ident ($name: ident: &Vm, $($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { - pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func($name: &$crate::vm::Vm, $($arg_name: $arg_ty),*) -> $ret_ty $code + $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { + fn _func($name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; let mut index = 1; - $( + $($( let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; index += 1; - )* - let ret = _func(&vm, $($arg_name),*); + )*)? + let ret = _func(&vm $(, $($arg_name),*)?); ret.into_param(&vm) as _ } }; ( - fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block ) => { - pub extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { + $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; From 053db51fb39c84944f59c4194f9b950e84a97447 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 9 Mar 2025 20:38:48 +0100 Subject: [PATCH 055/527] Added base design for userdata --- core/src/macros/mod.rs | 2 + core/src/macros/userdata.rs | 71 +++++++++++++++++++++++ core/src/macros/userdata_func.rs | 97 ++++++++++++++++++++++++++++++++ core/src/vm/mod.rs | 3 +- core/src/vm/userdata.rs | 69 +++++++++++++++++++++++ 5 files changed, 241 insertions(+), 1 deletion(-) create mode 100644 core/src/macros/userdata.rs create mode 100644 core/src/macros/userdata_func.rs create mode 100644 core/src/vm/userdata.rs diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index d5dd9c2..2e592c2 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -27,3 +27,5 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod lib_func; +mod userdata_func; +mod userdata; diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs new file mode 100644 index 0000000..108be1a --- /dev/null +++ b/core/src/macros/userdata.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! decl_userdata { + ( + impl $obj_name: ident { + $( + $vis: vis fn $fn_name: ident(this: &$obj_name2: ident$($tokens: tt)*) -> $ret_ty: ty $code: block + )* + } + ) => { + $( + $crate::decl_userdata_func! { + $vis fn $fn_name(this: &$obj_name$($tokens)*) -> $ret_ty $code + } + )* + + impl $crate::vm::userdata::UserData for $obj_name { + const CLASS_NAME: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($obj_name), "\0").as_ptr() as _) }; + } + + unsafe impl $crate::vm::userdata::UserDataImmutable for $obj_name {} + }; +} + +#[macro_export] +macro_rules! decl_userdata_mut { + ( + impl $obj_name: ident { + $( + $vis: vis fn $fn_name: ident($($tokens: tt)*) -> $ret_ty: ty $code: block + )* + } + ) => { + $( + $crate::decl_userdata_func! { + $vis fn $fn_name($($tokens)*) -> $ret_ty $code + } + )* + + impl $crate::vm::userdata::UserData for $obj_name { + const CLASS_NAME: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($obj_name), "\0").as_ptr() as _) }; + } + }; +} diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs new file mode 100644 index 0000000..d977628 --- /dev/null +++ b/core/src/macros/userdata_func.rs @@ -0,0 +1,97 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! decl_userdata_func { + ( + $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + ) => { + impl $obj_name { + $vis fn $fn_name() -> $crate::vm::userdata::Function { + fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; + 0 + } + let mut f = $crate::vm::userdata::Function::new(_cfunc); + f.mutable(); + //$($(f.arg::<$arg_type>();)*)? + f + } + } + }; + ( + $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + ) => { + impl $obj_name { + $vis fn $fn_name() -> $crate::vm::userdata::Function { + fn _func($this: &mut $obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; + 0 + } + let mut f = $crate::vm::userdata::Function::new(_cfunc); + f.mutable(); + //$($(f.arg::<$arg_type>();)*)? + f + } + } + }; + ( + $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + ) => { + impl $obj_name { + $vis fn $fn_name() -> $crate::vm::userdata::Function { + fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; + 0 + } + let mut f = $crate::vm::userdata::Function::new(_cfunc); + //$($(f.arg::<$arg_type>();)*)? + f + } + } + }; + ( + $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + ) => { + impl $obj_name { + $vis fn $fn_name() -> $crate::vm::userdata::Function { + fn _func($this: &$obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; + 0 + } + let mut f = $crate::vm::userdata::Function::new(_cfunc); + //$($(f.arg::<$arg_type>();)*)? + f + } + } + }; +} diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index a3cd3e4..28f14ff 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -30,7 +30,8 @@ mod core; pub mod function; pub mod value; pub mod error; -mod util; +pub mod util; +pub mod userdata; pub use core::*; diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs new file mode 100644 index 0000000..9e66e6b --- /dev/null +++ b/core/src/vm/userdata.rs @@ -0,0 +1,69 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::any::TypeId; +use std::ffi::CStr; +use crate::ffi::lua::CFunction; +use crate::vm::util::LuaType; + +pub struct Function { + is_mutable: bool, + args: Vec, + func: CFunction +} + +impl Function { + pub fn new(func: CFunction) -> Function { + Function { + is_mutable: false, + args: Vec::new(), + func + } + } + + pub fn mutable(&mut self) -> &mut Self { + self.is_mutable = true; + self + } + + pub fn arg(&mut self) -> &mut Self { + self.args.push(T::lua_type()); + self + } +} + +pub trait UserData: Sized { + const CLASS_NAME: &'static CStr; + + //fn register(registry: &Registry); +} + +//TODO: Implement FromLua on UserData only when that userdata is also UserDataImmutable +//TODO: most likely need another luajit hack to allow returning errors from luaL_checkudata instead +// of unwinding +pub unsafe trait UserDataImmutable: UserData {} From 78cb2e8aa6124fc7bd3fe1c019014ae91936ab23 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 9 Mar 2025 21:36:28 +0100 Subject: [PATCH 056/527] Added support for LuaType throughout FromParam --- core/src/macros/mod.rs | 7 ++++++ core/src/macros/userdata.rs | 4 +-- core/src/macros/userdata_func.rs | 20 +++++++++------ core/src/vm/function/core.rs | 42 ++++++++++++++++++++++++++++--- core/src/vm/function/interface.rs | 4 +-- core/src/vm/userdata.rs | 7 +++--- core/src/vm/util.rs | 13 +++++++++- core/src/vm/value/table.rs | 4 ++- core/tests/test_vm_tables.rs | 2 +- 9 files changed, 81 insertions(+), 22 deletions(-) diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 2e592c2..e29b213 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -29,3 +29,10 @@ mod lib_func; mod userdata_func; mod userdata; + +#[macro_export] +macro_rules! c_stringify { + ($str: ident) => { + unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($str), "\0").as_ptr() as _) } + }; +} diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index 108be1a..0f77445 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -42,7 +42,7 @@ macro_rules! decl_userdata { )* impl $crate::vm::userdata::UserData for $obj_name { - const CLASS_NAME: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($obj_name), "\0").as_ptr() as _) }; + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); } unsafe impl $crate::vm::userdata::UserDataImmutable for $obj_name {} @@ -65,7 +65,7 @@ macro_rules! decl_userdata_mut { )* impl $crate::vm::userdata::UserData for $obj_name { - const CLASS_NAME: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($obj_name), "\0").as_ptr() as _) }; + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); } }; } diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index d977628..29e2501 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -38,9 +38,10 @@ macro_rules! decl_userdata_func { let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; 0 } - let mut f = $crate::vm::userdata::Function::new(_cfunc); + let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); - //$($(f.arg::<$arg_type>();)*)? + f.arg::<&$obj_name>(); + $($(f.arg::<$arg_type>();)*)? f } } @@ -55,9 +56,10 @@ macro_rules! decl_userdata_func { let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; 0 } - let mut f = $crate::vm::userdata::Function::new(_cfunc); + let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); - //$($(f.arg::<$arg_type>();)*)? + f.arg::<&$obj_name>(); + $($(f.arg::<$arg_type>();)*)? f } } @@ -72,8 +74,9 @@ macro_rules! decl_userdata_func { let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; 0 } - let mut f = $crate::vm::userdata::Function::new(_cfunc); - //$($(f.arg::<$arg_type>();)*)? + let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + f.arg::<&$obj_name>(); + $($(f.arg::<$arg_type>();)*)? f } } @@ -88,8 +91,9 @@ macro_rules! decl_userdata_func { let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; 0 } - let mut f = $crate::vm::userdata::Function::new(_cfunc); - //$($(f.arg::<$arg_type>();)*)? + let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + f.arg::<&$obj_name>(); + $($(f.arg::<$arg_type>();)*)? f } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 34b1210..9a17c94 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -28,14 +28,15 @@ use std::error::Error; use std::slice; -use crate::ffi::laux::luaL_checklstring; -use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Type}; +use crate::ffi::laux::{luaL_checklstring, luaL_checkudata}; +use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::util::{lua_rust_error}; +use crate::vm::userdata::UserData; +use crate::vm::util::{lua_rust_error, LuaType, SimpleDrop}; use crate::vm::Vm; -impl<'a, T: FromParam<'a> + Copy> FromParam<'a> for Option { +impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { let l = vm.as_ptr(); let ty = lua_type(l, index); @@ -47,6 +48,8 @@ impl<'a, T: FromParam<'a> + Copy> FromParam<'a> for Option { } } +impl LuaType for &str {} + impl<'a> FromParam<'a> for &'a str { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { let mut len: usize = 0; @@ -61,6 +64,8 @@ impl<'a> FromParam<'a> for &'a str { } } +unsafe impl SimpleDrop for &str {} + impl IntoParam for &str { fn into_param(self, vm: &Vm) -> u16 { unsafe { @@ -73,6 +78,14 @@ impl IntoParam for &str { macro_rules! impl_integer { ($($t: ty),*) => { $( + unsafe impl SimpleDrop for $t {} + + impl LuaType for $t { + fn lua_type() -> &'static str { + std::any::type_name::() + } + } + impl FromParam<'_> for $t { unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checkinteger(vm.as_ptr(), index) as _ @@ -99,6 +112,14 @@ impl_integer!(i8, u8, i16, u16, i32, u32); macro_rules! impl_float { ($($t: ty),*) => { $( + unsafe impl SimpleDrop for $t {} + + impl LuaType for $t { + fn lua_type() -> &'static str { + std::any::type_name::() + } + } + impl FromParam<'_> for $t { unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checknumber(vm.as_ptr(), index) as _ @@ -158,3 +179,16 @@ impl IntoParam for () { 0 } } + +impl LuaType for &T { + fn lua_type() -> &'static str { + unsafe { T::CLASS_NAME.to_str().unwrap_unchecked() } + } +} + +impl<'a, T: UserData> FromParam<'a> for &'a T { + unsafe fn from_param(vm: &'a Vm, index: i32) -> &'a T { + let obj_ptr = unsafe { luaL_checkudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + unsafe { &*obj_ptr } + } +} diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 0bd1b0f..a049947 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::vm::Vm; -use crate::vm::util::SimpleDrop; +use crate::vm::util::{LuaType, SimpleDrop}; /// This trait represents a function return value. pub trait IntoParam: Sized { @@ -44,7 +44,7 @@ pub trait IntoParam: Sized { } /// This trait represents a function parameter. -pub trait FromParam<'a>: Sized + SimpleDrop { +pub trait FromParam<'a>: Sized + SimpleDrop + LuaType { /// Reads this value from the given lua stack. /// /// # Arguments diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index 9e66e6b..a3a22ef 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -26,22 +26,23 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::any::TypeId; use std::ffi::CStr; use crate::ffi::lua::CFunction; use crate::vm::util::LuaType; pub struct Function { is_mutable: bool, - args: Vec, + args: Vec<&'static str>, + name: &'static CStr, func: CFunction } impl Function { - pub fn new(func: CFunction) -> Function { + pub fn new(name: &'static CStr, func: CFunction) -> Function { Function { is_mutable: false, args: Vec::new(), + name, func } } diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index 2038b91..7991ea4 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -32,9 +32,20 @@ use std::ffi::{CStr, CString}; use crate::ffi::laux::luaL_loadstring; use crate::ffi::lua::{lua_error, lua_pushlstring, State, ThreadStatus}; +pub trait LuaType { + /// Returns the closest rust type matching this lua value. + fn lua_type() -> &'static str { + std::any::type_name::() + } +} + +impl LuaType for Option {} + pub unsafe trait SimpleDrop {} -unsafe impl SimpleDrop for T {} +unsafe impl SimpleDrop for Option {} +unsafe impl SimpleDrop for Result {} +unsafe impl SimpleDrop for &T {} pub unsafe fn lua_rust_error(l: State, error: E) -> ! { // At this point the function is assumed to be a non-POF (error and String). diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index bf80b3e..cc1f7d2 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -32,7 +32,7 @@ use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_p use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; use crate::vm::error::TypeError; -use crate::vm::util::{AnyStr, SimpleDrop}; +use crate::vm::util::{AnyStr, LuaType, SimpleDrop}; use crate::vm::value::{FromLua, IntoLua}; pub struct Table<'a> { @@ -184,3 +184,5 @@ impl IntoParam for Table<'_> { 1 } } + +impl LuaType for Table<'_> {} diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index 37c8501..ec59d42 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::{RootVm}; +use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::table::Table; #[test] From c33896db81c74fe6f1c31b8de436fd056b3e1c9a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 21:25:32 +0100 Subject: [PATCH 057/527] Added support for multi-type LuaType --- core/src/vm/function/core.rs | 16 ++++++++-------- core/src/vm/userdata.rs | 8 +++++--- core/src/vm/util.rs | 17 ++++++++++++++--- 3 files changed, 27 insertions(+), 14 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 9a17c94..2df9881 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -33,7 +33,7 @@ use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pus use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; -use crate::vm::util::{lua_rust_error, LuaType, SimpleDrop}; +use crate::vm::util::{lua_rust_error, LuaType, SimpleDrop, TypeName}; use crate::vm::Vm; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { @@ -48,7 +48,7 @@ impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { } } -impl LuaType for &str {} +impl LuaType for &str { } impl<'a> FromParam<'a> for &'a str { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { @@ -81,8 +81,8 @@ macro_rules! impl_integer { unsafe impl SimpleDrop for $t {} impl LuaType for $t { - fn lua_type() -> &'static str { - std::any::type_name::() + fn lua_type() -> Vec { + vec![TypeName::Some(std::any::type_name::())] } } @@ -115,8 +115,8 @@ macro_rules! impl_float { unsafe impl SimpleDrop for $t {} impl LuaType for $t { - fn lua_type() -> &'static str { - std::any::type_name::() + fn lua_type() -> Vec { + vec![TypeName::Some(std::any::type_name::())] } } @@ -181,8 +181,8 @@ impl IntoParam for () { } impl LuaType for &T { - fn lua_type() -> &'static str { - unsafe { T::CLASS_NAME.to_str().unwrap_unchecked() } + fn lua_type() -> Vec { + vec![TypeName::Some(unsafe { T::CLASS_NAME.to_str().unwrap_unchecked() })] } } diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index a3a22ef..33f5dc3 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -28,11 +28,11 @@ use std::ffi::CStr; use crate::ffi::lua::CFunction; -use crate::vm::util::LuaType; +use crate::vm::util::{LuaType, TypeName}; pub struct Function { is_mutable: bool, - args: Vec<&'static str>, + args: Vec, name: &'static CStr, func: CFunction } @@ -53,7 +53,9 @@ impl Function { } pub fn arg(&mut self) -> &mut Self { - self.args.push(T::lua_type()); + for ty in T::lua_type() { + self.args.push(ty); + } self } } diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index 7991ea4..ef76a72 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -32,14 +32,25 @@ use std::ffi::{CStr, CString}; use crate::ffi::laux::luaL_loadstring; use crate::ffi::lua::{lua_error, lua_pushlstring, State, ThreadStatus}; +pub enum TypeName { + Some(&'static str), + None +} + pub trait LuaType { /// Returns the closest rust type matching this lua value. - fn lua_type() -> &'static str { - std::any::type_name::() + fn lua_type() -> Vec { + vec![TypeName::Some(std::any::type_name::())] } } -impl LuaType for Option {} +impl LuaType for Option { + fn lua_type() -> Vec { + let mut v = T::lua_type(); + v.push(TypeName::None); + v + } +} pub unsafe trait SimpleDrop {} From 0a4a26773c9fda71e71638760e564eca9a21ddee Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 22:26:18 +0100 Subject: [PATCH 058/527] Added initial version of userdata registry. --- core/src/macros/lib_func.rs | 14 +----- core/src/macros/mod.rs | 14 ++++++ core/src/macros/userdata.rs | 16 +++++++ core/src/macros/userdata_func.rs | 40 ++++++++++++----- core/src/vm/userdata.rs | 75 ++++++++++++++++++++++++++++++-- core/src/vm/util.rs | 1 + 6 files changed, 132 insertions(+), 28 deletions(-) diff --git a/core/src/macros/lib_func.rs b/core/src/macros/lib_func.rs index a493105..1ff01f8 100644 --- a/core/src/macros/lib_func.rs +++ b/core/src/macros/lib_func.rs @@ -33,14 +33,9 @@ macro_rules! decl_lib_func { ) => { $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { fn _func($name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code - use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - let mut index = 1; - $($( - let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; - index += 1; - )*)? + $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? let ret = _func(&vm $(, $($arg_name),*)?); ret.into_param(&vm) as _ } @@ -50,14 +45,9 @@ macro_rules! decl_lib_func { ) => { $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code - use $crate::vm::function::FromParam; use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - let mut index = 1; - $( - let $arg_name: $arg_ty = unsafe { FromParam::from_param(&vm, index) }; - index += 1; - )* + $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); let ret = _func($($arg_name),*); ret.into_param(&vm) as _ } diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index e29b213..6e420da 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -36,3 +36,17 @@ macro_rules! c_stringify { unsafe { std::ffi::CStr::from_ptr(concat!(stringify!($str), "\0").as_ptr() as _) } }; } + +#[macro_export] +macro_rules! decl_from_param { + ( + $vm: ident, $start_index: literal, $($arg_name: ident: $arg_ty: ty)* + ) => { + use $crate::vm::function::FromParam; + let mut index = $start_index; + $( + let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, index) }; + index += 1; + )* + } +} diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index 0f77445..df4299d 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -43,6 +43,14 @@ macro_rules! decl_userdata { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); + + fn register(registry: &Registry) -> Result<(), $crate::vm::userdata::Error> { + $( + let (name, func) = unsafe { $obj_name::$fn_name().build()? }; + registry.add_method(name, func); + )* + Ok(()) + } } unsafe impl $crate::vm::userdata::UserDataImmutable for $obj_name {} @@ -66,6 +74,14 @@ macro_rules! decl_userdata_mut { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); + + fn register(registry: &Registry) -> Result<(), $crate::vm::userdata::Error> { + $( + let (name, func) = unsafe { $obj_name::$fn_name().build()? }; + registry.add_method(name, func); + )* + Ok(()) + } } }; } diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 29e2501..72d5017 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -33,10 +33,14 @@ macro_rules! decl_userdata_func { ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { - fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; - 0 + fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &mut *this_ptr }, &vm $(, $($arg_name),*)?); + ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); @@ -51,10 +55,14 @@ macro_rules! decl_userdata_func { ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { - fn _func($this: &mut $obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; - 0 + fn _func($this: &mut $obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &mut *this_ptr }, $(, $($arg_name),*)?); + ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); @@ -69,10 +77,14 @@ macro_rules! decl_userdata_func { ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { - fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; - 0 + fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &*this_ptr }, &vm, $(, $($arg_name),*)?); + ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); @@ -86,10 +98,14 @@ macro_rules! decl_userdata_func { ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { - fn _func($this: &$obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - let self_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 0, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) }; - 0 + fn _func($this: &$obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &*this_ptr }, $(, $($arg_name),*)?); + ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index 33f5dc3..174d1b8 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -27,8 +27,21 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::ffi::CStr; -use crate::ffi::lua::CFunction; +use std::marker::PhantomData; +use bp3d_util::simple_error; +use crate::ffi::laux::luaL_newmetatable; +use crate::ffi::lua::{lua_pushcclosure, lua_setfield, lua_settop, CFunction}; use crate::vm::util::{LuaType, TypeName}; +use crate::vm::Vm; + +simple_error! { + pub Error { + ArgsEmpty => "no arguments specified in userdata function, please add at least one argument matching the type of self", + MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", + Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", + AlreadyRegistered(&'static CStr) => "userdata with class name {:?} has already been registered" + } +} pub struct Function { is_mutable: bool, @@ -58,15 +71,69 @@ impl Function { } self } + + /// Checks and builds this userdata function + /// + /// # Safety + /// + /// All function arguments must be added through the arg function, if not calling this function + /// is considered UB. + pub unsafe fn build(&self) -> Result<(&'static CStr, CFunction), Error> { + if self.args.is_empty() { + return Err(Error::ArgsEmpty); + } + if self.name == c"__gc" { + return Err(Error::Gc); + } + if self.is_mutable { + let initial = &self.args[0]; + for v in self.args.iter().skip(1) { + if initial == v { + return Err(Error::MutViolation(self.name)); + } + } + } + Ok((self.name, self.func)) + } +} + +pub struct Registry<'a, T> { + vm: &'a Vm, + useless: PhantomData, +} + +impl<'a, T: UserData> Registry<'a, T> { + pub fn new(vm: &'a Vm) -> Result { + let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + if res != 1 { + return Err(Error::AlreadyRegistered(T::CLASS_NAME)); + } + Ok(Registry { vm, useless: PhantomData }) + } + + pub fn add_method(&self, name: &'static CStr, func: CFunction) { + unsafe { + lua_pushcclosure(self.vm.as_ptr(), func, 0); + lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); + } + } +} + +impl<'a, T> Drop for Registry<'a, T> { + fn drop(&mut self) { + unsafe { + // Pop the userdata metatable from the stack. + lua_settop(self.vm.as_ptr(), -2); + } + } } pub trait UserData: Sized { const CLASS_NAME: &'static CStr; - //fn register(registry: &Registry); + fn register(registry: &Registry) -> Result<(), Error>; } //TODO: Implement FromLua on UserData only when that userdata is also UserDataImmutable -//TODO: most likely need another luajit hack to allow returning errors from luaL_checkudata instead -// of unwinding +//TODO: luaL_testudata to avoid unwinding with luaL_checkudata pub unsafe trait UserDataImmutable: UserData {} diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index ef76a72..463191a 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -32,6 +32,7 @@ use std::ffi::{CStr, CString}; use crate::ffi::laux::luaL_loadstring; use crate::ffi::lua::{lua_error, lua_pushlstring, State, ThreadStatus}; +#[derive(Debug, PartialEq, Eq)] pub enum TypeName { Some(&'static str), None From 2088d673ae0c35a5381b56028da19bb20d624ceb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 22:36:46 +0100 Subject: [PATCH 059/527] Added userdata registry function --- core/src/vm/core.rs | 7 +++++++ core/src/vm/error.rs | 3 ++- core/src/vm/userdata.rs | 18 +++++++++++++++--- 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 5a74af5..7e139b4 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -31,6 +31,7 @@ use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; use crate::vm::error::{Error, RuntimeError}; +use crate::vm::userdata::{Registry, UserData}; use crate::vm::util::{AnyStr, LoadCode}; use crate::vm::value::{FromLua, IntoLua}; @@ -75,6 +76,12 @@ impl Vm { } } + pub fn register_userdata(&self) -> crate::vm::Result<()> { + let reg = unsafe { Registry::::new(self) }.map_err(Error::UserData)?; + T::register(®).map_err(Error::UserData)?; + Ok(()) + } + /// Returns the absolute stack index for the given index. #[inline] pub fn absolute(&self, index: i32) -> i32 { diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index c42508f..c046037 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -87,7 +87,8 @@ simple_error! { Unknown => "unknown error", Error => "error in error handler", Null => "string contains a null character", - MultiValue => "only one value is supported by this API" + MultiValue => "only one value is supported by this API", + UserData(crate::vm::userdata::Error) => "userdata: {}" } } diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index 174d1b8..eb8205c 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -36,10 +36,10 @@ use crate::vm::Vm; simple_error! { pub Error { - ArgsEmpty => "no arguments specified in userdata function, please add at least one argument matching the type of self", + ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", - AlreadyRegistered(&'static CStr) => "userdata with class name {:?} has already been registered" + AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered" } } @@ -103,7 +103,19 @@ pub struct Registry<'a, T> { } impl<'a, T: UserData> Registry<'a, T> { - pub fn new(vm: &'a Vm) -> Result { + /// Creates a new [Registry] from the given Vm. + /// + /// # Arguments + /// + /// * `vm`: the vm in which to register the userdata metatable. + /// + /// returns: Result, Error> + /// + /// # Safety + /// + /// Running operations on the vm after calling this method is UB unless this [Registry] object + /// is dropped. + pub unsafe fn new(vm: &'a Vm) -> Result { let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; if res != 1 { return Err(Error::AlreadyRegistered(T::CLASS_NAME)); From 60c5e744bd86086960399bce28694d01ed0737ec Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 22:39:28 +0100 Subject: [PATCH 060/527] Added check for alignment --- core/src/vm/userdata.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index eb8205c..de867f3 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -39,7 +39,8 @@ simple_error! { ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", - AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered" + AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", + Alignment(usize) => "too strict alignment required ({} bytes), max is 8 bytes" } } @@ -120,6 +121,9 @@ impl<'a, T: UserData> Registry<'a, T> { if res != 1 { return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } + if align_of::() > 8 { + return Err(Error::Alignment(align_of::())); + } Ok(Registry { vm, useless: PhantomData }) } From 8750c465227b99ee9505f26fddc6b1741c316545 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 22:45:20 +0100 Subject: [PATCH 061/527] Added FromLua implementation on UserDataImmutable --- core/src/ffi/laux.rs | 1 + core/src/vm/userdata.rs | 2 -- core/src/vm/value/core.rs | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/core/src/ffi/laux.rs b/core/src/ffi/laux.rs index ae6253e..290eb35 100644 --- a/core/src/ffi/laux.rs +++ b/core/src/ffi/laux.rs @@ -66,6 +66,7 @@ extern "C" { pub fn luaL_checkany(l: State, narg: c_int); pub fn luaL_checkudata(l: State, ud: c_int, tname: *const c_char) -> *mut c_void; + pub fn luaL_testudata(l: State, ud: c_int, tname: *const c_char) -> *mut c_void; } //------------------------ diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index de867f3..89aaefe 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -150,6 +150,4 @@ pub trait UserData: Sized { fn register(registry: &Registry) -> Result<(), Error>; } -//TODO: Implement FromLua on UserData only when that userdata is also UserDataImmutable -//TODO: luaL_testudata to avoid unwinding with luaL_checkudata pub unsafe trait UserDataImmutable: UserData {} diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 43f1cc5..bc28062 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -26,10 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_pushcclosure}; use crate::vm::function::IntoParam; use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; +use crate::vm::userdata::UserDataImmutable; use crate::vm::value::{FromLua, IntoLua, RFunction}; impl<'a> FromLua<'a> for &'a str { @@ -116,3 +118,16 @@ impl FromLua<'_> for () { 0 } } + +impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let this_ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + if this_ptr.is_null() { + return Err(Error::Type(TypeError { + expected: Type::Userdata, + actual: unsafe { lua_type(vm.as_ptr(), index) }, + })); + } + Ok(unsafe { &*this_ptr }) + } +} From 886a42c002542b648a959bba3fcd876a7344a506 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 23:13:30 +0100 Subject: [PATCH 062/527] Fixed various bugs in userdata generation macros and added initial test --- core/src/macros/userdata.rs | 8 ++-- core/src/macros/userdata_func.rs | 30 +++++++-------- core/tests/test_vm_userdata.rs | 64 ++++++++++++++++++++++++++++++++ 3 files changed, 83 insertions(+), 19 deletions(-) create mode 100644 core/tests/test_vm_userdata.rs diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index df4299d..b791c59 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -31,20 +31,20 @@ macro_rules! decl_userdata { ( impl $obj_name: ident { $( - $vis: vis fn $fn_name: ident(this: &$obj_name2: ident$($tokens: tt)*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident($this: ident: &$obj_name2: ident $($tokens: tt)*) -> $ret_ty: ty $code: block )* } ) => { $( $crate::decl_userdata_func! { - $vis fn $fn_name(this: &$obj_name$($tokens)*) -> $ret_ty $code + $vis fn $fn_name($this: &$obj_name $($tokens)*) -> $ret_ty $code } )* impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::Registry) -> Result<(), $crate::vm::userdata::Error> { $( let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); @@ -75,7 +75,7 @@ macro_rules! decl_userdata_mut { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::Registry) -> Result<(), $crate::vm::userdata::Error> { $( let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 72d5017..895d2b3 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -29,12 +29,12 @@ #[macro_export] macro_rules! decl_userdata_func { ( - $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; @@ -45,71 +45,71 @@ macro_rules! decl_userdata_func { let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); - $($(f.arg::<$arg_type>();)*)? + $($(f.arg::<$arg_ty>();)*)? f } } }; ( - $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &mut $obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + fn _func($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &mut *this_ptr }, $(, $($arg_name),*)?); + let ret = _func(unsafe { &mut *this_ptr } $(, $($arg_name),*)?); ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); - $($(f.arg::<$arg_type>();)*)? + $($(f.arg::<$arg_ty>();)*)? f } } }; ( - $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &*this_ptr }, &vm, $(, $($arg_name),*)?); + let ret = _func(unsafe { &*this_ptr }, &vm $(, $($arg_name),*)?); ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); - $($(f.arg::<$arg_type>();)*)? + $($(f.arg::<$arg_ty>();)*)? f } } }; ( - $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_type: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &$obj_name$(, $($arg_name: $arg_type),*)?) -> $ret_ty $code + fn _func($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &*this_ptr }, $(, $($arg_name),*)?); + let ret = _func(unsafe { &*this_ptr } $(, $($arg_name),*)?); ret.into_param(&vm) as _ } let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); - $($(f.arg::<$arg_type>();)*)? + $($(f.arg::<$arg_ty>();)*)? f } } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs new file mode 100644 index 0000000..6e7af22 --- /dev/null +++ b/core/tests/test_vm_userdata.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::decl_userdata; +use bp3d_lua::ffi::lua::Number; +use bp3d_lua::vm::RootVm; + +pub struct MyInt(i64); + +decl_userdata! { + impl MyInt { + fn tonumber(this: &MyInt) -> Number { + this.0 as _ + } + + fn tostring(this: &MyInt) -> String { + this.0.to_string() + } + + fn eq(this: &MyInt, other: &MyInt) -> bool { + this.0 == other.0 + } + + fn lt(this: &MyInt, other: &MyInt) -> bool { + this.0 < other.0 + } + + fn gt(this: &MyInt, other: &MyInt) -> bool { + this.0 > other.0 + } + } +} + +#[test] +fn test_vm_userdata() { + let vm = RootVm::new(); + vm.register_userdata::().unwrap() + +} From 8b0a0d7a69739db40c272be59e2f39933a15bf1d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 10 Mar 2025 23:22:58 +0100 Subject: [PATCH 063/527] Added error handling tests --- core/tests/test_vm_userdata.rs | 51 ++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 6e7af22..ee19609 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::decl_userdata; +use bp3d_lua::{decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::RootVm; @@ -56,9 +56,54 @@ decl_userdata! { } } +#[derive(Debug)] +pub struct BrokenObject; + +decl_userdata_mut! { + impl BrokenObject { + // this should blow up at init time + fn replace(this: &mut BrokenObject, other: &BrokenObject) -> () { + println!("this: {:?}, other: {:?}", this, other) + } + } +} + +pub struct BrokenObject2(pub u128); + +decl_userdata! { + impl BrokenObject2 { + } +} + +#[derive(Debug)] +pub struct BrokenObject3; + +decl_userdata! { + impl BrokenObject3 { + fn __gc(this: &BrokenObject3) -> () { + println!("{:?}", this); + } + } +} + #[test] fn test_vm_userdata() { let vm = RootVm::new(); - vm.register_userdata::().unwrap() - + vm.register_userdata::().unwrap(); + let res = vm.register_userdata::(); + assert!(res.is_err()); + let msg = res.unwrap_err().to_string(); + assert_eq!(msg, "userdata: violation of the unique type rule for mutable method \"replace\""); + let res = vm.register_userdata::(); + assert!(res.is_err()); + let msg = res.unwrap_err().to_string(); + assert_eq!(msg, "userdata: too strict alignment required (16 bytes), max is 8 bytes"); + let res = vm.register_userdata::(); + assert!(res.is_err()); + let msg = res.unwrap_err().to_string(); + assert_eq!(msg, "userdata: __gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop"); + let res = vm.register_userdata::(); + assert!(res.is_err()); + let msg = res.unwrap_err().to_string(); + assert_eq!(msg, "userdata: class name \"MyInt\" has already been registered"); } From c7a07c32890f46b104dda17a238508e68c94b1fb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 11 Mar 2025 21:13:49 +0100 Subject: [PATCH 064/527] Fixed bug with userdata registration, added support for __gc to call Drop and improved userdata tests --- core/src/ffi/laux.rs | 1 + core/src/vm/core.rs | 15 ++++++++--- core/src/vm/function/core.rs | 13 +++++++-- core/src/vm/userdata.rs | 29 ++++++++++++++++---- core/tests/test_vm_userdata.rs | 48 +++++++++++++++++++++++++++++++--- 5 files changed, 92 insertions(+), 14 deletions(-) diff --git a/core/src/ffi/laux.rs b/core/src/ffi/laux.rs index 290eb35..15ad1bd 100644 --- a/core/src/ffi/laux.rs +++ b/core/src/ffi/laux.rs @@ -74,6 +74,7 @@ extern "C" { //------------------------ extern "C" { pub fn luaL_newmetatable(l: State, tname: *const c_char) -> c_int; + pub fn luaL_setmetatable(l: State, tname: *const c_char); pub fn luaL_getmetafield(l: State, obj: c_int, e: *const c_char) -> c_int; pub fn luaL_callmeta(l: State, obj: c_int, e: *const c_char) -> c_int; } diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 7e139b4..d798c81 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -29,7 +29,7 @@ use std::ffi::c_int; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX}; +use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::userdata::{Registry, UserData}; use crate::vm::util::{AnyStr, LoadCode}; @@ -78,8 +78,17 @@ impl Vm { pub fn register_userdata(&self) -> crate::vm::Result<()> { let reg = unsafe { Registry::::new(self) }.map_err(Error::UserData)?; - T::register(®).map_err(Error::UserData)?; - Ok(()) + let res = T::register(®).map_err(Error::UserData); + match res { + Ok(_) => Ok(()), + Err(e) => { + unsafe { + lua_pushnil(self.l); + lua_setfield(self.l, REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + } + Err(e) + } + } } /// Returns the absolute stack index for the given index. diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 2df9881..18036db 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -28,8 +28,8 @@ use std::error::Error; use std::slice; -use crate::ffi::laux::{luaL_checklstring, luaL_checkudata}; -use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; +use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable}; +use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; @@ -192,3 +192,12 @@ impl<'a, T: UserData> FromParam<'a> for &'a T { unsafe { &*obj_ptr } } } + +impl IntoParam for T { + fn into_param(self, vm: &Vm) -> u16 { + let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; + unsafe { userdata.write(self) }; + unsafe { luaL_setmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + 1 + } +} diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata.rs index 89aaefe..a251ca7 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata.rs @@ -29,8 +29,8 @@ use std::ffi::CStr; use std::marker::PhantomData; use bp3d_util::simple_error; -use crate::ffi::laux::luaL_newmetatable; -use crate::ffi::lua::{lua_pushcclosure, lua_setfield, lua_settop, CFunction}; +use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; +use crate::ffi::lua::{lua_pushcclosure, lua_pushvalue, lua_setfield, lua_settop, CFunction, State}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::Vm; @@ -39,6 +39,7 @@ simple_error! { ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", + Index => "__index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index", AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", Alignment(usize) => "too strict alignment required ({} bytes), max is 8 bytes" } @@ -86,6 +87,9 @@ impl Function { if self.name == c"__gc" { return Err(Error::Gc); } + if self.name == c"__index" { + return Err(Error::Index); + } if self.is_mutable { let initial = &self.args[0]; for v in self.args.iter().skip(1) { @@ -117,14 +121,24 @@ impl<'a, T: UserData> Registry<'a, T> { /// Running operations on the vm after calling this method is UB unless this [Registry] object /// is dropped. pub unsafe fn new(vm: &'a Vm) -> Result { + if align_of::() > 8 { + return Err(Error::Alignment(align_of::())); + } let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; if res != 1 { + unsafe { lua_settop(vm.as_ptr(), -2) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } - if align_of::() > 8 { - return Err(Error::Alignment(align_of::())); + let reg = Registry { vm, useless: PhantomData }; + if std::mem::needs_drop::() { + extern "C-unwind" fn run_drop(l: State) -> i32 { + let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; + unsafe { std::ptr::drop_in_place(udata) }; + 0 + } + reg.add_method(c"__gc", run_drop::); } - Ok(Registry { vm, useless: PhantomData }) + Ok(reg) } pub fn add_method(&self, name: &'static CStr, func: CFunction) { @@ -138,6 +152,8 @@ impl<'a, T: UserData> Registry<'a, T> { impl<'a, T> Drop for Registry<'a, T> { fn drop(&mut self) { unsafe { + lua_pushvalue(self.vm.as_ptr(), -1); + lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); // Pop the userdata metatable from the stack. lua_settop(self.vm.as_ptr(), -2); } @@ -151,3 +167,6 @@ pub trait UserData: Sized { } pub unsafe trait UserDataImmutable: UserData {} + +//TODO: implement __gc for Drop (std::mem::drop_in_place) and LuaDrop +//TODO: only implement __gc for std::mem::needs_drop return false \ No newline at end of file diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index ee19609..bdbc742 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -26,9 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::{decl_userdata, decl_userdata_mut}; +use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::RFunction; pub struct MyInt(i64); @@ -42,17 +43,21 @@ decl_userdata! { this.0.to_string() } - fn eq(this: &MyInt, other: &MyInt) -> bool { + fn __eq(this: &MyInt, other: &MyInt) -> bool { this.0 == other.0 } - fn lt(this: &MyInt, other: &MyInt) -> bool { + fn __lt(this: &MyInt, other: &MyInt) -> bool { this.0 < other.0 } - fn gt(this: &MyInt, other: &MyInt) -> bool { + fn __gt(this: &MyInt, other: &MyInt) -> bool { this.0 > other.0 } + + fn __add(this: &MyInt, other: &MyInt) -> MyInt { + MyInt(this.0 + other.0) + } } } @@ -86,24 +91,59 @@ decl_userdata! { } } +decl_lib_func! { + fn my_int(i: i64) -> MyInt { + MyInt(i) + } +} + +#[test] +fn test_vm_userdata_forgot_reg() { + let vm = RootVm::new(); + vm.set_global(c"MyInt", RFunction(my_int)).unwrap(); + vm.run_code::<()>(c"a = MyInt(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt(456)").unwrap(); + assert!(vm.run_code::(c"return a < b").is_err()); + assert!(vm.run_code::(c"return a + b").is_err()); +} + #[test] fn test_vm_userdata() { let vm = RootVm::new(); + let top = vm.top(); vm.register_userdata::().unwrap(); + assert_eq!(top, vm.top()); let res = vm.register_userdata::(); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: violation of the unique type rule for mutable method \"replace\""); + assert_eq!(top, vm.top()); let res = vm.register_userdata::(); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: too strict alignment required (16 bytes), max is 8 bytes"); + assert_eq!(top, vm.top()); let res = vm.register_userdata::(); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: __gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop"); + assert_eq!(top, vm.top()); let res = vm.register_userdata::(); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: class name \"MyInt\" has already been registered"); + assert_eq!(top, vm.top()); + vm.set_global(c"MyInt", RFunction(my_int)).unwrap(); + assert_eq!(top, vm.top()); + vm.run_code::<()>(c"a = MyInt(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt(456)").unwrap(); + vm.run_code::<()>(c"c = MyInt(456)").unwrap(); + assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); + assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); + assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); + assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); + assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); + assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); + assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); + assert_eq!(top + 7, vm.top()); } From 9919dcc0b2729c4b0aac20c84ee45a649fb39e5e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 11 Mar 2025 21:30:00 +0100 Subject: [PATCH 065/527] Refactored test_vm_userdata and added more error checks --- core/tests/test_vm_userdata.rs | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index bdbc742..f46cf9a 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -91,6 +91,17 @@ decl_userdata! { } } +#[derive(Debug)] +pub struct BrokenObject4; + +decl_userdata! { + impl BrokenObject4 { + fn __index(this: &BrokenObject3) -> () { + println!("{:?}", this); + } + } +} + decl_lib_func! { fn my_int(i: i64) -> MyInt { MyInt(i) @@ -108,7 +119,7 @@ fn test_vm_userdata_forgot_reg() { } #[test] -fn test_vm_userdata() { +fn test_vm_userdata_error_handling() { let vm = RootVm::new(); let top = vm.top(); vm.register_userdata::().unwrap(); @@ -133,6 +144,19 @@ fn test_vm_userdata() { let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: class name \"MyInt\" has already been registered"); assert_eq!(top, vm.top()); + let res = vm.register_userdata::(); + assert!(res.is_err()); + let msg = res.unwrap_err().to_string(); + assert_eq!(msg, "userdata: __index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index"); + assert_eq!(top, vm.top()); +} + +#[test] +fn test_vm_userdata() { + let vm = RootVm::new(); + let top = vm.top(); + vm.register_userdata::().unwrap(); + assert_eq!(top, vm.top()); vm.set_global(c"MyInt", RFunction(my_int)).unwrap(); assert_eq!(top, vm.top()); vm.run_code::<()>(c"a = MyInt(123)").unwrap(); From 4e38da9c216b4d364d49452dca6f8944f247ebd5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 11 Mar 2025 21:35:48 +0100 Subject: [PATCH 066/527] Renamed absolute to get_absolute_index --- core/src/vm/core.rs | 2 +- core/src/vm/value/table.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index d798c81..4ea671c 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -93,7 +93,7 @@ impl Vm { /// Returns the absolute stack index for the given index. #[inline] - pub fn absolute(&self, index: i32) -> i32 { + pub fn get_absolute_index(&self, index: i32) -> i32 { if index < 0 { unsafe { lua_gettop(self.l) + index + 1 } } else { diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index cc1f7d2..0f836c3 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -163,7 +163,7 @@ impl<'a> FromParam<'a> for Table<'a> { impl<'a> FromLua<'a> for Table<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let ty = unsafe { lua_type(vm.as_ptr(), index) }; - let index = vm.absolute(index); + let index = vm.get_absolute_index(index); if ty == Type::Table { Ok(Table { vm, index }) } else { From 8fccdd7c78aa8f101e899a3f9ed435c2bde3e956 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 11 Mar 2025 22:14:29 +0100 Subject: [PATCH 067/527] Added impl_from_param which auto-implements FromParam on structures using lua tables --- core/src/macros/impl_from_param.rs | 100 +++++++++++++++++++++++++++++ core/src/macros/mod.rs | 1 + 2 files changed, 101 insertions(+) create mode 100644 core/src/macros/impl_from_param.rs diff --git a/core/src/macros/impl_from_param.rs b/core/src/macros/impl_from_param.rs new file mode 100644 index 0000000..0fe9e2e --- /dev/null +++ b/core/src/macros/impl_from_param.rs @@ -0,0 +1,100 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! _impl_from_param_struct { + ( + $name:ident <$life: lifetime> { + $($value_name: ident: $value_ty: ty),* + } + ) => { + impl<$life> $crate::vm::function::FromParam<$life> for $name<$life> { + unsafe fn from_param(vm: &'a $crate::vm::Vm, index: i32) -> Self { + unsafe { $crate::ffi::laux::luaL_checktype(vm.as_ptr(), index, $crate::ffi::lua::Type::Table); } + let mut top = vm.top(); + $( + unsafe { $crate::ffi::lua::lua_getfield(vm.as_ptr(), index, $crate::c_stringify!($value_name).as_ptr()); } + top += 1; + let $value_name = unsafe { <$value_ty as $crate::vm::function::FromParam>::from_param(vm, top) }; + )* + Self { $($value_name),* } + } + } + }; + ( + $name:ident { + $($value_name: ident: $value_ty: ty),* + } + ) => { + impl<'a> $crate::vm::function::FromParam<'a> for $name { + unsafe fn from_param(vm: &'a $crate::vm::Vm, index: i32) -> Self { + unsafe { $crate::ffi::laux::luaL_checktype(vm.as_ptr(), index, $crate::ffi::lua::Type::Table); } + let mut top = vm.top(); + $( + unsafe { $crate::ffi::lua::lua_getfield(vm.as_ptr(), index, $crate::c_stringify!($value_name).as_ptr()); } + top += 1; + let $value_name = unsafe { <$value_ty as $crate::vm::function::FromParam>::from_param(vm, top) }; + )* + Self { $($value_name),* } + } + } + }; +} + +#[macro_export] +macro_rules! impl_from_param { + ( + $vis: vis struct $name:ident $(<$life: lifetime>)? { + $($value_vis: vis $value_name: ident: $value_ty: ty),* + } + ) => { + $vis struct $name $(<$life>)? { + $($value_vis $value_name: $value_ty),* + } + + unsafe impl$(<$life>)? $crate::vm::util::SimpleDrop for $name$(<$life>)? {} + + impl$(<$life>)? $crate::vm::util::LuaType for $name$(<$life>)? { + fn lua_type() -> Vec<$crate::vm::util::TypeName> { + let mut ret = Vec::new(); + $( + for v in <$value_ty as $crate::vm::util::LuaType>::lua_type() { + ret.push(v); + } + )* + ret + } + } + + $crate::_impl_from_param_struct! { + $name $(<$life>)? { + $($value_name: $value_ty),* + } + } + }; +} diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 6e420da..0462cfe 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -29,6 +29,7 @@ mod lib_func; mod userdata_func; mod userdata; +mod impl_from_param; #[macro_export] macro_rules! c_stringify { From 07c5c7a2189dbfac18b48f792c9c17dbab19546c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 12 Mar 2025 21:28:17 +0100 Subject: [PATCH 068/527] Added various extensions to LuaJIT to allow changing the global JIT state from Rust rather than Lua. --- core/src/ffi/ext.rs | 14 +++ patch/lib_init.patch | 10 +- patch/lj_disable_jit.patch | 206 ++++++++++++++++++------------------- 3 files changed, 123 insertions(+), 107 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index 1582a85..95a2e59 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -45,3 +45,17 @@ extern "C" { extern "C" { pub fn lua_ext_tab_len(l: State, idx: c_int, outsize: *mut MSize) -> c_int; } + +//----- +// JIT +//----- +extern "C" { + /// Sets the global mode of the JIT. + pub fn lua_ext_setjitmode(l: State, mode: c_int) -> c_int; + + /// Returns global flags of the JIT. + pub fn lua_ext_getjitflags(l: State) -> u32; + + /// Sets global JIT flags. + pub fn lua_ext_setjitflags(l: State, flags: u32) -> c_int; +} diff --git a/patch/lib_init.patch b/patch/lib_init.patch index 88fae6a..3bed121 100644 --- a/patch/lib_init.patch +++ b/patch/lib_init.patch @@ -1,10 +1,12 @@ diff --git a/src/lib_init.c b/src/lib_init.c -index 01cecf2f..0cfb3dbf 100644 +index 01cecf2f..8ed34a94 100644 --- a/src/lib_init.c +++ b/src/lib_init.c -@@ -19,23 +19,13 @@ static const luaL_Reg lj_lib_load[] = { +@@ -17,25 +17,14 @@ + + static const luaL_Reg lj_lib_load[] = { { "", luaopen_base }, - { LUA_LOADLIBNAME, luaopen_package }, +- { LUA_LOADLIBNAME, luaopen_package }, { LUA_TABLIBNAME, luaopen_table }, - { LUA_IOLIBNAME, luaopen_io }, - { LUA_OSLIBNAME, luaopen_os }, @@ -26,7 +28,7 @@ index 01cecf2f..0cfb3dbf 100644 LUALIB_API void luaL_openlibs(lua_State *L) { const luaL_Reg *lib; -@@ -44,12 +34,5 @@ LUALIB_API void luaL_openlibs(lua_State *L) +@@ -44,12 +33,5 @@ LUALIB_API void luaL_openlibs(lua_State *L) lua_pushstring(L, lib->name); lua_call(L, 1, 0); } diff --git a/patch/lj_disable_jit.patch b/patch/lj_disable_jit.patch index ec3fe2e..7c90949 100644 --- a/patch/lj_disable_jit.patch +++ b/patch/lj_disable_jit.patch @@ -1,14 +1,11 @@ diff --git a/src/lib_jit.c b/src/lib_jit.c -index fd8e585b..d22b8332 100644 +index fd8e585b..882f4425 100644 --- a/src/lib_jit.c +++ b/src/lib_jit.c -@@ -40,582 +40,8 @@ - - #define LJLIB_MODULE_jit - --static int setjitmode(lua_State *L, int mode) --{ -- int idx = 0; +@@ -43,19 +43,15 @@ + static int setjitmode(lua_State *L, int mode) + { + int idx = 0; - if (L->base == L->top || tvisnil(L->base)) { /* jit.on/off/flush([nil]) */ - mode |= LUAJIT_MODE_ENGINE; - } else { @@ -22,37 +19,48 @@ index fd8e585b..d22b8332 100644 - else - mode |= LUAJIT_MODE_FUNC; - } -- if (luaJIT_setmode(L, idx, mode) != 1) { -- if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE) -- lj_err_caller(L, LJ_ERR_NOJIT); -- err: -- lj_err_argt(L, 1, LUA_TFUNCTION); -- } -- return 0; --} -- --LJLIB_CF(jit_on) --{ -- return setjitmode(L, LUAJIT_MODE_ON); --} -- --LJLIB_CF(jit_off) --{ -- return setjitmode(L, LUAJIT_MODE_OFF); --} -- --LJLIB_CF(jit_flush) --{ --#if LJ_HASJIT -- if (L->base < L->top && tvisnumber(L->base)) { -- int traceno = lj_lib_checkint(L, 1); -- luaJIT_setmode(L, traceno, LUAJIT_MODE_FLUSH|LUAJIT_MODE_TRACE); -- return 0; -- } --#endif -- return setjitmode(L, LUAJIT_MODE_FLUSH); --} -- ++ /* jit.on/off/flush(func|proto, nil|true|false) */ ++ if (tvisfunc(L->base) || tvisproto(L->base)) ++ idx = 1; ++ else if (!tvistrue(L->base)) /* jit.on/off/flush(true, nil|true|false) */ ++ goto err; ++ if (L->base+1 < L->top && tvisbool(L->base+1)) ++ mode |= boolV(L->base+1) ? LUAJIT_MODE_ALLFUNC : LUAJIT_MODE_ALLSUBFUNC; ++ else ++ mode |= LUAJIT_MODE_FUNC; + if (luaJIT_setmode(L, idx, mode) != 1) { + if ((mode & LUAJIT_MODE_MASK) == LUAJIT_MODE_ENGINE) + lj_err_caller(L, LJ_ERR_NOJIT); +@@ -67,16 +63,25 @@ static int setjitmode(lua_State *L, int mode) + + LJLIB_CF(jit_on) + { ++ if (L->base == L->top || tvisnil(L->base)) { ++ luaL_error(L, "attempt to apply global jit state, this is forbidden by sandbox"); ++ } + return setjitmode(L, LUAJIT_MODE_ON); + } + + LJLIB_CF(jit_off) + { ++ if (L->base == L->top || tvisnil(L->base)) { ++ luaL_error(L, "attempt to apply global jit state, this is forbidden by sandbox"); ++ } + return setjitmode(L, LUAJIT_MODE_OFF); + } + + LJLIB_CF(jit_flush) + { ++ if (L->base == L->top || tvisnil(L->base)) { ++ luaL_error(L, "attempt to apply global jit state, this is forbidden by sandbox"); ++ } + #if LJ_HASJIT + if (L->base < L->top && tvisnumber(L->base)) { + int traceno = lj_lib_checkint(L, 1); +@@ -87,535 +92,29 @@ LJLIB_CF(jit_flush) + return setjitmode(L, LUAJIT_MODE_FLUSH); + } + -#if LJ_HASJIT -/* Push a string for every flag bit that is set. */ -static void flagbits_to_strings(lua_State *L, uint32_t flags, uint32_t base, @@ -117,10 +125,12 @@ index fd8e585b..d22b8332 100644 -LJLIB_PUSH(top-5) LJLIB_SET(os) -LJLIB_PUSH(top-4) LJLIB_SET(arch) -LJLIB_PUSH(top-3) LJLIB_SET(version_num) --LJLIB_PUSH(top-2) LJLIB_SET(version) -- --#include "lj_libdef.h" -- ++LJLIB_PUSH(top-4) LJLIB_SET(os) ++LJLIB_PUSH(top-3) LJLIB_SET(arch) + LJLIB_PUSH(top-2) LJLIB_SET(version) + + #include "lj_libdef.h" + -/* -- jit.util.* functions ------------------------------------------------ */ - -#define LJLIB_MODULE_jit_util @@ -173,7 +183,8 @@ index fd8e585b..d22b8332 100644 - } - return 1; -} -- ++/* -- JIT compiler configuration ----------------------------------------- */ + -/* local ins, m = jit.util.funcbc(func, pc) */ -LJLIB_CF(jit_util_funcbc) -{ @@ -189,8 +200,10 @@ index fd8e585b..d22b8332 100644 - return 2; - } - return 0; --} -- ++uint32_t lua_ext_getjitflags(lua_State *L, uint32_t flags) { ++ return L2J(L)->flags; + } + -/* local k = jit.util.funck(func, idx) */ -LJLIB_CF(jit_util_funck) -{ @@ -200,7 +213,13 @@ index fd8e585b..d22b8332 100644 - if (idx < (ptrdiff_t)pt->sizekn) { - copyTV(L, L->top-1, proto_knumtv(pt, idx)); - return 1; -- } ++int lua_ext_setjitflags(lua_State *L, uint32_t flags) { ++ jit_State *J = L2J(L); ++ uint32_t current = J->flags; ++ if ((current & JIT_F_ON) != (flags & JIT_F_ON)) { ++ // we have attempted to change the global JIT mode outside of the setjitmode ext function, error. ++ return -1; + } - } else { - if (~idx < (ptrdiff_t)pt->sizekgc) { - GCobj *gc = proto_kgc(pt, idx); @@ -379,8 +398,8 @@ index fd8e585b..d22b8332 100644 - -#endif - - #include "lj_libdef.h" - +-#include "lj_libdef.h" +- -static int luaopen_jit_util(lua_State *L) -{ - LJ_LIB_REG(L, NULL, jit_util); @@ -578,22 +597,22 @@ index fd8e585b..d22b8332 100644 -{ - LJ_LIB_REG(L, NULL, jit_profile); - return 1; --} -- ++ J->flags = flags; ++ return 0; + } + -#endif - /* -- JIT compiler initialization ----------------------------------------- */ #if LJ_HASJIT -@@ -723,22 +149,6 @@ LUALIB_API int luaopen_jit(lua_State *L) - #if LJ_HASJIT - jit_init(L); +@@ -725,20 +224,9 @@ LUALIB_API int luaopen_jit(lua_State *L) #endif -- lua_pushliteral(L, LJ_OS_NAME); -- lua_pushliteral(L, LJ_ARCH_NAME); + lua_pushliteral(L, LJ_OS_NAME); + lua_pushliteral(L, LJ_ARCH_NAME); - lua_pushinteger(L, LUAJIT_VERSION_NUM); /* Deprecated. */ -- lua_pushliteral(L, LUAJIT_VERSION); -- LJ_LIB_REG(L, LUA_JITLIBNAME, jit); + lua_pushliteral(L, LUAJIT_VERSION); + LJ_LIB_REG(L, LUA_JITLIBNAME, jit); -#if LJ_HASPROFILE - lj_lib_prereg(L, LUA_JITLIBNAME ".profile", luaopen_jit_profile, - tabref(L->env)); @@ -605,55 +624,36 @@ index fd8e585b..d22b8332 100644 - LJ_LIB_REG(L, "jit.opt", jit_opt); -#endif - L->top -= 2; ++ L->top -= 1; return 1; } -diff --git a/src/lj_ffrecord.c b/src/lj_ffrecord.c -index 9ea81e54..292e27dc 100644 ---- a/src/lj_ffrecord.c -+++ b/src/lj_ffrecord.c -@@ -173,7 +173,7 @@ static void LJ_FASTCALL recff_nyi(jit_State *J, RecordFFData *rd) - switch (J->fn->c.ffid) { - case FF_error: - case FF_debug_sethook: -- case FF_jit_flush: -+ //case FF_jit_flush: - break; /* Don't stitch across special builtins. */ - default: - recff_stitch(J); /* Use trace stitching. */ -diff --git a/src/luajit.c b/src/luajit.c -index a725db1c..5095ae36 100644 ---- a/src/luajit.c -+++ b/src/luajit.c -@@ -134,24 +134,15 @@ static void print_version(void) - fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout); +diff --git a/src/lj_dispatch.c b/src/lj_dispatch.c +index 78608316..94f0077e 100644 +--- a/src/lj_dispatch.c ++++ b/src/lj_dispatch.c +@@ -241,6 +241,24 @@ static void setptmode_all(global_State *g, GCproto *pt, int mode) } + #endif -+#include "lj_dispatch.h" -+#include "lj_jit.h" ++int lua_ext_setjitmode(lua_State *L, int mode) { ++ global_State *g = G(L); ++ lj_trace_abort(g); /* Abort recording on any state change. */ ++ /* Avoid pulling the rug from under our own feet. */ ++ if ((g->hookmask & HOOK_GC)) ++ return -1; ++ if ((mode & LUAJIT_MODE_FLUSH)) { ++ lj_trace_flushall(L); ++ } else { ++ if (!(mode & LUAJIT_MODE_ON)) ++ G2J(g)->flags &= ~(uint32_t)JIT_F_ON; ++ else ++ G2J(g)->flags |= (uint32_t)JIT_F_ON; ++ lj_dispatch_update(g); ++ } ++ return 0; ++} + - static void print_jit_status(lua_State *L) + /* Public API function: control the JIT engine. */ + int luaJIT_setmode(lua_State *L, int idx, int mode) { -- int n; -- const char *s; -- lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); -- lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ -- lua_remove(L, -2); -- lua_getfield(L, -1, "status"); -- lua_remove(L, -2); -- n = lua_gettop(L); -- lua_call(L, 0, LUA_MULTRET); -- fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout); -- for (n++; (s = lua_tostring(L, n)); n++) { -- putc(' ', stdout); -- fputs(s, stdout); -- } -- putc('\n', stdout); -- lua_settop(L, 0); /* clear stack */ -+ jit_State *J = L2J(L); -+ fputs(J->flags & JIT_F_ON ? "JIT: ON" : "JIT: OFF", stdout); -+ fputs(J->flags & JIT_F_OPT_DEFAULT ? " OPT: DEFAULT" : " OPT: OFF", stdout); -+ fprintf(stdout, "\n"); - } - - static void createargtable(lua_State *L, char **argv, int argc, int argf) From 7a67ed6f58be98f291030307fea68c81aff11ac6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 13 Mar 2025 22:53:36 +0100 Subject: [PATCH 069/527] Added initial support for RClosure as a faster alternative to full rust closures --- core/src/macros/closure.rs | 63 +++++++++++++++++ core/src/macros/mod.rs | 1 + core/src/vm/closure/core.rs | 114 ++++++++++++++++++++++++++++++ core/src/vm/closure/interface.rs | 53 ++++++++++++++ core/src/vm/closure/mod.rs | 33 +++++++++ core/src/vm/closure/types.rs | 59 ++++++++++++++++ core/src/vm/core.rs | 2 +- core/src/vm/function/interface.rs | 4 +- core/src/vm/function/mod.rs | 1 + core/src/vm/function/types.rs | 49 +++++++++++++ core/src/vm/mod.rs | 1 + core/src/vm/value/core.rs | 14 +--- core/src/vm/value/interface.rs | 3 - core/tests/test_vm_closures.rs | 46 ++++++++++++ core/tests/test_vm_destructor.rs | 4 +- core/tests/test_vm_run_string.rs | 4 +- core/tests/test_vm_userdata.rs | 6 +- testbin/src/main.rs | 4 +- 18 files changed, 434 insertions(+), 27 deletions(-) create mode 100644 core/src/macros/closure.rs create mode 100644 core/src/vm/closure/core.rs create mode 100644 core/src/vm/closure/interface.rs create mode 100644 core/src/vm/closure/mod.rs create mode 100644 core/src/vm/closure/types.rs create mode 100644 core/src/vm/function/types.rs create mode 100644 core/tests/test_vm_closures.rs diff --git a/core/src/macros/closure.rs b/core/src/macros/closure.rs new file mode 100644 index 0000000..4512013 --- /dev/null +++ b/core/src/macros/closure.rs @@ -0,0 +1,63 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! decl_closure { + ( + $vis: vis fn $fn_name: ident |$upvalue_name: ident: $upvalue_ty: ty| ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + ) => { + $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + fn _func($name: &$crate::vm::Vm, $upvalue_name: $upvalue_ty$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(&vm, 1) }; + $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? + let ret = _func(&vm, $upvalue_name$(, $($arg_name),*)?); + ret.into_param(&vm) as _ + } + $crate::vm::closure::types::RClosure::new(_cfunc, upvalue) + } + }; + ( + $vis: vis fn $fn_name: ident |$upvalue_name: ident: $upvalue_ty: ty| ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + ) => { + $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { + extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { + fn _func($upvalue_name: $upvalue_ty, $($arg_name: $arg_ty),*) -> $ret_ty $code + use $crate::vm::function::IntoParam; + let vm = unsafe { $crate::vm::Vm::from_raw(l) }; + let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(&vm, 1) }; + $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); + let ret = _func($upvalue_name, $($arg_name),*); + ret.into_param(&vm) as _ + } + $crate::vm::closure::types::RClosure::new(_cfunc, upvalue) + } + }; +} diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 0462cfe..98cd270 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -30,6 +30,7 @@ mod lib_func; mod userdata_func; mod userdata; mod impl_from_param; +mod closure; #[macro_export] macro_rules! c_stringify { diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs new file mode 100644 index 0000000..d22be6a --- /dev/null +++ b/core/src/vm/closure/core.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::slice; +use crate::ffi::lua::{lua_pushlightuserdata, lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, GLOBALSINDEX}; +use crate::vm::closure::{FromUpvalue, IntoUpvalue}; +use crate::vm::function::IntoParam; +use crate::vm::util::{lua_rust_error, SimpleDrop}; +use crate::vm::Vm; + +impl<'a> FromUpvalue<'a> for &'a str { + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let mut len: usize = 0; + let str = lua_tolstring(vm.as_ptr(), GLOBALSINDEX - index, &mut len as _); + let slice = slice::from_raw_parts(str as *const u8, len); + match std::str::from_utf8(slice){ + Ok(v) => v, + Err(e) => { + lua_rust_error(vm.as_ptr(), e); + } + } + } +} + +macro_rules! impl_integer { + ($($t: ty),*) => { + $( + impl FromUpvalue<'_> for $t { + unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { + lua_tointeger(vm.as_ptr(), GLOBALSINDEX - index) as _ + } + } + )* + }; +} + +#[cfg(target_pointer_width = "64")] +impl_integer!(i64, u64); + +impl_integer!(i8, u8, i16, u16, i32, u32); + +macro_rules! impl_float { + ($($t: ty),*) => { + $( + impl FromUpvalue<'_> for $t { + unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { + lua_tonumber(vm.as_ptr(), GLOBALSINDEX - index) as _ + } + } + )* + }; +} + +impl_float!(f32, f64); + +unsafe impl SimpleDrop for *mut T {} +unsafe impl SimpleDrop for *const T {} + +impl FromUpvalue<'_> for *mut T { + unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { + lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ + } +} + +impl FromUpvalue<'_> for *const T { + unsafe fn from_upvalue(vm: &'_ Vm, index: i32) -> Self { + lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ + } +} + +impl IntoUpvalue for T { + fn into_upvalue(self, vm: &Vm) -> u16 { + self.into_param(vm) + } +} + +impl IntoUpvalue for *mut T { + fn into_upvalue(self, vm: &Vm) -> u16 { + unsafe { lua_pushlightuserdata(vm.as_ptr(), self as _) }; + 1 + } +} + +impl IntoUpvalue for *const T { + fn into_upvalue(self, vm: &Vm) -> u16 { + unsafe { lua_pushlightuserdata(vm.as_ptr(), self as _) }; + 1 + } +} diff --git a/core/src/vm/closure/interface.rs b/core/src/vm/closure/interface.rs new file mode 100644 index 0000000..8d573c5 --- /dev/null +++ b/core/src/vm/closure/interface.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::util::SimpleDrop; +use crate::vm::Vm; + +/// This trait represents a closure parameter. +pub trait FromUpvalue<'a>: Sized + SimpleDrop { + /// Reads this value from the given lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to read from. + /// * `index`: index of the parameter to read. + /// + /// returns: Self + /// + /// # Safety + /// + /// Calling this function outside the body of a [CFunction](crate::ffi::lua::CFunction) is UB. + /// Calling this function in a non-POF segment of that CFunction is also UB. Finally, if the + /// type of the value at index `index` is not of [Self], calling this function is UB. + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self; +} + +pub trait IntoUpvalue { + fn into_upvalue(self, vm: &Vm) -> u16; +} diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs new file mode 100644 index 0000000..d8518c6 --- /dev/null +++ b/core/src/vm/closure/mod.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +mod core; +pub mod types; + +pub use interface::*; diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs new file mode 100644 index 0000000..cfd713b --- /dev/null +++ b/core/src/vm/closure/types.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_pushcclosure, CFunction}; +use crate::vm::closure::IntoUpvalue; +use crate::vm::value::IntoLua; +use crate::vm::Vm; + +pub struct RClosure { + func: CFunction, + upvalue: T +} + +impl RClosure { + /// Creates a new [RClosure]. + /// + /// # Arguments + /// + /// * `func`: the [CFunction] to be associated with an upvalue. + /// * `upvalue`: the upvalue to bind to the [CFunction]. + /// + /// returns: RClosure + pub fn new(func: CFunction, upvalue: T) -> Self { + Self { func, upvalue } + } +} + +impl IntoLua for RClosure { + fn into_lua(self, vm: &Vm) -> crate::vm::Result { + let num = self.upvalue.into_upvalue(vm); + unsafe { lua_pushcclosure(vm.as_ptr(), self.func, num as _) }; + Ok(1) + } +} diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 4ea671c..499024d 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -178,7 +178,7 @@ impl Vm { }; } // Read and return the result of the function from the stack. - FromLua::from_lua(self, -1) + FromLua::from_lua(self, -(R::num_values() as i32)) } } diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index a049947..9461f1c 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -56,7 +56,7 @@ pub trait FromParam<'a>: Sized + SimpleDrop + LuaType { /// /// # Safety /// - /// Calling this function outside the body of a CFunction is UB. Calling this function in a - /// non-POF segment of that CFunction is also UB. + /// Calling this function outside the body of a [CFunction](crate::ffi::lua::CFunction) is UB. + /// Calling this function in a non-POF segment of that CFunction is also UB. unsafe fn from_param(vm: &'a Vm, index: i32) -> Self; } diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index eb1d07f..d8518c6 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -28,5 +28,6 @@ mod interface; mod core; +pub mod types; pub use interface::*; diff --git a/core/src/vm/function/types.rs b/core/src/vm/function/types.rs new file mode 100644 index 0000000..80e4593 --- /dev/null +++ b/core/src/vm/function/types.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_pushcclosure, CFunction}; +use crate::vm::value::IntoLua; +use crate::vm::Vm; + +pub struct RFunction(CFunction); + +impl RFunction { + pub fn wrap(inner: CFunction) -> Self { + Self(inner) + } +} + +impl IntoLua for RFunction { + fn into_lua(self, vm: &Vm) -> crate::vm::Result { + let l = vm.as_ptr(); + unsafe { + lua_pushcclosure(l, self.0, 0); + } + Ok(1) + } +} diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 28f14ff..c9539f6 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -32,6 +32,7 @@ pub mod value; pub mod error; pub mod util; pub mod userdata; +pub mod closure; pub use core::*; diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index bc28062..1b02268 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -27,12 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_pushcclosure}; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean}; use crate::vm::function::IntoParam; use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::UserDataImmutable; -use crate::vm::value::{FromLua, IntoLua, RFunction}; +use crate::vm::value::{FromLua, IntoLua}; impl<'a> FromLua<'a> for &'a str { fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { @@ -99,16 +99,6 @@ impl IntoLua for T { } } -impl IntoLua for RFunction { - fn into_lua(self, vm: &Vm) -> crate::vm::Result { - let l = vm.as_ptr(); - unsafe { - lua_pushcclosure(l, self.0, 0); - } - Ok(1) - } -} - impl FromLua<'_> for () { fn from_lua(_vm: &Vm, _: i32) -> crate::vm::Result<()> { Ok(()) diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 129dc2c..f15d405 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::CFunction; use crate::vm::Vm; pub trait FromLua<'a>: Sized { @@ -58,5 +57,3 @@ pub trait IntoLua: Sized { /// returns: Result fn into_lua(self, vm: &Vm) -> crate::vm::Result; } - -pub struct RFunction(pub CFunction); diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs new file mode 100644 index 0000000..6a378de --- /dev/null +++ b/core/tests/test_vm_closures.rs @@ -0,0 +1,46 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::decl_closure; +use bp3d_lua::vm::RootVm; + +decl_closure! { + fn test |upvalue: &str| (val: f32) -> String { + format!("{}: {}", upvalue, val) + } +} + +#[test] +fn test_vm_closures() { + let vm = RootVm::new(); + let top = vm.top(); + vm.set_global(c"test", test("this is a test")).unwrap(); + assert_eq!(top, vm.top()); + let s: &str = vm.run_code(c"return test(42.42)").unwrap(); + assert_eq!(s, "this is a test: 42.42"); +} diff --git a/core/tests/test_vm_destructor.rs b/core/tests/test_vm_destructor.rs index 1c31fbe..24304e5 100644 --- a/core/tests/test_vm_destructor.rs +++ b/core/tests/test_vm_destructor.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::RFunction; +use bp3d_lua::vm::function::types::RFunction; struct ValueWithDrop; impl ValueWithDrop { @@ -53,7 +53,7 @@ decl_lib_func! { #[test] fn test_vm_destructor() { let mut vm = RootVm::new(); - vm.set_global(c"test_c_function", RFunction(test_c_function)).unwrap(); + vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)).unwrap(); let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); diff --git a/core/tests/test_vm_run_string.rs b/core/tests/test_vm_run_string.rs index 13715ba..b48b3c4 100644 --- a/core/tests/test_vm_run_string.rs +++ b/core/tests/test_vm_run_string.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::RFunction; +use bp3d_lua::vm::function::types::RFunction; decl_lib_func! { fn dostring(vm: &Vm, code: &str) -> bp3d_lua::vm::Result<()> { @@ -44,7 +44,7 @@ fn test_run_string() { let res = vm.run_code::<()>(c"dostring('test')"); assert!(res.is_err()); assert_eq!(res.unwrap_err().to_string(), "runtime error: [string \"dostring('test')\"]:1: attempt to call global 'dostring' (a nil value)"); - vm.set_global(c"dostring", RFunction(dostring)).unwrap(); + vm.set_global(c"dostring", RFunction::wrap(dostring)).unwrap(); assert!(vm.run_code::<()>(c"dostring('test')").is_err()); assert!(vm.run_code::<()>(c"dostring('print(\"whatever 123\")')").is_ok()); assert!(vm.run_code::<()>(c"dostring('root = 42')").is_ok()); diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index f46cf9a..e414246 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -29,7 +29,7 @@ use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::RFunction; +use bp3d_lua::vm::function::types::RFunction; pub struct MyInt(i64); @@ -111,7 +111,7 @@ decl_lib_func! { #[test] fn test_vm_userdata_forgot_reg() { let vm = RootVm::new(); - vm.set_global(c"MyInt", RFunction(my_int)).unwrap(); + vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); vm.run_code::<()>(c"a = MyInt(123)").unwrap(); vm.run_code::<()>(c"b = MyInt(456)").unwrap(); assert!(vm.run_code::(c"return a < b").is_err()); @@ -157,7 +157,7 @@ fn test_vm_userdata() { let top = vm.top(); vm.register_userdata::().unwrap(); assert_eq!(top, vm.top()); - vm.set_global(c"MyInt", RFunction(my_int)).unwrap(); + vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); assert_eq!(top, vm.top()); vm.run_code::<()>(c"a = MyInt(123)").unwrap(); vm.run_code::<()>(c"b = MyInt(456)").unwrap(); diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 06cd3d1..63ce555 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -30,7 +30,7 @@ use std::time::Duration; use mlua::Lua; use bp3d_lua::decl_lib_func; use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::RFunction; +use bp3d_lua::vm::function::types::RFunction; struct ValueWithDrop; impl ValueWithDrop { @@ -52,7 +52,7 @@ decl_lib_func! { fn test_vm_destructor() -> Duration { let mut vm = RootVm::new(); - vm.set_global(c"test_c_function", RFunction(test_c_function)).unwrap(); + vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)).unwrap(); let time = std::time::Instant::now(); for _ in 0..20000 { let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); From 88afb59e84114d3a2326545df86d30a488b505ba Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 14 Mar 2025 08:07:16 +0100 Subject: [PATCH 070/527] Added support for rust based closures --- core/src/vm/closure/types.rs | 24 +++++++++++++++++++++--- core/src/vm/core.rs | 19 +++++++++++++++++-- core/src/vm/function/core.rs | 6 ++++++ core/tests/test_vm_closures.rs | 16 +++++++++++++++- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index cfd713b..ee06b82 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -26,10 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushcclosure, CFunction}; -use crate::vm::closure::IntoUpvalue; +use std::ffi::c_void; +use crate::ffi::lua::{lua_pushcclosure, CFunction, State}; +use crate::vm::closure::{FromUpvalue, IntoUpvalue}; use crate::vm::value::IntoLua; -use crate::vm::Vm; +use crate::vm::{RootVm, Vm}; +use crate::vm::function::{FromParam, IntoParam}; pub struct RClosure { func: CFunction, @@ -57,3 +59,19 @@ impl IntoLua for RClosure { Ok(1) } } + +impl RClosure<*const c_void> { + pub fn from_rust R + 'static>(root: &mut RootVm, fun: F) -> Self + where for<'a> T: FromParam<'a>, R: IntoParam { + let ptr = root.leak(Box::new(fun)); + extern "C-unwind" fn _cfunc R>(l: State) -> i32 + where for<'a> T: FromParam<'a>, R: IntoParam { + let vm = unsafe { Vm::from_raw(l) }; + let upvalue: *const F = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let res = unsafe { (*upvalue)(args) }; + res.into_param(&vm) as _ + } + RClosure::new(_cfunc::, ptr as *const _) + } +} diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 499024d..203d3c5 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -183,7 +183,8 @@ impl Vm { } pub struct RootVm { - vm: Vm + vm: Vm, + leaked: Vec> } impl RootVm { @@ -191,9 +192,18 @@ impl RootVm { let l = unsafe { luaL_newstate() }; unsafe { luaL_openlibs(l) }; RootVm { - vm: unsafe { Vm::from_raw(l) } + vm: unsafe { Vm::from_raw(l) }, + leaked: Vec::new() } } + + pub fn leak(&mut self, bx: Box) -> *mut T { + let ptr = Box::into_raw(bx); + self.leaked.push(Box::new(move || { + unsafe { drop(Box::from_raw(ptr)) }; + })); + ptr + } } impl Deref for RootVm { @@ -216,5 +226,10 @@ impl Drop for RootVm { println!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); } + println!("Deleting leaked pointers..."); + let v = std::mem::replace(&mut self.leaked, Vec::new()); + for f in v { + f() + } } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 18036db..6256fd6 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -75,6 +75,12 @@ impl IntoParam for &str { } } +impl IntoParam for String { + fn into_param(self, vm: &Vm) -> u16 { + (&*self).into_param(vm) + } +} + macro_rules! impl_integer { ($($t: ty),*) => { $( diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 6a378de..0b9ff03 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::RootVm; decl_closure! { @@ -36,7 +37,7 @@ decl_closure! { } #[test] -fn test_vm_closures() { +fn test_vm_fast_closure() { let vm = RootVm::new(); let top = vm.top(); vm.set_global(c"test", test("this is a test")).unwrap(); @@ -44,3 +45,16 @@ fn test_vm_closures() { let s: &str = vm.run_code(c"return test(42.42)").unwrap(); assert_eq!(s, "this is a test: 42.42"); } + +#[test] +fn test_vm_rust_closure() { + let mut vm = RootVm::new(); + let top = vm.top(); + let closure = RClosure::from_rust(&mut vm, |val: f32| { + format!("this is a test: {}", val) + }); + vm.set_global(c"test", closure).unwrap(); + assert_eq!(top, vm.top()); + let s: &str = vm.run_code(c"return test(42.42)").unwrap(); + assert_eq!(s, "this is a test: 42.42"); +} From a2e3f396ed5a3b74acbe420ae7d7d0af6408b150 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 14 Mar 2025 23:37:45 +0100 Subject: [PATCH 071/527] Added multivalue support --- core/src/vm/value/core.rs | 27 +++++++++++++++++++++++ core/tests/test_vm_multivalue.rs | 37 ++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+) create mode 100644 core/tests/test_vm_multivalue.rs diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 1b02268..e40bd7c 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -121,3 +121,30 @@ impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { Ok(unsafe { &*this_ptr }) } } + +macro_rules! count_tts { + () => {0}; + ($_head:tt $($tail:tt)*) => {1 + count_tts!($($tail)*)}; +} + +macro_rules! impl_tuple { + ($($name: ident: $name2: ident),*) => { + impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { + fn num_values() -> u16 { + count_tts!($($name),*) + } + + fn from_lua(vm: &'a Vm, mut index: i32) -> crate::vm::Result<($($name),*)> { + $( + let $name2: $name = FromLua::from_lua(vm, index)?; + index += 1; + )* + Ok(($($name2),*)) + } + } + }; +} + +impl_tuple!(T: t, T1: t1); +impl_tuple!(T: t, T1: t1, T2: t2); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3); diff --git a/core/tests/test_vm_multivalue.rs b/core/tests/test_vm_multivalue.rs new file mode 100644 index 0000000..8a1df6f --- /dev/null +++ b/core/tests/test_vm_multivalue.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; + +#[test] +fn test_vm_multivalue() { + let vm = RootVm::new(); + let (i, n): (i32, &str) = vm.run_code(c"return 123, 'this is a test'").unwrap(); + assert_eq!(i, 123); + assert_eq!(n, "this is a test"); +} From ccf50f7c47c32495a56d0375d03f9e60494e92a0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 08:45:47 +0100 Subject: [PATCH 072/527] Prepared for future implementation of LuaFunction --- core/src/vm/value/function.rs | 55 +++++++++++++++++++++++++++++++++++ core/src/vm/value/mod.rs | 1 + 2 files changed, 56 insertions(+) create mode 100644 core/src/vm/value/function.rs diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs new file mode 100644 index 0000000..b7c1cfa --- /dev/null +++ b/core/src/vm/value/function.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::Type; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; + +pub struct LuaFunction<'a> { + vm: &'a Vm, + index: i32 +} + +impl<'a> LuaFunction<'a> { + pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, value: T) -> crate::vm::Result { + //TODO: Implement + todo!() + } +} + +impl<'a> FromLua<'a> for LuaFunction<'a> { + const EXPECTED_TYPE: Type = Type::Function; + + #[inline] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> LuaFunction<'a> { + LuaFunction { + vm, + index: vm.get_absolute_index(index) + } + } +} diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 89c10cd..f56b4bd 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -29,5 +29,6 @@ mod interface; mod core; pub mod table; +mod function; pub use interface::*; From 9b5ae3e7ae8afc5cc45d8db69e2851231109f97d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 09:11:28 +0100 Subject: [PATCH 073/527] Use inline(always) to ensure functions are inlined --- core/src/vm/core.rs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 203d3c5..74c41f9 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -70,6 +70,7 @@ pub struct Vm { } impl Vm { + #[inline(always)] pub unsafe fn from_raw(l: State) -> Self { Self { l @@ -92,7 +93,7 @@ impl Vm { } /// Returns the absolute stack index for the given index. - #[inline] + #[inline(always)] pub fn get_absolute_index(&self, index: i32) -> i32 { if index < 0 { unsafe { lua_gettop(self.l) + index + 1 } @@ -102,18 +103,18 @@ impl Vm { } /// Returns the top of the lua stack. - #[inline] + #[inline(always)] pub fn top(&self) -> i32 { unsafe { lua_gettop(self.l) } } /// Clears the lua stack. - #[inline] + #[inline(always)] pub fn clear(&mut self) { unsafe { lua_settop(self.l, 0); } } - #[inline] + #[inline(always)] pub fn as_ptr(&self) -> State { self.l } From 3ed1c8c55613c508fe5aa229003cd010ed6b86d3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 09:13:57 +0100 Subject: [PATCH 074/527] Added from_lua_unchecked in preparation of future registry key API --- core/src/vm/value/core.rs | 42 ++++++++++++++++++++++++++-------- core/src/vm/value/function.rs | 9 +++++--- core/src/vm/value/interface.rs | 17 ++++++++++++++ core/src/vm/value/mod.rs | 1 + core/src/vm/value/table.rs | 20 +++++++--------- core/src/vm/value/util.rs | 42 ++++++++++++++++++++++++++++++++++ 6 files changed, 106 insertions(+), 25 deletions(-) create mode 100644 core/src/vm/value/util.rs diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index e40bd7c..815ff32 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::check_single_type; use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean}; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_touserdata}; use crate::vm::function::IntoParam; use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; @@ -35,6 +36,13 @@ use crate::vm::userdata::UserDataImmutable; use crate::vm::value::{FromLua, IntoLua}; impl<'a> FromLua<'a> for &'a str { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let mut len: usize = 0; + let s = lua_tolstring(vm.as_ptr(), index, &mut len as _); + let slice = std::slice::from_raw_parts(s as _, len); + std::str::from_utf8_unchecked(slice) + } + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { let l = vm.as_ptr(); unsafe { @@ -58,17 +66,14 @@ impl<'a> FromLua<'a> for &'a str { macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &Vm, index: i32) -> Self { + $func(vm.as_ptr(), index) $($ret)* + } + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - let l = vm.as_ptr(); unsafe { - let ty = lua_type(l, index); - match ty { - Type::$expected => Ok($func(l, index) $($ret)*), - _ => Err(Error::Type(TypeError { - expected: Type::$expected, - actual: ty - })) - } + check_single_type!(Type::$expected => (vm, index) { $func(vm.as_ptr(), index) $($ret)* }) } } } @@ -100,6 +105,10 @@ impl IntoLua for T { } impl FromLua<'_> for () { + unsafe fn from_lua_unchecked(_: &'_ Vm, _: i32) -> Self { + () + } + fn from_lua(_vm: &Vm, _: i32) -> crate::vm::Result<()> { Ok(()) } @@ -110,6 +119,11 @@ impl FromLua<'_> for () { } impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + &*(lua_touserdata(vm.as_ptr(), index) as *const T) + } + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let this_ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; if this_ptr.is_null() { @@ -134,6 +148,14 @@ macro_rules! impl_tuple { count_tts!($($name),*) } + unsafe fn from_lua_unchecked(vm: &'a Vm, mut index: i32) -> Self { + $( + let $name2: $name = FromLua::from_lua_unchecked(vm, index); + index += 1; + )* + ($($name2),*) + } + fn from_lua(vm: &'a Vm, mut index: i32) -> crate::vm::Result<($($name),*)> { $( let $name2: $name = FromLua::from_lua(vm, index)?; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index b7c1cfa..9773618 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::check_single_type; use crate::ffi::lua::Type; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; @@ -43,13 +44,15 @@ impl<'a> LuaFunction<'a> { } impl<'a> FromLua<'a> for LuaFunction<'a> { - const EXPECTED_TYPE: Type = Type::Function; - - #[inline] + #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> LuaFunction<'a> { LuaFunction { vm, index: vm.get_absolute_index(index) } } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + check_single_type!(Type::Function => (vm, index) { LuaFunction { vm, index: vm.get_absolute_index(index) } }) + } } diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index f15d405..91b1b44 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -26,9 +26,26 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::lua::lua_type; +use crate::vm::error::{Error, TypeError}; use crate::vm::Vm; pub trait FromLua<'a>: Sized { + /// Reads the value at the specified index in the given [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to read from. + /// * `index`: the index at which to try reading the value from. + /// + /// returns: Result + /// + /// # Safety + /// + /// This function assumes the type of the value at index `index` is already of the expected type, + /// if not, calling this function is UB. + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self; + /// Attempt to read the value at the specified index in the given [Vm]. /// /// # Arguments diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index f56b4bd..261d029 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -30,5 +30,6 @@ mod interface; mod core; pub mod table; mod function; +mod util; pub use interface::*; diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index 0f836c3..1eba9fe 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::check_single_type; use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_type, Type}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, Type}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; -use crate::vm::error::TypeError; use crate::vm::util::{AnyStr, LuaType, SimpleDrop}; use crate::vm::value::{FromLua, IntoLua}; @@ -161,17 +161,13 @@ impl<'a> FromParam<'a> for Table<'a> { } impl<'a> FromLua<'a> for Table<'a> { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Table { vm, index: vm.get_absolute_index(index) } + } + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - let ty = unsafe { lua_type(vm.as_ptr(), index) }; - let index = vm.get_absolute_index(index); - if ty == Type::Table { - Ok(Table { vm, index }) - } else { - Err(crate::vm::error::Error::Type(TypeError { - expected: Type::Table, - actual: ty - })) - } + check_single_type!(Type::Table => (vm, index) { Table { vm, index: vm.get_absolute_index(index) } }) } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs new file mode 100644 index 0000000..5562b15 --- /dev/null +++ b/core/src/vm/value/util.rs @@ -0,0 +1,42 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[macro_export] +macro_rules! check_single_type { + ($expected: expr => ($vm: ident, $index: ident) { $ret: expr }) => {{ + let ty = unsafe { crate::ffi::lua::lua_type($vm.as_ptr(), $index) }; + if ty == $expected { + Ok($ret) + } else { + Err(crate::vm::error::Error::Type(crate::vm::error::TypeError { + expected: $expected, + actual: ty + })) + } + }}; +} From bdcd133588d04b951ccf9ea0cf3f8ce1b4899dfb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 09:14:45 +0100 Subject: [PATCH 075/527] Fixed some warnings --- core/src/vm/value/core.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 815ff32..e0420d1 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -72,9 +72,7 @@ macro_rules! impl_from_lua { } fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - unsafe { - check_single_type!(Type::$expected => (vm, index) { $func(vm.as_ptr(), index) $($ret)* }) - } + check_single_type!(Type::$expected => (vm, index) { unsafe { $func(vm.as_ptr(), index) $($ret)* } }) } } }; From 1d18530c5e3ba1d6ed91939241d9f93e09caecd5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 09:23:57 +0100 Subject: [PATCH 076/527] Implement FromUpvalue in terms of from_lua_unchecked --- core/src/vm/closure/core.rs | 50 ++++++++++--------------------------- core/src/vm/util.rs | 3 +++ 2 files changed, 16 insertions(+), 37 deletions(-) diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index d22be6a..fc9cfeb 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -26,60 +26,36 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::slice; -use crate::ffi::lua::{lua_pushlightuserdata, lua_tointeger, lua_tolstring, lua_tonumber, lua_topointer, GLOBALSINDEX}; +use crate::ffi::lua::{lua_pushlightuserdata, lua_topointer, GLOBALSINDEX}; use crate::vm::closure::{FromUpvalue, IntoUpvalue}; use crate::vm::function::IntoParam; -use crate::vm::util::{lua_rust_error, SimpleDrop}; use crate::vm::Vm; +use crate::vm::value::FromLua; -impl<'a> FromUpvalue<'a> for &'a str { - unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let mut len: usize = 0; - let str = lua_tolstring(vm.as_ptr(), GLOBALSINDEX - index, &mut len as _); - let slice = slice::from_raw_parts(str as *const u8, len); - match std::str::from_utf8(slice){ - Ok(v) => v, - Err(e) => { - lua_rust_error(vm.as_ptr(), e); - } - } - } -} - -macro_rules! impl_integer { +macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { $( impl FromUpvalue<'_> for $t { + #[inline(always)] unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { - lua_tointeger(vm.as_ptr(), GLOBALSINDEX - index) as _ + <$t>::from_lua_unchecked(vm, GLOBALSINDEX - index) } } )* }; } -#[cfg(target_pointer_width = "64")] -impl_integer!(i64, u64); - -impl_integer!(i8, u8, i16, u16, i32, u32); - -macro_rules! impl_float { - ($($t: ty),*) => { - $( - impl FromUpvalue<'_> for $t { - unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { - lua_tonumber(vm.as_ptr(), GLOBALSINDEX - index) as _ - } - } - )* - }; +impl<'a> FromUpvalue<'a> for &'a str { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + FromLua::from_lua_unchecked(vm, GLOBALSINDEX - index) + } } -impl_float!(f32, f64); +#[cfg(target_pointer_width = "64")] +impl_from_upvalue_using_from_lua_unchecked!(i64, u64); -unsafe impl SimpleDrop for *mut T {} -unsafe impl SimpleDrop for *const T {} +impl_from_upvalue_using_from_lua_unchecked!(i8, u8, i16, u16, i32, u32, f32, f64, bool); impl FromUpvalue<'_> for *mut T { unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index 463191a..2686d4a 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -55,6 +55,9 @@ impl LuaType for Option { pub unsafe trait SimpleDrop {} +unsafe impl SimpleDrop for *mut T {} +unsafe impl SimpleDrop for *const T {} +unsafe impl SimpleDrop for bool {} unsafe impl SimpleDrop for Option {} unsafe impl SimpleDrop for Result {} unsafe impl SimpleDrop for &T {} From 896a4fe4081be499f7b60f7046c99ab0feec20d8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 09:29:37 +0100 Subject: [PATCH 077/527] Refactored Rust utilities and VM utilities --- core/src/lib.rs | 1 + core/src/util.rs | 71 +++++++++++++++++++++++++++++++ core/src/vm/closure/interface.rs | 2 +- core/src/vm/core.rs | 3 +- core/src/vm/function/core.rs | 3 +- core/src/vm/function/interface.rs | 3 +- core/src/vm/util.rs | 40 ----------------- core/src/vm/value/interface.rs | 2 - core/src/vm/value/table.rs | 3 +- 9 files changed, 81 insertions(+), 47 deletions(-) create mode 100644 core/src/util.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index c9cd540..71af5e2 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -29,3 +29,4 @@ pub mod ffi; pub mod vm; mod macros; +pub mod util; diff --git a/core/src/util.rs b/core/src/util.rs new file mode 100644 index 0000000..9a77029 --- /dev/null +++ b/core/src/util.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! Generic rust utilities module. + +use std::borrow::Cow; +use std::ffi::{CStr, CString}; + +pub trait AnyStr { + fn to_str(&self) -> crate::vm::Result>; +} + +impl AnyStr for String { + fn to_str(&self) -> crate::vm::Result> { + let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; + Ok(Cow::Owned(cstr)) + } +} + +impl AnyStr for &str { + fn to_str(&self) -> crate::vm::Result> { + let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; + Ok(Cow::Owned(cstr)) + } +} + +impl AnyStr for CString { + fn to_str(&self) -> crate::vm::Result> { + Ok(Cow::Borrowed(&**self)) + } +} + +impl AnyStr for &CStr { + fn to_str(&self) -> crate::vm::Result> { + Ok(Cow::Borrowed(&**self)) + } +} + +pub unsafe trait SimpleDrop {} + +unsafe impl SimpleDrop for *mut T {} +unsafe impl SimpleDrop for *const T {} +unsafe impl SimpleDrop for bool {} +unsafe impl SimpleDrop for Option {} +unsafe impl SimpleDrop for Result {} +unsafe impl SimpleDrop for &T {} diff --git a/core/src/vm/closure/interface.rs b/core/src/vm/closure/interface.rs index 8d573c5..7511170 100644 --- a/core/src/vm/closure/interface.rs +++ b/core/src/vm/closure/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::util::SimpleDrop; +use crate::util::SimpleDrop; use crate::vm::Vm; /// This trait represents a closure parameter. diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 74c41f9..bb482cc 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -30,9 +30,10 @@ use std::ffi::c_int; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX}; +use crate::util::AnyStr; use crate::vm::error::{Error, RuntimeError}; use crate::vm::userdata::{Registry, UserData}; -use crate::vm::util::{AnyStr, LoadCode}; +use crate::vm::util::LoadCode; use crate::vm::value::{FromLua, IntoLua}; const TRACEBACK_NONE: &[u8] = b"\n"; diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 6256fd6..ba20127 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -31,9 +31,10 @@ use std::slice; use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; +use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; -use crate::vm::util::{lua_rust_error, LuaType, SimpleDrop, TypeName}; +use crate::vm::util::{lua_rust_error, LuaType, TypeName}; use crate::vm::Vm; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 9461f1c..9bbf1a5 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::util::SimpleDrop; use crate::vm::Vm; -use crate::vm::util::{LuaType, SimpleDrop}; +use crate::vm::util::LuaType; /// This trait represents a function return value. pub trait IntoParam: Sized { diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index 2686d4a..d74b589 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; use std::error::Error; use std::ffi::{CStr, CString}; use crate::ffi::laux::luaL_loadstring; @@ -53,15 +52,6 @@ impl LuaType for Option { } } -pub unsafe trait SimpleDrop {} - -unsafe impl SimpleDrop for *mut T {} -unsafe impl SimpleDrop for *const T {} -unsafe impl SimpleDrop for bool {} -unsafe impl SimpleDrop for Option {} -unsafe impl SimpleDrop for Result {} -unsafe impl SimpleDrop for &T {} - pub unsafe fn lua_rust_error(l: State, error: E) -> ! { // At this point the function is assumed to be a non-POF (error and String). let s = format!("rust error: {}", error); @@ -99,33 +89,3 @@ impl LoadCode for &str { } } } - -pub trait AnyStr { - fn to_str(&self) -> crate::vm::Result>; -} - -impl AnyStr for String { - fn to_str(&self) -> crate::vm::Result> { - let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; - Ok(Cow::Owned(cstr)) - } -} - -impl AnyStr for &str { - fn to_str(&self) -> crate::vm::Result> { - let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; - Ok(Cow::Owned(cstr)) - } -} - -impl AnyStr for CString { - fn to_str(&self) -> crate::vm::Result> { - Ok(Cow::Borrowed(&**self)) - } -} - -impl AnyStr for &CStr { - fn to_str(&self) -> crate::vm::Result> { - Ok(Cow::Borrowed(&**self)) - } -} diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 91b1b44..ee0a395 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -26,8 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::lua_type; -use crate::vm::error::{Error, TypeError}; use crate::vm::Vm; pub trait FromLua<'a>: Sized { diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index 1eba9fe..b2d394a 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -30,9 +30,10 @@ use crate::check_single_type; use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, Type}; +use crate::util::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::Vm; -use crate::vm::util::{AnyStr, LuaType, SimpleDrop}; +use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; pub struct Table<'a> { From 1c74d130f62f3f32fdb690ce036e216f0d25f35e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 14:25:47 +0100 Subject: [PATCH 078/527] Added initial implementation of registry system --- core/src/vm/mod.rs | 1 + core/src/vm/registry/core.rs | 84 +++++++++++++++++++++++++++++++ core/src/vm/registry/interface.rs | 50 ++++++++++++++++++ core/src/vm/registry/mod.rs | 33 ++++++++++++ core/src/vm/registry/types.rs | 52 +++++++++++++++++++ 5 files changed, 220 insertions(+) create mode 100644 core/src/vm/registry/core.rs create mode 100644 core/src/vm/registry/interface.rs create mode 100644 core/src/vm/registry/mod.rs create mode 100644 core/src/vm/registry/types.rs diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index c9539f6..356d43a 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -33,6 +33,7 @@ pub mod error; pub mod util; pub mod userdata; pub mod closure; +pub mod registry; pub use core::*; diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs new file mode 100644 index 0000000..9fdd787 --- /dev/null +++ b/core/src/vm/registry/core.rs @@ -0,0 +1,84 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::c_int; +use std::marker::PhantomData; +use crate::ffi::laux::{luaL_ref, luaL_unref}; +use crate::ffi::lua::{lua_rawgeti, REGISTRYINDEX}; +use crate::vm::registry::RegistryValue; +use crate::vm::Vm; + +pub struct RegistryKey { + key: c_int, + useless: PhantomData +} + +impl RegistryKey { + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { + unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; + T::to_lua_value(vm, -1) + } + + /// Deletes this registry key from the specified [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to unregister from. + /// + /// returns: () + pub fn delete(self, vm: &Vm) { + unsafe { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.key) }; + } + + /// Creates a new [RegistryKey] from the top of the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance representing the lua stack. + /// + /// returns: RegistryKey + /// + /// # Safety + /// + /// The type T must match the type of the value at the top of the stack. Additionally, the value + /// at the top of the stack must not be referenced as it will be popped. + pub unsafe fn from_top(vm: &Vm) -> RegistryKey { + let key = luaL_ref(vm.as_ptr(), REGISTRYINDEX); + RegistryKey { + key, + useless: PhantomData + } + } +} diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs new file mode 100644 index 0000000..30efee9 --- /dev/null +++ b/core/src/vm/registry/interface.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::registry::core::RegistryKey; +use crate::vm::Vm; + +pub trait RegistryValue: 'static { + type Value<'a>; + + fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a>; +} + +/// A trait to produce registry values safely. +pub trait Register { + type RegistryValue: RegistryValue; + + /// Register this value into the registry. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach this value to. + /// + /// returns: RegistryKey + fn register(self, vm: &Vm) -> RegistryKey; +} diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs new file mode 100644 index 0000000..0a53de9 --- /dev/null +++ b/core/src/vm/registry/mod.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod core; +mod interface; +pub mod types; + +pub use interface::*; diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs new file mode 100644 index 0000000..eb83d31 --- /dev/null +++ b/core/src/vm/registry/types.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::registry::RegistryValue; +use crate::vm::value::FromLua; +use crate::vm::Vm; + +pub struct Table; +pub struct Function; + +impl RegistryValue for Table { + type Value<'a> = crate::vm::value::table::Table<'a>; + + #[inline(always)] + fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe { crate::vm::value::table::Table::from_lua_unchecked(vm, index) } + } +} + +impl RegistryValue for Function { + type Value<'a> = crate::vm::value::function::LuaFunction<'a>; + + #[inline(always)] + fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe { crate::vm::value::function::LuaFunction::from_lua_unchecked(vm, index) } + } +} From 3162664de57bc77d7104c6969730d96071f870d8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 14:26:32 +0100 Subject: [PATCH 079/527] Refactored value utils and added support for new Register trait to Table and LuaFunction --- core/src/vm/value/core.rs | 5 +++-- core/src/vm/value/function.rs | 17 ++++++++++++-- core/src/vm/value/mod.rs | 4 ++-- core/src/vm/value/table.rs | 17 ++++++++++++-- core/src/vm/value/util.rs | 42 +++++++++++++++++++++++++---------- 5 files changed, 65 insertions(+), 20 deletions(-) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index e0420d1..c334d7f 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::check_single_type; use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_touserdata}; use crate::vm::function::IntoParam; @@ -34,6 +33,7 @@ use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::UserDataImmutable; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::util::ensure_type_equals; impl<'a> FromLua<'a> for &'a str { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -72,7 +72,8 @@ macro_rules! impl_from_lua { } fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - check_single_type!(Type::$expected => (vm, index) { unsafe { $func(vm.as_ptr(), index) $($ret)* } }) + ensure_type_equals(vm, index, Type::$expected)?; + Ok(unsafe { $func(vm.as_ptr(), index) $($ret)* }) } } }; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 9773618..1d93958 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,9 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::check_single_type; use crate::ffi::lua::Type; +use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::Register; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; use crate::vm::Vm; pub struct LuaFunction<'a> { @@ -53,6 +55,17 @@ impl<'a> FromLua<'a> for LuaFunction<'a> { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - check_single_type!(Type::Function => (vm, index) { LuaFunction { vm, index: vm.get_absolute_index(index) } }) + ensure_type_equals(vm, index, Type::Function)?; + Ok(LuaFunction { vm, index: vm.get_absolute_index(index) }) + } +} + +impl Register for LuaFunction<'_> { + type RegistryValue = crate::vm::registry::types::Table; + + fn register(self, vm: &Vm) -> RegistryKey { + // If the function is not at the top of the stack, move it to the top. + ensure_value_top(vm, self.index); + unsafe { RegistryKey::from_top(vm) } } } diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 261d029..7244c7f 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -29,7 +29,7 @@ mod interface; mod core; pub mod table; -mod function; -mod util; +pub mod function; +pub mod util; pub use interface::*; diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index b2d394a..b51f380 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -26,15 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::check_single_type; use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, Type}; use crate::util::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::Register; use crate::vm::Vm; use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; pub struct Table<'a> { vm: &'a Vm, @@ -168,7 +170,8 @@ impl<'a> FromLua<'a> for Table<'a> { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - check_single_type!(Type::Table => (vm, index) { Table { vm, index: vm.get_absolute_index(index) } }) + ensure_type_equals(vm, index, Type::Table)?; + Ok(Table { vm, index: vm.get_absolute_index(index) }) } } @@ -183,3 +186,13 @@ impl IntoParam for Table<'_> { } impl LuaType for Table<'_> {} + +impl Register for Table<'_> { + type RegistryValue = crate::vm::registry::types::Table; + + fn register(self, vm: &Vm) -> RegistryKey { + // If the table is not at the top of the stack, move it to the top. + ensure_value_top(vm, self.index); + unsafe { RegistryKey::from_top(vm) } + } +} diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 5562b15..555e697 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -26,17 +26,35 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#[macro_export] -macro_rules! check_single_type { - ($expected: expr => ($vm: ident, $index: ident) { $ret: expr }) => {{ - let ty = unsafe { crate::ffi::lua::lua_type($vm.as_ptr(), $index) }; - if ty == $expected { - Ok($ret) - } else { - Err(crate::vm::error::Error::Type(crate::vm::error::TypeError { - expected: $expected, - actual: ty - })) +use crate::ffi::lua::{lua_pushnil, lua_pushvalue, lua_replace, Type}; +use crate::vm::error::{Error, TypeError}; +use crate::vm::Vm; + +/// Ensures the given lua value at index is of a specified type. +#[inline(always)] +pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { + let ty = unsafe { crate::ffi::lua::lua_type(vm.as_ptr(), index) }; + if ty == expected { + Ok(()) + } else { + Err(Error::Type(TypeError { + expected, + actual: ty + })) + } +} + +/// Ensures the given lua value at index is at the top of the stack. +/// If the value at index is not at the top of the stack, this function moves it to the top and +/// replaces the original index by a nil value. +#[inline(always)] +pub fn ensure_value_top(vm: &Vm, index: i32) { + if index != vm.top() { + let l = vm.as_ptr(); + unsafe { + lua_pushvalue(l, index); + lua_pushnil(l); + lua_replace(l, index); // Replace the value at index by a nil. } - }}; + } } From ae3763ee2b8a9b173e8ca9f78f11717c85257f52 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 14:30:45 +0100 Subject: [PATCH 080/527] Fixed UB in function Register implementation refactored vm registry types --- core/src/vm/registry/types.rs | 4 ++-- core/src/vm/value/function.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index eb83d31..5d1f470 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -31,7 +31,7 @@ use crate::vm::value::FromLua; use crate::vm::Vm; pub struct Table; -pub struct Function; +pub struct LuaFunction; impl RegistryValue for Table { type Value<'a> = crate::vm::value::table::Table<'a>; @@ -42,7 +42,7 @@ impl RegistryValue for Table { } } -impl RegistryValue for Function { +impl RegistryValue for LuaFunction { type Value<'a> = crate::vm::value::function::LuaFunction<'a>; #[inline(always)] diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 1d93958..1d69924 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -61,7 +61,7 @@ impl<'a> FromLua<'a> for LuaFunction<'a> { } impl Register for LuaFunction<'_> { - type RegistryValue = crate::vm::registry::types::Table; + type RegistryValue = crate::vm::registry::types::LuaFunction; fn register(self, vm: &Vm) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. From bbe6ce7a2649fd25d2501437c424b77c2d75d4ca Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 14:50:49 +0100 Subject: [PATCH 081/527] Added initial implementation of LuaFunction::call --- core/src/vm/core.rs | 60 ++++++++++++++++++++--------------- core/src/vm/value/function.rs | 7 ++-- 2 files changed, 40 insertions(+), 27 deletions(-) diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index bb482cc..9a2d712 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -66,6 +66,39 @@ extern "C-unwind" fn error_handler(l: State) -> c_int { } } +#[inline(always)] +pub(super) fn push_error_handler(l: State) -> c_int { + unsafe { + lua_pushcclosure(l, error_handler, 0); + lua_gettop(l) + } +} + +pub(super) fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { + let l = vm.as_ptr(); + unsafe { + // Call the function created by load_code. + let res = lua_pcall(l, nargs, nreturns, handler_pos); + // At this point the stack should no longer have the function but still has the error + // handler and R::num_values results. + // First remove error handler as we no longer need it. + lua_remove(l, handler_pos); + match res { + ThreadStatus::Ok => Ok(()), + ThreadStatus::ErrRun => { + // We've got a runtime error when executing the function so read the full stack + // trace produced by luaL_traceback and remove it from the stack. + let full_traceback: &str = FromLua::from_lua(vm, -1)?; + lua_remove(l, -1); + Err(Error::Runtime(RuntimeError::new(full_traceback.into()))) + } + ThreadStatus::ErrMem => Err(Error::Memory), + ThreadStatus::ErrErr => Err(Error::Error), + _ => Err(Error::Unknown) + } + } +} + pub struct Vm { l: State } @@ -138,10 +171,7 @@ impl Vm { pub fn run_code<'a, R: FromLua<'a>>(&'a self, code: impl LoadCode) -> crate::vm::Result { let l = self.as_ptr(); // Push error handler and the get the stack position of it. - let handler_pos = unsafe { - lua_pushcclosure(l, error_handler, 0); - lua_gettop(l) - }; + let handler_pos = push_error_handler(l); // Push the lua code. let res = code.load_code(l); if res != ThreadStatus::Ok { @@ -158,27 +188,7 @@ impl Vm { ThreadStatus::ErrMem => return Err(Error::Memory), _ => return Err(Error::Unknown) }; - unsafe { - // Call the function created by load_code. - let res = lua_pcall(l, 0, R::num_values() as _, handler_pos); - // At this point the stack should no longer have the function but still has the error - // handler and R::num_values results. - // First remove error handler as we no longer need it. - lua_remove(l, handler_pos); - match res { - ThreadStatus::Ok => (), - ThreadStatus::ErrRun => { - // We've got a runtime error when executing the function so read the full stack - // trace produced by luaL_traceback and remove it from the stack. - let full_traceback: &str = FromLua::from_lua(self, -1)?; - lua_remove(l, -1); - return Err(Error::Runtime(RuntimeError::new(full_traceback.into()))); - } - ThreadStatus::ErrMem => return Err(Error::Memory), - ThreadStatus::ErrErr => return Err(Error::Error), - _ => return Err(Error::Unknown) - }; - } + pcall(self, 0, R::num_values() as _, handler_pos)?; // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 1d69924..0e84490 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::Type; +use crate::vm::core::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Register; use crate::vm::value::{FromLua, IntoLua}; @@ -40,8 +41,10 @@ pub struct LuaFunction<'a> { impl<'a> LuaFunction<'a> { pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, value: T) -> crate::vm::Result { - //TODO: Implement - todo!() + let pos = push_error_handler(self.vm.as_ptr()); + let num_values = value.into_lua(self.vm)?; + pcall(self.vm, num_values as _, R::num_values() as _, pos)?; + R::from_lua(self.vm, -(R::num_values() as i32)) } } From d0efea712d3fa808e07611ccd1041697bf79c751 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 16:29:29 +0100 Subject: [PATCH 082/527] Improved performance of generated decl_from_param code --- core/src/macros/mod.rs | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 98cd270..f9bf682 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -46,9 +46,16 @@ macro_rules! decl_from_param { ) => { use $crate::vm::function::FromParam; let mut index = $start_index; - $( - let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, index) }; - index += 1; - )* - } + $crate::decl_from_param!(_from_param $vm, index, $(($arg_name: $arg_ty))*); + }; + + (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty)) => { + let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, $index) }; + }; + + (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty) $(($arg_name2: ident: $arg_ty2: ty))*) => { + let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, $index) }; + $index += 1; + $crate::decl_from_param!(_from_param $vm, $index, $(($arg_name2: $arg_ty2))*); + }; } From 9987e1c0ef741199afd98ae28ac29a520275b60c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 16:30:29 +0100 Subject: [PATCH 083/527] Fixed warnings in impl_tuple macro used by FromLua --- core/src/vm/value/core.rs | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index c334d7f..92def44 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -148,22 +148,36 @@ macro_rules! impl_tuple { } unsafe fn from_lua_unchecked(vm: &'a Vm, mut index: i32) -> Self { - $( - let $name2: $name = FromLua::from_lua_unchecked(vm, index); - index += 1; - )* + impl_tuple!(_from_lua_unchecked vm, index, $($name2: $name),*); ($($name2),*) } fn from_lua(vm: &'a Vm, mut index: i32) -> crate::vm::Result<($($name),*)> { - $( - let $name2: $name = FromLua::from_lua(vm, index)?; - index += 1; - )* + impl_tuple!(_from_lua vm, index, $($name2: $name),*); Ok(($($name2),*)) } } }; + + (_from_lua_unchecked $vm: ident, $index: ident, $name2: ident: $name: ident) => { + let $name2: $name = FromLua::from_lua_unchecked($vm, $index); + }; + + (_from_lua_unchecked $vm: ident, $index: ident, $name2: ident: $name: ident, $($name3: ident: $name4: ident),*) => { + let $name2: $name = FromLua::from_lua_unchecked($vm, $index); + $index += 1; + impl_tuple!(_from_lua_unchecked $vm, $index, $($name3: $name4),*); + }; + + (_from_lua $vm: ident, $index: ident, $name2: ident: $name: ident) => { + let $name2: $name = FromLua::from_lua($vm, $index)?; + }; + + (_from_lua $vm: ident, $index: ident, $name2: ident: $name: ident, $($name3: ident: $name4: ident),*) => { + let $name2: $name = FromLua::from_lua($vm, $index)?; + $index += 1; + impl_tuple!(_from_lua $vm, $index, $($name3: $name4),*); + }; } impl_tuple!(T: t, T1: t1); From f96078118d36c84ef88bcb8dc68d9dd0eda45610 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 16:34:30 +0100 Subject: [PATCH 084/527] Added support for FromLua up to 10 arguments in the tuple --- core/src/vm/value/core.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 92def44..ecd7be2 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -183,3 +183,9 @@ macro_rules! impl_tuple { impl_tuple!(T: t, T1: t1); impl_tuple!(T: t, T1: t1, T2: t2); impl_tuple!(T: t, T1: t1, T2: t2, T3: t3); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8); +impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8, T9: t9); From f73e60e7d2e19fea4e8ac0b0447201633198be28 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 16:57:29 +0100 Subject: [PATCH 085/527] Added support for IntoLua on tuples up to size 10 --- core/src/vm/value/core.rs | 53 ++++++++++++++++++++++++++++----------- 1 file changed, 38 insertions(+), 15 deletions(-) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index ecd7be2..cbf18bf 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -140,20 +140,20 @@ macro_rules! count_tts { ($_head:tt $($tail:tt)*) => {1 + count_tts!($($tail)*)}; } -macro_rules! impl_tuple { +macro_rules! impl_from_lua_tuple { ($($name: ident: $name2: ident),*) => { impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { fn num_values() -> u16 { - count_tts!($($name),*) + count_tts!($($name)*) } unsafe fn from_lua_unchecked(vm: &'a Vm, mut index: i32) -> Self { - impl_tuple!(_from_lua_unchecked vm, index, $($name2: $name),*); + impl_from_lua_tuple!(_from_lua_unchecked vm, index, $($name2: $name),*); ($($name2),*) } fn from_lua(vm: &'a Vm, mut index: i32) -> crate::vm::Result<($($name),*)> { - impl_tuple!(_from_lua vm, index, $($name2: $name),*); + impl_from_lua_tuple!(_from_lua vm, index, $($name2: $name),*); Ok(($($name2),*)) } } @@ -166,7 +166,7 @@ macro_rules! impl_tuple { (_from_lua_unchecked $vm: ident, $index: ident, $name2: ident: $name: ident, $($name3: ident: $name4: ident),*) => { let $name2: $name = FromLua::from_lua_unchecked($vm, $index); $index += 1; - impl_tuple!(_from_lua_unchecked $vm, $index, $($name3: $name4),*); + impl_from_lua_tuple!(_from_lua_unchecked $vm, $index, $($name3: $name4),*); }; (_from_lua $vm: ident, $index: ident, $name2: ident: $name: ident) => { @@ -176,16 +176,39 @@ macro_rules! impl_tuple { (_from_lua $vm: ident, $index: ident, $name2: ident: $name: ident, $($name3: ident: $name4: ident),*) => { let $name2: $name = FromLua::from_lua($vm, $index)?; $index += 1; - impl_tuple!(_from_lua $vm, $index, $($name3: $name4),*); + impl_from_lua_tuple!(_from_lua $vm, $index, $($name3: $name4),*); }; } -impl_tuple!(T: t, T1: t1); -impl_tuple!(T: t, T1: t1, T2: t2); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8); -impl_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8, T9: t9); +impl_from_lua_tuple!(T: t, T1: t1); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8); +impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8, T9: t9); + +macro_rules! impl_into_lua_tuple { + ($($name: ident: $name2: tt),*) => { + impl<$($name: IntoLua),*> IntoLua for ($($name),*) { + fn into_lua(self, vm: &Vm) -> Result { + $( + self.$name2.into_lua(vm)?; + )* + Ok(count_tts!($($name)*)) + } + } + }; +} + +impl_into_lua_tuple!(T: 0, T1: 1); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); +impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); From 79e8e15cee5d5b1f4e05f660632050d4cb838f92 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:30:00 +0100 Subject: [PATCH 086/527] Fixed crash in LuaFunction::call and added first 2 tests for functions --- core/src/vm/core.rs | 5 ++-- core/src/vm/value/function.rs | 5 ++-- core/tests/test_vm_functions.rs | 52 +++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 4 deletions(-) create mode 100644 core/tests/test_vm_functions.rs diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 9a2d712..b6ae39f 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -74,7 +74,8 @@ pub(super) fn push_error_handler(l: State) -> c_int { } } -pub(super) fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { +/// This function is highly unsafe it may crash at any time. +pub(super) unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { let l = vm.as_ptr(); unsafe { // Call the function created by load_code. @@ -188,7 +189,7 @@ impl Vm { ThreadStatus::ErrMem => return Err(Error::Memory), _ => return Err(Error::Unknown) }; - pcall(self, 0, R::num_values() as _, handler_pos)?; + unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 0e84490..a1bd9ec 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::Type; +use crate::ffi::lua::{lua_pushvalue, Type}; use crate::vm::core::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Register; @@ -42,8 +42,9 @@ pub struct LuaFunction<'a> { impl<'a> LuaFunction<'a> { pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, value: T) -> crate::vm::Result { let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index); } let num_values = value.into_lua(self.vm)?; - pcall(self.vm, num_values as _, R::num_values() as _, pos)?; + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } } diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs new file mode 100644 index 0000000..3680bfc --- /dev/null +++ b/core/tests/test_vm_functions.rs @@ -0,0 +1,52 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::function::LuaFunction; + +#[test] +fn test_vm_function_1_arg() { + let mut vm = RootVm::new(); + let f: LuaFunction = vm.run_code(c"return function(value) return 'this is a test ' .. value end").unwrap(); + let str: &str = f.call(42.42).unwrap(); + assert_eq!(str, "this is a test 42.42"); + let str: &str = f.call(42).unwrap(); + assert_eq!(str, "this is a test 42"); + vm.clear(); +} + +#[test] +fn test_vm_function_2_args() { + let mut vm = RootVm::new(); + let f: LuaFunction = vm.run_code(c"return function(value, value2) return 'this ' .. value .. ' is a test ' .. tostring(value2) end").unwrap(); + let str: &str = f.call((42.42, false)).unwrap(); + assert_eq!(str, "this 42.42 is a test false"); + let str: &str = f.call((42, true)).unwrap(); + assert_eq!(str, "this 42 is a test true"); + vm.clear(); +} From 6dde2771695078f1a0246a259573f09e732a04e2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:38:37 +0100 Subject: [PATCH 087/527] Added call to RegistryKey to improve performance --- core/src/vm/registry/core.rs | 11 +++++++++++ core/src/vm/registry/types.rs | 14 +++++++++++++- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 9fdd787..e6668c3 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -51,6 +51,17 @@ impl RegistryKey { T::to_lua_value(vm, -1) } + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + pub fn raw_push(&self, vm: &Vm) { + unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; + } + /// Deletes this registry key from the specified [Vm]. /// /// # Arguments diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 5d1f470..ebcdc32 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,8 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::registry::core::RegistryKey; use crate::vm::registry::RegistryValue; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; pub struct Table; @@ -50,3 +52,13 @@ impl RegistryValue for LuaFunction { unsafe { crate::vm::value::function::LuaFunction::from_lua_unchecked(vm, index) } } } + +impl RegistryKey { + pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { + let pos = push_error_handler(vm.as_ptr()); + self.raw_push(vm); + let num_values = value.into_lua(vm)?; + unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(vm, -(R::num_values() as i32)) + } +} From 0a98924b1032fb1cd51625a3d50fc9301a72357c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:41:40 +0100 Subject: [PATCH 088/527] Added call to Table for stack optimization --- core/src/vm/value/table.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index b51f380..4ef7215 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -30,6 +30,7 @@ use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, Type}; use crate::util::{AnyStr, SimpleDrop}; +use crate::vm::core::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Register; @@ -100,6 +101,14 @@ impl<'a> Scope<'a> { T::from_lua(self.vm, -1) } } + + pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + let num_values = value.into_lua(self.vm)?; + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } } impl Drop for Scope<'_> { From 77932a718856246afa7b6ff199f96d2add117094 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:48:42 +0100 Subject: [PATCH 089/527] Renamed call to call_function and added call_method to optimize for table method calls --- core/src/vm/value/table.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index 4ef7215..e68f145 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -102,13 +102,22 @@ impl<'a> Scope<'a> { } } - pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { let pos = push_error_handler(self.vm.as_ptr()); unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; let num_values = value.into_lua(self.vm)?; unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } + + pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + let num_values = value.into_lua(self.vm)?; + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } } impl Drop for Scope<'_> { From ea8ec50e5a8e1c8df9ecd182ddc20691caac837f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:56:00 +0100 Subject: [PATCH 090/527] Moved call_function and call_method to Table instead of Scope --- core/src/vm/value/table.rs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index e68f145..e6a0ad4 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -101,23 +101,6 @@ impl<'a> Scope<'a> { T::from_lua(self.vm, -1) } } - - pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - let num_values = value.into_lua(self.vm)?; - unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } - - pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - let num_values = value.into_lua(self.vm)?; - unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } } impl Drop for Scope<'_> { @@ -167,6 +150,23 @@ impl<'a> Table<'a> { pub fn is_empty(&self) -> bool { self.len() == 0 } + + pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + let num_values = value.into_lua(self.vm)?; + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } + + pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + let num_values = value.into_lua(self.vm)?; + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } } unsafe impl<'a> SimpleDrop for Table<'a> {} From acd883b9b23a848dcd0fd34482923026743f55ae Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 17:59:18 +0100 Subject: [PATCH 091/527] Fixed bug in call_method and added more tests --- core/src/vm/value/table.rs | 2 +- core/tests/test_vm_functions.rs | 27 +++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index e6a0ad4..27bbbf7 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -164,7 +164,7 @@ impl<'a> Table<'a> { unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; let num_values = value.into_lua(self.vm)?; - unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } } diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 3680bfc..6a4deca 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -26,27 +26,54 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::CStr; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::value::table::Table; #[test] fn test_vm_function_1_arg() { let mut vm = RootVm::new(); + let top = vm.top(); let f: LuaFunction = vm.run_code(c"return function(value) return 'this is a test ' .. value end").unwrap(); let str: &str = f.call(42.42).unwrap(); assert_eq!(str, "this is a test 42.42"); let str: &str = f.call(42).unwrap(); assert_eq!(str, "this is a test 42"); + assert_eq!(vm.top(), top + 3); // Function + 2 results vm.clear(); } #[test] fn test_vm_function_2_args() { let mut vm = RootVm::new(); + let top = vm.top(); let f: LuaFunction = vm.run_code(c"return function(value, value2) return 'this ' .. value .. ' is a test ' .. tostring(value2) end").unwrap(); let str: &str = f.call((42.42, false)).unwrap(); assert_eq!(str, "this 42.42 is a test false"); let str: &str = f.call((42, true)).unwrap(); assert_eq!(str, "this 42 is a test true"); + assert_eq!(vm.top(), top + 3); // Function + 2 results + vm.clear(); +} + +const METHODS: &CStr = c" +local obj = { ctx = 'this is a test' } + +function obj:greeting() + return 'Hello ' .. self.ctx +end + +return obj +"; + +#[test] +fn test_vm_function_method() { + let mut vm = RootVm::new(); + let top = vm.top(); + let obj: Table = vm.run_code(METHODS).unwrap(); + let str: &str = obj.call_method(c"greeting", ()).unwrap(); + assert_eq!(str, "Hello this is a test"); + assert_eq!(vm.top(), top + 2); // Table + 1 result vm.clear(); } From 262305b337b1be7ac35b49cd5d6b873debd15843 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 22:56:40 +0100 Subject: [PATCH 092/527] Added support for reading and writing &[u8] values to Lua --- core/src/util.rs | 1 + core/src/vm/closure/core.rs | 7 +++++++ core/src/vm/function/core.rs | 30 ++++++++++++++++++++++++++++++ core/src/vm/value/core.rs | 28 ++++++++++++++++++++++++++++ 4 files changed, 66 insertions(+) diff --git a/core/src/util.rs b/core/src/util.rs index 9a77029..d974bcc 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -69,3 +69,4 @@ unsafe impl SimpleDrop for bool {} unsafe impl SimpleDrop for Option {} unsafe impl SimpleDrop for Result {} unsafe impl SimpleDrop for &T {} +unsafe impl SimpleDrop for &[u8] {} diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index fc9cfeb..23ab8b7 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -52,6 +52,13 @@ impl<'a> FromUpvalue<'a> for &'a str { } } +impl<'a> FromUpvalue<'a> for &'a [u8] { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + FromLua::from_lua_unchecked(vm, GLOBALSINDEX - index) + } +} + #[cfg(target_pointer_width = "64")] impl_from_upvalue_using_from_lua_unchecked!(i64, u64); diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index ba20127..98e00cb 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -65,9 +65,31 @@ impl<'a> FromParam<'a> for &'a str { } } +impl LuaType for &[u8] {} + +impl<'a> FromParam<'a> for &'a [u8] { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + let mut len: usize = 0; + let str = luaL_checklstring(vm.as_ptr(), index, &mut len as _); + let slice = slice::from_raw_parts(str as *const u8, len); + slice + } +} + unsafe impl SimpleDrop for &str {} impl IntoParam for &str { + #[inline(always)] + fn into_param(self, vm: &Vm) -> u16 { + unsafe { + lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); + } + 1 + } +} + +impl IntoParam for &[u8] { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); @@ -77,6 +99,7 @@ impl IntoParam for &str { } impl IntoParam for String { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { (&*self).into_param(vm) } @@ -94,12 +117,14 @@ macro_rules! impl_integer { } impl FromParam<'_> for $t { + #[inline(always)] unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checkinteger(vm.as_ptr(), index) as _ } } impl IntoParam for $t { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushinteger(vm.as_ptr(), self as _); @@ -128,12 +153,14 @@ macro_rules! impl_float { } impl FromParam<'_> for $t { + #[inline(always)] unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checknumber(vm.as_ptr(), index) as _ } } impl IntoParam for $t { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushnumber(vm.as_ptr(), self as _); @@ -148,6 +175,7 @@ macro_rules! impl_float { impl_float!(f32, f64); impl IntoParam for bool { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushboolean(vm.as_ptr(), if self { 1 } else { 0 }) }; 1 @@ -182,6 +210,7 @@ impl IntoParam for Option { } impl IntoParam for () { + #[inline(always)] fn into_param(self, _: &Vm) -> u16 { 0 } @@ -194,6 +223,7 @@ impl LuaType for &T { } impl<'a, T: UserData> FromParam<'a> for &'a T { + #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> &'a T { let obj_ptr = unsafe { luaL_checkudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; unsafe { &*obj_ptr } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index cbf18bf..6b99237 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -63,6 +63,34 @@ impl<'a> FromLua<'a> for &'a str { } } +impl<'a> FromLua<'a> for &'a [u8] { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let mut len: usize = 0; + let str = lua_tolstring(vm.as_ptr(), index, &mut len as _); + let slice = std::slice::from_raw_parts(str as *const u8, len); + slice + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let l = vm.as_ptr(); + unsafe { + let ty = lua_type(l, index); + match ty { + Type::String => { + let mut len: usize = 0; + let s = lua_tolstring(l, index, &mut len as _); + let slice = std::slice::from_raw_parts(s as *const u8, len); + Ok(slice) + }, + _ => Err(Error::Type(TypeError { + expected: Type::String, + actual: ty + })) + } + } + } +} + macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { From 389b31deea43489006bc5c4bf93113c661b77555 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 15 Mar 2025 23:02:11 +0100 Subject: [PATCH 093/527] Improved optimization of IntoLua by removing unused Result type --- core/src/util.rs | 8 ++++---- core/src/vm/closure/types.rs | 4 ++-- core/src/vm/core.rs | 2 +- core/src/vm/function/types.rs | 5 +++-- core/src/vm/registry/types.rs | 2 +- core/src/vm/value/core.rs | 11 ++++++----- core/src/vm/value/function.rs | 2 +- core/src/vm/value/interface.rs | 4 ++-- core/src/vm/value/table.rs | 11 +++++++---- 9 files changed, 27 insertions(+), 22 deletions(-) diff --git a/core/src/util.rs b/core/src/util.rs index d974bcc..4f0484b 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -37,25 +37,25 @@ pub trait AnyStr { impl AnyStr for String { fn to_str(&self) -> crate::vm::Result> { - let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; - Ok(Cow::Owned(cstr)) + Ok(Cow::Owned(CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?)) } } impl AnyStr for &str { fn to_str(&self) -> crate::vm::Result> { - let cstr = CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?; - Ok(Cow::Owned(cstr)) + Ok(Cow::Owned(CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?)) } } impl AnyStr for CString { + #[inline(always)] fn to_str(&self) -> crate::vm::Result> { Ok(Cow::Borrowed(&**self)) } } impl AnyStr for &CStr { + #[inline(always)] fn to_str(&self) -> crate::vm::Result> { Ok(Cow::Borrowed(&**self)) } diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index ee06b82..cdd6574 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -53,10 +53,10 @@ impl RClosure { } impl IntoLua for RClosure { - fn into_lua(self, vm: &Vm) -> crate::vm::Result { + fn into_lua(self, vm: &Vm) -> u16 { let num = self.upvalue.into_upvalue(vm); unsafe { lua_pushcclosure(vm.as_ptr(), self.func, num as _) }; - Ok(1) + 1 } } diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index b6ae39f..ccfa3ea 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -155,7 +155,7 @@ impl Vm { } pub fn set_global(&self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { - value.into_lua(self)?; + value.into_lua(self); unsafe { lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } diff --git a/core/src/vm/function/types.rs b/core/src/vm/function/types.rs index 80e4593..c23a700 100644 --- a/core/src/vm/function/types.rs +++ b/core/src/vm/function/types.rs @@ -33,17 +33,18 @@ use crate::vm::Vm; pub struct RFunction(CFunction); impl RFunction { + #[inline(always)] pub fn wrap(inner: CFunction) -> Self { Self(inner) } } impl IntoLua for RFunction { - fn into_lua(self, vm: &Vm) -> crate::vm::Result { + fn into_lua(self, vm: &Vm) -> u16 { let l = vm.as_ptr(); unsafe { lua_pushcclosure(l, self.0, 0); } - Ok(1) + 1 } } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index ebcdc32..f92dc1f 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -57,7 +57,7 @@ impl RegistryKey { pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { let pos = push_error_handler(vm.as_ptr()); self.raw_push(vm); - let num_values = value.into_lua(vm)?; + let num_values = value.into_lua(vm); unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(vm, -(R::num_values() as i32)) } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 6b99237..ac9e0ec 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -126,8 +126,9 @@ impl_from_lua!(f64, Number, lua_tonumber, as _); impl_from_lua!(bool, Boolean, lua_toboolean, == 1); impl IntoLua for T { - fn into_lua(self, vm: &Vm) -> Result { - Ok(self.into_param(vm)) + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + self.into_param(vm) } } @@ -221,11 +222,11 @@ impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t macro_rules! impl_into_lua_tuple { ($($name: ident: $name2: tt),*) => { impl<$($name: IntoLua),*> IntoLua for ($($name),*) { - fn into_lua(self, vm: &Vm) -> Result { + fn into_lua(self, vm: &Vm) -> u16 { $( - self.$name2.into_lua(vm)?; + self.$name2.into_lua(vm); )* - Ok(count_tts!($($name)*)) + count_tts!($($name)*) } } }; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index a1bd9ec..6f93717 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -43,7 +43,7 @@ impl<'a> LuaFunction<'a> { pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, value: T) -> crate::vm::Result { let pos = push_error_handler(self.vm.as_ptr()); unsafe { lua_pushvalue(self.vm.as_ptr(), self.index); } - let num_values = value.into_lua(self.vm)?; + let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index ee0a395..7ba570b 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -69,6 +69,6 @@ pub trait IntoLua: Sized { /// /// * `vm`: the [Vm] to push into. /// - /// returns: Result - fn into_lua(self, vm: &Vm) -> crate::vm::Result; + /// returns: u16 number of elements pushed onto the Lua stack. + fn into_lua(self, vm: &Vm) -> u16; } diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs index 27bbbf7..d1f090a 100644 --- a/core/src/vm/value/table.rs +++ b/core/src/vm/value/table.rs @@ -58,7 +58,7 @@ impl<'a> Scope<'a> { pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - let nums = value.into_lua(self.vm)?; + let nums = value.into_lua(self.vm); if nums > 1 { // Clear the stack. lua_settop(self.vm.as_ptr(), -(nums as i32)-1); @@ -81,7 +81,7 @@ impl<'a> Scope<'a> { pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - let nums = value.into_lua(self.vm)?; + let nums = value.into_lua(self.vm); if nums > 1 { // Clear the stack. lua_settop(self.vm.as_ptr(), -(nums as i32)-1); @@ -126,6 +126,7 @@ impl<'a> Table<'a> { Self { vm, index } } + #[inline(always)] pub fn lock(&mut self) -> Scope { Scope::new(self.vm, self.index) } @@ -147,6 +148,7 @@ impl<'a> Table<'a> { count } + #[inline(always)] pub fn is_empty(&self) -> bool { self.len() == 0 } @@ -154,7 +156,7 @@ impl<'a> Table<'a> { pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { let pos = push_error_handler(self.vm.as_ptr()); unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - let num_values = value.into_lua(self.vm)?; + let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } @@ -163,7 +165,7 @@ impl<'a> Table<'a> { let pos = push_error_handler(self.vm.as_ptr()); unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - let num_values = value.into_lua(self.vm)?; + let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } @@ -172,6 +174,7 @@ impl<'a> Table<'a> { unsafe impl<'a> SimpleDrop for Table<'a> {} impl<'a> FromParam<'a> for Table<'a> { + #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { luaL_checktype(vm.as_ptr(), index, Type::Table); Table { From 3d781a069054e266df721052a17acfb1805f6e85 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 16:42:33 +0100 Subject: [PATCH 094/527] Refactored Table to its own module --- core/src/vm/mod.rs | 1 + core/src/vm/registry/core.rs | 3 + core/src/vm/registry/types.rs | 4 +- core/src/vm/table/core.rs | 123 ++++++++++++++++++++++++++++++++ core/src/vm/table/interface.rs | 83 +++++++++++++++++++++ core/src/vm/table/mod.rs | 34 +++++++++ core/src/vm/table/scope.rs | 101 ++++++++++++++++++++++++++ core/tests/test_vm_functions.rs | 2 +- core/tests/test_vm_tables.rs | 2 +- 9 files changed, 349 insertions(+), 4 deletions(-) create mode 100644 core/src/vm/table/core.rs create mode 100644 core/src/vm/table/interface.rs create mode 100644 core/src/vm/table/mod.rs create mode 100644 core/src/vm/table/scope.rs diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 356d43a..8227cba 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -34,6 +34,7 @@ pub mod util; pub mod userdata; pub mod closure; pub mod registry; +pub mod table; pub use core::*; diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index e6668c3..5004e2f 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -46,6 +46,7 @@ impl RegistryKey { /// * `vm`: the [Vm] to attach the produced lua value to. /// /// returns: ::Value + #[inline(always)] pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; T::to_lua_value(vm, -1) @@ -58,6 +59,7 @@ impl RegistryKey { /// * `vm`: the [Vm] to attach the produced lua value to. /// /// returns: ::Value + #[inline(always)] pub fn raw_push(&self, vm: &Vm) { unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; } @@ -69,6 +71,7 @@ impl RegistryKey { /// * `vm`: the [Vm] to unregister from. /// /// returns: () + #[inline(always)] pub fn delete(self, vm: &Vm) { unsafe { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.key) }; } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index f92dc1f..63f85d5 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -36,11 +36,11 @@ pub struct Table; pub struct LuaFunction; impl RegistryValue for Table { - type Value<'a> = crate::vm::value::table::Table<'a>; + type Value<'a> = crate::vm::table::Table<'a>; #[inline(always)] fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { - unsafe { crate::vm::value::table::Table::from_lua_unchecked(vm, index) } + unsafe { crate::vm::table::Table::from_lua_unchecked(vm, index) } } } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs new file mode 100644 index 0000000..1948c0e --- /dev/null +++ b/core/src/vm/table/core.rs @@ -0,0 +1,123 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::ext::{lua_ext_tab_len, MSize}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_settop}; +use crate::util::AnyStr; +use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::table::Scope; +use crate::vm::Vm; + +pub struct Table<'a> { + vm: &'a Vm, + index: i32 +} + +impl<'a> Table<'a> { + /// Creates a table from a raw Vm and index. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a table and is absolute. If index is not absolute then + /// using the produced table is UB. If the index points to any other type then using the produced + /// table is also UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self { vm, index } + } + + pub fn new(vm: &'a Vm) -> Self { + unsafe { lua_createtable(vm.as_ptr(), 0, 0) }; + let index = unsafe { lua_gettop(vm.as_ptr()) }; + Self { vm, index } + } + + pub fn with_capacity(vm: &'a Vm, array_capacity: usize, non_array_capcity: usize) -> Self { + unsafe { lua_createtable(vm.as_ptr(), array_capacity as _, non_array_capcity as _) }; + let index = unsafe { lua_gettop(vm.as_ptr()) }; + Self { vm, index } + } + + #[inline(always)] + pub fn lock(&mut self) -> Scope { + Scope::new(self.vm, self.index) + } + + pub fn len(&self) -> usize { + let mut size: MSize = 0; + let ret = unsafe { lua_ext_tab_len(self.vm.as_ptr(), self.index, &mut size) }; + if ret == 0 { + return size as _; + } + let mut count = 0; + unsafe { + lua_pushnil(self.vm.as_ptr()); + while lua_next(self.vm.as_ptr(), self.index) != 0 { + lua_settop(self.vm.as_ptr(), -2); + count += 1; + } + } + count + } + + /// Returns the absolute index of this table on the Lua stack. + #[inline(always)] + pub fn index(&self) -> i32 { + self.index + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + let num_values = value.into_lua(self.vm); + unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } + + pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + let pos = push_error_handler(self.vm.as_ptr()); + unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + let num_values = value.into_lua(self.vm); + unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; + R::from_lua(self.vm, -(R::num_values() as i32)) + } +} diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs new file mode 100644 index 0000000..2a144e8 --- /dev/null +++ b/core/src/vm/table/interface.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::laux::luaL_checktype; +use crate::ffi::lua::{lua_gettop, lua_pushvalue, Type}; +use crate::util::SimpleDrop; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::Register; +use crate::vm::util::LuaType; +use crate::vm::value::FromLua; +use crate::vm::table::Table; +use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::Vm; + +unsafe impl<'a> SimpleDrop for Table<'a> {} + +impl<'a> FromParam<'a> for Table<'a> { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + luaL_checktype(vm.as_ptr(), index, Type::Table); + Table::from_raw(vm, index) + } +} + +impl<'a> FromLua<'a> for Table<'a> { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Table::from_raw(vm, vm.get_absolute_index(index)) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + ensure_type_equals(vm, index, Type::Table)?; + Ok(unsafe { Table::from_raw(vm, vm.get_absolute_index(index)) }) + } +} + +impl IntoParam for Table<'_> { + fn into_param(self, vm: &Vm) -> u16 { + let top = unsafe { lua_gettop(vm.as_ptr()) }; + if top != self.index() { + unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; + } + 1 + } +} + +impl LuaType for Table<'_> {} + +impl Register for Table<'_> { + type RegistryValue = crate::vm::registry::types::Table; + + fn register(self, vm: &Vm) -> RegistryKey { + // If the table is not at the top of the stack, move it to the top. + ensure_value_top(vm, self.index()); + unsafe { RegistryKey::from_top(vm) } + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs new file mode 100644 index 0000000..c320e85 --- /dev/null +++ b/core/src/vm/table/mod.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod scope; +mod core; +mod interface; + +pub use core::Table; +pub use scope::Scope; diff --git a/core/src/vm/table/scope.rs b/core/src/vm/table/scope.rs new file mode 100644 index 0000000..563d863 --- /dev/null +++ b/core/src/vm/table/scope.rs @@ -0,0 +1,101 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_getfield, lua_gettop, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop}; +use crate::util::AnyStr; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; + +pub struct Scope<'a> { + vm: &'a Vm, + index: i32, + initial_top: i32 +} + +impl<'a> Scope<'a> { + pub(super) fn new(vm: &'a Vm, index: i32) -> Self { + let initial_top = unsafe { lua_gettop(vm.as_ptr()) }; + Self { vm, index, initial_top } + } + + pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm); + if nums > 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); + } + Ok(()) + } + + pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { + if T::num_values() > 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); + T::from_lua(self.vm, -1) + } + } + + pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm); + if nums > 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_rawseti(self.vm.as_ptr(), self.index, i); + } + Ok(()) + } + + pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { + if T::num_values() > 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + lua_rawgeti(self.vm.as_ptr(), self.index, i); + T::from_lua(self.vm, -1) + } + } +} + +impl Drop for Scope<'_> { + fn drop(&mut self) { + let top = unsafe { lua_gettop(self.vm.as_ptr()) }; + let count = top - self.initial_top; + // Pop count values off the stack to ensure the stack is cleared after all table + // manipulations are finished. + unsafe { lua_settop(self.vm.as_ptr(), -count-1) }; + } +} diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 6a4deca..af743bb 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -29,7 +29,7 @@ use std::ffi::CStr; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; -use bp3d_lua::vm::value::table::Table; +use bp3d_lua::vm::table::Table; #[test] fn test_vm_function_1_arg() { diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index ec59d42..8ea37c6 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::table::Table; +use bp3d_lua::vm::table::Table; #[test] fn tables() { From 8002a3650a4ebce742046b22065d0cdc4af6612e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 16:42:50 +0100 Subject: [PATCH 095/527] Added new AnyValue --- core/src/vm/error.rs | 3 +- core/src/vm/value/any.rs | 79 ++++++++++++++++++++++++++++++++++++++++ core/src/vm/value/mod.rs | 2 +- 3 files changed, 82 insertions(+), 2 deletions(-) create mode 100644 core/src/vm/value/any.rs diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index c046037..eacd2e3 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -88,7 +88,8 @@ simple_error! { Error => "error in error handler", Null => "string contains a null character", MultiValue => "only one value is supported by this API", - UserData(crate::vm::userdata::Error) => "userdata: {}" + UserData(crate::vm::userdata::Error) => "userdata: {}", + UnsupportedType(Type) => "unsupported lua type: {:?}" } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs new file mode 100644 index 0000000..da24187 --- /dev/null +++ b/core/src/vm/value/any.rs @@ -0,0 +1,79 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_toboolean, lua_tonumber, lua_type, Type}; +use crate::vm::error::Error; +use crate::vm::value::FromLua; +use crate::vm::value::function::LuaFunction; +use crate::vm::table::Table; +use crate::vm::Vm; + +pub enum AnyValue<'a> { + None, + Nil, + Number(f64), + Boolean(bool), + String(&'a str), + Buffer(&'a [u8]), + Function(LuaFunction<'a>), + Table(Table<'a>) +} + +impl<'a> FromLua<'a> for AnyValue<'a> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Self::from_lua(vm, index).unwrap_unchecked() + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let ty = unsafe { lua_type(vm.as_ptr(), index) }; + match ty { + Type::None => Ok(AnyValue::None), + Type::Nil => Ok(AnyValue::Nil), + Type::Boolean => { + let value = unsafe { lua_toboolean(vm.as_ptr(), index) }; + Ok(AnyValue::Boolean(value == 1)) + } + Type::LightUserdata => Err(Error::UnsupportedType(ty)), + Type::Number => { + let value = unsafe { lua_tonumber(vm.as_ptr(), index) }; + Ok(AnyValue::Number(value)) + } + Type::String => { + let buffer: &[u8] = unsafe { FromLua::from_lua_unchecked(vm, index) }; + match std::str::from_utf8(buffer) { + Ok(s) => Ok(AnyValue::String(s)), + Err(_) => Ok(AnyValue::Buffer(buffer)) + } + } + Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), + Type::Function => Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }), + Type::Userdata => Err(Error::UnsupportedType(ty)), + Type::Thread => Err(Error::UnsupportedType(ty)) + } + } +} diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 7244c7f..a5063aa 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -28,8 +28,8 @@ mod interface; mod core; -pub mod table; pub mod function; pub mod util; +pub mod any; pub use interface::*; From 5e6d63c66eefec13c4dfb8b2e8360bb1670c2746 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 16:43:12 +0100 Subject: [PATCH 096/527] Removed now unused table module in value --- core/src/vm/value/table.rs | 219 ------------------------------------- 1 file changed, 219 deletions(-) delete mode 100644 core/src/vm/value/table.rs diff --git a/core/src/vm/value/table.rs b/core/src/vm/value/table.rs deleted file mode 100644 index d1f090a..0000000 --- a/core/src/vm/value/table.rs +++ /dev/null @@ -1,219 +0,0 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, Type}; -use crate::util::{AnyStr, SimpleDrop}; -use crate::vm::core::{pcall, push_error_handler}; -use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::registry::core::RegistryKey; -use crate::vm::registry::Register; -use crate::vm::Vm; -use crate::vm::util::LuaType; -use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; - -pub struct Table<'a> { - vm: &'a Vm, - index: i32 -} - -pub struct Scope<'a> { - vm: &'a Vm, - index: i32, - initial_top: i32 -} - -impl<'a> Scope<'a> { - fn new(vm: &'a Vm, index: i32) -> Self { - let initial_top = unsafe { lua_gettop(vm.as_ptr()) }; - Self { vm, index, initial_top } - } - - pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { - unsafe { - let nums = value.into_lua(self.vm); - if nums > 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } - lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); - } - Ok(()) - } - - pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { - if T::num_values() > 1 { - return Err(crate::vm::error::Error::MultiValue); - } - unsafe { - lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); - T::from_lua(self.vm, -1) - } - } - - pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { - unsafe { - let nums = value.into_lua(self.vm); - if nums > 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } - lua_rawseti(self.vm.as_ptr(), self.index, i); - } - Ok(()) - } - - pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { - if T::num_values() > 1 { - return Err(crate::vm::error::Error::MultiValue); - } - unsafe { - lua_rawgeti(self.vm.as_ptr(), self.index, i); - T::from_lua(self.vm, -1) - } - } -} - -impl Drop for Scope<'_> { - fn drop(&mut self) { - let top = unsafe { lua_gettop(self.vm.as_ptr()) }; - let count = top - self.initial_top; - // Pop count values off the stack to ensure the stack is cleared after all table - // manipulations are finished. - unsafe { lua_settop(self.vm.as_ptr(), -count-1) }; - } -} - -impl<'a> Table<'a> { - pub fn new(vm: &'a Vm) -> Self { - unsafe { lua_createtable(vm.as_ptr(), 0, 0) }; - let index = unsafe { lua_gettop(vm.as_ptr()) }; - Self { vm, index } - } - - pub fn with_capacity(vm: &'a Vm, array_capacity: usize, non_array_capcity: usize) -> Self { - unsafe { lua_createtable(vm.as_ptr(), array_capacity as _, non_array_capcity as _) }; - let index = unsafe { lua_gettop(vm.as_ptr()) }; - Self { vm, index } - } - - #[inline(always)] - pub fn lock(&mut self) -> Scope { - Scope::new(self.vm, self.index) - } - - pub fn len(&self) -> usize { - let mut size: MSize = 0; - let ret = unsafe { lua_ext_tab_len(self.vm.as_ptr(), self.index, &mut size) }; - if ret == 0 { - return size as _; - } - let mut count = 0; - unsafe { - lua_pushnil(self.vm.as_ptr()); - while lua_next(self.vm.as_ptr(), self.index) != 0 { - lua_settop(self.vm.as_ptr(), -2); - count += 1; - } - } - count - } - - #[inline(always)] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - let num_values = value.into_lua(self.vm); - unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } - - pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - let num_values = value.into_lua(self.vm); - unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } -} - -unsafe impl<'a> SimpleDrop for Table<'a> {} - -impl<'a> FromParam<'a> for Table<'a> { - #[inline(always)] - unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { - luaL_checktype(vm.as_ptr(), index, Type::Table); - Table { - vm, - index - } - } -} - -impl<'a> FromLua<'a> for Table<'a> { - #[inline(always)] - unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { - Table { vm, index: vm.get_absolute_index(index) } - } - - fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::Table)?; - Ok(Table { vm, index: vm.get_absolute_index(index) }) - } -} - -impl IntoParam for Table<'_> { - fn into_param(self, vm: &Vm) -> u16 { - let top = unsafe { lua_gettop(vm.as_ptr()) }; - if top != self.index { - unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; - } - 1 - } -} - -impl LuaType for Table<'_> {} - -impl Register for Table<'_> { - type RegistryValue = crate::vm::registry::types::Table; - - fn register(self, vm: &Vm) -> RegistryKey { - // If the table is not at the top of the stack, move it to the top. - ensure_value_top(vm, self.index); - unsafe { RegistryKey::from_top(vm) } - } -} From 4deafb78cc8b89b554df1b136b6179e2739e68b0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 21:17:02 +0100 Subject: [PATCH 097/527] Re-implemented Table::len in function of new table iterator system --- core/src/vm/table/core.rs | 20 +++++----- core/src/vm/table/iter.rs | 83 +++++++++++++++++++++++++++++++++++++++ core/src/vm/table/mod.rs | 1 + 3 files changed, 94 insertions(+), 10 deletions(-) create mode 100644 core/src/vm/table/iter.rs diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 1948c0e..cf2e8e5 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,9 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_next, lua_pushnil, lua_pushvalue, lua_settop}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue}; use crate::util::AnyStr; use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::table::iter::Iter; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::table::Scope; use crate::vm::Vm; @@ -82,15 +83,7 @@ impl<'a> Table<'a> { if ret == 0 { return size as _; } - let mut count = 0; - unsafe { - lua_pushnil(self.vm.as_ptr()); - while lua_next(self.vm.as_ptr(), self.index) != 0 { - lua_settop(self.vm.as_ptr(), -2); - count += 1; - } - } - count + Iter::from_raw(self.vm, self.index).count() as _ } /// Returns the absolute index of this table on the Lua stack. @@ -120,4 +113,11 @@ impl<'a> Table<'a> { unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } + + /// Creates a new iterator for this table. + /// + /// This function borrows mutably to avoid messing up the Lua stack while iterating. + pub fn iter(&mut self) -> Iter { + Iter::from_raw(self.vm, self.index) + } } diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs new file mode 100644 index 0000000..122be2c --- /dev/null +++ b/core/src/vm/table/iter.rs @@ -0,0 +1,83 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_next, lua_pushnil, lua_settop}; +use crate::vm::value::any::AnyValue; +use crate::vm::value::FromLua; +use crate::vm::Vm; + +pub struct Iter<'a> { + vm: &'a Vm, + index: i32, + has_ended: bool, + has_started: bool +} + +impl<'a> Iter<'a> { + pub(super) fn from_raw(vm: &'a Vm, index: i32) -> Self { + // Push a nil value on the stack to allow the iterator to work. + unsafe { lua_pushnil(vm.as_ptr()) }; + Self { vm, index, has_ended: false, has_started: false } + } +} + +impl<'a> Iterator for Iter<'a> { + type Item = crate::vm::Result<(AnyValue<'a>, AnyValue<'a>)>; + + fn next(&mut self) -> Option { + if self.has_started { + // Pop the last value on the stack which corresponds to the last value from lua_next. + // Only if the iterator was started. + unsafe { lua_settop(self.vm.as_ptr(), -2) }; + } + let ret = unsafe { lua_next(self.vm.as_ptr(), self.index) }; + self.has_started = true; + if ret != 0 { + let value = AnyValue::from_lua(self.vm, -1); + let key = AnyValue::from_lua(self.vm, -2); + Some(match (value, key) { + (Ok(key), Ok(value)) => Ok((key, value)), + (Ok(_), Err(e)) => Err(e), + (Err(e), Ok(_)) => Err(e), + (Err(_), Err(e)) => Err(e), + }) + } else { + self.has_ended = true; + None + } + } +} + +impl Drop for Iter<'_> { + fn drop(&mut self) { + if !self.has_ended { + // If the iterator did not reach the end, clear key-value pair from the lua stack. + unsafe { lua_settop(self.vm.as_ptr(), -3) }; + } + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index c320e85..b5c6c77 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -29,6 +29,7 @@ mod scope; mod core; mod interface; +mod iter; pub use core::Table; pub use scope::Scope; From aeca44e9015ad122a8a608643c9e092b2a14f09d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 21:24:10 +0100 Subject: [PATCH 098/527] Added test for drop implementation on user data --- core/tests/test_vm_userdata.rs | 47 ++++++++++++++++++++++------------ 1 file changed, 30 insertions(+), 17 deletions(-) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index e414246..6c09846 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -31,8 +31,18 @@ use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::function::types::RFunction; +static mut DROP_COUNTER: i32 = 0; + pub struct MyInt(i64); +impl Drop for MyInt { + fn drop(&mut self) { + unsafe { + DROP_COUNTER += 1; + } + } +} + decl_userdata! { impl MyInt { fn tonumber(this: &MyInt) -> Number { @@ -153,21 +163,24 @@ fn test_vm_userdata_error_handling() { #[test] fn test_vm_userdata() { - let vm = RootVm::new(); - let top = vm.top(); - vm.register_userdata::().unwrap(); - assert_eq!(top, vm.top()); - vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); - assert_eq!(top, vm.top()); - vm.run_code::<()>(c"a = MyInt(123)").unwrap(); - vm.run_code::<()>(c"b = MyInt(456)").unwrap(); - vm.run_code::<()>(c"c = MyInt(456)").unwrap(); - assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); - assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); - assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); - assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); - assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); - assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); - assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); - assert_eq!(top + 7, vm.top()); + { + let vm = RootVm::new(); + let top = vm.top(); + vm.register_userdata::().unwrap(); + assert_eq!(top, vm.top()); + vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); + assert_eq!(top, vm.top()); + vm.run_code::<()>(c"a = MyInt(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt(456)").unwrap(); + vm.run_code::<()>(c"c = MyInt(456)").unwrap(); + assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); + assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); + assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); + assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); + assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); + assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); + assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); + assert_eq!(top + 7, vm.top()); + } + assert_eq!(unsafe { DROP_COUNTER }, 6) } From 2f0cb021580c9d04b5fd616f0561b5ba140f156c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 16 Mar 2025 21:33:21 +0100 Subject: [PATCH 099/527] Refactor: moved userdata to its own directory --- core/src/macros/userdata.rs | 4 +- core/src/macros/userdata_func.rs | 16 ++++---- core/src/vm/core.rs | 2 +- core/src/vm/{userdata.rs => userdata/core.rs} | 24 +---------- core/src/vm/userdata/error.rs | 41 +++++++++++++++++++ core/src/vm/userdata/interface.rs | 38 +++++++++++++++++ core/src/vm/userdata/mod.rs | 37 +++++++++++++++++ core/tests/test_vm_userdata.rs | 2 +- 8 files changed, 129 insertions(+), 35 deletions(-) rename core/src/vm/{userdata.rs => userdata/core.rs} (83%) create mode 100644 core/src/vm/userdata/error.rs create mode 100644 core/src/vm/userdata/interface.rs create mode 100644 core/src/vm/userdata/mod.rs diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index b791c59..6fc7593 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -44,7 +44,7 @@ macro_rules! decl_userdata { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &$crate::vm::userdata::Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { $( let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); @@ -75,7 +75,7 @@ macro_rules! decl_userdata_mut { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &$crate::vm::userdata::Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { $( let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 895d2b3..991eb25 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -32,7 +32,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::Function { + $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -42,7 +42,7 @@ macro_rules! decl_userdata_func { let ret = _func(unsafe { &mut *this_ptr }, &vm $(, $($arg_name),*)?); ret.into_param(&vm) as _ } - let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? @@ -54,7 +54,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::Function { + $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -64,7 +64,7 @@ macro_rules! decl_userdata_func { let ret = _func(unsafe { &mut *this_ptr } $(, $($arg_name),*)?); ret.into_param(&vm) as _ } - let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? @@ -76,7 +76,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::Function { + $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -86,7 +86,7 @@ macro_rules! decl_userdata_func { let ret = _func(unsafe { &*this_ptr }, &vm $(, $($arg_name),*)?); ret.into_param(&vm) as _ } - let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? f @@ -97,7 +97,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::Function { + $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -107,7 +107,7 @@ macro_rules! decl_userdata_func { let ret = _func(unsafe { &*this_ptr } $(, $($arg_name),*)?); ret.into_param(&vm) as _ } - let mut f = $crate::vm::userdata::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? f diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index ccfa3ea..47985c5 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -32,7 +32,7 @@ use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceba use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; use crate::vm::error::{Error, RuntimeError}; -use crate::vm::userdata::{Registry, UserData}; +use crate::vm::userdata::{core::Registry, UserData}; use crate::vm::util::LoadCode; use crate::vm::value::{FromLua, IntoLua}; diff --git a/core/src/vm/userdata.rs b/core/src/vm/userdata/core.rs similarity index 83% rename from core/src/vm/userdata.rs rename to core/src/vm/userdata/core.rs index a251ca7..72fbe3c 100644 --- a/core/src/vm/userdata.rs +++ b/core/src/vm/userdata/core.rs @@ -28,23 +28,12 @@ use std::ffi::CStr; use std::marker::PhantomData; -use bp3d_util::simple_error; use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; use crate::ffi::lua::{lua_pushcclosure, lua_pushvalue, lua_setfield, lua_settop, CFunction, State}; +use crate::vm::userdata::{Error, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::Vm; -simple_error! { - pub Error { - ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", - MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", - Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", - Index => "__index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index", - AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", - Alignment(usize) => "too strict alignment required ({} bytes), max is 8 bytes" - } -} - pub struct Function { is_mutable: bool, args: Vec, @@ -159,14 +148,3 @@ impl<'a, T> Drop for Registry<'a, T> { } } } - -pub trait UserData: Sized { - const CLASS_NAME: &'static CStr; - - fn register(registry: &Registry) -> Result<(), Error>; -} - -pub unsafe trait UserDataImmutable: UserData {} - -//TODO: implement __gc for Drop (std::mem::drop_in_place) and LuaDrop -//TODO: only implement __gc for std::mem::needs_drop return false \ No newline at end of file diff --git a/core/src/vm/userdata/error.rs b/core/src/vm/userdata/error.rs new file mode 100644 index 0000000..68d335d --- /dev/null +++ b/core/src/vm/userdata/error.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::CStr; +use bp3d_util::simple_error; + +simple_error! { + pub Error { + ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", + MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", + Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", + Index => "__index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index", + AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", + Alignment(usize) => "too strict alignment required ({} bytes), max is 8 bytes" + } +} diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs new file mode 100644 index 0000000..057bc11 --- /dev/null +++ b/core/src/vm/userdata/interface.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::CStr; +use crate::vm::userdata::{Error, core::Registry}; + +pub trait UserData: Sized { + const CLASS_NAME: &'static CStr; + + fn register(registry: &Registry) -> Result<(), Error>; +} + +pub unsafe trait UserDataImmutable: UserData {} diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs new file mode 100644 index 0000000..29f0a04 --- /dev/null +++ b/core/src/vm/userdata/mod.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +mod error; +pub mod core; + +pub use error::Error; +pub use interface::*; + +//TODO: implement __gc for Drop (std::mem::drop_in_place) and LuaDrop +//TODO: only implement __gc for std::mem::needs_drop return false \ No newline at end of file diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 6c09846..a19279a 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -182,5 +182,5 @@ fn test_vm_userdata() { assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); assert_eq!(top + 7, vm.top()); } - assert_eq!(unsafe { DROP_COUNTER }, 6) + assert_eq!(unsafe { DROP_COUNTER }, 6); } From ab182539faeb379c2c4ceb862fa07cfe98f249da Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 21:52:04 +0100 Subject: [PATCH 100/527] Added support for LuaDrop to userdata Registry --- core/src/macros/userdata.rs | 4 ++ core/src/vm/userdata/core.rs | 72 ++++++++++++++++++++++++++----- core/src/vm/userdata/interface.rs | 9 ++++ core/tests/test_vm_userdata.rs | 13 +++++- 4 files changed, 87 insertions(+), 11 deletions(-) diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index 6fc7593..59fd0a0 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -49,6 +49,8 @@ macro_rules! decl_userdata { let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); )* + use $crate::vm::userdata::AddGcMethod; + (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); Ok(()) } } @@ -80,6 +82,8 @@ macro_rules! decl_userdata_mut { let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); )* + use $crate::vm::userdata::AddGcMethod; + (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); Ok(()) } } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 72fbe3c..ca7bff0 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -26,11 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; use crate::ffi::lua::{lua_pushcclosure, lua_pushvalue, lua_setfield, lua_settop, CFunction, State}; -use crate::vm::userdata::{Error, UserData}; +use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::Vm; @@ -91,9 +92,10 @@ impl Function { } } -pub struct Registry<'a, T> { +pub struct Registry<'a, T: UserData> { vm: &'a Vm, useless: PhantomData, + has_gc: OnceCell<()> } impl<'a, T: UserData> Registry<'a, T> { @@ -118,28 +120,78 @@ impl<'a, T: UserData> Registry<'a, T> { unsafe { lua_settop(vm.as_ptr(), -2) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } - let reg = Registry { vm, useless: PhantomData }; + Ok(Registry { vm, useless: PhantomData, has_gc: OnceCell::new() }) + } + + pub fn add_method(&self, name: &'static CStr, func: CFunction) { + unsafe { + lua_pushcclosure(self.vm.as_ptr(), func, 0); + lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); + } + } + + pub fn add_gc_method(&self) { if std::mem::needs_drop::() { extern "C-unwind" fn run_drop(l: State) -> i32 { let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; unsafe { std::ptr::drop_in_place(udata) }; 0 } - reg.add_method(c"__gc", run_drop::); + self.add_method(c"__gc", run_drop::); } - Ok(reg) + self.has_gc.set(()).unwrap(); } +} - pub fn add_method(&self, name: &'static CStr, func: CFunction) { - unsafe { - lua_pushcclosure(self.vm.as_ptr(), func, 0); - lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); +impl<'a, T: UserData + LuaDrop> Registry<'a, T> { + pub fn add_gc_method_with_lua_drop(&self) { + extern "C-unwind" fn run_lua_drop(l: State) -> i32 { + let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; + unsafe { (&*udata).lua_drop(&Vm::from_raw(l)) }; + 0 } + extern "C-unwind" fn run_lua_drop_full(l: State) -> i32 { + let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; + unsafe { (&*udata).lua_drop(&Vm::from_raw(l)) }; + unsafe { std::ptr::drop_in_place(udata) }; + 0 + } + if std::mem::needs_drop::() { + self.add_method(c"__gc", run_lua_drop_full::); + } else { + self.add_method(c"__gc", run_lua_drop::); + } + self.has_gc.set(()).unwrap(); + } +} + +pub struct AddGcMethodAuto(PhantomData); + +impl Default for AddGcMethodAuto { + fn default() -> Self { + AddGcMethodAuto(PhantomData) } } -impl<'a, T> Drop for Registry<'a, T> { +impl AddGcMethod for AddGcMethodAuto { + fn add_gc_method(&self, reg: &Registry) { + reg.add_gc_method_with_lua_drop(); + } +} + +impl AddGcMethod for &AddGcMethodAuto { + fn add_gc_method(&self, reg: &Registry) { + reg.add_gc_method(); + } +} + +impl<'a, T: UserData> Drop for Registry<'a, T> { fn drop(&mut self) { + if std::mem::needs_drop::() && self.has_gc.get().is_none() { + println!("No __gc method registered on a drop userdata type!"); + // No __gc method found in object that needs it force add it before finishing it. + self.add_gc_method(); + } unsafe { lua_pushvalue(self.vm.as_ptr(), -1); lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 057bc11..88634f9 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -28,6 +28,7 @@ use std::ffi::CStr; use crate::vm::userdata::{Error, core::Registry}; +use crate::vm::Vm; pub trait UserData: Sized { const CLASS_NAME: &'static CStr; @@ -36,3 +37,11 @@ pub trait UserData: Sized { } pub unsafe trait UserDataImmutable: UserData {} + +pub trait LuaDrop { + fn lua_drop(&self, vm: &Vm); +} + +pub trait AddGcMethod { + fn add_gc_method(&self, reg: &Registry); +} diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index a19279a..3cc6c38 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -28,13 +28,23 @@ use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; -use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::userdata::LuaDrop; static mut DROP_COUNTER: i32 = 0; +static mut LUA_DROP_COUNTER: i32 = 0; pub struct MyInt(i64); +impl LuaDrop for MyInt { + fn lua_drop(&self, _: &Vm) { + unsafe { + LUA_DROP_COUNTER += 1; + } + } +} + impl Drop for MyInt { fn drop(&mut self) { unsafe { @@ -183,4 +193,5 @@ fn test_vm_userdata() { assert_eq!(top + 7, vm.top()); } assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } From 1c6dd06c06bc4b6f5c8aa8cf5b53303a177910aa Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 22:20:55 +0100 Subject: [PATCH 101/527] Added better docs to FromParam and FromLua traits --- core/src/vm/function/interface.rs | 5 +++-- core/src/vm/value/interface.rs | 6 ++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 9bbf1a5..54db055 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -51,13 +51,14 @@ pub trait FromParam<'a>: Sized + SimpleDrop + LuaType { /// # Arguments /// /// * `vm`: the [Vm] to read from. - /// * `index`: index of the parameter to read. + /// * `index`: index of the parameter to read. This index is guaranteed to be absolute. /// /// returns: Self /// /// # Safety /// /// Calling this function outside the body of a [CFunction](crate::ffi::lua::CFunction) is UB. - /// Calling this function in a non-POF segment of that CFunction is also UB. + /// Calling this function in a non-POF segment of that CFunction is also UB. If the index is not + /// absolute, this function may cause UB. unsafe fn from_param(vm: &'a Vm, index: i32) -> Self; } diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 7ba570b..b469a36 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -34,7 +34,8 @@ pub trait FromLua<'a>: Sized { /// # Arguments /// /// * `vm`: the [Vm] to read from. - /// * `index`: the index at which to try reading the value from. + /// * `index`: the index at which to try reading the value from. The index does not have to be + /// absolute. /// /// returns: Result /// @@ -49,7 +50,8 @@ pub trait FromLua<'a>: Sized { /// # Arguments /// /// * `vm`: the [Vm] to read from. - /// * `index`: the index at which to try reading the value from. + /// * `index`: the index at which to try reading the value from. The index does not have to be + /// absolute. /// /// returns: Result fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result; From 460b3ce7d7aba6ac1c05e28c072412cb6f68844a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 22:22:37 +0100 Subject: [PATCH 102/527] Added support for AnyUserData --- core/src/vm/userdata/any.rs | 103 ++++++++++++++++++++++++++++++++++++ core/src/vm/userdata/mod.rs | 5 +- core/src/vm/value/any.rs | 6 ++- 3 files changed, 109 insertions(+), 5 deletions(-) create mode 100644 core/src/vm/userdata/any.rs diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs new file mode 100644 index 0000000..b658f92 --- /dev/null +++ b/core/src/vm/userdata/any.rs @@ -0,0 +1,103 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::laux::luaL_testudata; +use crate::ffi::lua::{lua_type, Type}; +use crate::vm::error::{Error, TypeError}; +use crate::vm::userdata::{UserData, UserDataImmutable}; +use crate::vm::value::FromLua; +use crate::vm::Vm; + +pub struct AnyUserData<'a> { + vm: &'a Vm, + index: i32 +} + +impl<'a> AnyUserData<'a> { + /// Creates an AnyUserData from a raw Vm and index. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a UserData and is absolute. If index is not absolute then + /// using the produced object is UB. If the index points to any other type then using the produced + /// object is also UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self { vm, index } + } + + /// Returns a reference to this UserData value cast to `T`. + #[inline(always)] + pub fn get(&self) -> crate::vm::Result<&T> { + crate::vm::value::FromLua::from_lua(self.vm, self.index) + } + + /// Returns a mutable reference to a UserData value. + /// + /// # Safety + /// + /// The caller is responsible for guaranteeing that only a single reference to this object is + /// created. That is no other references to this underlying userdata value must exist in Rust + /// code otherwise using this function is UB. + pub unsafe fn get_mut(&mut self) -> crate::vm::Result<&mut T> { + let this_ptr = unsafe { luaL_testudata(self.vm.as_ptr(), self.index, T::CLASS_NAME.as_ptr()) } as *mut T; + if this_ptr.is_null() { + return Err(Error::Type(TypeError { + expected: Type::Userdata, + actual: unsafe { lua_type(self.vm.as_ptr(), self.index) }, + })); + } + Ok(unsafe { &mut *this_ptr }) + } +} + +impl<'a> FromLua<'a> for AnyUserData<'a> { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + AnyUserData::from_raw(vm, vm.get_absolute_index(index)) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let ty = unsafe { lua_type(vm.as_ptr(), index) }; + if ty == Type::Userdata { + Ok(unsafe { AnyUserData::from_raw(vm, vm.get_absolute_index(index)) }) + } else { + Err(Error::Type(TypeError { + expected: Type::Userdata, + actual: ty, + })) + } + } +} diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs index 29f0a04..9530fa3 100644 --- a/core/src/vm/userdata/mod.rs +++ b/core/src/vm/userdata/mod.rs @@ -29,9 +29,8 @@ mod interface; mod error; pub mod core; +mod any; pub use error::Error; pub use interface::*; - -//TODO: implement __gc for Drop (std::mem::drop_in_place) and LuaDrop -//TODO: only implement __gc for std::mem::needs_drop return false \ No newline at end of file +pub use any::AnyUserData; diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index da24187..0ebea89 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -31,6 +31,7 @@ use crate::vm::error::Error; use crate::vm::value::FromLua; use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; +use crate::vm::userdata::AnyUserData; use crate::vm::Vm; pub enum AnyValue<'a> { @@ -41,7 +42,8 @@ pub enum AnyValue<'a> { String(&'a str), Buffer(&'a [u8]), Function(LuaFunction<'a>), - Table(Table<'a>) + Table(Table<'a>), + UserData(AnyUserData<'a>) } impl<'a> FromLua<'a> for AnyValue<'a> { @@ -72,7 +74,7 @@ impl<'a> FromLua<'a> for AnyValue<'a> { } Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), Type::Function => Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }), - Type::Userdata => Err(Error::UnsupportedType(ty)), + Type::Userdata => Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }), Type::Thread => Err(Error::UnsupportedType(ty)) } } From 65f15016fa0c8ee5b36f708384671e9891325008 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 22:26:37 +0100 Subject: [PATCH 103/527] Removed unnecessary path prefix --- core/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.rs b/core/build.rs index 4de9d02..aaf887a 100644 --- a/core/build.rs +++ b/core/build.rs @@ -51,7 +51,7 @@ static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator>) -> ExitStatus { let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); - std::process::Command::new(cmd) + Command::new(cmd) .args(args) .current_dir(path.join("LuaJIT")).status().expect(text) } From be2cd94b1d890f3f476be3fcb04bcec2198c9869 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 22:38:38 +0100 Subject: [PATCH 104/527] Removed some code duplications --- core/src/macros/userdata.rs | 47 ++++++++++++++++-------------------- core/src/util.rs | 6 ++++- core/src/vm/function/core.rs | 10 ++------ 3 files changed, 28 insertions(+), 35 deletions(-) diff --git a/core/src/macros/userdata.rs b/core/src/macros/userdata.rs index 59fd0a0..e903991 100644 --- a/core/src/macros/userdata.rs +++ b/core/src/macros/userdata.rs @@ -26,6 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[macro_export] +macro_rules! _impl_userdata { + ($obj_name: ident, $($fn_name: ident),*) => { + impl $crate::vm::userdata::UserData for $obj_name { + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); + + fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { + $( + let (name, func) = unsafe { $obj_name::$fn_name().build()? }; + registry.add_method(name, func); + )* + use $crate::vm::userdata::AddGcMethod; + (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); + Ok(()) + } + } + }; +} + #[macro_export] macro_rules! decl_userdata { ( @@ -41,19 +60,7 @@ macro_rules! decl_userdata { } )* - impl $crate::vm::userdata::UserData for $obj_name { - const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - - fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { - $( - let (name, func) = unsafe { $obj_name::$fn_name().build()? }; - registry.add_method(name, func); - )* - use $crate::vm::userdata::AddGcMethod; - (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); - Ok(()) - } - } + $crate::_impl_userdata!($obj_name, $($fn_name),*); unsafe impl $crate::vm::userdata::UserDataImmutable for $obj_name {} }; @@ -74,18 +81,6 @@ macro_rules! decl_userdata_mut { } )* - impl $crate::vm::userdata::UserData for $obj_name { - const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - - fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { - $( - let (name, func) = unsafe { $obj_name::$fn_name().build()? }; - registry.add_method(name, func); - )* - use $crate::vm::userdata::AddGcMethod; - (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); - Ok(()) - } - } + $crate::_impl_userdata!($obj_name, $($fn_name),*); }; } diff --git a/core/src/util.rs b/core/src/util.rs index 4f0484b..866aeb7 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -36,8 +36,11 @@ pub trait AnyStr { } impl AnyStr for String { + #[inline(always)] fn to_str(&self) -> crate::vm::Result> { - Ok(Cow::Owned(CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?)) + // Somehow Rust is too stupid to see that to_str on &str returns an Owned CString, so force + // it using transmute... + unsafe { std::mem::transmute((&**self).to_str()) } } } @@ -66,6 +69,7 @@ pub unsafe trait SimpleDrop {} unsafe impl SimpleDrop for *mut T {} unsafe impl SimpleDrop for *const T {} unsafe impl SimpleDrop for bool {} +unsafe impl SimpleDrop for &str {} unsafe impl SimpleDrop for Option {} unsafe impl SimpleDrop for Result {} unsafe impl SimpleDrop for &T {} diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 98e00cb..8a09cfe 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -76,14 +76,10 @@ impl<'a> FromParam<'a> for &'a [u8] { } } -unsafe impl SimpleDrop for &str {} - impl IntoParam for &str { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { - lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); - } + unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } 1 } } @@ -91,9 +87,7 @@ impl IntoParam for &str { impl IntoParam for &[u8] { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { - lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); - } + unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } 1 } } From 8416e6f585ebc004f46e82c3444e1c274ca51b19 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 17 Mar 2025 22:41:43 +0100 Subject: [PATCH 105/527] Removed AnyStr implementation for CString and String as these were unused --- core/src/util.rs | 16 ---------------- core/src/vm/util.rs | 5 ++--- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/core/src/util.rs b/core/src/util.rs index 866aeb7..7538c03 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -35,28 +35,12 @@ pub trait AnyStr { fn to_str(&self) -> crate::vm::Result>; } -impl AnyStr for String { - #[inline(always)] - fn to_str(&self) -> crate::vm::Result> { - // Somehow Rust is too stupid to see that to_str on &str returns an Owned CString, so force - // it using transmute... - unsafe { std::mem::transmute((&**self).to_str()) } - } -} - impl AnyStr for &str { fn to_str(&self) -> crate::vm::Result> { Ok(Cow::Owned(CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?)) } } -impl AnyStr for CString { - #[inline(always)] - fn to_str(&self) -> crate::vm::Result> { - Ok(Cow::Borrowed(&**self)) - } -} - impl AnyStr for &CStr { #[inline(always)] fn to_str(&self) -> crate::vm::Result> { diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index d74b589..deb4709 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -71,10 +71,9 @@ pub trait LoadCode { } impl LoadCode for &CStr { + #[inline(always)] fn load_code(&self, l: State) -> ThreadStatus { - unsafe { - luaL_loadstring(l, self.as_ptr()) - } + unsafe { luaL_loadstring(l, self.as_ptr()) } } } From e5279c6ab54482442f0349b0c20bc15780021fbc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 20 Mar 2025 22:41:57 +0100 Subject: [PATCH 106/527] Fixed possible safety bug with malicious IntoParam trait implementations. --- core/src/vm/function/core.rs | 20 ++++++++++---------- core/src/vm/function/interface.rs | 9 ++++++++- core/src/vm/table/interface.rs | 2 +- 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 8a09cfe..bf9e421 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -76,7 +76,7 @@ impl<'a> FromParam<'a> for &'a [u8] { } } -impl IntoParam for &str { +unsafe impl IntoParam for &str { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } @@ -84,7 +84,7 @@ impl IntoParam for &str { } } -impl IntoParam for &[u8] { +unsafe impl IntoParam for &[u8] { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } @@ -92,7 +92,7 @@ impl IntoParam for &[u8] { } } -impl IntoParam for String { +unsafe impl IntoParam for String { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { (&*self).into_param(vm) @@ -117,7 +117,7 @@ macro_rules! impl_integer { } } - impl IntoParam for $t { + unsafe impl IntoParam for $t { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { @@ -153,7 +153,7 @@ macro_rules! impl_float { } } - impl IntoParam for $t { + unsafe impl IntoParam for $t { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { @@ -168,7 +168,7 @@ macro_rules! impl_float { impl_float!(f32, f64); -impl IntoParam for bool { +unsafe impl IntoParam for bool { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushboolean(vm.as_ptr(), if self { 1 } else { 0 }) }; @@ -176,7 +176,7 @@ impl IntoParam for bool { } } -impl IntoParam for Result { +unsafe impl IntoParam for Result { fn into_param(self, vm: &Vm) -> u16 { match self { Ok(v) => v.into_param(vm), @@ -189,7 +189,7 @@ impl IntoParam for Result { } } -impl IntoParam for Option { +unsafe impl IntoParam for Option { fn into_param(self, vm: &Vm) -> u16 { match self { None => { @@ -203,7 +203,7 @@ impl IntoParam for Option { } } -impl IntoParam for () { +unsafe impl IntoParam for () { #[inline(always)] fn into_param(self, _: &Vm) -> u16 { 0 @@ -224,7 +224,7 @@ impl<'a, T: UserData> FromParam<'a> for &'a T { } } -impl IntoParam for T { +unsafe impl IntoParam for T { fn into_param(self, vm: &Vm) -> u16 { let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; unsafe { userdata.write(self) }; diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 54db055..6bc4e75 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -31,7 +31,14 @@ use crate::vm::Vm; use crate::vm::util::LuaType; /// This trait represents a function return value. -pub trait IntoParam: Sized { +/// +/// # Safety +/// +/// When implementing this trait, ensure that the number returned by +/// [into_param](IntoParam::into_param) is EXACTLY equal to the number of values pushed onto the lua +/// stack. If more or fewer than advertised values exists on the stack after the call then the impl +/// is considered UB. +pub unsafe trait IntoParam: Sized { /// Turns self into a function return parameter. /// /// This function returns the number of parameters pushed onto the lua stack. diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 2a144e8..c1c5007 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -60,7 +60,7 @@ impl<'a> FromLua<'a> for Table<'a> { } } -impl IntoParam for Table<'_> { +unsafe impl IntoParam for Table<'_> { fn into_param(self, vm: &Vm) -> u16 { let top = unsafe { lua_gettop(vm.as_ptr()) }; if top != self.index() { From 79b38a2dc6cea909931b04d40a3cff1b422cd70b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 20 Mar 2025 22:45:46 +0100 Subject: [PATCH 107/527] Fixed possible safety bug with malicious IntoLua trait implementations. --- core/src/vm/closure/types.rs | 2 +- core/src/vm/function/types.rs | 2 +- core/src/vm/value/core.rs | 4 ++-- core/src/vm/value/interface.rs | 11 ++++++++++- 4 files changed, 14 insertions(+), 5 deletions(-) diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index cdd6574..199d568 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -52,7 +52,7 @@ impl RClosure { } } -impl IntoLua for RClosure { +unsafe impl IntoLua for RClosure { fn into_lua(self, vm: &Vm) -> u16 { let num = self.upvalue.into_upvalue(vm); unsafe { lua_pushcclosure(vm.as_ptr(), self.func, num as _) }; diff --git a/core/src/vm/function/types.rs b/core/src/vm/function/types.rs index c23a700..ff5fddc 100644 --- a/core/src/vm/function/types.rs +++ b/core/src/vm/function/types.rs @@ -39,7 +39,7 @@ impl RFunction { } } -impl IntoLua for RFunction { +unsafe impl IntoLua for RFunction { fn into_lua(self, vm: &Vm) -> u16 { let l = vm.as_ptr(); unsafe { diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index ac9e0ec..ff1f85a 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -125,7 +125,7 @@ impl_from_lua!(f64, Number, lua_tonumber, as _); impl_from_lua!(bool, Boolean, lua_toboolean, == 1); -impl IntoLua for T { +unsafe impl IntoLua for T { #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { self.into_param(vm) @@ -221,7 +221,7 @@ impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t macro_rules! impl_into_lua_tuple { ($($name: ident: $name2: tt),*) => { - impl<$($name: IntoLua),*> IntoLua for ($($name),*) { + unsafe impl<$($name: IntoLua),*> IntoLua for ($($name),*) { fn into_lua(self, vm: &Vm) -> u16 { $( self.$name2.into_lua(vm); diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index b469a36..b2d0cab 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -62,7 +62,16 @@ pub trait FromLua<'a>: Sized { } } -pub trait IntoLua: Sized { +/// This trait represents a value convertible to lua outside Rust function calls. For lua values +/// returned by Rust functions, see [IntoParam](crate::vm::function::IntoParam). +/// +/// # Safety +/// +/// When implementing this trait, ensure that the number returned by +/// [into_lua](IntoLua::into_lua) is EXACTLY equal to the number of values pushed onto the lua +/// stack. If more or fewer than advertised values exists on the stack after the call then the impl +/// is considered UB. +pub unsafe trait IntoLua: Sized { /// Attempt to push self onto the top of the stack in the given [Vm]. /// /// Returns the number values pushed into the lua stack. From 8158d4005bc64853f498f6b74f67071d4edfcbd4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 21 Mar 2025 21:59:54 +0100 Subject: [PATCH 108/527] Added initial version of codegen tool to generate FromParam and LuaType --- Cargo.toml | 3 +- codegen/Cargo.toml | 12 +++++ codegen/src/gen/from_param.rs | 95 +++++++++++++++++++++++++++++++++ codegen/src/gen/lua_type.rs | 70 ++++++++++++++++++++++++ codegen/src/gen/mod.rs | 33 ++++++++++++ codegen/src/lib.rs | 47 ++++++++++++++++ codegen/src/parser/interface.rs | 62 +++++++++++++++++++++ codegen/src/parser/mod.rs | 32 +++++++++++ codegen/src/parser/structs.rs | 62 +++++++++++++++++++++ 9 files changed, 415 insertions(+), 1 deletion(-) create mode 100644 codegen/Cargo.toml create mode 100644 codegen/src/gen/from_param.rs create mode 100644 codegen/src/gen/lua_type.rs create mode 100644 codegen/src/gen/mod.rs create mode 100644 codegen/src/lib.rs create mode 100644 codegen/src/parser/interface.rs create mode 100644 codegen/src/parser/mod.rs create mode 100644 codegen/src/parser/structs.rs diff --git a/Cargo.toml b/Cargo.toml index c66655d..f4f3147 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "codegen", "core", "testbin" -] \ No newline at end of file +] diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml new file mode 100644 index 0000000..3ab6188 --- /dev/null +++ b/codegen/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "bp3d-lua-codegen" +version = "0.1.0" +edition = "2021" #Due to gen be a reserved keyword! + +[lib] +proc-macro = true + +[dependencies] +quote = "1.0" +syn = { version = "2.0.98", features = ["full"] } +proc-macro2 = "1.0.26" diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs new file mode 100644 index 0000000..370d235 --- /dev/null +++ b/codegen/src/gen/from_param.rs @@ -0,0 +1,95 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::{Ident, TokenStream}; +use quote::{quote, ToTokens}; +use syn::Generics; +use crate::parser::Parser; +use crate::parser::structs::StructField; + +pub struct FromParam { + name: Ident, + generics: Generics, + is_index_based: bool +} + +impl FromParam { + pub fn new(name: Ident, generics: Generics) -> Self { + Self { name, generics, is_index_based: false } + } +} + +impl Parser for FromParam { + type ParsedField = (Ident, TokenStream); + type ParsedVariant = (); + + fn parse_field(&mut self, field: StructField) -> Self::ParsedField { + let name = field.unique_name; + let ty = field.ty; + self.is_index_based = field.unique_name_is_index; + let reader = if field.unique_name_is_index { + let index = field.index + 1; //Lua indices starts at 1, not 0. + quote! { bp3d_lua::ffi::lua::lua_rawgeti(vm.as_ptr(), index, #index as _) } + } else { + quote! { bp3d_lua::ffi::lua::lua_getfield(vm.as_ptr(), index, bp3d_lua::c_stringify!(#name).as_ptr()) } + }; + (name.clone(), quote! { + #reader; + top += 1; + let #name: #ty = bp3d_lua::vm::function::FromParam::from_param(&vm, top); + }) + } + + fn gen_struct(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + let lifetime = generics.lifetimes().next().map(|v| v.into_token_stream()).unwrap_or(quote! { '_ }); + let from_params = parsed.iter().map(|(_, v)| v); + let values = parsed.iter().map(|(k, _)| k); + let end = if self.is_index_based { + quote! { + #name(#(#values),*) + } + } else { + quote! { + #name { #(#values),* } + } + }; + quote! { + impl #generics bp3d_lua::vm::function::FromParam<#lifetime> for #name #generics { + unsafe fn from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Self { + let mut top = vm.top(); + #(#from_params)* + #end + } + } + + unsafe impl #generics bp3d_lua::util::SimpleDrop for #name #generics { } + } + } +} diff --git a/codegen/src/gen/lua_type.rs b/codegen/src/gen/lua_type.rs new file mode 100644 index 0000000..645cfc8 --- /dev/null +++ b/codegen/src/gen/lua_type.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::{Ident, TokenStream}; +use quote::quote; +use syn::Generics; +use crate::parser::Parser; +use crate::parser::structs::StructField; + +pub struct LuaType { + name: Ident, + generics: Generics, +} + +impl LuaType { + pub fn new(name: Ident, generics: Generics) -> Self { + Self { name, generics } + } +} + +impl Parser for LuaType { + type ParsedField = TokenStream; + type ParsedVariant = (); + + fn parse_field(&mut self, field: StructField) -> Self::ParsedField { + let ty = field.ty; + quote! { + types.append(&mut <#ty as bp3d_lua::vm::util::LuaType>::lua_type()); + } + } + + fn gen_struct(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + quote! { + impl #generics bp3d_lua::vm::util::LuaType for #name #generics { + fn lua_type() -> Vec { + let mut types = Vec::new(); + #(#parsed)* + types + } + } + } + } +} diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs new file mode 100644 index 0000000..fa3e114 --- /dev/null +++ b/codegen/src/gen/mod.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod from_param; +mod lua_type; + +pub use lua_type::*; +pub use from_param::*; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs new file mode 100644 index 0000000..7e7012c --- /dev/null +++ b/codegen/src/lib.rs @@ -0,0 +1,47 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod parser; +mod gen; + +use proc_macro::TokenStream; +use syn::{parse_macro_input, DeriveInput}; +use crate::parser::Parser; +use crate::gen::{FromParam, LuaType}; + +#[proc_macro_derive(FromParam)] +pub fn from_param(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + FromParam::new(ident, generics).parse(data).into() +} + +#[proc_macro_derive(LuaType)] +pub fn lua_type(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + LuaType::new(ident, generics).parse(data).into() +} diff --git a/codegen/src/parser/interface.rs b/codegen/src/parser/interface.rs new file mode 100644 index 0000000..fb61458 --- /dev/null +++ b/codegen/src/parser/interface.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::TokenStream; +use syn::Data; +use crate::parser::structs::{StructField, StructParser}; + +pub trait Parser: Sized { + type ParsedField; + type ParsedVariant; + + fn parse_field(&mut self, field: StructField) -> Self::ParsedField; + + fn gen_struct(self, parsed: Vec) -> TokenStream; + + fn parse(mut self, data: Data) -> TokenStream { + match data { + Data::Struct(v) => { + + let mut parser = StructParser::new(); + let mut parsed = Vec::new(); + for v in v.fields { + parsed.push(self.parse_field(parser.parse(v))); + } + self.gen_struct(parsed) + } + Data::Enum(_) => { + /*let mut parser = Self::new_enum_parser(params); + for v in v.variants { + parser.parse_variant(v); + }*/ + todo!() + } + _ => panic!("Unsupported type") + } + } +} diff --git a/codegen/src/parser/mod.rs b/codegen/src/parser/mod.rs new file mode 100644 index 0000000..20528df --- /dev/null +++ b/codegen/src/parser/mod.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +pub mod structs; + +pub use interface::*; diff --git a/codegen/src/parser/structs.rs b/codegen/src/parser/structs.rs new file mode 100644 index 0000000..8872b94 --- /dev/null +++ b/codegen/src/parser/structs.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::{Ident, Span}; +use syn::{Field, Type}; + +pub struct StructField { + pub unique_name: Ident, + pub ty: Type, + pub index: usize, + pub unique_name_is_index: bool, +} + +pub struct StructParser { + cur_index: usize +} + +impl StructParser { + pub fn new() -> Self { + Self { cur_index: 0 } + } + + pub fn parse(&mut self, field: Field) -> StructField { + let index = Ident::new(&format!("value_{}", self.cur_index), Span::call_site()); + self.cur_index += 1; + let unique_name = field + .ident + .clone() + .unwrap_or(index); + StructField { + unique_name, + ty: field.ty, + index: self.cur_index - 1, + unique_name_is_index: field.ident.is_none() + } + } +} From 88fc953352cdc6c754dd9f0c8aac066f34aeebca Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 21 Mar 2025 22:00:11 +0100 Subject: [PATCH 109/527] Added test for codegen --- core/Cargo.toml | 1 + core/tests/test_vm_custom_from_param.rs | 103 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 core/tests/test_vm_custom_from_param.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index c88368d..9aee727 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,6 +15,7 @@ categories = ["graphics"] [dependencies] bp3d-util = { version = "1.4.0", features = ["simple-error"] } log = "0.4.26" +bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } diff --git a/core/tests/test_vm_custom_from_param.rs b/core/tests/test_vm_custom_from_param.rs new file mode 100644 index 0000000..c672eda --- /dev/null +++ b/core/tests/test_vm_custom_from_param.rs @@ -0,0 +1,103 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::decl_lib_func; +use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::{RootVm, Vm}; +use bp3d_lua::vm::function::IntoParam; +use bp3d_lua::vm::table::Table; +use bp3d_lua_codegen::FromParam; +use bp3d_lua_codegen::LuaType; + +#[derive(FromParam, LuaType)] +struct Test1<'a>(&'a str, i32); + +#[derive(FromParam, LuaType)] +struct Test2<'a> { + name: &'a str, + value: i32 +} + +unsafe impl IntoParam for Test2<'_> { + fn into_param(self, vm: &Vm) -> u16 { + let mut tbl = Table::new(vm); + { + let mut scope = tbl.lock(); + scope.set_field(c"name", self.name).unwrap(); + scope.set_field(c"value", self.value).unwrap(); + } + 1 + } +} + +#[derive(FromParam, LuaType)] +struct TestStatic { + value1: f32, + value2: i32 +} + +decl_lib_func! { + fn test(test1: Test1, test2: Test2, st: TestStatic) -> String { + format!("{} {}: {}, {}, (v1: {}, v2: {})", test1.0, test2.name, test1.1, test2.value, st.value1, st.value2) + } +} + +decl_lib_func! { + fn test2(name: &str) -> Test2 { + Test2 { name, value: 42 } + } +} + +#[test] +fn basic() { + let vm = RootVm::new(); + let top = vm.top(); + vm.set_global(c"test", RFunction::wrap(test)).unwrap(); + vm.set_global(c"test2", RFunction::wrap(test2)).unwrap(); + let out = vm.run_code::<&str>(c" + local test1 = { 'value', 42 } + local test2 = { name = 'of', value = 64 } + local st = { value1 = 42.42, value2 = 32 } + return test(test1, test2, st) + ").unwrap(); + assert_eq!(out, "value of: 42, 64, (v1: 42.42, v2: 32)"); + vm.set_global(c"test", Test2 { + name: "whatever", + value: 42, + }).unwrap(); + let out = vm.run_code::<&str>(c"return test.name").unwrap(); + assert_eq!(out, "whatever"); + let out = vm.run_code::(c"return test.value").unwrap(); + assert_eq!(out, 42); + vm.run_code::<()>(c" + local t2 = test2('test') + assert(t2.name == 'test') + assert(t2.value == 42) + ").unwrap(); + assert_eq!(top + 3, vm.top()) +} From 36d45d65085f0d8b9e115fc240565b290342282b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 22 Mar 2025 22:28:50 +0100 Subject: [PATCH 110/527] Added try_from_param function --- core/src/vm/function/core.rs | 45 +++++++++++++++++++++++++++++-- core/src/vm/function/interface.rs | 14 ++++++++++ core/src/vm/table/interface.rs | 7 ++++- 3 files changed, 63 insertions(+), 3 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index bf9e421..ce0a5a3 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -28,13 +28,14 @@ use std::error::Error; use std::slice; -use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable}; +use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; use crate::vm::util::{lua_rust_error, LuaType, TypeName}; +use crate::vm::value::FromLua; use crate::vm::Vm; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { @@ -47,6 +48,16 @@ impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { Some(T::from_param(vm, index)) } } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let l = vm.as_ptr(); + let ty = unsafe { lua_type(l, index) }; + if ty == Type::Nil || ty == Type::None { + None + } else { + Some(T::try_from_param(vm, index)) + } + } } impl LuaType for &str { } @@ -63,6 +74,11 @@ impl<'a> FromParam<'a> for &'a str { } } } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } } impl LuaType for &[u8] {} @@ -74,6 +90,11 @@ impl<'a> FromParam<'a> for &'a [u8] { let slice = slice::from_raw_parts(str as *const u8, len); slice } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } } unsafe impl IntoParam for &str { @@ -115,6 +136,11 @@ macro_rules! impl_integer { unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checkinteger(vm.as_ptr(), index) as _ } + + #[inline(always)] + fn try_from_param(vm: &Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } } unsafe impl IntoParam for $t { @@ -151,6 +177,11 @@ macro_rules! impl_float { unsafe fn from_param(vm: &Vm, index: i32) -> Self { lua_ext_fast_checknumber(vm.as_ptr(), index) as _ } + + #[inline(always)] + fn try_from_param(vm: &Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } } unsafe impl IntoParam for $t { @@ -219,9 +250,19 @@ impl LuaType for &T { impl<'a, T: UserData> FromParam<'a> for &'a T { #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> &'a T { - let obj_ptr = unsafe { luaL_checkudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + let obj_ptr = luaL_checkudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) as *const T; unsafe { &*obj_ptr } } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + if ptr.is_null() { + None + } else { + Some(unsafe { &*ptr }) + } + } } unsafe impl IntoParam for T { diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 6bc4e75..df6071d 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -68,4 +68,18 @@ pub trait FromParam<'a>: Sized + SimpleDrop + LuaType { /// Calling this function in a non-POF segment of that CFunction is also UB. If the index is not /// absolute, this function may cause UB. unsafe fn from_param(vm: &'a Vm, index: i32) -> Self; + + /// Attempts to read this value from the given lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to read from. + /// * `index`: index of the parameter to read. This index is guaranteed to be absolute. + /// + /// returns: Self + /// + /// # Errors + /// + /// Returns a [None] value if this value cannot be read from the lua stack. + fn try_from_param(vm: &'a Vm, index: i32) -> Option; } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index c1c5007..8d4d750 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_gettop, lua_pushvalue, Type}; +use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; @@ -46,6 +46,11 @@ impl<'a> FromParam<'a> for Table<'a> { luaL_checktype(vm.as_ptr(), index, Type::Table); Table::from_raw(vm, index) } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + if unsafe { lua_type(vm.as_ptr(), index) } != Type::Table { return None; } + Some(unsafe { Table::from_raw(vm, index) }) + } } impl<'a> FromLua<'a> for Table<'a> { From 91c0102360212a66e5938fb42ded362cecf5dbf6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 22 Mar 2025 22:54:20 +0100 Subject: [PATCH 111/527] Added initial untested version of FromParam derive for enums --- codegen/src/gen/from_param.rs | 131 +++++++++++++++++++++++++++++--- codegen/src/gen/lua_type.rs | 32 +++++++- codegen/src/parser/enums.rs | 81 ++++++++++++++++++++ codegen/src/parser/interface.rs | 15 ++-- codegen/src/parser/mod.rs | 1 + 5 files changed, 241 insertions(+), 19 deletions(-) create mode 100644 codegen/src/parser/enums.rs diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index 370d235..622ec62 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -29,24 +29,32 @@ use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use syn::Generics; +use crate::parser::enums::EnumVariant; use crate::parser::Parser; use crate::parser::structs::StructField; pub struct FromParam { name: Ident, generics: Generics, - is_index_based: bool + is_index_based: bool, + is_simpl_enum: bool } impl FromParam { pub fn new(name: Ident, generics: Generics) -> Self { - Self { name, generics, is_index_based: false } + Self { name, generics, is_index_based: false, is_simpl_enum: true } } } +pub struct Field { + name: Ident, + from_param: TokenStream, + try_from_param: TokenStream +} + impl Parser for FromParam { - type ParsedField = (Ident, TokenStream); - type ParsedVariant = (); + type ParsedField = Field; + type ParsedVariant = Field; fn parse_field(&mut self, field: StructField) -> Self::ParsedField { let name = field.unique_name; @@ -58,19 +66,76 @@ impl Parser for FromParam { } else { quote! { bp3d_lua::ffi::lua::lua_getfield(vm.as_ptr(), index, bp3d_lua::c_stringify!(#name).as_ptr()) } }; - (name.clone(), quote! { - #reader; - top += 1; - let #name: #ty = bp3d_lua::vm::function::FromParam::from_param(&vm, top); - }) + Field { + name: name.clone(), + from_param: quote! { + #reader; + top += 1; + let #name: #ty = bp3d_lua::vm::function::FromParam::from_param(vm, top); + }, + try_from_param: quote! { + unsafe { #reader }; + top += 1; + let #name: #ty = bp3d_lua::vm::function::FromParam::try_from_param(vm, top)?; + } + } + } + + fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant { + match variant { + EnumVariant::SingleField(v) => { + self.is_simpl_enum = false; + let ty = v.field.ty; + let name = self.name.clone(); + let variant = v.unique_name; + Field { + name: variant.clone(), + from_param: quote! { + match <#ty as bp3d_lua::vm::function::FromParam>::try_from_param(vm, index) { + Some(v) => return #name::#variant(v), + None => () + }; + }, + try_from_param: quote! { + match <#ty as bp3d_lua::vm::function::FromParam>::try_from_param(vm, index) { + Some(v) => return Some(#name::#variant(v)), + None => () + }; + } + } + } + EnumVariant::MultiField(_) => { + panic!("Multi-field enum variants are not supported"); + } + EnumVariant::None(variant) => { + let name = self.name.clone(); + let vname = variant.to_string(); + Field { + name: variant.clone(), + from_param: quote! { + match enum_name == #vname { + true => return #name::#variant, + false => () + }; + }, + try_from_param: quote! { + match enum_name == #vname { + true => return Some(#name::#variant), + false => () + }; + } + } + } + } } fn gen_struct(self, parsed: Vec) -> TokenStream { let name = self.name; let generics = self.generics; let lifetime = generics.lifetimes().next().map(|v| v.into_token_stream()).unwrap_or(quote! { '_ }); - let from_params = parsed.iter().map(|(_, v)| v); - let values = parsed.iter().map(|(k, _)| k); + let from_params = parsed.iter().map(|field| &field.from_param); + let try_from_params = parsed.iter().map(|field| &field.try_from_param); + let values = parsed.iter().map(|field| &field.name); let end = if self.is_index_based { quote! { #name(#(#values),*) @@ -83,12 +148,54 @@ impl Parser for FromParam { quote! { impl #generics bp3d_lua::vm::function::FromParam<#lifetime> for #name #generics { unsafe fn from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Self { + unsafe { bp3d_lua::ffi::laux::luaL_checktype(vm.as_ptr(), index, bp3d_lua::ffi::lua::Type::Table) }; let mut top = vm.top(); #(#from_params)* #end } + + fn try_from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Option { + bp3d_lua::vm::value::util::ensure_type_equals(vm, index, bp3d_lua::ffi::lua::Type::Table).ok()?; + let mut top = vm.top(); + let mut f = || -> Option { + #(#try_from_params)* + Some(#end) + }; + match f() { + Some(v) => Some(v), + None => { + // Reset stack position. + unsafe { bp3d_lua::ffi::lua::lua_settop(vm.as_ptr(), top) }; + None + } + } + } } - + + unsafe impl #generics bp3d_lua::util::SimpleDrop for #name #generics { } + } + } + + fn gen_enum(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + let lifetime = generics.lifetimes().next().map(|v| v.into_token_stream()).unwrap_or(quote! { '_ }); + let from_params = parsed.iter().map(|field| &field.from_param); + let try_from_params = parsed.iter().map(|field| &field.try_from_param); + quote! { + impl #generics bp3d_lua::vm::function::FromParam<#lifetime> for #name #generics { + unsafe fn from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Self { + #(#from_params)* + bp3d_lua::ffi::laux::luaL_error(vm.as_ptr(), "Unable to find a type satisfying constraints"); + std::hint::unreachable_unchecked() + } + + fn try_from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Option { + #(#try_from_params)* + None + } + } + unsafe impl #generics bp3d_lua::util::SimpleDrop for #name #generics { } } } diff --git a/codegen/src/gen/lua_type.rs b/codegen/src/gen/lua_type.rs index 645cfc8..ecc412a 100644 --- a/codegen/src/gen/lua_type.rs +++ b/codegen/src/gen/lua_type.rs @@ -29,6 +29,7 @@ use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::Generics; +use crate::parser::enums::EnumVariant; use crate::parser::Parser; use crate::parser::structs::StructField; @@ -45,7 +46,7 @@ impl LuaType { impl Parser for LuaType { type ParsedField = TokenStream; - type ParsedVariant = (); + type ParsedVariant = Vec; fn parse_field(&mut self, field: StructField) -> Self::ParsedField { let ty = field.ty; @@ -54,6 +55,20 @@ impl Parser for LuaType { } } + fn parse_variant(&mut self, field: EnumVariant) -> Self::ParsedVariant { + let mut tokens = Vec::new(); + match field { + EnumVariant::SingleField(v) => tokens.push(self.parse_field(v.field)), + EnumVariant::MultiField(v) => { + for v in v.fields { + tokens.push(self.parse_field(v)); + } + } + EnumVariant::None(_) => () + } + tokens + } + fn gen_struct(self, parsed: Vec) -> TokenStream { let name = self.name; let generics = self.generics; @@ -67,4 +82,19 @@ impl Parser for LuaType { } } } + + fn gen_enum(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + let tokens = parsed.into_iter().map(|v| quote! { #(#v)* }); + quote! { + impl #generics bp3d_lua::vm::util::LuaType for #name #generics { + fn lua_type() -> Vec { + let mut types = Vec::new(); + #(#tokens)* + types + } + } + } + } } diff --git a/codegen/src/parser/enums.rs b/codegen/src/parser/enums.rs new file mode 100644 index 0000000..f6fd4d2 --- /dev/null +++ b/codegen/src/parser/enums.rs @@ -0,0 +1,81 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::Ident; +use syn::{Fields, Variant}; +use crate::parser::structs::{StructField, StructParser}; + +pub struct EnumVariantSingle { + pub unique_name: Ident, + pub field: StructField, +} + +pub struct EnumVariantMulti { + pub unique_name: Ident, + pub fields: Vec, +} + +pub enum EnumVariant { + SingleField(EnumVariantSingle), + MultiField(EnumVariantMulti), + None(Ident) +} + +pub struct EnumParser; + +impl EnumParser { + pub fn parse(&mut self, variant: Variant) -> EnumVariant { + let unique_name = variant.ident; + match variant.fields { + Fields::Named(v) => { + let mut parser = StructParser::new(); + let fields = v.named.into_iter().map(|v| parser.parse(v)); + EnumVariant::MultiField(EnumVariantMulti { + unique_name, + fields: fields.collect() + }) + } + Fields::Unnamed(v) => { + let mut parser = StructParser::new(); + if v.unnamed.len() == 1 { + EnumVariant::SingleField(EnumVariantSingle { + unique_name, + field: parser.parse(v.unnamed.into_iter().next().unwrap()) + }) + } else { + let fields = v.unnamed.into_iter().map(|v| parser.parse(v)); + EnumVariant::MultiField(EnumVariantMulti { + unique_name, + fields: fields.collect() + }) + } + } + Fields::Unit => EnumVariant::None(unique_name) + } + } +} diff --git a/codegen/src/parser/interface.rs b/codegen/src/parser/interface.rs index fb61458..0702187 100644 --- a/codegen/src/parser/interface.rs +++ b/codegen/src/parser/interface.rs @@ -28,6 +28,7 @@ use proc_macro2::TokenStream; use syn::Data; +use crate::parser::enums::{EnumParser, EnumVariant}; use crate::parser::structs::{StructField, StructParser}; pub trait Parser: Sized { @@ -35,13 +36,14 @@ pub trait Parser: Sized { type ParsedVariant; fn parse_field(&mut self, field: StructField) -> Self::ParsedField; + fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant; fn gen_struct(self, parsed: Vec) -> TokenStream; + fn gen_enum(self, parsed: Vec) -> TokenStream; fn parse(mut self, data: Data) -> TokenStream { match data { Data::Struct(v) => { - let mut parser = StructParser::new(); let mut parsed = Vec::new(); for v in v.fields { @@ -49,12 +51,13 @@ pub trait Parser: Sized { } self.gen_struct(parsed) } - Data::Enum(_) => { - /*let mut parser = Self::new_enum_parser(params); + Data::Enum(v) => { + let mut parser = EnumParser; + let mut parsed = Vec::new(); for v in v.variants { - parser.parse_variant(v); - }*/ - todo!() + parsed.push(self.parse_variant(parser.parse(v))); + } + self.gen_enum(parsed) } _ => panic!("Unsupported type") } diff --git a/codegen/src/parser/mod.rs b/codegen/src/parser/mod.rs index 20528df..972260f 100644 --- a/codegen/src/parser/mod.rs +++ b/codegen/src/parser/mod.rs @@ -28,5 +28,6 @@ mod interface; pub mod structs; +pub mod enums; pub use interface::*; From 230f7925e3e1af486f3e09f33d5932e17efe971a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 23 Mar 2025 16:45:52 +0100 Subject: [PATCH 112/527] Added support for lifetimes to macros --- core/src/macros/closure.rs | 28 ++++++++++------ core/src/macros/lib_func.rs | 28 ++++++++++------ core/src/macros/mod.rs | 4 +-- core/src/macros/userdata_func.rs | 56 ++++++++++++++++++++------------ 4 files changed, 74 insertions(+), 42 deletions(-) diff --git a/core/src/macros/closure.rs b/core/src/macros/closure.rs index 4512013..8b489af 100644 --- a/core/src/macros/closure.rs +++ b/core/src/macros/closure.rs @@ -29,33 +29,41 @@ #[macro_export] macro_rules! decl_closure { ( - $vis: vis fn $fn_name: ident |$upvalue_name: ident: $upvalue_ty: ty| ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? |$upvalue_name: ident: $upvalue_ty: ty| ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($name: &$crate::vm::Vm, $upvalue_name: $upvalue_ty$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(&vm, 1) }; - $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? - let ret = _func(&vm, $upvalue_name$(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; + $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? + let ret = _func(vm, $upvalue_name $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(&vm) } $crate::vm::closure::types::RClosure::new(_cfunc, upvalue) } }; ( - $vis: vis fn $fn_name: ident |$upvalue_name: ident: $upvalue_ty: ty| ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? |$upvalue_name: ident: $upvalue_ty: ty| ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block ) => { $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func($upvalue_name: $upvalue_ty, $($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(&vm, 1) }; - $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); - let ret = _func($upvalue_name, $($arg_name),*); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; + $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); + let ret = _func($upvalue_name, $($arg_name),*); + ret.into_param(vm) as _ + } + _vmfunc(&vm) } $crate::vm::closure::types::RClosure::new(_cfunc, upvalue) } diff --git a/core/src/macros/lib_func.rs b/core/src/macros/lib_func.rs index 1ff01f8..263e8a4 100644 --- a/core/src/macros/lib_func.rs +++ b/core/src/macros/lib_func.rs @@ -29,27 +29,35 @@ #[macro_export] macro_rules! decl_lib_func { ( - $vis: vis fn $fn_name: ident ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func($name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? - let ret = _func(&vm $(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? + let ret = _func(vm $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(&vm) } }; ( - $vis: vis fn $fn_name: ident ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($($arg_name: ident: $arg_ty: ty),*) -> $ret_ty: ty $code: block ) => { $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func($($arg_name: $arg_ty),*) -> $ret_ty $code + fn _func $(<$lifetime>)? ($($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); - let ret = _func($($arg_name),*); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); + let ret = _func($($arg_name),*); + ret.into_param(vm) as _ + } + _vmfunc(&vm) } }; } diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index f9bf682..953993a 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -50,11 +50,11 @@ macro_rules! decl_from_param { }; (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty)) => { - let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, $index) }; + let $arg_name: $arg_ty = unsafe { FromParam::from_param($vm, $index) }; }; (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty) $(($arg_name2: ident: $arg_ty2: ty))*) => { - let $arg_name: $arg_ty = unsafe { FromParam::from_param(&$vm, $index) }; + let $arg_name: $arg_ty = unsafe { FromParam::from_param($vm, $index) }; $index += 1; $crate::decl_from_param!(_from_param $vm, $index, $(($arg_name2: $arg_ty2))*); }; diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 991eb25..2973ea0 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -29,18 +29,22 @@ #[macro_export] macro_rules! decl_userdata_func { ( - $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &mut *this_ptr }, &vm $(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &mut *this_ptr }, vm $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(this_ptr, &vm) } let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); @@ -51,18 +55,22 @@ macro_rules! decl_userdata_func { } }; ( - $vis: vis fn $fn_name: ident($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &mut *this_ptr } $(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &mut *this_ptr } $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(this_ptr, &vm) } let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); @@ -73,18 +81,22 @@ macro_rules! decl_userdata_func { } }; ( - $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &*this_ptr }, &vm $(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &*this_ptr }, vm $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(this_ptr, &vm) } let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); @@ -94,18 +106,22 @@ macro_rules! decl_userdata_func { } }; ( - $vis: vis fn $fn_name: ident($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &*this_ptr } $(, $($arg_name),*)?); - ret.into_param(&vm) as _ + #[inline(always)] + extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { + $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + let ret = _func(unsafe { &*this_ptr } $(, $($arg_name),*)?); + ret.into_param(vm) as _ + } + _vmfunc(this_ptr, &vm) } let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); From b023298c9da651e92fba36cece082915b1db48fe Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 23 Mar 2025 16:46:15 +0100 Subject: [PATCH 113/527] Added lifetime test to custom_from_param --- core/tests/test_vm_custom_from_param.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/tests/test_vm_custom_from_param.rs b/core/tests/test_vm_custom_from_param.rs index c672eda..d47b999 100644 --- a/core/tests/test_vm_custom_from_param.rs +++ b/core/tests/test_vm_custom_from_param.rs @@ -73,12 +73,20 @@ decl_lib_func! { } } +decl_lib_func! { + fn test3<'a>(name: &'a str, name2: &str) -> Test2<'a> { + println!("{}", name2); + Test2 { name, value: 42 } + } +} + #[test] fn basic() { let vm = RootVm::new(); let top = vm.top(); vm.set_global(c"test", RFunction::wrap(test)).unwrap(); vm.set_global(c"test2", RFunction::wrap(test2)).unwrap(); + vm.set_global(c"test3", RFunction::wrap(test3)).unwrap(); let out = vm.run_code::<&str>(c" local test1 = { 'value', 42 } local test2 = { name = 'of', value = 64 } @@ -99,5 +107,10 @@ fn basic() { assert(t2.name == 'test') assert(t2.value == 42) ").unwrap(); + vm.run_code::<()>(c" + local t2 = test3('test42', 'test2') + assert(t2.name == 'test42') + assert(t2.value == 42) + ").unwrap(); assert_eq!(top + 3, vm.top()) } From 3cd56f3e69150ace9b7c4d38a37508d5d216ec8b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 23 Mar 2025 17:16:36 +0100 Subject: [PATCH 114/527] Added support for IntoParam derive macro --- codegen/src/gen/from_param.rs | 10 +- codegen/src/gen/into_param.rs | 116 ++++++++++++++++++ codegen/src/gen/mod.rs | 6 +- codegen/src/lib.rs | 8 +- ...rom_param.rs => test_vm_custom_structs.rs} | 24 +--- 5 files changed, 136 insertions(+), 28 deletions(-) create mode 100644 codegen/src/gen/into_param.rs rename core/tests/{test_vm_custom_from_param.rs => test_vm_custom_structs.rs} (86%) diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index 622ec62..a69173e 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -37,12 +37,12 @@ pub struct FromParam { name: Ident, generics: Generics, is_index_based: bool, - is_simpl_enum: bool + is_simple_enum: bool } impl FromParam { pub fn new(name: Ident, generics: Generics) -> Self { - Self { name, generics, is_index_based: false, is_simpl_enum: true } + Self { name, generics, is_index_based: false, is_simple_enum: true } } } @@ -84,7 +84,7 @@ impl Parser for FromParam { fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant { match variant { EnumVariant::SingleField(v) => { - self.is_simpl_enum = false; + self.is_simple_enum = false; let ty = v.field.ty; let name = self.name.clone(); let variant = v.unique_name; @@ -104,9 +104,7 @@ impl Parser for FromParam { } } } - EnumVariant::MultiField(_) => { - panic!("Multi-field enum variants are not supported"); - } + EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), EnumVariant::None(variant) => { let name = self.name.clone(); let vname = variant.to_string(); diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs new file mode 100644 index 0000000..7d0ea06 --- /dev/null +++ b/codegen/src/gen/into_param.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use proc_macro2::{Ident, TokenStream}; +use quote::quote; +use syn::{Generics, Index}; +use crate::parser::enums::EnumVariant; +use crate::parser::Parser; +use crate::parser::structs::StructField; + +pub struct IntoParam { + name: Ident, + generics: Generics +} + +impl IntoParam { + pub fn new(name: Ident, generics: Generics) -> Self { + Self { name, generics } + } +} + +impl Parser for IntoParam { + type ParsedField = TokenStream; + type ParsedVariant = TokenStream; + + fn parse_field(&mut self, field: StructField) -> Self::ParsedField { + if field.unique_name_is_index { + let name_idx = Index::from(field.index); + // Table indices starts at 1 rather than 0 in Lua. + let index = (field.index + 1) as i32; + quote! { + scope.set(#index, self.#name_idx).unwrap(); + } + } else { + let name = field.unique_name; + quote! { + scope.set_field(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); + } + } + } + + fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant { + match variant { + EnumVariant::SingleField(v) => { + let name = v.unique_name; + let ty = v.field.ty; + quote! { + Self::#name(v) => <#ty as bp3d_lua::vm::function::IntoParam>::into_param(v, vm), + } + }, + EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), + EnumVariant::None(name) => { + let str = name.to_string(); + quote! { + Self::#name => <&str as bp3d_lua::vm::function::IntoParam>::into_param(#str, vm), + } + } + } + } + + fn gen_struct(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + quote! { + unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { + fn into_param(self, vm: &bp3d_lua::vm::Vm) -> u16 { + let mut tbl = bp3d_lua::vm::table::Table::new(vm); + { + let mut scope = tbl.lock(); + #(#parsed)* + } + 1 + } + } + } + } + + fn gen_enum(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + quote! { + unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { + fn into_param(self, vm: &bp3d_lua::vm::Vm) -> u16 { + match self { + #(#parsed)* + } + } + } + } + } +} diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs index fa3e114..ecbce60 100644 --- a/codegen/src/gen/mod.rs +++ b/codegen/src/gen/mod.rs @@ -28,6 +28,8 @@ mod from_param; mod lua_type; +mod into_param; -pub use lua_type::*; -pub use from_param::*; +pub use lua_type::LuaType; +pub use from_param::FromParam; +pub use into_param::IntoParam; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 7e7012c..c321e8f 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -32,7 +32,7 @@ mod gen; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; use crate::parser::Parser; -use crate::gen::{FromParam, LuaType}; +use crate::gen::{FromParam, IntoParam, LuaType}; #[proc_macro_derive(FromParam)] pub fn from_param(input: TokenStream) -> TokenStream { @@ -40,6 +40,12 @@ pub fn from_param(input: TokenStream) -> TokenStream { FromParam::new(ident, generics).parse(data).into() } +#[proc_macro_derive(IntoParam)] +pub fn into_param(input: TokenStream) -> TokenStream { + let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + IntoParam::new(ident, generics).parse(data).into() +} + #[proc_macro_derive(LuaType)] pub fn lua_type(input: TokenStream) -> TokenStream { let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); diff --git a/core/tests/test_vm_custom_from_param.rs b/core/tests/test_vm_custom_structs.rs similarity index 86% rename from core/tests/test_vm_custom_from_param.rs rename to core/tests/test_vm_custom_structs.rs index d47b999..1558aac 100644 --- a/core/tests/test_vm_custom_from_param.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -28,34 +28,20 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::{RootVm, Vm}; -use bp3d_lua::vm::function::IntoParam; -use bp3d_lua::vm::table::Table; -use bp3d_lua_codegen::FromParam; +use bp3d_lua::vm::RootVm; +use bp3d_lua_codegen::{FromParam, IntoParam}; use bp3d_lua_codegen::LuaType; -#[derive(FromParam, LuaType)] +#[derive(FromParam, LuaType, IntoParam)] struct Test1<'a>(&'a str, i32); -#[derive(FromParam, LuaType)] +#[derive(FromParam, LuaType, IntoParam)] struct Test2<'a> { name: &'a str, value: i32 } -unsafe impl IntoParam for Test2<'_> { - fn into_param(self, vm: &Vm) -> u16 { - let mut tbl = Table::new(vm); - { - let mut scope = tbl.lock(); - scope.set_field(c"name", self.name).unwrap(); - scope.set_field(c"value", self.value).unwrap(); - } - 1 - } -} - -#[derive(FromParam, LuaType)] +#[derive(FromParam, LuaType, IntoParam)] struct TestStatic { value1: f32, value2: i32 From 78b8976bc59b16efeb5b68c83dfb399939faff25 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 23 Mar 2025 17:17:34 +0100 Subject: [PATCH 115/527] Removed now unneeded impl_from_param macro --- core/src/macros/impl_from_param.rs | 100 ----------------------------- core/src/macros/mod.rs | 1 - 2 files changed, 101 deletions(-) delete mode 100644 core/src/macros/impl_from_param.rs diff --git a/core/src/macros/impl_from_param.rs b/core/src/macros/impl_from_param.rs deleted file mode 100644 index 0fe9e2e..0000000 --- a/core/src/macros/impl_from_param.rs +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -#[macro_export] -macro_rules! _impl_from_param_struct { - ( - $name:ident <$life: lifetime> { - $($value_name: ident: $value_ty: ty),* - } - ) => { - impl<$life> $crate::vm::function::FromParam<$life> for $name<$life> { - unsafe fn from_param(vm: &'a $crate::vm::Vm, index: i32) -> Self { - unsafe { $crate::ffi::laux::luaL_checktype(vm.as_ptr(), index, $crate::ffi::lua::Type::Table); } - let mut top = vm.top(); - $( - unsafe { $crate::ffi::lua::lua_getfield(vm.as_ptr(), index, $crate::c_stringify!($value_name).as_ptr()); } - top += 1; - let $value_name = unsafe { <$value_ty as $crate::vm::function::FromParam>::from_param(vm, top) }; - )* - Self { $($value_name),* } - } - } - }; - ( - $name:ident { - $($value_name: ident: $value_ty: ty),* - } - ) => { - impl<'a> $crate::vm::function::FromParam<'a> for $name { - unsafe fn from_param(vm: &'a $crate::vm::Vm, index: i32) -> Self { - unsafe { $crate::ffi::laux::luaL_checktype(vm.as_ptr(), index, $crate::ffi::lua::Type::Table); } - let mut top = vm.top(); - $( - unsafe { $crate::ffi::lua::lua_getfield(vm.as_ptr(), index, $crate::c_stringify!($value_name).as_ptr()); } - top += 1; - let $value_name = unsafe { <$value_ty as $crate::vm::function::FromParam>::from_param(vm, top) }; - )* - Self { $($value_name),* } - } - } - }; -} - -#[macro_export] -macro_rules! impl_from_param { - ( - $vis: vis struct $name:ident $(<$life: lifetime>)? { - $($value_vis: vis $value_name: ident: $value_ty: ty),* - } - ) => { - $vis struct $name $(<$life>)? { - $($value_vis $value_name: $value_ty),* - } - - unsafe impl$(<$life>)? $crate::vm::util::SimpleDrop for $name$(<$life>)? {} - - impl$(<$life>)? $crate::vm::util::LuaType for $name$(<$life>)? { - fn lua_type() -> Vec<$crate::vm::util::TypeName> { - let mut ret = Vec::new(); - $( - for v in <$value_ty as $crate::vm::util::LuaType>::lua_type() { - ret.push(v); - } - )* - ret - } - } - - $crate::_impl_from_param_struct! { - $name $(<$life>)? { - $($value_name: $value_ty),* - } - } - }; -} diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 953993a..0df73c2 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -29,7 +29,6 @@ mod lib_func; mod userdata_func; mod userdata; -mod impl_from_param; mod closure; #[macro_export] From 979b4f410272c919eb8127100a0d6c3f8f6a47f7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 26 Mar 2025 22:30:04 +0100 Subject: [PATCH 116/527] Added support for different FromUpvalue and IntoUpvalue types --- core/src/macros/closure.rs | 10 +++++----- core/src/vm/closure/core.rs | 27 ++++++++++++++++++++++++++- core/src/vm/closure/interface.rs | 5 +++++ 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/core/src/macros/closure.rs b/core/src/macros/closure.rs index 8b489af..62fff24 100644 --- a/core/src/macros/closure.rs +++ b/core/src/macros/closure.rs @@ -31,14 +31,14 @@ macro_rules! decl_closure { ( $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? |$upvalue_name: ident: $upvalue_ty: ty| ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { - $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { + $vis fn $fn_name(upvalue: <$upvalue_ty as $crate::vm::closure::FromUpvalue>::Into) -> $crate::vm::closure::types::RClosure<<$upvalue_ty as $crate::vm::closure::FromUpvalue>::Into> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($name: &$crate::vm::Vm, $upvalue_name: $upvalue_ty$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func($name: &$crate::vm::Vm, $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; + let $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_> = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? let ret = _func(vm, $upvalue_name $(, $($arg_name),*)?); ret.into_param(vm) as _ @@ -53,12 +53,12 @@ macro_rules! decl_closure { ) => { $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($upvalue_name: $upvalue_ty, $($arg_name: $arg_ty),*) -> $ret_ty $code + fn _func<'a>($upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>, $($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - let $upvalue_name: $upvalue_ty = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; + let $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_> = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); let ret = _func($upvalue_name, $($arg_name),*); ret.into_param(vm) as _ diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index 23ab8b7..da308aa 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_pushlightuserdata, lua_topointer, GLOBALSINDEX}; -use crate::vm::closure::{FromUpvalue, IntoUpvalue}; +use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; use crate::vm::Vm; use crate::vm::value::FromLua; @@ -41,6 +41,11 @@ macro_rules! impl_from_upvalue_using_from_lua_unchecked { <$t>::from_lua_unchecked(vm, GLOBALSINDEX - index) } } + + impl Upvalue for $t { + type From<'a> = $t; + type Into<'a> = $t; + } )* }; } @@ -52,6 +57,11 @@ impl<'a> FromUpvalue<'a> for &'a str { } } +impl Upvalue for &str { + type From<'a> = &'a str; + type Into<'a> = &'a str; +} + impl<'a> FromUpvalue<'a> for &'a [u8] { #[inline(always)] unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { @@ -59,6 +69,11 @@ impl<'a> FromUpvalue<'a> for &'a [u8] { } } +impl Upvalue for &[u8] { + type From<'a> = &'a [u8]; + type Into<'a> = &'a [u8]; +} + #[cfg(target_pointer_width = "64")] impl_from_upvalue_using_from_lua_unchecked!(i64, u64); @@ -95,3 +110,13 @@ impl IntoUpvalue for *const T { 1 } } + +impl Upvalue for *mut T { + type From<'a> = *mut T; + type Into<'a> = *mut T; +} + +impl Upvalue for *const T { + type From<'a> = *const T; + type Into<'a> = *const T; +} diff --git a/core/src/vm/closure/interface.rs b/core/src/vm/closure/interface.rs index 7511170..eab015c 100644 --- a/core/src/vm/closure/interface.rs +++ b/core/src/vm/closure/interface.rs @@ -51,3 +51,8 @@ pub trait FromUpvalue<'a>: Sized + SimpleDrop { pub trait IntoUpvalue { fn into_upvalue(self, vm: &Vm) -> u16; } + +pub trait Upvalue { + type From<'a>: FromUpvalue<'a>; + type Into<'a>: IntoUpvalue; +} From a70151e845e2b29951dfc7fe515e047100446124 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 26 Mar 2025 22:38:21 +0100 Subject: [PATCH 117/527] Added new RawRegistryKey type which is much more unsafe but is much closer to lua registry keys --- core/src/vm/registry/core.rs | 84 ++++++++++++++++++++++++++++--- core/src/vm/registry/interface.rs | 15 +++++- core/src/vm/registry/types.rs | 6 +-- 3 files changed, 93 insertions(+), 12 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 5004e2f..2d22961 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -33,8 +33,77 @@ use crate::ffi::lua::{lua_rawgeti, REGISTRYINDEX}; use crate::vm::registry::RegistryValue; use crate::vm::Vm; +//TODO: Check if key can be a NonZeroI32 + +#[derive(Debug, Copy, Clone)] +#[repr(transparent)] +pub struct RawRegistryKey(c_int); + +impl RawRegistryKey { + /// Returns the raw key. + #[inline(always)] + pub fn as_int(&self) -> c_int { + self.0 + } + + /// Wraps a raw integer as a registry key. + #[inline(always)] + pub fn from_int(v: c_int) -> RawRegistryKey { + RawRegistryKey(v) + } + + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + /// + /// # Safety + /// + /// This is UB to call if the key is invalid or already freed. + #[inline(always)] + pub fn push(&self, vm: &Vm) { + unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.0) }; + } + + /// Deletes this registry key from the specified [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to unregister from. + /// + /// returns: () + /// + /// # Safety + /// + /// This is UB to call if the registry key is invalid or was already freed. + #[inline(always)] + pub unsafe fn delete(self, vm: &Vm) { + unsafe { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.0) }; + } + + /// Creates a new [RawRegistryKey] from the top of the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance representing the lua stack. + /// + /// returns: RegistryKey + /// + /// # Safety + /// + /// This is UB to call if the stack is empty. + #[inline(always)] + pub unsafe fn from_top(vm: &Vm) -> RawRegistryKey { + let key = unsafe { luaL_ref(vm.as_ptr(), REGISTRYINDEX) }; + RawRegistryKey(key) + } +} + pub struct RegistryKey { - key: c_int, + raw: RawRegistryKey, useless: PhantomData } @@ -48,8 +117,8 @@ impl RegistryKey { /// returns: ::Value #[inline(always)] pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { - unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; - T::to_lua_value(vm, -1) + self.raw.push(vm); + unsafe { T::to_lua_value(vm, -1) } } /// Pushes the lua value associated to this registry key on the lua stack. @@ -60,8 +129,8 @@ impl RegistryKey { /// /// returns: ::Value #[inline(always)] - pub fn raw_push(&self, vm: &Vm) { - unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.key) }; + pub fn as_raw(&self) -> RawRegistryKey { + self.raw } /// Deletes this registry key from the specified [Vm]. @@ -73,7 +142,7 @@ impl RegistryKey { /// returns: () #[inline(always)] pub fn delete(self, vm: &Vm) { - unsafe { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.key) }; + unsafe { self.raw.delete(vm) }; } /// Creates a new [RegistryKey] from the top of the lua stack. @@ -89,9 +158,8 @@ impl RegistryKey { /// The type T must match the type of the value at the top of the stack. Additionally, the value /// at the top of the stack must not be referenced as it will be popped. pub unsafe fn from_top(vm: &Vm) -> RegistryKey { - let key = luaL_ref(vm.as_ptr(), REGISTRYINDEX); RegistryKey { - key, + raw: RawRegistryKey::from_top(vm), useless: PhantomData } } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 30efee9..4137c44 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -32,7 +32,20 @@ use crate::vm::Vm; pub trait RegistryValue: 'static { type Value<'a>; - fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a>; + /// Reads the upvalue at the given location on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to read from. + /// * `index`: the index of the value. This index is not guaranteed to be absolute. + /// + /// returns: Self::Value + /// + /// # Safety + /// + /// This function assumes the value at the top of the stack is of type `Self`. This function is + /// UB otherwise. + unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a>; } /// A trait to produce registry values safely. diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 63f85d5..27e36cd 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -39,7 +39,7 @@ impl RegistryValue for Table { type Value<'a> = crate::vm::table::Table<'a>; #[inline(always)] - fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { unsafe { crate::vm::table::Table::from_lua_unchecked(vm, index) } } } @@ -48,7 +48,7 @@ impl RegistryValue for LuaFunction { type Value<'a> = crate::vm::value::function::LuaFunction<'a>; #[inline(always)] - fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { unsafe { crate::vm::value::function::LuaFunction::from_lua_unchecked(vm, index) } } } @@ -56,7 +56,7 @@ impl RegistryValue for LuaFunction { impl RegistryKey { pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { let pos = push_error_handler(vm.as_ptr()); - self.raw_push(vm); + self.as_raw().push(vm); let num_values = value.into_lua(vm); unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(vm, -(R::num_values() as i32)) From 1e9581dbcc3b75a48bb0eae323a6ab45903d5eb2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 26 Mar 2025 22:38:49 +0100 Subject: [PATCH 118/527] Added initial version of context system with macro test (still no runtime tests) --- core/src/vm/closure/context.rs | 133 +++++++++++++++++++++++++++++++++ core/src/vm/closure/mod.rs | 1 + core/tests/test_vm_closures.rs | 27 +++++++ 3 files changed, 161 insertions(+) create mode 100644 core/src/vm/closure/context.rs diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs new file mode 100644 index 0000000..853a7fa --- /dev/null +++ b/core/src/vm/closure/context.rs @@ -0,0 +1,133 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! A module to simplify declaring functions with associated to a context (rust object). + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use crate::ffi::laux::luaL_error; +use crate::ffi::lua::{lua_settop, lua_topointer}; +use crate::util::SimpleDrop; +use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::registry::core::{RawRegistryKey}; +use crate::vm::Vm; + +pub struct Context { + key: RawRegistryKey, + obj: PhantomData +} + +pub struct ContextMut { + key: RawRegistryKey, + obj: PhantomData +} + +#[repr(transparent)] +pub struct Ref<'a, T>(&'a T); + +#[repr(transparent)] +pub struct Mut<'a, T>(&'a mut T); + +impl<'a, T: 'static> Deref for Ref<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T: 'static> Deref for Mut<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T: 'static> DerefMut for Mut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> { } +unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> { } + +impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let key = RawRegistryKey::from_int(FromUpvalue::from_upvalue(vm, index)); + key.push(vm); + let ptr = lua_topointer(vm.as_ptr(), -1) as *const T; + //Remove lightuserdata on the top of the stack. + lua_settop(vm.as_ptr(), -2); + if ptr.is_null() { + luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + // luaL_error raises a lua exception and unwinds, so this cannot be reached. + std::hint::unreachable_unchecked(); + } + Ref(unsafe { &*ptr }) + } +} + +impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let key = RawRegistryKey::from_int(FromUpvalue::from_upvalue(vm, index)); + key.push(vm); + let ptr = lua_topointer(vm.as_ptr(), -1) as *mut T; + //Remove lightuserdata on the top of the stack. + lua_settop(vm.as_ptr(), -2); + if ptr.is_null() { + luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + // luaL_error raises a lua exception and unwinds, so this cannot be reached. + std::hint::unreachable_unchecked(); + } + Mut(unsafe { &mut *ptr }) + } +} + +impl IntoUpvalue for Context { + fn into_upvalue(self, vm: &Vm) -> u16 { + self.key.as_int().into_upvalue(vm) + } +} + +impl IntoUpvalue for ContextMut { + fn into_upvalue(self, vm: &Vm) -> u16 { + self.key.as_int().into_upvalue(vm) + } +} + +impl Upvalue for Context { + type From<'a> = Ref<'a, T>; + type Into<'a> = Context; +} + +impl Upvalue for ContextMut { + type From<'a> = Mut<'a, T>; + type Into<'a> = ContextMut; +} diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index d8518c6..4e874bd 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -29,5 +29,6 @@ mod interface; mod core; pub mod types; +pub mod context; pub use interface::*; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 0b9ff03..9fb3ce6 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,9 +27,36 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::context::ContextMut; use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::RootVm; +struct TestContext { + value: i32, + value3: Vec +} + +decl_closure! { + fn context_set_value |ctx: ContextMut| (val: i32) -> () { + let mut ctx = ctx; + ctx.value = val; + } +} + +decl_closure! { + fn context_push |ctx: ContextMut| (val: u64) -> () { + let mut ctx = ctx; + ctx.value3.push(val); + } +} + +decl_closure! { + fn context_pop |ctx: ContextMut| () -> Option { + let mut ctx = ctx; + ctx.value3.pop() + } +} + decl_closure! { fn test |upvalue: &str| (val: f32) -> String { format!("{}: {}", upvalue, val) From c0638161977742d3530f057e1bf380bb164fede7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Mar 2025 22:44:08 +0100 Subject: [PATCH 119/527] Added replace function to RawRegistryKey --- core/src/vm/registry/core.rs | 26 +++++++++++++++++++++----- core/src/vm/registry/types.rs | 2 +- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 2d22961..cf6fd0f 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -29,7 +29,7 @@ use std::ffi::c_int; use std::marker::PhantomData; use crate::ffi::laux::{luaL_ref, luaL_unref}; -use crate::ffi::lua::{lua_rawgeti, REGISTRYINDEX}; +use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; use crate::vm::registry::RegistryValue; use crate::vm::Vm; @@ -64,8 +64,8 @@ impl RawRegistryKey { /// /// This is UB to call if the key is invalid or already freed. #[inline(always)] - pub fn push(&self, vm: &Vm) { - unsafe { lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.0) }; + pub unsafe fn push(&self, vm: &Vm) { + lua_rawgeti(vm.as_ptr(), REGISTRYINDEX, self.0); } /// Deletes this registry key from the specified [Vm]. @@ -81,7 +81,23 @@ impl RawRegistryKey { /// This is UB to call if the registry key is invalid or was already freed. #[inline(always)] pub unsafe fn delete(self, vm: &Vm) { - unsafe { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.0) }; + luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.0); + } + + /// Replaces the content of this key with the value on top of the stack. + /// + /// # Arguments + /// + /// * `vm`: the vm associated with this key. + /// + /// returns: () + /// + /// # Safety + /// + /// This is UB to call if the key has already been deleted. + #[inline(always)] + pub unsafe fn replace(&self, vm: &Vm) { + lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); } /// Creates a new [RawRegistryKey] from the top of the lua stack. @@ -117,7 +133,7 @@ impl RegistryKey { /// returns: ::Value #[inline(always)] pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { - self.raw.push(vm); + unsafe { self.raw.push(vm) }; unsafe { T::to_lua_value(vm, -1) } } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 27e36cd..e1742fd 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -56,7 +56,7 @@ impl RegistryValue for LuaFunction { impl RegistryKey { pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { let pos = push_error_handler(vm.as_ptr()); - self.as_raw().push(vm); + unsafe { self.as_raw().push(vm) }; let num_values = value.into_lua(vm); unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(vm, -(R::num_values() as i32)) From 6cea70156073f43fe639d8958d22b84eae55e830 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Mar 2025 22:44:52 +0100 Subject: [PATCH 120/527] Fixed build error in decl_from_param macro --- core/src/macros/mod.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 0df73c2..5811c74 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -48,6 +48,8 @@ macro_rules! decl_from_param { $crate::decl_from_param!(_from_param $vm, index, $(($arg_name: $arg_ty))*); }; + (_from_param $vm: ident, $index: ident, ) => { }; + (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty)) => { let $arg_name: $arg_ty = unsafe { FromParam::from_param($vm, $index) }; }; From 4c2a373f20288b85a1ced56c6ff333f72ae4bae7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Mar 2025 22:45:20 +0100 Subject: [PATCH 121/527] Finished initial version of context system --- core/src/vm/closure/context.rs | 88 +++++++++++++++++++++++++++++++--- 1 file changed, 82 insertions(+), 6 deletions(-) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 853a7fa..3830933 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -28,10 +28,11 @@ //! A module to simplify declaring functions with associated to a context (rust object). +use std::ffi::c_void; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::luaL_error; -use crate::ffi::lua::{lua_settop, lua_topointer}; +use crate::ffi::lua::{lua_pushlightuserdata, lua_settop, lua_topointer}; use crate::util::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::registry::core::{RawRegistryKey}; @@ -39,12 +40,87 @@ use crate::vm::Vm; pub struct Context { key: RawRegistryKey, - obj: PhantomData + useless: PhantomData } -pub struct ContextMut { - key: RawRegistryKey, - obj: PhantomData +impl Clone for Context { + fn clone(&self) -> Self { + Self { + key: self.key, + useless: self.useless + } + } +} + +impl Copy for Context {} + +pub struct ContextMut(Context); + +impl Clone for ContextMut { + fn clone(&self) -> Self { + Self(self.0) + } +} + +impl Copy for ContextMut { } + +impl Context { + pub fn new(vm: &Vm) -> Self { + let key = unsafe { + lua_pushlightuserdata(vm.as_ptr(), std::ptr::null_mut()); + RawRegistryKey::from_top(vm) + }; + Self { + key, + useless: PhantomData + } + } + + pub fn bind<'a, 'b>(&self, vm: &'a Vm, obj: &'b T) -> Guard<'a, &'b T> { + unsafe { + lua_pushlightuserdata(vm.as_ptr(), obj as *const T as *mut T as *mut c_void); + self.key.replace(vm); + Guard { + vm, + ptr: obj, + key: self.key + } + } + } +} + +impl ContextMut { + pub fn new(vm: &Vm) -> Self { + Self(Context::new(vm)) + } + + pub fn bind<'a, 'b>(&self, vm: &'a Vm, obj: &'b mut T) -> Guard<'a, &'b mut T> { + unsafe { + lua_pushlightuserdata(vm.as_ptr(), obj as *mut T as *mut c_void); + self.0.key.replace(vm); + Guard { + vm, + ptr: obj, + key: self.0.key + } + } + } +} + +pub struct Guard<'a, T> { + vm: &'a Vm, + #[allow(dead_code)] + ptr: T, + key: RawRegistryKey +} + +impl<'a, T> Drop for Guard<'a, T> { + fn drop(&mut self) { + unsafe { + lua_pushlightuserdata(self.vm.as_ptr(), std::ptr::null_mut()); + self.key.replace(self.vm); + } + } } #[repr(transparent)] @@ -118,7 +194,7 @@ impl IntoUpvalue for Context { impl IntoUpvalue for ContextMut { fn into_upvalue(self, vm: &Vm) -> u16 { - self.key.as_int().into_upvalue(vm) + self.0.key.as_int().into_upvalue(vm) } } From 40fd4f398b73bd380f0b5d27ad1c74003049aad3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Mar 2025 22:45:32 +0100 Subject: [PATCH 122/527] Added initial context test --- core/src/vm/error.rs | 6 +++--- core/tests/test_vm_closures.rs | 14 ++++++++++++++ core/tests/test_vm_destructor.rs | 2 +- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index eacd2e3..e44745f 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -94,10 +94,10 @@ simple_error! { } impl Error { - pub fn into_runtime(self) -> RuntimeError { + pub fn into_runtime(self) -> Option { match self { - Error::Runtime(e) => e, - _ => panic!("error is not a runtime error") + Error::Runtime(e) => Some(e), + _ => None } } } diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 9fb3ce6..ce8246c 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -85,3 +85,17 @@ fn test_vm_rust_closure() { let s: &str = vm.run_code(c"return test(42.42)").unwrap(); assert_eq!(s, "this is a test: 42.42"); } + +#[test] +fn test_vm_context() { + let vm = RootVm::new(); + let top = vm.top(); + let ctx = ContextMut::new(&vm); + vm.set_global(c"context_push", context_push(ctx)).unwrap(); + vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); + vm.set_global(c"context_set_value", context_set_value(ctx)).unwrap(); + assert_eq!(top, vm.top()); + let res = vm.run_code::<()>(c"context_set_value(42)"); + assert!(res.is_err()); + assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context_set_value(42)\"]:1: Context is not available in this function."); +} diff --git a/core/tests/test_vm_destructor.rs b/core/tests/test_vm_destructor.rs index 24304e5..7e5d57c 100644 --- a/core/tests/test_vm_destructor.rs +++ b/core/tests/test_vm_destructor.rs @@ -57,7 +57,7 @@ fn test_vm_destructor() { let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); - let err = res.unwrap_err().into_runtime(); + let err = res.unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: invalid utf-8 sequence of 1 bytes from index 14"); assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); From 5c0e907bec17afb9855fe8d79e6d6663e3331b72 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 27 Mar 2025 22:57:28 +0100 Subject: [PATCH 123/527] Improved context test --- core/tests/test_vm_closures.rs | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index ce8246c..43a1d03 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -98,4 +98,34 @@ fn test_vm_context() { let res = vm.run_code::<()>(c"context_set_value(42)"); assert!(res.is_err()); assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context_set_value(42)\"]:1: Context is not available in this function."); + let mut obj = TestContext { + value: 0, + value3: vec![], + }; + { + let _obj = ctx.bind(&vm, &mut obj); + vm.run_code::<()>(c"context_set_value(42)").unwrap(); + } + let res = vm.run_code::<()>(c"context_set_value(84)"); + assert!(res.is_err()); + assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context_set_value(84)\"]:1: Context is not available in this function."); + assert_eq!(obj.value, 42); + { + let _obj = ctx.bind(&vm, &mut obj); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + vm.run_code::<()>(c"context_push(1)").unwrap(); + vm.run_code::<()>(c"context_push(2)").unwrap(); + vm.run_code::<()>(c"context_push(3)").unwrap(); + } + assert_eq!(obj.value3.len(), 3); + { + let _obj = ctx.bind(&vm, &mut obj); + vm.run_code::<()>(c"assert(context_pop() == 3)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == 2)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == 1)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + } + assert_eq!(obj.value3.len(), 0); + assert_eq!(top, vm.top()); } From 199f6a630158faef37e344d8918041005726882d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 00:05:55 +0100 Subject: [PATCH 124/527] Improved closure API --- core/src/vm/closure/context.rs | 2 -- core/src/vm/closure/core.rs | 7 +------ core/src/vm/closure/interface.rs | 3 +-- 3 files changed, 2 insertions(+), 10 deletions(-) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 3830933..1efb72b 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -200,10 +200,8 @@ impl IntoUpvalue for ContextMut { impl Upvalue for Context { type From<'a> = Ref<'a, T>; - type Into<'a> = Context; } impl Upvalue for ContextMut { type From<'a> = Mut<'a, T>; - type Into<'a> = ContextMut; } diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index da308aa..e747f60 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -44,7 +44,6 @@ macro_rules! impl_from_upvalue_using_from_lua_unchecked { impl Upvalue for $t { type From<'a> = $t; - type Into<'a> = $t; } )* }; @@ -59,7 +58,6 @@ impl<'a> FromUpvalue<'a> for &'a str { impl Upvalue for &str { type From<'a> = &'a str; - type Into<'a> = &'a str; } impl<'a> FromUpvalue<'a> for &'a [u8] { @@ -71,7 +69,6 @@ impl<'a> FromUpvalue<'a> for &'a [u8] { impl Upvalue for &[u8] { type From<'a> = &'a [u8]; - type Into<'a> = &'a [u8]; } #[cfg(target_pointer_width = "64")] @@ -91,7 +88,7 @@ impl FromUpvalue<'_> for *const T { } } -impl IntoUpvalue for T { +impl IntoUpvalue for T { fn into_upvalue(self, vm: &Vm) -> u16 { self.into_param(vm) } @@ -113,10 +110,8 @@ impl IntoUpvalue for *const T { impl Upvalue for *mut T { type From<'a> = *mut T; - type Into<'a> = *mut T; } impl Upvalue for *const T { type From<'a> = *const T; - type Into<'a> = *const T; } diff --git a/core/src/vm/closure/interface.rs b/core/src/vm/closure/interface.rs index eab015c..4c60d35 100644 --- a/core/src/vm/closure/interface.rs +++ b/core/src/vm/closure/interface.rs @@ -48,11 +48,10 @@ pub trait FromUpvalue<'a>: Sized + SimpleDrop { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self; } -pub trait IntoUpvalue { +pub trait IntoUpvalue: Upvalue { fn into_upvalue(self, vm: &Vm) -> u16; } pub trait Upvalue { type From<'a>: FromUpvalue<'a>; - type Into<'a>: IntoUpvalue; } From 2899b4032515d42ec474c9baad4e55d7a8254f44 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 00:40:23 +0100 Subject: [PATCH 125/527] Added performance test comparison with mlua for context based functions --- testbin/src/context.rs | 121 +++++++++++++++++++++++++++++++++++++++++ testbin/src/main.rs | 20 ++++++- 2 files changed, 138 insertions(+), 3 deletions(-) create mode 100644 testbin/src/context.rs diff --git a/testbin/src/context.rs b/testbin/src/context.rs new file mode 100644 index 0000000..00711ef --- /dev/null +++ b/testbin/src/context.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use mlua::{Lua, UserDataMethods}; +use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::context::ContextMut; +use bp3d_lua::vm::RootVm; + +struct TestContext { + value3: Vec +} + +decl_closure! { + fn context_push |ctx: ContextMut| (val: u64) -> () { + let mut ctx = ctx; + ctx.value3.push(val); + } +} + +decl_closure! { + fn context_pop |ctx: ContextMut| () -> Option { + let mut ctx = ctx; + ctx.value3.pop() + } +} + +pub fn test_context_mlua() -> Duration { + let lua = Lua::new(); + lua.register_userdata_type::(|reg| { + reg.add_method_mut("push", |_, this, val: u64| { + this.value3.push(val); + Ok(()) + }); + reg.add_method_mut("pop", |_, this, _: ()| { + Ok(this.value3.pop()) + }); + }).unwrap(); + let mut ctx = TestContext { + value3: Vec::new() + }; + let time = std::time::Instant::now(); + for _ in 0..20000 { + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + lua.globals().set("ctx", ud).unwrap(); + lua.load("assert(ctx:pop() == nil)").eval::<()>().unwrap(); + lua.load("ctx:push(1)").eval::<()>().unwrap(); + lua.load("ctx:push(2)").eval::<()>().unwrap(); + lua.load("ctx:push(3)").eval::<()>().unwrap(); + Ok(()) + }).unwrap(); + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + lua.globals().set("ctx", ud).unwrap(); + lua.load("assert(ctx:pop() == 3)").eval::<()>().unwrap(); + lua.load("assert(ctx:pop() == 2)").eval::<()>().unwrap(); + lua.load("assert(ctx:pop() == 1)").eval::<()>().unwrap(); + lua.load("assert(ctx:pop() == nil)").eval::<()>().unwrap(); + lua.load("assert(ctx:pop() == nil)").eval::<()>().unwrap(); + Ok(()) + }).unwrap(); + } + let time = time.elapsed(); + time +} + +pub fn test_context_vm() -> Duration { + let vm = RootVm::new(); + let ctx = ContextMut::new(&vm); + vm.set_global(c"context_push", context_push(ctx)).unwrap(); + vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); + let mut obj = TestContext { + value3: vec![], + }; + let time = std::time::Instant::now(); + for _ in 0..20000 { + { + let _obj = ctx.bind(&vm, &mut obj); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + vm.run_code::<()>(c"context_push(1)").unwrap(); + vm.run_code::<()>(c"context_push(2)").unwrap(); + vm.run_code::<()>(c"context_push(3)").unwrap(); + } + { + let _obj = ctx.bind(&vm, &mut obj); + vm.run_code::<()>(c"assert(context_pop() == 3)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == 2)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == 1)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + } + } + let time = time.elapsed(); + time +} diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 63ce555..357ec36 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod context; + use std::time::Duration; use mlua::Lua; use bp3d_lua::decl_lib_func; @@ -90,14 +92,26 @@ fn main() { const RUNS: u32 = 10; let mut lua = Duration::new(0, 0); let mut mlua = Duration::new(0, 0); + let mut ctx_lua = Duration::new(0, 0); + let mut ctx_mlua = Duration::new(0, 0); for _ in 0..RUNS { lua += test_vm_destructor(); mlua += test_vm_mlua(); + ctx_lua += context::test_context_vm(); + ctx_mlua += context::test_context_mlua(); } lua = lua / RUNS; mlua = mlua / RUNS; - println!("average tools.lua: {:?}", lua); - println!("average mlua: {:?}", mlua); + ctx_lua = ctx_lua / RUNS; + ctx_mlua = ctx_mlua / RUNS; + + println!("average tools.lua (basic): {:?}", lua); + println!("average mlua (basic): {:?}", mlua); + assert!(lua < mlua); + println!("average diff (basic): {:?}", mlua - lua); + + println!("average tools.lua (context): {:?}", ctx_lua); + println!("average mlua (context): {:?}", ctx_mlua); assert!(lua < mlua); - println!("average diff: {:?}", mlua - lua); + println!("average diff (context): {:?}", ctx_mlua - ctx_lua); } From 61ea3587014b510f551f18292d5bc0c689618e41 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 09:28:40 +0100 Subject: [PATCH 126/527] Added initial support for lua threads --- core/src/ffi/lua.rs | 6 ++- core/src/vm/mod.rs | 1 + core/src/vm/thread.rs | 116 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 2 deletions(-) create mode 100644 core/src/vm/thread.rs diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 54fdbbd..ccbd6d2 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -86,6 +86,8 @@ extern "C" { pub fn lua_close(l: State); pub fn lua_atpanic(l: State, panicf: CFunction) -> CFunction; + + pub fn lua_newthread(l: State) -> State; } //-------------------------- @@ -189,8 +191,8 @@ extern "C" { //--------------------- extern "C" { pub fn lua_yield(l: State, nresults: c_int) -> c_int; - pub fn lua_resume(l: State, narg: c_int) -> c_int; - pub fn lua_status(l: State) -> c_int; + pub fn lua_resume(l: State, narg: c_int) -> ThreadStatus; + pub fn lua_status(l: State) -> ThreadStatus; } //----------------------------------------- diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 8227cba..6017547 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -35,6 +35,7 @@ pub mod userdata; pub mod closure; pub mod registry; pub mod table; +pub mod thread; pub use core::*; diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs new file mode 100644 index 0000000..5810dca --- /dev/null +++ b/core/src/vm/thread.rs @@ -0,0 +1,116 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use crate::ffi::laux::luaL_checktype; +use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; +use crate::util::SimpleDrop; +use crate::vm::error::{Error, RuntimeError}; +use crate::vm::function::FromParam; +use crate::vm::util::{LoadCode, LuaType}; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::util::ensure_type_equals; +use crate::vm::Vm; + +pub enum State { + Yielded, + Finished +} + +pub struct Thread<'a> { + vm: Vm, + useless: PhantomData<&'a ()> +} + +impl<'a> Thread<'a> { + #[inline(always)] + pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadCode) -> crate::vm::Result { + self.vm.run_code(code) + } + + #[inline(always)] + pub fn status(&self) -> ThreadStatus { + unsafe { lua_status(self.vm.as_ptr()) } + } + + pub fn resume(&self, args: impl IntoLua) -> crate::vm::Result { + let num = args.into_lua(&self.vm); + let res = unsafe { lua_resume(self.vm.as_ptr(), num as _) }; + match res { + ThreadStatus::Ok => Ok(State::Finished), + ThreadStatus::Yield => Ok(State::Yielded), + ThreadStatus::ErrRun => { + // We've got a runtime error when executing the function so read the full stack + // trace produced by luaL_traceback and remove it from the stack. + let error_message: &str = FromLua::from_lua(&self.vm, -1)?; + unsafe { lua_remove(self.vm.as_ptr(), -1) }; + Err(Error::Runtime(RuntimeError::new(String::from(error_message) + "\n"))) + } + ThreadStatus::ErrMem => Err(Error::Memory), + ThreadStatus::ErrErr => Err(Error::Error), + _ => std::unreachable!() + } + } +} + +impl<'a> FromLua<'a> for Thread<'a> { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Thread { + vm: Vm::from_raw(lua_tothread(vm.as_ptr(), index)), + useless: PhantomData + } + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + ensure_type_equals(vm, index, Type::Thread)?; + Ok(Thread { + vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, + useless: PhantomData + }) + } +} + +unsafe impl SimpleDrop for Thread<'_> { } + +impl LuaType for Thread<'_> { } + +impl<'a> FromParam<'a> for Thread<'a> { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + luaL_checktype(vm.as_ptr(), index, Type::Thread); + Thread { + vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, + useless: PhantomData + } + } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } +} From 9e19e910f93462cb95684b3ae530c1212e71c21d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 09:30:43 +0100 Subject: [PATCH 127/527] Added Thread to AnyValue --- core/src/vm/value/any.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 0ebea89..8d2cbf4 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -31,6 +31,7 @@ use crate::vm::error::Error; use crate::vm::value::FromLua; use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; +use crate::vm::thread::Thread; use crate::vm::userdata::AnyUserData; use crate::vm::Vm; @@ -43,7 +44,8 @@ pub enum AnyValue<'a> { Buffer(&'a [u8]), Function(LuaFunction<'a>), Table(Table<'a>), - UserData(AnyUserData<'a>) + UserData(AnyUserData<'a>), + Thread(Thread<'a>) } impl<'a> FromLua<'a> for AnyValue<'a> { @@ -75,7 +77,7 @@ impl<'a> FromLua<'a> for AnyValue<'a> { Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), Type::Function => Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }), Type::Userdata => Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }), - Type::Thread => Err(Error::UnsupportedType(ty)) + Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), } } } From 511072975b32cdec6db32857a486a24d874e78c3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 09:56:54 +0100 Subject: [PATCH 128/527] Fixed usability issue in LuaFunction --- core/src/vm/value/function.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 6f93717..6e2585f 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -40,7 +40,7 @@ pub struct LuaFunction<'a> { } impl<'a> LuaFunction<'a> { - pub fn call<'b, T: IntoLua, R: FromLua<'b>>(&'b self, value: T) -> crate::vm::Result { + pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { let pos = push_error_handler(self.vm.as_ptr()); unsafe { lua_pushvalue(self.vm.as_ptr(), self.index); } let num_values = value.into_lua(self.vm); From 7128c26597e2c44ec926aef722ca3295ce14c930 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 09:57:40 +0100 Subject: [PATCH 129/527] Added a more reasonable test using function calls rather than run_code --- testbin/src/context_opt.rs | 140 +++++++++++++++++++++++++++++++++++++ testbin/src/main.rs | 16 ++++- 2 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 testbin/src/context_opt.rs diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs new file mode 100644 index 0000000..a7ea87a --- /dev/null +++ b/testbin/src/context_opt.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use mlua::{Function, Lua, UserDataMethods}; +use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::context::ContextMut; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::function::LuaFunction; + +struct TestContext { + value3: Vec +} + +decl_closure! { + fn context_push |ctx: ContextMut| (val: u64) -> () { + let mut ctx = ctx; + ctx.value3.push(val); + } +} + +decl_closure! { + fn context_pop |ctx: ContextMut| () -> Option { + let mut ctx = ctx; + ctx.value3.pop() + } +} + +pub fn test_context_mlua() -> Duration { + let lua = Lua::new(); + lua.register_userdata_type::(|reg| { + reg.add_method_mut("push", |_, this, val: u64| { + this.value3.push(val); + Ok(()) + }); + reg.add_method_mut("pop", |_, this, _: ()| { + Ok(this.value3.pop()) + }); + }).unwrap(); + lua.load(" + function part1(ctx) + assert(ctx:pop() == nil) + ctx:push(1) + ctx:push(2) + ctx:push(3) + end + function part2(ctx) + assert(ctx:pop() == 3) + assert(ctx:pop() == 2) + assert(ctx:pop() == 1) + assert(ctx:pop() == nil) + assert(ctx:pop() == nil) + end + ").eval::<()>().unwrap(); + let part1: Function = lua.globals().get("part1").unwrap(); + let part2: Function = lua.globals().get("part2").unwrap(); + let mut ctx = TestContext { + value3: Vec::new() + }; + let time = std::time::Instant::now(); + for _ in 0..20000 { + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + part1.call::<()>(ud).unwrap(); + Ok(()) + }).unwrap(); + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + part2.call::<()>(ud).unwrap(); + Ok(()) + }).unwrap(); + } + let time = time.elapsed(); + time +} + +pub fn test_context_vm() -> Duration { + let vm = RootVm::new(); + let ctx = ContextMut::new(&vm); + vm.set_global(c"context_push", context_push(ctx)).unwrap(); + vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); + vm.run_code::<()>(c" + function part1() + assert(context_pop() == nil) + context_push(1) + context_push(2) + context_push(3) + end + function part2() + assert(context_pop() == 3) + assert(context_pop() == 2) + assert(context_pop() == 1) + assert(context_pop() == nil) + assert(context_pop() == nil) + end + ").unwrap(); + let part1: LuaFunction = vm.get_global("part1").unwrap(); + let part2: LuaFunction = vm.get_global("part2").unwrap(); + let mut obj = TestContext { + value3: vec![], + }; + let time = std::time::Instant::now(); + for _ in 0..20000 { + { + let _obj = ctx.bind(&vm, &mut obj); + part1.call::<()>(()).unwrap(); + } + { + let _obj = ctx.bind(&vm, &mut obj); + part2.call::<()>(()).unwrap(); + } + } + let time = time.elapsed(); + time +} diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 357ec36..b0574f8 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod context; +mod context_opt; use std::time::Duration; use mlua::Lua; @@ -94,16 +95,24 @@ fn main() { let mut mlua = Duration::new(0, 0); let mut ctx_lua = Duration::new(0, 0); let mut ctx_mlua = Duration::new(0, 0); + let mut ctx_lua_opt = Duration::new(0, 0); + let mut ctx_mlua_opt = Duration::new(0, 0); + for _ in 0..RUNS { lua += test_vm_destructor(); mlua += test_vm_mlua(); ctx_lua += context::test_context_vm(); ctx_mlua += context::test_context_mlua(); + ctx_lua_opt += context_opt::test_context_vm(); + ctx_mlua_opt += context_opt::test_context_mlua(); } + lua = lua / RUNS; mlua = mlua / RUNS; ctx_lua = ctx_lua / RUNS; ctx_mlua = ctx_mlua / RUNS; + ctx_lua_opt = ctx_lua_opt / RUNS; + ctx_mlua_opt = ctx_mlua_opt / RUNS; println!("average tools.lua (basic): {:?}", lua); println!("average mlua (basic): {:?}", mlua); @@ -112,6 +121,11 @@ fn main() { println!("average tools.lua (context): {:?}", ctx_lua); println!("average mlua (context): {:?}", ctx_mlua); - assert!(lua < mlua); + assert!(ctx_lua < ctx_mlua); println!("average diff (context): {:?}", ctx_mlua - ctx_lua); + + println!("average tools.lua (context_opt): {:?}", ctx_lua_opt); + println!("average mlua (context_opt): {:?}", ctx_mlua_opt); + assert!(ctx_lua_opt < ctx_mlua_opt); + println!("average diff (context_opt): {:?}", ctx_mlua_opt - ctx_lua_opt); } From ac5037e780a795f2fd0732a758994011e0675be3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 15:22:20 +0100 Subject: [PATCH 130/527] Use real-time based Instant from bp3d-os to avoid biased clock_gettime --- testbin/Cargo.toml | 1 + testbin/src/context.rs | 4 ++-- testbin/src/context_opt.rs | 4 ++-- testbin/src/main.rs | 4 ++-- 4 files changed, 7 insertions(+), 6 deletions(-) diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index fe56e55..15d22aa 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -9,3 +9,4 @@ publish = false [dependencies] mlua = { version = "0.10.3", features = ["luajit", "vendored"] } bp3d-lua = { version = "0.2.0", path = "../core" } +bp3d-os = { version = "1.0.0-rc.4.2", features = ["time"] } diff --git a/testbin/src/context.rs b/testbin/src/context.rs index 00711ef..d479604 100644 --- a/testbin/src/context.rs +++ b/testbin/src/context.rs @@ -64,7 +64,7 @@ pub fn test_context_mlua() -> Duration { let mut ctx = TestContext { value3: Vec::new() }; - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { lua.scope(|l| { let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); @@ -98,7 +98,7 @@ pub fn test_context_vm() -> Duration { let mut obj = TestContext { value3: vec![], }; - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { { let _obj = ctx.bind(&vm, &mut obj); diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index a7ea87a..ff37b27 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -82,7 +82,7 @@ pub fn test_context_mlua() -> Duration { let mut ctx = TestContext { value3: Vec::new() }; - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { lua.scope(|l| { let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); @@ -124,7 +124,7 @@ pub fn test_context_vm() -> Duration { let mut obj = TestContext { value3: vec![], }; - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { { let _obj = ctx.bind(&vm, &mut obj); diff --git a/testbin/src/main.rs b/testbin/src/main.rs index b0574f8..2c577fe 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -56,7 +56,7 @@ decl_lib_func! { fn test_vm_destructor() -> Duration { let mut vm = RootVm::new(); vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)).unwrap(); - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); @@ -77,7 +77,7 @@ fn test_vm_mlua() -> Duration { Ok(format!("Hello {} ({})", name, value)) }).unwrap(); lua.globals().set("test_c_function", f).unwrap(); - let time = std::time::Instant::now(); + let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { let res: mlua::Result = lua.load("return test_c_function('this is a test\\xFF', 0.42)").call(()); assert!(res.is_err()); From 68793882f8cb50f3ac2ec36f7845060d34d35f28 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 23:00:08 +0100 Subject: [PATCH 131/527] Updated bp3d-os to fix bug with real-time Instant --- testbin/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index 15d22aa..82d5725 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -9,4 +9,4 @@ publish = false [dependencies] mlua = { version = "0.10.3", features = ["luajit", "vendored"] } bp3d-lua = { version = "0.2.0", path = "../core" } -bp3d-os = { version = "1.0.0-rc.4.2", features = ["time"] } +bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } From 8e1d79f9c601eaba33cb629477261bceaadee6cc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 29 Mar 2025 23:09:21 +0100 Subject: [PATCH 132/527] Added support to run named lua code --- core/Cargo.toml | 2 +- core/src/vm/core.rs | 49 +++++++++++++++++++++++++++------------ core/src/vm/error.rs | 3 ++- core/src/vm/value/util.rs | 2 +- core/tests/test_vm_run.rs | 38 ++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 18 deletions(-) create mode 100644 core/tests/test_vm_run.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 9aee727..011ef78 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -18,7 +18,7 @@ log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } [build-dependencies] -bp3d-os = { version = "1.0.0-rc.4.1.1", features = ["fs"] } +bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } phf = { version = "0.11.3", features = ["macros"] } cc = "1.2.15" diff --git a/core/src/vm/core.rs b/core/src/vm/core.rs index 47985c5..4852ec2 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core.rs @@ -28,8 +28,8 @@ use std::ffi::c_int; use std::ops::{Deref, DerefMut}; -use crate::ffi::laux::{luaL_callmeta, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX}; +use crate::ffi::laux::{luaL_callmeta, luaL_loadbuffer, luaL_newstate, luaL_openlibs, luaL_traceback}; +use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX, SIGNATURE}; use crate::util::AnyStr; use crate::vm::error::{Error, RuntimeError}; use crate::vm::userdata::{core::Registry, UserData}; @@ -100,6 +100,23 @@ pub(super) unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: } } +unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int) -> crate::vm::Result<()> { + if res != ThreadStatus::Ok { + unsafe { lua_remove(vm.as_ptr(), handler_pos) }; + } + match res { + ThreadStatus::Ok => Ok(()), + ThreadStatus::ErrSyntax => { + // If we've got an error, read it and clear the stack. + let str: &str = FromLua::from_lua(vm, -1)?; + unsafe { lua_remove(vm.as_ptr(), -1) }; + Err(Error::Syntax(str.into())) + } + ThreadStatus::ErrMem => Err(Error::Memory), + _ => Err(Error::Unknown) + } +} + pub struct Vm { l: State } @@ -175,20 +192,22 @@ impl Vm { let handler_pos = push_error_handler(l); // Push the lua code. let res = code.load_code(l); - if res != ThreadStatus::Ok { - unsafe { lua_remove(l, handler_pos) }; + unsafe { handle_syntax_error(self, res, handler_pos)? }; + unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; + // Read and return the result of the function from the stack. + FromLua::from_lua(self, -(R::num_values() as i32)) + } + + pub fn run_named_code<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr, code: impl AsRef<[u8]>) -> crate::vm::Result { + let bytes = code.as_ref(); + if bytes.starts_with(SIGNATURE) { + return Err(Error::ByteCode); } - match res { - ThreadStatus::Ok => (), - ThreadStatus::ErrSyntax => { - // If we've got an error, read it and clear the stack. - let str: &str = FromLua::from_lua(self, -1)?; - unsafe { lua_remove(l, -1) }; - return Err(Error::Syntax(str.into())) - } - ThreadStatus::ErrMem => return Err(Error::Memory), - _ => return Err(Error::Unknown) - }; + let name = name.to_str()?; + let l = self.as_ptr(); + let handler_pos = push_error_handler(l); + let res = unsafe { luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()) }; + unsafe { handle_syntax_error(self, res, handler_pos)? }; unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index e44745f..4c396c8 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -89,7 +89,8 @@ simple_error! { Null => "string contains a null character", MultiValue => "only one value is supported by this API", UserData(crate::vm::userdata::Error) => "userdata: {}", - UnsupportedType(Type) => "unsupported lua type: {:?}" + UnsupportedType(Type) => "unsupported lua type: {:?}", + ByteCode => "attempt to run raw lua byte code" } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 555e697..1e471a2 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -34,7 +34,7 @@ use crate::vm::Vm; #[inline(always)] pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { let ty = unsafe { crate::ffi::lua::lua_type(vm.as_ptr(), index) }; - if ty == expected { + if ty == expected { //FIXME: likely branch Ok(()) } else { Err(Error::Type(TypeError { diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs new file mode 100644 index 0000000..60d8e19 --- /dev/null +++ b/core/tests/test_vm_run.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; + +#[test] +fn test_vm_run() { + let vm = RootVm::new(); + let res = vm.run_named_code::<()>(c"test", b"return 1 + b"); + assert!(res.is_err()); + let err = res.unwrap_err().into_runtime().unwrap(); + assert_eq!(err.msg(), "[string \"test\"]:1: attempt to perform arithmetic on global 'b' (a nil value)") +} From f4c17516691eabacc2d1879206fc2b7db7fae129 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 30 Mar 2025 16:16:58 +0200 Subject: [PATCH 133/527] Added lua_load_no_bc patch --- core/build.rs | 28 ++++++-------- patch/lua_load_no_bc.patch | 77 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+), 16 deletions(-) create mode 100644 patch/lua_load_no_bc.patch diff --git a/core/build.rs b/core/build.rs index aaf887a..2717b86 100644 --- a/core/build.rs +++ b/core/build.rs @@ -83,6 +83,13 @@ fn build_luajit(build_dir: &Path) { } } +fn apply_patch(path: &Path, name: &str) { + let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch").join(name).as_os_str()]); + if !result.success() { + panic!("Failed to patch LuaJIT"); + } +} + fn main() { // Rerun this script if any of the patch files changed. println!("cargo:rerun-if-changed=build.rs"); @@ -97,22 +104,11 @@ fn main() { // Apply patches to LuaJIT source code. let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lib_init.patch").as_os_str()]); - if !result.success() { - panic!("Failed to patch LuaJIT"); - } - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lj_disable_jit.patch").as_os_str()]); - if !result.success() { - panic!("Failed to patch LuaJIT"); - } - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/disable_lua_load.patch").as_os_str()]); - if !result.success() { - panic!("Failed to patch LuaJIT"); - } - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch/lua_ext.patch").as_os_str()]); - if !result.success() { - panic!("Failed to patch LuaJIT"); - } + apply_patch(&path, "lib_init.patch"); // Disable unsafe/un-sandboxed libs. + apply_patch(&path, "lj_disable_jit.patch"); // Disable global JIT state changes from Lua code. + apply_patch(&path, "disable_lua_load.patch"); // Disable loadstring, dostring, etc from base lib. + apply_patch(&path, "lua_ext.patch"); // Ext library such as lua_ext_tab_len, etc. + apply_patch(&path, "lua_load_no_bc.patch"); // Treat all inputs as strings (no bytecode allowed). // Copy the source directory to the build directory. println!("{}", out_path.display()); diff --git a/patch/lua_load_no_bc.patch b/patch/lua_load_no_bc.patch new file mode 100644 index 0000000..635638b --- /dev/null +++ b/patch/lua_load_no_bc.patch @@ -0,0 +1,77 @@ +diff --git a/src/lj_lex.c b/src/lj_lex.c +index a986aeb8..a8427739 100644 +--- a/src/lj_lex.c ++++ b/src/lj_lex.c +@@ -397,7 +397,6 @@ static LexToken lex_scan(LexState *ls, TValue *tv) + /* Setup lexer state. */ + int lj_lex_setup(lua_State *L, LexState *ls) + { +- int header = 0; + ls->L = L; + ls->fs = NULL; + ls->pe = ls->p = NULL; +@@ -413,33 +412,6 @@ int lj_lex_setup(lua_State *L, LexState *ls) + ls->endmark = 0; + ls->fr2 = LJ_FR2; /* Generate native bytecode by default. */ + lex_next(ls); /* Read-ahead first char. */ +- if (ls->c == 0xef && ls->p + 2 <= ls->pe && (uint8_t)ls->p[0] == 0xbb && +- (uint8_t)ls->p[1] == 0xbf) { /* Skip UTF-8 BOM (if buffered). */ +- ls->p += 2; +- lex_next(ls); +- header = 1; +- } +- if (ls->c == '#') { /* Skip POSIX #! header line. */ +- do { +- lex_next(ls); +- if (ls->c == LEX_EOF) return 0; +- } while (!lex_iseol(ls)); +- lex_newline(ls); +- header = 1; +- } +- if (ls->c == LUA_SIGNATURE[0]) { /* Bytecode dump. */ +- if (header) { +- /* +- ** Loading bytecode with an extra header is disabled for security +- ** reasons. This may circumvent the usual check for bytecode vs. +- ** Lua code by looking at the first char. Since this is a potential +- ** security violation no attempt is made to echo the chunkname either. +- */ +- setstrV(L, L->top++, lj_err_str(L, LJ_ERR_BCBAD)); +- lj_err_throw(L, LUA_ERRSYNTAX); +- } +- return 1; +- } + return 0; + } + +diff --git a/src/lj_load.c b/src/lj_load.c +index 828bf8ae..7b7f1478 100644 +--- a/src/lj_load.c ++++ b/src/lj_load.c +@@ -30,24 +30,10 @@ static TValue *cpparser(lua_State *L, lua_CFunction dummy, void *ud) + LexState *ls = (LexState *)ud; + GCproto *pt; + GCfunc *fn; +- int bc; + UNUSED(dummy); + cframe_errfunc(L->cframe) = -1; /* Inherit error function. */ +- bc = lj_lex_setup(L, ls); +- if (ls->mode) { +- int xmode = 1; +- const char *mode = ls->mode; +- char c; +- while ((c = *mode++)) { +- if (c == (bc ? 'b' : 't')) xmode = 0; +- if (c == (LJ_FR2 ? 'W' : 'X')) ls->fr2 = !LJ_FR2; +- } +- if (xmode) { +- setstrV(L, L->top++, lj_err_str(L, LJ_ERR_XMODE)); +- lj_err_throw(L, LUA_ERRSYNTAX); +- } +- } +- pt = bc ? lj_bcread(ls) : lj_parse(ls); ++ (void)lj_lex_setup(L, ls); ++ pt = lj_parse(ls); + if (ls->fr2 == LJ_FR2) { + fn = lj_func_newL_empty(L, pt, tabref(L->env)); + /* Don't combine above/below into one statement. */ From 5bcdc73e00593b44f61c9f2edc9d1642c0154e61 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 30 Mar 2025 16:46:58 +0200 Subject: [PATCH 134/527] Large refactoring of the core module --- core/src/ffi/lua.rs | 3 + core/src/vm/core/interface.rs | 37 +++++++ core/src/vm/core/load.rs | 51 +++++++++ core/src/vm/core/mod.rs | 35 ++++++ core/src/vm/core/util.rs | 158 ++++++++++++++++++++++++++++ core/src/vm/{core.rs => core/vm.rs} | 115 ++++---------------- core/src/vm/error.rs | 3 +- core/src/vm/registry/types.rs | 4 +- core/src/vm/table/core.rs | 6 +- core/src/vm/thread.rs | 5 +- core/src/vm/util.rs | 39 +++---- core/src/vm/value/function.rs | 4 +- 12 files changed, 329 insertions(+), 131 deletions(-) create mode 100644 core/src/vm/core/interface.rs create mode 100644 core/src/vm/core/load.rs create mode 100644 core/src/vm/core/mod.rs create mode 100644 core/src/vm/core/util.rs rename core/src/vm/{core.rs => core/vm.rs} (57%) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index ccbd6d2..66e8da2 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -31,6 +31,9 @@ use std::ffi::{c_char, c_double, c_int, c_void}; /* mark for precompiled code (`Lua') */ pub const SIGNATURE: &[u8] = b"\033Lua"; +/// The maximum size of a lua chunkname. This is used by Vm::run_named_code for optimization. +pub const IDSIZE: usize = 60; + pub const REGISTRYINDEX: c_int = -10000; pub const ENVIRONINDEX: c_int = -10001; pub const GLOBALSINDEX: c_int = -10002; diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs new file mode 100644 index 0000000..eb1b36b --- /dev/null +++ b/core/src/vm/core/interface.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{State, ThreadStatus}; + +pub trait LoadString { + fn load_string(&self, l: State) -> ThreadStatus; +} + +pub trait Load { + +} diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs new file mode 100644 index 0000000..10e8098 --- /dev/null +++ b/core/src/vm/core/load.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::{CStr, CString}; +use crate::ffi::laux::luaL_loadstring; +use crate::ffi::lua::{State, ThreadStatus}; +use crate::vm::LoadString; + +impl LoadString for &CStr { + #[inline(always)] + fn load_string(&self, l: State) -> ThreadStatus { + unsafe { luaL_loadstring(l, self.as_ptr()) } + } +} + +impl LoadString for &str { + fn load_string(&self, l: State) -> ThreadStatus { + let s = CString::new(*self); + match s { + Ok(v) => { + (&*v).load_string(l) + } + Err(_) => ThreadStatus::ErrSyntax + } + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs new file mode 100644 index 0000000..2994e4a --- /dev/null +++ b/core/src/vm/core/mod.rs @@ -0,0 +1,35 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +pub mod util; +mod vm; +mod load; + +pub use vm::{Vm, RootVm}; +pub use interface::*; diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs new file mode 100644 index 0000000..d1bf49e --- /dev/null +++ b/core/src/vm/core/util.rs @@ -0,0 +1,158 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::c_int; +use crate::ffi::laux::{luaL_callmeta, luaL_traceback}; +use crate::ffi::lua::{lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_tolstring, lua_type, State, ThreadStatus, Type}; +use crate::vm::error::{Error, RuntimeError}; +use crate::vm::value::FromLua; +use crate::vm::Vm; + +const TRACEBACK_NONE: &[u8] = b"\n"; +extern "C-unwind" fn error_handler(l: State) -> c_int { + unsafe { + let ty = lua_type(l, 1); + if ty != Type::String { + // Non-string error object? Try metamethod. + if (ty == Type::Nil || ty == Type::None) || + luaL_callmeta(l, 1, c"__tostring".as_ptr()) != 1 || + lua_isstring(l, -1) != 1 { + // Object does not turn into a string remove it alongside the return value of + // __tostring. + lua_remove(l, 1); + lua_remove(l, 1); + // Push a place-holder string to avoid the rust code from crashing because the stack + // would be empty otherwise. + lua_pushlstring(l, TRACEBACK_NONE.as_ptr() as _, TRACEBACK_NONE.len()); + return 1; + } + // Remove the object from the stack so that error message becomes now index 1. + lua_remove(l, 1); + } + // Call traceback with the actual error message as a string which should push onto the stack + // the stacktrace as a string. + luaL_traceback(l, l, lua_tolstring(l, 1, std::ptr::null_mut()), 1); + // Remove the original error message string from the stack. + lua_remove(l, 1); + 1 + } +} + +/// Pushes the error handler on the Lua stack and return its absolute stack index. +/// +/// The error handler replaces the error message by a full traceback using [luaL_traceback]. +/// +/// # Arguments +/// +/// * `l`: the lua State on which to push the error handler function. +/// +/// returns: i32 +/// +/// # Safety +/// +/// You must ensure that the error handler function is NEVER called outside the context of an error. +#[inline(always)] +pub unsafe fn push_error_handler(l: State) -> c_int { + unsafe { + lua_pushcclosure(l, error_handler, 0); + lua_gettop(l) + } +} + +/// Calls the lua function at the top of the stack in a protected environment. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] instance on which to call the function. +/// * `nargs`: the number of arguments push on top of the stack. +/// * `nreturns`: the number of returns expected from the function call. +/// * `handler_pos`: the absolute position of the handler on the stack. +/// +/// returns: Result<(), Error> +/// +/// # Safety +/// +/// This function shall not be used without [push_error_handler]. This is also UB if `nargs` does +/// not match the count of arguments push on top of the stack. If the error handler is not the first +/// item on the stack, before function and function arguments, this is UB. +pub unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { + let l = vm.as_ptr(); + unsafe { + // Call the function created by load_code. + let res = lua_pcall(l, nargs, nreturns, handler_pos); + // At this point the stack should no longer have the function but still has the error + // handler and R::num_values results. + // First remove error handler as we no longer need it. + lua_remove(l, handler_pos); + match res { + ThreadStatus::Ok => Ok(()), + ThreadStatus::ErrRun => { + // We've got a runtime error when executing the function so read the full stack + // trace produced by luaL_traceback and remove it from the stack. + let full_traceback: &str = FromLua::from_lua(vm, -1)?; + lua_remove(l, -1); + Err(Error::Runtime(RuntimeError::new(full_traceback.into()))) + } + ThreadStatus::ErrMem => Err(Error::Memory), + ThreadStatus::ErrErr => Err(Error::Error), + _ => Err(Error::Unknown) + } + } +} + +/// Handles a syntax error. A syntax error is an error which may occur as part of a lua_load family +/// of functions. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] instance which has produced the syntax error. +/// * `res`: the result of the load family of function. +/// * `handler_pos`: the absolute position of the error handler on the stack. +/// +/// returns: Result<(), Error> +/// +/// # Safety +/// +/// Calling this function with a `handler_pos` which does not correspond to the actual error handler +/// C function is UB. This is also UB if the res is not the result of a load function. +pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int) -> crate::vm::Result<()> { + if res != ThreadStatus::Ok { + unsafe { lua_remove(vm.as_ptr(), handler_pos) }; + } + match res { + ThreadStatus::Ok => Ok(()), + ThreadStatus::ErrSyntax => { + // If we've got an error, read it and clear the stack. + let str: &str = FromLua::from_lua(vm, -1)?; + unsafe { lua_remove(vm.as_ptr(), -1) }; + Err(Error::Syntax(str.into())) + } + ThreadStatus::ErrMem => Err(Error::Memory), + _ => Err(Error::Unknown) + } +} diff --git a/core/src/vm/core.rs b/core/src/vm/core/vm.rs similarity index 57% rename from core/src/vm/core.rs rename to core/src/vm/core/vm.rs index 4852ec2..0e08fcf 100644 --- a/core/src/vm/core.rs +++ b/core/src/vm/core/vm.rs @@ -26,97 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_int; use std::ops::{Deref, DerefMut}; -use crate::ffi::laux::{luaL_callmeta, luaL_loadbuffer, luaL_newstate, luaL_openlibs, luaL_traceback}; -use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_pushnil, lua_remove, lua_setfield, lua_settop, lua_tolstring, lua_type, State, ThreadStatus, Type, GLOBALSINDEX, REGISTRYINDEX, SIGNATURE}; +use crate::ffi::laux::{luaL_loadbuffer, luaL_newstate, luaL_openlibs}; +use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; -use crate::vm::error::{Error, RuntimeError}; -use crate::vm::userdata::{core::Registry, UserData}; -use crate::vm::util::LoadCode; +use crate::vm::core::LoadString; +use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; +use crate::vm::error::Error; +use crate::vm::userdata::core::Registry; +use crate::vm::userdata::UserData; use crate::vm::value::{FromLua, IntoLua}; -const TRACEBACK_NONE: &[u8] = b"\n"; -extern "C-unwind" fn error_handler(l: State) -> c_int { - unsafe { - let ty = lua_type(l, 1); - if ty != Type::String { - // Non-string error object? Try metamethod. - if (ty == Type::Nil || ty == Type::None) || - luaL_callmeta(l, 1, c"__tostring".as_ptr()) != 1 || - lua_isstring(l, -1) != 1 { - // Object does not turn into a string remove it alongside the return value of - // __tostring. - lua_remove(l, 1); - lua_remove(l, 1); - // Push a place-holder string to avoid the rust code from crashing because the stack - // would be empty otherwise. - lua_pushlstring(l, TRACEBACK_NONE.as_ptr() as _, TRACEBACK_NONE.len()); - return 1; - } - // Remove the object from the stack so that error message becomes now index 1. - lua_remove(l, 1); - } - // Call traceback with the actual error message as a string which should push onto the stack - // the stacktrace as a string. - luaL_traceback(l, l, lua_tolstring(l, 1, std::ptr::null_mut()), 1); - // Remove the original error message string from the stack. - lua_remove(l, 1); - 1 - } -} - -#[inline(always)] -pub(super) fn push_error_handler(l: State) -> c_int { - unsafe { - lua_pushcclosure(l, error_handler, 0); - lua_gettop(l) - } -} - -/// This function is highly unsafe it may crash at any time. -pub(super) unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { - let l = vm.as_ptr(); - unsafe { - // Call the function created by load_code. - let res = lua_pcall(l, nargs, nreturns, handler_pos); - // At this point the stack should no longer have the function but still has the error - // handler and R::num_values results. - // First remove error handler as we no longer need it. - lua_remove(l, handler_pos); - match res { - ThreadStatus::Ok => Ok(()), - ThreadStatus::ErrRun => { - // We've got a runtime error when executing the function so read the full stack - // trace produced by luaL_traceback and remove it from the stack. - let full_traceback: &str = FromLua::from_lua(vm, -1)?; - lua_remove(l, -1); - Err(Error::Runtime(RuntimeError::new(full_traceback.into()))) - } - ThreadStatus::ErrMem => Err(Error::Memory), - ThreadStatus::ErrErr => Err(Error::Error), - _ => Err(Error::Unknown) - } - } -} - -unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int) -> crate::vm::Result<()> { - if res != ThreadStatus::Ok { - unsafe { lua_remove(vm.as_ptr(), handler_pos) }; - } - match res { - ThreadStatus::Ok => Ok(()), - ThreadStatus::ErrSyntax => { - // If we've got an error, read it and clear the stack. - let str: &str = FromLua::from_lua(vm, -1)?; - unsafe { lua_remove(vm.as_ptr(), -1) }; - Err(Error::Syntax(str.into())) - } - ThreadStatus::ErrMem => Err(Error::Memory), - _ => Err(Error::Unknown) - } -} - pub struct Vm { l: State } @@ -186,26 +106,33 @@ impl Vm { R::from_lua(self, -1) } - pub fn run_code<'a, R: FromLua<'a>>(&'a self, code: impl LoadCode) -> crate::vm::Result { + pub fn run_code<'a, R: FromLua<'a>>(&'a self, code: impl LoadString) -> crate::vm::Result { let l = self.as_ptr(); // Push error handler and the get the stack position of it. - let handler_pos = push_error_handler(l); + let handler_pos = unsafe { push_error_handler(l) }; // Push the lua code. - let res = code.load_code(l); + let res = code.load_string(l); unsafe { handle_syntax_error(self, res, handler_pos)? }; unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } + pub fn run<'a, R: FromLua<'a>>(&'a self, ) -> crate::vm::Result { + let l = self.as_ptr(); + let handler_pos = unsafe { push_error_handler(l) }; + //let res = unsafe { luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()) }; + //unsafe { handle_syntax_error(self, res, handler_pos)? }; + //unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; + // Read and return the result of the function from the stack. + FromLua::from_lua(self, -(R::num_values() as i32)) + } + pub fn run_named_code<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr, code: impl AsRef<[u8]>) -> crate::vm::Result { let bytes = code.as_ref(); - if bytes.starts_with(SIGNATURE) { - return Err(Error::ByteCode); - } let name = name.to_str()?; let l = self.as_ptr(); - let handler_pos = push_error_handler(l); + let handler_pos = unsafe { push_error_handler(l) }; let res = unsafe { luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()) }; unsafe { handle_syntax_error(self, res, handler_pos)? }; unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index 4c396c8..e44745f 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -89,8 +89,7 @@ simple_error! { Null => "string contains a null character", MultiValue => "only one value is supported by this API", UserData(crate::vm::userdata::Error) => "userdata: {}", - UnsupportedType(Type) => "unsupported lua type: {:?}", - ByteCode => "attempt to run raw lua byte code" + UnsupportedType(Type) => "unsupported lua type: {:?}" } } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index e1742fd..830aa6f 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::RegistryValue; use crate::vm::value::{FromLua, IntoLua}; @@ -55,7 +55,7 @@ impl RegistryValue for LuaFunction { impl RegistryKey { pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { - let pos = push_error_handler(vm.as_ptr()); + let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.as_raw().push(vm) }; let num_values = value.into_lua(vm); unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index cf2e8e5..336a905 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -29,7 +29,7 @@ use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue}; use crate::util::AnyStr; -use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::table::Scope; @@ -98,7 +98,7 @@ impl<'a> Table<'a> { } pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); + let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; @@ -106,7 +106,7 @@ impl<'a> Table<'a> { } pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); + let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; let num_values = value.into_lua(self.vm); diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 5810dca..10c2907 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -32,7 +32,8 @@ use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadSt use crate::util::SimpleDrop; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::FromParam; -use crate::vm::util::{LoadCode, LuaType}; +use crate::vm::util::LuaType; +use crate::vm::core::LoadString; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_type_equals; use crate::vm::Vm; @@ -49,7 +50,7 @@ pub struct Thread<'a> { impl<'a> Thread<'a> { #[inline(always)] - pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadCode) -> crate::vm::Result { + pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadString) -> crate::vm::Result { self.vm.run_code(code) } diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index deb4709..d22898c 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -27,9 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::error::Error; -use std::ffi::{CStr, CString}; -use crate::ffi::laux::luaL_loadstring; -use crate::ffi::lua::{lua_error, lua_pushlstring, State, ThreadStatus}; +use crate::ffi::lua::{lua_error, lua_pushlstring, State}; #[derive(Debug, PartialEq, Eq)] pub enum TypeName { @@ -52,6 +50,18 @@ impl LuaType for Option { } } +/// Converts a Rust error to a Lua error. This function does not return as it unwinds using luajit. +/// +/// # Arguments +/// +/// * `l`: the lua State on which to raise the lua exception. +/// * `error`: the Rust error to be converted. +/// +/// returns: ! +/// +/// # Safety +/// +/// It is UB to call this function outside a lua [CFunction](crate::ffi::lua::CFunction). pub unsafe fn lua_rust_error(l: State, error: E) -> ! { // At this point the function is assumed to be a non-POF (error and String). let s = format!("rust error: {}", error); @@ -65,26 +75,3 @@ pub unsafe fn lua_rust_error(l: State, error: E) -> ! { // If this is reached, then lua_error has silently failed. std::unreachable!() } - -pub trait LoadCode { - fn load_code(&self, l: State) -> ThreadStatus; -} - -impl LoadCode for &CStr { - #[inline(always)] - fn load_code(&self, l: State) -> ThreadStatus { - unsafe { luaL_loadstring(l, self.as_ptr()) } - } -} - -impl LoadCode for &str { - fn load_code(&self, l: State) -> ThreadStatus { - let s = CString::new(*self); - match s { - Ok(v) => { - (&*v).load_code(l) - } - Err(_) => ThreadStatus::ErrSyntax - } - } -} diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 6e2585f..207e3e4 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_pushvalue, Type}; -use crate::vm::core::{pcall, push_error_handler}; +use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Register; use crate::vm::value::{FromLua, IntoLua}; @@ -41,7 +41,7 @@ pub struct LuaFunction<'a> { impl<'a> LuaFunction<'a> { pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { - let pos = push_error_handler(self.vm.as_ptr()); + let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { lua_pushvalue(self.vm.as_ptr(), self.index); } let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; From db059143db008dfe2b0b9edb3055032d4888fe75 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 30 Mar 2025 17:04:36 +0200 Subject: [PATCH 135/527] Tweaked more inline attributes to gain reduce slightly running time --- core/src/vm/core/vm.rs | 32 ++++++++++++++++---------------- core/src/vm/value/core.rs | 4 ++++ core/src/vm/value/interface.rs | 1 + 3 files changed, 21 insertions(+), 16 deletions(-) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 0e08fcf..9521c59 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -93,27 +93,25 @@ impl Vm { pub fn set_global(&self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { value.into_lua(self); - unsafe { - lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); - } + unsafe { lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } Ok(()) } pub fn get_global<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr) -> crate::vm::Result { - unsafe { - lua_getfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); - } + unsafe { lua_getfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } R::from_lua(self, -1) } pub fn run_code<'a, R: FromLua<'a>>(&'a self, code: impl LoadString) -> crate::vm::Result { let l = self.as_ptr(); - // Push error handler and the get the stack position of it. - let handler_pos = unsafe { push_error_handler(l) }; - // Push the lua code. - let res = code.load_string(l); - unsafe { handle_syntax_error(self, res, handler_pos)? }; - unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; + unsafe { + // Push error handler and the get the stack position of it. + let handler_pos = push_error_handler(l); + // Push the lua code. + let res = code.load_string(l); + handle_syntax_error(self, res, handler_pos)?; + pcall(self, 0, R::num_values() as _, handler_pos)?; + } // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } @@ -132,10 +130,12 @@ impl Vm { let bytes = code.as_ref(); let name = name.to_str()?; let l = self.as_ptr(); - let handler_pos = unsafe { push_error_handler(l) }; - let res = unsafe { luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()) }; - unsafe { handle_syntax_error(self, res, handler_pos)? }; - unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; + unsafe { + let handler_pos = push_error_handler(l); + let res = luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()); + handle_syntax_error(self, res, handler_pos)?; + pcall(self, 0, R::num_values() as _, handler_pos)?; + } // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index ff1f85a..5a81958 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -133,14 +133,17 @@ unsafe impl IntoLua for T { } impl FromLua<'_> for () { + #[inline(always)] unsafe fn from_lua_unchecked(_: &'_ Vm, _: i32) -> Self { () } + #[inline(always)] fn from_lua(_vm: &Vm, _: i32) -> crate::vm::Result<()> { Ok(()) } + #[inline(always)] fn num_values() -> u16 { 0 } @@ -172,6 +175,7 @@ macro_rules! count_tts { macro_rules! impl_from_lua_tuple { ($($name: ident: $name2: ident),*) => { impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { + #[inline(always)] fn num_values() -> u16 { count_tts!($($name)*) } diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index b2d0cab..91e4742 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -57,6 +57,7 @@ pub trait FromLua<'a>: Sized { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result; /// Returns the number of values to be expected on the lua stack, after reading this value. + #[inline(always)] fn num_values() -> u16 { 1 } From da682b8fdb2977e4979e77cc20a44692f8536d55 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 31 Mar 2025 21:56:33 +0200 Subject: [PATCH 136/527] Added backtrace test --- core/tests/test_vm_backtrace.rs | 70 +++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 core/tests/test_vm_backtrace.rs diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs new file mode 100644 index 0000000..48ca2fe --- /dev/null +++ b/core/tests/test_vm_backtrace.rs @@ -0,0 +1,70 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_util::simple_error; +use bp3d_lua::decl_lib_func; +use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::function::LuaFunction; + +simple_error! { + pub Error { + Useless => "useless function called" + } +} + +decl_lib_func! { + fn error_func() -> Result<(), Error> { + Err(Error::Useless) + } +} + +#[test] +fn test_vm_backtrace() { + let vm = RootVm::new(); + let top = vm.top(); + vm.set_global(c"error_func", RFunction::wrap(error_func)).unwrap(); + vm.run_code::<()>(c" + local function raise() + error_func() + end + + local function a() + raise() + end + + function main() + a() + end + ").unwrap(); + let func: LuaFunction = vm.get_global(c"main").unwrap(); + let err = func.call::<()>(()).unwrap_err().into_runtime().unwrap(); + assert_eq!(err.msg(), "rust error: useless function called"); + assert_eq!(err.traceback(), "rust error: useless function called\nstack traceback:\n\t[C]: in function 'error_func'\n\t[string \"...\"]:3: in function 'raise'\n\t[string \"...\"]:7: in function 'a'\n\t[string \"...\"]:11: in function <[string \"...\"]:10>"); + assert_eq!(vm.top(), top + 1); +} From ef58f9f62ace537c83c2ef0460724130c557b07b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 31 Mar 2025 21:57:32 +0200 Subject: [PATCH 137/527] Renamed traceback to backtrace --- core/src/vm/error.rs | 2 +- core/tests/test_vm_backtrace.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index e44745f..fcfb1cb 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -66,7 +66,7 @@ impl RuntimeError { &self.traceback[self.index + 1..] } - pub fn traceback(&self) -> &str { + pub fn backtrace(&self) -> &str { &self.traceback } } diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index 48ca2fe..4036f63 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -65,6 +65,6 @@ fn test_vm_backtrace() { let func: LuaFunction = vm.get_global(c"main").unwrap(); let err = func.call::<()>(()).unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: useless function called"); - assert_eq!(err.traceback(), "rust error: useless function called\nstack traceback:\n\t[C]: in function 'error_func'\n\t[string \"...\"]:3: in function 'raise'\n\t[string \"...\"]:7: in function 'a'\n\t[string \"...\"]:11: in function <[string \"...\"]:10>"); + assert_eq!(err.backtrace(), "rust error: useless function called\nstack traceback:\n\t[C]: in function 'error_func'\n\t[string \"...\"]:3: in function 'raise'\n\t[string \"...\"]:7: in function 'a'\n\t[string \"...\"]:11: in function <[string \"...\"]:10>"); assert_eq!(vm.top(), top + 1); } From 31d1d4899989d5f3d0108be7783ab8e905ed0da7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 31 Mar 2025 22:54:26 +0200 Subject: [PATCH 138/527] Added run function with associated test --- core/Cargo.toml | 2 +- core/src/vm/core/interface.rs | 2 +- core/src/vm/core/load.rs | 37 +++++++++++++++++++++++++++++++-- core/src/vm/core/mod.rs | 2 +- core/src/vm/core/util.rs | 39 ++++++++++++++++++++++++++++++++++- core/src/vm/core/vm.rs | 25 ++++++---------------- core/tests/test_vm_run.rs | 19 ++++++++++++----- 7 files changed, 96 insertions(+), 30 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 011ef78..da7e927 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,7 +13,7 @@ categories = ["graphics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bp3d-util = { version = "1.4.0", features = ["simple-error"] } +bp3d-util = { version = "1.4.2", features = ["simple-error", "format"] } log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs index eb1b36b..dce2ed9 100644 --- a/core/src/vm/core/interface.rs +++ b/core/src/vm/core/interface.rs @@ -33,5 +33,5 @@ pub trait LoadString { } pub trait Load { - + fn load(&self, l: State) -> crate::vm::Result; } diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs index 10e8098..f6e42e5 100644 --- a/core/src/vm/core/load.rs +++ b/core/src/vm/core/load.rs @@ -26,10 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::Write; use std::ffi::{CStr, CString}; -use crate::ffi::laux::luaL_loadstring; +use crate::ffi::laux::{luaL_loadbuffer, luaL_loadstring}; use crate::ffi::lua::{State, ThreadStatus}; -use crate::vm::LoadString; +use crate::vm::{Load, LoadString}; +use crate::vm::core::util::ChunkNameBuilder; impl LoadString for &CStr { #[inline(always)] @@ -49,3 +51,34 @@ impl LoadString for &str { } } } + +pub struct Code<'a> { + name: &'a str, + code: &'a [u8] +} + +impl<'a> Code<'a> { + pub fn new(name: &'a str, code: &'a [u8]) -> Self { + Self { + name, + code + } + } +} + +impl Load for Code<'_> { + fn load(&self, l: State) -> crate::vm::Result { + let mut builder = ChunkNameBuilder::new(); + let _ = write!(&mut builder, "={}", self.name); + let name = builder.build(); + unsafe { + Ok(luaL_loadbuffer(l, self.code.as_ptr() as _, self.code.len(), name.cstr().as_ptr())) + } + } +} + +impl Load for T { + fn load(&self, l: State) -> crate::vm::Result { + Ok(self.load_string(l)) + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 2994e4a..60eb7f7 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -29,7 +29,7 @@ mod interface; pub mod util; mod vm; -mod load; +pub mod load; pub use vm::{Vm, RootVm}; pub use interface::*; diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index d1bf49e..d1649bb 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -26,12 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_int; +use std::ffi::{c_int, CStr}; +use bp3d_util::format::FixedBufStr; use crate::ffi::laux::{luaL_callmeta, luaL_traceback}; use crate::ffi::lua::{lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_tolstring, lua_type, State, ThreadStatus, Type}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::value::FromLua; use crate::vm::Vm; +use crate::ffi::lua::IDSIZE; const TRACEBACK_NONE: &[u8] = b"\n"; extern "C-unwind" fn error_handler(l: State) -> c_int { @@ -156,3 +158,38 @@ pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int _ => Err(Error::Unknown) } } + +pub struct ChunkNameBuilder { + inner: FixedBufStr<59> +} + +impl ChunkNameBuilder { + pub fn new() -> Self { + Self { inner: FixedBufStr::new() } + } + + pub fn build(self) -> ChunkName { + let mut buf = FixedBufStr::::new(); + unsafe { + buf.write(self.inner.str().as_bytes()); + buf.write(b"\0"); + } + ChunkName { inner: buf } + } +} + +impl std::fmt::Write for ChunkNameBuilder { + fn write_str(&mut self, s: &str) -> Result<(), std::fmt::Error> { + self.inner.write_str(s) + } +} + +pub struct ChunkName { + inner: FixedBufStr<60> +} + +impl ChunkName { + pub fn cstr(&self) -> &CStr { + unsafe { CStr::from_bytes_with_nul_unchecked(self.inner.str().as_bytes()) } + } +} diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 9521c59..ea8b4f5 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -27,12 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::ops::{Deref, DerefMut}; -use crate::ffi::laux::{luaL_loadbuffer, luaL_newstate, luaL_openlibs}; +use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; use crate::vm::core::LoadString; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::error::Error; +use crate::vm::Load; use crate::vm::userdata::core::Registry; use crate::vm::userdata::UserData; use crate::vm::value::{FromLua, IntoLua}; @@ -116,26 +117,12 @@ impl Vm { FromLua::from_lua(self, -(R::num_values() as i32)) } - pub fn run<'a, R: FromLua<'a>>(&'a self, ) -> crate::vm::Result { + pub fn run<'a, R: FromLua<'a>>(&'a self, obj: impl Load) -> crate::vm::Result { let l = self.as_ptr(); let handler_pos = unsafe { push_error_handler(l) }; - //let res = unsafe { luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()) }; - //unsafe { handle_syntax_error(self, res, handler_pos)? }; - //unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; - // Read and return the result of the function from the stack. - FromLua::from_lua(self, -(R::num_values() as i32)) - } - - pub fn run_named_code<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr, code: impl AsRef<[u8]>) -> crate::vm::Result { - let bytes = code.as_ref(); - let name = name.to_str()?; - let l = self.as_ptr(); - unsafe { - let handler_pos = push_error_handler(l); - let res = luaL_loadbuffer(l, bytes.as_ptr() as _, bytes.len(), name.as_ptr()); - handle_syntax_error(self, res, handler_pos)?; - pcall(self, 0, R::num_values() as _, handler_pos)?; - } + let res = obj.load(l)?; + unsafe { handle_syntax_error(self, res, handler_pos)? }; + unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 60d8e19..87edef2 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -26,13 +26,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::load::Code; +use bp3d_lua::vm::{Load, RootVm, Vm}; + +fn run_assert_err(vm: &Vm, obj: impl Load, err_msg: &str) { + let res = vm.run::<()>(obj); + assert!(res.is_err()); + let err = res.unwrap_err().into_runtime().unwrap(); + assert_eq!(err.msg(), err_msg); +} #[test] fn test_vm_run() { let vm = RootVm::new(); - let res = vm.run_named_code::<()>(c"test", b"return 1 + b"); - assert!(res.is_err()); - let err = res.unwrap_err().into_runtime().unwrap(); - assert_eq!(err.msg(), "[string \"test\"]:1: attempt to perform arithmetic on global 'b' (a nil value)") + let top = vm.top(); + run_assert_err(&vm, Code::new("test", b"return 1 + b"), "test:1: attempt to perform arithmetic on global 'b' (a nil value)"); + run_assert_err(&vm, c"return 1 + b", "[string \"return 1 + b\"]:1: attempt to perform arithmetic on global 'b' (a nil value)"); + run_assert_err(&vm, Code::new("this is an amazingly long text which should get truncated我", b"return 1 + b"), "this is an amazingly long text which should get truncated:1: attempt to perform arithmetic on global 'b' (a nil value)"); + assert_eq!(vm.top(), top); } From 2cbec6e720514ae3d836e9b27513188056a7cc0d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 31 Mar 2025 23:33:43 +0200 Subject: [PATCH 139/527] Added load_custom function with associated test and refactored core module --- core/src/ffi/lua.rs | 2 +- core/src/vm/core/interface.rs | 2 +- core/src/vm/core/load.rs | 49 ++++++++++++++++++++++++++++------- core/src/vm/core/util.rs | 6 +++++ core/src/vm/core/vm.rs | 5 ++-- core/src/vm/error.rs | 3 ++- core/src/vm/mod.rs | 4 +-- core/tests/test_vm_run.rs | 28 ++++++++++++++++++-- 8 files changed, 79 insertions(+), 20 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 66e8da2..6b9c453 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -59,7 +59,7 @@ pub struct State(*mut c_void); pub type CFunction = extern "C-unwind" fn(l: State) -> i32; -pub type Reader = extern "C" fn(l: State, ud: *mut c_void, sz: usize) -> *const c_char; +pub type Reader = extern "C-unwind" fn(l: State, ud: *mut c_void, sz: *mut usize) -> *const c_char; pub type Writer = extern "C" fn(l: State, p: *const c_void, sz: usize, ud: *mut c_void) -> c_int; diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs index dce2ed9..4f2a236 100644 --- a/core/src/vm/core/interface.rs +++ b/core/src/vm/core/interface.rs @@ -33,5 +33,5 @@ pub trait LoadString { } pub trait Load { - fn load(&self, l: State) -> crate::vm::Result; + fn load(&self, l: State) -> ThreadStatus; } diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs index f6e42e5..946f3dc 100644 --- a/core/src/vm/core/load.rs +++ b/core/src/vm/core/load.rs @@ -27,11 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::fmt::Write; -use std::ffi::{CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString}; use crate::ffi::laux::{luaL_loadbuffer, luaL_loadstring}; -use crate::ffi::lua::{State, ThreadStatus}; -use crate::vm::{Load, LoadString}; -use crate::vm::core::util::ChunkNameBuilder; +use crate::ffi::lua::{lua_load, State, ThreadStatus}; +use crate::vm::core::{Load, LoadString}; +use crate::vm::core::util::{ChunkName, ChunkNameBuilder}; +use crate::vm::util::lua_rust_error; impl LoadString for &CStr { #[inline(always)] @@ -67,18 +68,46 @@ impl<'a> Code<'a> { } impl Load for Code<'_> { - fn load(&self, l: State) -> crate::vm::Result { + fn load(&self, l: State) -> ThreadStatus { let mut builder = ChunkNameBuilder::new(); let _ = write!(&mut builder, "={}", self.name); let name = builder.build(); - unsafe { - Ok(luaL_loadbuffer(l, self.code.as_ptr() as _, self.code.len(), name.cstr().as_ptr())) - } + unsafe { luaL_loadbuffer(l, self.code.as_ptr() as _, self.code.len(), name.cstr().as_ptr()) } } } impl Load for T { - fn load(&self, l: State) -> crate::vm::Result { - Ok(self.load_string(l)) + fn load(&self, l: State) -> ThreadStatus { + self.load_string(l) + } +} + +pub trait Custom { + type Error: std::error::Error; + + fn read_data(&mut self) -> Result<&[u8], Self::Error>; +} + +/// Bind a custom Rust loader to Lua. +/// +/// # Safety +/// +/// This is UB to call outside a [Load] trait implementation. +pub unsafe fn load_custom(l: State, chunk_name: ChunkName, mut custom: T) -> ThreadStatus { + extern "C-unwind" fn _reader(l: State, ud: *mut c_void, sz: *mut usize) -> *const c_char { + let obj = ud as *mut T; + unsafe { + let res = (&mut *obj).read_data(); + match res { + Err(e) => { + lua_rust_error(l, e); + }, + Ok(v) => { + *sz = v.len(); + v.as_ptr() as _ + } + } + } } + lua_load(l, _reader::, &mut custom as *mut T as _, chunk_name.cstr().as_ptr()) } diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index d1649bb..de53b6b 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -154,6 +154,12 @@ pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int unsafe { lua_remove(vm.as_ptr(), -1) }; Err(Error::Syntax(str.into())) } + ThreadStatus::ErrRun => { + // If we've got an error, read it and clear the stack. + let str: &str = FromLua::from_lua(vm, -1)?; + unsafe { lua_remove(vm.as_ptr(), -1) }; + Err(Error::Loader(str.into())) + } ThreadStatus::ErrMem => Err(Error::Memory), _ => Err(Error::Unknown) } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index ea8b4f5..f74e249 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -30,10 +30,9 @@ use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; -use crate::vm::core::LoadString; +use crate::vm::core::{Load, LoadString}; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::error::Error; -use crate::vm::Load; use crate::vm::userdata::core::Registry; use crate::vm::userdata::UserData; use crate::vm::value::{FromLua, IntoLua}; @@ -120,7 +119,7 @@ impl Vm { pub fn run<'a, R: FromLua<'a>>(&'a self, obj: impl Load) -> crate::vm::Result { let l = self.as_ptr(); let handler_pos = unsafe { push_error_handler(l) }; - let res = obj.load(l)?; + let res = obj.load(l); unsafe { handle_syntax_error(self, res, handler_pos)? }; unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; // Read and return the result of the function from the stack. diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index fcfb1cb..5fb0ef4 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -89,7 +89,8 @@ simple_error! { Null => "string contains a null character", MultiValue => "only one value is supported by this API", UserData(crate::vm::userdata::Error) => "userdata: {}", - UnsupportedType(Type) => "unsupported lua type: {:?}" + UnsupportedType(Type) => "unsupported lua type: {:?}", + Loader(String) => "loader error: {}" } } diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 6017547..3d0482c 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod core; +pub mod core; pub mod function; pub mod value; pub mod error; @@ -37,6 +37,6 @@ pub mod registry; pub mod table; pub mod thread; -pub use core::*; +pub use core::{Vm, RootVm}; pub type Result = std::result::Result; diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 87edef2..7ec31fa 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -26,8 +26,30 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::load::Code; -use bp3d_lua::vm::{Load, RootVm, Vm}; +use std::fmt::Write; +use bp3d_lua::ffi::lua::{State, ThreadStatus}; +use bp3d_lua::vm::core::load::{load_custom, Code}; +use bp3d_lua::vm::core::Load; +use bp3d_lua::vm::core::util::ChunkNameBuilder; +use bp3d_lua::vm::{RootVm, Vm}; + +struct BrokenReader; + +impl bp3d_lua::vm::core::load::Custom for BrokenReader { + type Error = bp3d_lua::vm::error::Error; + + fn read_data(&mut self) -> Result<&[u8], Self::Error> { + Err(bp3d_lua::vm::error::Error::Error) + } +} + +impl Load for BrokenReader { + fn load(&self, l: State) -> ThreadStatus { + let mut builder = ChunkNameBuilder::new(); + let _ = write!(&mut builder, "broken"); + unsafe { load_custom(l, builder.build(), BrokenReader) } + } +} fn run_assert_err(vm: &Vm, obj: impl Load, err_msg: &str) { let res = vm.run::<()>(obj); @@ -43,5 +65,7 @@ fn test_vm_run() { run_assert_err(&vm, Code::new("test", b"return 1 + b"), "test:1: attempt to perform arithmetic on global 'b' (a nil value)"); run_assert_err(&vm, c"return 1 + b", "[string \"return 1 + b\"]:1: attempt to perform arithmetic on global 'b' (a nil value)"); run_assert_err(&vm, Code::new("this is an amazingly long text which should get truncated我", b"return 1 + b"), "this is an amazingly long text which should get truncated:1: attempt to perform arithmetic on global 'b' (a nil value)"); + let err = vm.run::<()>(BrokenReader).unwrap_err(); + assert_eq!(err.to_string(), "loader error: rust error: error in error handler"); assert_eq!(vm.top(), top); } From fd6f6d1d62eb08938177a99ea9368726a563067a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 31 Mar 2025 23:58:53 +0200 Subject: [PATCH 140/527] Added new Script load component to run actual script files --- core/src/vm/core/interface.rs | 2 +- core/src/vm/core/load.rs | 59 +++++++++++++++++++++++++++++++++-- core/tests/lua/basic.lua | 5 +++ core/tests/lua/broken.lua | 4 +++ core/tests/test_vm_run.rs | 7 +++-- 5 files changed, 71 insertions(+), 6 deletions(-) create mode 100644 core/tests/lua/basic.lua create mode 100644 core/tests/lua/broken.lua diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs index 4f2a236..1580fe2 100644 --- a/core/src/vm/core/interface.rs +++ b/core/src/vm/core/interface.rs @@ -33,5 +33,5 @@ pub trait LoadString { } pub trait Load { - fn load(&self, l: State) -> ThreadStatus; + fn load(self, l: State) -> ThreadStatus; } diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs index 946f3dc..297dc08 100644 --- a/core/src/vm/core/load.rs +++ b/core/src/vm/core/load.rs @@ -27,7 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::fmt::Write; -use std::ffi::{c_char, c_void, CStr, CString}; +use std::ffi::{c_char, c_void, CStr, CString, OsStr}; +use std::fs::File; +use std::path::Path; use crate::ffi::laux::{luaL_loadbuffer, luaL_loadstring}; use crate::ffi::lua::{lua_load, State, ThreadStatus}; use crate::vm::core::{Load, LoadString}; @@ -68,7 +70,7 @@ impl<'a> Code<'a> { } impl Load for Code<'_> { - fn load(&self, l: State) -> ThreadStatus { + fn load(self, l: State) -> ThreadStatus { let mut builder = ChunkNameBuilder::new(); let _ = write!(&mut builder, "={}", self.name); let name = builder.build(); @@ -77,7 +79,7 @@ impl Load for Code<'_> { } impl Load for T { - fn load(&self, l: State) -> ThreadStatus { + fn load(self, l: State) -> ThreadStatus { self.load_string(l) } } @@ -111,3 +113,54 @@ pub unsafe fn load_custom(l: State, chunk_name: ChunkName, mut custom } lua_load(l, _reader::, &mut custom as *mut T as _, chunk_name.cstr().as_ptr()) } + +const BUF_SIZE: usize = 8192; + +pub struct Read { + inner: T, + buffer: [u8; BUF_SIZE], + len: usize +} + +impl Read { + pub fn new(inner: T) -> Self { + Self { + inner, + buffer: [0; BUF_SIZE], + len: 0 + } + } +} + +impl Custom for Read { + type Error = std::io::Error; + + fn read_data(&mut self) -> Result<&[u8], Self::Error> { + self.len = self.inner.read(&mut self.buffer[..])?; + Ok(&self.buffer[..self.len]) + } +} + +pub struct Script { + file: File, + chunk_name: ChunkName +} + +impl Script { + pub fn from_path(path: impl AsRef) -> std::io::Result { + let mut builder = ChunkNameBuilder::new(); + let file_name = path.as_ref().file_name().unwrap_or(OsStr::new("unnamed")).to_str().unwrap_or("not-unicode"); + let _ = write!(&mut builder, "@{}", file_name); + let file = File::open(path)?; + Ok(Self { + file, + chunk_name: builder.build(), + }) + } +} + +impl Load for Script { + fn load(self, l: State) -> ThreadStatus { + unsafe { load_custom(l, self.chunk_name, Read::new(self.file)) } + } +} diff --git a/core/tests/lua/basic.lua b/core/tests/lua/basic.lua new file mode 100644 index 0000000..02d21cc --- /dev/null +++ b/core/tests/lua/basic.lua @@ -0,0 +1,5 @@ +function main() + error("nope") +end + +return 1 + main() diff --git a/core/tests/lua/broken.lua b/core/tests/lua/broken.lua new file mode 100644 index 0000000..62e484b --- /dev/null +++ b/core/tests/lua/broken.lua @@ -0,0 +1,4 @@ +function bro + end + +return + diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 7ec31fa..a8e1c37 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -28,7 +28,7 @@ use std::fmt::Write; use bp3d_lua::ffi::lua::{State, ThreadStatus}; -use bp3d_lua::vm::core::load::{load_custom, Code}; +use bp3d_lua::vm::core::load::{load_custom, Code, Script}; use bp3d_lua::vm::core::Load; use bp3d_lua::vm::core::util::ChunkNameBuilder; use bp3d_lua::vm::{RootVm, Vm}; @@ -44,7 +44,7 @@ impl bp3d_lua::vm::core::load::Custom for BrokenReader { } impl Load for BrokenReader { - fn load(&self, l: State) -> ThreadStatus { + fn load(self, l: State) -> ThreadStatus { let mut builder = ChunkNameBuilder::new(); let _ = write!(&mut builder, "broken"); unsafe { load_custom(l, builder.build(), BrokenReader) } @@ -67,5 +67,8 @@ fn test_vm_run() { run_assert_err(&vm, Code::new("this is an amazingly long text which should get truncated我", b"return 1 + b"), "this is an amazingly long text which should get truncated:1: attempt to perform arithmetic on global 'b' (a nil value)"); let err = vm.run::<()>(BrokenReader).unwrap_err(); assert_eq!(err.to_string(), "loader error: rust error: error in error handler"); + run_assert_err(&vm, Script::from_path("./tests/lua/basic.lua").unwrap(), "basic.lua:2: nope"); + let err = vm.run::<()>(Script::from_path("./tests/lua/broken.lua").unwrap()).unwrap_err(); + assert_eq!(err.to_string(), "syntax error: broken.lua:2: '(' expected near 'end'"); assert_eq!(vm.top(), top); } From 1ac2f399c121dc7457818ec98671c65086cdf4e9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 1 Apr 2025 22:17:24 +0200 Subject: [PATCH 141/527] Added more tests for userdata --- core/src/macros/userdata_func.rs | 8 +-- core/src/vm/userdata/core.rs | 18 +++++- core/src/vm/userdata/error.rs | 2 + core/tests/test_vm_userdata.rs | 98 +++++++++++++++++++++++++++----- 4 files changed, 106 insertions(+), 20 deletions(-) diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 2973ea0..6832980 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -36,7 +36,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -62,7 +62,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *mut $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -88,7 +88,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -113,7 +113,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::lua::lua_touserdata(l, 1) } as *const $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index ca7bff0..6ad1704 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -33,6 +33,7 @@ use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; use crate::ffi::lua::{lua_pushcclosure, lua_pushvalue, lua_setfield, lua_settop, CFunction, State}; use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, UserData}; use crate::vm::util::{LuaType, TypeName}; +use crate::vm::value::IntoLua; use crate::vm::Vm; pub struct Function { @@ -80,6 +81,9 @@ impl Function { if self.name == c"__index" { return Err(Error::Index); } + if self.name == c"__metatable" { + return Err(Error::Metatable); + } if self.is_mutable { let initial = &self.args[0]; for v in self.args.iter().skip(1) { @@ -120,7 +124,19 @@ impl<'a, T: UserData> Registry<'a, T> { unsafe { lua_settop(vm.as_ptr(), -2) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } - Ok(Registry { vm, useless: PhantomData, has_gc: OnceCell::new() }) + let reg = Registry { vm, useless: PhantomData, has_gc: OnceCell::new() }; + reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()).unwrap_unchecked(); + Ok(reg) + } + + pub fn add_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { + let num = value.into_lua(self.vm); + if num > 1 { + unsafe { lua_settop(self.vm.as_ptr(), -(num as i32) - 1) }; + return Err(Error::MultiValueField); + } + unsafe { lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); } + Ok(()) } pub fn add_method(&self, name: &'static CStr, func: CFunction) { diff --git a/core/src/vm/userdata/error.rs b/core/src/vm/userdata/error.rs index 68d335d..c03315e 100644 --- a/core/src/vm/userdata/error.rs +++ b/core/src/vm/userdata/error.rs @@ -35,6 +35,8 @@ simple_error! { MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", Index => "__index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index", + Metatable => "__metatable is set for security reasons and cannot be altered", + MultiValueField => "multi-value fields are not supported", AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", Alignment(usize) => "too strict alignment required ({} bytes), max is 8 bytes" } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 3cc6c38..95430e1 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -26,12 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::sync::Mutex; use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::LuaDrop; +static MUTEX: Mutex<()> = Mutex::new(()); + static mut DROP_COUNTER: i32 = 0; static mut LUA_DROP_COUNTER: i32 = 0; @@ -171,26 +174,91 @@ fn test_vm_userdata_error_handling() { assert_eq!(top, vm.top()); } +fn test_vm_userdata_base(vm: &Vm) { + unsafe { + DROP_COUNTER = 0; + LUA_DROP_COUNTER = 0; + } + let top = vm.top(); + vm.register_userdata::().unwrap(); + assert_eq!(top, vm.top()); + vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); + assert_eq!(top, vm.top()); + vm.run_code::<()>(c"a = MyInt(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt(456)").unwrap(); + vm.run_code::<()>(c"c = MyInt(456)").unwrap(); + assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); + assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); + assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); + assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); + assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); + assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); + assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); + assert_eq!(vm.run_code::(c"return a.tonumber(b)").unwrap(), 456.0); + assert_eq!(top + 8, vm.top()); +} + #[test] fn test_vm_userdata() { + let _guard = MUTEX.lock(); { let vm = RootVm::new(); let top = vm.top(); - vm.register_userdata::().unwrap(); - assert_eq!(top, vm.top()); - vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); - assert_eq!(top, vm.top()); - vm.run_code::<()>(c"a = MyInt(123)").unwrap(); - vm.run_code::<()>(c"b = MyInt(456)").unwrap(); - vm.run_code::<()>(c"c = MyInt(456)").unwrap(); - assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); - assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); - assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); - assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); - assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); - assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); - assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); - assert_eq!(top + 7, vm.top()); + test_vm_userdata_base(&vm); + assert_eq!(top + 8, vm.top()); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security1() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"getmetatable(a).__gc = function() print(\"Lua has hacked Rust\") end").unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security2() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"a.__gc = function() print(\"Lua has hacked Rust\") end").unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security3() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"setmetatable(a, nil)").unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security4() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c" + local func = a.tonumber + local tbl = {} + tbl.tonumber = func + tbl:tonumber() + ").unwrap_err(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); From 9bfc5394db41b9c6ada16469f87f148781cd0cf6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 21:07:58 +0200 Subject: [PATCH 142/527] Refactored table abstraction to no longer require a scope. The scope facility is now moved to Vm. --- codegen/src/gen/into_param.rs | 9 +-- core/src/vm/core/vm.rs | 7 +++ core/src/vm/table/core.rs | 54 +++++++++++++++--- core/src/vm/table/mod.rs | 6 +- core/src/vm/table/scope.rs | 101 ---------------------------------- core/tests/test_vm_tables.rs | 55 +++++++++--------- 6 files changed, 86 insertions(+), 146 deletions(-) delete mode 100644 core/src/vm/table/scope.rs diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index 7d0ea06..f23ce86 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -54,12 +54,12 @@ impl Parser for IntoParam { // Table indices starts at 1 rather than 0 in Lua. let index = (field.index + 1) as i32; quote! { - scope.set(#index, self.#name_idx).unwrap(); + tbl.set(#index, self.#name_idx).unwrap(); } } else { let name = field.unique_name; quote! { - scope.set_field(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); + tbl.set_field(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); } } } @@ -90,10 +90,7 @@ impl Parser for IntoParam { unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { fn into_param(self, vm: &bp3d_lua::vm::Vm) -> u16 { let mut tbl = bp3d_lua::vm::table::Table::new(vm); - { - let mut scope = tbl.lock(); - #(#parsed)* - } + #(#parsed)* 1 } } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index f74e249..291c20f 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -49,6 +49,13 @@ impl Vm { } } + pub fn scope crate::vm::Result>(&self, f: F) -> crate::vm::Result { + let top = self.top(); + let r = f(self)?; + unsafe { lua_settop(self.l, top) }; + Ok(r) + } + pub fn register_userdata(&self) -> crate::vm::Result<()> { let reg = unsafe { Registry::::new(self) }.map_err(Error::UserData)?; let res = T::register(®).map_err(Error::UserData); diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 336a905..984d1c4 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,12 +27,11 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::table::Scope; use crate::vm::Vm; pub struct Table<'a> { @@ -72,11 +71,6 @@ impl<'a> Table<'a> { Self { vm, index } } - #[inline(always)] - pub fn lock(&mut self) -> Scope { - Scope::new(self.vm, self.index) - } - pub fn len(&self) -> usize { let mut size: MSize = 0; let ret = unsafe { lua_ext_tab_len(self.vm.as_ptr(), self.index, &mut size) }; @@ -120,4 +114,50 @@ impl<'a> Table<'a> { pub fn iter(&mut self) -> Iter { Iter::from_raw(self.vm, self.index) } + + pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm); + if nums > 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); + } + Ok(()) + } + + pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { + if T::num_values() > 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); + T::from_lua(self.vm, -1) + } + } + + pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = value.into_lua(self.vm); + if nums > 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_rawseti(self.vm.as_ptr(), self.index, i); + } + Ok(()) + } + + pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { + if T::num_values() > 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + lua_rawgeti(self.vm.as_ptr(), self.index, i); + T::from_lua(self.vm, -1) + } + } } diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index b5c6c77..53e3aca 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -26,10 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod scope; mod core; mod interface; mod iter; pub use core::Table; -pub use scope::Scope; + +//TODO: Remove scope put back all functions into the main table structure, not a problem since +// values which require absolute stack index already do their conversions locally. +//TODO: Re-introduce scope as Vm tool instead of a table tool. diff --git a/core/src/vm/table/scope.rs b/core/src/vm/table/scope.rs deleted file mode 100644 index 563d863..0000000 --- a/core/src/vm/table/scope.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use crate::ffi::lua::{lua_getfield, lua_gettop, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop}; -use crate::util::AnyStr; -use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::Vm; - -pub struct Scope<'a> { - vm: &'a Vm, - index: i32, - initial_top: i32 -} - -impl<'a> Scope<'a> { - pub(super) fn new(vm: &'a Vm, index: i32) -> Self { - let initial_top = unsafe { lua_gettop(vm.as_ptr()) }; - Self { vm, index, initial_top } - } - - pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { - unsafe { - let nums = value.into_lua(self.vm); - if nums > 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } - lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); - } - Ok(()) - } - - pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { - if T::num_values() > 1 { - return Err(crate::vm::error::Error::MultiValue); - } - unsafe { - lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); - T::from_lua(self.vm, -1) - } - } - - pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { - unsafe { - let nums = value.into_lua(self.vm); - if nums > 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } - lua_rawseti(self.vm.as_ptr(), self.index, i); - } - Ok(()) - } - - pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { - if T::num_values() > 1 { - return Err(crate::vm::error::Error::MultiValue); - } - unsafe { - lua_rawgeti(self.vm.as_ptr(), self.index, i); - T::from_lua(self.vm, -1) - } - } -} - -impl Drop for Scope<'_> { - fn drop(&mut self) { - let top = unsafe { lua_gettop(self.vm.as_ptr()) }; - let count = top - self.initial_top; - // Pop count values off the stack to ensure the stack is cleared after all table - // manipulations are finished. - unsafe { lua_settop(self.vm.as_ptr(), -count-1) }; - } -} diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index 8ea37c6..07d8edf 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -33,22 +33,18 @@ use bp3d_lua::vm::table::Table; fn tables() { let mut vm = RootVm::new(); let top = vm.top(); - let mut tbl = Table::new(&vm); - { - let mut scope = tbl.lock(); - scope.set_field(c"a", 0.42).unwrap(); - scope.set_field(c"b", "My great string").unwrap(); - let mut new_table = Table::new(&vm); - { - let mut scope = new_table.lock(); - scope.set_field(c"whatever", 42).unwrap(); - } - let s: &str = scope.get_field(c"b").unwrap(); + vm.scope(|vm| { + let mut tbl = Table::new(vm); + tbl.set_field(c"a", 0.42)?; + tbl.set_field(c"b", "My great string")?; + let mut new_table = Table::new(vm); + new_table.set_field(c"whatever", 42)?; + let s: &str = tbl.get_field(c"b")?; assert_eq!(s, "My great string"); - scope.set_field(c"sub", new_table).unwrap(); - } - assert_eq!(tbl.len(), 3); - vm.set_global(c"myTable", tbl).unwrap(); + tbl.set_field(c"sub", new_table)?; + assert_eq!(tbl.len(), 3); + vm.set_global(c"myTable", tbl) + }).unwrap(); let new_top = vm.top(); assert_eq!(top, new_top); let v = vm.run_code::(c"return myTable.c"); @@ -65,20 +61,19 @@ fn tables() { vm.clear(); let new_top_1 = vm.top(); assert_eq!(new_top, new_top_1); - let mut tbl: Table = vm.get_global("myTable").unwrap(); - assert_eq!(tbl.len(), 3); - { - let scope = tbl.lock(); - let v: f64 = scope.get_field(c"a").unwrap(); - assert_eq!(v, 0.42); - } - let v = vm.run_code::<&str>(c"return myTable.b").unwrap(); - assert_eq!(v, "My great string"); - { - let scope = tbl.lock(); - let v: f64 = scope.get_field(c"a").unwrap(); + vm.scope(|vm| { + let tbl: Table = vm.get_global("myTable")?; + assert_eq!(tbl.len(), 3); + let v: f64 = tbl.get_field(c"a")?; assert_eq!(v, 0.42); - } - assert_eq!(v, "My great string"); - assert_eq!(vm.top(), new_top + 2); + let v = vm.run_code::<&str>(c"return myTable.b")?; + assert_eq!(v, "My great string"); + { + let v: f64 = tbl.get_field(c"a")?; + assert_eq!(v, 0.42); + } + assert_eq!(v, "My great string"); + Ok(()) + }).unwrap(); + assert_eq!(vm.top(), new_top); } From 7f3559a1d8431c849117968a0fab4188e0d9298f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 21:08:41 +0200 Subject: [PATCH 143/527] Removed comments from main table module --- core/src/vm/table/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 53e3aca..58c9677 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -31,7 +31,3 @@ mod interface; mod iter; pub use core::Table; - -//TODO: Remove scope put back all functions into the main table structure, not a problem since -// values which require absolute stack index already do their conversions locally. -//TODO: Re-introduce scope as Vm tool instead of a table tool. From a3f7f66bda06a0ce66cfe8a9aba7f748481f21d9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:07:08 +0200 Subject: [PATCH 144/527] Added *const T to ensure registry/context types are not Send and not Sync --- core/src/vm/closure/context.rs | 2 +- core/src/vm/registry/core.rs | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 1efb72b..138ffe5 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -40,7 +40,7 @@ use crate::vm::Vm; pub struct Context { key: RawRegistryKey, - useless: PhantomData + useless: PhantomData<*const T> } impl Clone for Context { diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index cf6fd0f..2c1904e 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -120,7 +120,7 @@ impl RawRegistryKey { pub struct RegistryKey { raw: RawRegistryKey, - useless: PhantomData + useless: PhantomData<*const T> } impl RegistryKey { @@ -161,6 +161,8 @@ impl RegistryKey { unsafe { self.raw.delete(vm) }; } + //TODO: Implement replace function + /// Creates a new [RegistryKey] from the top of the lua stack. /// /// # Arguments From 6bcfc917b00c1c41cb9f507580e66df8a4b048c0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:10:45 +0200 Subject: [PATCH 145/527] Added HAS_VM flag to ensure registry keys and contexts are safe --- core/src/vm/core/vm.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 291c20f..3e5730b 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::cell::Cell; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; @@ -134,6 +135,10 @@ impl Vm { } } +thread_local! { + static HAS_VM: Cell = Cell::new(false); +} + pub struct RootVm { vm: Vm, leaked: Vec> @@ -141,8 +146,12 @@ pub struct RootVm { impl RootVm { pub fn new() -> RootVm { + if HAS_VM.with(|v| v.get()) { + panic!("A VM already exists for this thread.") + } let l = unsafe { luaL_newstate() }; unsafe { luaL_openlibs(l) }; + HAS_VM.set(true); RootVm { vm: unsafe { Vm::from_raw(l) }, leaked: Vec::new() @@ -174,14 +183,15 @@ impl DerefMut for RootVm { impl Drop for RootVm { fn drop(&mut self) { - unsafe { - println!("Closing Lua VM..."); - lua_close(self.vm.as_ptr()); - } println!("Deleting leaked pointers..."); let v = std::mem::replace(&mut self.leaked, Vec::new()); for f in v { f() } + unsafe { + println!("Closing Lua VM..."); + lua_close(self.vm.as_ptr()); + } + HAS_VM.set(false); } } From 96273a9828487f1836bbf890940b4b1a32e87261 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:29:32 +0200 Subject: [PATCH 146/527] Added support for replacing registry values --- core/src/vm/registry/core.rs | 2 -- core/src/vm/registry/interface.rs | 17 ++++++++++++++--- core/src/vm/table/interface.rs | 15 ++++++++++++--- core/src/vm/value/function.rs | 15 ++++++++++++--- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 2c1904e..791c246 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -161,8 +161,6 @@ impl RegistryKey { unsafe { self.raw.delete(vm) }; } - //TODO: Implement replace function - /// Creates a new [RegistryKey] from the top of the lua stack. /// /// # Arguments diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 4137c44..8764f3a 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -49,15 +49,26 @@ pub trait RegistryValue: 'static { } /// A trait to produce registry values safely. -pub trait Register { +pub trait Registry: Sized { type RegistryValue: RegistryValue; /// Register this value into the registry. /// - /// # Arguments + /// # Arguments /// /// * `vm`: the [Vm] to attach this value to. /// /// returns: RegistryKey - fn register(self, vm: &Vm) -> RegistryKey; + fn registry_put(self, vm: &Vm) -> RegistryKey; + + /// Swaps the value pointed by `old` in the registry to this value. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach this value to. + /// * `old`: the old registry key to be replaced. + fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + old.delete(vm); + self.registry_put(vm) + } } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 8d4d750..7732ac5 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -31,7 +31,7 @@ use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; -use crate::vm::registry::Register; +use crate::vm::registry::Registry; use crate::vm::util::LuaType; use crate::vm::value::FromLua; use crate::vm::table::Table; @@ -77,12 +77,21 @@ unsafe impl IntoParam for Table<'_> { impl LuaType for Table<'_> {} -impl Register for Table<'_> { +impl Registry for Table<'_> { type RegistryValue = crate::vm::registry::types::Table; - fn register(self, vm: &Vm) -> RegistryKey { + #[inline(always)] + fn registry_put(self, vm: &Vm) -> RegistryKey { // If the table is not at the top of the stack, move it to the top. ensure_value_top(vm, self.index()); unsafe { RegistryKey::from_top(vm) } } + + #[inline(always)] + fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + // If the table is not at the top of the stack, move it to the top. + ensure_value_top(vm, self.index()); + unsafe { old.as_raw().replace(vm) }; + old + } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 207e3e4..34fa446 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -29,7 +29,7 @@ use crate::ffi::lua::{lua_pushvalue, Type}; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; -use crate::vm::registry::Register; +use crate::vm::registry::Registry; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; use crate::vm::Vm; @@ -64,12 +64,21 @@ impl<'a> FromLua<'a> for LuaFunction<'a> { } } -impl Register for LuaFunction<'_> { +impl Registry for LuaFunction<'_> { type RegistryValue = crate::vm::registry::types::LuaFunction; - fn register(self, vm: &Vm) -> RegistryKey { + #[inline(always)] + fn registry_put(self, vm: &Vm) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. ensure_value_top(vm, self.index); unsafe { RegistryKey::from_top(vm) } } + + #[inline(always)] + fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + // If the function is not at the top of the stack, move it to the top. + ensure_value_top(vm, self.index); + unsafe { old.as_raw().replace(vm) }; + old + } } From ed9ae48190392881f1cc88eaa1d9aa7c457e8511 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:52:41 +0200 Subject: [PATCH 147/527] Added support for FromLua to Option --- core/src/vm/value/core.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 5a81958..0199b40 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -245,3 +245,23 @@ impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); + +impl<'a, T: FromLua<'a>> FromLua<'a> for Option { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let ty = unsafe { lua_type(vm.as_ptr(), index) }; + if ty == Type::Nil { + None + } else { + Some(FromLua::from_lua_unchecked(vm, index)) + } + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let ty = unsafe { lua_type(vm.as_ptr(), index) }; + if ty == Type::Nil { + Ok(None) + } else { + Ok(Some(FromLua::from_lua(vm, index)?)) + } + } +} From dcc9d12caf463f26e3dac3895d433d1364ff432e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:58:01 +0200 Subject: [PATCH 148/527] Fixed stack not cleaned when reading a None value from the top of the stack --- core/src/vm/value/core.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 0199b40..e4a8c61 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_touserdata}; +use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_touserdata, lua_settop}; use crate::vm::function::IntoParam; use crate::vm::Vm; use crate::vm::error::{Error, TypeError}; @@ -250,6 +250,10 @@ impl<'a, T: FromLua<'a>> FromLua<'a> for Option { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let ty = unsafe { lua_type(vm.as_ptr(), index) }; if ty == Type::Nil { + // Clear the nil value at the top of the stack. + if index == -1 { + unsafe { lua_settop(vm.as_ptr(), -2) }; + } None } else { Some(FromLua::from_lua_unchecked(vm, index)) @@ -259,6 +263,10 @@ impl<'a, T: FromLua<'a>> FromLua<'a> for Option { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let ty = unsafe { lua_type(vm.as_ptr(), index) }; if ty == Type::Nil { + // Clear the nil value at the top of the stack. + if index == -1 { + unsafe { lua_settop(vm.as_ptr(), -2) }; + } Ok(None) } else { Ok(Some(FromLua::from_lua(vm, index)?)) From 03ea1dc6566e777745d611078aac729434c2c2d5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 2 Apr 2025 22:59:00 +0200 Subject: [PATCH 149/527] Added new Namespace tool and first test in test_vm_closures --- core/src/vm/mod.rs | 1 + core/src/vm/namespace.rs | 87 ++++++++++++++++++++++++++++++++++ core/tests/test_vm_closures.rs | 40 +++++++++------- 3 files changed, 111 insertions(+), 17 deletions(-) create mode 100644 core/src/vm/namespace.rs diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 3d0482c..2e974e2 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -36,6 +36,7 @@ pub mod closure; pub mod registry; pub mod table; pub mod thread; +pub mod namespace; pub use core::{Vm, RootVm}; diff --git a/core/src/vm/namespace.rs b/core/src/vm/namespace.rs new file mode 100644 index 0000000..0dc74ec --- /dev/null +++ b/core/src/vm/namespace.rs @@ -0,0 +1,87 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::lua_settop; +use crate::vm::registry::Registry; +use crate::vm::table::Table; +use crate::vm::value::IntoLua; +use crate::vm::Vm; + +pub struct Namespace<'a> { + vm: &'a Vm, + table: Table<'a> +} + +impl<'a> Namespace<'a> { + fn from_table<'b>(vm: &'a Vm, table: Table<'a>, names: impl Iterator) -> crate::vm::Result { + let mut key = table.registry_put(vm); + for name in names { + let mut table = key.push(vm); + let tbl: Option = table.get_field(name)?; + let tab = match tbl { + Some(v) => v, + None => { + table.set_field(name, Table::new(vm))?; + table.get_field(name)? + } + }; + key = tab.registry_swap(vm, key); + } + let table = key.push(vm); + key.delete(vm); + Ok(Self { vm, table }) + } + + pub fn new(vm: &'a Vm, path: &str) -> crate::vm::Result { + let mut names = path.split("."); + let name = names.next().expect("Attempt to build an empty namespace"); + let value: Option> = vm.get_global(name)?; + let table = match value { + Some(table) => table, + None => { + vm.set_global(name, Table::new(vm))?; + vm.get_global(name)? + } + }; + Self::from_table(vm, table, names) + } + + pub fn add<'b, T: IntoLua>(&mut self, items: impl IntoIterator) -> crate::vm::Result<()> { + for (name, item) in items { + self.table.set_field(name, item)?; + } + Ok(()) + } +} + +impl Drop for Namespace<'_> { + fn drop(&mut self) { + // Clear the table which should be on top of the stack. + unsafe { lua_settop(self.vm.as_ptr(), -2) }; + } +} diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 43a1d03..ed41480 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -29,6 +29,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::ContextMut; use bp3d_lua::vm::closure::types::RClosure; +use bp3d_lua::vm::namespace::Namespace; use bp3d_lua::vm::RootVm; struct TestContext { @@ -91,40 +92,45 @@ fn test_vm_context() { let vm = RootVm::new(); let top = vm.top(); let ctx = ContextMut::new(&vm); - vm.set_global(c"context_push", context_push(ctx)).unwrap(); - vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); - vm.set_global(c"context_set_value", context_set_value(ctx)).unwrap(); + { + let mut namespace = Namespace::new(&vm, "context").unwrap(); + namespace.add([ + ("push", context_push(ctx)), + ("pop", context_pop(ctx)), + ("set_value", context_set_value(ctx)) + ]).unwrap(); + } assert_eq!(top, vm.top()); - let res = vm.run_code::<()>(c"context_set_value(42)"); + let res = vm.run_code::<()>(c"context.set_value(42)"); assert!(res.is_err()); - assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context_set_value(42)\"]:1: Context is not available in this function."); + assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(42)\"]:1: Context is not available in this function."); let mut obj = TestContext { value: 0, value3: vec![], }; { let _obj = ctx.bind(&vm, &mut obj); - vm.run_code::<()>(c"context_set_value(42)").unwrap(); + vm.run_code::<()>(c"context.set_value(42)").unwrap(); } - let res = vm.run_code::<()>(c"context_set_value(84)"); + let res = vm.run_code::<()>(c"context.set_value(84)"); assert!(res.is_err()); - assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context_set_value(84)\"]:1: Context is not available in this function."); + assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(84)\"]:1: Context is not available in this function."); assert_eq!(obj.value, 42); { let _obj = ctx.bind(&vm, &mut obj); - vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); - vm.run_code::<()>(c"context_push(1)").unwrap(); - vm.run_code::<()>(c"context_push(2)").unwrap(); - vm.run_code::<()>(c"context_push(3)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == nil)").unwrap(); + vm.run_code::<()>(c"context.push(1)").unwrap(); + vm.run_code::<()>(c"context.push(2)").unwrap(); + vm.run_code::<()>(c"context.push(3)").unwrap(); } assert_eq!(obj.value3.len(), 3); { let _obj = ctx.bind(&vm, &mut obj); - vm.run_code::<()>(c"assert(context_pop() == 3)").unwrap(); - vm.run_code::<()>(c"assert(context_pop() == 2)").unwrap(); - vm.run_code::<()>(c"assert(context_pop() == 1)").unwrap(); - vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); - vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == 3)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == 2)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == 1)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == nil)").unwrap(); + vm.run_code::<()>(c"assert(context.pop() == nil)").unwrap(); } assert_eq!(obj.value3.len(), 0); assert_eq!(top, vm.top()); From 068ff5bff0f95cd9e50ac117b8d975a439f385aa Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 4 Apr 2025 04:31:18 +0200 Subject: [PATCH 150/527] Added class test --- core/tests/lua/class.lua | 58 +++++++++++++++++++++++++++++++++++++++ core/tests/test_vm_run.rs | 1 + 2 files changed, 59 insertions(+) create mode 100644 core/tests/lua/class.lua diff --git a/core/tests/lua/class.lua b/core/tests/lua/class.lua new file mode 100644 index 0000000..22a9b08 --- /dev/null +++ b/core/tests/lua/class.lua @@ -0,0 +1,58 @@ +--- @param name string +--- @param parent table +function AbstractClass(name, parent) + local class = {} + if not class.init then + class.init = function(_) end + end + class.__name = name + class.__index = class + if parent then + setmetatable(class, parent) + end + return class +end + +--- @param name string +--- @param parent table +function Class(name, parent) + local class = {} + if not class.init then + class.init = function(_) end + end + class.__name = name + class.new = function(...) + local obj = {} + setmetatable(obj, class) + obj:init(...) + return obj + end + class.__index = class + if parent then + setmetatable(class, parent) + end + return class +end + +--- @class Parent +local Parent = AbstractClass("Parent") + +function Parent:value() + return 42 +end + +--- @class Child +local Child = Class("Child", Parent) + +function Child:init(a) + Parent.init(self) + self._a = a +end + +function Child:value2() + return self:value() + self._a +end + +local obj = Child.new(42) +assert(obj:value2() == 84) +assert(obj:value() == 42) diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index a8e1c37..2f283c2 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -70,5 +70,6 @@ fn test_vm_run() { run_assert_err(&vm, Script::from_path("./tests/lua/basic.lua").unwrap(), "basic.lua:2: nope"); let err = vm.run::<()>(Script::from_path("./tests/lua/broken.lua").unwrap()).unwrap_err(); assert_eq!(err.to_string(), "syntax error: broken.lua:2: '(' expected near 'end'"); + vm.run::<()>(Script::from_path("./tests/lua/class.lua").unwrap()).unwrap(); assert_eq!(vm.top(), top); } From 3c3ed5ade16577d32f20d68435409e18bfe866b4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 4 Apr 2025 05:01:13 +0200 Subject: [PATCH 151/527] Fixed possible bug with userdata and use bp3d-debug for logs --- core/Cargo.toml | 1 + core/src/vm/core/vm.rs | 5 +++-- core/src/vm/userdata/core.rs | 34 +++++++++++++++++++++++++--------- 3 files changed, 29 insertions(+), 11 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index da7e927..cd573ec 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,6 +16,7 @@ categories = ["graphics"] bp3d-util = { version = "1.4.2", features = ["simple-error", "format"] } log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } +bp3d-debug = "1.0.0-rc.6.1.0" [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 3e5730b..4ba518f 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -28,6 +28,7 @@ use std::cell::Cell; use std::ops::{Deref, DerefMut}; +use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; @@ -183,13 +184,13 @@ impl DerefMut for RootVm { impl Drop for RootVm { fn drop(&mut self) { - println!("Deleting leaked pointers..."); + debug!("Deleting leaked pointers..."); let v = std::mem::replace(&mut self.leaked, Vec::new()); for f in v { f() } unsafe { - println!("Closing Lua VM..."); + debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); } HAS_VM.set(false); diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 6ad1704..c633c6c 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -29,8 +29,9 @@ use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; +use bp3d_debug::{debug, warning}; use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; -use crate::ffi::lua::{lua_pushcclosure, lua_pushvalue, lua_setfield, lua_settop, CFunction, State}; +use crate::ffi::lua::{lua_pushcclosure, lua_pushnil, lua_pushvalue, lua_setfield, lua_setmetatable, lua_settop, CFunction, State}; use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; @@ -149,11 +150,16 @@ impl<'a, T: UserData> Registry<'a, T> { pub fn add_gc_method(&self) { if std::mem::needs_drop::() { extern "C-unwind" fn run_drop(l: State) -> i32 { - let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; - unsafe { std::ptr::drop_in_place(udata) }; + unsafe { + let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + lua_pushnil(l); + lua_setmetatable(l, 1); + std::ptr::drop_in_place(udata); + } 0 } self.add_method(c"__gc", run_drop::); + debug!({UD=?T::CLASS_NAME}, "Type registered with simple Drop"); } self.has_gc.set(()).unwrap(); } @@ -162,20 +168,30 @@ impl<'a, T: UserData> Registry<'a, T> { impl<'a, T: UserData + LuaDrop> Registry<'a, T> { pub fn add_gc_method_with_lua_drop(&self) { extern "C-unwind" fn run_lua_drop(l: State) -> i32 { - let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; - unsafe { (&*udata).lua_drop(&Vm::from_raw(l)) }; + unsafe { + let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + lua_pushnil(l); + lua_setmetatable(l, 1); + (&*udata).lua_drop(&Vm::from_raw(l)); + } 0 } extern "C-unwind" fn run_lua_drop_full(l: State) -> i32 { - let udata = unsafe { luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) } as *mut T; - unsafe { (&*udata).lua_drop(&Vm::from_raw(l)) }; - unsafe { std::ptr::drop_in_place(udata) }; + unsafe { + let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + lua_pushnil(l); + lua_setmetatable(l, 1); + (&*udata).lua_drop(&Vm::from_raw(l)); + std::ptr::drop_in_place(udata); + } 0 } if std::mem::needs_drop::() { self.add_method(c"__gc", run_lua_drop_full::); + debug!({UD=?T::CLASS_NAME}, "Type registered with Drop and LuaDrop"); } else { self.add_method(c"__gc", run_lua_drop::); + debug!({UD=?T::CLASS_NAME}, "Type registered with LuaDrop"); } self.has_gc.set(()).unwrap(); } @@ -204,7 +220,7 @@ impl AddGcMethod for &AddGcMethodAuto { impl<'a, T: UserData> Drop for Registry<'a, T> { fn drop(&mut self) { if std::mem::needs_drop::() && self.has_gc.get().is_none() { - println!("No __gc method registered on a drop userdata type!"); + warning!("No __gc method registered on a drop userdata type!"); // No __gc method found in object that needs it force add it before finishing it. self.add_gc_method(); } From c6cbaf009166a3ded08f48716e1a896028f23934 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 4 Apr 2025 05:29:08 +0200 Subject: [PATCH 152/527] Added support for AnyReturn, which allows to transfer all results --- core/src/vm/value/any.rs | 25 +++++++++++++++++++++++++ core/src/vm/value/core.rs | 4 ++-- core/src/vm/value/interface.rs | 2 +- 3 files changed, 28 insertions(+), 3 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 8d2cbf4..373095a 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -28,6 +28,7 @@ use crate::ffi::lua::{lua_toboolean, lua_tonumber, lua_type, Type}; use crate::vm::error::Error; +use crate::vm::function::IntoParam; use crate::vm::value::FromLua; use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; @@ -81,3 +82,27 @@ impl<'a> FromLua<'a> for AnyValue<'a> { } } } + +pub struct AnyReturn { + nresults: u16 +} + +impl FromLua<'_> for AnyReturn { + unsafe fn from_lua_unchecked(vm: &Vm, _: i32) -> Self { + AnyReturn { nresults: vm.top() as _ } + } + + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { + Ok(unsafe { Self::from_lua_unchecked(vm, index) }) + } + + fn num_values() -> i16 { + -1 + } +} + +unsafe impl IntoParam for AnyReturn { + fn into_param(self, _: &Vm) -> u16 { + self.nresults + } +} diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index e4a8c61..87560b7 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -144,7 +144,7 @@ impl FromLua<'_> for () { } #[inline(always)] - fn num_values() -> u16 { + fn num_values() -> i16 { 0 } } @@ -176,7 +176,7 @@ macro_rules! impl_from_lua_tuple { ($($name: ident: $name2: ident),*) => { impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { #[inline(always)] - fn num_values() -> u16 { + fn num_values() -> i16 { count_tts!($($name)*) } diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 91e4742..51ef640 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -58,7 +58,7 @@ pub trait FromLua<'a>: Sized { /// Returns the number of values to be expected on the lua stack, after reading this value. #[inline(always)] - fn num_values() -> u16 { + fn num_values() -> i16 { 1 } } From 251a88c0adb09e99cb21ace88bbc46aaeaca3f50 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 16:40:11 +0200 Subject: [PATCH 153/527] Added UncheckedAnyReturn to return any number of lua results --- core/src/vm/value/any.rs | 42 ++++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 10 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 373095a..523f4dd 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -83,17 +83,16 @@ impl<'a> FromLua<'a> for AnyValue<'a> { } } -pub struct AnyReturn { - nresults: u16 -} +/// A marker struct to run lua code which may return any number of values on the stack. +pub struct AnyParam; -impl FromLua<'_> for AnyReturn { - unsafe fn from_lua_unchecked(vm: &Vm, _: i32) -> Self { - AnyReturn { nresults: vm.top() as _ } +impl FromLua<'_> for AnyParam { + unsafe fn from_lua_unchecked(_: &Vm, _: i32) -> Self { + AnyParam } - fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - Ok(unsafe { Self::from_lua_unchecked(vm, index) }) + fn from_lua(_: &Vm, _: i32) -> crate::vm::Result { + Ok(AnyParam) } fn num_values() -> i16 { @@ -101,8 +100,31 @@ impl FromLua<'_> for AnyReturn { } } -unsafe impl IntoParam for AnyReturn { +/// A raw primitive to return arbitrary count of values from a C function. +pub struct UncheckedAnyReturn(u16); + +impl UncheckedAnyReturn { + /// Construct a [UncheckedAnyReturn]. + /// + /// # Panic + /// + /// This function panics when the count of arguments is greater than the lua stack size itself. + /// + /// # Safety + /// + /// It is UB to run any operation which may alter the lua stack after constructing this + /// primitive. + pub unsafe fn new(vm: &Vm, count: u16) -> Self { + let top = vm.top(); + if count > top as _ { + panic!() + } + UncheckedAnyReturn(count) + } +} + +unsafe impl IntoParam for UncheckedAnyReturn { fn into_param(self, _: &Vm) -> u16 { - self.nresults + self.0 } } From af8b27be7a7e228a35a83e4c058426fb79d4c728 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 16:48:38 +0200 Subject: [PATCH 154/527] Added load_code and load functions to Vm --- core/src/vm/core/util.rs | 5 +---- core/src/vm/core/vm.rs | 36 ++++++++++++++++++++++++++++++++---- core/tests/test_vm_run.rs | 4 +++- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index de53b6b..0f116c2 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -142,10 +142,7 @@ pub unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) /// /// Calling this function with a `handler_pos` which does not correspond to the actual error handler /// C function is UB. This is also UB if the res is not the result of a load function. -pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus, handler_pos: c_int) -> crate::vm::Result<()> { - if res != ThreadStatus::Ok { - unsafe { lua_remove(vm.as_ptr(), handler_pos) }; - } +pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus) -> crate::vm::Result<()> { match res { ThreadStatus::Ok => Ok(()), ThreadStatus::ErrSyntax => { diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 4ba518f..178bb65 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -30,7 +30,7 @@ use std::cell::Cell; use std::ops::{Deref, DerefMut}; use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; -use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_setfield, lua_settop, State, GLOBALSINDEX, REGISTRYINDEX}; +use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; use crate::vm::core::{Load, LoadString}; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; @@ -38,6 +38,7 @@ use crate::vm::error::Error; use crate::vm::userdata::core::Registry; use crate::vm::userdata::UserData; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::function::LuaFunction; pub struct Vm { l: State @@ -118,22 +119,49 @@ impl Vm { let handler_pos = push_error_handler(l); // Push the lua code. let res = code.load_string(l); - handle_syntax_error(self, res, handler_pos)?; + if res != ThreadStatus::Ok { + lua_remove(l, handler_pos); + } + handle_syntax_error(self, res)?; pcall(self, 0, R::num_values() as _, handler_pos)?; } // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } + pub fn load_code(&self, code: impl LoadString) -> crate::vm::Result { + let l = self.as_ptr(); + unsafe { + // Push the lua code. + let res = code.load_string(l); + handle_syntax_error(self, res)?; + Ok(FromLua::from_lua_unchecked(self, -1)) + } + } + pub fn run<'a, R: FromLua<'a>>(&'a self, obj: impl Load) -> crate::vm::Result { let l = self.as_ptr(); let handler_pos = unsafe { push_error_handler(l) }; let res = obj.load(l); - unsafe { handle_syntax_error(self, res, handler_pos)? }; - unsafe { pcall(self, 0, R::num_values() as _, handler_pos)? }; + unsafe { + if res != ThreadStatus::Ok { + lua_remove(l, handler_pos); + } + handle_syntax_error(self, res)?; + pcall(self, 0, R::num_values() as _, handler_pos)?; + } // Read and return the result of the function from the stack. FromLua::from_lua(self, -(R::num_values() as i32)) } + + pub fn load(&self, obj: impl Load) -> crate::vm::Result { + let l = self.as_ptr(); + let res = obj.load(l); + unsafe { + handle_syntax_error(self, res)?; + Ok(FromLua::from_lua_unchecked(self, -1)) + } + } } thread_local! { diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 2f283c2..958f9d1 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -71,5 +71,7 @@ fn test_vm_run() { let err = vm.run::<()>(Script::from_path("./tests/lua/broken.lua").unwrap()).unwrap_err(); assert_eq!(err.to_string(), "syntax error: broken.lua:2: '(' expected near 'end'"); vm.run::<()>(Script::from_path("./tests/lua/class.lua").unwrap()).unwrap(); - assert_eq!(vm.top(), top); + let func = vm.load_code(c"return 1 + b").unwrap(); + func.call::<()>(()).unwrap_err(); + assert_eq!(vm.top(), top + 1); } From 8fe025d49959904e45cd9b986171b390fb5e0063 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 17:06:39 +0200 Subject: [PATCH 155/527] Fixed bug in macros --- core/src/macros/lib_func.rs | 2 +- core/src/macros/userdata_func.rs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/macros/lib_func.rs b/core/src/macros/lib_func.rs index 263e8a4..36e4e7c 100644 --- a/core/src/macros/lib_func.rs +++ b/core/src/macros/lib_func.rs @@ -32,7 +32,7 @@ macro_rules! decl_lib_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { $vis extern "C-unwind" fn $fn_name(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] diff --git a/core/src/macros/userdata_func.rs b/core/src/macros/userdata_func.rs index 6832980..b065e30 100644 --- a/core/src/macros/userdata_func.rs +++ b/core/src/macros/userdata_func.rs @@ -34,7 +34,7 @@ macro_rules! decl_userdata_func { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; @@ -86,7 +86,7 @@ macro_rules! decl_userdata_func { impl $obj_name { $vis fn $fn_name() -> $crate::vm::userdata::core::Function { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; From d56e437b09f452c31ab15e381d12b110f84585f1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 17:07:26 +0200 Subject: [PATCH 156/527] Added support for tuples to IntoParam and removed IntoLua implementations on tuples --- core/src/vm/function/core.rs | 28 ++++++++++++++++++++++++++++ core/src/vm/value/core.rs | 23 ----------------------- 2 files changed, 28 insertions(+), 23 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index ce0a5a3..baf440e 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -273,3 +273,31 @@ unsafe impl IntoParam for T { 1 } } + +macro_rules! count_tts { + () => {0}; + ($_head:tt $($tail:tt)*) => {1 + count_tts!($($tail)*)}; +} + +macro_rules! impl_into_param_tuple { + ($($name: ident: $name2: tt),*) => { + unsafe impl<$($name: IntoParam),*> IntoParam for ($($name),*) { + fn into_param(self, vm: &Vm) -> u16 { + $( + self.$name2.into_param(vm); + )* + count_tts!($($name)*) + } + } + }; +} + +impl_into_param_tuple!(T: 0, T1: 1); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); +impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 87560b7..f1babf8 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -223,29 +223,6 @@ impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8); impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8, T9: t9); -macro_rules! impl_into_lua_tuple { - ($($name: ident: $name2: tt),*) => { - unsafe impl<$($name: IntoLua),*> IntoLua for ($($name),*) { - fn into_lua(self, vm: &Vm) -> u16 { - $( - self.$name2.into_lua(vm); - )* - count_tts!($($name)*) - } - } - }; -} - -impl_into_lua_tuple!(T: 0, T1: 1); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); -impl_into_lua_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); - impl<'a, T: FromLua<'a>> FromLua<'a> for Option { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let ty = unsafe { lua_type(vm.as_ptr(), index) }; From 3424df3286f79b3682b274e80b3b2408f485ac12 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 17:07:47 +0200 Subject: [PATCH 157/527] Added support for FromParam and IntoParam to LuaFunction --- core/src/vm/value/function.rs | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 34fa446..05dfa0d 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,10 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_pushvalue, Type}; +use crate::util::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; +use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Registry; +use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; use crate::vm::Vm; @@ -39,6 +43,28 @@ pub struct LuaFunction<'a> { index: i32 } +unsafe impl SimpleDrop for LuaFunction<'_> { } + +impl LuaType for LuaFunction<'_> { } + +impl<'a> FromParam<'a> for LuaFunction<'a> { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + unsafe { luaL_checktype(vm.as_ptr(), index, Type::Function) }; + LuaFunction { vm, index } + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } +} + +unsafe impl IntoParam for LuaFunction<'_> { + fn into_param(self, vm: &Vm) -> u16 { + unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; + 1 + } +} + impl<'a> LuaFunction<'a> { pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; From 2199907943a318e56fa6489bb9bd18702cac5eec Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 5 Apr 2025 17:39:54 +0200 Subject: [PATCH 158/527] Added initial version of bp3d.lua library with associated test --- core/src/lib.rs | 1 + core/src/libs/lua/load.rs | 96 ++++++++++++++++++++++++++++++++++++++ core/src/libs/lua/mod.rs | 58 +++++++++++++++++++++++ core/src/libs/mod.rs | 29 ++++++++++++ core/tests/test_vm_libs.rs | 48 +++++++++++++++++++ 5 files changed, 232 insertions(+) create mode 100644 core/src/libs/lua/load.rs create mode 100644 core/src/libs/lua/mod.rs create mode 100644 core/src/libs/mod.rs create mode 100644 core/tests/test_vm_libs.rs diff --git a/core/src/lib.rs b/core/src/lib.rs index 71af5e2..3ec5770 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,3 +30,4 @@ pub mod ffi; pub mod vm; mod macros; pub mod util; +pub mod libs; diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs new file mode 100644 index 0000000..3e66134 --- /dev/null +++ b/core/src/libs/lua/load.rs @@ -0,0 +1,96 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::vm::core::load::Code; +use crate::vm::function::IntoParam; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; +use crate::vm::value::function::LuaFunction; +use crate::vm::Vm; + +enum FunctionOrString<'a> { + Func(LuaFunction<'a>), + String(String) +} + +unsafe impl IntoParam for FunctionOrString<'_> { + fn into_param(self, vm: &Vm) -> u16 { + match self { + FunctionOrString::Func(v) => v.into_param(vm), + FunctionOrString::String(v) => v.into_param(vm) + } + } +} + +decl_lib_func! { + fn dostring(vm: &Vm, s: &str, chunkname: Option<&str>) -> UncheckedAnyReturn { + let top = vm.top(); + true.into_param(vm); + let ret = match chunkname { + None => vm.run_code::(s), + Some(name) => vm.run::(Code::new(name, s.as_bytes())) + }; + match ret { + Ok(_) => { + let new_top = vm.top(); + unsafe { UncheckedAnyReturn::new(vm, (new_top - top) as _) } + }, + Err(e) => { + let s = e.to_string(); + false.into_param(vm); + s.into_param(vm); + unsafe { UncheckedAnyReturn::new(vm, 2) } + } + } + } +} + +decl_lib_func! { + fn loadstring<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (bool, FunctionOrString<'a>) { + match chunkname { + None => match vm.load_code(s) { + Ok(v) => (true, FunctionOrString::Func(v)), + Err(v) => (false, FunctionOrString::String(v.to_string())) + }, + Some(name) => match vm.load(Code::new(name, s.as_bytes())) { + Ok(v) => (true, FunctionOrString::Func(v)), + Err(v) => (false, FunctionOrString::String(v.to_string())) + } + } + } +} + +pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.lua")?; + namespace.add([ + ("dostring", RFunction::wrap(dostring)), + ("loadstring", RFunction::wrap(loadstring)) + ]) +} diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs new file mode 100644 index 0000000..f88ca1d --- /dev/null +++ b/core/src/libs/lua/mod.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; + +mod load; + +const PATCH_LIST: &[&str] = &[ + "disable_lua_load", + "lib_init", + "lj_disable_jit", + "lua_ext", + "lua_load_no_bc" +]; + +pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { + vm.scope(|vm| { + load::register(vm)?; + let mut namespace = Namespace::new(vm, "bp3d.lua")?; + namespace.add([ + ("name", "bp3d-lua"), + ("version", env!("CARGO_PKG_VERSION")) + ])?; + let mut patches = Table::with_capacity(vm, PATCH_LIST.len(), 0); + for (i, name) in PATCH_LIST.into_iter().enumerate() { + // Lua indices starts at 1 not 0. + patches.set((i + 1) as _, *name)?; + } + namespace.add([("patches", patches)])?; + Ok(()) + }) +} diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs new file mode 100644 index 0000000..2543c7f --- /dev/null +++ b/core/src/libs/mod.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod lua; diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs new file mode 100644 index 0000000..67acbc8 --- /dev/null +++ b/core/tests/test_vm_libs.rs @@ -0,0 +1,48 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; + +#[test] +fn test_vm_lib_lua() { + let vm = RootVm::new(); + let top = vm.top(); + bp3d_lua::libs::lua::register(&vm).unwrap(); + vm.run_code::<()>(c" + assert(bp3d.lua.name == 'bp3d-lua') + assert(bp3d.lua.version == '0.2.0') + assert(#bp3d.lua.patches == 5) + local flag, func = bp3d.lua.loadstring('return 1 + 1') + assert(flag) + assert(func() == 2) + local flag, err = bp3d.lua.loadstring('ret a + 2') + assert(not flag) + assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") + ").unwrap(); + assert_eq!(vm.top(), top); +} From 98b52976c7d6c64e74fa8732faa5f8e32f3e57f8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 15:32:19 +0200 Subject: [PATCH 159/527] Fixed mutable variable warning when only one argument is needed --- core/src/macros/mod.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/macros/mod.rs b/core/src/macros/mod.rs index 5811c74..3f11aeb 100644 --- a/core/src/macros/mod.rs +++ b/core/src/macros/mod.rs @@ -40,6 +40,13 @@ macro_rules! c_stringify { #[macro_export] macro_rules! decl_from_param { + ( + $vm: ident, $start_index: literal, $arg_name: ident: $arg_ty: ty + ) => { + use $crate::vm::function::FromParam; + let $arg_name: $arg_ty = unsafe { FromParam::from_param($vm, $start_index) }; + }; + ( $vm: ident, $start_index: literal, $($arg_name: ident: $arg_ty: ty)* ) => { From 09a713355e28c8e28ad3da43e084a277fdd32d63 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 15:33:14 +0200 Subject: [PATCH 160/527] Renamed dostring to runString and loadstring to loadString --- core/src/libs/lua/load.rs | 8 ++++---- core/tests/test_vm_libs.rs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 3e66134..74656ea 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -50,7 +50,7 @@ unsafe impl IntoParam for FunctionOrString<'_> { } decl_lib_func! { - fn dostring(vm: &Vm, s: &str, chunkname: Option<&str>) -> UncheckedAnyReturn { + fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> UncheckedAnyReturn { let top = vm.top(); true.into_param(vm); let ret = match chunkname { @@ -73,7 +73,7 @@ decl_lib_func! { } decl_lib_func! { - fn loadstring<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (bool, FunctionOrString<'a>) { + fn load_string<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (bool, FunctionOrString<'a>) { match chunkname { None => match vm.load_code(s) { Ok(v) => (true, FunctionOrString::Func(v)), @@ -90,7 +90,7 @@ decl_lib_func! { pub fn register(vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([ - ("dostring", RFunction::wrap(dostring)), - ("loadstring", RFunction::wrap(loadstring)) + ("runString", RFunction::wrap(run_string)), + ("loadString", RFunction::wrap(load_string)) ]) } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 67acbc8..f73fcee 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -37,10 +37,10 @@ fn test_vm_lib_lua() { assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '0.2.0') assert(#bp3d.lua.patches == 5) - local flag, func = bp3d.lua.loadstring('return 1 + 1') + local flag, func = bp3d.lua.loadString('return 1 + 1') assert(flag) assert(func() == 2) - local flag, err = bp3d.lua.loadstring('ret a + 2') + local flag, err = bp3d.lua.loadString('ret a + 2') assert(not flag) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") ").unwrap(); From 47150f4170aaf00e665ed25bacc6d53b7b26bdc4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 15:41:20 +0200 Subject: [PATCH 161/527] Added test for runString --- core/tests/test_vm_libs.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index f73fcee..34c94e4 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -43,6 +43,9 @@ fn test_vm_lib_lua() { local flag, err = bp3d.lua.loadString('ret a + 2') assert(not flag) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") + local flag, res = bp3d.lua.runString('return 1 + 1') + assert(flag) + assert(res == 2) ").unwrap(); assert_eq!(vm.top(), top); } From 37ef918b32fbe3085a9931dde429d589a95ded59 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 15:43:57 +0200 Subject: [PATCH 162/527] Updated version --- core/Cargo.toml | 2 +- testbin/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index cd573ec..b488510 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua" -version = "0.2.0" +version = "1.0.0-rc.1.0.0" authors = ["Yuri Edward "] edition = "2021" description = "Lua wrapper and base library for BP3D." diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index 82d5725..67a6fa1 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -8,5 +8,5 @@ publish = false [dependencies] mlua = { version = "0.10.3", features = ["luajit", "vendored"] } -bp3d-lua = { version = "0.2.0", path = "../core" } +bp3d-lua = { version = "1.0.0-rc.1.0.0", path = "../core" } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } From 9d0265bc75036544e14bf107cabdf9598dc15d22 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 15:44:34 +0200 Subject: [PATCH 163/527] Fixed test --- core/tests/test_vm_libs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 34c94e4..bf19eba 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -35,7 +35,7 @@ fn test_vm_lib_lua() { bp3d_lua::libs::lua::register(&vm).unwrap(); vm.run_code::<()>(c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '0.2.0') + assert(bp3d.lua.version == '1.0.0-rc.1.0.0') assert(#bp3d.lua.patches == 5) local flag, func = bp3d.lua.loadString('return 1 + 1') assert(flag) From 9992764d0023f77f78b9adf271c57d7ab994703a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 16:01:31 +0200 Subject: [PATCH 164/527] Renamed RootVm::leak to RootVm::attach_box --- core/src/vm/closure/types.rs | 2 +- core/src/vm/core/vm.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 199d568..9854ede 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -63,7 +63,7 @@ unsafe impl IntoLua for RClosure { impl RClosure<*const c_void> { pub fn from_rust R + 'static>(root: &mut RootVm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam { - let ptr = root.leak(Box::new(fun)); + let ptr = root.attach_box(Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam { let vm = unsafe { Vm::from_raw(l) }; diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 178bb65..d030e4d 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -187,7 +187,7 @@ impl RootVm { } } - pub fn leak(&mut self, bx: Box) -> *mut T { + pub fn attach_box(&mut self, bx: Box) -> *mut T { let ptr = Box::into_raw(bx); self.leaked.push(Box::new(move || { unsafe { drop(Box::from_raw(ptr)) }; From 59337362022eaef997502c8bdc7050954e30826b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 16:02:21 +0200 Subject: [PATCH 165/527] Fixed build errors in decl_closure --- core/src/macros/closure.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/macros/closure.rs b/core/src/macros/closure.rs index 62fff24..0f8a0e3 100644 --- a/core/src/macros/closure.rs +++ b/core/src/macros/closure.rs @@ -31,9 +31,9 @@ macro_rules! decl_closure { ( $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? |$upvalue_name: ident: $upvalue_ty: ty| ($name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { - $vis fn $fn_name(upvalue: <$upvalue_ty as $crate::vm::closure::FromUpvalue>::Into) -> $crate::vm::closure::types::RClosure<<$upvalue_ty as $crate::vm::closure::FromUpvalue>::Into> { + $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func($name: &$crate::vm::Vm, $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($name: &$($lifetime)? $crate::vm::Vm, $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] @@ -53,7 +53,7 @@ macro_rules! decl_closure { ) => { $vis fn $fn_name(upvalue: $upvalue_ty) -> $crate::vm::closure::types::RClosure<$upvalue_ty> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func<'a>($upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>, $($arg_name: $arg_ty),*) -> $ret_ty $code + fn _func $(<$lifetime>)? ($upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_>, $($arg_name: $arg_ty),*) -> $ret_ty $code use $crate::vm::function::IntoParam; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] From 00e4a4490dbe93b95f0ed6701650e609068c0ad9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Apr 2025 16:39:11 +0200 Subject: [PATCH 166/527] Added support for &Path and &OsStr to closures --- core/src/util.rs | 5 ++++- core/src/vm/closure/core.rs | 38 +++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/core/src/util.rs b/core/src/util.rs index 7538c03..414f485 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -29,7 +29,8 @@ //! Generic rust utilities module. use std::borrow::Cow; -use std::ffi::{CStr, CString}; +use std::ffi::{CStr, CString, OsStr}; +use std::path::Path; pub trait AnyStr { fn to_str(&self) -> crate::vm::Result>; @@ -58,3 +59,5 @@ unsafe impl SimpleDrop for Option {} unsafe impl SimpleDrop for Result {} unsafe impl SimpleDrop for &T {} unsafe impl SimpleDrop for &[u8] {} +unsafe impl SimpleDrop for &OsStr { } +unsafe impl SimpleDrop for &Path { } diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index e747f60..e472132 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::OsStr; +use std::path::Path; use crate::ffi::lua::{lua_pushlightuserdata, lua_topointer, GLOBALSINDEX}; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; @@ -115,3 +117,39 @@ impl Upvalue for *mut T { impl Upvalue for *const T { type From<'a> = *const T; } + +impl<'a> FromUpvalue<'a> for &'a OsStr { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + OsStr::from_encoded_bytes_unchecked(FromUpvalue::from_upvalue(vm, index)) + } +} + +impl IntoUpvalue for &OsStr { + #[inline(always)] + fn into_upvalue(self, vm: &Vm) -> u16 { + self.as_encoded_bytes().into_upvalue(vm) + } +} + +impl Upvalue for &OsStr { + type From<'a> = &'a OsStr; +} + +impl<'a> FromUpvalue<'a> for &'a Path { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + Path::new(OsStr::from_encoded_bytes_unchecked(FromUpvalue::from_upvalue(vm, index))) + } +} + +impl IntoUpvalue for &Path { + #[inline(always)] + fn into_upvalue(self, vm: &Vm) -> u16 { + self.as_os_str().into_upvalue(vm) + } +} + +impl Upvalue for &Path { + type From<'a> = &'a Path; +} From a23171fe4fa1bbfee14c02be8aa0a80716054f31 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 05:49:00 +0200 Subject: [PATCH 167/527] Added support for new Raw trait to attach any type with destructor to RootVm --- core/src/vm/closure/types.rs | 2 +- core/src/vm/core/interface.rs | 40 +++++++++++++++++++++++++++++++++++ core/src/vm/core/vm.rs | 10 ++++----- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 9854ede..2e2b6d8 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -63,7 +63,7 @@ unsafe impl IntoLua for RClosure { impl RClosure<*const c_void> { pub fn from_rust R + 'static>(root: &mut RootVm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam { - let ptr = root.attach_box(Box::new(fun)); + let ptr = root.attach(Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam { let vm = unsafe { Vm::from_raw(l) }; diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs index 1580fe2..da6ac16 100644 --- a/core/src/vm/core/interface.rs +++ b/core/src/vm/core/interface.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::rc::Rc; use crate::ffi::lua::{State, ThreadStatus}; pub trait LoadString { @@ -35,3 +36,42 @@ pub trait LoadString { pub trait Load { fn load(self, l: State) -> ThreadStatus; } + +/// This trait represents a value which can be attached to a [RootVm](crate::vm::RootVm). +pub trait Raw { + type Ptr: Copy; + + fn into_raw(self) -> Self::Ptr; + + /// Deletes the raw pointer. + /// + /// # Safety + /// + /// This function must be called with the same pointer that originated from the same type using + /// the [into_raw](Raw::into_raw) method. + unsafe fn delete(ptr: Self::Ptr); +} + +impl Raw for Box { + type Ptr = *mut T; + + fn into_raw(self) -> Self::Ptr { + Box::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + drop(Box::from_raw(ptr)) + } +} + +impl Raw for Rc { + type Ptr = *const T; + + fn into_raw(self) -> Self::Ptr { + Rc::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + drop(Rc::from_raw(ptr)) + } +} diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index d030e4d..891f957 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -32,7 +32,7 @@ use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; -use crate::vm::core::{Load, LoadString}; +use crate::vm::core::{Load, LoadString, Raw}; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; @@ -175,7 +175,7 @@ pub struct RootVm { impl RootVm { pub fn new() -> RootVm { - if HAS_VM.with(|v| v.get()) { + if HAS_VM.get() { panic!("A VM already exists for this thread.") } let l = unsafe { luaL_newstate() }; @@ -187,10 +187,10 @@ impl RootVm { } } - pub fn attach_box(&mut self, bx: Box) -> *mut T { - let ptr = Box::into_raw(bx); + pub fn attach(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { + let ptr = R::into_raw(raw); self.leaked.push(Box::new(move || { - unsafe { drop(Box::from_raw(ptr)) }; + unsafe { R::delete(ptr) }; })); ptr } From 9ee712d3184611c01b678f3c59aef34cb5ea25c2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 05:49:56 +0200 Subject: [PATCH 168/527] Added support to plug Rc into closures --- core/src/vm/closure/mod.rs | 1 + core/src/vm/closure/rc.rs | 74 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 core/src/vm/closure/rc.rs diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 4e874bd..5ab1f68 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -30,5 +30,6 @@ mod interface; mod core; pub mod types; pub mod context; +pub mod rc; pub use interface::*; diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs new file mode 100644 index 0000000..7947cb8 --- /dev/null +++ b/core/src/vm/closure/rc.rs @@ -0,0 +1,74 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ops::Deref; +use crate::util::SimpleDrop; +use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::{RootVm, Vm}; + +#[repr(transparent)] +pub struct Rc(*const T); + +#[repr(transparent)] +pub struct Ref<'a, T>(&'a T); + +unsafe impl SimpleDrop for Ref<'_, T> { } + +impl<'a, T> Deref for Ref<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T> FromUpvalue<'a> for Ref<'a, T> { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let ptr: *const T = FromUpvalue::from_upvalue(vm, index); + Ref(&*ptr) + } +} + +impl Upvalue for Rc { + type From<'a> = Ref<'a, T>; +} + +impl IntoUpvalue for Rc { + #[inline(always)] + fn into_upvalue(self, vm: &Vm) -> u16 { + self.0.into_upvalue(vm) + } +} + +impl Rc { + #[inline(always)] + pub fn from_rust(root: &mut RootVm, rc: std::rc::Rc) -> Rc { + Rc(root.attach(rc)) + } +} From 46838110cf3e8d74290af59630ab912ca789b8c3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 07:15:53 +0200 Subject: [PATCH 169/527] Fixed leakage in Namespace tool --- core/src/vm/namespace.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/core/src/vm/namespace.rs b/core/src/vm/namespace.rs index 0dc74ec..bb05972 100644 --- a/core/src/vm/namespace.rs +++ b/core/src/vm/namespace.rs @@ -40,18 +40,21 @@ pub struct Namespace<'a> { impl<'a> Namespace<'a> { fn from_table<'b>(vm: &'a Vm, table: Table<'a>, names: impl Iterator) -> crate::vm::Result { let mut key = table.registry_put(vm); - for name in names { - let mut table = key.push(vm); - let tbl: Option
= table.get_field(name)?; - let tab = match tbl { - Some(v) => v, - None => { - table.set_field(name, Table::new(vm))?; - table.get_field(name)? - } - }; - key = tab.registry_swap(vm, key); - } + let key = vm.scope(|vm| { + for name in names { + let mut table = key.push(vm); + let tbl: Option
= table.get_field(name)?; + let tab = match tbl { + Some(v) => v, + None => { + table.set_field(name, Table::new(vm))?; + table.get_field(name)? + } + }; + key = tab.registry_swap(vm, key); + } + Ok(key) + })?; let table = key.push(vm); key.delete(vm); Ok(Self { vm, table }) From ef3f37718049b7e692a1d491c93a608795f105ef Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 07:23:12 +0200 Subject: [PATCH 170/527] Added initial support for require function --- core/src/libs/lua/mod.rs | 35 ++++++++------- core/src/libs/lua/options.rs | 54 ++++++++++++++++++++++ core/src/libs/lua/require.rs | 87 ++++++++++++++++++++++++++++++++++++ 3 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 core/src/libs/lua/options.rs create mode 100644 core/src/libs/lua/require.rs diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index f88ca1d..4102133 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -30,6 +30,10 @@ use crate::vm::namespace::Namespace; use crate::vm::table::Table; mod load; +pub mod require; +mod options; + +pub use options::Options; const PATCH_LIST: &[&str] = &[ "disable_lua_load", @@ -39,20 +43,19 @@ const PATCH_LIST: &[&str] = &[ "lua_load_no_bc" ]; -pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { - vm.scope(|vm| { - load::register(vm)?; - let mut namespace = Namespace::new(vm, "bp3d.lua")?; - namespace.add([ - ("name", "bp3d-lua"), - ("version", env!("CARGO_PKG_VERSION")) - ])?; - let mut patches = Table::with_capacity(vm, PATCH_LIST.len(), 0); - for (i, name) in PATCH_LIST.into_iter().enumerate() { - // Lua indices starts at 1 not 0. - patches.set((i + 1) as _, *name)?; - } - namespace.add([("patches", patches)])?; - Ok(()) - }) +pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Result<()> { + require::register(root, options.provider.unwrap_or_default())?; + load::register(root)?; + let mut namespace = Namespace::new(root, "bp3d.lua")?; + namespace.add([ + ("name", "bp3d-lua"), + ("version", env!("CARGO_PKG_VERSION")) + ])?; + let mut patches = Table::with_capacity(root, PATCH_LIST.len(), 0); + for (i, name) in PATCH_LIST.into_iter().enumerate() { + // Lua indices starts at 1 not 0. + patches.set((i + 1) as _, *name)?; + } + namespace.add([("patches", patches)])?; + Ok(()) } diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs new file mode 100644 index 0000000..ad73bc9 --- /dev/null +++ b/core/src/libs/lua/options.rs @@ -0,0 +1,54 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::Path; +use crate::libs::lua::require::Provider; + +pub struct Options<'a> { + pub(super) load_chroot_path: Option<&'a Path>, + pub(super) provider: Option> +} + +impl<'a> Options<'a> { + pub fn new() -> Self { + Self { + load_chroot_path: None, + provider: None + } + } + + pub fn load_chroot_path(mut self, path: &'a Path) -> Self { + self.load_chroot_path = Some(path); + self + } + + pub fn provider(mut self, provider: std::rc::Rc) -> Self { + self.provider = Some(provider); + self + } +} diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs new file mode 100644 index 0000000..c0d83b9 --- /dev/null +++ b/core/src/libs/lua/require.rs @@ -0,0 +1,87 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::RefCell; +use std::collections::HashMap; +use bp3d_util::simple_error; +use crate::decl_closure; +use crate::vm::closure::rc::Rc; +use crate::vm::value::any::{AnyParam, AnyValue, UncheckedAnyReturn}; +use crate::vm::{RootVm, Vm}; +use crate::vm::namespace::Namespace; +use crate::vm::value::FromLua; + +simple_error! { + pub Error { + (impl From) Vm(crate::vm::error::Error) => "lua error: {}", + InvalidSyntax => "invalid syntax for require", + UnknownSource(String) => "unknown source name {}" + } +} + +pub trait Source { + fn run(&self, vm: &Vm, path: &str) -> crate::vm::Result; +} + +#[derive(Default)] +pub struct Provider(RefCell>>); + +impl Provider { + pub fn new() -> Self { + Provider::default() + } + + pub fn add_source(&self, name: String, source: S) { + let mut guard = self.0.borrow_mut(); + guard.insert(name, Box::new(source)); + } + + pub fn require(&self, vm: &Vm, path: &str) -> Result { + let id = path.find('.').ok_or(Error::InvalidSyntax)?; + let source = &path[..id]; + let guard = self.0.borrow(); + let src = guard.get(source).ok_or_else(|| Error::UnknownSource(source.into()))?; + let ret = src.run(vm, path)?; + Ok(ret) + } +} + +decl_closure! { + fn require |provider: Rc| (vm: &Vm, path: &str) -> Result { + let top = vm.top(); + provider.require(vm, path)?; + unsafe { Ok(UncheckedAnyReturn::new(vm, (vm.top() - top) as _)) } + } +} + +pub(super) fn register(root: &mut RootVm, provider: std::rc::Rc) -> crate::vm::Result<()> { + let rc = Rc::from_rust(root, provider); + let mut namespace = Namespace::new(root, "bp3d.lua")?; + namespace.add([("require", require(rc))])?; + Ok(()) +} From 2070803eb0042c69ac4196e93d033f4308ead635 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 07:23:59 +0200 Subject: [PATCH 171/527] Greatly simplified run_string and load_string --- core/src/libs/lua/load.rs | 52 +++++++++++++-------------------------- 1 file changed, 17 insertions(+), 35 deletions(-) diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 74656ea..01d5e0c 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -26,68 +26,50 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::decl_lib_func; +use std::path::Path; +use crate::{decl_closure, decl_lib_func}; use crate::vm::core::load::Code; -use crate::vm::function::IntoParam; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::LuaFunction; use crate::vm::Vm; -enum FunctionOrString<'a> { - Func(LuaFunction<'a>), - String(String) -} - -unsafe impl IntoParam for FunctionOrString<'_> { - fn into_param(self, vm: &Vm) -> u16 { - match self { - FunctionOrString::Func(v) => v.into_param(vm), - FunctionOrString::String(v) => v.into_param(vm) - } - } -} - decl_lib_func! { - fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> UncheckedAnyReturn { + fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> crate::vm::Result { let top = vm.top(); - true.into_param(vm); let ret = match chunkname { None => vm.run_code::(s), Some(name) => vm.run::(Code::new(name, s.as_bytes())) }; - match ret { - Ok(_) => { - let new_top = vm.top(); - unsafe { UncheckedAnyReturn::new(vm, (new_top - top) as _) } - }, - Err(e) => { - let s = e.to_string(); - false.into_param(vm); - s.into_param(vm); - unsafe { UncheckedAnyReturn::new(vm, 2) } - } - } + ret.map(|_| unsafe { UncheckedAnyReturn::new(vm, (vm.top() - top) as _) }) } } decl_lib_func! { - fn load_string<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (bool, FunctionOrString<'a>) { + fn load_string<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (Option>, Option) { match chunkname { None => match vm.load_code(s) { - Ok(v) => (true, FunctionOrString::Func(v)), - Err(v) => (false, FunctionOrString::String(v.to_string())) + Ok(v) => (Some(v), None), + Err(v) => (None, Some(v.to_string())) }, Some(name) => match vm.load(Code::new(name, s.as_bytes())) { - Ok(v) => (true, FunctionOrString::Func(v)), - Err(v) => (false, FunctionOrString::String(v.to_string())) + Ok(v) => (Some(v), None), + Err(v) => (None, Some(v.to_string())) } } } } +decl_closure! { + fn load_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> (Option>, Option) { + + todo!() + } +} + pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([ ("runString", RFunction::wrap(run_string)), From 10cf452c16d65f29aaa5cf8afc2a7cdee1eceacf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 07:24:05 +0200 Subject: [PATCH 172/527] Updated tests --- core/tests/test_vm_libs.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index bf19eba..ab530ec 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -26,26 +26,27 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_lua::libs::lua::Options; use bp3d_lua::vm::RootVm; #[test] fn test_vm_lib_lua() { - let vm = RootVm::new(); + let mut vm = RootVm::new(); let top = vm.top(); - bp3d_lua::libs::lua::register(&vm).unwrap(); + bp3d_lua::libs::lua::register(&mut vm, Options::new()).unwrap(); vm.run_code::<()>(c" assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') assert(#bp3d.lua.patches == 5) - local flag, func = bp3d.lua.loadString('return 1 + 1') - assert(flag) + local func = bp3d.lua.loadString('return 1 + 1') + assert(func) assert(func() == 2) - local flag, err = bp3d.lua.loadString('ret a + 2') - assert(not flag) + local func, err = bp3d.lua.loadString('ret a + 2') + assert(func == nil) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") - local flag, res = bp3d.lua.runString('return 1 + 1') - assert(flag) - assert(res == 2) + assert(bp3d.lua.runString('return 1 + 1') == 2) ").unwrap(); + let err = vm.run_code::<()>(c"bp3d.lua.require \"not.existing.file\"").unwrap_err().into_runtime().unwrap(); + assert_eq!(err.msg(), "rust error: unknown source name not"); assert_eq!(vm.top(), top); } From 3128016a248e459604b33f072152d59a1b790022 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 07:42:57 +0200 Subject: [PATCH 173/527] Added bp3d.lua.pcall function which supports backtrace information --- core/src/libs/lua/call.rs | 59 ++++++++++++++++++++++++++++++++++++++ core/src/libs/lua/mod.rs | 2 ++ core/tests/test_vm_libs.rs | 9 ++++++ 3 files changed, 70 insertions(+) create mode 100644 core/src/libs/lua/call.rs diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs new file mode 100644 index 0000000..3db15a1 --- /dev/null +++ b/core/src/libs/lua/call.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::vm::error::Error; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; +use crate::vm::value::function::LuaFunction; +use crate::vm::Vm; + +decl_lib_func! { + fn pcall(vm: &Vm, func: LuaFunction) -> UncheckedAnyReturn { + let top = vm.top(); + true.into_param(vm); + let ret = func.call::(()); + let new_top = vm.top(); + match ret { + Ok(_) => unsafe { UncheckedAnyReturn::new(vm, (new_top - top) as _) }, + Err(e) => { + match e { + Error::Runtime(e) => unsafe { UncheckedAnyReturn::new(vm, (false, e.backtrace()).into_param(vm)) }, + e => unsafe { UncheckedAnyReturn::new(vm, (false, e.to_string()).into_param(vm)) } + } + } + } + } +} + +pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.lua")?; + namespace.add([("pcall", RFunction::wrap(pcall))])?; + Ok(()) +} diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 4102133..5a05e25 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -32,6 +32,7 @@ use crate::vm::table::Table; mod load; pub mod require; mod options; +mod call; pub use options::Options; @@ -46,6 +47,7 @@ const PATCH_LIST: &[&str] = &[ pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Result<()> { require::register(root, options.provider.unwrap_or_default())?; load::register(root)?; + call::register(root)?; let mut namespace = Namespace::new(root, "bp3d.lua")?; namespace.add([ ("name", "bp3d-lua"), diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index ab530ec..3b78b2c 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -48,5 +48,14 @@ fn test_vm_lib_lua() { ").unwrap(); let err = vm.run_code::<()>(c"bp3d.lua.require \"not.existing.file\"").unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); + vm.run_code::<()>(c" + local function test() + bp3d.lua.require \"not.existing.file\" + end + local flag, err = bp3d.lua.pcall(test) + assert(not flag) + print(err) + assert(err ~= '') + ").unwrap(); assert_eq!(vm.top(), top); } From 1ff37a06773df5509bb76d957451d9a985ba66a0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 22:10:50 +0200 Subject: [PATCH 174/527] Added support for loadFile and runFile behind a chroot --- core/build.rs | 1 + core/src/libs/lua/load.rs | 62 ++++++++++++++++++++++++++++++++---- core/src/libs/lua/mod.rs | 2 +- core/src/libs/lua/require.rs | 3 +- core/src/libs/mod.rs | 4 +++ 5 files changed, 63 insertions(+), 9 deletions(-) diff --git a/core/build.rs b/core/build.rs index 2717b86..59d0e5d 100644 --- a/core/build.rs +++ b/core/build.rs @@ -109,6 +109,7 @@ fn main() { apply_patch(&path, "disable_lua_load.patch"); // Disable loadstring, dostring, etc from base lib. apply_patch(&path, "lua_ext.patch"); // Ext library such as lua_ext_tab_len, etc. apply_patch(&path, "lua_load_no_bc.patch"); // Treat all inputs as strings (no bytecode allowed). + //TODO: Patch to re-enable os lib with time and date // Copy the source directory to the build directory. println!("{}", out_path.display()); diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 01d5e0c..57f37ec 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -26,9 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::Path; +use std::path::{Path, PathBuf}; +use bp3d_util::simple_error; use crate::{decl_closure, decl_lib_func}; -use crate::vm::core::load::Code; +use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; @@ -61,18 +62,67 @@ decl_lib_func! { } } +simple_error! { + Error { + EscapeChroot(String) => "invalid path {}: attempt to escape chroot", + (impl From) Io(std::io::Error) => "io error: {}", + (impl From) Vm(crate::vm::error::Error) => "lua error: {}" + } +} + +fn parse_lua_path(chroot: &Path, path: &str) -> Result { + let iter = path.split("/"); + let mut cur_path = Vec::new(); + for component in iter { + if component == ".." { + if let None = cur_path.pop() { + return Err(Error::EscapeChroot(path.into())); + } + } else if component != "." { + cur_path.push(component); + } + } + Ok(chroot.join(path)) +} + decl_closure! { fn load_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> (Option>, Option) { - - todo!() + let path = match parse_lua_path(chroot, path) { + Ok(v) => v, + Err(e) => return (None, Some(e.to_string())) + }; + let script = match Script::from_path(path) { + Ok(v) => v, + Err(e) => return (None, Some(e.to_string())) + }; + match vm.load(script) { + Ok(v) => (Some(v), None), + Err(e) => (None, Some(e.to_string())) + } } } -pub fn register(vm: &Vm) -> crate::vm::Result<()> { +decl_closure! { + fn run_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> Result { + let path = parse_lua_path(chroot, path)?; + let script = Script::from_path(path)?; + let top = vm.top(); + vm.run::(script)?; + unsafe { Ok(UncheckedAnyReturn::new(vm, (vm.top() - top) as _)) } + } +} +pub fn register(vm: &Vm, chroot: Option<&Path>) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([ ("runString", RFunction::wrap(run_string)), ("loadString", RFunction::wrap(load_string)) - ]) + ])?; + if let Some(chroot) = chroot { + namespace.add([ + ("loadFile", load_file(chroot)), + ("runFile", run_file(chroot)) + ])?; + } + Ok(()) } diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 5a05e25..f17943e 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -46,7 +46,7 @@ const PATCH_LIST: &[&str] = &[ pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Result<()> { require::register(root, options.provider.unwrap_or_default())?; - load::register(root)?; + load::register(root, options.load_chroot_path)?; call::register(root)?; let mut namespace = Namespace::new(root, "bp3d.lua")?; namespace.add([ diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index c0d83b9..030fb3c 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -31,10 +31,9 @@ use std::collections::HashMap; use bp3d_util::simple_error; use crate::decl_closure; use crate::vm::closure::rc::Rc; -use crate::vm::value::any::{AnyParam, AnyValue, UncheckedAnyReturn}; +use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::{RootVm, Vm}; use crate::vm::namespace::Namespace; -use crate::vm::value::FromLua; simple_error! { pub Error { diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index 2543c7f..e6d9fcc 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -27,3 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod lua; +//TODO: maybe add a stack debug function which prints the content of the lua stack +//TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) +// make sure thread join is time-limited. +//TODO: utf8 lib with string functions operating on UTF8-strings From 1d8e02016a7603c5eead766c11f5b8439b22a8f2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 7 Apr 2025 22:32:52 +0200 Subject: [PATCH 175/527] Tweaked inline and memory representations for performance --- core/src/vm/core/vm.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 891f957..8331ed4 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -40,6 +40,7 @@ use crate::vm::userdata::UserData; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::function::LuaFunction; +#[repr(transparent)] pub struct Vm { l: State } @@ -199,12 +200,14 @@ impl RootVm { impl Deref for RootVm { type Target = Vm; + #[inline(always)] fn deref(&self) -> &Self::Target { &self.vm } } impl DerefMut for RootVm { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.vm } From 506931850fb684478d8fbdd47e90604323859114 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 21:37:06 +0200 Subject: [PATCH 176/527] Added check to avoid breaking lua stack if into_lua returns 0. --- core/src/vm/table/core.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 984d1c4..e43f313 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -118,7 +118,7 @@ impl<'a> Table<'a> { pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let nums = value.into_lua(self.vm); - if nums > 1 { + if nums != 1 { // Clear the stack. lua_settop(self.vm.as_ptr(), -(nums as i32)-1); return Err(crate::vm::error::Error::MultiValue); @@ -129,7 +129,7 @@ impl<'a> Table<'a> { } pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { - if T::num_values() > 1 { + if T::num_values() != 1 { return Err(crate::vm::error::Error::MultiValue); } unsafe { @@ -141,7 +141,7 @@ impl<'a> Table<'a> { pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let nums = value.into_lua(self.vm); - if nums > 1 { + if nums != 1 { // Clear the stack. lua_settop(self.vm.as_ptr(), -(nums as i32)-1); return Err(crate::vm::error::Error::MultiValue); @@ -152,7 +152,7 @@ impl<'a> Table<'a> { } pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { - if T::num_values() > 1 { + if T::num_values() != 1 { return Err(crate::vm::error::Error::MultiValue); } unsafe { From b8eefcd81debd1038c24b10bc5e30391c1a2f399 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:00:50 +0200 Subject: [PATCH 177/527] Fixed bug in table iterator --- core/src/vm/table/iter.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 122be2c..6d4332b 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -58,8 +58,8 @@ impl<'a> Iterator for Iter<'a> { let ret = unsafe { lua_next(self.vm.as_ptr(), self.index) }; self.has_started = true; if ret != 0 { - let value = AnyValue::from_lua(self.vm, -1); - let key = AnyValue::from_lua(self.vm, -2); + let value = AnyValue::from_lua(self.vm, -2); + let key = AnyValue::from_lua(self.vm, -1); Some(match (value, key) { (Ok(key), Ok(value)) => Ok((key, value)), (Ok(_), Err(e)) => Err(e), From 49e073027ebde4f4b830389ea8f66996d134af1e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:01:31 +0200 Subject: [PATCH 178/527] Added support for IntoLua and ty to AnyValue --- core/src/vm/value/any.rs | 43 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 40 insertions(+), 3 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 523f4dd..1052e02 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_toboolean, lua_tonumber, lua_type, Type}; +use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; use crate::vm::error::Error; use crate::vm::function::IntoParam; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; use crate::vm::thread::Thread; @@ -49,6 +49,43 @@ pub enum AnyValue<'a> { Thread(Thread<'a>) } +impl AnyValue<'_> { + pub fn ty(&self) -> Type { + match self { + AnyValue::None => Type::None, + AnyValue::Nil => Type::Nil, + AnyValue::Number(_) => Type::Number, + AnyValue::Boolean(_) => Type::Boolean, + AnyValue::String(_) => Type::String, + AnyValue::Buffer(_) => Type::String, + AnyValue::Function(_) => Type::Function, + AnyValue::Table(_) => Type::Table, + AnyValue::UserData(_) => Type::Userdata, + AnyValue::Thread(_) => Type::Thread + } + } +} + +unsafe impl IntoLua for AnyValue<'_> { + fn into_lua(self, vm: &Vm) -> u16 { + match self { + AnyValue::None => 0, + AnyValue::Nil => { + unsafe { lua_pushnil(vm.as_ptr()) }; + 1 + }, + AnyValue::Number(v) => v.into_lua(vm), + AnyValue::Boolean(v) => v.into_lua(vm), + AnyValue::String(v) => v.into_lua(vm), + AnyValue::Buffer(v) => v.into_lua(vm), + AnyValue::Function(v) => v.into_lua(vm), + AnyValue::Table(v) => v.into_lua(vm), + AnyValue::UserData(_) => 0, + AnyValue::Thread(_) => 0 + } + } +} + impl<'a> FromLua<'a> for AnyValue<'a> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self::from_lua(vm, index).unwrap_unchecked() @@ -105,7 +142,7 @@ pub struct UncheckedAnyReturn(u16); impl UncheckedAnyReturn { /// Construct a [UncheckedAnyReturn]. - /// + /// /// # Panic /// /// This function panics when the count of arguments is greater than the lua stack size itself. From fa60cd117735c92e98bcc8124ed660895cdf91a3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:09:58 +0200 Subject: [PATCH 179/527] Moved load related functions to sub-namespace load --- core/src/libs/lua/load.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 57f37ec..1f226a2 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -113,7 +113,7 @@ decl_closure! { } pub fn register(vm: &Vm, chroot: Option<&Path>) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.lua")?; + let mut namespace = Namespace::new(vm, "bp3d.lua.load")?; namespace.add([ ("runString", RFunction::wrap(run_string)), ("loadString", RFunction::wrap(load_string)) From 6d5e22c34bc7f70d49f22023926af6c528b60072 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:24:47 +0200 Subject: [PATCH 180/527] Added support for Debug and Display implementations to AnyValue --- core/src/vm/table/core.rs | 15 ++++++++++++++- core/src/vm/thread.rs | 15 ++++++++++++++- core/src/vm/userdata/any.rs | 15 ++++++++++++++- core/src/vm/value/any.rs | 19 +++++++++++++++++++ core/src/vm/value/function.rs | 15 ++++++++++++++- 5 files changed, 75 insertions(+), 4 deletions(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index e43f313..e2cb4cd 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::{Debug, Display}; use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_topointer}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; @@ -39,6 +40,18 @@ pub struct Table<'a> { index: i32 } +impl Display for Table<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "table@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) + } +} + +impl Debug for Table<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Table({:?})", self.index) + } +} + impl<'a> Table<'a> { /// Creates a table from a raw Vm and index. /// diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 10c2907..7f8d607 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -26,9 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::{Debug, Display}; use std::marker::PhantomData; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; +use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_topointer, lua_tothread, ThreadStatus, Type}; use crate::util::SimpleDrop; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::FromParam; @@ -48,6 +49,18 @@ pub struct Thread<'a> { useless: PhantomData<&'a ()> } +impl Display for Thread<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "thread@{:X}", unsafe { std::mem::transmute::<_, usize>(self.vm.as_ptr()) }) + } +} + +impl Debug for Thread<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str("Thread") + } +} + impl<'a> Thread<'a> { #[inline(always)] pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadString) -> crate::vm::Result { diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index b658f92..36cb477 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_type, Type}; +use crate::ffi::lua::{lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::FromLua; @@ -38,6 +39,18 @@ pub struct AnyUserData<'a> { index: i32 } +impl Display for AnyUserData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "userdata@{:X}", unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize) + } +} + +impl Debug for AnyUserData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "UserData({:?})", self.index) + } +} + impl<'a> AnyUserData<'a> { /// Creates an AnyUserData from a raw Vm and index. /// diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 1052e02..4b6e695 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::Display; use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; use crate::vm::error::Error; use crate::vm::function::IntoParam; @@ -36,6 +37,7 @@ use crate::vm::thread::Thread; use crate::vm::userdata::AnyUserData; use crate::vm::Vm; +#[derive(Debug)] pub enum AnyValue<'a> { None, Nil, @@ -49,6 +51,23 @@ pub enum AnyValue<'a> { Thread(Thread<'a>) } +impl Display for AnyValue<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyValue::None => f.write_str(""), + AnyValue::Nil => f.write_str("nil"), + AnyValue::Number(v) => write!(f, "{}", v), + AnyValue::Boolean(v) => write!(f, "{}", v), + AnyValue::String(v) => write!(f, "{}", v), + AnyValue::Buffer(v) => write!(f, "{:?}", v), + AnyValue::Function(v) => write!(f, "{}", v), + AnyValue::Table(v) => write!(f, "{}", v), + AnyValue::UserData(v) => write!(f, "{}", v), + AnyValue::Thread(v) => write!(f, "{}", v) + } + } +} + impl AnyValue<'_> { pub fn ty(&self) -> Type { match self { diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 05dfa0d..d6ea8cd 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_pushvalue, Type}; +use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; use crate::util::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; @@ -43,6 +44,18 @@ pub struct LuaFunction<'a> { index: i32 } +impl Display for LuaFunction<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "function@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) + } +} + +impl Debug for LuaFunction<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "LuaFunction({:?})", self.index) + } +} + unsafe impl SimpleDrop for LuaFunction<'_> { } impl LuaType for LuaFunction<'_> { } From 40890949b66485d56610e2957973d47e865d727e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:37:01 +0200 Subject: [PATCH 181/527] Removed unused import --- core/src/vm/thread.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 7f8d607..51928ea 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -29,7 +29,7 @@ use std::fmt::{Debug, Display}; use std::marker::PhantomData; use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_topointer, lua_tothread, ThreadStatus, Type}; +use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; use crate::util::SimpleDrop; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::FromParam; From 261429a911299e5864e82d5f43468901374ae156 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:39:36 +0200 Subject: [PATCH 182/527] Added mini table toolkit --- core/src/libs/lua/mod.rs | 2 + core/src/libs/lua/table.rs | 93 ++++++++++++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 22 +++++++-- 3 files changed, 114 insertions(+), 3 deletions(-) create mode 100644 core/src/libs/lua/table.rs diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index f17943e..098dc7d 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -33,6 +33,7 @@ mod load; pub mod require; mod options; mod call; +mod table; pub use options::Options; @@ -48,6 +49,7 @@ pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Re require::register(root, options.provider.unwrap_or_default())?; load::register(root, options.load_chroot_path)?; call::register(root)?; + table::register(root)?; let mut namespace = Namespace::new(root, "bp3d.lua")?; namespace.add([ ("name", "bp3d-lua"), diff --git a/core/src/libs/lua/table.rs b/core/src/libs/lua/table.rs new file mode 100644 index 0000000..76b89e7 --- /dev/null +++ b/core/src/libs/lua/table.rs @@ -0,0 +1,93 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::table::Table; +use crate::decl_lib_func; +use crate::ffi::lua::Type; +use crate::vm::error::{Error, TypeError}; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::value::any::AnyValue; +use crate::vm::Vm; + +decl_lib_func! { + fn update(dst: Table, src: Table) -> crate::vm::Result<()> { + let mut src = src; + let mut dst = dst; + for res in src.iter() { + let (k, v) = res?; + match k { + AnyValue::String(name) => { + dst.set_field(name, v)? + }, + AnyValue::Number(num) => dst.set(num as _, v)?, + _ => return Err(Error::Type(TypeError { + expected: Type::String, + actual: k.ty() + })) + } + } + Ok(()) + } +} + +decl_lib_func! { + fn count(src: Table) -> u64 { + src.len() as _ + } +} + +fn to_string_rec(prefix: String, mut table: Table) -> crate::vm::Result> { + let mut lines = Vec::new(); + for res in table.iter() { + let (k, v) = res?; + match v { + AnyValue::Table(v) => { + lines.push(format!("{}:", k)); + lines.extend(to_string_rec(prefix.clone() + " ", v)?); + }, + v => lines.push(format!("{}: {}", k, v)) + } + } + Ok(lines) +} + +decl_lib_func! { + fn to_string(src: Table) -> crate::vm::Result { + to_string_rec("".into(), src).map(|v| v.join("\n")) + } +} + +pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.lua.table")?; + namespace.add([ + ("update", RFunction::wrap(update)), + ("count", RFunction::wrap(count)), + ("tostring", RFunction::wrap(to_string)), + ]) +} diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 3b78b2c..b8fda7d 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -38,13 +38,13 @@ fn test_vm_lib_lua() { assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') assert(#bp3d.lua.patches == 5) - local func = bp3d.lua.loadString('return 1 + 1') + local func = bp3d.lua.load.loadString('return 1 + 1') assert(func) assert(func() == 2) - local func, err = bp3d.lua.loadString('ret a + 2') + local func, err = bp3d.lua.load.loadString('ret a + 2') assert(func == nil) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") - assert(bp3d.lua.runString('return 1 + 1') == 2) + assert(bp3d.lua.load.runString('return 1 + 1') == 2) ").unwrap(); let err = vm.run_code::<()>(c"bp3d.lua.require \"not.existing.file\"").unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); @@ -57,5 +57,21 @@ fn test_vm_lib_lua() { print(err) assert(err ~= '') ").unwrap(); + vm.run_code::<()>(c" + local src = { + a = 1, + b = 2 + } + local dst = { + c = 3 + } + bp3d.lua.table.update(dst, src) + assert(dst.a == 1) + assert(dst.b == 2) + assert(dst.c == 3) + assert(bp3d.lua.table.count(dst) == 3) + assert(bp3d.lua.table.count(src) == 2) + assert(bp3d.lua.table.tostring(dst) == 'b: 2\\na: 1\\nc: 3') + ").unwrap(); assert_eq!(vm.top(), top); } From c647f331ef3b9a76ddf14a51cf7729255f0a1728 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 22:51:53 +0200 Subject: [PATCH 183/527] Refactored table utility into its own lib --- core/src/libs/lua/load.rs | 2 +- core/src/libs/lua/mod.rs | 2 -- core/src/libs/mod.rs | 2 ++ core/src/libs/util/mod.rs | 37 ++++++++++++++++++++++++++++ core/src/libs/{lua => util}/table.rs | 2 +- core/tests/test_vm_libs.rs | 23 +++++++++++------ 6 files changed, 57 insertions(+), 11 deletions(-) create mode 100644 core/src/libs/util/mod.rs rename core/src/libs/{lua => util}/table.rs (98%) diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 1f226a2..57f37ec 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -113,7 +113,7 @@ decl_closure! { } pub fn register(vm: &Vm, chroot: Option<&Path>) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.lua.load")?; + let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([ ("runString", RFunction::wrap(run_string)), ("loadString", RFunction::wrap(load_string)) diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 098dc7d..f17943e 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -33,7 +33,6 @@ mod load; pub mod require; mod options; mod call; -mod table; pub use options::Options; @@ -49,7 +48,6 @@ pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Re require::register(root, options.provider.unwrap_or_default())?; load::register(root, options.load_chroot_path)?; call::register(root)?; - table::register(root)?; let mut namespace = Namespace::new(root, "bp3d.lua")?; namespace.add([ ("name", "bp3d-lua"), diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index e6d9fcc..37da277 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -27,6 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod lua; +pub mod util; + //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs new file mode 100644 index 0000000..84cbe65 --- /dev/null +++ b/core/src/libs/util/mod.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; + +mod table; + +pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { + table::register(vm)?; + Ok(()) +} diff --git a/core/src/libs/lua/table.rs b/core/src/libs/util/table.rs similarity index 98% rename from core/src/libs/lua/table.rs rename to core/src/libs/util/table.rs index 76b89e7..b7b0f8c 100644 --- a/core/src/libs/lua/table.rs +++ b/core/src/libs/util/table.rs @@ -84,7 +84,7 @@ decl_lib_func! { } pub fn register(vm: &Vm) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.lua.table")?; + let mut namespace = Namespace::new(vm, "bp3d.util.table")?; namespace.add([ ("update", RFunction::wrap(update)), ("count", RFunction::wrap(count)), diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index b8fda7d..a83af18 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -38,13 +38,13 @@ fn test_vm_lib_lua() { assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') assert(#bp3d.lua.patches == 5) - local func = bp3d.lua.load.loadString('return 1 + 1') + local func = bp3d.lua.loadString('return 1 + 1') assert(func) assert(func() == 2) - local func, err = bp3d.lua.load.loadString('ret a + 2') + local func, err = bp3d.lua.loadString('ret a + 2') assert(func == nil) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") - assert(bp3d.lua.load.runString('return 1 + 1') == 2) + assert(bp3d.lua.runString('return 1 + 1') == 2) ").unwrap(); let err = vm.run_code::<()>(c"bp3d.lua.require \"not.existing.file\"").unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); @@ -57,6 +57,14 @@ fn test_vm_lib_lua() { print(err) assert(err ~= '') ").unwrap(); + assert_eq!(vm.top(), top); +} + +#[test] +fn test_vm_lib_util() { + let vm = RootVm::new(); + let top = vm.top(); + bp3d_lua::libs::util::register(&vm).unwrap(); vm.run_code::<()>(c" local src = { a = 1, @@ -65,13 +73,14 @@ fn test_vm_lib_lua() { local dst = { c = 3 } - bp3d.lua.table.update(dst, src) + bp3d.util.table.update(dst, src) assert(dst.a == 1) assert(dst.b == 2) assert(dst.c == 3) - assert(bp3d.lua.table.count(dst) == 3) - assert(bp3d.lua.table.count(src) == 2) - assert(bp3d.lua.table.tostring(dst) == 'b: 2\\na: 1\\nc: 3') + assert(bp3d.util.table.count(dst) == 3) + assert(bp3d.util.table.count(src) == 2) + print(bp3d.util.table.tostring(dst)) + assert(bp3d.util.table.tostring(dst) == 'c: 3\\na: 1\\nb: 2') ").unwrap(); assert_eq!(vm.top(), top); } From 087a9e01983ebb7071668549b79694069e0dfc33 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 23:17:20 +0200 Subject: [PATCH 184/527] Added support for Eq, PartialEq, FromParam and IntoParam to AnyValue --- core/src/ffi/lua.rs | 2 +- core/src/vm/table/core.rs | 10 ++++++++++ core/src/vm/thread.rs | 8 ++++++++ core/src/vm/userdata/any.rs | 12 +++++++++++- core/src/vm/value/any.rs | 36 +++++++++++++++++++++++++++++++---- core/src/vm/value/function.rs | 10 ++++++++++ 6 files changed, 72 insertions(+), 6 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 6b9c453..0921053 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -54,7 +54,7 @@ pub enum ThreadStatus { } #[repr(transparent)] -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Eq, PartialEq)] pub struct State(*mut c_void); pub type CFunction = extern "C-unwind" fn(l: State) -> i32; diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index e2cb4cd..7d9b8cf 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -40,6 +40,16 @@ pub struct Table<'a> { index: i32 } +impl PartialEq for Table<'_> { + fn eq(&self, other: &Self) -> bool { + let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; + let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; + a == b + } +} + +impl Eq for Table<'_> { } + impl Display for Table<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "table@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 51928ea..b865449 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -49,6 +49,14 @@ pub struct Thread<'a> { useless: PhantomData<&'a ()> } +impl PartialEq for Thread<'_> { + fn eq(&self, other: &Self) -> bool { + self.vm.as_ptr() == other.vm.as_ptr() + } +} + +impl Eq for Thread<'_> { } + impl Display for Thread<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "thread@{:X}", unsafe { std::mem::transmute::<_, usize>(self.vm.as_ptr()) }) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 36cb477..0bdb477 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -28,7 +28,7 @@ use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::FromLua; @@ -39,6 +39,16 @@ pub struct AnyUserData<'a> { index: i32 } +impl PartialEq for AnyUserData<'_> { + fn eq(&self, other: &Self) -> bool { + let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; + let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; + a == b + } +} + +impl Eq for AnyUserData<'_> { } + impl Display for AnyUserData<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "userdata@{:X}", unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 4b6e695..8eb79e3 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -28,16 +28,18 @@ use std::fmt::Display; use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; +use crate::util::SimpleDrop; use crate::vm::error::Error; -use crate::vm::function::IntoParam; +use crate::vm::function::{FromParam, IntoParam}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; use crate::vm::thread::Thread; use crate::vm::userdata::AnyUserData; +use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::Vm; -#[derive(Debug)] +#[derive(Debug, PartialEq)] pub enum AnyValue<'a> { None, Nil, @@ -51,6 +53,8 @@ pub enum AnyValue<'a> { Thread(Thread<'a>) } +impl Eq for AnyValue<'_> { } + impl Display for AnyValue<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { @@ -85,8 +89,8 @@ impl AnyValue<'_> { } } -unsafe impl IntoLua for AnyValue<'_> { - fn into_lua(self, vm: &Vm) -> u16 { +unsafe impl IntoParam for AnyValue<'_> { + fn into_param(self, vm: &Vm) -> u16 { match self { AnyValue::None => 0, AnyValue::Nil => { @@ -106,6 +110,7 @@ unsafe impl IntoLua for AnyValue<'_> { } impl<'a> FromLua<'a> for AnyValue<'a> { + #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self::from_lua(vm, index).unwrap_unchecked() } @@ -139,18 +144,40 @@ impl<'a> FromLua<'a> for AnyValue<'a> { } } +unsafe impl SimpleDrop for AnyValue<'_> {} + +impl LuaType for AnyValue<'_> { } + +impl<'a> FromParam<'a> for AnyValue<'a> { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + match FromLua::from_lua(vm, index) { + Ok(v) => v, + Err(e) => lua_rust_error(vm.as_ptr(), e) + } + } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } +} + /// A marker struct to run lua code which may return any number of values on the stack. pub struct AnyParam; impl FromLua<'_> for AnyParam { + #[inline(always)] unsafe fn from_lua_unchecked(_: &Vm, _: i32) -> Self { AnyParam } + #[inline(always)] fn from_lua(_: &Vm, _: i32) -> crate::vm::Result { Ok(AnyParam) } + #[inline(always)] fn num_values() -> i16 { -1 } @@ -180,6 +207,7 @@ impl UncheckedAnyReturn { } unsafe impl IntoParam for UncheckedAnyReturn { + #[inline(always)] fn into_param(self, _: &Vm) -> u16 { self.0 } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index d6ea8cd..ecdd29a 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -44,6 +44,16 @@ pub struct LuaFunction<'a> { index: i32 } +impl PartialEq for LuaFunction<'_> { + fn eq(&self, other: &Self) -> bool { + let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; + let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; + a == b + } +} + +impl Eq for LuaFunction<'_> { } + impl Display for LuaFunction<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "function@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) From b11ed9829d0be172f8034c6e55499cdd81d94a53 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 8 Apr 2025 23:17:53 +0200 Subject: [PATCH 185/527] Added bp3d.util.table.contains and containsKey --- core/src/libs/util/mod.rs | 3 --- core/src/libs/util/table.rs | 28 ++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 8 +++++++- 3 files changed, 35 insertions(+), 4 deletions(-) diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 84cbe65..2969ab7 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -26,9 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::namespace::Namespace; -use crate::vm::table::Table; - mod table; pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index b7b0f8c..2f40fdd 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -83,11 +83,39 @@ decl_lib_func! { } } +decl_lib_func! { + fn contains(src: Table, value: AnyValue) -> crate::vm::Result { + let mut src = src; + for res in src.iter() { + let (_, v) = res?; + if v == value { + return Ok(true) + } + } + Ok(false) + } +} + +decl_lib_func! { + fn contains_key(src: Table, key: AnyValue) -> crate::vm::Result { + let mut src = src; + for res in src.iter() { + let (k, _) = res?; + if k == key { + return Ok(true) + } + } + Ok(false) + } +} + pub fn register(vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.util.table")?; namespace.add([ ("update", RFunction::wrap(update)), ("count", RFunction::wrap(count)), ("tostring", RFunction::wrap(to_string)), + ("contains", RFunction::wrap(contains)), + ("containsKey", RFunction::wrap(contains_key)) ]) } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index a83af18..16dec2b 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -79,8 +79,14 @@ fn test_vm_lib_util() { assert(dst.c == 3) assert(bp3d.util.table.count(dst) == 3) assert(bp3d.util.table.count(src) == 2) + assert(bp3d.util.table.contains(dst, 1)) + assert(bp3d.util.table.contains(dst, 2)) + assert(bp3d.util.table.contains(dst, 3)) + assert(bp3d.util.table.containsKey(dst, 'a')) + assert(bp3d.util.table.containsKey(dst, 'b')) + assert(bp3d.util.table.containsKey(dst, 'c')) print(bp3d.util.table.tostring(dst)) - assert(bp3d.util.table.tostring(dst) == 'c: 3\\na: 1\\nb: 2') + --assert(bp3d.util.table.tostring(dst) == 'c: 3\\na: 1\\nb: 2') ").unwrap(); assert_eq!(vm.top(), top); } From 9395af59c7d1c4976929f761f19bc043438b1536 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Apr 2025 00:00:31 +0200 Subject: [PATCH 186/527] Added bp3d.util.string.contains and split --- core/src/libs/util/mod.rs | 2 ++ core/src/libs/util/string.rs | 64 ++++++++++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 12 +++++-- 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 core/src/libs/util/string.rs diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 2969ab7..462b225 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -27,8 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod table; +mod string; pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { table::register(vm)?; + string::register(vm)?; Ok(()) } diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs new file mode 100644 index 0000000..41875c0 --- /dev/null +++ b/core/src/libs/util/string.rs @@ -0,0 +1,64 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; +use crate::vm::Vm; + +decl_lib_func! { + fn contains(src: &[u8], needle: &[u8]) -> bool { + if needle.len() == 0 { + return true; + } + src.windows(needle.len()).any(|window| window == needle) + } +} + +decl_lib_func! { + fn split<'a>(vm: &Vm, src: &[u8], pattern: u8) -> crate::vm::Result> { + let split = src.split(|v| *v == pattern); + let mut tbl = Table::new(vm); + for (i, v) in split.enumerate() { + // Indices starts at 1 in lua. + tbl.set((i + 1) as _, v)?; + } + Ok(tbl) + } +} + +//TODO: implement string replace + +pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.util.string")?; + namespace.add([ + ("contains", RFunction::wrap(contains)), + ("split", RFunction::wrap(split)) + ]) +} diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 16dec2b..9dd54a9 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -85,8 +85,16 @@ fn test_vm_lib_util() { assert(bp3d.util.table.containsKey(dst, 'a')) assert(bp3d.util.table.containsKey(dst, 'b')) assert(bp3d.util.table.containsKey(dst, 'c')) - print(bp3d.util.table.tostring(dst)) - --assert(bp3d.util.table.tostring(dst) == 'c: 3\\na: 1\\nb: 2') + local str = bp3d.util.table.tostring(dst) .. '\\n' + assert(bp3d.util.string.contains(str, 'a: 1\\n')) + assert(bp3d.util.string.contains(str, 'b: 2\\n')) + assert(bp3d.util.string.contains(str, 'c: 3\\n')) + local str = bp3d.util.table.tostring(dst) + local tbl = bp3d.util.string.split(str, 0x0A) + assert(#tbl == 3) + assert(bp3d.util.table.contains(tbl, 'a: 1')) + assert(bp3d.util.table.contains(tbl, 'b: 2')) + assert(bp3d.util.table.contains(tbl, 'c: 3')) ").unwrap(); assert_eq!(vm.top(), top); } From ea346f38f352597c263b91dbae1919a8cfd4064e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Apr 2025 08:22:03 +0200 Subject: [PATCH 187/527] Added check to avoid iterating over moved values --- core/src/vm/table/iter.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 6d4332b..64d98ff 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -35,14 +35,15 @@ pub struct Iter<'a> { vm: &'a Vm, index: i32, has_ended: bool, - has_started: bool + has_started: bool, + last_top: i32 } impl<'a> Iter<'a> { pub(super) fn from_raw(vm: &'a Vm, index: i32) -> Self { // Push a nil value on the stack to allow the iterator to work. unsafe { lua_pushnil(vm.as_ptr()) }; - Self { vm, index, has_ended: false, has_started: false } + Self { vm, index, has_ended: false, has_started: false, last_top: vm.top() } } } @@ -51,11 +52,16 @@ impl<'a> Iterator for Iter<'a> { fn next(&mut self) -> Option { if self.has_started { + // This ensures the iterator remains safe. + if self.vm.top() != self.last_top { + panic!("Attempt to iterate on moved values (expected Vm top: {}, got: {})", self.last_top, self.vm.top()); + } // Pop the last value on the stack which corresponds to the last value from lua_next. // Only if the iterator was started. unsafe { lua_settop(self.vm.as_ptr(), -2) }; } let ret = unsafe { lua_next(self.vm.as_ptr(), self.index) }; + self.last_top = self.vm.top(); self.has_started = true; if ret != 0 { let value = AnyValue::from_lua(self.vm, -2); From c674c40dd856c8e69793a74371da7e4968a34ded Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Apr 2025 20:47:00 +0200 Subject: [PATCH 188/527] Added initial version of utf8 lib --- core/src/libs/util/mod.rs | 2 + core/src/libs/util/string.rs | 2 - core/src/libs/util/utf8.rs | 94 ++++++++++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 14 ++++++ 4 files changed, 110 insertions(+), 2 deletions(-) create mode 100644 core/src/libs/util/utf8.rs diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 462b225..572b18a 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -28,9 +28,11 @@ mod table; mod string; +mod utf8; pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { table::register(vm)?; string::register(vm)?; + utf8::register(vm)?; Ok(()) } diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 41875c0..9082b22 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -53,8 +53,6 @@ decl_lib_func! { } } -//TODO: implement string replace - pub fn register(vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.util.string")?; namespace.add([ diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs new file mode 100644 index 0000000..bc13664 --- /dev/null +++ b/core/src/libs/util/utf8.rs @@ -0,0 +1,94 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; +use crate::vm::Vm; + +decl_lib_func! { + fn contains(src: &str, needle: &str) -> bool { + src.contains(needle) + } +} + +decl_lib_func! { + fn split<'a>(vm: &Vm, src: &str, pattern: &str) -> crate::vm::Result> { + let split = src.split(pattern); + let mut tbl = Table::new(vm); + for (i, v) in split.enumerate() { + // Indices starts at 1 in lua. + tbl.set((i + 1) as _, v)?; + } + Ok(tbl) + } +} + +decl_lib_func! { + fn replace(src: &str, pattern: &str, replacement: &str) -> String { + src.replace(pattern, replacement) + } +} + +decl_lib_func! { + fn count(src: &str) -> u32 { + src.chars().count() as u32 + } +} + +decl_lib_func! { + fn char_at(src: &str, pos: u32) -> Option { + src.chars().nth(pos as usize).map(|v| v as u32) + } +} + +decl_lib_func! { + fn from_string<'a>(src: &'a [u8]) -> Option<&'a str> { + std::str::from_utf8(src).ok() + } +} + +decl_lib_func! { + fn from_string_lossy(src: &[u8]) -> String { + String::from_utf8_lossy(src).into() + } +} + +pub fn register(vm: &Vm) -> crate::vm::Result<()> { + let mut namespace = Namespace::new(vm, "bp3d.util.utf8")?; + namespace.add([ + ("contains", RFunction::wrap(contains)), + ("split", RFunction::wrap(split)), + ("replace", RFunction::wrap(replace)), + ("count", RFunction::wrap(count)), + ("charAt", RFunction::wrap(char_at)), + ("fromString", RFunction::wrap(from_string)), + ("fromStringLossy", RFunction::wrap(from_string_lossy)) + ]) +} diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 9dd54a9..e85c622 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -96,5 +96,19 @@ fn test_vm_lib_util() { assert(bp3d.util.table.contains(tbl, 'b: 2')) assert(bp3d.util.table.contains(tbl, 'c: 3')) ").unwrap(); + vm.run_code::<()>(c" + local utf8 = bp3d.util.utf8 + assert(utf8.fromString('abc') ~= nil) + assert(utf8.count('abc') == 3) + local tbl = utf8.split('a;b;c;d', ';') + assert(#tbl == 4) + assert(tbl[1] == 'a') + assert(tbl[2] == 'b') + assert(tbl[3] == 'c') + assert(tbl[4] == 'd') + assert(utf8.charAt('abc', 0) == 0x61) + assert(utf8.charAt('abc', 1) == 0x62) + assert(utf8.charAt('abc', 2) == 0x63) + ").unwrap(); assert_eq!(vm.top(), top); } From 4f050716978a2a68051173ff9e564dc1aca58a2f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 9 Apr 2025 21:40:28 +0200 Subject: [PATCH 189/527] Renamed module macros to macro --- core/src/lib.rs | 2 +- core/src/libs/util/utf8.rs | 2 ++ core/src/{macros => macro}/closure.rs | 0 core/src/{macros => macro}/lib_func.rs | 0 core/src/{macros => macro}/mod.rs | 0 core/src/{macros => macro}/userdata.rs | 0 core/src/{macros => macro}/userdata_func.rs | 0 7 files changed, 3 insertions(+), 1 deletion(-) rename core/src/{macros => macro}/closure.rs (100%) rename core/src/{macros => macro}/lib_func.rs (100%) rename core/src/{macros => macro}/mod.rs (100%) rename core/src/{macros => macro}/userdata.rs (100%) rename core/src/{macros => macro}/userdata_func.rs (100%) diff --git a/core/src/lib.rs b/core/src/lib.rs index 3ec5770..7c59496 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -28,6 +28,6 @@ pub mod ffi; pub mod vm; -mod macros; +mod r#macro; pub mod util; pub mod libs; diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index bc13664..0e8e65c 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -80,6 +80,8 @@ decl_lib_func! { } } +//TODO: implement function to substring respecting UTF8 codes (instead of the panicking rust version). + pub fn register(vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.util.utf8")?; namespace.add([ diff --git a/core/src/macros/closure.rs b/core/src/macro/closure.rs similarity index 100% rename from core/src/macros/closure.rs rename to core/src/macro/closure.rs diff --git a/core/src/macros/lib_func.rs b/core/src/macro/lib_func.rs similarity index 100% rename from core/src/macros/lib_func.rs rename to core/src/macro/lib_func.rs diff --git a/core/src/macros/mod.rs b/core/src/macro/mod.rs similarity index 100% rename from core/src/macros/mod.rs rename to core/src/macro/mod.rs diff --git a/core/src/macros/userdata.rs b/core/src/macro/userdata.rs similarity index 100% rename from core/src/macros/userdata.rs rename to core/src/macro/userdata.rs diff --git a/core/src/macros/userdata_func.rs b/core/src/macro/userdata_func.rs similarity index 100% rename from core/src/macros/userdata_func.rs rename to core/src/macro/userdata_func.rs From fb610974a5054dabd7bc22c07dfda5cb41cb6651 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 10 Apr 2025 21:57:04 +0200 Subject: [PATCH 190/527] Added new Lib trait for future thread system --- core/src/libs/interface.rs | 47 ++++++++++++++++++++++++++++++++++++ core/src/libs/lua/call.rs | 14 +++++++---- core/src/libs/lua/load.rs | 27 ++++++++++++--------- core/src/libs/lua/mod.rs | 37 ++++++++++++++++------------ core/src/libs/lua/require.rs | 20 +++++++++++---- core/src/libs/mod.rs | 4 ++- core/src/libs/util/table.rs | 2 ++ core/src/vm/namespace.rs | 4 +++ core/tests/test_vm_libs.rs | 3 ++- 9 files changed, 120 insertions(+), 38 deletions(-) create mode 100644 core/src/libs/interface.rs diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs new file mode 100644 index 0000000..b6ea996 --- /dev/null +++ b/core/src/libs/interface.rs @@ -0,0 +1,47 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::namespace::Namespace; +use crate::vm::RootVm; + +pub trait Lib { + const NAMESPACE: &'static str; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()>; + + fn load_libs(&self, _: &mut RootVm) -> crate::vm::Result<()> { + Ok(()) + } + + fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + self.load_libs(vm)?; + let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; + self.load(&mut namespace)?; + Ok(()) + } +} diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index 3db15a1..e5c227c 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -27,12 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::decl_lib_func; +use crate::libs::interface::Lib; use crate::vm::error::Error; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::LuaFunction; -use crate::vm::Vm; decl_lib_func! { fn pcall(vm: &Vm, func: LuaFunction) -> UncheckedAnyReturn { @@ -52,8 +52,12 @@ decl_lib_func! { } } -pub fn register(vm: &Vm) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.lua")?; - namespace.add([("pcall", RFunction::wrap(pcall))])?; - Ok(()) +pub struct Call; + +impl Lib for Call { + const NAMESPACE: &'static str = "bp3d.lua"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([("pcall", RFunction::wrap(pcall))]) + } } diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 57f37ec..52dca0f 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -29,12 +29,12 @@ use std::path::{Path, PathBuf}; use bp3d_util::simple_error; use crate::{decl_closure, decl_lib_func}; +use crate::libs::interface::Lib; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::LuaFunction; -use crate::vm::Vm; decl_lib_func! { fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> crate::vm::Result { @@ -112,17 +112,22 @@ decl_closure! { } } -pub fn register(vm: &Vm, chroot: Option<&Path>) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.lua")?; - namespace.add([ - ("runString", RFunction::wrap(run_string)), - ("loadString", RFunction::wrap(load_string)) - ])?; - if let Some(chroot) = chroot { +pub struct Load<'a>(pub Option<&'a Path>); + +impl Lib for Load<'_> { + const NAMESPACE: &'static str = "bp3d.lua"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([ - ("loadFile", load_file(chroot)), - ("runFile", run_file(chroot)) + ("runString", RFunction::wrap(run_string)), + ("loadString", RFunction::wrap(load_string)) ])?; + if let Some(chroot) = self.0 { + namespace.add([ + ("loadFile", load_file(chroot)), + ("runFile", run_file(chroot)) + ])?; + } + Ok(()) } - Ok(()) } diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index f17943e..d7f177b 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -35,6 +35,8 @@ mod options; mod call; pub use options::Options; +use crate::libs::interface::Lib; +use crate::vm::RootVm; const PATCH_LIST: &[&str] = &[ "disable_lua_load", @@ -44,20 +46,25 @@ const PATCH_LIST: &[&str] = &[ "lua_load_no_bc" ]; -pub fn register(root: &mut crate::vm::RootVm, options: Options) -> crate::vm::Result<()> { - require::register(root, options.provider.unwrap_or_default())?; - load::register(root, options.load_chroot_path)?; - call::register(root)?; - let mut namespace = Namespace::new(root, "bp3d.lua")?; - namespace.add([ - ("name", "bp3d-lua"), - ("version", env!("CARGO_PKG_VERSION")) - ])?; - let mut patches = Table::with_capacity(root, PATCH_LIST.len(), 0); - for (i, name) in PATCH_LIST.into_iter().enumerate() { - // Lua indices starts at 1 not 0. - patches.set((i + 1) as _, *name)?; +impl<'a> Lib for Options<'a> { + const NAMESPACE: &'static str = "bp3d.lua"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("name", "bp3d-lua"), + ("version", env!("CARGO_PKG_VERSION")) + ])?; + let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); + for (i, name) in PATCH_LIST.into_iter().enumerate() { + // Lua indices starts at 1 not 0. + patches.set((i + 1) as _, *name)?; + } + namespace.add([("patches", patches)]) + } + + fn load_libs(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + require::Require(self.provider.clone().unwrap_or_default()).register(vm)?; + load::Load(self.load_chroot_path).register(vm)?; + call::Call.register(vm) } - namespace.add([("patches", patches)])?; - Ok(()) } diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 030fb3c..e374c37 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -30,6 +30,7 @@ use std::cell::RefCell; use std::collections::HashMap; use bp3d_util::simple_error; use crate::decl_closure; +use crate::libs::interface::Lib; use crate::vm::closure::rc::Rc; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::{RootVm, Vm}; @@ -78,9 +79,18 @@ decl_closure! { } } -pub(super) fn register(root: &mut RootVm, provider: std::rc::Rc) -> crate::vm::Result<()> { - let rc = Rc::from_rust(root, provider); - let mut namespace = Namespace::new(root, "bp3d.lua")?; - namespace.add([("require", require(rc))])?; - Ok(()) +pub struct Require(pub std::rc::Rc); + +impl Lib for Require { + const NAMESPACE: &'static str = "bp3d.lua"; + + fn load(&self, _: &mut Namespace) -> crate::vm::Result<()> { + std::unreachable!() + } + + fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + let rc = Rc::from_rust(vm, self.0.clone()); + let mut namespace = Namespace::new(vm, "bp3d.lua")?; + namespace.add([("require", require(rc))]) + } } diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index 37da277..4795ca2 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -28,8 +28,10 @@ pub mod lua; pub mod util; - +mod interface; //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. //TODO: utf8 lib with string functions operating on UTF8-strings + +pub use interface::*; diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 2f40fdd..abe4f0d 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -109,6 +109,8 @@ decl_lib_func! { } } +//TODO: table.protect + pub fn register(vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, "bp3d.util.table")?; namespace.add([ diff --git a/core/src/vm/namespace.rs b/core/src/vm/namespace.rs index bb05972..9650fd6 100644 --- a/core/src/vm/namespace.rs +++ b/core/src/vm/namespace.rs @@ -80,6 +80,10 @@ impl<'a> Namespace<'a> { } Ok(()) } + + pub fn vm(&self) -> &'a Vm { + self.vm + } } impl Drop for Namespace<'_> { diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index e85c622..5885081 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_lua::libs::Lib; use bp3d_lua::libs::lua::Options; use bp3d_lua::vm::RootVm; @@ -33,7 +34,7 @@ use bp3d_lua::vm::RootVm; fn test_vm_lib_lua() { let mut vm = RootVm::new(); let top = vm.top(); - bp3d_lua::libs::lua::register(&mut vm, Options::new()).unwrap(); + Options::new().register(&mut vm).unwrap(); vm.run_code::<()>(c" assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') From 6884a3c0dd8973338c5c6339fda6d8e23b9c21c3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 10 Apr 2025 22:16:58 +0200 Subject: [PATCH 191/527] Added additional security test --- core/tests/test_vm_userdata.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 95430e1..0e3eda6 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -263,3 +263,17 @@ fn test_vm_userdata_security4() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_security5() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c" + rawset(a, '__gc', nil) + ").unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From ad935561feb4b2cd132e4fc2b593bd47045d8808 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 09:09:33 +0200 Subject: [PATCH 192/527] Refactored lib system to include support for tuples and multi-libs --- core/src/libs/interface.rs | 48 +++++++++++++++++++++++++---- core/src/libs/lua/base.rs | 58 ++++++++++++++++++++++++++++++++++++ core/src/libs/lua/mod.rs | 39 ++---------------------- core/src/libs/lua/options.rs | 14 +++++++-- core/tests/test_vm_libs.rs | 4 +-- 5 files changed, 116 insertions(+), 47 deletions(-) create mode 100644 core/src/libs/lua/base.rs diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index b6ea996..396f7a7 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -34,14 +34,52 @@ pub trait Lib { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()>; - fn load_libs(&self, _: &mut RootVm) -> crate::vm::Result<()> { - Ok(()) - } - fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { - self.load_libs(vm)?; let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; self.load(&mut namespace)?; Ok(()) } } + +macro_rules! impl_tuple_lib { + ($($t: ident: $id: tt),*) => { + impl<$($t: $crate::libs::Lib),*> $crate::libs::Lib for ($($t),*) { + const NAMESPACE: &'static str = ""; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + $( + self.$id.load(namespace)?; + )* + Ok(()) + } + + fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + $( + self.$id.register(vm)?; + )* + Ok(()) + } + } + }; +} + +impl_tuple_lib!(T: 0, T1: 1); +impl_tuple_lib!(T: 0, T1: 1, T2: 2); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15, T16: 16); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15, T16: 16, T17: 17); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15, T16: 16, T17: 17, T18: 18); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15, T16: 16, T17: 17, T18: 18, T19: 19); +impl_tuple_lib!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9, T10: 10, T11: 11, T12: 12, T13: 13, T14: 14, T15: 15, T16: 16, T17: 17, T18: 18, T19: 19, T20: 20); diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs new file mode 100644 index 0000000..8e22b4c --- /dev/null +++ b/core/src/libs/lua/base.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::libs::Lib; +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; + +const PATCH_LIST: &[&str] = &[ + "disable_lua_load", + "lib_init", + "lj_disable_jit", + "lua_ext", + "lua_load_no_bc" +]; + +pub struct Base; + +impl Lib for Base { + const NAMESPACE: &'static str = "bp3d.lua"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("name", "bp3d-lua"), + ("version", env!("CARGO_PKG_VERSION")) + ])?; + let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); + for (i, name) in PATCH_LIST.into_iter().enumerate() { + // Lua indices starts at 1 not 0. + patches.set((i + 1) as _, *name)?; + } + namespace.add([("patches", patches)]) + } +} diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index d7f177b..9b0cb49 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -26,45 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::namespace::Namespace; -use crate::vm::table::Table; - mod load; pub mod require; mod options; mod call; +mod base; -pub use options::Options; -use crate::libs::interface::Lib; -use crate::vm::RootVm; - -const PATCH_LIST: &[&str] = &[ - "disable_lua_load", - "lib_init", - "lj_disable_jit", - "lua_ext", - "lua_load_no_bc" -]; - -impl<'a> Lib for Options<'a> { - const NAMESPACE: &'static str = "bp3d.lua"; - - fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.add([ - ("name", "bp3d-lua"), - ("version", env!("CARGO_PKG_VERSION")) - ])?; - let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); - for (i, name) in PATCH_LIST.into_iter().enumerate() { - // Lua indices starts at 1 not 0. - patches.set((i + 1) as _, *name)?; - } - namespace.add([("patches", patches)]) - } - - fn load_libs(&self, vm: &mut RootVm) -> crate::vm::Result<()> { - require::Require(self.provider.clone().unwrap_or_default()).register(vm)?; - load::Load(self.load_chroot_path).register(vm)?; - call::Call.register(vm) - } -} +pub use options::Lua; diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index ad73bc9..de7afd7 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -27,14 +27,18 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::path::Path; -use crate::libs::lua::require::Provider; +use crate::libs::Lib; +use crate::libs::lua::base::Base; +use crate::libs::lua::call::Call; +use crate::libs::lua::load::Load; +use crate::libs::lua::require::{Provider, Require}; -pub struct Options<'a> { +pub struct Lua<'a> { pub(super) load_chroot_path: Option<&'a Path>, pub(super) provider: Option> } -impl<'a> Options<'a> { +impl<'a> Lua<'a> { pub fn new() -> Self { Self { load_chroot_path: None, @@ -51,4 +55,8 @@ impl<'a> Options<'a> { self.provider = Some(provider); self } + + pub fn build(self) -> impl Lib + 'a { + (Base, Call, Load(self.load_chroot_path), Require(self.provider.unwrap_or_default())) + } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 5885081..fe5c0cc 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -27,14 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::libs::Lib; -use bp3d_lua::libs::lua::Options; +use bp3d_lua::libs::lua::Lua; use bp3d_lua::vm::RootVm; #[test] fn test_vm_lib_lua() { let mut vm = RootVm::new(); let top = vm.top(); - Options::new().register(&mut vm).unwrap(); + Lua::new().build().register(&mut vm).unwrap(); vm.run_code::<()>(c" assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') From 0dec072331f67b6c2809615fab6fd95a56776c7f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 09:35:50 +0200 Subject: [PATCH 193/527] Refactored util lib to new Lib system --- core/src/libs/lua/mod.rs | 5 +++++ core/src/libs/util/mod.rs | 13 ++++++------ core/src/libs/util/string.rs | 19 +++++++++++------- core/src/libs/util/table.rs | 39 ++++++++++++++++++++---------------- core/src/libs/util/utf8.rs | 29 ++++++++++++++++----------- core/tests/test_vm_libs.rs | 5 +++-- 6 files changed, 66 insertions(+), 44 deletions(-) diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 9b0cb49..abb51ca 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -32,4 +32,9 @@ mod options; mod call; mod base; +pub use base::Base; +pub use call::Call; +pub use load::Load; +pub use require::Require; + pub use options::Lua; diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 572b18a..6b1d051 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -30,9 +30,10 @@ mod table; mod string; mod utf8; -pub fn register(vm: &crate::vm::Vm) -> crate::vm::Result<()> { - table::register(vm)?; - string::register(vm)?; - utf8::register(vm)?; - Ok(()) -} +pub use table::Table; +pub use string::String; +pub use utf8::Utf8; + +// Workaround for language defect #22259. +#[allow(non_upper_case_globals)] +pub const Util: (Table, String, Utf8) = (Table, String, Utf8); diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 9082b22..05a84a7 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -27,10 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::decl_lib_func; +use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::table::Table; -use crate::vm::Vm; decl_lib_func! { fn contains(src: &[u8], needle: &[u8]) -> bool { @@ -53,10 +53,15 @@ decl_lib_func! { } } -pub fn register(vm: &Vm) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.util.string")?; - namespace.add([ - ("contains", RFunction::wrap(contains)), - ("split", RFunction::wrap(split)) - ]) +pub struct String; + +impl Lib for String { + const NAMESPACE: &'static str = "bp3d.util.string"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("contains", RFunction::wrap(contains)), + ("split", RFunction::wrap(split)) + ]) + } } diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index abe4f0d..fedfe41 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -26,17 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::table::Table; +use crate::vm::table::Table as LuaTable; use crate::decl_lib_func; use crate::ffi::lua::Type; +use crate::libs::Lib; use crate::vm::error::{Error, TypeError}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::AnyValue; -use crate::vm::Vm; decl_lib_func! { - fn update(dst: Table, src: Table) -> crate::vm::Result<()> { + fn update(dst: LuaTable, src: LuaTable) -> crate::vm::Result<()> { let mut src = src; let mut dst = dst; for res in src.iter() { @@ -57,12 +57,12 @@ decl_lib_func! { } decl_lib_func! { - fn count(src: Table) -> u64 { + fn count(src: LuaTable) -> u64 { src.len() as _ } } -fn to_string_rec(prefix: String, mut table: Table) -> crate::vm::Result> { +fn to_string_rec(prefix: String, mut table: LuaTable) -> crate::vm::Result> { let mut lines = Vec::new(); for res in table.iter() { let (k, v) = res?; @@ -78,13 +78,13 @@ fn to_string_rec(prefix: String, mut table: Table) -> crate::vm::Result crate::vm::Result { + fn to_string(src: LuaTable) -> crate::vm::Result { to_string_rec("".into(), src).map(|v| v.join("\n")) } } decl_lib_func! { - fn contains(src: Table, value: AnyValue) -> crate::vm::Result { + fn contains(src: LuaTable, value: AnyValue) -> crate::vm::Result { let mut src = src; for res in src.iter() { let (_, v) = res?; @@ -97,7 +97,7 @@ decl_lib_func! { } decl_lib_func! { - fn contains_key(src: Table, key: AnyValue) -> crate::vm::Result { + fn contains_key(src: LuaTable, key: AnyValue) -> crate::vm::Result { let mut src = src; for res in src.iter() { let (k, _) = res?; @@ -111,13 +111,18 @@ decl_lib_func! { //TODO: table.protect -pub fn register(vm: &Vm) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.util.table")?; - namespace.add([ - ("update", RFunction::wrap(update)), - ("count", RFunction::wrap(count)), - ("tostring", RFunction::wrap(to_string)), - ("contains", RFunction::wrap(contains)), - ("containsKey", RFunction::wrap(contains_key)) - ]) +pub struct Table; + +impl Lib for Table { + const NAMESPACE: &'static str = "bp3d.util.table"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("update", RFunction::wrap(update)), + ("count", RFunction::wrap(count)), + ("tostring", RFunction::wrap(to_string)), + ("contains", RFunction::wrap(contains)), + ("containsKey", RFunction::wrap(contains_key)) + ]) + } } diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index 0e8e65c..87ddcff 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -27,10 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::decl_lib_func; +use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::table::Table; -use crate::vm::Vm; decl_lib_func! { fn contains(src: &str, needle: &str) -> bool { @@ -82,15 +82,20 @@ decl_lib_func! { //TODO: implement function to substring respecting UTF8 codes (instead of the panicking rust version). -pub fn register(vm: &Vm) -> crate::vm::Result<()> { - let mut namespace = Namespace::new(vm, "bp3d.util.utf8")?; - namespace.add([ - ("contains", RFunction::wrap(contains)), - ("split", RFunction::wrap(split)), - ("replace", RFunction::wrap(replace)), - ("count", RFunction::wrap(count)), - ("charAt", RFunction::wrap(char_at)), - ("fromString", RFunction::wrap(from_string)), - ("fromStringLossy", RFunction::wrap(from_string_lossy)) - ]) +pub struct Utf8; + +impl Lib for Utf8 { + const NAMESPACE: &'static str = "bp3d.util.utf8"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("contains", RFunction::wrap(contains)), + ("split", RFunction::wrap(split)), + ("replace", RFunction::wrap(replace)), + ("count", RFunction::wrap(count)), + ("charAt", RFunction::wrap(char_at)), + ("fromString", RFunction::wrap(from_string)), + ("fromStringLossy", RFunction::wrap(from_string_lossy)) + ]) + } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index fe5c0cc..b026ab4 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -28,6 +28,7 @@ use bp3d_lua::libs::Lib; use bp3d_lua::libs::lua::Lua; +use bp3d_lua::libs::util::Util; use bp3d_lua::vm::RootVm; #[test] @@ -63,9 +64,9 @@ fn test_vm_lib_lua() { #[test] fn test_vm_lib_util() { - let vm = RootVm::new(); + let mut vm = RootVm::new(); let top = vm.top(); - bp3d_lua::libs::util::register(&vm).unwrap(); + Util.register(&mut vm).unwrap(); vm.run_code::<()>(c" local src = { a = 1, From f78705b3f6fd83a49a0b14b5b4230252844f783a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 10:11:13 +0200 Subject: [PATCH 194/527] Fixed build warning when function requires no arguments --- core/src/macro/mod.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/core/src/macro/mod.rs b/core/src/macro/mod.rs index 3f11aeb..cd9cc7d 100644 --- a/core/src/macro/mod.rs +++ b/core/src/macro/mod.rs @@ -40,6 +40,11 @@ macro_rules! c_stringify { #[macro_export] macro_rules! decl_from_param { + ( + $vm: ident, $start_index: literal, + ) => { + }; + ( $vm: ident, $start_index: literal, $arg_name: ident: $arg_ty: ty ) => { From 1156f4c00c7833eeffa286754d09664f047df1ac Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 10:12:41 +0200 Subject: [PATCH 195/527] Added support to set the metatable of a Lua table --- core/src/vm/table/core.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 7d9b8cf..b1758c9 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -28,7 +28,7 @@ use std::fmt::{Debug, Display}; use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_settop, lua_topointer}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settop, lua_topointer}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; @@ -103,6 +103,11 @@ impl<'a> Table<'a> { Iter::from_raw(self.vm, self.index).count() as _ } + pub fn set_metatable(&mut self, other: Table) { + other.into_lua(self.vm); + unsafe { lua_setmetatable(self.vm.as_ptr(), self.index) }; + } + /// Returns the absolute index of this table on the Lua stack. #[inline(always)] pub fn index(&self) -> i32 { From c821b415bdc6556454bcb143ea7ee9f98f09fcc4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 10:16:01 +0200 Subject: [PATCH 196/527] Added bp3d.util.table.protect --- core/src/libs/util/table.rs | 27 +++++++++++++++++++++++++-- core/tests/test_vm_libs.rs | 10 ++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index fedfe41..89a4cd0 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_util::simple_error; use crate::vm::table::Table as LuaTable; use crate::decl_lib_func; use crate::ffi::lua::Type; @@ -109,7 +110,28 @@ decl_lib_func! { } } -//TODO: table.protect +simple_error! { + ProtectError { + NewIndex => "attempt to set value into protected table." + } +} + +decl_lib_func! { + fn __newindex() -> Result<(), ProtectError> { + Err(ProtectError::NewIndex) + } +} + +decl_lib_func! { + fn protect<'a>(vm: &Vm, src: LuaTable) -> crate::vm::Result> { + let mut wrapper = LuaTable::new(vm); + let mut metatable = LuaTable::new(vm); + metatable.set_field(c"__index", src)?; + metatable.set_field(c"__newindex", RFunction::wrap(__newindex))?; + wrapper.set_metatable(metatable); + Ok(wrapper) + } +} pub struct Table; @@ -122,7 +144,8 @@ impl Lib for Table { ("count", RFunction::wrap(count)), ("tostring", RFunction::wrap(to_string)), ("contains", RFunction::wrap(contains)), - ("containsKey", RFunction::wrap(contains_key)) + ("containsKey", RFunction::wrap(contains_key)), + ("protect", RFunction::wrap(protect)) ]) } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index b026ab4..68ff683 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -112,5 +112,15 @@ fn test_vm_lib_util() { assert(utf8.charAt('abc', 1) == 0x62) assert(utf8.charAt('abc', 2) == 0x63) ").unwrap(); + vm.run_code::<()>(c" + local tbl = { value = 42 } + local protected = bp3d.util.table.protect(tbl) + assert(protected.value == 42) + ").unwrap(); + vm.run_code::<()>(c" + local tbl = { value = 42 } + local protected = bp3d.util.table.protect(tbl) + protected.value = 84 + ").unwrap_err(); assert_eq!(vm.top(), top); } From 6849dd5c2d5140a378ffe23d3f2f3f5f49395592 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 18:30:49 +0200 Subject: [PATCH 197/527] Added support for IntoParam to OsString --- core/src/vm/function/core.rs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index baf440e..a50f056 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::error::Error; +use std::ffi::OsString; use std::slice; use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; @@ -120,6 +121,12 @@ unsafe impl IntoParam for String { } } +unsafe impl IntoParam for OsString { + fn into_param(self, vm: &Vm) -> u16 { + self.as_encoded_bytes().into_param(vm) + } +} + macro_rules! impl_integer { ($($t: ty),*) => { $( From 379afb0ab7d7b0e62452d07a15ee69b39596710b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 12 Apr 2025 18:32:34 +0200 Subject: [PATCH 198/527] Added initial version of base os lib --- core/Cargo.toml | 2 + core/src/libs/mod.rs | 1 + core/src/libs/os/base.rs | 196 +++++++++++++++++++++++++++++++++++++++ core/src/libs/os/mod.rs | 31 +++++++ 4 files changed, 230 insertions(+) create mode 100644 core/src/libs/os/base.rs create mode 100644 core/src/libs/os/mod.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index b488510..8114a62 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,6 +17,8 @@ bp3d-util = { version = "1.4.2", features = ["simple-error", "format"] } log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } bp3d-debug = "1.0.0-rc.6.1.0" +bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } +time = { version = "0.3.37", features = ["formatting"] } [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index 4795ca2..ac517ae 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -29,6 +29,7 @@ pub mod lua; pub mod util; mod interface; +mod os; //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/base.rs new file mode 100644 index 0000000..7175214 --- /dev/null +++ b/core/src/libs/os/base.rs @@ -0,0 +1,196 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::OsString; +use std::time::Instant; +use bp3d_os::time::{LocalUtcOffset, MonthExt}; +use bp3d_util::simple_error; +use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset}; +use time::format_description::parse; +use crate::decl_lib_func; +use crate::libs::Lib; +use crate::vm::function::IntoParam; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; +use crate::vm::Vm; + +enum TableOrString<'a> { + Table(Table<'a>), + String(String) +} + +unsafe impl IntoParam for TableOrString<'_> { + fn into_param(self, vm: &Vm) -> u16 { + match self { + TableOrString::Table(t) => t.into_param(vm), + TableOrString::String(s) => s.into_param(vm) + } + } +} + +fn get_std_offset() -> UtcOffset { + let now = OffsetDateTime::now_utc(); + let jan = PrimitiveDateTime::new(Date::from_calendar_date(now.year(), Month::January, 1).unwrap(), Time::MIDNIGHT).assume_utc(); + let jul = PrimitiveDateTime::new(Date::from_calendar_date(now.year(), Month::July, 1).unwrap(), Time::MIDNIGHT).assume_utc(); + let offset_jan = UtcOffset::local_offset_at(jan).unwrap(); + let offset_jul = UtcOffset::local_offset_at(jul).unwrap(); + std::cmp::max(offset_jan, offset_jul) +} + +const REPLACEMENTS: &[(&str, &str)] = &[ + ("%a", "[weekday repr:short]"), + ("%A", "[weekday repr:long]"), + ("%b", "[month repr:short]"), + ("%B", "[month repr:long]"), + ("%d", "[day]"), + ("%H", "[hour repr:24]"), + ("%I", "[hour repr:12]"), + ("%M", "[minute]"), + ("%m", "[month]"), + ("%p", "[period]"), + ("%S", "[second]"), + ("%w", "[weekday]"), + ("%Y", "[year]"), + ("%y", "[year repr:last_two]"), + ("%%", "%"), + ("[", "[["), +]; + +decl_lib_func! { + fn date<'a>(vm: &Vm, format: Option<&str>, time: Option) -> Option> { + let mut format = format.unwrap_or("%c"); + let mut time = time.map(OffsetDateTime::from_unix_timestamp).unwrap_or(Ok(OffsetDateTime::now_utc())).ok()?; + if format.starts_with('!') { + format = &format[1..]; + } else { + let offset = UtcOffset::local_offset_at(time)?; + time = time.to_offset(offset); + } + if format == "*t" { + let std_offset = get_std_offset(); + let mut table = Table::new(vm); + table.set_field(c"sec", time.second()).unwrap(); + table.set_field(c"min", time.minute()).unwrap(); + table.set_field(c"hour", time.hour()).unwrap(); + table.set_field(c"day", time.day()).unwrap(); + table.set_field(c"month", time.month() as u8).unwrap(); + table.set_field(c"year", time.year()).unwrap(); + table.set_field(c"wday", time.weekday() as u8 + 1).unwrap(); + table.set_field(c"yday", time.to_julian_day()).unwrap(); + table.set_field(c"isdst", time.offset() < std_offset).unwrap(); + Some(TableOrString::Table(table)) + } else { + let mut format = String::from(format); + for (k, v) in REPLACEMENTS { + format = format.replace(k, v); + } + let format = parse(format.as_str()).ok()?; + time.format(&format).map(TableOrString::String).ok() + } + } +} + +simple_error! { + TimeFormatError { + (impl From)Vm(crate::vm::error::Error) => "vm error: {}", + InvalidMonthIndex(u8) => "invalid month index {}", + (impl From)Time(time::error::ComponentRange) => "out of range error: {}" + } +} + +fn get_time_from_table(table: Table) -> Result { + let year: i32 = table.get_field(c"year")?; + let month: u8 = table.get_field(c"month")?; + let day: u8 = table.get_field(c"day")?; + let date = Date::from_calendar_date(year, Month::from_index(month).ok_or(TimeFormatError::InvalidMonthIndex(month))?, day)?; + let hour: Option = table.get_field(c"hour")?; + let minute: Option = table.get_field(c"min")?; + let second: Option = table.get_field(c"sec")?; + let mut hour = hour.unwrap_or(12); + let minute = minute.unwrap_or(0); + let second = second.unwrap_or(0); + let dst: Option = table.get_field(c"isdst")?; + let dst = dst.unwrap_or(false); + // Consider DST to be always +1H, this may not always be true but is true in most countries. + if dst { + hour += 1; + } + let time = Time::from_hms(hour, minute, second)?; + let time = PrimitiveDateTime::new(date, time).assume_utc(); + Ok(time) +} + +decl_lib_func! { + fn time(table: Option
) -> Result { + match table { + Some(table) => get_time_from_table(table).map(|v| v.unix_timestamp()), + None => Ok(OffsetDateTime::now_utc().unix_timestamp()) + } + } +} + +decl_lib_func! { + fn difftime(a: i64, b: Option) -> Option { + let a = OffsetDateTime::from_unix_timestamp(a).ok()?; + let b = OffsetDateTime::from_unix_timestamp(b.unwrap_or(0)).ok()?; + Some((a - b).as_seconds_f64()) + } +} + +thread_local! { + static NOW: Instant = Instant::now(); +} + +decl_lib_func! { + fn clock() -> f64 { + NOW.with(|v| v.elapsed().as_secs_f64()) + } +} + +decl_lib_func! { + fn getenv(key: &str) -> Option { + std::env::var_os(key) + } +} + +pub struct Base; + +impl Lib for Base { + const NAMESPACE: &'static str = "bp3d.os"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("date", RFunction::wrap(date)), + ("time", RFunction::wrap(time)), + ("clock", RFunction::wrap(clock)), + ("difftime", RFunction::wrap(difftime)), + ("getenv", RFunction::wrap(getenv)) + ]) + } +} diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs new file mode 100644 index 0000000..2cc8998 --- /dev/null +++ b/core/src/libs/os/mod.rs @@ -0,0 +1,31 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod base; + +pub use base::Base; From c1539b0a172f5e33043cc3fa089af0fdf445a54c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 09:43:55 +0200 Subject: [PATCH 199/527] Fixed build bug with userdata macro --- core/src/macro/userdata.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index e903991..0f7f7ec 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -50,13 +50,13 @@ macro_rules! decl_userdata { ( impl $obj_name: ident { $( - $vis: vis fn $fn_name: ident($this: ident: &$obj_name2: ident $($tokens: tt)*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name2: ident $($tokens: tt)*) -> $ret_ty: ty $code: block )* } ) => { $( $crate::decl_userdata_func! { - $vis fn $fn_name($this: &$obj_name $($tokens)*) -> $ret_ty $code + $vis fn $fn_name $(<$lifetime>)? ($this: &$obj_name $($tokens)*) -> $ret_ty $code } )* @@ -71,13 +71,13 @@ macro_rules! decl_userdata_mut { ( impl $obj_name: ident { $( - $vis: vis fn $fn_name: ident($($tokens: tt)*) -> $ret_ty: ty $code: block + $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($($tokens: tt)*) -> $ret_ty: ty $code: block )* } ) => { $( $crate::decl_userdata_func! { - $vis fn $fn_name($($tokens)*) -> $ret_ty $code + $vis fn $fn_name $(<$lifetime>)? ($($tokens)*) -> $ret_ty $code } )* From a8da0304d31e362c65a32313ff3f4b095bd9571d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 09:44:25 +0200 Subject: [PATCH 200/527] Added initial support for bp3d.os.time lib --- core/src/libs/mod.rs | 8 +- core/src/libs/os/mod.rs | 2 + core/src/libs/os/time.rs | 184 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 core/src/libs/os/time.rs diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index ac517ae..480fab6 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -26,10 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +/// The bp3d-lua core library. pub mod lua; + +/// Utility toolkit. pub mod util; + +/// OS toolkit. +pub mod os; + mod interface; -mod os; //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs index 2cc8998..201b559 100644 --- a/core/src/libs/os/mod.rs +++ b/core/src/libs/os/mod.rs @@ -27,5 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod base; +mod time; pub use base::Base; +pub use time::Time; diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs new file mode 100644 index 0000000..857392f --- /dev/null +++ b/core/src/libs/os/time.rs @@ -0,0 +1,184 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_os::time::{LocalOffsetDateTime, MonthExt}; +use bp3d_util::simple_error; +use time::error::{ComponentRange, Format, InvalidFormatDescription}; +use time::format_description::parse; +use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, UtcOffset}; +use crate::{decl_lib_func, decl_userdata}; +use crate::libs::Lib; +use crate::vm::function::IntoParam; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; +use crate::vm::table::Table; +use crate::vm::Vm; + +simple_error! { + FormatError { + (impl From)InvalidDescription(InvalidFormatDescription) => "invalid format description: {}", + (impl From)Format(Format) => "format error: {}" + } +} + +struct Wrapper(OffsetDateTime); + +decl_userdata! { + impl Wrapper { + fn format(this: &Wrapper, format: &str) -> Result { + let desc = parse(format)?; + let str = this.0.format(&desc)?; + Ok(str) + } + + fn __add(this: &Wrapper, duration: f64) -> Option { + this.0.checked_add(Duration::seconds_f64(duration)).map(Wrapper) + } + + fn __sub(this: &Wrapper, other: &Wrapper) -> f64 { + (this.0 - other.0).as_seconds_f64() + } + + fn __gt(this: &Wrapper, other: &Wrapper) -> bool { + this.0 > other.0 + } + + fn __ge(this: &Wrapper, other: &Wrapper) -> bool { + this.0 >= other.0 + } + + fn __lt(this: &Wrapper, other: &Wrapper) -> bool { + this.0 < other.0 + } + + fn __le(this: &Wrapper, other: &Wrapper) -> bool { + this.0 <= other.0 + } + + fn get_date<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + let mut table = Table::with_capacity(vm, 0, 3); + table.set_field(c"year", this.0.year())?; + table.set_field(c"month", this.0.month() as u8)?; + table.set_field(c"day", this.0.day())?; + Ok(table) + } + + fn get_time<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + let mut table = Table::with_capacity(vm, 0, 3); + table.set_field(c"hour", this.0.hour())?; + table.set_field(c"minute", this.0.minute())?; + table.set_field(c"second", this.0.second())?; + Ok(table) + } + + fn get_offset<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + let mut table = Table::with_capacity(vm, 0, 3); + table.set_field(c"hours", this.0.offset().whole_hours())?; + table.set_field(c"minutes", this.0.offset().whole_minutes())?; + table.set_field(c"seconds", this.0.offset().whole_seconds())?; + Ok(table) + } + } +} + +unsafe impl IntoParam for OffsetDateTime { + fn into_param(self, vm: &Vm) -> u16 { + Wrapper(self).into_param(vm) + } +} + +decl_lib_func! { + fn now_utc() -> OffsetDateTime { + OffsetDateTime::now_utc() + } +} + +decl_lib_func! { + fn now_local() -> Option { + OffsetDateTime::now_local() + } +} + +decl_lib_func! { + fn from_unix_timestamp(timestamp: i64) -> Result { + OffsetDateTime::from_unix_timestamp(timestamp) + } +} + +simple_error! { + DateTimeError { + (impl From)Vm(crate::vm::error::Error) => "vm error: {}", + InvalidMonthIndex(u8) => "invalid month index {}", + (impl From)Time(ComponentRange) => "out of range error: {}" + } +} + +decl_lib_func! { + fn new(table: Table) -> Result { + let year: i32 = table.get_field(c"year")?; + let month: u8 = table.get_field(c"month")?; + let day: u8 = table.get_field(c"day")?; + let date = Date::from_calendar_date(year, Month::from_index(month).ok_or(DateTimeError::InvalidMonthIndex(month))?, day)?; + let hour: Option = table.get_field(c"hour")?; + let minute: Option = table.get_field(c"min")?; + let second: Option = table.get_field(c"sec")?; + let hour = hour.unwrap_or(12); + let minute = minute.unwrap_or(0); + let second = second.unwrap_or(0); + let time = time::Time::from_hms(hour, minute, second)?; + let offset: Option
= table.get_field(c"offset")?; + if let Some(offset) = offset { + let offset_hours: i8 = offset.get_field(c"hours")?; + let offset_minutes: i8 = offset.get_field(c"minutes")?; + let offset_seconds: i8 = offset.get_field(c"seconds")?; + let offset = UtcOffset::from_hms(offset_hours, offset_minutes, offset_seconds)?; + Ok(OffsetDateTime::new_in_offset(date, time, offset)) + } else { + Ok(PrimitiveDateTime::new(date, time).assume_utc()) + } + } +} + +//TODO: Implement the performance counter using actual bp3d_os::time::Instant + +pub struct Time; + +impl Lib for Time { + const NAMESPACE: &'static str = "bp3d.os.time"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + //TODO: Support rename userdata methods to camelCase. + namespace.vm().register_userdata::()?; + namespace.add([ + ("nowUtc", RFunction::wrap(now_utc)), + ("nowLocal", RFunction::wrap(now_local)), + ("fromUnixTimestamp", RFunction::wrap(from_unix_timestamp)), + ("new", RFunction::wrap(new)) + ]) + } +} From 9fd49d6a9110854bfe8df27c80c2c2c7306751b2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 09:47:35 +0200 Subject: [PATCH 201/527] Moved base os lib (lua compatibility) to os lib --- core/src/libs/os/base.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/base.rs index 7175214..a95f0bf 100644 --- a/core/src/libs/os/base.rs +++ b/core/src/libs/os/base.rs @@ -182,7 +182,7 @@ decl_lib_func! { pub struct Base; impl Lib for Base { - const NAMESPACE: &'static str = "bp3d.os"; + const NAMESPACE: &'static str = "os"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([ From 97b49087363b986d2997a162aeb0c423e9744b23 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 09:48:47 +0200 Subject: [PATCH 202/527] Renamed base to compat to mean a compatibility module for standard lua --- core/src/libs/os/base.rs | 4 ++-- core/src/libs/os/mod.rs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/base.rs index a95f0bf..8e698f5 100644 --- a/core/src/libs/os/base.rs +++ b/core/src/libs/os/base.rs @@ -179,9 +179,9 @@ decl_lib_func! { } } -pub struct Base; +pub struct Compat; -impl Lib for Base { +impl Lib for Compat { const NAMESPACE: &'static str = "os"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs index 201b559..f2cfd9b 100644 --- a/core/src/libs/os/mod.rs +++ b/core/src/libs/os/mod.rs @@ -29,5 +29,5 @@ mod base; mod time; -pub use base::Base; +pub use base::Compat; pub use time::Time; From 7aed83a8b28c2aeb93ae6f42f90f725f7364d38d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 10:24:58 +0200 Subject: [PATCH 203/527] Added initial version of instant lib --- core/src/libs/os/instant.rs | 59 +++++++++++++++++++++++++++++++++++++ core/src/libs/os/mod.rs | 2 ++ core/src/libs/os/time.rs | 2 -- 3 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 core/src/libs/os/instant.rs diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs new file mode 100644 index 0000000..6ed8a8c --- /dev/null +++ b/core/src/libs/os/instant.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::{decl_lib_func, decl_userdata}; +use crate::libs::Lib; +use crate::vm::function::types::RFunction; +use crate::vm::namespace::Namespace; + +struct Wrapper(bp3d_os::time::Instant); + +decl_userdata! { + impl Wrapper { + fn elapsed(this: &Wrapper) -> f64 { + this.0.elapsed().as_secs_f64() + } + } +} + +decl_lib_func! { + fn now() -> Wrapper { + Wrapper(bp3d_os::time::Instant::now()) + } +} + +pub struct Instant; + +impl Lib for Instant { + const NAMESPACE: &'static str = "bp3d.os.instant"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.vm().register_userdata::()?; + namespace.add([("now", RFunction::wrap(now))]) + } +} diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs index f2cfd9b..4e63198 100644 --- a/core/src/libs/os/mod.rs +++ b/core/src/libs/os/mod.rs @@ -28,6 +28,8 @@ mod base; mod time; +mod instant; pub use base::Compat; pub use time::Time; +pub use instant::Instant; diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 857392f..9f439b4 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -164,8 +164,6 @@ decl_lib_func! { } } -//TODO: Implement the performance counter using actual bp3d_os::time::Instant - pub struct Time; impl Lib for Time { From 3a337c2f200950bcdc7e44c5e79f2a092e8cedbf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 11:08:03 +0200 Subject: [PATCH 204/527] Added small test for time and instant --- core/tests/test_vm_libs.rs | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 68ff683..d2baf84 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -124,3 +124,43 @@ fn test_vm_lib_util() { ").unwrap_err(); assert_eq!(vm.top(), top); } + +#[test] +fn test_vm_lib_os_time() { + let mut vm = RootVm::new(); + bp3d_lua::libs::os::Time.register(&mut vm).unwrap(); + vm.run_code::<()>(c" + time = bp3d.os.time.nowLocal() + time2 = bp3d.os.time.nowUtc() + ").unwrap(); + std::thread::sleep(std::time::Duration::from_millis(500)); + vm.run_code::<()>(c" + local function testDateTime(a, b) + local ymd = a:get_date() + local ymd2 = b:get_date() + assert(ymd.year == ymd2.year) + assert(ymd.month == ymd2.month) + assert(ymd.day == ymd2.day) + end + local now2 = bp3d.os.time.nowUtc() + local now = bp3d.os.time.nowLocal() + assert(now > time) + assert(now2 > time2) + testDateTime(now, time) + testDateTime(now2, time2) + ").unwrap(); +} + +#[test] +fn test_vm_lib_os_instant() { + let mut vm = RootVm::new(); + bp3d_lua::libs::os::Instant.register(&mut vm).unwrap(); + vm.run_code::<()>(c" + instant = bp3d.os.instant.now() + ").unwrap(); + std::thread::sleep(std::time::Duration::from_millis(500)); + vm.run_code::<()>(c" + local diff = instant:elapsed() + assert((diff - 0.5) < 0.1) + ").unwrap(); +} From e20dd90c27aea7d53dc71d1909ef82b648b3972e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 13 Apr 2025 11:29:02 +0200 Subject: [PATCH 205/527] Added small test for os base lib --- core/tests/test_vm_libs.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index d2baf84..5088b69 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -164,3 +164,17 @@ fn test_vm_lib_os_instant() { assert((diff - 0.5) < 0.1) ").unwrap(); } + +#[test] +fn test_vm_lib_os() { + let mut vm = RootVm::new(); + bp3d_lua::libs::os::Compat.register(&mut vm).unwrap(); + vm.run_code::<()>(c" + clock = os.clock() + ").unwrap(); + std::thread::sleep(std::time::Duration::from_millis(500)); + vm.run_code::<()>(c" + local now = os.clock() + assert((clock - now) < 0.1) + ").unwrap(); +} From 17d9c899498d2bdf72e730fc1e9fc3daa3837911 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 21:12:24 +0200 Subject: [PATCH 206/527] Added support for IntoParam on Cow --- core/src/vm/function/core.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index a50f056..b06944a 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Cow; use std::error::Error; use std::ffi::OsString; use std::slice; @@ -127,6 +128,16 @@ unsafe impl IntoParam for OsString { } } +unsafe impl<'a, T: IntoParam + Clone> IntoParam for Cow<'a, T> + where &'a T: IntoParam { + fn into_param(self, vm: &Vm) -> u16 { + match self { + Cow::Borrowed(v) => v.into_param(vm), + Cow::Owned(v) => v.into_param(vm) + } + } +} + macro_rules! impl_integer { ($($t: ty),*) => { $( From 7de275149a66cc7c7851cd283abd396ea38e5dfa Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 21:12:47 +0200 Subject: [PATCH 207/527] Using new Cow implementation in bp3d.util.utf8 --- core/src/libs/util/utf8.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index 87ddcff..f753e64 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Cow; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; @@ -75,8 +76,8 @@ decl_lib_func! { } decl_lib_func! { - fn from_string_lossy(src: &[u8]) -> String { - String::from_utf8_lossy(src).into() + fn from_string_lossy(src: &[u8]) -> Cow { + String::from_utf8_lossy(src) } } From 0a2526f1430afb3eb9ad73882efa0459f61f1c82 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 21:15:42 +0200 Subject: [PATCH 208/527] Replaced OsString implementation by Vec --- core/src/libs/os/base.rs | 5 ++--- core/src/vm/function/core.rs | 4 ++-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/base.rs index 8e698f5..abf7099 100644 --- a/core/src/libs/os/base.rs +++ b/core/src/libs/os/base.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::OsString; use std::time::Instant; use bp3d_os::time::{LocalUtcOffset, MonthExt}; use bp3d_util::simple_error; @@ -174,8 +173,8 @@ decl_lib_func! { } decl_lib_func! { - fn getenv(key: &str) -> Option { - std::env::var_os(key) + fn getenv(key: &str) -> Option> { + std::env::var_os(key).map(|v| v.into_encoded_bytes()) } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index b06944a..a5bd1b6 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -122,9 +122,9 @@ unsafe impl IntoParam for String { } } -unsafe impl IntoParam for OsString { +unsafe impl IntoParam for Vec { fn into_param(self, vm: &Vm) -> u16 { - self.as_encoded_bytes().into_param(vm) + self.as_slice().into_param(vm) } } From c431b198e29cb68374c05ae96f59a9e7ec01f7d9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 21:15:55 +0200 Subject: [PATCH 209/527] Fixed warning --- core/src/vm/function/core.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index a5bd1b6..d37adfe 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -28,7 +28,6 @@ use std::borrow::Cow; use std::error::Error; -use std::ffi::OsString; use std::slice; use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; From a8f6ab1936affb93da2619e4f5ef577a9fa11ae8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 22:10:33 +0200 Subject: [PATCH 210/527] Added support for case convertions to userdata registration --- core/Cargo.toml | 3 +- core/src/libs/os/instant.rs | 2 +- core/src/libs/os/time.rs | 3 +- core/src/macro/userdata.rs | 2 +- core/src/vm/core/vm.rs | 6 +-- core/src/vm/userdata/case.rs | 85 +++++++++++++++++++++++++++++++ core/src/vm/userdata/core.rs | 30 ++++++----- core/src/vm/userdata/interface.rs | 9 +++- core/src/vm/userdata/mod.rs | 1 + core/tests/test_vm_libs.rs | 4 +- core/tests/test_vm_userdata.rs | 14 ++--- 11 files changed, 128 insertions(+), 31 deletions(-) create mode 100644 core/src/vm/userdata/case.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 8114a62..19b9dc2 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,12 +13,13 @@ categories = ["graphics"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bp3d-util = { version = "1.4.2", features = ["simple-error", "format"] } +bp3d-util = { version = "2.0.1", features = ["simple-error", "format", "string"] } log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } bp3d-debug = "1.0.0-rc.6.1.0" bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } time = { version = "0.3.37", features = ["formatting"] } +itertools = { version = "0.14.0" } [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index 6ed8a8c..50ab643 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -53,7 +53,7 @@ impl Lib for Instant { const NAMESPACE: &'static str = "bp3d.os.instant"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.vm().register_userdata::()?; + namespace.vm().register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([("now", RFunction::wrap(now))]) } } diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 9f439b4..49df93c 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -170,8 +170,7 @@ impl Lib for Time { const NAMESPACE: &'static str = "bp3d.os.time"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - //TODO: Support rename userdata methods to camelCase. - namespace.vm().register_userdata::()?; + namespace.vm().register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([ ("nowUtc", RFunction::wrap(now_utc)), ("nowLocal", RFunction::wrap(now_local)), diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index 0f7f7ec..b7d68ca 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -32,7 +32,7 @@ macro_rules! _impl_userdata { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { $( let (name, func) = unsafe { $obj_name::$fn_name().build()? }; registry.add_method(name, func); diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 8331ed4..c2e454a 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -36,7 +36,7 @@ use crate::vm::core::{Load, LoadString, Raw}; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; -use crate::vm::userdata::UserData; +use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::function::LuaFunction; @@ -60,8 +60,8 @@ impl Vm { Ok(r) } - pub fn register_userdata(&self) -> crate::vm::Result<()> { - let reg = unsafe { Registry::::new(self) }.map_err(Error::UserData)?; + pub fn register_userdata(&self, case: impl NameConvert) -> crate::vm::Result<()> { + let reg = unsafe { Registry::::new(self, case) }.map_err(Error::UserData)?; let res = T::register(®).map_err(Error::UserData); match res { Ok(_) => Ok(()), diff --git a/core/src/vm/userdata/case.rs b/core/src/vm/userdata/case.rs new file mode 100644 index 0000000..f5cb5bc --- /dev/null +++ b/core/src/vm/userdata/case.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::borrow::Cow; +use std::ffi::{CStr, CString}; +use bp3d_util::string::BufTools; +use itertools::Itertools; +use crate::vm::userdata::NameConvert; + +fn to_string_lossy(bytes: Cow<[u8]>) -> Cow { + match bytes { + Cow::Borrowed(v) => String::from_utf8_lossy(v), + Cow::Owned(v) => String::from(&*String::from_utf8_lossy(&*v)).into(), + } +} + +pub struct Snake; + +impl NameConvert for Snake { + fn name_convert(&self, name: &'static CStr) -> Cow<'static, CStr> { + Cow::Borrowed(name) + } +} + +pub struct Camel; + +impl NameConvert for Camel { + fn name_convert(&self, name: &'static CStr) -> Cow<'static, CStr> { + let s = match name.to_str() { + Ok(v) => v, + // Return the same unconverted string if we failed. + Err(_) => return Cow::Borrowed(name), + }; + let s: String = s.split("_") + .enumerate() + .map(|(i, v)| if i != 0 { v.as_bytes().capitalise_ascii() } else { v.as_bytes().into() }) + .map(to_string_lossy) + .join("") + .into(); + CString::new(s).unwrap().into() + } +} + +pub struct Pascal; + +impl NameConvert for Pascal { + fn name_convert(&self, name: &'static CStr) -> Cow<'static, CStr> { + let s = match name.to_str() { + Ok(v) => v, + // Return the same unconverted string if we failed. + Err(_) => return Cow::Borrowed(name), + }; + let s: String = s.split("_") + .map(|v| v.as_bytes().capitalise_ascii()) + .map(to_string_lossy) + .join("") + .into(); + CString::new(s).unwrap().into() + } +} diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index c633c6c..ff781fd 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -32,7 +32,7 @@ use std::marker::PhantomData; use bp3d_debug::{debug, warning}; use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; use crate::ffi::lua::{lua_pushcclosure, lua_pushnil, lua_pushvalue, lua_setfield, lua_setmetatable, lua_settop, CFunction, State}; -use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, UserData}; +use crate::vm::userdata::{AddGcMethod, NameConvert, Error, LuaDrop, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; use crate::vm::Vm; @@ -97,18 +97,20 @@ impl Function { } } -pub struct Registry<'a, T: UserData> { +pub struct Registry<'a, T: UserData, C: NameConvert> { vm: &'a Vm, useless: PhantomData, - has_gc: OnceCell<()> + has_gc: OnceCell<()>, + case: C } -impl<'a, T: UserData> Registry<'a, T> { +impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { /// Creates a new [Registry] from the given Vm. /// /// # Arguments /// /// * `vm`: the vm in which to register the userdata metatable. + /// * `case`: the case converter to apply to each name to be registered. /// /// returns: Result, Error> /// @@ -116,7 +118,7 @@ impl<'a, T: UserData> Registry<'a, T> { /// /// Running operations on the vm after calling this method is UB unless this [Registry] object /// is dropped. - pub unsafe fn new(vm: &'a Vm) -> Result { + pub unsafe fn new(vm: &'a Vm, case: C) -> Result { if align_of::() > 8 { return Err(Error::Alignment(align_of::())); } @@ -125,7 +127,7 @@ impl<'a, T: UserData> Registry<'a, T> { unsafe { lua_settop(vm.as_ptr(), -2) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } - let reg = Registry { vm, useless: PhantomData, has_gc: OnceCell::new() }; + let reg = Registry { vm, useless: PhantomData, has_gc: OnceCell::new(), case }; reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()).unwrap_unchecked(); Ok(reg) } @@ -136,14 +138,18 @@ impl<'a, T: UserData> Registry<'a, T> { unsafe { lua_settop(self.vm.as_ptr(), -(num as i32) - 1) }; return Err(Error::MultiValueField); } - unsafe { lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); } + unsafe { lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); } Ok(()) } pub fn add_method(&self, name: &'static CStr, func: CFunction) { unsafe { lua_pushcclosure(self.vm.as_ptr(), func, 0); - lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); + if &name.to_bytes()[..2] == b"__" { + lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); + } else { + lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); + } } } @@ -165,7 +171,7 @@ impl<'a, T: UserData> Registry<'a, T> { } } -impl<'a, T: UserData + LuaDrop> Registry<'a, T> { +impl<'a, T: UserData + LuaDrop, C: NameConvert> Registry<'a, T, C> { pub fn add_gc_method_with_lua_drop(&self) { extern "C-unwind" fn run_lua_drop(l: State) -> i32 { unsafe { @@ -206,18 +212,18 @@ impl Default for AddGcMethodAuto { } impl AddGcMethod for AddGcMethodAuto { - fn add_gc_method(&self, reg: &Registry) { + fn add_gc_method(&self, reg: &Registry) { reg.add_gc_method_with_lua_drop(); } } impl AddGcMethod for &AddGcMethodAuto { - fn add_gc_method(&self, reg: &Registry) { + fn add_gc_method(&self, reg: &Registry) { reg.add_gc_method(); } } -impl<'a, T: UserData> Drop for Registry<'a, T> { +impl<'a, T: UserData, C: NameConvert> Drop for Registry<'a, T, C> { fn drop(&mut self) { if std::mem::needs_drop::() && self.has_gc.get().is_none() { warning!("No __gc method registered on a drop userdata type!"); diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 88634f9..5a38e77 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Cow; use std::ffi::CStr; use crate::vm::userdata::{Error, core::Registry}; use crate::vm::Vm; @@ -33,7 +34,7 @@ use crate::vm::Vm; pub trait UserData: Sized { const CLASS_NAME: &'static CStr; - fn register(registry: &Registry) -> Result<(), Error>; + fn register(registry: &Registry) -> Result<(), Error>; } pub unsafe trait UserDataImmutable: UserData {} @@ -43,5 +44,9 @@ pub trait LuaDrop { } pub trait AddGcMethod { - fn add_gc_method(&self, reg: &Registry); + fn add_gc_method(&self, reg: &Registry); +} + +pub trait NameConvert { + fn name_convert(&self, name: &'static CStr) -> Cow<'static, CStr>; } diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs index 9530fa3..51ee746 100644 --- a/core/src/vm/userdata/mod.rs +++ b/core/src/vm/userdata/mod.rs @@ -30,6 +30,7 @@ mod interface; mod error; pub mod core; mod any; +pub mod case; pub use error::Error; pub use interface::*; diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 5088b69..d3846fb 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -136,8 +136,8 @@ fn test_vm_lib_os_time() { std::thread::sleep(std::time::Duration::from_millis(500)); vm.run_code::<()>(c" local function testDateTime(a, b) - local ymd = a:get_date() - local ymd2 = b:get_date() + local ymd = a:getDate() + local ymd2 = b:getDate() assert(ymd.year == ymd2.year) assert(ymd.month == ymd2.month) assert(ymd.day == ymd2.day) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 0e3eda6..7606183 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -145,29 +145,29 @@ fn test_vm_userdata_forgot_reg() { fn test_vm_userdata_error_handling() { let vm = RootVm::new(); let top = vm.top(); - vm.register_userdata::().unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); assert_eq!(top, vm.top()); - let res = vm.register_userdata::(); + let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: violation of the unique type rule for mutable method \"replace\""); assert_eq!(top, vm.top()); - let res = vm.register_userdata::(); + let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: too strict alignment required (16 bytes), max is 8 bytes"); assert_eq!(top, vm.top()); - let res = vm.register_userdata::(); + let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: __gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop"); assert_eq!(top, vm.top()); - let res = vm.register_userdata::(); + let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: class name \"MyInt\" has already been registered"); assert_eq!(top, vm.top()); - let res = vm.register_userdata::(); + let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!(msg, "userdata: __index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index"); @@ -180,7 +180,7 @@ fn test_vm_userdata_base(vm: &Vm) { LUA_DROP_COUNTER = 0; } let top = vm.top(); - vm.register_userdata::().unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); assert_eq!(top, vm.top()); vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); assert_eq!(top, vm.top()); From 7249e921d03b37edf908ce6e24494fdf49ea364b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 22:18:49 +0200 Subject: [PATCH 211/527] Added mappings for capitalise/decapitalise and sub functions --- core/src/libs/util/string.rs | 18 +++++++++++++++- core/src/libs/util/utf8.rs | 41 ++++++++++++++++++++++++++++++++++-- 2 files changed, 56 insertions(+), 3 deletions(-) diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 05a84a7..b97a260 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::borrow::Cow; +use bp3d_util::string::BufTools; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; @@ -53,6 +55,18 @@ decl_lib_func! { } } +decl_lib_func! { + fn capitalise(src: &[u8]) -> Cow<[u8]> { + src.capitalise_ascii() + } +} + +decl_lib_func! { + fn decapitalise(src: &[u8]) -> Cow<[u8]> { + src.decapitalise_ascii() + } +} + pub struct String; impl Lib for String { @@ -61,7 +75,9 @@ impl Lib for String { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([ ("contains", RFunction::wrap(contains)), - ("split", RFunction::wrap(split)) + ("split", RFunction::wrap(split)), + ("capitalise", RFunction::wrap(capitalise)), + ("decapitalise", RFunction::wrap(decapitalise)) ]) } } diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index f753e64..bcf14c2 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::borrow::Cow; +use bp3d_util::string::StrTools; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; @@ -81,7 +82,38 @@ decl_lib_func! { } } -//TODO: implement function to substring respecting UTF8 codes (instead of the panicking rust version). +decl_lib_func! { + fn capitalise(src: &str) -> Cow { + src.capitalise() + } +} + +decl_lib_func! { + fn decapitalise(src: &str) -> Cow { + src.decapitalise() + } +} + +decl_lib_func! { + fn upper(src: &str) -> String { + src.to_uppercase() + } +} + +decl_lib_func! { + fn lower(src: &str) -> String { + src.to_lowercase() + } +} + +decl_lib_func! { + fn sub(src: &str, start: u32, end: Option) -> &str { + match end { + None => src.sub_nearest((start as usize)..), + Some(v) => src.sub_nearest((start as usize)..(v as usize)) + } + } +} pub struct Utf8; @@ -96,7 +128,12 @@ impl Lib for Utf8 { ("count", RFunction::wrap(count)), ("charAt", RFunction::wrap(char_at)), ("fromString", RFunction::wrap(from_string)), - ("fromStringLossy", RFunction::wrap(from_string_lossy)) + ("fromStringLossy", RFunction::wrap(from_string_lossy)), + ("capitalise", RFunction::wrap(capitalise)), + ("decapitalise", RFunction::wrap(decapitalise)), + ("upper", RFunction::wrap(upper)), + ("lower", RFunction::wrap(lower)), + ("sub", RFunction::wrap(sub)) ]) } } From faa4e0d84d621ef1d21e2d23ccdaf7c6883eddff Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 14 Apr 2025 22:21:06 +0200 Subject: [PATCH 212/527] Added small test for utf8.sub --- core/tests/test_vm_libs.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index d3846fb..8da8bdd 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -111,6 +111,8 @@ fn test_vm_lib_util() { assert(utf8.charAt('abc', 0) == 0x61) assert(utf8.charAt('abc', 1) == 0x62) assert(utf8.charAt('abc', 2) == 0x63) + local s = '我是' + assert(utf8.sub(s, 1) == '是') ").unwrap(); vm.run_code::<()>(c" local tbl = { value = 42 } From 1af18e976a0eace28347455983329f62e4b5eb7c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 19:31:43 +0200 Subject: [PATCH 213/527] Added support for cloning lua values --- core/src/vm/table/core.rs | 7 +++++++ core/src/vm/thread.rs | 9 +++++++++ core/src/vm/userdata/any.rs | 9 ++++++++- core/src/vm/value/any.rs | 2 +- core/src/vm/value/function.rs | 7 +++++++ 5 files changed, 32 insertions(+), 2 deletions(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index b1758c9..fed6dba 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -40,6 +40,13 @@ pub struct Table<'a> { index: i32 } +impl Clone for Table<'_> { + fn clone(&self) -> Self { + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + Table { vm: self.vm, index: self.vm.top() } + } +} + impl PartialEq for Table<'_> { fn eq(&self, other: &Self) -> bool { let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index b865449..1bbcb7f 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -49,6 +49,15 @@ pub struct Thread<'a> { useless: PhantomData<&'a ()> } +impl Clone for Thread<'_> { + fn clone(&self) -> Self { + Self { + vm: unsafe { Vm::from_raw(self.vm.as_ptr()) }, + useless: PhantomData + } + } +} + impl PartialEq for Thread<'_> { fn eq(&self, other: &Self) -> bool { self.vm.as_ptr() == other.vm.as_ptr() diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 0bdb477..c59879a 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -28,7 +28,7 @@ use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_topointer, lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{lua_pushvalue, lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::FromLua; @@ -39,6 +39,13 @@ pub struct AnyUserData<'a> { index: i32 } +impl Clone for AnyUserData<'_> { + fn clone(&self) -> Self { + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + AnyUserData { vm: self.vm, index: self.vm.top() } + } +} + impl PartialEq for AnyUserData<'_> { fn eq(&self, other: &Self) -> bool { let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 8eb79e3..99df9d7 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -39,7 +39,7 @@ use crate::vm::userdata::AnyUserData; use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::Vm; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Clone)] pub enum AnyValue<'a> { None, Nil, diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index ecdd29a..7c5c10f 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -44,6 +44,13 @@ pub struct LuaFunction<'a> { index: i32 } +impl Clone for LuaFunction<'_> { + fn clone(&self) -> Self { + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + LuaFunction { vm: self.vm, index: self.vm.top() } + } +} + impl PartialEq for LuaFunction<'_> { fn eq(&self, other: &Self) -> bool { let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; From 351683797adafc9afdf05f23ac54df5a94448648 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 19:33:28 +0200 Subject: [PATCH 214/527] Renamed set/get to seti/geti --- core/src/libs/lua/base.rs | 2 +- core/src/libs/util/string.rs | 2 +- core/src/libs/util/table.rs | 2 +- core/src/libs/util/utf8.rs | 2 +- core/src/vm/table/core.rs | 4 ++-- 5 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index 8e22b4c..1dd43c7 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -51,7 +51,7 @@ impl Lib for Base { let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); for (i, name) in PATCH_LIST.into_iter().enumerate() { // Lua indices starts at 1 not 0. - patches.set((i + 1) as _, *name)?; + patches.seti((i + 1) as _, *name)?; } namespace.add([("patches", patches)]) } diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index b97a260..80b55ac 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -49,7 +49,7 @@ decl_lib_func! { let mut tbl = Table::new(vm); for (i, v) in split.enumerate() { // Indices starts at 1 in lua. - tbl.set((i + 1) as _, v)?; + tbl.seti((i + 1) as _, v)?; } Ok(tbl) } diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 89a4cd0..d763a55 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -46,7 +46,7 @@ decl_lib_func! { AnyValue::String(name) => { dst.set_field(name, v)? }, - AnyValue::Number(num) => dst.set(num as _, v)?, + AnyValue::Number(num) => dst.seti(num as _, v)?, _ => return Err(Error::Type(TypeError { expected: Type::String, actual: k.ty() diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index bcf14c2..cb8307a 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -46,7 +46,7 @@ decl_lib_func! { let mut tbl = Table::new(vm); for (i, v) in split.enumerate() { // Indices starts at 1 in lua. - tbl.set((i + 1) as _, v)?; + tbl.seti((i + 1) as _, v)?; } Ok(tbl) } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index fed6dba..2bdde8c 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -173,7 +173,7 @@ impl<'a> Table<'a> { } } - pub fn set(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { + pub fn seti(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let nums = value.into_lua(self.vm); if nums != 1 { @@ -186,7 +186,7 @@ impl<'a> Table<'a> { Ok(()) } - pub fn get<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { + pub fn geti<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { if T::num_values() != 1 { return Err(crate::vm::error::Error::MultiValue); } From 4f0b81d25b15cb0be505700c090da65172771bcf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 20:06:30 +0200 Subject: [PATCH 215/527] Added support for get/set functions on table with any key type and any value type --- core/src/vm/table/core.rs | 37 ++++++++++++++++++++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 2bdde8c..0e3fb74 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -28,7 +28,7 @@ use std::fmt::{Debug, Display}; use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settop, lua_topointer}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_settop, lua_topointer}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; @@ -195,4 +195,39 @@ impl<'a> Table<'a> { T::from_lua(self.vm, -1) } } + + pub fn set(&mut self, key: impl IntoLua, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let nums = key.into_lua(self.vm); + if nums != 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + let nums = value.into_lua(self.vm); + if nums != 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_settable(self.vm.as_ptr(), self.index); + } + Ok(()) + } + + pub fn get<'b, T: FromLua<'b>>(&'b self, key: impl IntoLua) -> crate::vm::Result { + if T::num_values() != 1 { + return Err(crate::vm::error::Error::MultiValue); + } + unsafe { + let nums = key.into_lua(self.vm); + if nums != 1 { + // Clear the stack. + lua_settop(self.vm.as_ptr(), -(nums as i32)-1); + return Err(crate::vm::error::Error::MultiValue); + } + lua_gettable(self.vm.as_ptr(), self.index); + T::from_lua(self.vm, -1) + } + } } From 2fa9ba90f88e9fb1ccff6e0dfb44ff58f0068429 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 21:50:31 +0200 Subject: [PATCH 216/527] Added support to append at the end of a lua table --- core/src/vm/table/core.rs | 47 +++++++++++++-------------------------- core/src/vm/value/util.rs | 20 ++++++++++++++++- 2 files changed, 35 insertions(+), 32 deletions(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 0e3fb74..cac8135 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -28,11 +28,12 @@ use std::fmt::{Debug, Display}; use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_settop, lua_topointer}; +use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::util::ensure_single_into_lua; use crate::vm::Vm; pub struct Table<'a> { @@ -152,12 +153,7 @@ impl<'a> Table<'a> { pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - let nums = value.into_lua(self.vm); - if nums != 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } + ensure_single_into_lua(self.vm, value)?; lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); } Ok(()) @@ -175,12 +171,7 @@ impl<'a> Table<'a> { pub fn seti(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - let nums = value.into_lua(self.vm); - if nums != 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } + ensure_single_into_lua(self.vm, value)?; lua_rawseti(self.vm.as_ptr(), self.index, i); } Ok(()) @@ -198,18 +189,8 @@ impl<'a> Table<'a> { pub fn set(&mut self, key: impl IntoLua, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - let nums = key.into_lua(self.vm); - if nums != 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } - let nums = value.into_lua(self.vm); - if nums != 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } + ensure_single_into_lua(self.vm, key)?; + ensure_single_into_lua(self.vm, value)?; lua_settable(self.vm.as_ptr(), self.index); } Ok(()) @@ -220,14 +201,18 @@ impl<'a> Table<'a> { return Err(crate::vm::error::Error::MultiValue); } unsafe { - let nums = key.into_lua(self.vm); - if nums != 1 { - // Clear the stack. - lua_settop(self.vm.as_ptr(), -(nums as i32)-1); - return Err(crate::vm::error::Error::MultiValue); - } + ensure_single_into_lua(self.vm, key)?; lua_gettable(self.vm.as_ptr(), self.index); T::from_lua(self.vm, -1) } } + + pub fn push(&mut self, value: impl IntoLua) -> crate::vm::Result<()> { + unsafe { + let len = lua_objlen(self.vm.as_ptr(), self.index); + ensure_single_into_lua(self.vm, value)?; + lua_rawseti(self.vm.as_ptr(), self.index, len as i32 + 1); + } + Ok(()) + } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 1e471a2..c5f1758 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushnil, lua_pushvalue, lua_replace, Type}; +use crate::ffi::lua::{lua_pushnil, lua_pushvalue, lua_replace, lua_settop, Type}; use crate::vm::error::{Error, TypeError}; +use crate::vm::value::IntoLua; use crate::vm::Vm; /// Ensures the given lua value at index is of a specified type. @@ -58,3 +59,20 @@ pub fn ensure_value_top(vm: &Vm, index: i32) { } } } + +/// Ensures a single value is pushed onto the lua stack, this function automatically reverts the +/// stack if value pushed more than 1 element onto the stack. +/// +/// # Arguments +/// +/// * `vm`: the vm to operate on. +/// * `value`: the value to be placed on the lua stack. +pub fn ensure_single_into_lua(vm: &Vm, value: impl IntoLua) -> crate::vm::Result<()> { + let nums = value.into_lua(vm); + if nums != 1 { + // Clear the stack. + unsafe { lua_settop(vm.as_ptr(), -(nums as i32)-1) }; + return Err(Error::MultiValue); + } + Ok(()) +} From bb66246c659ea370e77dab9bc973583b27f90e30 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 22:26:43 +0200 Subject: [PATCH 217/527] Added new iter module to core module to support iterating the lua stack --- core/src/vm/core/iter.rs | 58 ++++++++++++++++++++++++++++++++++++++++ core/src/vm/core/mod.rs | 1 + 2 files changed, 59 insertions(+) create mode 100644 core/src/vm/core/iter.rs diff --git a/core/src/vm/core/iter.rs b/core/src/vm/core/iter.rs new file mode 100644 index 0000000..eeadf63 --- /dev/null +++ b/core/src/vm/core/iter.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use crate::vm::value::FromLua; +use crate::vm::Vm; + +pub struct Iter<'a, T> { + vm: &'a Vm, + index: i32, + useless: PhantomData +} + +impl<'a, T: FromLua<'a>> Iterator for Iter<'a, T> { + type Item = crate::vm::Result; + + fn next(&mut self) -> Option { + if self.index > self.vm.top() { + return None; + } + let item = T::from_lua(self.vm, self.index); + self.index += 1; + Some(item) + } +} + +pub fn start<'a, T: FromLua<'a>>(vm: &'a Vm, start_index: i32) -> Iter<'a, T> { + Iter { + vm, + index: start_index, + useless: PhantomData + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 60eb7f7..2cd6ddb 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -30,6 +30,7 @@ mod interface; pub mod util; mod vm; pub mod load; +pub mod iter; pub use vm::{Vm, RootVm}; pub use interface::*; From c9aeb9e0d6d74727169dfc7ad42f94fb9c854ce6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Apr 2025 22:27:16 +0200 Subject: [PATCH 218/527] Added a few more utilities to bp3d.util.table --- core/src/libs/util/table.rs | 65 ++++++++++++++++++++++++++++--------- core/tests/test_vm_libs.rs | 31 ++++++++++++++++++ 2 files changed, 80 insertions(+), 16 deletions(-) diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index d763a55..45dcc75 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -29,34 +29,65 @@ use bp3d_util::simple_error; use crate::vm::table::Table as LuaTable; use crate::decl_lib_func; -use crate::ffi::lua::Type; use crate::libs::Lib; -use crate::vm::error::{Error, TypeError}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::AnyValue; +use crate::vm::Vm; + +fn update_rec(vm: &Vm, mut dst: LuaTable, mut src: LuaTable) -> crate::vm::Result<()> { + for res in src.iter() { + let (k, v) = res?; + match v { + AnyValue::Table(v) => { + vm.scope(|_| { + let dst1: Option = dst.get(k.clone())?; + match dst1 { + None => { + let tbl = LuaTable::new(vm); + update_rec(vm, tbl.clone(), v)?; + dst.set(k, tbl)?; + }, + Some(v1) => update_rec(vm, v1, v)? + } + Ok(()) + })? + } + _ => dst.set(k, v)? + } + } + Ok(()) +} decl_lib_func! { - fn update(dst: LuaTable, src: LuaTable) -> crate::vm::Result<()> { - let mut src = src; + fn update(vm: &Vm, dst: LuaTable, src: LuaTable) -> crate::vm::Result<()> { + update_rec(vm, dst, src) + } +} + +decl_lib_func! { + fn concat(vm: &Vm, dst: LuaTable) -> crate::vm::Result<()> { let mut dst = dst; - for res in src.iter() { - let (k, v) = res?; - match k { - AnyValue::String(name) => { - dst.set_field(name, v)? - }, - AnyValue::Number(num) => dst.seti(num as _, v)?, - _ => return Err(Error::Type(TypeError { - expected: Type::String, - actual: k.ty() - })) + let iter = crate::vm::core::iter::start::(vm, 2); + for res in iter { + let mut src = res?; + for res in src.iter() { + let (_, v) = res?; + dst.push(v)?; } } Ok(()) } } +decl_lib_func! { + fn copy<'a>(vm: &Vm, src: LuaTable) -> crate::vm::Result> { + let tbl = crate::vm::table::Table::new(vm); + update_rec(vm, tbl.clone(), src)?; + Ok(tbl) + } +} + decl_lib_func! { fn count(src: LuaTable) -> u64 { src.len() as _ @@ -145,7 +176,9 @@ impl Lib for Table { ("tostring", RFunction::wrap(to_string)), ("contains", RFunction::wrap(contains)), ("containsKey", RFunction::wrap(contains_key)), - ("protect", RFunction::wrap(protect)) + ("protect", RFunction::wrap(protect)), + ("copy", RFunction::wrap(copy)), + ("concat", RFunction::wrap(concat)) ]) } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 8da8bdd..d6c4080 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -124,6 +124,37 @@ fn test_vm_lib_util() { local protected = bp3d.util.table.protect(tbl) protected.value = 84 ").unwrap_err(); + vm.run_code::<()>(c" + local src = { value = 42, adding = { a = 1 } } + local dst = { value = 42, adding = { } } + bp3d.util.table.update(dst, src) + assert(dst.value == 42) + assert(dst.adding.a == 1) + local dst2 = { value = 42 } + bp3d.util.table.update(dst2, src) + assert(dst2.value == 42) + assert(dst2.adding.a == 1) + ").unwrap(); + vm.run_code::<()>(c" + local src = { value = 42, adding = { a = 1 } } + local dst = bp3d.util.table.copy(src) + assert(dst.value == 42) + assert(dst.adding.a == 1) + dst.adding.b = 2 + dst.b = 84 + assert(dst.b == 84) + assert(src.b == nil) + assert(dst.adding.b == 2) + assert(src.adding.b == nil) + ").unwrap(); + vm.run_code::<()>(c" + local list = { 1, 2, 3, 4 } + local list2 = { 5, 6, 7, 8 } + bp3d.util.table.concat(list, list2) + assert(#list == 8) + local str = bp3d.util.table.tostring(list) + assert(str == '1: 1\\n2: 2\\n3: 3\\n4: 4\\n5: 5\\n6: 6\\n7: 7\\n8: 8') + ").unwrap(); assert_eq!(vm.top(), top); } From b2c3a5eb9a94e9f53cdd7343a7bda6092be05232 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 16 Apr 2025 21:46:12 +0200 Subject: [PATCH 219/527] Added support for lua_sethook to FFI --- core/src/ffi/lua.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 0921053..43b0f91 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -221,8 +221,23 @@ extern "C" { //------------------------- // Miscellaneous functions //------------------------- +pub type Debug = *mut c_void; +pub type Hook = extern "C-unwind" fn(l: State, debug: Debug); + +const HOOKCALL: c_int = 0; +const HOOKRET: c_int = 1; +const HOOKLINE: c_int = 2; +const HOOKCOUNT: c_int = 3; + +pub const MASKCALL: c_int = 1 << HOOKCALL; +pub const MASKRET: c_int = 1 << HOOKRET; +pub const MASKLINE: c_int = 1 << HOOKLINE; +pub const MASKCOUNT: c_int = 1 << HOOKCOUNT; + extern "C" { pub fn lua_error(l: State) -> c_int; pub fn lua_next(l: State, idx: c_int) -> c_int; pub fn lua_concat(l: State, n: c_int); + + pub fn lua_sethook(l: State, hook: Hook, mask: c_int, count: c_int) -> c_int; } From 5a02aae400af514a0a78008c110c851e75b2923c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 16 Apr 2025 21:46:44 +0200 Subject: [PATCH 220/527] Added initial design for Vm interruption system --- core/Cargo.toml | 4 + core/src/vm/core/interrupt/mod.rs | 47 ++++++++++ core/src/vm/core/interrupt/unix.rs | 136 +++++++++++++++++++++++++++++ core/src/vm/core/mod.rs | 3 + 4 files changed, 190 insertions(+) create mode 100644 core/src/vm/core/interrupt/mod.rs create mode 100644 core/src/vm/core/interrupt/unix.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 19b9dc2..516b72c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -21,9 +21,13 @@ bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } time = { version = "0.3.37", features = ["formatting"] } itertools = { version = "0.14.0" } +[target.'cfg(unix)'.dependencies] +libc = { version = "0.2.170", optional = true } + [build-dependencies] bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } phf = { version = "0.11.3", features = ["macros"] } cc = "1.2.15" [features] +interrupt = ["libc"] diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs new file mode 100644 index 0000000..c1365a3 --- /dev/null +++ b/core/src/vm/core/interrupt/mod.rs @@ -0,0 +1,47 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! This module contains tools to allow interrupting a root Vm. + +#[cfg(unix)] +mod unix; + +#[cfg(unix)] +pub use unix::Signal; + +unsafe impl Send for Signal {} +unsafe impl Sync for Signal {} + +use bp3d_util::simple_error; +simple_error! { + pub Error { + AlreadyInterrupting => "attempt to interrupt a Vm while interrupting a different Vm", + IncorrectThread => "attempt to interrupt a Vm from the wrong thread", + Timeout => "the lua hook did not trigger in the requested time (is the JIT enabled?)" + } +} diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs new file mode 100644 index 0000000..9ce3476 --- /dev/null +++ b/core/src/vm/core/interrupt/unix.rs @@ -0,0 +1,136 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::sync::{Mutex, Once}; +use std::thread::ThreadId; +use std::time::Duration; +use bp3d_debug::error; +use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; +use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCOUNT}; +use crate::vm::core::interrupt::Error; +use crate::vm::RootVm; + +pub struct Signal { + l: State, + thread: ThreadId, + th: pthread_t +} + +struct SigState { + l: State, + thread: ThreadId, + return_chan: std::sync::mpsc::Sender>, + notify_chan: std::sync::mpsc::Sender<()>, +} + +unsafe impl Send for SigState {} + +static SIG_STATE: Mutex> = Mutex::new(None); + +extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { + { + let mut state = SIG_STATE.lock().unwrap(); + if let Some(sig) = state.take() { + let _ = sig.notify_chan.send(()); + } + } + unsafe { + lua_pushstring(l, c"interrupted".as_ptr()); + lua_error(l); + } +} + +extern "C" fn signal_handler(_: c_int) { + let res = SIG_STATE.try_lock(); + match res { + Ok(mut v) => { + if let Some(v) = &*v { + let current_id = std::thread::current().id(); + if current_id != v.thread { + v.return_chan.send(Err(Error::IncorrectThread)).unwrap(); + return; + } + // Run the hook 1 instruction later. + unsafe { lua_sethook(v.l, lua_interrupt, MASKCOUNT, 1) }; + v.return_chan.send(Ok(())).unwrap(); + } + } + Err(e) => { + error!({error=?e}, "Attempt to interrupt a Vm while interrupting a different Vm"); + } + } +} + +static SIG_BOUND: Once = Once::new(); + +impl Signal { + pub fn create(vm: &mut RootVm) -> Self { + let th = unsafe { pthread_self() }; + let l = vm.as_ptr(); + let thread = std::thread::current().id(); + SIG_BOUND.call_once(|| { + let sig = libc::sigaction { + sa_sigaction: signal_handler as _, + sa_mask: 0, + sa_flags: 0, + }; + unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; + }); + Self { + l, + thread, + th + } + } + + pub fn send(&self, duration: Duration) -> Result<(), Error> { + let (send, recv) = std::sync::mpsc::channel(); + let (send2, recv2) = std::sync::mpsc::channel(); + { + let mut lock = SIG_STATE.try_lock().map_err(|_| Error::AlreadyInterrupting)?; + *lock = Some(SigState { + l: self.l, + thread: self.thread, + return_chan: send, + notify_chan: send2, + }); + } + unsafe { pthread_kill(self.th, SIGUSR1) }; + recv.recv().unwrap()?; + match recv2.recv_timeout(duration) { + Ok(()) => Ok(()), + Err(_) => { + { + let mut guard = SIG_STATE.lock().unwrap(); + *guard = None; + } + Err(Error::Timeout) + } + } + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 2cd6ddb..c3a853e 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -32,5 +32,8 @@ mod vm; pub mod load; pub mod iter; +#[cfg(feature = "interrupt")] +pub mod interrupt; + pub use vm::{Vm, RootVm}; pub use interface::*; From d23921399f8669dcdd622bf72552cd2ac6de605a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 16 Apr 2025 21:55:13 +0200 Subject: [PATCH 221/527] Added spawn_interruptible function to simplify passing Signal out of a thread. --- core/src/vm/core/interrupt/mod.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index c1365a3..1cf9107 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -45,3 +45,13 @@ simple_error! { Timeout => "the lua hook did not trigger in the requested time (is the JIT enabled?)" } } + +pub fn spawn_interruptible(f: impl FnOnce(&mut crate::vm::RootVm) + Send + 'static) -> Signal { + let (send, recv) = std::sync::mpsc::channel(); + std::thread::spawn(move || { + let mut vm = crate::vm::RootVm::new(); + send.send(Signal::create(&mut vm)).unwrap(); + f(&mut vm); + }); + recv.recv().unwrap() +} From 51488e43419c18c7eedcb93e6584436d475fa643 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 16 Apr 2025 22:28:19 +0200 Subject: [PATCH 222/527] Added better error handling to vm interrupt system and added test --- core/src/vm/core/interrupt/mod.rs | 12 ++++--- core/src/vm/core/interrupt/unix.rs | 10 ++++-- core/tests/test_vm_interrupt.rs | 53 ++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 8 deletions(-) create mode 100644 core/tests/test_vm_interrupt.rs diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index 1cf9107..dc901bc 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -31,6 +31,7 @@ #[cfg(unix)] mod unix; +use std::thread::JoinHandle; #[cfg(unix)] pub use unix::Signal; @@ -42,16 +43,17 @@ simple_error! { pub Error { AlreadyInterrupting => "attempt to interrupt a Vm while interrupting a different Vm", IncorrectThread => "attempt to interrupt a Vm from the wrong thread", - Timeout => "the lua hook did not trigger in the requested time (is the JIT enabled?)" + Timeout => "the lua hook did not trigger in the requested time (is the JIT enabled?)", + Unknown => "unknown system error" } } -pub fn spawn_interruptible(f: impl FnOnce(&mut crate::vm::RootVm) + Send + 'static) -> Signal { +pub fn spawn_interruptible(f: impl FnOnce(&mut crate::vm::RootVm) -> R + Send + 'static) -> (Signal, JoinHandle) { let (send, recv) = std::sync::mpsc::channel(); - std::thread::spawn(move || { + let handle = std::thread::spawn(move || { let mut vm = crate::vm::RootVm::new(); send.send(Signal::create(&mut vm)).unwrap(); - f(&mut vm); + f(&mut vm) }); - recv.recv().unwrap() + (recv.recv().unwrap(), handle) } diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 9ce3476..87f78c8 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -68,7 +68,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { extern "C" fn signal_handler(_: c_int) { let res = SIG_STATE.try_lock(); match res { - Ok(mut v) => { + Ok(v) => { if let Some(v) = &*v { let current_id = std::thread::current().id(); if current_id != v.thread { @@ -99,7 +99,8 @@ impl Signal { sa_mask: 0, sa_flags: 0, }; - unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; + let ret = unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; + assert_eq!(ret, 0); }); Self { l, @@ -120,7 +121,10 @@ impl Signal { notify_chan: send2, }); } - unsafe { pthread_kill(self.th, SIGUSR1) }; + let ret = unsafe { pthread_kill(self.th, SIGUSR1) }; + if ret != 0 { + return Err(Error::Unknown); + } recv.recv().unwrap()?; match recv2.recv_timeout(duration) { Ok(()) => Ok(()), diff --git a/core/tests/test_vm_interrupt.rs b/core/tests/test_vm_interrupt.rs new file mode 100644 index 0000000..0cf8ad5 --- /dev/null +++ b/core/tests/test_vm_interrupt.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#![cfg(feature = "interrupt")] + +use std::time::Duration; +use bp3d_lua::libs::Lib; +use bp3d_lua::vm::core::interrupt; + +#[test] +fn test_vm_interrupt() { + let (signal, handle) = interrupt::spawn_interruptible(|vm| { + bp3d_lua::libs::os::Compat.register(vm).unwrap(); + // Run the malicious code. + vm.run_code::<()>(c" + local tbl = {} + while (true) do + local time = os.date('%H:%M:%S') + table.insert(tbl, time) + end + ") + }); + // Give the chance to the thread to run and pump a bit of RAM. + std::thread::sleep(Duration::from_millis(500)); + signal.send(Duration::from_secs(2)).unwrap(); + let res = handle.join().unwrap(); + assert!(res.is_err()); +} From 1c55329d3a498d470404255df65826a3575deeb2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 10:34:33 +0200 Subject: [PATCH 223/527] Fixed bug in os compat lib and added additional test --- core/src/libs/os/base.rs | 6 +++--- core/tests/test_vm_libs.rs | 6 ++++++ 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/base.rs index abf7099..1cf242a 100644 --- a/core/src/libs/os/base.rs +++ b/core/src/libs/os/base.rs @@ -63,6 +63,8 @@ fn get_std_offset() -> UtcOffset { } const REPLACEMENTS: &[(&str, &str)] = &[ + ("[", "[["), + ("%%", "%"), ("%a", "[weekday repr:short]"), ("%A", "[weekday repr:long]"), ("%b", "[month repr:short]"), @@ -76,9 +78,7 @@ const REPLACEMENTS: &[(&str, &str)] = &[ ("%S", "[second]"), ("%w", "[weekday]"), ("%Y", "[year]"), - ("%y", "[year repr:last_two]"), - ("%%", "%"), - ("[", "[["), + ("%y", "[year repr:last_two]") ]; decl_lib_func! { diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index d6c4080..38b0741 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -210,4 +210,10 @@ fn test_vm_lib_os() { local now = os.clock() assert((clock - now) < 0.1) ").unwrap(); + let s = vm.run_code::<&str>(c" + return os.date('%H:%M:%S') + ").unwrap(); + assert!(s.contains(":")); + assert!(!s.contains("[")); + assert!(!s.contains("]")); } From f007e8d8075b714bf1c20bfa4bbcfdc21b912d5e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 10:36:37 +0200 Subject: [PATCH 224/527] Added initial support for dynamic linking against luajit --- core/Cargo.toml | 2 ++ core/build.rs | 43 ++++++++++++++++++++++++++++++++++++++++--- core/src/lib.rs | 3 +++ 3 files changed, 45 insertions(+), 3 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 516b72c..95a55fa 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -9,6 +9,7 @@ repository = "https://gitlab.com/bp3d/lua" readme = "../README.MD" keywords = ["lua"] categories = ["graphics"] +publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -31,3 +32,4 @@ cc = "1.2.15" [features] interrupt = ["libc"] +dynamic = [] diff --git a/core/build.rs b/core/build.rs index 59d0e5d..1ac8320 100644 --- a/core/build.rs +++ b/core/build.rs @@ -83,6 +83,34 @@ fn build_luajit(build_dir: &Path) { } } +#[cfg(feature="dynamic")] +fn post_build(build_dir: &Path) { + let target_name = std::env::var("TARGET").expect("Failed to read build target"); + let target = TARGET_MAP.get(&target_name).unwrap_or(&Target::Unsupported); + match target { + Target::MacAmd64 | Target::MacAarch64 => { + //TODO: parse crate version. + let path_to_so = build_dir.join("src"); + let cmd = Command::new("install_name_tool").args(["-id", "libbp3d-luajit-1.0.0-rc.1.0.0.dylib", "libluajit.so"]) + .current_dir(&path_to_so).status().unwrap(); + assert!(cmd.success()); + let path_to_dylib = build_dir.join("libbp3d-luajit-1.0.0-rc.1.0.0.dylib"); + std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib).expect("Failed to copy bp3d luajit dynamic lib"); + let path_to_dylib2 = build_dir.join("../../../../libbp3d-luajit-1.0.0-rc.1.0.0.dylib"); + std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib2).expect("Failed to copy bp3d luajit dynamic lib"); + }, + Target::Linux => { + let path_to_so = build_dir.join("src").join("libluajit.so"); + let path_to_dylib = build_dir.join("libbp3d-luajit-1.0.0-rc.1.0.0.so"); + std::fs::copy(&path_to_so, path_to_dylib).expect("Failed to copy bp3d luajit dynamic lib"); + let path_to_dylib2 = build_dir.join("../../../../libbp3d-luajit-1.0.0-rc.1.0.0.so"); + std::fs::copy(path_to_so, path_to_dylib2).expect("Failed to copy bp3d luajit dynamic lib"); + }, + Target::Windows => {}, + Target::Unsupported => panic!("Unsupported build target {}", target_name) + } +} + fn apply_patch(path: &Path, name: &str) { let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch").join(name).as_os_str()]); if !result.success() { @@ -109,7 +137,6 @@ fn main() { apply_patch(&path, "disable_lua_load.patch"); // Disable loadstring, dostring, etc from base lib. apply_patch(&path, "lua_ext.patch"); // Ext library such as lua_ext_tab_len, etc. apply_patch(&path, "lua_load_no_bc.patch"); // Treat all inputs as strings (no bytecode allowed). - //TODO: Patch to re-enable os lib with time and date // Copy the source directory to the build directory. println!("{}", out_path.display()); @@ -122,8 +149,18 @@ fn main() { // Build LuaJIT. build_luajit(&out_path); + #[cfg(feature="dynamic")] + post_build(&out_path); // Attempt to setup linkage. - println!("cargo:rustc-link-search=native={}", out_path.join("src").display()); - println!("cargo:rustc-link-lib=static:-bundle,+whole-archive=luajit"); + #[cfg(not(feature="dynamic"))] + { + println!("cargo:rustc-link-search=native={}", out_path.join("src").display()); + println!("cargo:rustc-link-lib=static:-bundle,+whole-archive=luajit"); + } + #[cfg(feature="dynamic")] + { + println!("cargo:rustc-link-search=native={}", out_path.display()); + println!("cargo:rustc-link-lib=dylib=bp3d-luajit-1.0.0-rc.1.0.0"); + } } diff --git a/core/src/lib.rs b/core/src/lib.rs index 7c59496..62fa985 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,6 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//TODO: Support dynamic linking and modules by dynamic linking to luajit. +//TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. + pub mod ffi; pub mod vm; mod r#macro; From 42aba341acb72b7a6b0a56f0c2d64fa79bbdbedf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 10:44:19 +0200 Subject: [PATCH 225/527] Updated LuaJIT submodule --- LuaJIT | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/LuaJIT b/LuaJIT index a4f56a4..eec7a80 160000 --- a/LuaJIT +++ b/LuaJIT @@ -1 +1 @@ -Subproject commit a4f56a459a588ae768801074b46ba0adcfb49eb1 +Subproject commit eec7a8016c3381b949b5d84583800d05897fa960 From 4aad98c94fece44aa9854a118a00b0aa9f453d50 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 10:46:39 +0200 Subject: [PATCH 226/527] Updated workflows --- .github/workflows/development.yml | 143 +++--------------------------- .github/workflows/release.yml | 44 +-------- 2 files changed, 16 insertions(+), 171 deletions(-) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index 64e8000..a5ed8d4 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -5,137 +5,18 @@ on: - workflow_dispatch jobs: - test-build: - name: Build & Test - strategy: - matrix: - os: - - ubuntu-20.04 - - macos-11 - - windows-2019 - runs-on: ${{ matrix.os }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Build - run: cargo build --all-features - - name: Test - uses: bp3d-actions/cargo@main - with: - check-name: cargo test (${{ matrix.os }}) - command: test - args: --all-features --no-fail-fast - token: ${{ secrets.GITHUB_TOKEN }} + Test: + uses: BlockProject3D/workflows/.github/workflows/Build_Test.yml@main - clippy: - name: Check | Clippy - if: ${{ always() }} - needs: test-build - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - components: clippy - - name: Run check - uses: bp3d-actions/clippy-check@main - with: - token: ${{ secrets.GITHUB_TOKEN }} - args: --all-features + Analyze: + uses: BlockProject3D/workflows/.github/workflows/Analyze.yml@main + needs: Test - audit: - name: Check | Audit - if: ${{ always() }} - needs: test-build - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Install Audit Tool - run: cargo install cargo-audit - - name: Run check - uses: bp3d-actions/audit-check@main - with: - token: ${{ secrets.GITHUB_TOKEN }} + Coverage: + uses: BlockProject3D/workflows/.github/workflows/Coverage.yml@main - fmt: - name: Format Code - if: ${{ always() && github.ref != 'refs/heads/master' }} - needs: - - clippy - - audit - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - with: - components: rustfmt - - name: Run code formatter - uses: bp3d-actions/rustfmt-check@main - with: - token: ${{ secrets.GITHUB_TOKEN }} - - version: - name: Get Version - needs: test-build - runs-on: ubuntu-latest - outputs: - name: ${{ steps.version.outputs.name }} - version: ${{ steps.version.outputs.version }} - isnew: ${{ steps.version.outputs.isnew }} - ispre: ${{ steps.version.outputs.ispre }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Get Version - id: version - uses: bp3d-actions/cargo-version@main - with: - mode: get - token: ${{ secrets.GITHUB_TOKEN }} - - create-pre-release: - name: Create Pre Release - needs: version - if: github.ref == 'refs/heads/develop' && needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'true' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Setup cargo - run: cargo login ${{ secrets.RELEASE_TOKEN }} - - name: Publish - run: cargo publish - - name: Create - uses: ncipollo/release-action@main - with: - tag: ${{ needs.version.outputs.version }} - commit: ${{ github.ref }} - prerelease: true - name: ${{ needs.version.outputs.name }} release ${{ needs.version.outputs.version }} - body: "[Link to crates.io](https://crates.io/crates/${{ needs.version.outputs.name }})" - - create-release-pr: - name: Create Release Pull Request - needs: version - if: needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'false' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Create Pull Request - uses: repo-sync/pull-request@master - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - destination_branch: master - pr_title: Release ${{ needs.version.outputs.version }} + Release: + uses: BlockProject3D/workflows/.github/workflows/Release.yml@main + needs: Test + secrets: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cf2e432..b4df58a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -6,43 +6,7 @@ on: - master jobs: - version: - name: Get Version - runs-on: ubuntu-latest - outputs: - name: ${{ steps.version.outputs.name }} - version: ${{ steps.version.outputs.version }} - isnew: ${{ steps.version.outputs.isnew }} - ispre: ${{ steps.version.outputs.ispre }} - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Get Version - id: version - uses: bp3d-actions/cargo-version@main - with: - mode: get - token: ${{ secrets.GITHUB_TOKEN }} - - create-release: - name: Release - needs: version - if: needs.version.outputs.isnew == 'true' && needs.version.outputs.ispre == 'false' - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - name: Install Rust - uses: dtolnay/rust-toolchain@stable - - name: Setup cargo - run: cargo login ${{ secrets.RELEASE_TOKEN }} - - name: Publish - run: cargo publish - - name: Create - uses: ncipollo/release-action@main - with: - tag: ${{ needs.version.outputs.version }} - commit: ${{ github.ref }} - prerelease: false - name: ${{ needs.version.outputs.name }} release ${{ needs.version.outputs.version }} - body: "[Link to crates.io](https://crates.io/crates/${{ needs.version.outputs.name }})" + Publish: + uses: BlockProject3D/workflows/.github/workflows/Publish.yml@main + secrets: + RELEASE_TOKEN: ${{ secrets.RELEASE_TOKEN }} From 27d6a8958dc5302f48042b143eb65bce2532a088 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 16:08:55 +0200 Subject: [PATCH 227/527] Fixed vm interrupt under Linux --- core/src/vm/core/interrupt/unix.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 87f78c8..3730987 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::mem::MaybeUninit; use std::sync::{Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; @@ -94,11 +95,8 @@ impl Signal { let l = vm.as_ptr(); let thread = std::thread::current().id(); SIG_BOUND.call_once(|| { - let sig = libc::sigaction { - sa_sigaction: signal_handler as _, - sa_mask: 0, - sa_flags: 0, - }; + let mut sig: libc::sigaction = unsafe { MaybeUninit::zeroed().assume_init() }; + sig.sa_sigaction = signal_handler as _; let ret = unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; assert_eq!(ret, 0); }); From 03ab2fa8b491a7fce3a54a48a772ee2e7e956a79 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 16:33:07 +0200 Subject: [PATCH 228/527] Fixed crash in lua_interrupt --- core/src/vm/core/interrupt/unix.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 3730987..51406e9 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -32,7 +32,7 @@ use std::thread::ThreadId; use std::time::Duration; use bp3d_debug::error; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; -use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCOUNT}; +use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCOUNT}; use crate::vm::core::interrupt::Error; use crate::vm::RootVm; @@ -61,6 +61,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { } } unsafe { + lua_sethook(l, std::mem::transmute::<*const (), Hook>(std::ptr::null()), 0, 0); lua_pushstring(l, c"interrupted".as_ptr()); lua_error(l); } From fcef6ee952e0832d58999f433e359341cb4f56a6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 16:34:08 +0200 Subject: [PATCH 229/527] Fixed dynamic linking under linux --- core/build.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/build.rs b/core/build.rs index 1ac8320..cb0fb69 100644 --- a/core/build.rs +++ b/core/build.rs @@ -67,6 +67,7 @@ fn build_luajit(build_dir: &Path) { .env("MACOSX_DEPLOYMENT_TARGET", "11.0") .current_dir(build_dir).status(), Target::Linux => Command::new("make") + .arg("TARGET_SONAME=libbp3d-luajit-1.0.0-rc.1.0.0.so") .current_dir(build_dir).status(), Target::Windows => { let mut cmd = Command::new("msvcbuild.bat"); From d4838919c1d0788af08850d8f6ac79fd3d31d58b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 19:37:11 +0200 Subject: [PATCH 230/527] Added new build support crate and refactored build script to use new build support crate --- Cargo.toml | 1 + build/Cargo.toml | 10 +++ build/src/build/interface.rs | 49 ++++++++++ build/src/build/linux.rs | 71 +++++++++++++++ build/src/build/mac.rs | 79 ++++++++++++++++ build/src/build/mod.rs | 38 ++++++++ build/src/build/windows.rs | 77 ++++++++++++++++ build/src/info.rs | 76 ++++++++++++++++ build/src/lib.rs | 37 ++++++++ build/src/patch.rs | 71 +++++++++++++++ build/src/target.rs | 53 +++++++++++ build/src/util.rs | 49 ++++++++++ core/Cargo.toml | 3 +- core/build.rs | 144 ++++++++---------------------- patch/windows_set_lib_names.patch | 13 +++ 15 files changed, 660 insertions(+), 111 deletions(-) create mode 100644 build/Cargo.toml create mode 100644 build/src/build/interface.rs create mode 100644 build/src/build/linux.rs create mode 100644 build/src/build/mac.rs create mode 100644 build/src/build/mod.rs create mode 100644 build/src/build/windows.rs create mode 100644 build/src/info.rs create mode 100644 build/src/lib.rs create mode 100644 build/src/patch.rs create mode 100644 build/src/target.rs create mode 100644 build/src/util.rs create mode 100644 patch/windows_set_lib_names.patch diff --git a/Cargo.toml b/Cargo.toml index f4f3147..111b8f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,6 @@ [workspace] members = [ + "build", "codegen", "core", "testbin" diff --git a/build/Cargo.toml b/build/Cargo.toml new file mode 100644 index 0000000..fbd0328 --- /dev/null +++ b/build/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bp3d-lua-build" +version = "1.0.0-rc.1.0.0" +edition = "2024" +publish = false + +[dependencies] +bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } +phf = { version = "0.11.3", features = ["macros"] } +cc = "1.2.15" diff --git a/build/src/build/interface.rs b/build/src/build/interface.rs new file mode 100644 index 0000000..067f8a0 --- /dev/null +++ b/build/src/build/interface.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::PathBuf; +use crate::BuildInfo; +use crate::util::CommandRunner; + +pub struct Lib { + pub name: String, + pub path: PathBuf, + pub dynamic: bool +} + +pub trait Build { + fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()>; + fn post_build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()>; + fn get_linked_lib(info: &BuildInfo) -> Lib; + + fn run(info: &BuildInfo) -> std::io::Result { + Self::build(info, &CommandRunner::new("failed to build LuaJIT"))?; + Self::post_build(info, &CommandRunner::new("failed to post-build LuaJIT"))?; + Ok(Self::get_linked_lib(info)) + } +} diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs new file mode 100644 index 0000000..75342b1 --- /dev/null +++ b/build/src/build/linux.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::process::Command; +use crate::build::Build; +use crate::BuildInfo; +use crate::build::interface::Lib; +use crate::util::CommandRunner; + +pub struct Linux; + +impl Build for Linux { + fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { + let soname = format!("TARGET_SONAME=libbp3d-luajit-{}.so", info.version()); + runner.run(Command::new("make") + .arg(soname) + .current_dir(info.build_dir())) + } + + fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { + let filename = format!("libbp3d-luajit-{}.so", info.version()); + let path_to_so = info.build_dir().join("src").join("libluajit.so"); + let path_to_dylib = info.build_dir().join(&filename); + std::fs::copy(&path_to_so, path_to_dylib)?; + let path_to_dylib2 = info.target_dir().join(filename); + std::fs::copy(path_to_so, path_to_dylib2)?; + Ok(()) + } + + fn get_linked_lib(info: &BuildInfo) -> Lib { + if info.dynamic() { + let name = format!("bp3d-luajit-{}", info.version()); + Lib { + name, + path: info.build_dir().into(), + dynamic: true + } + } else { + Lib { + name: "luajit".into(), + path: info.build_dir().into(), + dynamic: false + } + } + } +} diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs new file mode 100644 index 0000000..78a1d7a --- /dev/null +++ b/build/src/build/mac.rs @@ -0,0 +1,79 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::io::{Error, ErrorKind}; +use std::process::Command; +use crate::{BuildInfo, Target}; +use crate::build::Build; +use crate::build::interface::Lib; +use crate::util::CommandRunner; + +pub struct MacOS; + +impl Build for MacOS { + fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { + match info.target() { + Target::MacAarch64 => runner.run(Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "11.0") + .current_dir(info.build_dir())), + Target::MacAmd64 => runner.run(Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "10.11") + .current_dir(info.build_dir())), + _ => Err(Error::new(ErrorKind::Other, "unsupported target")) + } + } + + fn post_build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { + let filename = format!("libbp3d-luajit-{}.dylib", info.version()); + let path_to_so = info.build_dir().join("src"); + runner.run(Command::new("install_name_tool").args(["-id", &filename, "libluajit.so"]) + .current_dir(&path_to_so))?; + let path_to_dylib = info.build_dir().join(&filename); + std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib)?; + let path_to_dylib2 = info.target_dir().join(filename); + std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib2)?; + Ok(()) + } + + fn get_linked_lib(info: &BuildInfo) -> Lib { + if info.dynamic() { + let name = format!("bp3d-luajit-{}", info.version()); + Lib { + name, + path: info.build_dir().into(), + dynamic: true, + } + } else { + Lib { + name: "luajit".into(), + path: info.build_dir().into(), + dynamic: false + } + } + } +} diff --git a/build/src/build/mod.rs b/build/src/build/mod.rs new file mode 100644 index 0000000..ff5083f --- /dev/null +++ b/build/src/build/mod.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod linux; +mod mac; +mod windows; +mod interface; + +pub use interface::*; + +pub use windows::Windows; +pub use linux::Linux; +pub use mac::MacOS; diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs new file mode 100644 index 0000000..77a85e0 --- /dev/null +++ b/build/src/build/windows.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::io::{Error, ErrorKind}; +use std::process::Command; +use crate::build::Build; +use crate::BuildInfo; +use crate::build::interface::Lib; +use crate::util::CommandRunner; + +pub struct Windows; + +impl Build for Windows { + fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { + let mut cmd = Command::new("msvcbuild.bat"); + let cl = cc::windows_registry::find_tool(info.target_name(), "cl.exe") + .ok_or(Error::new(ErrorKind::Other, "unable to find cl.exe"))?; + for (k, v) in cl.env() { + cmd.env(k, v); + } + if !info.dynamic() { + cmd.arg("static"); + } + let dllname = format!("libbp3d-luajit-{}.dll", info.version()); + let libname = format!("libbp3d-luajit-{}.lib", info.version()); + cmd.env("LJDLLNAME", dllname); + cmd.env("LJLIBNAME", libname); + runner.run(cmd.current_dir(info.build_dir().join("src"))) + } + + fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { + if !info.dynamic() { //Nothing to be done in non-dynamic builds. + return Ok(()) + } + let dllname = format!("libbp3d-luajit-{}.dll", info.version()); + let path_to_dll = info.build_dir().join("src").join(&dllname); + let path_to_dylib = info.build_dir().join(&dllname); + std::fs::copy(&path_to_dll, path_to_dylib)?; + let path_to_dylib2 = info.target_dir().join(dllname); + std::fs::copy(path_to_dll, path_to_dylib2)?; + Ok(()) + } + + fn get_linked_lib(info: &BuildInfo) -> Lib { + let libname = format!("libbp3d-luajit-{}.lib", info.version()); + Lib { + name: libname, + path: info.build_dir().join("src"), + dynamic: info.dynamic() + } + } +} diff --git a/build/src/info.rs b/build/src/info.rs new file mode 100644 index 0000000..493b0e8 --- /dev/null +++ b/build/src/info.rs @@ -0,0 +1,76 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::io::{Error, ErrorKind}; +use std::path::{Path, PathBuf}; +use crate::Target; + +pub struct BuildInfo { + build_dir: PathBuf, + target_dir: PathBuf, + crate_version: String, + target_name: String, + dynamic: bool +} + +const VERSION: &str = "version = \""; + +impl BuildInfo { + pub fn new(dynamic: bool, target_name: String, build_dir: PathBuf, path_to_manifest: &Path) -> std::io::Result { + let manifest = std::fs::read_to_string(path_to_manifest)?; + let target_dir = build_dir.join("../../../.."); + let start = manifest.find(VERSION).ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + let version = &manifest[start + VERSION.len()..]; + let end = version.find("\"").ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + Ok(Self { dynamic, target_name, build_dir, target_dir, crate_version: String::from(&version[..end]) }) + } + + pub fn build_dir(&self) -> &Path { + &self.build_dir + } + + pub fn dynamic(&self) -> bool { + self.dynamic + } + + pub fn target(&self) -> Target { + Target::get(&self.target_name) + } + + pub fn target_name(&self) -> &str { + &self.target_name + } + + pub fn target_dir(&self) -> &Path { + &self.target_dir + } + + pub fn version(&self) -> &str { + &self.crate_version + } +} diff --git a/build/src/lib.rs b/build/src/lib.rs new file mode 100644 index 0000000..660a645 --- /dev/null +++ b/build/src/lib.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod patch; +mod target; +mod util; +mod info; +pub mod build; + +pub use crate::patch::Patch; +pub use crate::target::Target; +pub use crate::info::BuildInfo; diff --git a/build/src/patch.rs b/build/src/patch.rs new file mode 100644 index 0000000..77212bb --- /dev/null +++ b/build/src/patch.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; +use std::process::Command; +use crate::util::CommandRunner; + +pub struct Patch { + src_path: PathBuf, + patch_dir: PathBuf, + patch_list: Vec +} + +impl Patch { + pub fn new(patch_dir: &Path, luajit_src: &Path) -> std::io::Result { + let patch_dir = bp3d_os::fs::get_absolute_path(patch_dir)?; + let src_path = bp3d_os::fs::get_absolute_path(luajit_src)?; + CommandRunner::new("failed to revert").run(Command::new("git") + .args(&["checkout", "."]).current_dir(&src_path))?; + Ok(Patch { + patch_dir, + src_path, + patch_list: Vec::new() + }) + } + + pub fn apply(&mut self, name: &str) -> std::io::Result<()> { + CommandRunner::new("failed to patch").run(Command::new("git") + .args(&[OsStr::new("apply"), self.patch_dir.join(format!("{}.patch", name)).as_os_str()]) + .current_dir(&self.src_path))?; + self.patch_list.push(name.into()); + Ok(()) + } + + pub fn get_patch_list(&self) -> impl Iterator { + self.patch_list.iter().map(|v| &**v) + } +} + +impl Drop for Patch { + fn drop(&mut self) { + CommandRunner::new("failed to revert").run(Command::new("git") + .args(&["checkout", "."]).current_dir(&self.src_path)).unwrap(); + } +} diff --git a/build/src/target.rs b/build/src/target.rs new file mode 100644 index 0000000..a0ed000 --- /dev/null +++ b/build/src/target.rs @@ -0,0 +1,53 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use phf::phf_map; + +#[derive(Copy, Clone, Eq, PartialEq)] +pub enum Target { + MacAmd64, + MacAarch64, + Linux, + Windows, + Unsupported +} + +static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { + "aarch64-apple-darwin" => Target::MacAarch64, + "aarch64-unknown-linux-gnu" => Target::Linux, + "i686-pc-windows-msvc" => Target::Windows, + "x86_64-pc-windows-msvc" => Target::Windows, + "x86_64-apple-darwin" => Target::MacAmd64, + "x86_64-unknown-linux-gnu" => Target::Linux +}; + +impl Target { + pub fn get(name: &str) -> Target { + *TARGET_MAP.get(name).unwrap_or(&Target::Unsupported) + } +} diff --git a/build/src/util.rs b/build/src/util.rs new file mode 100644 index 0000000..4525a0d --- /dev/null +++ b/build/src/util.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::io::{Error, ErrorKind}; +use std::process::Command; + +pub struct CommandRunner { + msg: &'static str +} + +impl CommandRunner { + pub fn new(msg: &'static str) -> CommandRunner { + Self { msg } + } + + pub fn run(&self, cmd: &mut Command) -> std::io::Result<()> { + let stat = cmd.status()?; + if stat.success() { + Ok(()) + } else { + Err(Error::new(ErrorKind::Other, "failed to patch")) + } + } +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 95a55fa..2b7dcb6 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -26,9 +26,8 @@ itertools = { version = "0.14.0" } libc = { version = "0.2.170", optional = true } [build-dependencies] +bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } -phf = { version = "0.11.3", features = ["macros"] } -cc = "1.2.15" [features] interrupt = ["libc"] diff --git a/core/build.rs b/core/build.rs index cb0fb69..6d2f0c4 100644 --- a/core/build.rs +++ b/core/build.rs @@ -27,95 +27,41 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::ffi::OsStr; -use std::path::Path; -use std::process::{Command, ExitStatus}; +use std::path::{Path, PathBuf}; use bp3d_os::fs::CopyOptions; -use phf::phf_map; +use bp3d_lua_build::{BuildInfo, Patch, Target}; +use bp3d_lua_build::build::{Build, Lib, Linux, MacOS, Windows}; -pub enum Target { - MacAmd64, - MacAarch64, - Linux, - Windows, - Unsupported -} - -static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { - "aarch64-apple-darwin" => Target::MacAarch64, - "aarch64-unknown-linux-gnu" => Target::Linux, - "i686-pc-windows-msvc" => Target::Windows, - "x86_64-pc-windows-msvc" => Target::Windows, - "x86_64-apple-darwin" => Target::MacAmd64, - "x86_64-unknown-linux-gnu" => Target::Linux -}; +#[cfg(feature = "dynamic")] +const DYNAMIC: bool = true; +#[cfg(not(feature = "dynamic"))] +const DYNAMIC: bool = false; -fn run_command_in_luajit(text: &str, cmd: &str, args: impl IntoIterator>) -> ExitStatus { - let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); - Command::new(cmd) - .args(args) - .current_dir(path.join("LuaJIT")).status().expect(text) -} +fn apply_patches(out_path: &Path) -> std::io::Result<()> { + let mut patch = Patch::new(&Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"))?; + patch.apply("lib_init")?; // Disable unsafe/un-sandboxed libs. + patch.apply("lj_disable_jit")?; // Disable global JIT state changes from Lua code. + patch.apply("disable_lua_load")?; // Disable loadstring, dostring, etc from base lib. + patch.apply("lua_ext")?; // Ext library such as lua_ext_tab_len, etc. + patch.apply("lua_load_no_bc")?; // Treat all inputs as strings (no bytecode allowed). -fn build_luajit(build_dir: &Path) { - let target_name = std::env::var("TARGET").expect("Failed to read build target"); - let target = TARGET_MAP.get(&target_name).unwrap_or(&Target::Unsupported); - let cmd = match target { - Target::MacAmd64 => Command::new("make") - .env("MACOSX_DEPLOYMENT_TARGET", "10.11") - .current_dir(build_dir).status(), - Target::MacAarch64 => Command::new("make") - .env("MACOSX_DEPLOYMENT_TARGET", "11.0") - .current_dir(build_dir).status(), - Target::Linux => Command::new("make") - .arg("TARGET_SONAME=libbp3d-luajit-1.0.0-rc.1.0.0.so") - .current_dir(build_dir).status(), - Target::Windows => { - let mut cmd = Command::new("msvcbuild.bat"); - let cl = cc::windows_registry::find_tool(&target_name, "cl.exe").expect("failed to find cl"); - for (k, v) in cl.env() { - cmd.env(k, v); - } - cmd.current_dir(build_dir.join("src")).status() - }, - Target::Unsupported => panic!("Unsupported build target {}", target_name) - }.expect("Failed to run build command"); - if !cmd.success() { - panic!("Failed to build LuaJIT"); + // Copy the source directory to the build directory. + println!("{}", out_path.display()); + if !out_path.is_dir() { + bp3d_os::fs::copy(&Path::new("..").join("LuaJIT"), &out_path, CopyOptions::new().exclude(OsStr::new(".git")))?; } + Ok(()) } -#[cfg(feature="dynamic")] -fn post_build(build_dir: &Path) { +fn run_build(build_dir: &Path) -> std::io::Result { + let manifest = std::env::var_os("CARGO_MANIFEST_PATH").map(PathBuf::from).expect("Failed to read manifest path"); let target_name = std::env::var("TARGET").expect("Failed to read build target"); - let target = TARGET_MAP.get(&target_name).unwrap_or(&Target::Unsupported); - match target { - Target::MacAmd64 | Target::MacAarch64 => { - //TODO: parse crate version. - let path_to_so = build_dir.join("src"); - let cmd = Command::new("install_name_tool").args(["-id", "libbp3d-luajit-1.0.0-rc.1.0.0.dylib", "libluajit.so"]) - .current_dir(&path_to_so).status().unwrap(); - assert!(cmd.success()); - let path_to_dylib = build_dir.join("libbp3d-luajit-1.0.0-rc.1.0.0.dylib"); - std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib).expect("Failed to copy bp3d luajit dynamic lib"); - let path_to_dylib2 = build_dir.join("../../../../libbp3d-luajit-1.0.0-rc.1.0.0.dylib"); - std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib2).expect("Failed to copy bp3d luajit dynamic lib"); - }, - Target::Linux => { - let path_to_so = build_dir.join("src").join("libluajit.so"); - let path_to_dylib = build_dir.join("libbp3d-luajit-1.0.0-rc.1.0.0.so"); - std::fs::copy(&path_to_so, path_to_dylib).expect("Failed to copy bp3d luajit dynamic lib"); - let path_to_dylib2 = build_dir.join("../../../../libbp3d-luajit-1.0.0-rc.1.0.0.so"); - std::fs::copy(path_to_so, path_to_dylib2).expect("Failed to copy bp3d luajit dynamic lib"); - }, - Target::Windows => {}, - Target::Unsupported => panic!("Unsupported build target {}", target_name) - } -} - -fn apply_patch(path: &Path, name: &str) { - let result = run_command_in_luajit("Failed to patch LuaJIT", "git", &[OsStr::new("apply"), path.join("patch").join(name).as_os_str()]); - if !result.success() { - panic!("Failed to patch LuaJIT"); + let info = BuildInfo::new(DYNAMIC, target_name, build_dir.into(), &manifest)?; + match info.target() { + Target::MacAmd64 | Target::MacAarch64 => MacOS::run(&info), + Target::Linux => Linux::run(&info), + Target::Windows => Windows::run(&info), + Target::Unsupported => panic!("attempt to build on currently unsupported target") } } @@ -124,44 +70,24 @@ fn main() { println!("cargo:rerun-if-changed=build.rs"); println!("cargo:rerun-if-changed=patch"); - // Revert patch to LuaJIT source code and cleanup. - run_command_in_luajit("Failed to revert LuaJIT patch", "git", &["checkout", "."]); - // Create build directory. let out = std::env::var_os("OUT_DIR").expect("Failed to acquire output directory"); let out_path = Path::new(&out).join("luajit-build"); // Apply patches to LuaJIT source code. - let path = bp3d_os::fs::get_absolute_path("../").expect("Failed to acquire current path"); - apply_patch(&path, "lib_init.patch"); // Disable unsafe/un-sandboxed libs. - apply_patch(&path, "lj_disable_jit.patch"); // Disable global JIT state changes from Lua code. - apply_patch(&path, "disable_lua_load.patch"); // Disable loadstring, dostring, etc from base lib. - apply_patch(&path, "lua_ext.patch"); // Ext library such as lua_ext_tab_len, etc. - apply_patch(&path, "lua_load_no_bc.patch"); // Treat all inputs as strings (no bytecode allowed). + apply_patches(&out_path).expect("Failed to patch LuaJIT"); // Copy the source directory to the build directory. - println!("{}", out_path.display()); - if !out_path.is_dir() { - bp3d_os::fs::copy(&path.join("LuaJIT"), &out_path, CopyOptions::new().exclude(OsStr::new(".git"))).expect("Failed to copy LuaJIT sources to build directory"); - } - - // Revert patch to LuaJIT source code and cleanup. - run_command_in_luajit("Failed to revert LuaJIT patch", "git", &["checkout", "."]); + println!("Internal LuaJIT build directory: {}", out_path.display()); // Build LuaJIT. - build_luajit(&out_path); - #[cfg(feature="dynamic")] - post_build(&out_path); + let lib = run_build(&out_path).expect("Failed to build LuaJIT"); // Attempt to setup linkage. - #[cfg(not(feature="dynamic"))] - { - println!("cargo:rustc-link-search=native={}", out_path.join("src").display()); - println!("cargo:rustc-link-lib=static:-bundle,+whole-archive=luajit"); - } - #[cfg(feature="dynamic")] - { - println!("cargo:rustc-link-search=native={}", out_path.display()); - println!("cargo:rustc-link-lib=dylib=bp3d-luajit-1.0.0-rc.1.0.0"); + println!("cargo:rustc-link-search=native={}", lib.path.display()); + if lib.dynamic { + println!("cargo:rustc-link-lib=dylib={}", lib.name); + } else { + println!("cargo:rustc-link-lib=static={}", lib.name); } } diff --git a/patch/windows_set_lib_names.patch b/patch/windows_set_lib_names.patch new file mode 100644 index 0000000..38b48df --- /dev/null +++ b/patch/windows_set_lib_names.patch @@ -0,0 +1,13 @@ +diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat +index 69c0c61a..0665eaa3 100644 +--- a/src/msvcbuild.bat ++++ b/src/msvcbuild.bat +@@ -29,8 +29,6 @@ + @set DASMDIR=..\dynasm + @set DASM=%DASMDIR%\dynasm.lua + @set DASC=vm_x64.dasc +-@set LJDLLNAME=lua51.dll +-@set LJLIBNAME=lua51.lib + @set ALL_LIB=lib_base.c lib_math.c lib_bit.c lib_string.c lib_table.c lib_io.c lib_os.c lib_package.c lib_debug.c lib_jit.c lib_ffi.c lib_buffer.c + + @setlocal From 431eace183d5cab31543527bdf7060ae25d43cd2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 19:38:54 +0200 Subject: [PATCH 231/527] Added new windows_set_lib_names patch --- core/build.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/build.rs b/core/build.rs index 6d2f0c4..2b83cad 100644 --- a/core/build.rs +++ b/core/build.rs @@ -44,6 +44,8 @@ fn apply_patches(out_path: &Path) -> std::io::Result<()> { patch.apply("disable_lua_load")?; // Disable loadstring, dostring, etc from base lib. patch.apply("lua_ext")?; // Ext library such as lua_ext_tab_len, etc. patch.apply("lua_load_no_bc")?; // Treat all inputs as strings (no bytecode allowed). + patch.apply("windows_set_lib_names")?; // Allow setting LJLIBNAME and LJDLLNAME from + // environment variables. // Copy the source directory to the build directory. println!("{}", out_path.display()); From 91ba98325128a767a45f2657a30df1dc1dd32fdb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 19:39:57 +0200 Subject: [PATCH 232/527] Fixed bug in util --- build/src/util.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/src/util.rs b/build/src/util.rs index 4525a0d..5bdec76 100644 --- a/build/src/util.rs +++ b/build/src/util.rs @@ -43,7 +43,7 @@ impl CommandRunner { if stat.success() { Ok(()) } else { - Err(Error::new(ErrorKind::Other, "failed to patch")) + Err(Error::new(ErrorKind::Other, self.msg)) } } } From c9040936338842c85adccfbc447713ce942e67b6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 22:39:29 +0200 Subject: [PATCH 233/527] Renamed base to compat --- core/src/libs/os/{base.rs => compat.rs} | 0 core/src/libs/os/mod.rs | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename core/src/libs/os/{base.rs => compat.rs} (100%) diff --git a/core/src/libs/os/base.rs b/core/src/libs/os/compat.rs similarity index 100% rename from core/src/libs/os/base.rs rename to core/src/libs/os/compat.rs diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs index 4e63198..a39e48c 100644 --- a/core/src/libs/os/mod.rs +++ b/core/src/libs/os/mod.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod base; +mod compat; mod time; mod instant; -pub use base::Compat; +pub use compat::Compat; pub use time::Time; pub use instant::Instant; From 22a649d464a462c35ae94e5b825fa29802ea4de1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 22:40:04 +0200 Subject: [PATCH 234/527] Added fix for test_vm_lib_os_time under CI --- core/tests/test_vm_libs.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 38b0741..4588a33 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -177,9 +177,13 @@ fn test_vm_lib_os_time() { end local now2 = bp3d.os.time.nowUtc() local now = bp3d.os.time.nowLocal() - assert(now > time) + if (now ~= nil and time ~= nil then + assert(now > time) + end assert(now2 > time2) - testDateTime(now, time) + if (now ~= nil and time ~= nil then + testDateTime(now, time) + end testDateTime(now2, time2) ").unwrap(); } From 87f8d3347fe3d8becfe395c286e1536012aac785 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 22:42:06 +0200 Subject: [PATCH 235/527] Added fix for test_vm_lib_os under CI --- core/tests/test_vm_libs.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 4588a33..2af45d9 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -177,11 +177,11 @@ fn test_vm_lib_os_time() { end local now2 = bp3d.os.time.nowUtc() local now = bp3d.os.time.nowLocal() - if (now ~= nil and time ~= nil then + if (now ~= nil and time ~= nil) then assert(now > time) end assert(now2 > time2) - if (now ~= nil and time ~= nil then + if (now ~= nil and time ~= nil) then testDateTime(now, time) end testDateTime(now2, time2) @@ -215,7 +215,7 @@ fn test_vm_lib_os() { assert((clock - now) < 0.1) ").unwrap(); let s = vm.run_code::<&str>(c" - return os.date('%H:%M:%S') + return os.date('!%H:%M:%S') ").unwrap(); assert!(s.contains(":")); assert!(!s.contains("[")); From 447c89d8b8b134c78c30b2519b38e306691cf89d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Apr 2025 22:45:36 +0200 Subject: [PATCH 236/527] Attempt at fixing interrupt test under CI --- core/tests/test_vm_interrupt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/test_vm_interrupt.rs b/core/tests/test_vm_interrupt.rs index 0cf8ad5..9c87951 100644 --- a/core/tests/test_vm_interrupt.rs +++ b/core/tests/test_vm_interrupt.rs @@ -47,7 +47,7 @@ fn test_vm_interrupt() { }); // Give the chance to the thread to run and pump a bit of RAM. std::thread::sleep(Duration::from_millis(500)); - signal.send(Duration::from_secs(2)).unwrap(); + signal.send(Duration::from_secs(10)).unwrap(); let res = handle.join().unwrap(); assert!(res.is_err()); } From bf778d8bc60af06b8a41b60907d3323107fd4924 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 09:55:02 +0200 Subject: [PATCH 237/527] Added more logs to figure out what went wrong in VM interrupt test --- core/src/vm/core/interrupt/unix.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 51406e9..f34c349 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -30,7 +30,7 @@ use std::mem::MaybeUninit; use std::sync::{Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; -use bp3d_debug::error; +use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCOUNT}; use crate::vm::core::interrupt::Error; @@ -57,7 +57,9 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { { let mut state = SIG_STATE.lock().unwrap(); if let Some(sig) = state.take() { - let _ = sig.notify_chan.send(()); + if let Err(e) = sig.notify_chan.send(()) { + error!({error=?e}, "Failed to notify interrupt signal") + } } } unsafe { @@ -127,7 +129,8 @@ impl Signal { recv.recv().unwrap()?; match recv2.recv_timeout(duration) { Ok(()) => Ok(()), - Err(_) => { + Err(e) => { + warning!({error=?e}, "Error attempting to wait for interrupt notification"); { let mut guard = SIG_STATE.lock().unwrap(); *guard = None; From f65a404a8c8eb5fb7b61101ab6557216c28277e4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 10:11:35 +0200 Subject: [PATCH 238/527] Fixed bug with interrupt system sometimes not calling the hook --- core/src/vm/core/interrupt/unix.rs | 4 ++-- core/tests/test_vm_interrupt.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index f34c349..e7a38b3 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -32,7 +32,7 @@ use std::thread::ThreadId; use std::time::Duration; use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; -use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCOUNT}; +use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET}; use crate::vm::core::interrupt::Error; use crate::vm::RootVm; @@ -80,7 +80,7 @@ extern "C" fn signal_handler(_: c_int) { return; } // Run the hook 1 instruction later. - unsafe { lua_sethook(v.l, lua_interrupt, MASKCOUNT, 1) }; + unsafe { lua_sethook(v.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1) }; v.return_chan.send(Ok(())).unwrap(); } } diff --git a/core/tests/test_vm_interrupt.rs b/core/tests/test_vm_interrupt.rs index 9c87951..7901cbc 100644 --- a/core/tests/test_vm_interrupt.rs +++ b/core/tests/test_vm_interrupt.rs @@ -40,14 +40,14 @@ fn test_vm_interrupt() { vm.run_code::<()>(c" local tbl = {} while (true) do - local time = os.date('%H:%M:%S') + local time = os.date('!%x %H:%M:%S') table.insert(tbl, time) end ") }); // Give the chance to the thread to run and pump a bit of RAM. std::thread::sleep(Duration::from_millis(500)); - signal.send(Duration::from_secs(10)).unwrap(); + signal.send(Duration::from_secs(2)).unwrap(); let res = handle.join().unwrap(); assert!(res.is_err()); } From 4df15b53e2b0d42f100e87e9a2b13365d6557c92 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 12:00:13 +0200 Subject: [PATCH 239/527] Fixed linker error on mac and linux --- build/src/build/linux.rs | 2 +- build/src/build/mac.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs index 75342b1..03976ed 100644 --- a/build/src/build/linux.rs +++ b/build/src/build/linux.rs @@ -63,7 +63,7 @@ impl Build for Linux { } else { Lib { name: "luajit".into(), - path: info.build_dir().into(), + path: info.build_dir().join("src"), dynamic: false } } diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 78a1d7a..4de7d48 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -71,7 +71,7 @@ impl Build for MacOS { } else { Lib { name: "luajit".into(), - path: info.build_dir().into(), + path: info.build_dir().join("src"), dynamic: false } } From 7f96e12aa55a6ac2d2cd07d471aec08919c7a571 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 12:00:31 +0200 Subject: [PATCH 240/527] Refactored and simplified build process --- build/src/info.rs | 41 ++++++++++++++++++++++++++++------------- build/src/lib.rs | 2 +- core/build.rs | 18 +++++++++--------- 3 files changed, 38 insertions(+), 23 deletions(-) diff --git a/build/src/info.rs b/build/src/info.rs index 493b0e8..6aaf90f 100644 --- a/build/src/info.rs +++ b/build/src/info.rs @@ -28,42 +28,48 @@ use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; +use crate::build::{Build, Lib, Linux, MacOS, Windows}; use crate::Target; -pub struct BuildInfo { - build_dir: PathBuf, +pub struct BuildInfoBase<'a> { + pub dynamic: bool, + pub target_name: &'a str, + pub build_dir: &'a Path, + pub manifest: &'a Path +} + +pub struct BuildInfo<'a> { + base: BuildInfoBase<'a>, target_dir: PathBuf, crate_version: String, - target_name: String, - dynamic: bool } const VERSION: &str = "version = \""; -impl BuildInfo { - pub fn new(dynamic: bool, target_name: String, build_dir: PathBuf, path_to_manifest: &Path) -> std::io::Result { - let manifest = std::fs::read_to_string(path_to_manifest)?; - let target_dir = build_dir.join("../../../.."); +impl<'a> BuildInfo<'a> { + pub fn new(base: BuildInfoBase<'a>) -> std::io::Result { + let manifest = std::fs::read_to_string(&base.manifest)?; + let target_dir = base.build_dir.join("../../../.."); let start = manifest.find(VERSION).ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; let version = &manifest[start + VERSION.len()..]; let end = version.find("\"").ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; - Ok(Self { dynamic, target_name, build_dir, target_dir, crate_version: String::from(&version[..end]) }) + Ok(Self { base, target_dir, crate_version: String::from(&version[..end]) }) } pub fn build_dir(&self) -> &Path { - &self.build_dir + &self.base.build_dir } pub fn dynamic(&self) -> bool { - self.dynamic + self.base.dynamic } pub fn target(&self) -> Target { - Target::get(&self.target_name) + Target::get(&self.base.target_name) } pub fn target_name(&self) -> &str { - &self.target_name + &self.base.target_name } pub fn target_dir(&self) -> &Path { @@ -73,4 +79,13 @@ impl BuildInfo { pub fn version(&self) -> &str { &self.crate_version } + + pub fn build(self) -> std::io::Result { + match self.target() { + Target::MacAmd64 | Target::MacAarch64 => MacOS::run(&self), + Target::Linux => Linux::run(&self), + Target::Windows => Windows::run(&self), + Target::Unsupported => Err(Error::new(ErrorKind::Other, "unsupported target")) + } + } } diff --git a/build/src/lib.rs b/build/src/lib.rs index 660a645..b2d0593 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -34,4 +34,4 @@ pub mod build; pub use crate::patch::Patch; pub use crate::target::Target; -pub use crate::info::BuildInfo; +pub use crate::info::{BuildInfo, BuildInfoBase}; diff --git a/core/build.rs b/core/build.rs index 2b83cad..c2b09d6 100644 --- a/core/build.rs +++ b/core/build.rs @@ -29,8 +29,8 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use bp3d_os::fs::CopyOptions; -use bp3d_lua_build::{BuildInfo, Patch, Target}; -use bp3d_lua_build::build::{Build, Lib, Linux, MacOS, Windows}; +use bp3d_lua_build::{BuildInfo, BuildInfoBase, Patch}; +use bp3d_lua_build::build::Lib; #[cfg(feature = "dynamic")] const DYNAMIC: bool = true; @@ -58,13 +58,13 @@ fn apply_patches(out_path: &Path) -> std::io::Result<()> { fn run_build(build_dir: &Path) -> std::io::Result { let manifest = std::env::var_os("CARGO_MANIFEST_PATH").map(PathBuf::from).expect("Failed to read manifest path"); let target_name = std::env::var("TARGET").expect("Failed to read build target"); - let info = BuildInfo::new(DYNAMIC, target_name, build_dir.into(), &manifest)?; - match info.target() { - Target::MacAmd64 | Target::MacAarch64 => MacOS::run(&info), - Target::Linux => Linux::run(&info), - Target::Windows => Windows::run(&info), - Target::Unsupported => panic!("attempt to build on currently unsupported target") - } + let base = BuildInfoBase { + dynamic: DYNAMIC, + target_name: &target_name, + build_dir, + manifest: &manifest + }; + BuildInfo::new(base)?.build() } fn main() { From 1cda0a44a0f9d5ff160def4ddd77f1a62e3530ee Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 12:23:13 +0200 Subject: [PATCH 241/527] Refactored and simplified patch system --- build/src/patch.rs | 11 +++++++++++ core/build.rs | 28 +++++++++++----------------- 2 files changed, 22 insertions(+), 17 deletions(-) diff --git a/build/src/patch.rs b/build/src/patch.rs index 77212bb..aa5f6a9 100644 --- a/build/src/patch.rs +++ b/build/src/patch.rs @@ -29,6 +29,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::process::Command; +use bp3d_os::fs::CopyOptions; use crate::util::CommandRunner; pub struct Patch { @@ -61,6 +62,16 @@ impl Patch { pub fn get_patch_list(&self) -> impl Iterator { self.patch_list.iter().map(|v| &**v) } + + pub fn apply_all<'a>(mut self, patches: impl IntoIterator, out_path: &Path) -> std::io::Result> { + for patch in patches { + self.apply(patch)?; + } + if !out_path.is_dir() { + bp3d_os::fs::copy(&self.src_path, out_path, CopyOptions::new().exclude(OsStr::new(".git")))?; + } + Ok(self.get_patch_list().map(String::from).collect()) + } } impl Drop for Patch { diff --git a/core/build.rs b/core/build.rs index c2b09d6..9091bcc 100644 --- a/core/build.rs +++ b/core/build.rs @@ -26,9 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::OsStr; use std::path::{Path, PathBuf}; -use bp3d_os::fs::CopyOptions; use bp3d_lua_build::{BuildInfo, BuildInfoBase, Patch}; use bp3d_lua_build::build::Lib; @@ -37,22 +35,18 @@ const DYNAMIC: bool = true; #[cfg(not(feature = "dynamic"))] const DYNAMIC: bool = false; -fn apply_patches(out_path: &Path) -> std::io::Result<()> { - let mut patch = Patch::new(&Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"))?; - patch.apply("lib_init")?; // Disable unsafe/un-sandboxed libs. - patch.apply("lj_disable_jit")?; // Disable global JIT state changes from Lua code. - patch.apply("disable_lua_load")?; // Disable loadstring, dostring, etc from base lib. - patch.apply("lua_ext")?; // Ext library such as lua_ext_tab_len, etc. - patch.apply("lua_load_no_bc")?; // Treat all inputs as strings (no bytecode allowed). - patch.apply("windows_set_lib_names")?; // Allow setting LJLIBNAME and LJDLLNAME from - // environment variables. +const PATCH_LIST: &[&str] = &[ + "lib_init", // Disable unsafe/un-sandboxed libs. + "lj_disable_jit", // Disable global JIT state changes from Lua code. + "disable_lua_load", // Disable loadstring, dostring, etc from base lib. + "lua_ext", // Ext library such as lua_ext_tab_len, etc. + "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). + "windows_set_lib_names" // Allow setting LJLIBNAME and LJDLLNAME from environment variables. +]; - // Copy the source directory to the build directory. - println!("{}", out_path.display()); - if !out_path.is_dir() { - bp3d_os::fs::copy(&Path::new("..").join("LuaJIT"), &out_path, CopyOptions::new().exclude(OsStr::new(".git")))?; - } - Ok(()) +fn apply_patches(out_path: &Path) -> std::io::Result> { + Patch::new(&Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"))? + .apply_all(PATCH_LIST.iter().map(|v| *v), out_path) } fn run_build(build_dir: &Path) -> std::io::Result { From 7629cbfbb1545ac5e43acbeb5abfd5a0e67b8014 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 12:26:20 +0200 Subject: [PATCH 242/527] Removed bp3d-os dep from build deps --- core/Cargo.toml | 1 - 1 file changed, 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 2b7dcb6..8ba5c6a 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -27,7 +27,6 @@ libc = { version = "0.2.170", optional = true } [build-dependencies] bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } -bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } [features] interrupt = ["libc"] From 8b9b84eb2ac8aa89b889410dad1c1115f7e007d3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 18:27:37 +0200 Subject: [PATCH 243/527] Added comment to test --- core/src/vm/closure/context.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 138ffe5..9b967ce 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -28,6 +28,9 @@ //! A module to simplify declaring functions with associated to a context (rust object). +//TODO: Investigate if wrapping the raw pointer in a userdata instead of a lightuserdata is any +// faster. + use std::ffi::c_void; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; From 7662d45399ee5833b623312202ae5466dd15e0c8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 21:07:46 +0200 Subject: [PATCH 244/527] Added initial version of context v2 --- core/src/vm/closure/context2.rs | 213 ++++++++++++++++++++++++++++++++ core/src/vm/closure/mod.rs | 1 + core/tests/test_vm_closures.rs | 9 +- 3 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 core/src/vm/closure/context2.rs diff --git a/core/src/vm/closure/context2.rs b/core/src/vm/closure/context2.rs new file mode 100644 index 0000000..e07f7c1 --- /dev/null +++ b/core/src/vm/closure/context2.rs @@ -0,0 +1,213 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +//! Second version of the context tool. + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use crate::ffi::laux::luaL_error; +use crate::ffi::lua::lua_newuserdata; +use crate::util::SimpleDrop; +use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::registry::core::RawRegistryKey; +use crate::vm::Vm; + +pub struct Binder { + ptr: *mut *const T +} + +impl Binder { + pub fn bind<'a>(&self, obj: &'a T) -> Guard<'a, T> { + unsafe { *self.ptr = obj as _ }; + Guard { + useless: PhantomData, + ud: self.ptr + } + } +} + +pub struct BinderMut { + ptr: *mut *const T +} + +impl BinderMut { + pub fn bind<'a>(&self, obj: &'a mut T) -> Guard<'a, T> { + unsafe { *self.ptr = obj as _ }; + Guard { + useless: PhantomData, + ud: self.ptr + } + } +} + +pub struct Context { + key: RawRegistryKey, + ptr: *mut *const T +} + +impl Clone for Context { + fn clone(&self) -> Self { + Self { + key: self.key, + ptr: self.ptr + } + } +} + +impl Copy for Context {} + +pub struct ContextMut(Context); + +impl Clone for ContextMut { + fn clone(&self) -> Self { + Self(self.0) + } +} + +impl Copy for ContextMut { } + +impl Context { + pub fn new(vm: &Vm) -> Self { + let (ptr, key) = unsafe { + let ptr = lua_newuserdata(vm.as_ptr(), 8); + (ptr, RawRegistryKey::from_top(vm)) + }; + Self { + key, + ptr: ptr as *mut *const T + } + } + + pub fn get(self) -> Binder { + Binder { + ptr: self.ptr + } + } +} + +impl ContextMut { + pub fn new(vm: &Vm) -> Self { + Self(Context::new(vm)) + } + + pub fn get(self) -> BinderMut { + BinderMut { + ptr: self.0.ptr + } + } +} + +pub struct Guard<'a, T> { + ud: *mut *const T, + useless: PhantomData<&'a T> +} + +impl<'a, T> Drop for Guard<'a, T> { + fn drop(&mut self) { + unsafe { + *self.ud = std::ptr::null(); + } + } +} + +#[repr(transparent)] +pub struct Ref<'a, T>(&'a T); + +#[repr(transparent)] +pub struct Mut<'a, T>(&'a mut T); + +impl<'a, T: 'static> Deref for Ref<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T: 'static> Deref for Mut<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T: 'static> DerefMut for Mut<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + self.0 + } +} + +unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> { } +unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> { } + +impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let ptr: *mut *const T = FromUpvalue::from_upvalue(vm, index); + if (*ptr).is_null() { + luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + // luaL_error raises a lua exception and unwinds, so this cannot be reached. + std::hint::unreachable_unchecked(); + } + Ref(unsafe { &**ptr }) + } +} + +impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let ptr: *mut *mut T = FromUpvalue::from_upvalue(vm, index); + if (*ptr).is_null() { + luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + // luaL_error raises a lua exception and unwinds, so this cannot be reached. + std::hint::unreachable_unchecked(); + } + Mut(unsafe { &mut **ptr }) + } +} + +impl IntoUpvalue for Context { + fn into_upvalue(self, vm: &Vm) -> u16 { + unsafe { self.key.push(vm) }; + 1 + } +} + +impl IntoUpvalue for ContextMut { + fn into_upvalue(self, vm: &Vm) -> u16 { + unsafe { self.0.key.push(vm) }; + 1 + } +} + +impl Upvalue for Context { + type From<'a> = Ref<'a, T>; +} + +impl Upvalue for ContextMut { + type From<'a> = Mut<'a, T>; +} diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 5ab1f68..7298224 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -31,5 +31,6 @@ mod core; pub mod types; pub mod context; pub mod rc; +pub mod context2; pub use interface::*; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index ed41480..f7f669f 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context::ContextMut; +use bp3d_lua::vm::closure::context2::ContextMut; use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::namespace::Namespace; use bp3d_lua::vm::RootVm; @@ -108,8 +108,9 @@ fn test_vm_context() { value: 0, value3: vec![], }; + let ctx = ctx.get(); { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); vm.run_code::<()>(c"context.set_value(42)").unwrap(); } let res = vm.run_code::<()>(c"context.set_value(84)"); @@ -117,7 +118,7 @@ fn test_vm_context() { assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(84)\"]:1: Context is not available in this function."); assert_eq!(obj.value, 42); { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); vm.run_code::<()>(c"assert(context.pop() == nil)").unwrap(); vm.run_code::<()>(c"context.push(1)").unwrap(); vm.run_code::<()>(c"context.push(2)").unwrap(); @@ -125,7 +126,7 @@ fn test_vm_context() { } assert_eq!(obj.value3.len(), 3); { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); vm.run_code::<()>(c"assert(context.pop() == 3)").unwrap(); vm.run_code::<()>(c"assert(context.pop() == 2)").unwrap(); vm.run_code::<()>(c"assert(context.pop() == 1)").unwrap(); From ce837a2990ccc11e0964ca2aad555f890405de0b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Apr 2025 21:15:49 +0200 Subject: [PATCH 245/527] Added new context_opt2 benchmark --- testbin/src/context_opt2.rs | 141 ++++++++++++++++++++++++++++++++++++ testbin/src/main.rs | 12 +++ 2 files changed, 153 insertions(+) create mode 100644 testbin/src/context_opt2.rs diff --git a/testbin/src/context_opt2.rs b/testbin/src/context_opt2.rs new file mode 100644 index 0000000..4edb931 --- /dev/null +++ b/testbin/src/context_opt2.rs @@ -0,0 +1,141 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use mlua::{Function, Lua, UserDataMethods}; +use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::context2::ContextMut; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::function::LuaFunction; + +struct TestContext { + value3: Vec +} + +decl_closure! { + fn context_push |ctx: ContextMut| (val: u64) -> () { + let mut ctx = ctx; + ctx.value3.push(val); + } +} + +decl_closure! { + fn context_pop |ctx: ContextMut| () -> Option { + let mut ctx = ctx; + ctx.value3.pop() + } +} + +pub fn test_context_mlua() -> Duration { + let lua = Lua::new(); + lua.register_userdata_type::(|reg| { + reg.add_method_mut("push", |_, this, val: u64| { + this.value3.push(val); + Ok(()) + }); + reg.add_method_mut("pop", |_, this, _: ()| { + Ok(this.value3.pop()) + }); + }).unwrap(); + lua.load(" + function part1(ctx) + assert(ctx:pop() == nil) + ctx:push(1) + ctx:push(2) + ctx:push(3) + end + function part2(ctx) + assert(ctx:pop() == 3) + assert(ctx:pop() == 2) + assert(ctx:pop() == 1) + assert(ctx:pop() == nil) + assert(ctx:pop() == nil) + end + ").eval::<()>().unwrap(); + let part1: Function = lua.globals().get("part1").unwrap(); + let part2: Function = lua.globals().get("part2").unwrap(); + let mut ctx = TestContext { + value3: Vec::new() + }; + let time = bp3d_os::time::Instant::now(); + for _ in 0..20000 { + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + part1.call::<()>(ud).unwrap(); + Ok(()) + }).unwrap(); + lua.scope(|l| { + let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); + part2.call::<()>(ud).unwrap(); + Ok(()) + }).unwrap(); + } + let time = time.elapsed(); + time +} + +pub fn test_context_vm() -> Duration { + let vm = RootVm::new(); + let ctx = ContextMut::new(&vm); + vm.set_global(c"context_push", context_push(ctx)).unwrap(); + vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); + vm.run_code::<()>(c" + function part1() + assert(context_pop() == nil) + context_push(1) + context_push(2) + context_push(3) + end + function part2() + assert(context_pop() == 3) + assert(context_pop() == 2) + assert(context_pop() == 1) + assert(context_pop() == nil) + assert(context_pop() == nil) + end + ").unwrap(); + let part1: LuaFunction = vm.get_global("part1").unwrap(); + let part2: LuaFunction = vm.get_global("part2").unwrap(); + let mut obj = TestContext { + value3: vec![], + }; + let ctx = ctx.get(); + let time = bp3d_os::time::Instant::now(); + for _ in 0..20000 { + { + let _obj = ctx.bind(&mut obj); + part1.call::<()>(()).unwrap(); + } + { + let _obj = ctx.bind(&mut obj); + part2.call::<()>(()).unwrap(); + } + } + let time = time.elapsed(); + time +} diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 2c577fe..57e3c6b 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -28,6 +28,7 @@ mod context; mod context_opt; +mod context_opt2; use std::time::Duration; use mlua::Lua; @@ -97,6 +98,8 @@ fn main() { let mut ctx_mlua = Duration::new(0, 0); let mut ctx_lua_opt = Duration::new(0, 0); let mut ctx_mlua_opt = Duration::new(0, 0); + let mut ctx_lua_opt2 = Duration::new(0, 0); + let mut ctx_mlua_opt2 = Duration::new(0, 0); for _ in 0..RUNS { lua += test_vm_destructor(); @@ -105,6 +108,8 @@ fn main() { ctx_mlua += context::test_context_mlua(); ctx_lua_opt += context_opt::test_context_vm(); ctx_mlua_opt += context_opt::test_context_mlua(); + ctx_lua_opt2 += context_opt2::test_context_vm(); + ctx_mlua_opt2 += context_opt2::test_context_mlua(); } lua = lua / RUNS; @@ -113,6 +118,8 @@ fn main() { ctx_mlua = ctx_mlua / RUNS; ctx_lua_opt = ctx_lua_opt / RUNS; ctx_mlua_opt = ctx_mlua_opt / RUNS; + ctx_lua_opt2 = ctx_lua_opt2 / RUNS; + ctx_mlua_opt2 = ctx_mlua_opt2 / RUNS; println!("average tools.lua (basic): {:?}", lua); println!("average mlua (basic): {:?}", mlua); @@ -128,4 +135,9 @@ fn main() { println!("average mlua (context_opt): {:?}", ctx_mlua_opt); assert!(ctx_lua_opt < ctx_mlua_opt); println!("average diff (context_opt): {:?}", ctx_mlua_opt - ctx_lua_opt); + + println!("average tools.lua (context_opt2): {:?}", ctx_lua_opt2); + println!("average mlua (context_opt2): {:?}", ctx_mlua_opt2); + assert!(ctx_lua_opt2 < ctx_mlua_opt2); + println!("average diff (context_opt2): {:?}", ctx_mlua_opt2 - ctx_lua_opt2); } From 9bddaab39b19ebbc987c0d678441bcd1d1f7d362 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Apr 2025 00:23:25 +0200 Subject: [PATCH 246/527] Improved API for context v2 --- core/src/vm/closure/context2.rs | 37 ++++++++++++++------------------- core/tests/test_vm_closures.rs | 10 ++++----- testbin/src/context_opt2.rs | 4 ++-- 3 files changed, 23 insertions(+), 28 deletions(-) diff --git a/core/src/vm/closure/context2.rs b/core/src/vm/closure/context2.rs index e07f7c1..85a604b 100644 --- a/core/src/vm/closure/context2.rs +++ b/core/src/vm/closure/context2.rs @@ -37,12 +37,16 @@ use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::registry::core::RawRegistryKey; use crate::vm::Vm; -pub struct Binder { +pub struct Cell { ptr: *mut *const T } -impl Binder { - pub fn bind<'a>(&self, obj: &'a T) -> Guard<'a, T> { +impl Cell { + pub fn new(ctx: Context) -> Self { + Self { ptr: ctx.ptr } + } + + pub fn bind<'a>(&mut self, obj: &'a T) -> Guard<'a, T> { unsafe { *self.ptr = obj as _ }; Guard { useless: PhantomData, @@ -51,12 +55,16 @@ impl Binder { } } -pub struct BinderMut { +pub struct CellMut { ptr: *mut *const T } -impl BinderMut { - pub fn bind<'a>(&self, obj: &'a mut T) -> Guard<'a, T> { +impl CellMut { + pub fn new(ctx: ContextMut) -> Self { + Self { ptr: ctx.0.ptr } + } + + pub fn bind<'a>(&mut self, obj: &'a mut T) -> Guard<'a, T> { unsafe { *self.ptr = obj as _ }; Guard { useless: PhantomData, @@ -102,26 +110,15 @@ impl Context { ptr: ptr as *mut *const T } } - - pub fn get(self) -> Binder { - Binder { - ptr: self.ptr - } - } } impl ContextMut { pub fn new(vm: &Vm) -> Self { Self(Context::new(vm)) } - - pub fn get(self) -> BinderMut { - BinderMut { - ptr: self.0.ptr - } - } } +#[repr(transparent)] pub struct Guard<'a, T> { ud: *mut *const T, useless: PhantomData<&'a T> @@ -129,9 +126,7 @@ pub struct Guard<'a, T> { impl<'a, T> Drop for Guard<'a, T> { fn drop(&mut self) { - unsafe { - *self.ud = std::ptr::null(); - } + unsafe { *self.ud = std::ptr::null(); } } } diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index f7f669f..bc7af26 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context2::ContextMut; +use bp3d_lua::vm::closure::context2::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::namespace::Namespace; use bp3d_lua::vm::RootVm; @@ -108,9 +108,9 @@ fn test_vm_context() { value: 0, value3: vec![], }; - let ctx = ctx.get(); + let mut cell = CellMut::new(ctx); { - let _obj = ctx.bind(&mut obj); + let _obj = cell.bind(&mut obj); vm.run_code::<()>(c"context.set_value(42)").unwrap(); } let res = vm.run_code::<()>(c"context.set_value(84)"); @@ -118,7 +118,7 @@ fn test_vm_context() { assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(84)\"]:1: Context is not available in this function."); assert_eq!(obj.value, 42); { - let _obj = ctx.bind(&mut obj); + let _obj = cell.bind(&mut obj); vm.run_code::<()>(c"assert(context.pop() == nil)").unwrap(); vm.run_code::<()>(c"context.push(1)").unwrap(); vm.run_code::<()>(c"context.push(2)").unwrap(); @@ -126,7 +126,7 @@ fn test_vm_context() { } assert_eq!(obj.value3.len(), 3); { - let _obj = ctx.bind(&mut obj); + let _obj = cell.bind(&mut obj); vm.run_code::<()>(c"assert(context.pop() == 3)").unwrap(); vm.run_code::<()>(c"assert(context.pop() == 2)").unwrap(); vm.run_code::<()>(c"assert(context.pop() == 1)").unwrap(); diff --git a/testbin/src/context_opt2.rs b/testbin/src/context_opt2.rs index 4edb931..b81f111 100644 --- a/testbin/src/context_opt2.rs +++ b/testbin/src/context_opt2.rs @@ -29,7 +29,7 @@ use std::time::Duration; use mlua::{Function, Lua, UserDataMethods}; use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context2::ContextMut; +use bp3d_lua::vm::closure::context2::{CellMut, ContextMut}; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; @@ -124,7 +124,7 @@ pub fn test_context_vm() -> Duration { let mut obj = TestContext { value3: vec![], }; - let ctx = ctx.get(); + let mut ctx = CellMut::new(ctx); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { { From ed0b74b87a84e9f7ebf2f8238fff21c9a3dccc2d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Apr 2025 09:34:10 +0200 Subject: [PATCH 247/527] Replaced context v1 by context v2 which seems about 2 ms faster --- core/src/vm/closure/context.rs | 124 +++++++++---------- core/src/vm/closure/context2.rs | 208 -------------------------------- core/src/vm/closure/core.rs | 3 + core/src/vm/closure/mod.rs | 1 - core/tests/test_vm_closures.rs | 2 +- testbin/src/context.rs | 7 +- testbin/src/context_opt.rs | 7 +- testbin/src/context_opt2.rs | 141 ---------------------- testbin/src/main.rs | 12 -- 9 files changed, 75 insertions(+), 430 deletions(-) delete mode 100644 core/src/vm/closure/context2.rs delete mode 100644 testbin/src/context_opt2.rs diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 9b967ce..81d89b5 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -26,31 +26,63 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//! A module to simplify declaring functions with associated to a context (rust object). +//! Second version of the context tool. -//TODO: Investigate if wrapping the raw pointer in a userdata instead of a lightuserdata is any -// faster. - -use std::ffi::c_void; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use crate::ffi::laux::luaL_error; -use crate::ffi::lua::{lua_pushlightuserdata, lua_settop, lua_topointer}; +use crate::ffi::lua::lua_newuserdata; use crate::util::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; -use crate::vm::registry::core::{RawRegistryKey}; +use crate::vm::registry::core::RawRegistryKey; use crate::vm::Vm; +pub struct Cell { + ptr: *mut *const T +} + +impl Cell { + pub fn new(ctx: Context) -> Self { + Self { ptr: ctx.ptr } + } + + pub fn bind<'a>(&mut self, obj: &'a T) -> Guard<'a, T> { + unsafe { *self.ptr = obj as _ }; + Guard { + useless: PhantomData, + ud: self.ptr + } + } +} + +pub struct CellMut { + ptr: *mut *const T +} + +impl CellMut { + pub fn new(ctx: ContextMut) -> Self { + Self { ptr: ctx.0.ptr } + } + + pub fn bind<'a>(&mut self, obj: &'a mut T) -> Guard<'a, T> { + unsafe { *self.ptr = obj as _ }; + Guard { + useless: PhantomData, + ud: self.ptr + } + } +} + pub struct Context { key: RawRegistryKey, - useless: PhantomData<*const T> + ptr: *mut *const T } impl Clone for Context { fn clone(&self) -> Self { Self { key: self.key, - useless: self.useless + ptr: self.ptr } } } @@ -69,25 +101,13 @@ impl Copy for ContextMut { } impl Context { pub fn new(vm: &Vm) -> Self { - let key = unsafe { - lua_pushlightuserdata(vm.as_ptr(), std::ptr::null_mut()); - RawRegistryKey::from_top(vm) + let (ptr, key) = unsafe { + let ptr = lua_newuserdata(vm.as_ptr(), 8); + (ptr, RawRegistryKey::from_top(vm)) }; Self { key, - useless: PhantomData - } - } - - pub fn bind<'a, 'b>(&self, vm: &'a Vm, obj: &'b T) -> Guard<'a, &'b T> { - unsafe { - lua_pushlightuserdata(vm.as_ptr(), obj as *const T as *mut T as *mut c_void); - self.key.replace(vm); - Guard { - vm, - ptr: obj, - key: self.key - } + ptr: ptr as *mut *const T } } } @@ -96,33 +116,18 @@ impl ContextMut { pub fn new(vm: &Vm) -> Self { Self(Context::new(vm)) } - - pub fn bind<'a, 'b>(&self, vm: &'a Vm, obj: &'b mut T) -> Guard<'a, &'b mut T> { - unsafe { - lua_pushlightuserdata(vm.as_ptr(), obj as *mut T as *mut c_void); - self.0.key.replace(vm); - Guard { - vm, - ptr: obj, - key: self.0.key - } - } - } } +#[repr(transparent)] pub struct Guard<'a, T> { - vm: &'a Vm, - #[allow(dead_code)] - ptr: T, - key: RawRegistryKey + ud: *mut *const T, + useless: PhantomData<&'a T> } impl<'a, T> Drop for Guard<'a, T> { + #[inline(always)] fn drop(&mut self) { - unsafe { - lua_pushlightuserdata(self.vm.as_ptr(), std::ptr::null_mut()); - self.key.replace(self.vm); - } + unsafe { *self.ud = std::ptr::null(); } } } @@ -135,6 +140,7 @@ pub struct Mut<'a, T>(&'a mut T); impl<'a, T: 'static> Deref for Ref<'a, T> { type Target = T; + #[inline(always)] fn deref(&self) -> &Self::Target { self.0 } @@ -143,12 +149,14 @@ impl<'a, T: 'static> Deref for Ref<'a, T> { impl<'a, T: 'static> Deref for Mut<'a, T> { type Target = T; + #[inline(always)] fn deref(&self) -> &Self::Target { self.0 } } impl<'a, T: 'static> DerefMut for Mut<'a, T> { + #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { self.0 } @@ -159,45 +167,39 @@ unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> { } impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let key = RawRegistryKey::from_int(FromUpvalue::from_upvalue(vm, index)); - key.push(vm); - let ptr = lua_topointer(vm.as_ptr(), -1) as *const T; - //Remove lightuserdata on the top of the stack. - lua_settop(vm.as_ptr(), -2); - if ptr.is_null() { + let ptr: *mut *const T = FromUpvalue::from_upvalue(vm, index); + if (*ptr).is_null() { luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Ref(unsafe { &*ptr }) + Ref(unsafe { &**ptr }) } } impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let key = RawRegistryKey::from_int(FromUpvalue::from_upvalue(vm, index)); - key.push(vm); - let ptr = lua_topointer(vm.as_ptr(), -1) as *mut T; - //Remove lightuserdata on the top of the stack. - lua_settop(vm.as_ptr(), -2); - if ptr.is_null() { + let ptr: *mut *mut T = FromUpvalue::from_upvalue(vm, index); + if (*ptr).is_null() { luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Mut(unsafe { &mut *ptr }) + Mut(unsafe { &mut **ptr }) } } impl IntoUpvalue for Context { fn into_upvalue(self, vm: &Vm) -> u16 { - self.key.as_int().into_upvalue(vm) + unsafe { self.key.push(vm) }; + 1 } } impl IntoUpvalue for ContextMut { fn into_upvalue(self, vm: &Vm) -> u16 { - self.0.key.as_int().into_upvalue(vm) + unsafe { self.0.key.push(vm) }; + 1 } } diff --git a/core/src/vm/closure/context2.rs b/core/src/vm/closure/context2.rs deleted file mode 100644 index 85a604b..0000000 --- a/core/src/vm/closure/context2.rs +++ /dev/null @@ -1,208 +0,0 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -//! Second version of the context tool. - -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; -use crate::ffi::laux::luaL_error; -use crate::ffi::lua::lua_newuserdata; -use crate::util::SimpleDrop; -use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; -use crate::vm::registry::core::RawRegistryKey; -use crate::vm::Vm; - -pub struct Cell { - ptr: *mut *const T -} - -impl Cell { - pub fn new(ctx: Context) -> Self { - Self { ptr: ctx.ptr } - } - - pub fn bind<'a>(&mut self, obj: &'a T) -> Guard<'a, T> { - unsafe { *self.ptr = obj as _ }; - Guard { - useless: PhantomData, - ud: self.ptr - } - } -} - -pub struct CellMut { - ptr: *mut *const T -} - -impl CellMut { - pub fn new(ctx: ContextMut) -> Self { - Self { ptr: ctx.0.ptr } - } - - pub fn bind<'a>(&mut self, obj: &'a mut T) -> Guard<'a, T> { - unsafe { *self.ptr = obj as _ }; - Guard { - useless: PhantomData, - ud: self.ptr - } - } -} - -pub struct Context { - key: RawRegistryKey, - ptr: *mut *const T -} - -impl Clone for Context { - fn clone(&self) -> Self { - Self { - key: self.key, - ptr: self.ptr - } - } -} - -impl Copy for Context {} - -pub struct ContextMut(Context); - -impl Clone for ContextMut { - fn clone(&self) -> Self { - Self(self.0) - } -} - -impl Copy for ContextMut { } - -impl Context { - pub fn new(vm: &Vm) -> Self { - let (ptr, key) = unsafe { - let ptr = lua_newuserdata(vm.as_ptr(), 8); - (ptr, RawRegistryKey::from_top(vm)) - }; - Self { - key, - ptr: ptr as *mut *const T - } - } -} - -impl ContextMut { - pub fn new(vm: &Vm) -> Self { - Self(Context::new(vm)) - } -} - -#[repr(transparent)] -pub struct Guard<'a, T> { - ud: *mut *const T, - useless: PhantomData<&'a T> -} - -impl<'a, T> Drop for Guard<'a, T> { - fn drop(&mut self) { - unsafe { *self.ud = std::ptr::null(); } - } -} - -#[repr(transparent)] -pub struct Ref<'a, T>(&'a T); - -#[repr(transparent)] -pub struct Mut<'a, T>(&'a mut T); - -impl<'a, T: 'static> Deref for Ref<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a, T: 'static> Deref for Mut<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - self.0 - } -} - -impl<'a, T: 'static> DerefMut for Mut<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - self.0 - } -} - -unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> { } -unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> { } - -impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { - unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: *mut *const T = FromUpvalue::from_upvalue(vm, index); - if (*ptr).is_null() { - luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); - // luaL_error raises a lua exception and unwinds, so this cannot be reached. - std::hint::unreachable_unchecked(); - } - Ref(unsafe { &**ptr }) - } -} - -impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { - unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: *mut *mut T = FromUpvalue::from_upvalue(vm, index); - if (*ptr).is_null() { - luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); - // luaL_error raises a lua exception and unwinds, so this cannot be reached. - std::hint::unreachable_unchecked(); - } - Mut(unsafe { &mut **ptr }) - } -} - -impl IntoUpvalue for Context { - fn into_upvalue(self, vm: &Vm) -> u16 { - unsafe { self.key.push(vm) }; - 1 - } -} - -impl IntoUpvalue for ContextMut { - fn into_upvalue(self, vm: &Vm) -> u16 { - unsafe { self.0.key.push(vm) }; - 1 - } -} - -impl Upvalue for Context { - type From<'a> = Ref<'a, T>; -} - -impl Upvalue for ContextMut { - type From<'a> = Mut<'a, T>; -} diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index e472132..6e18e5b 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -79,18 +79,21 @@ impl_from_upvalue_using_from_lua_unchecked!(i64, u64); impl_from_upvalue_using_from_lua_unchecked!(i8, u8, i16, u16, i32, u32, f32, f64, bool); impl FromUpvalue<'_> for *mut T { + #[inline(always)] unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ } } impl FromUpvalue<'_> for *const T { + #[inline(always)] unsafe fn from_upvalue(vm: &'_ Vm, index: i32) -> Self { lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ } } impl IntoUpvalue for T { + #[inline(always)] fn into_upvalue(self, vm: &Vm) -> u16 { self.into_param(vm) } diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 7298224..5ab1f68 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -31,6 +31,5 @@ mod core; pub mod types; pub mod context; pub mod rc; -pub mod context2; pub use interface::*; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index bc7af26..935227d 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context2::{CellMut, ContextMut}; +use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::namespace::Namespace; use bp3d_lua::vm::RootVm; diff --git a/testbin/src/context.rs b/testbin/src/context.rs index d479604..d6d7f97 100644 --- a/testbin/src/context.rs +++ b/testbin/src/context.rs @@ -29,7 +29,7 @@ use std::time::Duration; use mlua::{Lua, UserDataMethods}; use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context::ContextMut; +use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::RootVm; struct TestContext { @@ -98,17 +98,18 @@ pub fn test_context_vm() -> Duration { let mut obj = TestContext { value3: vec![], }; + let mut ctx = CellMut::new(ctx); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); vm.run_code::<()>(c"context_push(1)").unwrap(); vm.run_code::<()>(c"context_push(2)").unwrap(); vm.run_code::<()>(c"context_push(3)").unwrap(); } { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); vm.run_code::<()>(c"assert(context_pop() == 3)").unwrap(); vm.run_code::<()>(c"assert(context_pop() == 2)").unwrap(); vm.run_code::<()>(c"assert(context_pop() == 1)").unwrap(); diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index ff37b27..de67dd5 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -29,7 +29,7 @@ use std::time::Duration; use mlua::{Function, Lua, UserDataMethods}; use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context::ContextMut; +use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; @@ -124,14 +124,15 @@ pub fn test_context_vm() -> Duration { let mut obj = TestContext { value3: vec![], }; + let mut ctx = CellMut::new(ctx); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); part1.call::<()>(()).unwrap(); } { - let _obj = ctx.bind(&vm, &mut obj); + let _obj = ctx.bind(&mut obj); part2.call::<()>(()).unwrap(); } } diff --git a/testbin/src/context_opt2.rs b/testbin/src/context_opt2.rs deleted file mode 100644 index b81f111..0000000 --- a/testbin/src/context_opt2.rs +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::time::Duration; -use mlua::{Function, Lua, UserDataMethods}; -use bp3d_lua::decl_closure; -use bp3d_lua::vm::closure::context2::{CellMut, ContextMut}; -use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::function::LuaFunction; - -struct TestContext { - value3: Vec -} - -decl_closure! { - fn context_push |ctx: ContextMut| (val: u64) -> () { - let mut ctx = ctx; - ctx.value3.push(val); - } -} - -decl_closure! { - fn context_pop |ctx: ContextMut| () -> Option { - let mut ctx = ctx; - ctx.value3.pop() - } -} - -pub fn test_context_mlua() -> Duration { - let lua = Lua::new(); - lua.register_userdata_type::(|reg| { - reg.add_method_mut("push", |_, this, val: u64| { - this.value3.push(val); - Ok(()) - }); - reg.add_method_mut("pop", |_, this, _: ()| { - Ok(this.value3.pop()) - }); - }).unwrap(); - lua.load(" - function part1(ctx) - assert(ctx:pop() == nil) - ctx:push(1) - ctx:push(2) - ctx:push(3) - end - function part2(ctx) - assert(ctx:pop() == 3) - assert(ctx:pop() == 2) - assert(ctx:pop() == 1) - assert(ctx:pop() == nil) - assert(ctx:pop() == nil) - end - ").eval::<()>().unwrap(); - let part1: Function = lua.globals().get("part1").unwrap(); - let part2: Function = lua.globals().get("part2").unwrap(); - let mut ctx = TestContext { - value3: Vec::new() - }; - let time = bp3d_os::time::Instant::now(); - for _ in 0..20000 { - lua.scope(|l| { - let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); - part1.call::<()>(ud).unwrap(); - Ok(()) - }).unwrap(); - lua.scope(|l| { - let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); - part2.call::<()>(ud).unwrap(); - Ok(()) - }).unwrap(); - } - let time = time.elapsed(); - time -} - -pub fn test_context_vm() -> Duration { - let vm = RootVm::new(); - let ctx = ContextMut::new(&vm); - vm.set_global(c"context_push", context_push(ctx)).unwrap(); - vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); - vm.run_code::<()>(c" - function part1() - assert(context_pop() == nil) - context_push(1) - context_push(2) - context_push(3) - end - function part2() - assert(context_pop() == 3) - assert(context_pop() == 2) - assert(context_pop() == 1) - assert(context_pop() == nil) - assert(context_pop() == nil) - end - ").unwrap(); - let part1: LuaFunction = vm.get_global("part1").unwrap(); - let part2: LuaFunction = vm.get_global("part2").unwrap(); - let mut obj = TestContext { - value3: vec![], - }; - let mut ctx = CellMut::new(ctx); - let time = bp3d_os::time::Instant::now(); - for _ in 0..20000 { - { - let _obj = ctx.bind(&mut obj); - part1.call::<()>(()).unwrap(); - } - { - let _obj = ctx.bind(&mut obj); - part2.call::<()>(()).unwrap(); - } - } - let time = time.elapsed(); - time -} diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 57e3c6b..2c577fe 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -28,7 +28,6 @@ mod context; mod context_opt; -mod context_opt2; use std::time::Duration; use mlua::Lua; @@ -98,8 +97,6 @@ fn main() { let mut ctx_mlua = Duration::new(0, 0); let mut ctx_lua_opt = Duration::new(0, 0); let mut ctx_mlua_opt = Duration::new(0, 0); - let mut ctx_lua_opt2 = Duration::new(0, 0); - let mut ctx_mlua_opt2 = Duration::new(0, 0); for _ in 0..RUNS { lua += test_vm_destructor(); @@ -108,8 +105,6 @@ fn main() { ctx_mlua += context::test_context_mlua(); ctx_lua_opt += context_opt::test_context_vm(); ctx_mlua_opt += context_opt::test_context_mlua(); - ctx_lua_opt2 += context_opt2::test_context_vm(); - ctx_mlua_opt2 += context_opt2::test_context_mlua(); } lua = lua / RUNS; @@ -118,8 +113,6 @@ fn main() { ctx_mlua = ctx_mlua / RUNS; ctx_lua_opt = ctx_lua_opt / RUNS; ctx_mlua_opt = ctx_mlua_opt / RUNS; - ctx_lua_opt2 = ctx_lua_opt2 / RUNS; - ctx_mlua_opt2 = ctx_mlua_opt2 / RUNS; println!("average tools.lua (basic): {:?}", lua); println!("average mlua (basic): {:?}", mlua); @@ -135,9 +128,4 @@ fn main() { println!("average mlua (context_opt): {:?}", ctx_mlua_opt); assert!(ctx_lua_opt < ctx_mlua_opt); println!("average diff (context_opt): {:?}", ctx_mlua_opt - ctx_lua_opt); - - println!("average tools.lua (context_opt2): {:?}", ctx_lua_opt2); - println!("average mlua (context_opt2): {:?}", ctx_mlua_opt2); - assert!(ctx_lua_opt2 < ctx_mlua_opt2); - println!("average diff (context_opt2): {:?}", ctx_mlua_opt2 - ctx_lua_opt2); } From ceeb6c99b9bebe1af81e5b4113e0f236db0c466f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Apr 2025 12:46:44 +0200 Subject: [PATCH 248/527] Replaced RootVm attach by destructor pool --- core/src/vm/closure/rc.rs | 2 +- core/src/vm/closure/types.rs | 2 +- core/src/vm/core/destructor.rs | 128 +++++++++++++++++++++++++++++++++ core/src/vm/core/interface.rs | 40 ----------- core/src/vm/core/mod.rs | 1 + core/src/vm/core/vm.rs | 30 +++----- 6 files changed, 141 insertions(+), 62 deletions(-) create mode 100644 core/src/vm/core/destructor.rs diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 7947cb8..b5c03a4 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -69,6 +69,6 @@ impl IntoUpvalue for Rc { impl Rc { #[inline(always)] pub fn from_rust(root: &mut RootVm, rc: std::rc::Rc) -> Rc { - Rc(root.attach(rc)) + Rc(crate::vm::core::destructor::Pool::from_vm(root).attach(rc)) } } diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 2e2b6d8..77f5e4d 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -63,7 +63,7 @@ unsafe impl IntoLua for RClosure { impl RClosure<*const c_void> { pub fn from_rust R + 'static>(root: &mut RootVm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam { - let ptr = root.attach(Box::new(fun)); + let ptr = crate::vm::core::destructor::Pool::from_vm(root).attach(Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam { let vm = unsafe { Vm::from_raw(l) }; diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs new file mode 100644 index 0000000..6dc29c4 --- /dev/null +++ b/core/src/vm/core/destructor.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::rc::Rc; +use bp3d_debug::debug; +use crate::ffi::lua::{lua_gettable, lua_pushlightuserdata, lua_pushstring, lua_settable, lua_settop, lua_touserdata, REGISTRYINDEX}; +use crate::vm::Vm; + +/// This trait represents a value which can be attached to a [Pool](Pool). +pub trait Raw { + type Ptr: Copy; + + fn into_raw(self) -> Self::Ptr; + + /// Deletes the raw pointer. + /// + /// # Safety + /// + /// This function must be called with the same pointer that originated from the same type using + /// the [into_raw](Raw::into_raw) method. + unsafe fn delete(ptr: Self::Ptr); +} + +impl Raw for Box { + type Ptr = *mut T; + + fn into_raw(self) -> Self::Ptr { + Box::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + drop(Box::from_raw(ptr)) + } +} + +impl Raw for Rc { + type Ptr = *const T; + + fn into_raw(self) -> Self::Ptr { + Rc::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + drop(Rc::from_raw(ptr)) + } +} + +#[derive(Default)] +pub struct Pool { + leaked: Vec> +} + +impl Pool { + pub fn new() -> Self { + Self::default() + } + + /// Inserts this pool in the given Vm. + /// + /// # Safety + /// + /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. + pub unsafe fn new_in_vm<'a>(vm: &'a mut Vm) { + let l = vm.as_ptr(); + let b = Box::leak(Box::new(Pool::new())); + unsafe { + lua_pushstring(l, c"__destructor_pool__".as_ptr()); + let ptr = b as *mut Pool as _; + lua_pushlightuserdata(l, ptr); + lua_settable(l, REGISTRYINDEX); + }; + } + + pub fn from_vm<'a>(vm: &'a mut Vm) -> &'a mut Self { + let l = vm.as_ptr(); + unsafe { + lua_pushstring(l, c"__destructor_pool__".as_ptr()); + lua_gettable(l, REGISTRYINDEX); + let ptr = lua_touserdata(l, -1) as *mut Pool; + assert!(!ptr.is_null()); + lua_settop(l, -2); // Remove the pointer from the lua stack. + &mut *ptr + } + } + + pub fn attach(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { + let ptr = R::into_raw(raw); + self.leaked.push(Box::new(move || { + unsafe { R::delete(ptr) }; + })); + ptr + } +} + +impl Drop for Pool { + fn drop(&mut self) { + debug!({num=self.leaked.len() as u32}, "Deleting leaked pointers..."); + let v = std::mem::replace(&mut self.leaked, Vec::new()); + for f in v { + f() + } + } +} diff --git a/core/src/vm/core/interface.rs b/core/src/vm/core/interface.rs index da6ac16..1580fe2 100644 --- a/core/src/vm/core/interface.rs +++ b/core/src/vm/core/interface.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::rc::Rc; use crate::ffi::lua::{State, ThreadStatus}; pub trait LoadString { @@ -36,42 +35,3 @@ pub trait LoadString { pub trait Load { fn load(self, l: State) -> ThreadStatus; } - -/// This trait represents a value which can be attached to a [RootVm](crate::vm::RootVm). -pub trait Raw { - type Ptr: Copy; - - fn into_raw(self) -> Self::Ptr; - - /// Deletes the raw pointer. - /// - /// # Safety - /// - /// This function must be called with the same pointer that originated from the same type using - /// the [into_raw](Raw::into_raw) method. - unsafe fn delete(ptr: Self::Ptr); -} - -impl Raw for Box { - type Ptr = *mut T; - - fn into_raw(self) -> Self::Ptr { - Box::into_raw(self) - } - - unsafe fn delete(ptr: Self::Ptr) { - drop(Box::from_raw(ptr)) - } -} - -impl Raw for Rc { - type Ptr = *const T; - - fn into_raw(self) -> Self::Ptr { - Rc::into_raw(self) - } - - unsafe fn delete(ptr: Self::Ptr) { - drop(Rc::from_raw(ptr)) - } -} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index c3a853e..31680ee 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -31,6 +31,7 @@ pub mod util; mod vm; pub mod load; pub mod iter; +pub mod destructor; #[cfg(feature = "interrupt")] pub mod interrupt; diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index c2e454a..444cb2a 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -32,7 +32,8 @@ use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::AnyStr; -use crate::vm::core::{Load, LoadString, Raw}; +use crate::vm::core::{Load, LoadString}; +use crate::vm::core::destructor::Pool; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; @@ -170,8 +171,7 @@ thread_local! { } pub struct RootVm { - vm: Vm, - leaked: Vec> + vm: Vm } impl RootVm { @@ -182,18 +182,11 @@ impl RootVm { let l = unsafe { luaL_newstate() }; unsafe { luaL_openlibs(l) }; HAS_VM.set(true); - RootVm { - vm: unsafe { Vm::from_raw(l) }, - leaked: Vec::new() - } - } - - pub fn attach(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { - let ptr = R::into_raw(raw); - self.leaked.push(Box::new(move || { - unsafe { R::delete(ptr) }; - })); - ptr + let mut vm = RootVm { + vm: unsafe { Vm::from_raw(l) } + }; + unsafe { Pool::new_in_vm(&mut vm) }; + vm } } @@ -215,11 +208,8 @@ impl DerefMut for RootVm { impl Drop for RootVm { fn drop(&mut self) { - debug!("Deleting leaked pointers..."); - let v = std::mem::replace(&mut self.leaked, Vec::new()); - for f in v { - f() - } + debug!("Deleting destructor pool"); + unsafe { std::ptr::drop_in_place(Pool::from_vm(self)) }; unsafe { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); From e91edff16fd5e8418b06f7946a814d3125ceef46 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Apr 2025 12:53:48 +0200 Subject: [PATCH 249/527] Use Vm instead of RootVm in libs --- core/src/libs/interface.rs | 6 +++--- core/src/libs/lua/require.rs | 4 ++-- core/src/vm/closure/rc.rs | 4 ++-- core/src/vm/closure/types.rs | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index 396f7a7..79df714 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -27,14 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::vm::namespace::Namespace; -use crate::vm::RootVm; +use crate::vm::Vm; pub trait Lib { const NAMESPACE: &'static str; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()>; - fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; self.load(&mut namespace)?; Ok(()) @@ -53,7 +53,7 @@ macro_rules! impl_tuple_lib { Ok(()) } - fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { $( self.$id.register(vm)?; )* diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index e374c37..6f6cf4d 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -33,7 +33,7 @@ use crate::decl_closure; use crate::libs::interface::Lib; use crate::vm::closure::rc::Rc; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::{RootVm, Vm}; +use crate::vm::Vm; use crate::vm::namespace::Namespace; simple_error! { @@ -88,7 +88,7 @@ impl Lib for Require { std::unreachable!() } - fn register(&self, vm: &mut RootVm) -> crate::vm::Result<()> { + fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { let rc = Rc::from_rust(vm, self.0.clone()); let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([("require", require(rc))]) diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index b5c03a4..28c1849 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -29,7 +29,7 @@ use std::ops::Deref; use crate::util::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; -use crate::vm::{RootVm, Vm}; +use crate::vm::Vm; #[repr(transparent)] pub struct Rc(*const T); @@ -68,7 +68,7 @@ impl IntoUpvalue for Rc { impl Rc { #[inline(always)] - pub fn from_rust(root: &mut RootVm, rc: std::rc::Rc) -> Rc { + pub fn from_rust(root: &mut Vm, rc: std::rc::Rc) -> Rc { Rc(crate::vm::core::destructor::Pool::from_vm(root).attach(rc)) } } diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 77f5e4d..1fbf09d 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -30,7 +30,7 @@ use std::ffi::c_void; use crate::ffi::lua::{lua_pushcclosure, CFunction, State}; use crate::vm::closure::{FromUpvalue, IntoUpvalue}; use crate::vm::value::IntoLua; -use crate::vm::{RootVm, Vm}; +use crate::vm::Vm; use crate::vm::function::{FromParam, IntoParam}; pub struct RClosure { @@ -61,9 +61,9 @@ unsafe impl IntoLua for RClosure { } impl RClosure<*const c_void> { - pub fn from_rust R + 'static>(root: &mut RootVm, fun: F) -> Self + pub fn from_rust R + 'static>(vm: &mut Vm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam { - let ptr = crate::vm::core::destructor::Pool::from_vm(root).attach(Box::new(fun)); + let ptr = crate::vm::core::destructor::Pool::from_vm(vm).attach(Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam { let vm = unsafe { Vm::from_raw(l) }; From fe677e53f0b0c7b2ed29fb6d46b410737ab2b59e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Apr 2025 13:03:59 +0200 Subject: [PATCH 250/527] Added support to attach elements to the destructor pool immutably --- core/src/libs/interface.rs | 4 ++-- core/src/libs/lua/require.rs | 2 +- core/src/vm/closure/rc.rs | 4 ++-- core/src/vm/closure/types.rs | 4 ++-- core/src/vm/core/destructor.rs | 18 ++++++++++++++++-- 5 files changed, 23 insertions(+), 9 deletions(-) diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index 79df714..5db7c26 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -34,7 +34,7 @@ pub trait Lib { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()>; - fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { + fn register(&self, vm: &Vm) -> crate::vm::Result<()> { let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; self.load(&mut namespace)?; Ok(()) @@ -53,7 +53,7 @@ macro_rules! impl_tuple_lib { Ok(()) } - fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { + fn register(&self, vm: &Vm) -> crate::vm::Result<()> { $( self.$id.register(vm)?; )* diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 6f6cf4d..d7c2b95 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -88,7 +88,7 @@ impl Lib for Require { std::unreachable!() } - fn register(&self, vm: &mut Vm) -> crate::vm::Result<()> { + fn register(&self, vm: &Vm) -> crate::vm::Result<()> { let rc = Rc::from_rust(vm, self.0.clone()); let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([("require", require(rc))]) diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 28c1849..0e42ffb 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -68,7 +68,7 @@ impl IntoUpvalue for Rc { impl Rc { #[inline(always)] - pub fn from_rust(root: &mut Vm, rc: std::rc::Rc) -> Rc { - Rc(crate::vm::core::destructor::Pool::from_vm(root).attach(rc)) + pub fn from_rust(vm: &Vm, rc: std::rc::Rc) -> Rc { + Rc(crate::vm::core::destructor::Pool::attach(vm, rc)) } } diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 1fbf09d..72ac197 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -61,9 +61,9 @@ unsafe impl IntoLua for RClosure { } impl RClosure<*const c_void> { - pub fn from_rust R + 'static>(vm: &mut Vm, fun: F) -> Self + pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam { - let ptr = crate::vm::core::destructor::Pool::from_vm(vm).attach(Box::new(fun)); + let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam { let vm = unsafe { Vm::from_raw(l) }; diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 6dc29c4..e53ae3a 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -96,7 +96,12 @@ impl Pool { }; } - pub fn from_vm<'a>(vm: &'a mut Vm) -> &'a mut Self { + /// Extracts a destructor pool from the given [Vm]. + /// + /// # Safety + /// + /// The returned reference must not be aliased. + unsafe fn _from_vm(vm: &Vm) -> &mut Self { let l = vm.as_ptr(); unsafe { lua_pushstring(l, c"__destructor_pool__".as_ptr()); @@ -108,7 +113,16 @@ impl Pool { } } - pub fn attach(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { + pub fn from_vm(vm: &mut Vm) -> &mut Self { + unsafe { Self::_from_vm(vm) } + } + + pub fn attach(vm: &Vm, raw: R) -> R::Ptr where R::Ptr: 'static { + let ptr = unsafe { Self::_from_vm(vm) }; + ptr.attach_mut(raw) + } + + pub fn attach_mut(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { let ptr = R::into_raw(raw); self.leaked.push(Box::new(move || { unsafe { R::delete(ptr) }; From 6e43cc9318760cf4b8c52301192957dc05fe411c Mon Sep 17 00:00:00 2001 From: Yuri6037 Date: Mon, 21 Apr 2025 17:02:40 +0200 Subject: [PATCH 251/527] Fixed luajit build errors under windows --- build/src/build/windows.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs index 77a85e0..db5cf08 100644 --- a/build/src/build/windows.rs +++ b/build/src/build/windows.rs @@ -37,7 +37,8 @@ pub struct Windows; impl Build for Windows { fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { - let mut cmd = Command::new("msvcbuild.bat"); + let mut cmd = Command::new("cmd"); + cmd.arg("/C").arg("msvcbuild.bat"); let cl = cc::windows_registry::find_tool(info.target_name(), "cl.exe") .ok_or(Error::new(ErrorKind::Other, "unable to find cl.exe"))?; for (k, v) in cl.env() { @@ -67,7 +68,7 @@ impl Build for Windows { } fn get_linked_lib(info: &BuildInfo) -> Lib { - let libname = format!("libbp3d-luajit-{}.lib", info.version()); + let libname = format!("libbp3d-luajit-{}", info.version()); Lib { name: libname, path: info.build_dir().join("src"), From fcb1296530f2bb3af7e0c1c446b5081c5a74e161 Mon Sep 17 00:00:00 2001 From: Yuri6037 Date: Mon, 21 Apr 2025 17:03:35 +0200 Subject: [PATCH 252/527] Added initial support for Vm interrupt --- core/Cargo.toml | 5 +- core/src/vm/core/interrupt/mod.rs | 9 ++- core/src/vm/core/interrupt/windows.rs | 112 ++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 2 deletions(-) create mode 100644 core/src/vm/core/interrupt/windows.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 8ba5c6a..6d3bfb3 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -22,6 +22,9 @@ bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } time = { version = "0.3.37", features = ["formatting"] } itertools = { version = "0.14.0" } +[target.'cfg(windows)'.dependencies] +windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel"], optional = true } + [target.'cfg(unix)'.dependencies] libc = { version = "0.2.170", optional = true } @@ -29,5 +32,5 @@ libc = { version = "0.2.170", optional = true } bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } [features] -interrupt = ["libc"] +interrupt = ["libc", "windows-sys"] dynamic = [] diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index dc901bc..f34da30 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -31,14 +31,21 @@ #[cfg(unix)] mod unix; -use std::thread::JoinHandle; +#[cfg(windows)] +mod windows; + #[cfg(unix)] pub use unix::Signal; +#[cfg(windows)] +pub use windows::Signal; + unsafe impl Send for Signal {} unsafe impl Sync for Signal {} +use std::thread::JoinHandle; use bp3d_util::simple_error; + simple_error! { pub Error { AlreadyInterrupting => "attempt to interrupt a Vm while interrupting a different Vm", diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs new file mode 100644 index 0000000..da5e4fc --- /dev/null +++ b/core/src/vm/core/interrupt/windows.rs @@ -0,0 +1,112 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::time::Duration; +use std::sync::Mutex; +use crate::vm::RootVm; +use bp3d_debug::{error, warning}; +use windows_sys::Win32::Foundation::HANDLE; +use windows_sys::Win32::System::Threading::{ GetCurrentThread, SuspendThread, ResumeThread }; +use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; +use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET}; + +use super::Error; + +static SIG_STATE: Mutex>> = Mutex::new(None); + +extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { + { + let mut state = SIG_STATE.lock().unwrap(); + if let Some(sig) = state.take() { + if let Err(e) = sig.send(()) { + error!({error=?e}, "Failed to notify interrupt signal") + } + } + } + unsafe { + lua_sethook(l, std::mem::transmute::<*const (), Hook>(std::ptr::null()), 0, 0); + lua_pushstring(l, c"interrupted".as_ptr()); + lua_error(l); + } +} + +pub struct Signal { + l: State, + th: HANDLE +} + +impl Signal { + pub fn create(vm: &mut RootVm) -> Self { + let th = unsafe { GetCurrentThread() }; + let l = vm.as_ptr(); + Self { + l, + th + } + } + + pub fn send(&self, duration: Duration) -> Result<(), Error> { + let (send2, recv2) = std::sync::mpsc::channel(); + { + let mut lock = SIG_STATE.try_lock().map_err(|_| Error::AlreadyInterrupting)?; + *lock = Some(send2); + } + if self.th == unsafe { GetCurrentThread() } { + // If somehow the system thread that ineterrupts the Vm is the same as the one which started the Vm, then directly set the hook. + unsafe { + lua_sethook(self.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); + } + } else { + unsafe { + let mut ctx: CONTEXT = std::mem::zeroed(); + // Requests to suspend the thread. + if SuspendThread(self.th) == u32::MAX { //(DWORD) -1 + return Err(Error::Unknown); + } + // This call forces synchronization with the thread to be suspended. + if GetThreadContext(self.th, &mut ctx as _) == 0 { + return Err(Error::Unknown); + } + lua_sethook(self.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); + // Resume the thread. + let _ = ResumeThread(self.th); + } + } + match recv2.recv_timeout(duration) { + Ok(()) => Ok(()), + Err(e) => { + warning!({error=?e}, "Error attempting to wait for interrupt notification"); + { + let mut guard = SIG_STATE.lock().unwrap(); + *guard = None; + } + Err(Error::Timeout) + } + } + } +} From daace74fd2a3423b107b01b3be9435300840d08f Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 21 Apr 2025 15:11:36 +0000 Subject: [PATCH 253/527] Format Rust code using rustfmt --- build/src/build/interface.rs | 4 +- build/src/build/linux.rs | 16 +- build/src/build/mac.rs | 33 ++-- build/src/build/mod.rs | 4 +- build/src/build/windows.rs | 13 +- build/src/info.rs | 22 ++- build/src/lib.rs | 6 +- build/src/patch.rs | 49 ++++-- build/src/target.rs | 2 +- build/src/util.rs | 2 +- codegen/src/gen/from_param.rs | 35 ++-- codegen/src/gen/into_param.rs | 10 +- codegen/src/gen/lua_type.rs | 8 +- codegen/src/gen/mod.rs | 4 +- codegen/src/lib.rs | 27 ++- codegen/src/parser/enums.rs | 12 +- codegen/src/parser/interface.rs | 6 +- codegen/src/parser/mod.rs | 2 +- codegen/src/parser/structs.rs | 9 +- core/build.rs | 29 +-- core/src/ffi/ext.rs | 6 +- core/src/ffi/laux.rs | 23 ++- core/src/ffi/lua.rs | 22 ++- core/src/ffi/mod.rs | 4 +- core/src/lib.rs | 4 +- core/src/libs/lua/base.rs | 7 +- core/src/libs/lua/load.rs | 10 +- core/src/libs/lua/mod.rs | 6 +- core/src/libs/lua/options.rs | 15 +- core/src/libs/lua/require.rs | 12 +- core/src/libs/os/compat.rs | 38 ++-- core/src/libs/os/instant.rs | 6 +- core/src/libs/os/mod.rs | 4 +- core/src/libs/os/time.rs | 20 ++- core/src/libs/util/mod.rs | 4 +- core/src/libs/util/string.rs | 6 +- core/src/libs/util/table.rs | 36 ++-- core/src/libs/util/utf8.rs | 6 +- core/src/macro/mod.rs | 6 +- core/src/util.rs | 8 +- core/src/vm/closure/context.rs | 40 +++-- core/src/vm/closure/core.rs | 10 +- core/src/vm/closure/mod.rs | 6 +- core/src/vm/closure/rc.rs | 4 +- core/src/vm/closure/types.rs | 16 +- core/src/vm/core/destructor.rs | 26 ++- core/src/vm/core/interrupt/mod.rs | 6 +- core/src/vm/core/interrupt/unix.rs | 41 +++-- core/src/vm/core/interrupt/windows.rs | 242 ++++++++++++++------------ core/src/vm/core/iter.rs | 6 +- core/src/vm/core/load.rs | 66 ++++--- core/src/vm/core/mod.rs | 8 +- core/src/vm/core/util.rs | 37 ++-- core/src/vm/core/vm.rs | 42 +++-- core/src/vm/error.rs | 12 +- core/src/vm/function/core.rs | 55 +++--- core/src/vm/function/interface.rs | 2 +- core/src/vm/function/mod.rs | 2 +- core/src/vm/mod.rs | 14 +- core/src/vm/namespace.rs | 13 +- core/src/vm/registry/core.rs | 16 +- core/src/vm/registry/interface.rs | 22 ++- core/src/vm/registry/types.rs | 6 +- core/src/vm/table/core.rs | 36 +++- core/src/vm/table/interface.rs | 14 +- core/src/vm/table/iter.rs | 16 +- core/src/vm/thread.rs | 36 ++-- core/src/vm/userdata/any.rs | 21 ++- core/src/vm/userdata/case.rs | 20 ++- core/src/vm/userdata/core.rs | 35 ++-- core/src/vm/userdata/error.rs | 2 +- core/src/vm/userdata/interface.rs | 4 +- core/src/vm/userdata/mod.rs | 8 +- core/src/vm/util.rs | 8 +- core/src/vm/value/any.rs | 32 ++-- core/src/vm/value/core.rs | 26 +-- core/src/vm/value/function.rs | 40 +++-- core/src/vm/value/mod.rs | 4 +- core/src/vm/value/util.rs | 7 +- core/tests/test_vm_backtrace.rs | 14 +- core/tests/test_vm_closures.rs | 28 +-- core/tests/test_vm_custom_structs.rs | 40 +++-- core/tests/test_vm_destructor.rs | 22 ++- core/tests/test_vm_functions.rs | 10 +- core/tests/test_vm_interrupt.rs | 8 +- core/tests/test_vm_libs.rs | 121 +++++++++---- core/tests/test_vm_run.rs | 39 ++++- core/tests/test_vm_run_string.rs | 9 +- core/tests/test_vm_tables.rs | 8 +- core/tests/test_vm_userdata.rs | 62 +++++-- testbin/src/context.rs | 27 ++- testbin/src/context_opt.rs | 44 ++--- testbin/src/main.rs | 54 ++++-- 93 files changed, 1254 insertions(+), 769 deletions(-) diff --git a/build/src/build/interface.rs b/build/src/build/interface.rs index 067f8a0..6e26ed2 100644 --- a/build/src/build/interface.rs +++ b/build/src/build/interface.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::PathBuf; use crate::BuildInfo; use crate::util::CommandRunner; +use std::path::PathBuf; pub struct Lib { pub name: String, pub path: PathBuf, - pub dynamic: bool + pub dynamic: bool, } pub trait Build { diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs index 03976ed..2c8d3af 100644 --- a/build/src/build/linux.rs +++ b/build/src/build/linux.rs @@ -26,20 +26,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::process::Command; -use crate::build::Build; use crate::BuildInfo; +use crate::build::Build; use crate::build::interface::Lib; use crate::util::CommandRunner; +use std::process::Command; pub struct Linux; impl Build for Linux { fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { let soname = format!("TARGET_SONAME=libbp3d-luajit-{}.so", info.version()); - runner.run(Command::new("make") - .arg(soname) - .current_dir(info.build_dir())) + runner.run( + Command::new("make") + .arg(soname) + .current_dir(info.build_dir()), + ) } fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { @@ -58,13 +60,13 @@ impl Build for Linux { Lib { name, path: info.build_dir().into(), - dynamic: true + dynamic: true, } } else { Lib { name: "luajit".into(), path: info.build_dir().join("src"), - dynamic: false + dynamic: false, } } } diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 4de7d48..9d83b9a 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -26,33 +26,40 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::io::{Error, ErrorKind}; -use std::process::Command; -use crate::{BuildInfo, Target}; use crate::build::Build; use crate::build::interface::Lib; use crate::util::CommandRunner; +use crate::{BuildInfo, Target}; +use std::io::{Error, ErrorKind}; +use std::process::Command; pub struct MacOS; impl Build for MacOS { fn build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { match info.target() { - Target::MacAarch64 => runner.run(Command::new("make") - .env("MACOSX_DEPLOYMENT_TARGET", "11.0") - .current_dir(info.build_dir())), - Target::MacAmd64 => runner.run(Command::new("make") - .env("MACOSX_DEPLOYMENT_TARGET", "10.11") - .current_dir(info.build_dir())), - _ => Err(Error::new(ErrorKind::Other, "unsupported target")) + Target::MacAarch64 => runner.run( + Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "11.0") + .current_dir(info.build_dir()), + ), + Target::MacAmd64 => runner.run( + Command::new("make") + .env("MACOSX_DEPLOYMENT_TARGET", "10.11") + .current_dir(info.build_dir()), + ), + _ => Err(Error::new(ErrorKind::Other, "unsupported target")), } } fn post_build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { let filename = format!("libbp3d-luajit-{}.dylib", info.version()); let path_to_so = info.build_dir().join("src"); - runner.run(Command::new("install_name_tool").args(["-id", &filename, "libluajit.so"]) - .current_dir(&path_to_so))?; + runner.run( + Command::new("install_name_tool") + .args(["-id", &filename, "libluajit.so"]) + .current_dir(&path_to_so), + )?; let path_to_dylib = info.build_dir().join(&filename); std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib)?; let path_to_dylib2 = info.target_dir().join(filename); @@ -72,7 +79,7 @@ impl Build for MacOS { Lib { name: "luajit".into(), path: info.build_dir().join("src"), - dynamic: false + dynamic: false, } } } diff --git a/build/src/build/mod.rs b/build/src/build/mod.rs index ff5083f..16a8518 100644 --- a/build/src/build/mod.rs +++ b/build/src/build/mod.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod interface; mod linux; mod mac; mod windows; -mod interface; pub use interface::*; -pub use windows::Windows; pub use linux::Linux; pub use mac::MacOS; +pub use windows::Windows; diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs index db5cf08..2dfbce7 100644 --- a/build/src/build/windows.rs +++ b/build/src/build/windows.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::io::{Error, ErrorKind}; -use std::process::Command; -use crate::build::Build; use crate::BuildInfo; +use crate::build::Build; use crate::build::interface::Lib; use crate::util::CommandRunner; +use std::io::{Error, ErrorKind}; +use std::process::Command; pub struct Windows; @@ -55,8 +55,9 @@ impl Build for Windows { } fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { - if !info.dynamic() { //Nothing to be done in non-dynamic builds. - return Ok(()) + if !info.dynamic() { + //Nothing to be done in non-dynamic builds. + return Ok(()); } let dllname = format!("libbp3d-luajit-{}.dll", info.version()); let path_to_dll = info.build_dir().join("src").join(&dllname); @@ -72,7 +73,7 @@ impl Build for Windows { Lib { name: libname, path: info.build_dir().join("src"), - dynamic: info.dynamic() + dynamic: info.dynamic(), } } } diff --git a/build/src/info.rs b/build/src/info.rs index 6aaf90f..113b27d 100644 --- a/build/src/info.rs +++ b/build/src/info.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::Target; +use crate::build::{Build, Lib, Linux, MacOS, Windows}; use std::io::{Error, ErrorKind}; use std::path::{Path, PathBuf}; -use crate::build::{Build, Lib, Linux, MacOS, Windows}; -use crate::Target; pub struct BuildInfoBase<'a> { pub dynamic: bool, pub target_name: &'a str, pub build_dir: &'a Path, - pub manifest: &'a Path + pub manifest: &'a Path, } pub struct BuildInfo<'a> { @@ -50,10 +50,18 @@ impl<'a> BuildInfo<'a> { pub fn new(base: BuildInfoBase<'a>) -> std::io::Result { let manifest = std::fs::read_to_string(&base.manifest)?; let target_dir = base.build_dir.join("../../../.."); - let start = manifest.find(VERSION).ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + let start = manifest + .find(VERSION) + .ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; let version = &manifest[start + VERSION.len()..]; - let end = version.find("\"").ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; - Ok(Self { base, target_dir, crate_version: String::from(&version[..end]) }) + let end = version + .find("\"") + .ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + Ok(Self { + base, + target_dir, + crate_version: String::from(&version[..end]), + }) } pub fn build_dir(&self) -> &Path { @@ -85,7 +93,7 @@ impl<'a> BuildInfo<'a> { Target::MacAmd64 | Target::MacAarch64 => MacOS::run(&self), Target::Linux => Linux::run(&self), Target::Windows => Windows::run(&self), - Target::Unsupported => Err(Error::new(ErrorKind::Other, "unsupported target")) + Target::Unsupported => Err(Error::new(ErrorKind::Other, "unsupported target")), } } } diff --git a/build/src/lib.rs b/build/src/lib.rs index b2d0593..abc51c8 100644 --- a/build/src/lib.rs +++ b/build/src/lib.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod build; +mod info; mod patch; mod target; mod util; -mod info; -pub mod build; +pub use crate::info::{BuildInfo, BuildInfoBase}; pub use crate::patch::Patch; pub use crate::target::Target; -pub use crate::info::{BuildInfo, BuildInfoBase}; diff --git a/build/src/patch.rs b/build/src/patch.rs index aa5f6a9..f98348c 100644 --- a/build/src/patch.rs +++ b/build/src/patch.rs @@ -26,49 +26,65 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::util::CommandRunner; +use bp3d_os::fs::CopyOptions; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use std::process::Command; -use bp3d_os::fs::CopyOptions; -use crate::util::CommandRunner; pub struct Patch { src_path: PathBuf, patch_dir: PathBuf, - patch_list: Vec + patch_list: Vec, } impl Patch { pub fn new(patch_dir: &Path, luajit_src: &Path) -> std::io::Result { let patch_dir = bp3d_os::fs::get_absolute_path(patch_dir)?; let src_path = bp3d_os::fs::get_absolute_path(luajit_src)?; - CommandRunner::new("failed to revert").run(Command::new("git") - .args(&["checkout", "."]).current_dir(&src_path))?; + CommandRunner::new("failed to revert").run( + Command::new("git") + .args(&["checkout", "."]) + .current_dir(&src_path), + )?; Ok(Patch { patch_dir, src_path, - patch_list: Vec::new() + patch_list: Vec::new(), }) } pub fn apply(&mut self, name: &str) -> std::io::Result<()> { - CommandRunner::new("failed to patch").run(Command::new("git") - .args(&[OsStr::new("apply"), self.patch_dir.join(format!("{}.patch", name)).as_os_str()]) - .current_dir(&self.src_path))?; + CommandRunner::new("failed to patch").run( + Command::new("git") + .args(&[ + OsStr::new("apply"), + self.patch_dir.join(format!("{}.patch", name)).as_os_str(), + ]) + .current_dir(&self.src_path), + )?; self.patch_list.push(name.into()); Ok(()) } - pub fn get_patch_list(&self) -> impl Iterator { + pub fn get_patch_list(&self) -> impl Iterator { self.patch_list.iter().map(|v| &**v) } - pub fn apply_all<'a>(mut self, patches: impl IntoIterator, out_path: &Path) -> std::io::Result> { + pub fn apply_all<'a>( + mut self, + patches: impl IntoIterator, + out_path: &Path, + ) -> std::io::Result> { for patch in patches { self.apply(patch)?; } if !out_path.is_dir() { - bp3d_os::fs::copy(&self.src_path, out_path, CopyOptions::new().exclude(OsStr::new(".git")))?; + bp3d_os::fs::copy( + &self.src_path, + out_path, + CopyOptions::new().exclude(OsStr::new(".git")), + )?; } Ok(self.get_patch_list().map(String::from).collect()) } @@ -76,7 +92,12 @@ impl Patch { impl Drop for Patch { fn drop(&mut self) { - CommandRunner::new("failed to revert").run(Command::new("git") - .args(&["checkout", "."]).current_dir(&self.src_path)).unwrap(); + CommandRunner::new("failed to revert") + .run( + Command::new("git") + .args(&["checkout", "."]) + .current_dir(&self.src_path), + ) + .unwrap(); } } diff --git a/build/src/target.rs b/build/src/target.rs index a0ed000..eebdf1d 100644 --- a/build/src/target.rs +++ b/build/src/target.rs @@ -34,7 +34,7 @@ pub enum Target { MacAarch64, Linux, Windows, - Unsupported + Unsupported, } static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { diff --git a/build/src/util.rs b/build/src/util.rs index 5bdec76..a678a94 100644 --- a/build/src/util.rs +++ b/build/src/util.rs @@ -30,7 +30,7 @@ use std::io::{Error, ErrorKind}; use std::process::Command; pub struct CommandRunner { - msg: &'static str + msg: &'static str, } impl CommandRunner { diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index a69173e..f4b721c 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -26,30 +26,35 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::parser::enums::EnumVariant; +use crate::parser::structs::StructField; +use crate::parser::Parser; use proc_macro2::{Ident, TokenStream}; use quote::{quote, ToTokens}; use syn::Generics; -use crate::parser::enums::EnumVariant; -use crate::parser::Parser; -use crate::parser::structs::StructField; pub struct FromParam { name: Ident, generics: Generics, is_index_based: bool, - is_simple_enum: bool + is_simple_enum: bool, } impl FromParam { pub fn new(name: Ident, generics: Generics) -> Self { - Self { name, generics, is_index_based: false, is_simple_enum: true } + Self { + name, + generics, + is_index_based: false, + is_simple_enum: true, + } } } pub struct Field { name: Ident, from_param: TokenStream, - try_from_param: TokenStream + try_from_param: TokenStream, } impl Parser for FromParam { @@ -77,7 +82,7 @@ impl Parser for FromParam { unsafe { #reader }; top += 1; let #name: #ty = bp3d_lua::vm::function::FromParam::try_from_param(vm, top)?; - } + }, } } @@ -101,7 +106,7 @@ impl Parser for FromParam { Some(v) => return Some(#name::#variant(v)), None => () }; - } + }, } } EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), @@ -121,7 +126,7 @@ impl Parser for FromParam { true => return Some(#name::#variant), false => () }; - } + }, } } } @@ -130,7 +135,11 @@ impl Parser for FromParam { fn gen_struct(self, parsed: Vec) -> TokenStream { let name = self.name; let generics = self.generics; - let lifetime = generics.lifetimes().next().map(|v| v.into_token_stream()).unwrap_or(quote! { '_ }); + let lifetime = generics + .lifetimes() + .next() + .map(|v| v.into_token_stream()) + .unwrap_or(quote! { '_ }); let from_params = parsed.iter().map(|field| &field.from_param); let try_from_params = parsed.iter().map(|field| &field.try_from_param); let values = parsed.iter().map(|field| &field.name); @@ -177,7 +186,11 @@ impl Parser for FromParam { fn gen_enum(self, parsed: Vec) -> TokenStream { let name = self.name; let generics = self.generics; - let lifetime = generics.lifetimes().next().map(|v| v.into_token_stream()).unwrap_or(quote! { '_ }); + let lifetime = generics + .lifetimes() + .next() + .map(|v| v.into_token_stream()) + .unwrap_or(quote! { '_ }); let from_params = parsed.iter().map(|field| &field.from_param); let try_from_params = parsed.iter().map(|field| &field.try_from_param); quote! { diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index f23ce86..c2e4efd 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::parser::enums::EnumVariant; +use crate::parser::structs::StructField; +use crate::parser::Parser; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::{Generics, Index}; -use crate::parser::enums::EnumVariant; -use crate::parser::Parser; -use crate::parser::structs::StructField; pub struct IntoParam { name: Ident, - generics: Generics + generics: Generics, } impl IntoParam { @@ -72,7 +72,7 @@ impl Parser for IntoParam { quote! { Self::#name(v) => <#ty as bp3d_lua::vm::function::IntoParam>::into_param(v, vm), } - }, + } EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), EnumVariant::None(name) => { let str = name.to_string(); diff --git a/codegen/src/gen/lua_type.rs b/codegen/src/gen/lua_type.rs index ecc412a..4aade3a 100644 --- a/codegen/src/gen/lua_type.rs +++ b/codegen/src/gen/lua_type.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::parser::enums::EnumVariant; +use crate::parser::structs::StructField; +use crate::parser::Parser; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::Generics; -use crate::parser::enums::EnumVariant; -use crate::parser::Parser; -use crate::parser::structs::StructField; pub struct LuaType { name: Ident, @@ -64,7 +64,7 @@ impl Parser for LuaType { tokens.push(self.parse_field(v)); } } - EnumVariant::None(_) => () + EnumVariant::None(_) => (), } tokens } diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs index ecbce60..ddee92f 100644 --- a/codegen/src/gen/mod.rs +++ b/codegen/src/gen/mod.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod from_param; -mod lua_type; mod into_param; +mod lua_type; -pub use lua_type::LuaType; pub use from_param::FromParam; pub use into_param::IntoParam; +pub use lua_type::LuaType; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index c321e8f..c482b52 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -26,28 +26,43 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod parser; mod gen; +mod parser; +use crate::gen::{FromParam, IntoParam, LuaType}; +use crate::parser::Parser; use proc_macro::TokenStream; use syn::{parse_macro_input, DeriveInput}; -use crate::parser::Parser; -use crate::gen::{FromParam, IntoParam, LuaType}; #[proc_macro_derive(FromParam)] pub fn from_param(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + let DeriveInput { + ident, + data, + generics, + .. + } = parse_macro_input!(input); FromParam::new(ident, generics).parse(data).into() } #[proc_macro_derive(IntoParam)] pub fn into_param(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + let DeriveInput { + ident, + data, + generics, + .. + } = parse_macro_input!(input); IntoParam::new(ident, generics).parse(data).into() } #[proc_macro_derive(LuaType)] pub fn lua_type(input: TokenStream) -> TokenStream { - let DeriveInput { ident, data, generics, .. } = parse_macro_input!(input); + let DeriveInput { + ident, + data, + generics, + .. + } = parse_macro_input!(input); LuaType::new(ident, generics).parse(data).into() } diff --git a/codegen/src/parser/enums.rs b/codegen/src/parser/enums.rs index f6fd4d2..b55a709 100644 --- a/codegen/src/parser/enums.rs +++ b/codegen/src/parser/enums.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::parser::structs::{StructField, StructParser}; use proc_macro2::Ident; use syn::{Fields, Variant}; -use crate::parser::structs::{StructField, StructParser}; pub struct EnumVariantSingle { pub unique_name: Ident, @@ -43,7 +43,7 @@ pub struct EnumVariantMulti { pub enum EnumVariant { SingleField(EnumVariantSingle), MultiField(EnumVariantMulti), - None(Ident) + None(Ident), } pub struct EnumParser; @@ -57,7 +57,7 @@ impl EnumParser { let fields = v.named.into_iter().map(|v| parser.parse(v)); EnumVariant::MultiField(EnumVariantMulti { unique_name, - fields: fields.collect() + fields: fields.collect(), }) } Fields::Unnamed(v) => { @@ -65,17 +65,17 @@ impl EnumParser { if v.unnamed.len() == 1 { EnumVariant::SingleField(EnumVariantSingle { unique_name, - field: parser.parse(v.unnamed.into_iter().next().unwrap()) + field: parser.parse(v.unnamed.into_iter().next().unwrap()), }) } else { let fields = v.unnamed.into_iter().map(|v| parser.parse(v)); EnumVariant::MultiField(EnumVariantMulti { unique_name, - fields: fields.collect() + fields: fields.collect(), }) } } - Fields::Unit => EnumVariant::None(unique_name) + Fields::Unit => EnumVariant::None(unique_name), } } } diff --git a/codegen/src/parser/interface.rs b/codegen/src/parser/interface.rs index 0702187..673ef0f 100644 --- a/codegen/src/parser/interface.rs +++ b/codegen/src/parser/interface.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use proc_macro2::TokenStream; -use syn::Data; use crate::parser::enums::{EnumParser, EnumVariant}; use crate::parser::structs::{StructField, StructParser}; +use proc_macro2::TokenStream; +use syn::Data; pub trait Parser: Sized { type ParsedField; @@ -59,7 +59,7 @@ pub trait Parser: Sized { } self.gen_enum(parsed) } - _ => panic!("Unsupported type") + _ => panic!("Unsupported type"), } } } diff --git a/codegen/src/parser/mod.rs b/codegen/src/parser/mod.rs index 972260f..c1686d2 100644 --- a/codegen/src/parser/mod.rs +++ b/codegen/src/parser/mod.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod enums; mod interface; pub mod structs; -pub mod enums; pub use interface::*; diff --git a/codegen/src/parser/structs.rs b/codegen/src/parser/structs.rs index 8872b94..ef52b82 100644 --- a/codegen/src/parser/structs.rs +++ b/codegen/src/parser/structs.rs @@ -37,7 +37,7 @@ pub struct StructField { } pub struct StructParser { - cur_index: usize + cur_index: usize, } impl StructParser { @@ -48,15 +48,12 @@ impl StructParser { pub fn parse(&mut self, field: Field) -> StructField { let index = Ident::new(&format!("value_{}", self.cur_index), Span::call_site()); self.cur_index += 1; - let unique_name = field - .ident - .clone() - .unwrap_or(index); + let unique_name = field.ident.clone().unwrap_or(index); StructField { unique_name, ty: field.ty, index: self.cur_index - 1, - unique_name_is_index: field.ident.is_none() + unique_name_is_index: field.ident.is_none(), } } } diff --git a/core/build.rs b/core/build.rs index 9091bcc..02519ea 100644 --- a/core/build.rs +++ b/core/build.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::{Path, PathBuf}; -use bp3d_lua_build::{BuildInfo, BuildInfoBase, Patch}; use bp3d_lua_build::build::Lib; +use bp3d_lua_build::{BuildInfo, BuildInfoBase, Patch}; +use std::path::{Path, PathBuf}; #[cfg(feature = "dynamic")] const DYNAMIC: bool = true; @@ -36,27 +36,32 @@ const DYNAMIC: bool = true; const DYNAMIC: bool = false; const PATCH_LIST: &[&str] = &[ - "lib_init", // Disable unsafe/un-sandboxed libs. - "lj_disable_jit", // Disable global JIT state changes from Lua code. - "disable_lua_load", // Disable loadstring, dostring, etc from base lib. - "lua_ext", // Ext library such as lua_ext_tab_len, etc. - "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). - "windows_set_lib_names" // Allow setting LJLIBNAME and LJDLLNAME from environment variables. + "lib_init", // Disable unsafe/un-sandboxed libs. + "lj_disable_jit", // Disable global JIT state changes from Lua code. + "disable_lua_load", // Disable loadstring, dostring, etc from base lib. + "lua_ext", // Ext library such as lua_ext_tab_len, etc. + "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). + "windows_set_lib_names", // Allow setting LJLIBNAME and LJDLLNAME from environment variables. ]; fn apply_patches(out_path: &Path) -> std::io::Result> { - Patch::new(&Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"))? - .apply_all(PATCH_LIST.iter().map(|v| *v), out_path) + Patch::new( + &Path::new("..").join("patch"), + &Path::new("..").join("LuaJIT"), + )? + .apply_all(PATCH_LIST.iter().map(|v| *v), out_path) } fn run_build(build_dir: &Path) -> std::io::Result { - let manifest = std::env::var_os("CARGO_MANIFEST_PATH").map(PathBuf::from).expect("Failed to read manifest path"); + let manifest = std::env::var_os("CARGO_MANIFEST_PATH") + .map(PathBuf::from) + .expect("Failed to read manifest path"); let target_name = std::env::var("TARGET").expect("Failed to read build target"); let base = BuildInfoBase { dynamic: DYNAMIC, target_name: &target_name, build_dir, - manifest: &manifest + manifest: &manifest, }; BuildInfo::new(base)?.build() } diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index 95a2e59..3c188ac 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_int; use crate::ffi::lua::{Integer, Number, State}; +use std::ffi::c_int; pub type MSize = u32; @@ -52,10 +52,10 @@ extern "C" { extern "C" { /// Sets the global mode of the JIT. pub fn lua_ext_setjitmode(l: State, mode: c_int) -> c_int; - + /// Returns global flags of the JIT. pub fn lua_ext_getjitflags(l: State) -> u32; - + /// Sets global JIT flags. pub fn lua_ext_setjitflags(l: State, flags: u32) -> c_int; } diff --git a/core/src/ffi/laux.rs b/core/src/ffi/laux.rs index 15ad1bd..bdee28b 100644 --- a/core/src/ffi/laux.rs +++ b/core/src/ffi/laux.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::{c_char, c_int, c_void}; use crate::ffi::lua::{Integer, Number, State, ThreadStatus, Type}; +use std::ffi::{c_char, c_int, c_void}; //-------------------- // State manipulation @@ -53,7 +53,12 @@ extern "C" { //--------------- extern "C" { pub fn luaL_checklstring(l: State, numarg: c_int, len: *mut usize) -> *const c_char; - pub fn luaL_optlstring(l: State, numarg: c_int, def: *const c_char, len: *mut usize) -> *const c_char; + pub fn luaL_optlstring( + l: State, + numarg: c_int, + def: *const c_char, + len: *mut usize, + ) -> *const c_char; pub fn luaL_checknumber(l: State, numarg: c_int) -> Number; pub fn luaL_optnumber(l: State, narg: c_int, def: Number) -> Number; @@ -85,7 +90,12 @@ extern "C" { extern "C" { pub fn luaL_where(l: State, lvl: c_int); - pub fn luaL_checkoption(l: State, narg: c_int, def: *const c_char, lst: *const *const c_char) -> c_int; + pub fn luaL_checkoption( + l: State, + narg: c_int, + def: *const c_char, + lst: *const *const c_char, + ) -> c_int; } //---------- @@ -104,6 +114,11 @@ extern "C" { //------------------ extern "C" { pub fn luaL_loadfile(l: State, filename: *const c_char) -> ThreadStatus; - pub fn luaL_loadbuffer(l: State, buff: *const c_char, sz: usize, name: *const c_char) -> ThreadStatus; + pub fn luaL_loadbuffer( + l: State, + buff: *const c_char, + sz: usize, + name: *const c_char, + ) -> ThreadStatus; pub fn luaL_loadstring(l: State, s: *const c_char) -> ThreadStatus; } diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 43b0f91..0544e39 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -50,7 +50,7 @@ pub enum ThreadStatus { ErrRun = 2, ErrSyntax = 3, ErrMem = 4, - ErrErr = 5 + ErrErr = 5, } #[repr(transparent)] @@ -75,13 +75,12 @@ pub enum Type { Table = 5, Function = 6, Userdata = 7, - Thread = 8 + Thread = 8, } pub type Number = c_double; pub type Integer = isize; - //-------------------- // State manipulation //-------------------- @@ -155,11 +154,11 @@ extern "C" { // Get functions (Lua -> stack) //------------------------------- extern "C" { - pub fn lua_gettable(l: State, idx: c_int); - pub fn lua_getfield(l: State, idx: c_int, k: *const c_char); - pub fn lua_rawget(l: State, idx: c_int); - pub fn lua_rawgeti(l: State, idx: c_int, n: c_int); - pub fn lua_createtable(l: State, narr: c_int, nrec: c_int); + pub fn lua_gettable(l: State, idx: c_int); + pub fn lua_getfield(l: State, idx: c_int, k: *const c_char); + pub fn lua_rawget(l: State, idx: c_int); + pub fn lua_rawgeti(l: State, idx: c_int, n: c_int); + pub fn lua_createtable(l: State, narr: c_int, nrec: c_int); pub fn lua_newuserdata(l: State, sz: usize) -> *mut c_void; pub fn lua_getmetatable(l: State, objindex: c_int) -> c_int; pub fn lua_getfenv(l: State, idx: c_int); @@ -184,7 +183,12 @@ extern "C" { pub fn lua_call(l: State, nargs: c_int, nresults: c_int); pub fn lua_pcall(l: State, nargs: c_int, nresults: c_int, errfunc: c_int) -> ThreadStatus; pub fn lua_cpcall(l: State, func: CFunction, ud: *mut c_void) -> c_int; - pub fn lua_load(l: State, reader: Reader, dt: *mut c_void, chunkname: *const c_char) -> ThreadStatus; + pub fn lua_load( + l: State, + reader: Reader, + dt: *mut c_void, + chunkname: *const c_char, + ) -> ThreadStatus; pub fn lua_dump(l: State, writer: Writer, data: *mut c_void) -> c_int; } diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index 80a5329..9ac5561 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -26,6 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub mod lua; -pub mod laux; pub mod ext; +pub mod laux; +pub mod lua; diff --git a/core/src/lib.rs b/core/src/lib.rs index 62fa985..ef0ef34 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -30,7 +30,7 @@ //TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. pub mod ffi; -pub mod vm; +pub mod libs; mod r#macro; pub mod util; -pub mod libs; +pub mod vm; diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index 1dd43c7..fd988cf 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -35,7 +35,7 @@ const PATCH_LIST: &[&str] = &[ "lib_init", "lj_disable_jit", "lua_ext", - "lua_load_no_bc" + "lua_load_no_bc", ]; pub struct Base; @@ -44,10 +44,7 @@ impl Lib for Base { const NAMESPACE: &'static str = "bp3d.lua"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.add([ - ("name", "bp3d-lua"), - ("version", env!("CARGO_PKG_VERSION")) - ])?; + namespace.add([("name", "bp3d-lua"), ("version", env!("CARGO_PKG_VERSION"))])?; let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); for (i, name) in PATCH_LIST.into_iter().enumerate() { // Lua indices starts at 1 not 0. diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 52dca0f..622bebf 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::{Path, PathBuf}; -use bp3d_util::simple_error; -use crate::{decl_closure, decl_lib_func}; use crate::libs::interface::Lib; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::LuaFunction; +use crate::{decl_closure, decl_lib_func}; +use bp3d_util::simple_error; +use std::path::{Path, PathBuf}; decl_lib_func! { fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> crate::vm::Result { @@ -120,12 +120,12 @@ impl Lib for Load<'_> { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([ ("runString", RFunction::wrap(run_string)), - ("loadString", RFunction::wrap(load_string)) + ("loadString", RFunction::wrap(load_string)), ])?; if let Some(chroot) = self.0 { namespace.add([ ("loadFile", load_file(chroot)), - ("runFile", run_file(chroot)) + ("runFile", run_file(chroot)), ])?; } Ok(()) diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index abb51ca..76af4aa 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod base; +mod call; mod load; -pub mod require; mod options; -mod call; -mod base; +pub mod require; pub use base::Base; pub use call::Call; diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index de7afd7..3c48538 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -26,23 +26,23 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::Path; -use crate::libs::Lib; use crate::libs::lua::base::Base; use crate::libs::lua::call::Call; use crate::libs::lua::load::Load; use crate::libs::lua::require::{Provider, Require}; +use crate::libs::Lib; +use std::path::Path; pub struct Lua<'a> { pub(super) load_chroot_path: Option<&'a Path>, - pub(super) provider: Option> + pub(super) provider: Option>, } impl<'a> Lua<'a> { pub fn new() -> Self { Self { load_chroot_path: None, - provider: None + provider: None, } } @@ -57,6 +57,11 @@ impl<'a> Lua<'a> { } pub fn build(self) -> impl Lib + 'a { - (Base, Call, Load(self.load_chroot_path), Require(self.provider.unwrap_or_default())) + ( + Base, + Call, + Load(self.load_chroot_path), + Require(self.provider.unwrap_or_default()), + ) } } diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index d7c2b95..3b150db 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::RefCell; -use std::collections::HashMap; -use bp3d_util::simple_error; use crate::decl_closure; use crate::libs::interface::Lib; use crate::vm::closure::rc::Rc; +use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; -use crate::vm::namespace::Namespace; +use bp3d_util::simple_error; +use std::cell::RefCell; +use std::collections::HashMap; simple_error! { pub Error { @@ -65,7 +65,9 @@ impl Provider { let id = path.find('.').ok_or(Error::InvalidSyntax)?; let source = &path[..id]; let guard = self.0.borrow(); - let src = guard.get(source).ok_or_else(|| Error::UnknownSource(source.into()))?; + let src = guard + .get(source) + .ok_or_else(|| Error::UnknownSource(source.into()))?; let ret = src.run(vm, path)?; Ok(ret) } diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index 1cf242a..0121ab4 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -26,37 +26,45 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::time::Instant; -use bp3d_os::time::{LocalUtcOffset, MonthExt}; -use bp3d_util::simple_error; -use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset}; -use time::format_description::parse; use crate::decl_lib_func; use crate::libs::Lib; -use crate::vm::function::IntoParam; use crate::vm::function::types::RFunction; +use crate::vm::function::IntoParam; use crate::vm::namespace::Namespace; use crate::vm::table::Table; use crate::vm::Vm; +use bp3d_os::time::{LocalUtcOffset, MonthExt}; +use bp3d_util::simple_error; +use std::time::Instant; +use time::format_description::parse; +use time::{Date, Month, OffsetDateTime, PrimitiveDateTime, Time, UtcOffset}; enum TableOrString<'a> { Table(Table<'a>), - String(String) + String(String), } unsafe impl IntoParam for TableOrString<'_> { fn into_param(self, vm: &Vm) -> u16 { match self { TableOrString::Table(t) => t.into_param(vm), - TableOrString::String(s) => s.into_param(vm) + TableOrString::String(s) => s.into_param(vm), } } } fn get_std_offset() -> UtcOffset { let now = OffsetDateTime::now_utc(); - let jan = PrimitiveDateTime::new(Date::from_calendar_date(now.year(), Month::January, 1).unwrap(), Time::MIDNIGHT).assume_utc(); - let jul = PrimitiveDateTime::new(Date::from_calendar_date(now.year(), Month::July, 1).unwrap(), Time::MIDNIGHT).assume_utc(); + let jan = PrimitiveDateTime::new( + Date::from_calendar_date(now.year(), Month::January, 1).unwrap(), + Time::MIDNIGHT, + ) + .assume_utc(); + let jul = PrimitiveDateTime::new( + Date::from_calendar_date(now.year(), Month::July, 1).unwrap(), + Time::MIDNIGHT, + ) + .assume_utc(); let offset_jan = UtcOffset::local_offset_at(jan).unwrap(); let offset_jul = UtcOffset::local_offset_at(jul).unwrap(); std::cmp::max(offset_jan, offset_jul) @@ -78,7 +86,7 @@ const REPLACEMENTS: &[(&str, &str)] = &[ ("%S", "[second]"), ("%w", "[weekday]"), ("%Y", "[year]"), - ("%y", "[year repr:last_two]") + ("%y", "[year repr:last_two]"), ]; decl_lib_func! { @@ -127,7 +135,11 @@ fn get_time_from_table(table: Table) -> Result let year: i32 = table.get_field(c"year")?; let month: u8 = table.get_field(c"month")?; let day: u8 = table.get_field(c"day")?; - let date = Date::from_calendar_date(year, Month::from_index(month).ok_or(TimeFormatError::InvalidMonthIndex(month))?, day)?; + let date = Date::from_calendar_date( + year, + Month::from_index(month).ok_or(TimeFormatError::InvalidMonthIndex(month))?, + day, + )?; let hour: Option = table.get_field(c"hour")?; let minute: Option = table.get_field(c"min")?; let second: Option = table.get_field(c"sec")?; @@ -189,7 +201,7 @@ impl Lib for Compat { ("time", RFunction::wrap(time)), ("clock", RFunction::wrap(clock)), ("difftime", RFunction::wrap(difftime)), - ("getenv", RFunction::wrap(getenv)) + ("getenv", RFunction::wrap(getenv)), ]) } } diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index 50ab643..5710787 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{decl_lib_func, decl_userdata}; use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; +use crate::{decl_lib_func, decl_userdata}; struct Wrapper(bp3d_os::time::Instant); @@ -53,7 +53,9 @@ impl Lib for Instant { const NAMESPACE: &'static str = "bp3d.os.instant"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.vm().register_userdata::(crate::vm::userdata::case::Camel)?; + namespace + .vm() + .register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([("now", RFunction::wrap(now))]) } } diff --git a/core/src/libs/os/mod.rs b/core/src/libs/os/mod.rs index a39e48c..48f9a5b 100644 --- a/core/src/libs/os/mod.rs +++ b/core/src/libs/os/mod.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod compat; -mod time; mod instant; +mod time; pub use compat::Compat; -pub use time::Time; pub use instant::Instant; +pub use time::Time; diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 49df93c..84e25ff 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_os::time::{LocalOffsetDateTime, MonthExt}; -use bp3d_util::simple_error; -use time::error::{ComponentRange, Format, InvalidFormatDescription}; -use time::format_description::parse; -use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, UtcOffset}; -use crate::{decl_lib_func, decl_userdata}; use crate::libs::Lib; -use crate::vm::function::IntoParam; use crate::vm::function::types::RFunction; +use crate::vm::function::IntoParam; use crate::vm::namespace::Namespace; use crate::vm::table::Table; use crate::vm::Vm; +use crate::{decl_lib_func, decl_userdata}; +use bp3d_os::time::{LocalOffsetDateTime, MonthExt}; +use bp3d_util::simple_error; +use time::error::{ComponentRange, Format, InvalidFormatDescription}; +use time::format_description::parse; +use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, UtcOffset}; simple_error! { FormatError { @@ -170,12 +170,14 @@ impl Lib for Time { const NAMESPACE: &'static str = "bp3d.os.time"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.vm().register_userdata::(crate::vm::userdata::case::Camel)?; + namespace + .vm() + .register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([ ("nowUtc", RFunction::wrap(now_utc)), ("nowLocal", RFunction::wrap(now_local)), ("fromUnixTimestamp", RFunction::wrap(from_unix_timestamp)), - ("new", RFunction::wrap(new)) + ("new", RFunction::wrap(new)), ]) } } diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 6b1d051..4db407f 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod table; mod string; +mod table; mod utf8; -pub use table::Table; pub use string::String; +pub use table::Table; pub use utf8::Utf8; // Workaround for language defect #22259. diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 80b55ac..9e759d5 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use bp3d_util::string::BufTools; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::table::Table; +use bp3d_util::string::BufTools; +use std::borrow::Cow; decl_lib_func! { fn contains(src: &[u8], needle: &[u8]) -> bool { @@ -77,7 +77,7 @@ impl Lib for String { ("contains", RFunction::wrap(contains)), ("split", RFunction::wrap(split)), ("capitalise", RFunction::wrap(capitalise)), - ("decapitalise", RFunction::wrap(decapitalise)) + ("decapitalise", RFunction::wrap(decapitalise)), ]) } } diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 45dcc75..4bc305e 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -26,34 +26,32 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_util::simple_error; -use crate::vm::table::Table as LuaTable; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; +use crate::vm::table::Table as LuaTable; use crate::vm::value::any::AnyValue; use crate::vm::Vm; +use bp3d_util::simple_error; fn update_rec(vm: &Vm, mut dst: LuaTable, mut src: LuaTable) -> crate::vm::Result<()> { for res in src.iter() { let (k, v) = res?; match v { - AnyValue::Table(v) => { - vm.scope(|_| { - let dst1: Option = dst.get(k.clone())?; - match dst1 { - None => { - let tbl = LuaTable::new(vm); - update_rec(vm, tbl.clone(), v)?; - dst.set(k, tbl)?; - }, - Some(v1) => update_rec(vm, v1, v)? + AnyValue::Table(v) => vm.scope(|_| { + let dst1: Option = dst.get(k.clone())?; + match dst1 { + None => { + let tbl = LuaTable::new(vm); + update_rec(vm, tbl.clone(), v)?; + dst.set(k, tbl)?; } - Ok(()) - })? - } - _ => dst.set(k, v)? + Some(v1) => update_rec(vm, v1, v)?, + } + Ok(()) + })?, + _ => dst.set(k, v)?, } } Ok(()) @@ -102,8 +100,8 @@ fn to_string_rec(prefix: String, mut table: LuaTable) -> crate::vm::Result { lines.push(format!("{}:", k)); lines.extend(to_string_rec(prefix.clone() + " ", v)?); - }, - v => lines.push(format!("{}: {}", k, v)) + } + v => lines.push(format!("{}: {}", k, v)), } } Ok(lines) @@ -178,7 +176,7 @@ impl Lib for Table { ("containsKey", RFunction::wrap(contains_key)), ("protect", RFunction::wrap(protect)), ("copy", RFunction::wrap(copy)), - ("concat", RFunction::wrap(concat)) + ("concat", RFunction::wrap(concat)), ]) } } diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index cb8307a..e8c2371 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use bp3d_util::string::StrTools; use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::table::Table; +use bp3d_util::string::StrTools; +use std::borrow::Cow; decl_lib_func! { fn contains(src: &str, needle: &str) -> bool { @@ -133,7 +133,7 @@ impl Lib for Utf8 { ("decapitalise", RFunction::wrap(decapitalise)), ("upper", RFunction::wrap(upper)), ("lower", RFunction::wrap(lower)), - ("sub", RFunction::wrap(sub)) + ("sub", RFunction::wrap(sub)), ]) } } diff --git a/core/src/macro/mod.rs b/core/src/macro/mod.rs index cd9cc7d..95e9140 100644 --- a/core/src/macro/mod.rs +++ b/core/src/macro/mod.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod closure; mod lib_func; -mod userdata_func; mod userdata; -mod closure; +mod userdata_func; #[macro_export] macro_rules! c_stringify { @@ -41,7 +41,7 @@ macro_rules! c_stringify { #[macro_export] macro_rules! decl_from_param { ( - $vm: ident, $start_index: literal, + $vm: ident, $start_index: literal, ) => { }; diff --git a/core/src/util.rs b/core/src/util.rs index 414f485..350a8e2 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -38,7 +38,9 @@ pub trait AnyStr { impl AnyStr for &str { fn to_str(&self) -> crate::vm::Result> { - Ok(Cow::Owned(CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?)) + Ok(Cow::Owned( + CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?, + )) } } @@ -59,5 +61,5 @@ unsafe impl SimpleDrop for Option {} unsafe impl SimpleDrop for Result {} unsafe impl SimpleDrop for &T {} unsafe impl SimpleDrop for &[u8] {} -unsafe impl SimpleDrop for &OsStr { } -unsafe impl SimpleDrop for &Path { } +unsafe impl SimpleDrop for &OsStr {} +unsafe impl SimpleDrop for &Path {} diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 81d89b5..0948946 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -28,17 +28,17 @@ //! Second version of the context tool. -use std::marker::PhantomData; -use std::ops::{Deref, DerefMut}; use crate::ffi::laux::luaL_error; use crate::ffi::lua::lua_newuserdata; use crate::util::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::registry::core::RawRegistryKey; use crate::vm::Vm; +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; pub struct Cell { - ptr: *mut *const T + ptr: *mut *const T, } impl Cell { @@ -50,13 +50,13 @@ impl Cell { unsafe { *self.ptr = obj as _ }; Guard { useless: PhantomData, - ud: self.ptr + ud: self.ptr, } } } pub struct CellMut { - ptr: *mut *const T + ptr: *mut *const T, } impl CellMut { @@ -68,21 +68,21 @@ impl CellMut { unsafe { *self.ptr = obj as _ }; Guard { useless: PhantomData, - ud: self.ptr + ud: self.ptr, } } } pub struct Context { key: RawRegistryKey, - ptr: *mut *const T + ptr: *mut *const T, } impl Clone for Context { fn clone(&self) -> Self { Self { key: self.key, - ptr: self.ptr + ptr: self.ptr, } } } @@ -97,7 +97,7 @@ impl Clone for ContextMut { } } -impl Copy for ContextMut { } +impl Copy for ContextMut {} impl Context { pub fn new(vm: &Vm) -> Self { @@ -107,7 +107,7 @@ impl Context { }; Self { key, - ptr: ptr as *mut *const T + ptr: ptr as *mut *const T, } } } @@ -121,13 +121,15 @@ impl ContextMut { #[repr(transparent)] pub struct Guard<'a, T> { ud: *mut *const T, - useless: PhantomData<&'a T> + useless: PhantomData<&'a T>, } impl<'a, T> Drop for Guard<'a, T> { #[inline(always)] fn drop(&mut self) { - unsafe { *self.ud = std::ptr::null(); } + unsafe { + *self.ud = std::ptr::null(); + } } } @@ -162,14 +164,17 @@ impl<'a, T: 'static> DerefMut for Mut<'a, T> { } } -unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> { } -unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> { } +unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> {} +unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> {} impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { let ptr: *mut *const T = FromUpvalue::from_upvalue(vm, index); if (*ptr).is_null() { - luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + luaL_error( + vm.as_ptr(), + c"Context is not available in this function.".as_ptr(), + ); // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } @@ -181,7 +186,10 @@ impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { let ptr: *mut *mut T = FromUpvalue::from_upvalue(vm, index); if (*ptr).is_null() { - luaL_error(vm.as_ptr(), c"Context is not available in this function.".as_ptr()); + luaL_error( + vm.as_ptr(), + c"Context is not available in this function.".as_ptr(), + ); // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index 6e18e5b..e23e25f 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::OsStr; -use std::path::Path; use crate::ffi::lua::{lua_pushlightuserdata, lua_topointer, GLOBALSINDEX}; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; -use crate::vm::Vm; use crate::vm::value::FromLua; +use crate::vm::Vm; +use std::ffi::OsStr; +use std::path::Path; macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { @@ -142,7 +142,9 @@ impl Upvalue for &OsStr { impl<'a> FromUpvalue<'a> for &'a Path { #[inline(always)] unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - Path::new(OsStr::from_encoded_bytes_unchecked(FromUpvalue::from_upvalue(vm, index))) + Path::new(OsStr::from_encoded_bytes_unchecked( + FromUpvalue::from_upvalue(vm, index), + )) } } diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 5ab1f68..1d3cb24 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod interface; -mod core; -pub mod types; pub mod context; +mod core; +mod interface; pub mod rc; +pub mod types; pub use interface::*; diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 0e42ffb..273a32d 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ops::Deref; use crate::util::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::Vm; +use std::ops::Deref; #[repr(transparent)] pub struct Rc(*const T); @@ -37,7 +37,7 @@ pub struct Rc(*const T); #[repr(transparent)] pub struct Ref<'a, T>(&'a T); -unsafe impl SimpleDrop for Ref<'_, T> { } +unsafe impl SimpleDrop for Ref<'_, T> {} impl<'a, T> Deref for Ref<'a, T> { type Target = T; diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 72ac197..9a4fe52 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_void; use crate::ffi::lua::{lua_pushcclosure, CFunction, State}; use crate::vm::closure::{FromUpvalue, IntoUpvalue}; +use crate::vm::function::{FromParam, IntoParam}; use crate::vm::value::IntoLua; use crate::vm::Vm; -use crate::vm::function::{FromParam, IntoParam}; +use std::ffi::c_void; pub struct RClosure { func: CFunction, - upvalue: T + upvalue: T, } impl RClosure { @@ -62,10 +62,16 @@ unsafe impl IntoLua for RClosure { impl RClosure<*const c_void> { pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self - where for<'a> T: FromParam<'a>, R: IntoParam { + where + for<'a> T: FromParam<'a>, + R: IntoParam, + { let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 - where for<'a> T: FromParam<'a>, R: IntoParam { + where + for<'a> T: FromParam<'a>, + R: IntoParam, + { let vm = unsafe { Vm::from_raw(l) }; let upvalue: *const F = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; let args: T = unsafe { FromParam::from_param(&vm, 1) }; diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index e53ae3a..0bbac7e 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -26,10 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::rc::Rc; -use bp3d_debug::debug; -use crate::ffi::lua::{lua_gettable, lua_pushlightuserdata, lua_pushstring, lua_settable, lua_settop, lua_touserdata, REGISTRYINDEX}; +use crate::ffi::lua::{ + lua_gettable, lua_pushlightuserdata, lua_pushstring, lua_settable, lua_settop, lua_touserdata, + REGISTRYINDEX, +}; use crate::vm::Vm; +use bp3d_debug::debug; +use std::rc::Rc; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { @@ -72,7 +75,7 @@ impl Raw for Rc { #[derive(Default)] pub struct Pool { - leaked: Vec> + leaked: Vec>, } impl Pool { @@ -117,12 +120,18 @@ impl Pool { unsafe { Self::_from_vm(vm) } } - pub fn attach(vm: &Vm, raw: R) -> R::Ptr where R::Ptr: 'static { + pub fn attach(vm: &Vm, raw: R) -> R::Ptr + where + R::Ptr: 'static, + { let ptr = unsafe { Self::_from_vm(vm) }; ptr.attach_mut(raw) } - pub fn attach_mut(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static { + pub fn attach_mut(&mut self, raw: R) -> R::Ptr + where + R::Ptr: 'static, + { let ptr = R::into_raw(raw); self.leaked.push(Box::new(move || { unsafe { R::delete(ptr) }; @@ -133,7 +142,10 @@ impl Pool { impl Drop for Pool { fn drop(&mut self) { - debug!({num=self.leaked.len() as u32}, "Deleting leaked pointers..."); + debug!( + { num = self.leaked.len() as u32 }, + "Deleting leaked pointers..." + ); let v = std::mem::replace(&mut self.leaked, Vec::new()); for f in v { f() diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index f34da30..228c925 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -43,8 +43,8 @@ pub use windows::Signal; unsafe impl Send for Signal {} unsafe impl Sync for Signal {} -use std::thread::JoinHandle; use bp3d_util::simple_error; +use std::thread::JoinHandle; simple_error! { pub Error { @@ -55,7 +55,9 @@ simple_error! { } } -pub fn spawn_interruptible(f: impl FnOnce(&mut crate::vm::RootVm) -> R + Send + 'static) -> (Signal, JoinHandle) { +pub fn spawn_interruptible( + f: impl FnOnce(&mut crate::vm::RootVm) -> R + Send + 'static, +) -> (Signal, JoinHandle) { let (send, recv) = std::sync::mpsc::channel(); let handle = std::thread::spawn(move || { let mut vm = crate::vm::RootVm::new(); diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index e7a38b3..8d196a1 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -26,20 +26,23 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::lua::{ + lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, + MASKRET, +}; +use crate::vm::core::interrupt::Error; +use crate::vm::RootVm; +use bp3d_debug::{error, warning}; +use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use std::mem::MaybeUninit; use std::sync::{Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; -use bp3d_debug::{error, warning}; -use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; -use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET}; -use crate::vm::core::interrupt::Error; -use crate::vm::RootVm; pub struct Signal { l: State, thread: ThreadId, - th: pthread_t + th: pthread_t, } struct SigState { @@ -63,7 +66,12 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { } } unsafe { - lua_sethook(l, std::mem::transmute::<*const (), Hook>(std::ptr::null()), 0, 0); + lua_sethook( + l, + std::mem::transmute::<*const (), Hook>(std::ptr::null()), + 0, + 0, + ); lua_pushstring(l, c"interrupted".as_ptr()); lua_error(l); } @@ -80,7 +88,14 @@ extern "C" fn signal_handler(_: c_int) { return; } // Run the hook 1 instruction later. - unsafe { lua_sethook(v.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1) }; + unsafe { + lua_sethook( + v.l, + lua_interrupt, + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ) + }; v.return_chan.send(Ok(())).unwrap(); } } @@ -103,18 +118,16 @@ impl Signal { let ret = unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; assert_eq!(ret, 0); }); - Self { - l, - thread, - th - } + Self { l, thread, th } } pub fn send(&self, duration: Duration) -> Result<(), Error> { let (send, recv) = std::sync::mpsc::channel(); let (send2, recv2) = std::sync::mpsc::channel(); { - let mut lock = SIG_STATE.try_lock().map_err(|_| Error::AlreadyInterrupting)?; + let mut lock = SIG_STATE + .try_lock() + .map_err(|_| Error::AlreadyInterrupting)?; *lock = Some(SigState { l: self.l, thread: self.thread, diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index da5e4fc..7fba3cc 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -1,112 +1,130 @@ -// Copyright (c) 2025, BlockProject 3D -// -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without modification, -// are permitted provided that the following conditions are met: -// -// * Redistributions of source code must retain the above copyright notice, -// this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above copyright notice, -// this list of conditions and the following disclaimer in the documentation -// and/or other materials provided with the distribution. -// * Neither the name of BlockProject 3D nor the names of its contributors -// may be used to endorse or promote products derived from this software -// without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -use std::time::Duration; -use std::sync::Mutex; -use crate::vm::RootVm; -use bp3d_debug::{error, warning}; -use windows_sys::Win32::Foundation::HANDLE; -use windows_sys::Win32::System::Threading::{ GetCurrentThread, SuspendThread, ResumeThread }; -use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; -use crate::ffi::lua::{lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET}; - -use super::Error; - -static SIG_STATE: Mutex>> = Mutex::new(None); - -extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { - { - let mut state = SIG_STATE.lock().unwrap(); - if let Some(sig) = state.take() { - if let Err(e) = sig.send(()) { - error!({error=?e}, "Failed to notify interrupt signal") - } - } - } - unsafe { - lua_sethook(l, std::mem::transmute::<*const (), Hook>(std::ptr::null()), 0, 0); - lua_pushstring(l, c"interrupted".as_ptr()); - lua_error(l); - } -} - -pub struct Signal { - l: State, - th: HANDLE -} - -impl Signal { - pub fn create(vm: &mut RootVm) -> Self { - let th = unsafe { GetCurrentThread() }; - let l = vm.as_ptr(); - Self { - l, - th - } - } - - pub fn send(&self, duration: Duration) -> Result<(), Error> { - let (send2, recv2) = std::sync::mpsc::channel(); - { - let mut lock = SIG_STATE.try_lock().map_err(|_| Error::AlreadyInterrupting)?; - *lock = Some(send2); - } - if self.th == unsafe { GetCurrentThread() } { - // If somehow the system thread that ineterrupts the Vm is the same as the one which started the Vm, then directly set the hook. - unsafe { - lua_sethook(self.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); - } - } else { - unsafe { - let mut ctx: CONTEXT = std::mem::zeroed(); - // Requests to suspend the thread. - if SuspendThread(self.th) == u32::MAX { //(DWORD) -1 - return Err(Error::Unknown); - } - // This call forces synchronization with the thread to be suspended. - if GetThreadContext(self.th, &mut ctx as _) == 0 { - return Err(Error::Unknown); - } - lua_sethook(self.l, lua_interrupt, MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); - // Resume the thread. - let _ = ResumeThread(self.th); - } - } - match recv2.recv_timeout(duration) { - Ok(()) => Ok(()), - Err(e) => { - warning!({error=?e}, "Error attempting to wait for interrupt notification"); - { - let mut guard = SIG_STATE.lock().unwrap(); - *guard = None; - } - Err(Error::Timeout) - } - } - } -} +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{ + lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, + MASKRET, +}; +use crate::vm::RootVm; +use bp3d_debug::{error, warning}; +use std::sync::Mutex; +use std::time::Duration; +use windows_sys::Win32::Foundation::HANDLE; +use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; +use windows_sys::Win32::System::Threading::{GetCurrentThread, ResumeThread, SuspendThread}; + +use super::Error; + +static SIG_STATE: Mutex>> = Mutex::new(None); + +extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { + { + let mut state = SIG_STATE.lock().unwrap(); + if let Some(sig) = state.take() { + if let Err(e) = sig.send(()) { + error!({error=?e}, "Failed to notify interrupt signal") + } + } + } + unsafe { + lua_sethook( + l, + std::mem::transmute::<*const (), Hook>(std::ptr::null()), + 0, + 0, + ); + lua_pushstring(l, c"interrupted".as_ptr()); + lua_error(l); + } +} + +pub struct Signal { + l: State, + th: HANDLE, +} + +impl Signal { + pub fn create(vm: &mut RootVm) -> Self { + let th = unsafe { GetCurrentThread() }; + let l = vm.as_ptr(); + Self { l, th } + } + + pub fn send(&self, duration: Duration) -> Result<(), Error> { + let (send2, recv2) = std::sync::mpsc::channel(); + { + let mut lock = SIG_STATE + .try_lock() + .map_err(|_| Error::AlreadyInterrupting)?; + *lock = Some(send2); + } + if self.th == unsafe { GetCurrentThread() } { + // If somehow the system thread that ineterrupts the Vm is the same as the one which started the Vm, then directly set the hook. + unsafe { + lua_sethook( + self.l, + lua_interrupt, + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ); + } + } else { + unsafe { + let mut ctx: CONTEXT = std::mem::zeroed(); + // Requests to suspend the thread. + if SuspendThread(self.th) == u32::MAX { + //(DWORD) -1 + return Err(Error::Unknown); + } + // This call forces synchronization with the thread to be suspended. + if GetThreadContext(self.th, &mut ctx as _) == 0 { + return Err(Error::Unknown); + } + lua_sethook( + self.l, + lua_interrupt, + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ); + // Resume the thread. + let _ = ResumeThread(self.th); + } + } + match recv2.recv_timeout(duration) { + Ok(()) => Ok(()), + Err(e) => { + warning!({error=?e}, "Error attempting to wait for interrupt notification"); + { + let mut guard = SIG_STATE.lock().unwrap(); + *guard = None; + } + Err(Error::Timeout) + } + } + } +} diff --git a/core/src/vm/core/iter.rs b/core/src/vm/core/iter.rs index eeadf63..ac24255 100644 --- a/core/src/vm/core/iter.rs +++ b/core/src/vm/core/iter.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::marker::PhantomData; use crate::vm::value::FromLua; use crate::vm::Vm; +use std::marker::PhantomData; pub struct Iter<'a, T> { vm: &'a Vm, index: i32, - useless: PhantomData + useless: PhantomData, } impl<'a, T: FromLua<'a>> Iterator for Iter<'a, T> { @@ -53,6 +53,6 @@ pub fn start<'a, T: FromLua<'a>>(vm: &'a Vm, start_index: i32) -> Iter<'a, T> { Iter { vm, index: start_index, - useless: PhantomData + useless: PhantomData, } } diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs index 297dc08..af2f6b9 100644 --- a/core/src/vm/core/load.rs +++ b/core/src/vm/core/load.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Write; -use std::ffi::{c_char, c_void, CStr, CString, OsStr}; -use std::fs::File; -use std::path::Path; use crate::ffi::laux::{luaL_loadbuffer, luaL_loadstring}; use crate::ffi::lua::{lua_load, State, ThreadStatus}; -use crate::vm::core::{Load, LoadString}; use crate::vm::core::util::{ChunkName, ChunkNameBuilder}; +use crate::vm::core::{Load, LoadString}; use crate::vm::util::lua_rust_error; +use std::ffi::{c_char, c_void, CStr, CString, OsStr}; +use std::fmt::Write; +use std::fs::File; +use std::path::Path; impl LoadString for &CStr { #[inline(always)] @@ -47,25 +47,20 @@ impl LoadString for &str { fn load_string(&self, l: State) -> ThreadStatus { let s = CString::new(*self); match s { - Ok(v) => { - (&*v).load_string(l) - } - Err(_) => ThreadStatus::ErrSyntax + Ok(v) => (&*v).load_string(l), + Err(_) => ThreadStatus::ErrSyntax, } } } pub struct Code<'a> { name: &'a str, - code: &'a [u8] + code: &'a [u8], } impl<'a> Code<'a> { pub fn new(name: &'a str, code: &'a [u8]) -> Self { - Self { - name, - code - } + Self { name, code } } } @@ -74,7 +69,14 @@ impl Load for Code<'_> { let mut builder = ChunkNameBuilder::new(); let _ = write!(&mut builder, "={}", self.name); let name = builder.build(); - unsafe { luaL_loadbuffer(l, self.code.as_ptr() as _, self.code.len(), name.cstr().as_ptr()) } + unsafe { + luaL_loadbuffer( + l, + self.code.as_ptr() as _, + self.code.len(), + name.cstr().as_ptr(), + ) + } } } @@ -95,15 +97,23 @@ pub trait Custom { /// # Safety /// /// This is UB to call outside a [Load] trait implementation. -pub unsafe fn load_custom(l: State, chunk_name: ChunkName, mut custom: T) -> ThreadStatus { - extern "C-unwind" fn _reader(l: State, ud: *mut c_void, sz: *mut usize) -> *const c_char { +pub unsafe fn load_custom( + l: State, + chunk_name: ChunkName, + mut custom: T, +) -> ThreadStatus { + extern "C-unwind" fn _reader( + l: State, + ud: *mut c_void, + sz: *mut usize, + ) -> *const c_char { let obj = ud as *mut T; unsafe { let res = (&mut *obj).read_data(); match res { Err(e) => { lua_rust_error(l, e); - }, + } Ok(v) => { *sz = v.len(); v.as_ptr() as _ @@ -111,7 +121,12 @@ pub unsafe fn load_custom(l: State, chunk_name: ChunkName, mut custom } } } - lua_load(l, _reader::, &mut custom as *mut T as _, chunk_name.cstr().as_ptr()) + lua_load( + l, + _reader::, + &mut custom as *mut T as _, + chunk_name.cstr().as_ptr(), + ) } const BUF_SIZE: usize = 8192; @@ -119,7 +134,7 @@ const BUF_SIZE: usize = 8192; pub struct Read { inner: T, buffer: [u8; BUF_SIZE], - len: usize + len: usize, } impl Read { @@ -127,7 +142,7 @@ impl Read { Self { inner, buffer: [0; BUF_SIZE], - len: 0 + len: 0, } } } @@ -143,13 +158,18 @@ impl Custom for Read { pub struct Script { file: File, - chunk_name: ChunkName + chunk_name: ChunkName, } impl Script { pub fn from_path(path: impl AsRef) -> std::io::Result { let mut builder = ChunkNameBuilder::new(); - let file_name = path.as_ref().file_name().unwrap_or(OsStr::new("unnamed")).to_str().unwrap_or("not-unicode"); + let file_name = path + .as_ref() + .file_name() + .unwrap_or(OsStr::new("unnamed")) + .to_str() + .unwrap_or("not-unicode"); let _ = write!(&mut builder, "@{}", file_name); let file = File::open(path)?; Ok(Self { diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 31680ee..064f836 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod destructor; mod interface; +pub mod iter; +pub mod load; pub mod util; mod vm; -pub mod load; -pub mod iter; -pub mod destructor; #[cfg(feature = "interrupt")] pub mod interrupt; -pub use vm::{Vm, RootVm}; pub use interface::*; +pub use vm::{RootVm, Vm}; diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index 0f116c2..67573a0 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -26,14 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::{c_int, CStr}; -use bp3d_util::format::FixedBufStr; use crate::ffi::laux::{luaL_callmeta, luaL_traceback}; -use crate::ffi::lua::{lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, lua_tolstring, lua_type, State, ThreadStatus, Type}; +use crate::ffi::lua::IDSIZE; +use crate::ffi::lua::{ + lua_gettop, lua_isstring, lua_pcall, lua_pushcclosure, lua_pushlstring, lua_remove, + lua_tolstring, lua_type, State, ThreadStatus, Type, +}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::value::FromLua; use crate::vm::Vm; -use crate::ffi::lua::IDSIZE; +use bp3d_util::format::FixedBufStr; +use std::ffi::{c_int, CStr}; const TRACEBACK_NONE: &[u8] = b"\n"; extern "C-unwind" fn error_handler(l: State) -> c_int { @@ -41,9 +44,10 @@ extern "C-unwind" fn error_handler(l: State) -> c_int { let ty = lua_type(l, 1); if ty != Type::String { // Non-string error object? Try metamethod. - if (ty == Type::Nil || ty == Type::None) || - luaL_callmeta(l, 1, c"__tostring".as_ptr()) != 1 || - lua_isstring(l, -1) != 1 { + if (ty == Type::Nil || ty == Type::None) + || luaL_callmeta(l, 1, c"__tostring".as_ptr()) != 1 + || lua_isstring(l, -1) != 1 + { // Object does not turn into a string remove it alongside the return value of // __tostring. lua_remove(l, 1); @@ -102,7 +106,12 @@ pub unsafe fn push_error_handler(l: State) -> c_int { /// This function shall not be used without [push_error_handler]. This is also UB if `nargs` does /// not match the count of arguments push on top of the stack. If the error handler is not the first /// item on the stack, before function and function arguments, this is UB. -pub unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) -> crate::vm::Result<()> { +pub unsafe fn pcall( + vm: &Vm, + nargs: c_int, + nreturns: c_int, + handler_pos: c_int, +) -> crate::vm::Result<()> { let l = vm.as_ptr(); unsafe { // Call the function created by load_code. @@ -122,7 +131,7 @@ pub unsafe fn pcall(vm: &Vm, nargs: c_int, nreturns: c_int, handler_pos: c_int) } ThreadStatus::ErrMem => Err(Error::Memory), ThreadStatus::ErrErr => Err(Error::Error), - _ => Err(Error::Unknown) + _ => Err(Error::Unknown), } } } @@ -158,17 +167,19 @@ pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus) -> crate::vm::Resu Err(Error::Loader(str.into())) } ThreadStatus::ErrMem => Err(Error::Memory), - _ => Err(Error::Unknown) + _ => Err(Error::Unknown), } } pub struct ChunkNameBuilder { - inner: FixedBufStr<59> + inner: FixedBufStr<59>, } impl ChunkNameBuilder { pub fn new() -> Self { - Self { inner: FixedBufStr::new() } + Self { + inner: FixedBufStr::new(), + } } pub fn build(self) -> ChunkName { @@ -188,7 +199,7 @@ impl std::fmt::Write for ChunkNameBuilder { } pub struct ChunkName { - inner: FixedBufStr<60> + inner: FixedBufStr<60>, } impl ChunkName { diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 444cb2a..9ae4d41 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,35 +26,39 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; -use std::ops::{Deref, DerefMut}; -use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; -use crate::ffi::lua::{lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; +use crate::ffi::lua::{ + lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, + ThreadStatus, GLOBALSINDEX, REGISTRYINDEX, +}; use crate::util::AnyStr; -use crate::vm::core::{Load, LoadString}; use crate::vm::core::destructor::Pool; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; +use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::function::LuaFunction; +use crate::vm::value::{FromLua, IntoLua}; +use bp3d_debug::debug; +use std::cell::Cell; +use std::ops::{Deref, DerefMut}; #[repr(transparent)] pub struct Vm { - l: State + l: State, } impl Vm { #[inline(always)] pub unsafe fn from_raw(l: State) -> Self { - Self { - l - } + Self { l } } - pub fn scope crate::vm::Result>(&self, f: F) -> crate::vm::Result { + pub fn scope crate::vm::Result>( + &self, + f: F, + ) -> crate::vm::Result { let top = self.top(); let r = f(self)?; unsafe { lua_settop(self.l, top) }; @@ -95,7 +99,9 @@ impl Vm { /// Clears the lua stack. #[inline(always)] pub fn clear(&mut self) { - unsafe { lua_settop(self.l, 0); } + unsafe { + lua_settop(self.l, 0); + } } #[inline(always)] @@ -105,12 +111,16 @@ impl Vm { pub fn set_global(&self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { value.into_lua(self); - unsafe { lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } + unsafe { + lua_setfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); + } Ok(()) } pub fn get_global<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr) -> crate::vm::Result { - unsafe { lua_getfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); } + unsafe { + lua_getfield(self.as_ptr(), GLOBALSINDEX, name.to_str()?.as_ptr()); + } R::from_lua(self, -1) } @@ -171,7 +181,7 @@ thread_local! { } pub struct RootVm { - vm: Vm + vm: Vm, } impl RootVm { @@ -183,7 +193,7 @@ impl RootVm { unsafe { luaL_openlibs(l) }; HAS_VM.set(true); let mut vm = RootVm { - vm: unsafe { Vm::from_raw(l) } + vm: unsafe { Vm::from_raw(l) }, }; unsafe { Pool::new_in_vm(&mut vm) }; vm diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index 5fb0ef4..7b8b58a 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::lua::Type; +use bp3d_util::simple_error; use std::fmt::{Display, Formatter}; use std::str::Utf8Error; -use bp3d_util::simple_error; -use crate::ffi::lua::Type; #[derive(Debug, Copy, Clone)] pub struct TypeError { pub expected: Type, - pub actual: Type + pub actual: Type, } impl Display for TypeError { @@ -46,7 +46,7 @@ impl Display for TypeError { #[derive(Debug, Clone)] pub struct RuntimeError { traceback: String, - index: usize + index: usize, } impl RuntimeError { @@ -54,7 +54,7 @@ impl RuntimeError { let id = traceback.find('\n').unwrap(); Self { traceback, - index: id + index: id, } } @@ -98,7 +98,7 @@ impl Error { pub fn into_runtime(self) -> Option { match self { Error::Runtime(e) => Some(e), - _ => None + _ => None, } } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index d37adfe..147b6f1 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -26,18 +26,21 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use std::error::Error; -use std::slice; +use crate::ffi::ext::{lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; -use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type}; -use crate::ffi::ext::{lua_ext_fast_checknumber, lua_ext_fast_checkinteger}; +use crate::ffi::lua::{ + lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, + lua_pushnumber, lua_type, Integer, Number, Type, +}; use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; use crate::vm::util::{lua_rust_error, LuaType, TypeName}; use crate::vm::value::FromLua; use crate::vm::Vm; +use std::borrow::Cow; +use std::error::Error; +use std::slice; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { @@ -61,14 +64,14 @@ impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { } } -impl LuaType for &str { } +impl LuaType for &str {} impl<'a> FromParam<'a> for &'a str { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { let mut len: usize = 0; let str = luaL_checklstring(vm.as_ptr(), index, &mut len as _); let slice = slice::from_raw_parts(str as *const u8, len); - match std::str::from_utf8(slice){ + match std::str::from_utf8(slice) { Ok(v) => v, Err(e) => { lua_rust_error(vm.as_ptr(), e); @@ -101,7 +104,9 @@ impl<'a> FromParam<'a> for &'a [u8] { unsafe impl IntoParam for &str { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } + unsafe { + lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); + } 1 } } @@ -109,7 +114,9 @@ unsafe impl IntoParam for &str { unsafe impl IntoParam for &[u8] { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); } + unsafe { + lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); + } 1 } } @@ -128,11 +135,13 @@ unsafe impl IntoParam for Vec { } unsafe impl<'a, T: IntoParam + Clone> IntoParam for Cow<'a, T> - where &'a T: IntoParam { +where + &'a T: IntoParam, +{ fn into_param(self, vm: &Vm) -> u16 { match self { Cow::Borrowed(v) => v.into_param(vm), - Cow::Owned(v) => v.into_param(vm) + Cow::Owned(v) => v.into_param(vm), } } } @@ -228,11 +237,9 @@ unsafe impl IntoParam for Result { fn into_param(self, vm: &Vm) -> u16 { match self { Ok(v) => v.into_param(vm), - Err(e) => { - unsafe { - lua_rust_error(vm.as_ptr(), e); - } - } + Err(e) => unsafe { + lua_rust_error(vm.as_ptr(), e); + }, } } } @@ -240,13 +247,11 @@ unsafe impl IntoParam for Result { unsafe impl IntoParam for Option { fn into_param(self, vm: &Vm) -> u16 { match self { - None => { - unsafe { - lua_pushnil(vm.as_ptr()); - 1 - } - } - Some(v) => v.into_param(vm) + None => unsafe { + lua_pushnil(vm.as_ptr()); + 1 + }, + Some(v) => v.into_param(vm), } } } @@ -260,7 +265,9 @@ unsafe impl IntoParam for () { impl LuaType for &T { fn lua_type() -> Vec { - vec![TypeName::Some(unsafe { T::CLASS_NAME.to_str().unwrap_unchecked() })] + vec![TypeName::Some(unsafe { + T::CLASS_NAME.to_str().unwrap_unchecked() + })] } } diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index df6071d..09521a1 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -27,8 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::util::SimpleDrop; -use crate::vm::Vm; use crate::vm::util::LuaType; +use crate::vm::Vm; /// This trait represents a function return value. /// diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index d8518c6..0f7fbf0 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod interface; mod core; +mod interface; pub mod types; pub use interface::*; diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 2e974e2..0428faf 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod closure; pub mod core; -pub mod function; -pub mod value; pub mod error; -pub mod util; -pub mod userdata; -pub mod closure; +pub mod function; +pub mod namespace; pub mod registry; pub mod table; pub mod thread; -pub mod namespace; +pub mod userdata; +pub mod util; +pub mod value; -pub use core::{Vm, RootVm}; +pub use core::{RootVm, Vm}; pub type Result = std::result::Result; diff --git a/core/src/vm/namespace.rs b/core/src/vm/namespace.rs index 9650fd6..9fa2f0f 100644 --- a/core/src/vm/namespace.rs +++ b/core/src/vm/namespace.rs @@ -34,11 +34,15 @@ use crate::vm::Vm; pub struct Namespace<'a> { vm: &'a Vm, - table: Table<'a> + table: Table<'a>, } impl<'a> Namespace<'a> { - fn from_table<'b>(vm: &'a Vm, table: Table<'a>, names: impl Iterator) -> crate::vm::Result { + fn from_table<'b>( + vm: &'a Vm, + table: Table<'a>, + names: impl Iterator, + ) -> crate::vm::Result { let mut key = table.registry_put(vm); let key = vm.scope(|vm| { for name in names { @@ -74,7 +78,10 @@ impl<'a> Namespace<'a> { Self::from_table(vm, table, names) } - pub fn add<'b, T: IntoLua>(&mut self, items: impl IntoIterator) -> crate::vm::Result<()> { + pub fn add<'b, T: IntoLua>( + &mut self, + items: impl IntoIterator, + ) -> crate::vm::Result<()> { for (name, item) in items { self.table.set_field(name, item)?; } diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 791c246..9028b90 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_int; -use std::marker::PhantomData; use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; use crate::vm::registry::RegistryValue; use crate::vm::Vm; +use std::ffi::c_int; +use std::marker::PhantomData; //TODO: Check if key can be a NonZeroI32 @@ -59,9 +59,9 @@ impl RawRegistryKey { /// * `vm`: the [Vm] to attach the produced lua value to. /// /// returns: ::Value - /// + /// /// # Safety - /// + /// /// This is UB to call if the key is invalid or already freed. #[inline(always)] pub unsafe fn push(&self, vm: &Vm) { @@ -75,7 +75,7 @@ impl RawRegistryKey { /// * `vm`: the [Vm] to unregister from. /// /// returns: () - /// + /// /// # Safety /// /// This is UB to call if the registry key is invalid or was already freed. @@ -109,7 +109,7 @@ impl RawRegistryKey { /// returns: RegistryKey /// /// # Safety - /// + /// /// This is UB to call if the stack is empty. #[inline(always)] pub unsafe fn from_top(vm: &Vm) -> RawRegistryKey { @@ -120,7 +120,7 @@ impl RawRegistryKey { pub struct RegistryKey { raw: RawRegistryKey, - useless: PhantomData<*const T> + useless: PhantomData<*const T>, } impl RegistryKey { @@ -176,7 +176,7 @@ impl RegistryKey { pub unsafe fn from_top(vm: &Vm) -> RegistryKey { RegistryKey { raw: RawRegistryKey::from_top(vm), - useless: PhantomData + useless: PhantomData, } } } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 8764f3a..3bf6e99 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -33,16 +33,16 @@ pub trait RegistryValue: 'static { type Value<'a>; /// Reads the upvalue at the given location on the lua stack. - /// + /// /// # Arguments - /// + /// /// * `vm`: the [Vm] to read from. /// * `index`: the index of the value. This index is not guaranteed to be absolute. - /// - /// returns: Self::Value - /// + /// + /// returns: Self::Value + /// /// # Safety - /// + /// /// This function assumes the value at the top of the stack is of type `Self`. This function is /// UB otherwise. unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a>; @@ -53,9 +53,9 @@ pub trait Registry: Sized { type RegistryValue: RegistryValue; /// Register this value into the registry. - /// + /// /// # Arguments - /// + /// /// * `vm`: the [Vm] to attach this value to. /// /// returns: RegistryKey @@ -67,7 +67,11 @@ pub trait Registry: Sized { /// /// * `vm`: the [Vm] to attach this value to. /// * `old`: the old registry key to be replaced. - fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + fn registry_swap( + self, + vm: &Vm, + old: RegistryKey, + ) -> RegistryKey { old.delete(vm); self.registry_put(vm) } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 830aa6f..84fc853 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -54,7 +54,11 @@ impl RegistryValue for LuaFunction { } impl RegistryKey { - pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { + pub fn call<'a, T: IntoLua, R: FromLua<'a>>( + &self, + vm: &'a Vm, + value: T, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.as_raw().push(vm) }; let num_values = value.into_lua(vm); diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index cac8135..6ba2982 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -26,25 +26,31 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer}; +use crate::ffi::lua::{ + lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, + lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer, +}; use crate::util::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_single_into_lua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::{Debug, Display}; pub struct Table<'a> { vm: &'a Vm, - index: i32 + index: i32, } impl Clone for Table<'_> { fn clone(&self) -> Self { unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - Table { vm: self.vm, index: self.vm.top() } + Table { + vm: self.vm, + index: self.vm.top(), + } } } @@ -56,11 +62,15 @@ impl PartialEq for Table<'_> { } } -impl Eq for Table<'_> { } +impl Eq for Table<'_> {} impl Display for Table<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "table@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) + write!( + f, + "table@{:X}", + unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize + ) } } @@ -127,7 +137,11 @@ impl<'a> Table<'a> { self.len() == 0 } - pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>( + &'b self, + name: impl AnyStr, + value: T, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; let num_values = value.into_lua(self.vm); @@ -135,7 +149,11 @@ impl<'a> Table<'a> { R::from_lua(self.vm, -(R::num_values() as i32)) } - pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>(&'b self, name: impl AnyStr, value: T) -> crate::vm::Result { + pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>( + &'b self, + name: impl AnyStr, + value: T, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 7732ac5..10f2363 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -32,10 +32,10 @@ use crate::util::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Registry; -use crate::vm::util::LuaType; -use crate::vm::value::FromLua; use crate::vm::table::Table; +use crate::vm::util::LuaType; use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::value::FromLua; use crate::vm::Vm; unsafe impl<'a> SimpleDrop for Table<'a> {} @@ -48,7 +48,9 @@ impl<'a> FromParam<'a> for Table<'a> { } fn try_from_param(vm: &'a Vm, index: i32) -> Option { - if unsafe { lua_type(vm.as_ptr(), index) } != Type::Table { return None; } + if unsafe { lua_type(vm.as_ptr(), index) } != Type::Table { + return None; + } Some(unsafe { Table::from_raw(vm, index) }) } } @@ -88,7 +90,11 @@ impl Registry for Table<'_> { } #[inline(always)] - fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + fn registry_swap( + self, + vm: &Vm, + old: RegistryKey, + ) -> RegistryKey { // If the table is not at the top of the stack, move it to the top. ensure_value_top(vm, self.index()); unsafe { old.as_raw().replace(vm) }; diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 64d98ff..8a2c840 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -36,14 +36,20 @@ pub struct Iter<'a> { index: i32, has_ended: bool, has_started: bool, - last_top: i32 + last_top: i32, } impl<'a> Iter<'a> { pub(super) fn from_raw(vm: &'a Vm, index: i32) -> Self { // Push a nil value on the stack to allow the iterator to work. unsafe { lua_pushnil(vm.as_ptr()) }; - Self { vm, index, has_ended: false, has_started: false, last_top: vm.top() } + Self { + vm, + index, + has_ended: false, + has_started: false, + last_top: vm.top(), + } } } @@ -54,7 +60,11 @@ impl<'a> Iterator for Iter<'a> { if self.has_started { // This ensures the iterator remains safe. if self.vm.top() != self.last_top { - panic!("Attempt to iterate on moved values (expected Vm top: {}, got: {})", self.last_top, self.vm.top()); + panic!( + "Attempt to iterate on moved values (expected Vm top: {}, got: {})", + self.last_top, + self.vm.top() + ); } // Pop the last value on the stack which corresponds to the last value from lua_next. // Only if the iterator was started. diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 1bbcb7f..8b3c6b2 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -26,34 +26,34 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; -use std::marker::PhantomData; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; use crate::util::SimpleDrop; +use crate::vm::core::LoadString; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::FromParam; use crate::vm::util::LuaType; -use crate::vm::core::LoadString; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::{Debug, Display}; +use std::marker::PhantomData; pub enum State { Yielded, - Finished + Finished, } pub struct Thread<'a> { vm: Vm, - useless: PhantomData<&'a ()> + useless: PhantomData<&'a ()>, } impl Clone for Thread<'_> { fn clone(&self) -> Self { Self { vm: unsafe { Vm::from_raw(self.vm.as_ptr()) }, - useless: PhantomData + useless: PhantomData, } } } @@ -64,11 +64,13 @@ impl PartialEq for Thread<'_> { } } -impl Eq for Thread<'_> { } +impl Eq for Thread<'_> {} impl Display for Thread<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "thread@{:X}", unsafe { std::mem::transmute::<_, usize>(self.vm.as_ptr()) }) + write!(f, "thread@{:X}", unsafe { + std::mem::transmute::<_, usize>(self.vm.as_ptr()) + }) } } @@ -100,11 +102,13 @@ impl<'a> Thread<'a> { // trace produced by luaL_traceback and remove it from the stack. let error_message: &str = FromLua::from_lua(&self.vm, -1)?; unsafe { lua_remove(self.vm.as_ptr(), -1) }; - Err(Error::Runtime(RuntimeError::new(String::from(error_message) + "\n"))) + Err(Error::Runtime(RuntimeError::new( + String::from(error_message) + "\n", + ))) } ThreadStatus::ErrMem => Err(Error::Memory), ThreadStatus::ErrErr => Err(Error::Error), - _ => std::unreachable!() + _ => std::unreachable!(), } } } @@ -114,7 +118,7 @@ impl<'a> FromLua<'a> for Thread<'a> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Thread { vm: Vm::from_raw(lua_tothread(vm.as_ptr(), index)), - useless: PhantomData + useless: PhantomData, } } @@ -122,21 +126,21 @@ impl<'a> FromLua<'a> for Thread<'a> { ensure_type_equals(vm, index, Type::Thread)?; Ok(Thread { vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, - useless: PhantomData + useless: PhantomData, }) } } -unsafe impl SimpleDrop for Thread<'_> { } +unsafe impl SimpleDrop for Thread<'_> {} -impl LuaType for Thread<'_> { } +impl LuaType for Thread<'_> {} impl<'a> FromParam<'a> for Thread<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { luaL_checktype(vm.as_ptr(), index, Type::Thread); Thread { vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, - useless: PhantomData + useless: PhantomData, } } diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index c59879a..e8a12a8 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -26,23 +26,26 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{lua_pushvalue, lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::FromLua; use crate::vm::Vm; +use std::fmt::{Debug, Display}; pub struct AnyUserData<'a> { vm: &'a Vm, - index: i32 + index: i32, } impl Clone for AnyUserData<'_> { fn clone(&self) -> Self { unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - AnyUserData { vm: self.vm, index: self.vm.top() } + AnyUserData { + vm: self.vm, + index: self.vm.top(), + } } } @@ -54,11 +57,15 @@ impl PartialEq for AnyUserData<'_> { } } -impl Eq for AnyUserData<'_> { } +impl Eq for AnyUserData<'_> {} impl Display for AnyUserData<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "userdata@{:X}", unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize) + write!( + f, + "userdata@{:X}", + unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize + ) } } @@ -102,7 +109,9 @@ impl<'a> AnyUserData<'a> { /// created. That is no other references to this underlying userdata value must exist in Rust /// code otherwise using this function is UB. pub unsafe fn get_mut(&mut self) -> crate::vm::Result<&mut T> { - let this_ptr = unsafe { luaL_testudata(self.vm.as_ptr(), self.index, T::CLASS_NAME.as_ptr()) } as *mut T; + let this_ptr = + unsafe { luaL_testudata(self.vm.as_ptr(), self.index, T::CLASS_NAME.as_ptr()) } + as *mut T; if this_ptr.is_null() { return Err(Error::Type(TypeError { expected: Type::Userdata, diff --git a/core/src/vm/userdata/case.rs b/core/src/vm/userdata/case.rs index f5cb5bc..d037135 100644 --- a/core/src/vm/userdata/case.rs +++ b/core/src/vm/userdata/case.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use std::ffi::{CStr, CString}; +use crate::vm::userdata::NameConvert; use bp3d_util::string::BufTools; use itertools::Itertools; -use crate::vm::userdata::NameConvert; +use std::borrow::Cow; +use std::ffi::{CStr, CString}; fn to_string_lossy(bytes: Cow<[u8]>) -> Cow { match bytes { @@ -56,9 +56,16 @@ impl NameConvert for Camel { // Return the same unconverted string if we failed. Err(_) => return Cow::Borrowed(name), }; - let s: String = s.split("_") + let s: String = s + .split("_") .enumerate() - .map(|(i, v)| if i != 0 { v.as_bytes().capitalise_ascii() } else { v.as_bytes().into() }) + .map(|(i, v)| { + if i != 0 { + v.as_bytes().capitalise_ascii() + } else { + v.as_bytes().into() + } + }) .map(to_string_lossy) .join("") .into(); @@ -75,7 +82,8 @@ impl NameConvert for Pascal { // Return the same unconverted string if we failed. Err(_) => return Cow::Borrowed(name), }; - let s: String = s.split("_") + let s: String = s + .split("_") .map(|v| v.as_bytes().capitalise_ascii()) .map(to_string_lossy) .join("") diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index ff781fd..e0840eb 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -26,22 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::OnceCell; -use std::ffi::CStr; -use std::marker::PhantomData; -use bp3d_debug::{debug, warning}; use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; -use crate::ffi::lua::{lua_pushcclosure, lua_pushnil, lua_pushvalue, lua_setfield, lua_setmetatable, lua_settop, CFunction, State}; -use crate::vm::userdata::{AddGcMethod, NameConvert, Error, LuaDrop, UserData}; +use crate::ffi::lua::{ + lua_pushcclosure, lua_pushnil, lua_pushvalue, lua_setfield, lua_setmetatable, lua_settop, + CFunction, State, +}; +use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, NameConvert, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; use crate::vm::Vm; +use bp3d_debug::{debug, warning}; +use std::cell::OnceCell; +use std::ffi::CStr; +use std::marker::PhantomData; pub struct Function { is_mutable: bool, args: Vec, name: &'static CStr, - func: CFunction + func: CFunction, } impl Function { @@ -50,7 +53,7 @@ impl Function { is_mutable: false, args: Vec::new(), name, - func + func, } } @@ -101,7 +104,7 @@ pub struct Registry<'a, T: UserData, C: NameConvert> { vm: &'a Vm, useless: PhantomData, has_gc: OnceCell<()>, - case: C + case: C, } impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { @@ -127,8 +130,14 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { unsafe { lua_settop(vm.as_ptr(), -2) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } - let reg = Registry { vm, useless: PhantomData, has_gc: OnceCell::new(), case }; - reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()).unwrap_unchecked(); + let reg = Registry { + vm, + useless: PhantomData, + has_gc: OnceCell::new(), + case, + }; + reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()) + .unwrap_unchecked(); Ok(reg) } @@ -138,7 +147,9 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { unsafe { lua_settop(self.vm.as_ptr(), -(num as i32) - 1) }; return Err(Error::MultiValueField); } - unsafe { lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); } + unsafe { + lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); + } Ok(()) } diff --git a/core/src/vm/userdata/error.rs b/core/src/vm/userdata/error.rs index c03315e..1b4dcba 100644 --- a/core/src/vm/userdata/error.rs +++ b/core/src/vm/userdata/error.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::CStr; use bp3d_util::simple_error; +use std::ffi::CStr; simple_error! { pub Error { diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 5a38e77..0742464 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::userdata::{core::Registry, Error}; +use crate::vm::Vm; use std::borrow::Cow; use std::ffi::CStr; -use crate::vm::userdata::{Error, core::Registry}; -use crate::vm::Vm; pub trait UserData: Sized { const CLASS_NAME: &'static CStr; diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs index 51ee746..d8f434a 100644 --- a/core/src/vm/userdata/mod.rs +++ b/core/src/vm/userdata/mod.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod interface; -mod error; -pub mod core; mod any; pub mod case; +pub mod core; +mod error; +mod interface; +pub use any::AnyUserData; pub use error::Error; pub use interface::*; -pub use any::AnyUserData; diff --git a/core/src/vm/util.rs b/core/src/vm/util.rs index d22898c..30d80b8 100644 --- a/core/src/vm/util.rs +++ b/core/src/vm/util.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::error::Error; use crate::ffi::lua::{lua_error, lua_pushlstring, State}; +use std::error::Error; #[derive(Debug, PartialEq, Eq)] pub enum TypeName { Some(&'static str), - None + None, } pub trait LuaType { @@ -57,8 +57,8 @@ impl LuaType for Option { /// * `l`: the lua State on which to raise the lua exception. /// * `error`: the Rust error to be converted. /// -/// returns: ! -/// +/// returns: ! +/// /// # Safety /// /// It is UB to call this function outside a lua [CFunction](crate::ffi::lua::CFunction). diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 99df9d7..043a469 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Display; use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; use crate::util::SimpleDrop; use crate::vm::error::Error; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::value::function::LuaFunction; use crate::vm::table::Table; use crate::vm::thread::Thread; use crate::vm::userdata::AnyUserData; use crate::vm::util::{lua_rust_error, LuaType}; +use crate::vm::value::function::LuaFunction; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::Display; #[derive(Debug, PartialEq, Clone)] pub enum AnyValue<'a> { @@ -50,10 +50,10 @@ pub enum AnyValue<'a> { Function(LuaFunction<'a>), Table(Table<'a>), UserData(AnyUserData<'a>), - Thread(Thread<'a>) + Thread(Thread<'a>), } -impl Eq for AnyValue<'_> { } +impl Eq for AnyValue<'_> {} impl Display for AnyValue<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { @@ -67,7 +67,7 @@ impl Display for AnyValue<'_> { AnyValue::Function(v) => write!(f, "{}", v), AnyValue::Table(v) => write!(f, "{}", v), AnyValue::UserData(v) => write!(f, "{}", v), - AnyValue::Thread(v) => write!(f, "{}", v) + AnyValue::Thread(v) => write!(f, "{}", v), } } } @@ -84,7 +84,7 @@ impl AnyValue<'_> { AnyValue::Function(_) => Type::Function, AnyValue::Table(_) => Type::Table, AnyValue::UserData(_) => Type::Userdata, - AnyValue::Thread(_) => Type::Thread + AnyValue::Thread(_) => Type::Thread, } } } @@ -96,7 +96,7 @@ unsafe impl IntoParam for AnyValue<'_> { AnyValue::Nil => { unsafe { lua_pushnil(vm.as_ptr()) }; 1 - }, + } AnyValue::Number(v) => v.into_lua(vm), AnyValue::Boolean(v) => v.into_lua(vm), AnyValue::String(v) => v.into_lua(vm), @@ -104,7 +104,7 @@ unsafe impl IntoParam for AnyValue<'_> { AnyValue::Function(v) => v.into_lua(vm), AnyValue::Table(v) => v.into_lua(vm), AnyValue::UserData(_) => 0, - AnyValue::Thread(_) => 0 + AnyValue::Thread(_) => 0, } } } @@ -133,12 +133,16 @@ impl<'a> FromLua<'a> for AnyValue<'a> { let buffer: &[u8] = unsafe { FromLua::from_lua_unchecked(vm, index) }; match std::str::from_utf8(buffer) { Ok(s) => Ok(AnyValue::String(s)), - Err(_) => Ok(AnyValue::Buffer(buffer)) + Err(_) => Ok(AnyValue::Buffer(buffer)), } } Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), - Type::Function => Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }), - Type::Userdata => Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }), + Type::Function => { + Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }) + } + Type::Userdata => { + Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }) + } Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), } } @@ -146,14 +150,14 @@ impl<'a> FromLua<'a> for AnyValue<'a> { unsafe impl SimpleDrop for AnyValue<'_> {} -impl LuaType for AnyValue<'_> { } +impl LuaType for AnyValue<'_> {} impl<'a> FromParam<'a> for AnyValue<'a> { #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { match FromLua::from_lua(vm, index) { Ok(v) => v, - Err(e) => lua_rust_error(vm.as_ptr(), e) + Err(e) => lua_rust_error(vm.as_ptr(), e), } } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index f1babf8..ec2398a 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -27,13 +27,16 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_tolstring, lua_type, lua_tointeger, lua_tonumber, Type, lua_toboolean, lua_touserdata, lua_settop}; -use crate::vm::function::IntoParam; -use crate::vm::Vm; +use crate::ffi::lua::{ + lua_settop, lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_touserdata, + lua_type, Type, +}; use crate::vm::error::{Error, TypeError}; +use crate::vm::function::IntoParam; use crate::vm::userdata::UserDataImmutable; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; impl<'a> FromLua<'a> for &'a str { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -53,11 +56,11 @@ impl<'a> FromLua<'a> for &'a str { let s = lua_tolstring(l, index, &mut len as _); let slice = std::slice::from_raw_parts(s as _, len); std::str::from_utf8(slice).map_err(Error::InvalidUtf8) - }, + } _ => Err(Error::Type(TypeError { expected: Type::String, - actual: ty - })) + actual: ty, + })), } } } @@ -81,11 +84,11 @@ impl<'a> FromLua<'a> for &'a [u8] { let s = lua_tolstring(l, index, &mut len as _); let slice = std::slice::from_raw_parts(s as *const u8, len); Ok(slice) - }, + } _ => Err(Error::Type(TypeError { expected: Type::String, - actual: ty - })) + actual: ty, + })), } } } @@ -156,7 +159,8 @@ impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - let this_ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + let this_ptr = + unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; if this_ptr.is_null() { return Err(Error::Type(TypeError { expected: Type::Userdata, diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 7c5c10f..c237e4b 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; use crate::util::SimpleDrop; @@ -35,19 +34,23 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Registry; use crate::vm::util::LuaType; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::{Debug, Display}; pub struct LuaFunction<'a> { vm: &'a Vm, - index: i32 + index: i32, } impl Clone for LuaFunction<'_> { fn clone(&self) -> Self { unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - LuaFunction { vm: self.vm, index: self.vm.top() } + LuaFunction { + vm: self.vm, + index: self.vm.top(), + } } } @@ -59,11 +62,15 @@ impl PartialEq for LuaFunction<'_> { } } -impl Eq for LuaFunction<'_> { } +impl Eq for LuaFunction<'_> {} impl Display for LuaFunction<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "function@{:X}", unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize) + write!( + f, + "function@{:X}", + unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize + ) } } @@ -73,9 +80,9 @@ impl Debug for LuaFunction<'_> { } } -unsafe impl SimpleDrop for LuaFunction<'_> { } +unsafe impl SimpleDrop for LuaFunction<'_> {} -impl LuaType for LuaFunction<'_> { } +impl LuaType for LuaFunction<'_> {} impl<'a> FromParam<'a> for LuaFunction<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { @@ -98,7 +105,9 @@ unsafe impl IntoParam for LuaFunction<'_> { impl<'a> LuaFunction<'a> { pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; - unsafe { lua_pushvalue(self.vm.as_ptr(), self.index); } + unsafe { + lua_pushvalue(self.vm.as_ptr(), self.index); + } let num_values = value.into_lua(self.vm); unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) @@ -110,13 +119,16 @@ impl<'a> FromLua<'a> for LuaFunction<'a> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> LuaFunction<'a> { LuaFunction { vm, - index: vm.get_absolute_index(index) + index: vm.get_absolute_index(index), } } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { ensure_type_equals(vm, index, Type::Function)?; - Ok(LuaFunction { vm, index: vm.get_absolute_index(index) }) + Ok(LuaFunction { + vm, + index: vm.get_absolute_index(index), + }) } } @@ -131,7 +143,11 @@ impl Registry for LuaFunction<'_> { } #[inline(always)] - fn registry_swap(self, vm: &Vm, old: RegistryKey) -> RegistryKey { + fn registry_swap( + self, + vm: &Vm, + old: RegistryKey, + ) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. ensure_value_top(vm, self.index); unsafe { old.as_raw().replace(vm) }; diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index a5063aa..4985ac9 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod interface; +pub mod any; mod core; pub mod function; +mod interface; pub mod util; -pub mod any; pub use interface::*; diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index c5f1758..92d2125 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -35,12 +35,13 @@ use crate::vm::Vm; #[inline(always)] pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { let ty = unsafe { crate::ffi::lua::lua_type(vm.as_ptr(), index) }; - if ty == expected { //FIXME: likely branch + if ty == expected { + //FIXME: likely branch Ok(()) } else { Err(Error::Type(TypeError { expected, - actual: ty + actual: ty, })) } } @@ -71,7 +72,7 @@ pub fn ensure_single_into_lua(vm: &Vm, value: impl IntoLua) -> crate::vm::Result let nums = value.into_lua(vm); if nums != 1 { // Clear the stack. - unsafe { lua_settop(vm.as_ptr(), -(nums as i32)-1) }; + unsafe { lua_settop(vm.as_ptr(), -(nums as i32) - 1) }; return Err(Error::MultiValue); } Ok(()) diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index 4036f63..c2066d7 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_util::simple_error; use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::RootVm; +use bp3d_util::simple_error; simple_error! { pub Error { @@ -48,8 +48,10 @@ decl_lib_func! { fn test_vm_backtrace() { let vm = RootVm::new(); let top = vm.top(); - vm.set_global(c"error_func", RFunction::wrap(error_func)).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"error_func", RFunction::wrap(error_func)) + .unwrap(); + vm.run_code::<()>( + c" local function raise() error_func() end @@ -61,7 +63,9 @@ fn test_vm_backtrace() { function main() a() end - ").unwrap(); + ", + ) + .unwrap(); let func: LuaFunction = vm.get_global(c"main").unwrap(); let err = func.call::<()>(()).unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: useless function called"); diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 935227d..01c0fa5 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -34,7 +34,7 @@ use bp3d_lua::vm::RootVm; struct TestContext { value: i32, - value3: Vec + value3: Vec, } decl_closure! { @@ -78,9 +78,7 @@ fn test_vm_fast_closure() { fn test_vm_rust_closure() { let mut vm = RootVm::new(); let top = vm.top(); - let closure = RClosure::from_rust(&mut vm, |val: f32| { - format!("this is a test: {}", val) - }); + let closure = RClosure::from_rust(&mut vm, |val: f32| format!("this is a test: {}", val)); vm.set_global(c"test", closure).unwrap(); assert_eq!(top, vm.top()); let s: &str = vm.run_code(c"return test(42.42)").unwrap(); @@ -94,16 +92,21 @@ fn test_vm_context() { let ctx = ContextMut::new(&vm); { let mut namespace = Namespace::new(&vm, "context").unwrap(); - namespace.add([ - ("push", context_push(ctx)), - ("pop", context_pop(ctx)), - ("set_value", context_set_value(ctx)) - ]).unwrap(); + namespace + .add([ + ("push", context_push(ctx)), + ("pop", context_pop(ctx)), + ("set_value", context_set_value(ctx)), + ]) + .unwrap(); } assert_eq!(top, vm.top()); let res = vm.run_code::<()>(c"context.set_value(42)"); assert!(res.is_err()); - assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(42)\"]:1: Context is not available in this function."); + assert_eq!( + res.unwrap_err().into_runtime().unwrap().msg(), + "[string \"context.set_value(42)\"]:1: Context is not available in this function." + ); let mut obj = TestContext { value: 0, value3: vec![], @@ -115,7 +118,10 @@ fn test_vm_context() { } let res = vm.run_code::<()>(c"context.set_value(84)"); assert!(res.is_err()); - assert_eq!(res.unwrap_err().into_runtime().unwrap().msg(), "[string \"context.set_value(84)\"]:1: Context is not available in this function."); + assert_eq!( + res.unwrap_err().into_runtime().unwrap().msg(), + "[string \"context.set_value(84)\"]:1: Context is not available in this function." + ); assert_eq!(obj.value, 42); { let _obj = cell.bind(&mut obj); diff --git a/core/tests/test_vm_custom_structs.rs b/core/tests/test_vm_custom_structs.rs index 1558aac..e1a167d 100644 --- a/core/tests/test_vm_custom_structs.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -29,8 +29,8 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; -use bp3d_lua_codegen::{FromParam, IntoParam}; use bp3d_lua_codegen::LuaType; +use bp3d_lua_codegen::{FromParam, IntoParam}; #[derive(FromParam, LuaType, IntoParam)] struct Test1<'a>(&'a str, i32); @@ -38,13 +38,13 @@ struct Test1<'a>(&'a str, i32); #[derive(FromParam, LuaType, IntoParam)] struct Test2<'a> { name: &'a str, - value: i32 + value: i32, } #[derive(FromParam, LuaType, IntoParam)] struct TestStatic { value1: f32, - value2: i32 + value2: i32, } decl_lib_func! { @@ -73,30 +73,44 @@ fn basic() { vm.set_global(c"test", RFunction::wrap(test)).unwrap(); vm.set_global(c"test2", RFunction::wrap(test2)).unwrap(); vm.set_global(c"test3", RFunction::wrap(test3)).unwrap(); - let out = vm.run_code::<&str>(c" + let out = vm + .run_code::<&str>( + c" local test1 = { 'value', 42 } local test2 = { name = 'of', value = 64 } local st = { value1 = 42.42, value2 = 32 } return test(test1, test2, st) - ").unwrap(); + ", + ) + .unwrap(); assert_eq!(out, "value of: 42, 64, (v1: 42.42, v2: 32)"); - vm.set_global(c"test", Test2 { - name: "whatever", - value: 42, - }).unwrap(); + vm.set_global( + c"test", + Test2 { + name: "whatever", + value: 42, + }, + ) + .unwrap(); let out = vm.run_code::<&str>(c"return test.name").unwrap(); assert_eq!(out, "whatever"); let out = vm.run_code::(c"return test.value").unwrap(); assert_eq!(out, 42); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local t2 = test2('test') assert(t2.name == 'test') assert(t2.value == 42) - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local t2 = test3('test42', 'test2') assert(t2.name == 'test42') assert(t2.value == 42) - ").unwrap(); + ", + ) + .unwrap(); assert_eq!(top + 3, vm.top()) } diff --git a/core/tests/test_vm_destructor.rs b/core/tests/test_vm_destructor.rs index 7e5d57c..a87ee3c 100644 --- a/core/tests/test_vm_destructor.rs +++ b/core/tests/test_vm_destructor.rs @@ -27,8 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_lib_func; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::RootVm; struct ValueWithDrop; impl ValueWithDrop { @@ -53,16 +53,26 @@ decl_lib_func! { #[test] fn test_vm_destructor() { let mut vm = RootVm::new(); - vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)).unwrap(); + vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)) + .unwrap(); let time = std::time::Instant::now(); let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); let err = res.unwrap_err().into_runtime().unwrap(); - assert_eq!(err.msg(), "rust error: invalid utf-8 sequence of 1 bytes from index 14"); - assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); - let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); + assert_eq!( + err.msg(), + "rust error: invalid utf-8 sequence of 1 bytes from index 14" + ); + assert!(vm + .run_code::<&str>(c"return test_c_function('this is a test', 0.42)") + .is_ok()); + let s = vm + .run_code::<&str>(c"return test_c_function('this is a test', 0.42)") + .unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); - assert!(vm.run_code::(c"return test_c_function('this is a test', 0.42)").is_err()); + assert!(vm + .run_code::(c"return test_c_function('this is a test', 0.42)") + .is_err()); vm.clear(); let time = time.elapsed(); println!("time: {:?}", time); diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index af743bb..113d6f5 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -26,16 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::CStr; -use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::function::LuaFunction; use bp3d_lua::vm::table::Table; +use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::RootVm; +use std::ffi::CStr; #[test] fn test_vm_function_1_arg() { let mut vm = RootVm::new(); let top = vm.top(); - let f: LuaFunction = vm.run_code(c"return function(value) return 'this is a test ' .. value end").unwrap(); + let f: LuaFunction = vm + .run_code(c"return function(value) return 'this is a test ' .. value end") + .unwrap(); let str: &str = f.call(42.42).unwrap(); assert_eq!(str, "this is a test 42.42"); let str: &str = f.call(42).unwrap(); diff --git a/core/tests/test_vm_interrupt.rs b/core/tests/test_vm_interrupt.rs index 7901cbc..204e63d 100644 --- a/core/tests/test_vm_interrupt.rs +++ b/core/tests/test_vm_interrupt.rs @@ -28,22 +28,24 @@ #![cfg(feature = "interrupt")] -use std::time::Duration; use bp3d_lua::libs::Lib; use bp3d_lua::vm::core::interrupt; +use std::time::Duration; #[test] fn test_vm_interrupt() { let (signal, handle) = interrupt::spawn_interruptible(|vm| { bp3d_lua::libs::os::Compat.register(vm).unwrap(); // Run the malicious code. - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local tbl = {} while (true) do local time = os.date('!%x %H:%M:%S') table.insert(tbl, time) end - ") + ", + ) }); // Give the chance to the thread to run and pump a bit of RAM. std::thread::sleep(Duration::from_millis(500)); diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 2af45d9..ec203b3 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::libs::Lib; use bp3d_lua::libs::lua::Lua; use bp3d_lua::libs::util::Util; +use bp3d_lua::libs::Lib; use bp3d_lua::vm::RootVm; #[test] @@ -36,7 +36,8 @@ fn test_vm_lib_lua() { let mut vm = RootVm::new(); let top = vm.top(); Lua::new().build().register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == '1.0.0-rc.1.0.0') assert(#bp3d.lua.patches == 5) @@ -47,10 +48,17 @@ fn test_vm_lib_lua() { assert(func == nil) assert(err == \"syntax error: [string \\\"ret a + 2\\\"]:1: '=' expected near 'a'\") assert(bp3d.lua.runString('return 1 + 1') == 2) - ").unwrap(); - let err = vm.run_code::<()>(c"bp3d.lua.require \"not.existing.file\"").unwrap_err().into_runtime().unwrap(); + ", + ) + .unwrap(); + let err = vm + .run_code::<()>(c"bp3d.lua.require \"not.existing.file\"") + .unwrap_err() + .into_runtime() + .unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local function test() bp3d.lua.require \"not.existing.file\" end @@ -58,7 +66,9 @@ fn test_vm_lib_lua() { assert(not flag) print(err) assert(err ~= '') - ").unwrap(); + ", + ) + .unwrap(); assert_eq!(vm.top(), top); } @@ -67,7 +77,8 @@ fn test_vm_lib_util() { let mut vm = RootVm::new(); let top = vm.top(); Util.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local src = { a = 1, b = 2 @@ -97,8 +108,11 @@ fn test_vm_lib_util() { assert(bp3d.util.table.contains(tbl, 'a: 1')) assert(bp3d.util.table.contains(tbl, 'b: 2')) assert(bp3d.util.table.contains(tbl, 'c: 3')) - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local utf8 = bp3d.util.utf8 assert(utf8.fromString('abc') ~= nil) assert(utf8.count('abc') == 3) @@ -113,18 +127,27 @@ fn test_vm_lib_util() { assert(utf8.charAt('abc', 2) == 0x63) local s = '我是' assert(utf8.sub(s, 1) == '是') - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local tbl = { value = 42 } local protected = bp3d.util.table.protect(tbl) assert(protected.value == 42) - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local tbl = { value = 42 } local protected = bp3d.util.table.protect(tbl) protected.value = 84 - ").unwrap_err(); - vm.run_code::<()>(c" + ", + ) + .unwrap_err(); + vm.run_code::<()>( + c" local src = { value = 42, adding = { a = 1 } } local dst = { value = 42, adding = { } } bp3d.util.table.update(dst, src) @@ -134,8 +157,11 @@ fn test_vm_lib_util() { bp3d.util.table.update(dst2, src) assert(dst2.value == 42) assert(dst2.adding.a == 1) - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local src = { value = 42, adding = { a = 1 } } local dst = bp3d.util.table.copy(src) assert(dst.value == 42) @@ -146,15 +172,20 @@ fn test_vm_lib_util() { assert(src.b == nil) assert(dst.adding.b == 2) assert(src.adding.b == nil) - ").unwrap(); - vm.run_code::<()>(c" + ", + ) + .unwrap(); + vm.run_code::<()>( + c" local list = { 1, 2, 3, 4 } local list2 = { 5, 6, 7, 8 } bp3d.util.table.concat(list, list2) assert(#list == 8) local str = bp3d.util.table.tostring(list) assert(str == '1: 1\\n2: 2\\n3: 3\\n4: 4\\n5: 5\\n6: 6\\n7: 7\\n8: 8') - ").unwrap(); + ", + ) + .unwrap(); assert_eq!(vm.top(), top); } @@ -162,12 +193,16 @@ fn test_vm_lib_util() { fn test_vm_lib_os_time() { let mut vm = RootVm::new(); bp3d_lua::libs::os::Time.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" time = bp3d.os.time.nowLocal() time2 = bp3d.os.time.nowUtc() - ").unwrap(); + ", + ) + .unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local function testDateTime(a, b) local ymd = a:getDate() local ymd2 = b:getDate() @@ -185,38 +220,56 @@ fn test_vm_lib_os_time() { testDateTime(now, time) end testDateTime(now2, time2) - ").unwrap(); + ", + ) + .unwrap(); } #[test] fn test_vm_lib_os_instant() { let mut vm = RootVm::new(); bp3d_lua::libs::os::Instant.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" instant = bp3d.os.instant.now() - ").unwrap(); + ", + ) + .unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local diff = instant:elapsed() assert((diff - 0.5) < 0.1) - ").unwrap(); + ", + ) + .unwrap(); } #[test] fn test_vm_lib_os() { let mut vm = RootVm::new(); bp3d_lua::libs::os::Compat.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" clock = os.clock() - ").unwrap(); + ", + ) + .unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local now = os.clock() assert((clock - now) < 0.1) - ").unwrap(); - let s = vm.run_code::<&str>(c" + ", + ) + .unwrap(); + let s = vm + .run_code::<&str>( + c" return os.date('!%H:%M:%S') - ").unwrap(); + ", + ) + .unwrap(); assert!(s.contains(":")); assert!(!s.contains("[")); assert!(!s.contains("]")); diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 958f9d1..123422b 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Write; use bp3d_lua::ffi::lua::{State, ThreadStatus}; use bp3d_lua::vm::core::load::{load_custom, Code, Script}; -use bp3d_lua::vm::core::Load; use bp3d_lua::vm::core::util::ChunkNameBuilder; +use bp3d_lua::vm::core::Load; use bp3d_lua::vm::{RootVm, Vm}; +use std::fmt::Write; struct BrokenReader; @@ -62,15 +62,36 @@ fn run_assert_err(vm: &Vm, obj: impl Load, err_msg: &str) { fn test_vm_run() { let vm = RootVm::new(); let top = vm.top(); - run_assert_err(&vm, Code::new("test", b"return 1 + b"), "test:1: attempt to perform arithmetic on global 'b' (a nil value)"); - run_assert_err(&vm, c"return 1 + b", "[string \"return 1 + b\"]:1: attempt to perform arithmetic on global 'b' (a nil value)"); + run_assert_err( + &vm, + Code::new("test", b"return 1 + b"), + "test:1: attempt to perform arithmetic on global 'b' (a nil value)", + ); + run_assert_err( + &vm, + c"return 1 + b", + "[string \"return 1 + b\"]:1: attempt to perform arithmetic on global 'b' (a nil value)", + ); run_assert_err(&vm, Code::new("this is an amazingly long text which should get truncated我", b"return 1 + b"), "this is an amazingly long text which should get truncated:1: attempt to perform arithmetic on global 'b' (a nil value)"); let err = vm.run::<()>(BrokenReader).unwrap_err(); - assert_eq!(err.to_string(), "loader error: rust error: error in error handler"); - run_assert_err(&vm, Script::from_path("./tests/lua/basic.lua").unwrap(), "basic.lua:2: nope"); - let err = vm.run::<()>(Script::from_path("./tests/lua/broken.lua").unwrap()).unwrap_err(); - assert_eq!(err.to_string(), "syntax error: broken.lua:2: '(' expected near 'end'"); - vm.run::<()>(Script::from_path("./tests/lua/class.lua").unwrap()).unwrap(); + assert_eq!( + err.to_string(), + "loader error: rust error: error in error handler" + ); + run_assert_err( + &vm, + Script::from_path("./tests/lua/basic.lua").unwrap(), + "basic.lua:2: nope", + ); + let err = vm + .run::<()>(Script::from_path("./tests/lua/broken.lua").unwrap()) + .unwrap_err(); + assert_eq!( + err.to_string(), + "syntax error: broken.lua:2: '(' expected near 'end'" + ); + vm.run::<()>(Script::from_path("./tests/lua/class.lua").unwrap()) + .unwrap(); let func = vm.load_code(c"return 1 + b").unwrap(); func.call::<()>(()).unwrap_err(); assert_eq!(vm.top(), top + 1); diff --git a/core/tests/test_vm_run_string.rs b/core/tests/test_vm_run_string.rs index b48b3c4..61e434c 100644 --- a/core/tests/test_vm_run_string.rs +++ b/core/tests/test_vm_run_string.rs @@ -27,8 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_lib_func; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::RootVm; decl_lib_func! { fn dostring(vm: &Vm, code: &str) -> bp3d_lua::vm::Result<()> { @@ -44,9 +44,12 @@ fn test_run_string() { let res = vm.run_code::<()>(c"dostring('test')"); assert!(res.is_err()); assert_eq!(res.unwrap_err().to_string(), "runtime error: [string \"dostring('test')\"]:1: attempt to call global 'dostring' (a nil value)"); - vm.set_global(c"dostring", RFunction::wrap(dostring)).unwrap(); + vm.set_global(c"dostring", RFunction::wrap(dostring)) + .unwrap(); assert!(vm.run_code::<()>(c"dostring('test')").is_err()); - assert!(vm.run_code::<()>(c"dostring('print(\"whatever 123\")')").is_ok()); + assert!(vm + .run_code::<()>(c"dostring('print(\"whatever 123\")')") + .is_ok()); assert!(vm.run_code::<()>(c"dostring('root = 42')").is_ok()); let val: u32 = vm.get_global("root").unwrap(); assert_eq!(val, 42); diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index 07d8edf..a97c915 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::table::Table; +use bp3d_lua::vm::RootVm; #[test] fn tables() { @@ -44,7 +44,8 @@ fn tables() { tbl.set_field(c"sub", new_table)?; assert_eq!(tbl.len(), 3); vm.set_global(c"myTable", tbl) - }).unwrap(); + }) + .unwrap(); let new_top = vm.top(); assert_eq!(top, new_top); let v = vm.run_code::(c"return myTable.c"); @@ -74,6 +75,7 @@ fn tables() { } assert_eq!(v, "My great string"); Ok(()) - }).unwrap(); + }) + .unwrap(); assert_eq!(vm.top(), new_top); } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 7606183..d409bfa 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::sync::Mutex; -use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use bp3d_lua::ffi::lua::Number; -use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::LuaDrop; +use bp3d_lua::vm::{RootVm, Vm}; +use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; +use std::sync::Mutex; static MUTEX: Mutex<()> = Mutex::new(()); @@ -145,17 +145,24 @@ fn test_vm_userdata_forgot_reg() { fn test_vm_userdata_error_handling() { let vm = RootVm::new(); let top = vm.top(); - vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake) + .unwrap(); assert_eq!(top, vm.top()); let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); - assert_eq!(msg, "userdata: violation of the unique type rule for mutable method \"replace\""); + assert_eq!( + msg, + "userdata: violation of the unique type rule for mutable method \"replace\"" + ); assert_eq!(top, vm.top()); let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); - assert_eq!(msg, "userdata: too strict alignment required (16 bytes), max is 8 bytes"); + assert_eq!( + msg, + "userdata: too strict alignment required (16 bytes), max is 8 bytes" + ); assert_eq!(top, vm.top()); let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); @@ -165,7 +172,10 @@ fn test_vm_userdata_error_handling() { let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); - assert_eq!(msg, "userdata: class name \"MyInt\" has already been registered"); + assert_eq!( + msg, + "userdata: class name \"MyInt\" has already been registered" + ); assert_eq!(top, vm.top()); let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); @@ -180,7 +190,8 @@ fn test_vm_userdata_base(vm: &Vm) { LUA_DROP_COUNTER = 0; } let top = vm.top(); - vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake) + .unwrap(); assert_eq!(top, vm.top()); vm.set_global(c"MyInt", RFunction::wrap(my_int)).unwrap(); assert_eq!(top, vm.top()); @@ -192,9 +203,18 @@ fn test_vm_userdata_base(vm: &Vm) { assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); - assert_eq!(vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), "579"); - assert_eq!(vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0); - assert_eq!(vm.run_code::(c"return a.tonumber(b)").unwrap(), 456.0); + assert_eq!( + vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), + "579" + ); + assert_eq!( + vm.run_code::(c"return (a + b):tonumber()").unwrap(), + 579.0 + ); + assert_eq!( + vm.run_code::(c"return a.tonumber(b)").unwrap(), + 456.0 + ); assert_eq!(top + 8, vm.top()); } @@ -217,7 +237,8 @@ fn test_vm_userdata_security1() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c"getmetatable(a).__gc = function() print(\"Lua has hacked Rust\") end").unwrap_err(); + vm.run_code::<()>(c"getmetatable(a).__gc = function() print(\"Lua has hacked Rust\") end") + .unwrap_err(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); @@ -229,7 +250,8 @@ fn test_vm_userdata_security2() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c"a.__gc = function() print(\"Lua has hacked Rust\") end").unwrap_err(); + vm.run_code::<()>(c"a.__gc = function() print(\"Lua has hacked Rust\") end") + .unwrap_err(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); @@ -253,12 +275,15 @@ fn test_vm_userdata_security4() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local func = a.tonumber local tbl = {} tbl.tonumber = func tbl:tonumber() - ").unwrap_err(); + ", + ) + .unwrap_err(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); @@ -270,9 +295,12 @@ fn test_vm_userdata_security5() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" rawset(a, '__gc', nil) - ").unwrap_err(); + ", + ) + .unwrap_err(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); diff --git a/testbin/src/context.rs b/testbin/src/context.rs index d6d7f97..6955304 100644 --- a/testbin/src/context.rs +++ b/testbin/src/context.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::time::Duration; -use mlua::{Lua, UserDataMethods}; use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::RootVm; +use mlua::{Lua, UserDataMethods}; +use std::time::Duration; struct TestContext { - value3: Vec + value3: Vec, } decl_closure! { @@ -57,13 +57,10 @@ pub fn test_context_mlua() -> Duration { this.value3.push(val); Ok(()) }); - reg.add_method_mut("pop", |_, this, _: ()| { - Ok(this.value3.pop()) - }); - }).unwrap(); - let mut ctx = TestContext { - value3: Vec::new() - }; + reg.add_method_mut("pop", |_, this, _: ()| Ok(this.value3.pop())); + }) + .unwrap(); + let mut ctx = TestContext { value3: Vec::new() }; let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { lua.scope(|l| { @@ -74,7 +71,8 @@ pub fn test_context_mlua() -> Duration { lua.load("ctx:push(2)").eval::<()>().unwrap(); lua.load("ctx:push(3)").eval::<()>().unwrap(); Ok(()) - }).unwrap(); + }) + .unwrap(); lua.scope(|l| { let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); lua.globals().set("ctx", ud).unwrap(); @@ -84,7 +82,8 @@ pub fn test_context_mlua() -> Duration { lua.load("assert(ctx:pop() == nil)").eval::<()>().unwrap(); lua.load("assert(ctx:pop() == nil)").eval::<()>().unwrap(); Ok(()) - }).unwrap(); + }) + .unwrap(); } let time = time.elapsed(); time @@ -95,9 +94,7 @@ pub fn test_context_vm() -> Duration { let ctx = ContextMut::new(&vm); vm.set_global(c"context_push", context_push(ctx)).unwrap(); vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); - let mut obj = TestContext { - value3: vec![], - }; + let mut obj = TestContext { value3: vec![] }; let mut ctx = CellMut::new(ctx); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index de67dd5..e6ccf3b 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::time::Duration; -use mlua::{Function, Lua, UserDataMethods}; use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::RootVm; +use mlua::{Function, Lua, UserDataMethods}; +use std::time::Duration; struct TestContext { - value3: Vec + value3: Vec, } decl_closure! { @@ -58,11 +58,11 @@ pub fn test_context_mlua() -> Duration { this.value3.push(val); Ok(()) }); - reg.add_method_mut("pop", |_, this, _: ()| { - Ok(this.value3.pop()) - }); - }).unwrap(); - lua.load(" + reg.add_method_mut("pop", |_, this, _: ()| Ok(this.value3.pop())); + }) + .unwrap(); + lua.load( + " function part1(ctx) assert(ctx:pop() == nil) ctx:push(1) @@ -76,24 +76,27 @@ pub fn test_context_mlua() -> Duration { assert(ctx:pop() == nil) assert(ctx:pop() == nil) end - ").eval::<()>().unwrap(); + ", + ) + .eval::<()>() + .unwrap(); let part1: Function = lua.globals().get("part1").unwrap(); let part2: Function = lua.globals().get("part2").unwrap(); - let mut ctx = TestContext { - value3: Vec::new() - }; + let mut ctx = TestContext { value3: Vec::new() }; let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { lua.scope(|l| { let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); part1.call::<()>(ud).unwrap(); Ok(()) - }).unwrap(); + }) + .unwrap(); lua.scope(|l| { let ud = l.create_any_userdata_ref_mut(&mut ctx).unwrap(); part2.call::<()>(ud).unwrap(); Ok(()) - }).unwrap(); + }) + .unwrap(); } let time = time.elapsed(); time @@ -104,7 +107,8 @@ pub fn test_context_vm() -> Duration { let ctx = ContextMut::new(&vm); vm.set_global(c"context_push", context_push(ctx)).unwrap(); vm.set_global(c"context_pop", context_pop(ctx)).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" function part1() assert(context_pop() == nil) context_push(1) @@ -118,12 +122,12 @@ pub fn test_context_vm() -> Duration { assert(context_pop() == nil) assert(context_pop() == nil) end - ").unwrap(); + ", + ) + .unwrap(); let part1: LuaFunction = vm.get_global("part1").unwrap(); let part2: LuaFunction = vm.get_global("part2").unwrap(); - let mut obj = TestContext { - value3: vec![], - }; + let mut obj = TestContext { value3: vec![] }; let mut ctx = CellMut::new(ctx); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 2c577fe..3a62048 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -29,20 +29,18 @@ mod context; mod context_opt; -use std::time::Duration; -use mlua::Lua; use bp3d_lua::decl_lib_func; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::RootVm; +use mlua::Lua; +use std::time::Duration; struct ValueWithDrop; impl ValueWithDrop { - pub fn print(&self) { - } + pub fn print(&self) {} } impl Drop for ValueWithDrop { - fn drop(&mut self) { - } + fn drop(&mut self) {} } decl_lib_func! { @@ -55,13 +53,18 @@ decl_lib_func! { fn test_vm_destructor() -> Duration { let mut vm = RootVm::new(); - vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)).unwrap(); + vm.set_global(c"test_c_function", RFunction::wrap(test_c_function)) + .unwrap(); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { let res = vm.run_code::<&str>(c"return test_c_function('this is a test\\xFF', 0.42)"); assert!(res.is_err()); - assert!(vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").is_ok()); - let s = vm.run_code::<&str>(c"return test_c_function('this is a test', 0.42)").unwrap(); + assert!(vm + .run_code::<&str>(c"return test_c_function('this is a test', 0.42)") + .is_ok()); + let s = vm + .run_code::<&str>(c"return test_c_function('this is a test', 0.42)") + .unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); vm.clear(); } @@ -71,18 +74,28 @@ fn test_vm_destructor() -> Duration { fn test_vm_mlua() -> Duration { let lua = Lua::new(); - let f = lua.create_function(|_, (name, value): (String, f64)| { - let drop = ValueWithDrop; - drop.print(); - Ok(format!("Hello {} ({})", name, value)) - }).unwrap(); + let f = lua + .create_function(|_, (name, value): (String, f64)| { + let drop = ValueWithDrop; + drop.print(); + Ok(format!("Hello {} ({})", name, value)) + }) + .unwrap(); lua.globals().set("test_c_function", f).unwrap(); let time = bp3d_os::time::Instant::now(); for _ in 0..20000 { - let res: mlua::Result = lua.load("return test_c_function('this is a test\\xFF', 0.42)").call(()); + let res: mlua::Result = lua + .load("return test_c_function('this is a test\\xFF', 0.42)") + .call(()); assert!(res.is_err()); - assert!(lua.load("return test_c_function('this is a test', 0.42)").call::(()).is_ok()); - let s: String = lua.load("return test_c_function('this is a test', 0.42)").call(()).unwrap(); + assert!(lua + .load("return test_c_function('this is a test', 0.42)") + .call::(()) + .is_ok()); + let s: String = lua + .load("return test_c_function('this is a test', 0.42)") + .call(()) + .unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); } let time = time.elapsed(); @@ -127,5 +140,8 @@ fn main() { println!("average tools.lua (context_opt): {:?}", ctx_lua_opt); println!("average mlua (context_opt): {:?}", ctx_mlua_opt); assert!(ctx_lua_opt < ctx_mlua_opt); - println!("average diff (context_opt): {:?}", ctx_mlua_opt - ctx_lua_opt); + println!( + "average diff (context_opt): {:?}", + ctx_mlua_opt - ctx_lua_opt + ); } From 7339cf9d9adf78c761b0efc60654b0ac5628447d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 22:48:06 +0200 Subject: [PATCH 254/527] Fixed most clippy warnnings --- build/src/info.rs | 8 ++++---- build/src/patch.rs | 6 +++--- core/build.rs | 2 +- core/src/libs/lua/base.rs | 2 +- core/src/libs/lua/load.rs | 2 +- core/src/libs/lua/options.rs | 6 ++---- core/src/libs/util/string.rs | 2 +- core/src/util.rs | 5 +++++ core/src/vm/closure/context.rs | 19 ++++++++----------- core/src/vm/closure/rc.rs | 2 +- core/src/vm/core/destructor.rs | 4 ++-- core/src/vm/core/load.rs | 2 +- core/src/vm/core/util.rs | 5 ++--- core/src/vm/core/vm.rs | 9 ++++++++- core/src/vm/registry/interface.rs | 2 +- core/src/vm/registry/types.rs | 4 ++-- core/src/vm/table/interface.rs | 2 +- core/src/vm/thread.rs | 3 ++- core/src/vm/userdata/case.rs | 8 +++----- core/src/vm/userdata/core.rs | 8 ++++---- core/src/vm/userdata/interface.rs | 8 ++++++++ core/src/vm/value/core.rs | 1 - core/src/vm/value/function.rs | 2 +- core/src/vm/value/interface.rs | 3 +++ 24 files changed, 65 insertions(+), 50 deletions(-) diff --git a/build/src/info.rs b/build/src/info.rs index 113b27d..f3c3ee3 100644 --- a/build/src/info.rs +++ b/build/src/info.rs @@ -48,7 +48,7 @@ const VERSION: &str = "version = \""; impl<'a> BuildInfo<'a> { pub fn new(base: BuildInfoBase<'a>) -> std::io::Result { - let manifest = std::fs::read_to_string(&base.manifest)?; + let manifest = std::fs::read_to_string(base.manifest)?; let target_dir = base.build_dir.join("../../../.."); let start = manifest .find(VERSION) @@ -65,7 +65,7 @@ impl<'a> BuildInfo<'a> { } pub fn build_dir(&self) -> &Path { - &self.base.build_dir + self.base.build_dir } pub fn dynamic(&self) -> bool { @@ -73,11 +73,11 @@ impl<'a> BuildInfo<'a> { } pub fn target(&self) -> Target { - Target::get(&self.base.target_name) + Target::get(self.base.target_name) } pub fn target_name(&self) -> &str { - &self.base.target_name + self.base.target_name } pub fn target_dir(&self) -> &Path { diff --git a/build/src/patch.rs b/build/src/patch.rs index f98348c..84f9b68 100644 --- a/build/src/patch.rs +++ b/build/src/patch.rs @@ -44,7 +44,7 @@ impl Patch { let src_path = bp3d_os::fs::get_absolute_path(luajit_src)?; CommandRunner::new("failed to revert").run( Command::new("git") - .args(&["checkout", "."]) + .args(["checkout", "."]) .current_dir(&src_path), )?; Ok(Patch { @@ -57,7 +57,7 @@ impl Patch { pub fn apply(&mut self, name: &str) -> std::io::Result<()> { CommandRunner::new("failed to patch").run( Command::new("git") - .args(&[ + .args([ OsStr::new("apply"), self.patch_dir.join(format!("{}.patch", name)).as_os_str(), ]) @@ -95,7 +95,7 @@ impl Drop for Patch { CommandRunner::new("failed to revert") .run( Command::new("git") - .args(&["checkout", "."]) + .args(["checkout", "."]) .current_dir(&self.src_path), ) .unwrap(); diff --git a/core/build.rs b/core/build.rs index 02519ea..0b830ed 100644 --- a/core/build.rs +++ b/core/build.rs @@ -49,7 +49,7 @@ fn apply_patches(out_path: &Path) -> std::io::Result> { &Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"), )? - .apply_all(PATCH_LIST.iter().map(|v| *v), out_path) + .apply_all(PATCH_LIST.iter().copied(), out_path) } fn run_build(build_dir: &Path) -> std::io::Result { diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index fd988cf..aaefd09 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -46,7 +46,7 @@ impl Lib for Base { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([("name", "bp3d-lua"), ("version", env!("CARGO_PKG_VERSION"))])?; let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); - for (i, name) in PATCH_LIST.into_iter().enumerate() { + for (i, name) in PATCH_LIST.iter().enumerate() { // Lua indices starts at 1 not 0. patches.seti((i + 1) as _, *name)?; } diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 622bebf..d7522c6 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -75,7 +75,7 @@ fn parse_lua_path(chroot: &Path, path: &str) -> Result { let mut cur_path = Vec::new(); for component in iter { if component == ".." { - if let None = cur_path.pop() { + if cur_path.pop().is_none() { return Err(Error::EscapeChroot(path.into())); } } else if component != "." { diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index 3c48538..cd7446f 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -33,6 +33,7 @@ use crate::libs::lua::require::{Provider, Require}; use crate::libs::Lib; use std::path::Path; +#[derive(Default)] pub struct Lua<'a> { pub(super) load_chroot_path: Option<&'a Path>, pub(super) provider: Option>, @@ -40,10 +41,7 @@ pub struct Lua<'a> { impl<'a> Lua<'a> { pub fn new() -> Self { - Self { - load_chroot_path: None, - provider: None, - } + Self::default() } pub fn load_chroot_path(mut self, path: &'a Path) -> Self { diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 9e759d5..715c2c9 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -36,7 +36,7 @@ use std::borrow::Cow; decl_lib_func! { fn contains(src: &[u8], needle: &[u8]) -> bool { - if needle.len() == 0 { + if needle.is_empty() { return true; } src.windows(needle.len()).any(|window| window == needle) diff --git a/core/src/util.rs b/core/src/util.rs index 350a8e2..ba76ecd 100644 --- a/core/src/util.rs +++ b/core/src/util.rs @@ -51,6 +51,11 @@ impl AnyStr for &CStr { } } +/// Represents a type which can be trivially dropped (i.e. no Drop implementation). +/// +/// # Safety +/// +/// This is UB to implement this trait on types which are not trivially dropped. pub unsafe trait SimpleDrop {} unsafe impl SimpleDrop for *mut T {} diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 0948946..5b99795 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -80,10 +80,7 @@ pub struct Context { impl Clone for Context { fn clone(&self) -> Self { - Self { - key: self.key, - ptr: self.ptr, - } + *self } } @@ -93,7 +90,7 @@ pub struct ContextMut(Context); impl Clone for ContextMut { fn clone(&self) -> Self { - Self(self.0) + *self } } @@ -124,7 +121,7 @@ pub struct Guard<'a, T> { useless: PhantomData<&'a T>, } -impl<'a, T> Drop for Guard<'a, T> { +impl Drop for Guard<'_, T> { #[inline(always)] fn drop(&mut self) { unsafe { @@ -139,7 +136,7 @@ pub struct Ref<'a, T>(&'a T); #[repr(transparent)] pub struct Mut<'a, T>(&'a mut T); -impl<'a, T: 'static> Deref for Ref<'a, T> { +impl Deref for Ref<'_, T> { type Target = T; #[inline(always)] @@ -148,7 +145,7 @@ impl<'a, T: 'static> Deref for Ref<'a, T> { } } -impl<'a, T: 'static> Deref for Mut<'a, T> { +impl Deref for Mut<'_, T> { type Target = T; #[inline(always)] @@ -157,15 +154,15 @@ impl<'a, T: 'static> Deref for Mut<'a, T> { } } -impl<'a, T: 'static> DerefMut for Mut<'a, T> { +impl DerefMut for Mut<'_, T> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { self.0 } } -unsafe impl<'a, T: 'static> SimpleDrop for Ref<'a, T> {} -unsafe impl<'a, T: 'static> SimpleDrop for Mut<'a, T> {} +unsafe impl SimpleDrop for Ref<'_, T> {} +unsafe impl SimpleDrop for Mut<'_, T> {} impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 273a32d..ea8529f 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -39,7 +39,7 @@ pub struct Ref<'a, T>(&'a T); unsafe impl SimpleDrop for Ref<'_, T> {} -impl<'a, T> Deref for Ref<'a, T> { +impl Deref for Ref<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 0bbac7e..8b2ba53 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -88,7 +88,7 @@ impl Pool { /// # Safety /// /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. - pub unsafe fn new_in_vm<'a>(vm: &'a mut Vm) { + pub unsafe fn new_in_vm(vm: &mut Vm) { let l = vm.as_ptr(); let b = Box::leak(Box::new(Pool::new())); unsafe { @@ -146,7 +146,7 @@ impl Drop for Pool { { num = self.leaked.len() as u32 }, "Deleting leaked pointers..." ); - let v = std::mem::replace(&mut self.leaked, Vec::new()); + let v = std::mem::take(&mut self.leaked); for f in v { f() } diff --git a/core/src/vm/core/load.rs b/core/src/vm/core/load.rs index af2f6b9..5365c4c 100644 --- a/core/src/vm/core/load.rs +++ b/core/src/vm/core/load.rs @@ -109,7 +109,7 @@ pub unsafe fn load_custom( ) -> *const c_char { let obj = ud as *mut T; unsafe { - let res = (&mut *obj).read_data(); + let res = (*obj).read_data(); match res { Err(e) => { lua_rust_error(l, e); diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index 67573a0..54ffb31 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -171,15 +171,14 @@ pub unsafe fn handle_syntax_error(vm: &Vm, res: ThreadStatus) -> crate::vm::Resu } } +#[derive(Default)] pub struct ChunkNameBuilder { inner: FixedBufStr<59>, } impl ChunkNameBuilder { pub fn new() -> Self { - Self { - inner: FixedBufStr::new(), - } + Self::default() } pub fn build(self) -> ChunkName { diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 9ae4d41..21b69c1 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -177,13 +177,20 @@ impl Vm { } thread_local! { - static HAS_VM: Cell = Cell::new(false); + // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! + static HAS_VM: Cell = const { Cell::new(false) }; } pub struct RootVm { vm: Vm, } +impl Default for RootVm { + fn default() -> Self { + Self::new() + } +} + impl RootVm { pub fn new() -> RootVm { if HAS_VM.get() { diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 3bf6e99..de01254 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -45,7 +45,7 @@ pub trait RegistryValue: 'static { /// /// This function assumes the value at the top of the stack is of type `Self`. This function is /// UB otherwise. - unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a>; + unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_>; } /// A trait to produce registry values safely. diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 84fc853..680a94f 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -39,7 +39,7 @@ impl RegistryValue for Table { type Value<'a> = crate::vm::table::Table<'a>; #[inline(always)] - unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { unsafe { crate::vm::table::Table::from_lua_unchecked(vm, index) } } } @@ -48,7 +48,7 @@ impl RegistryValue for LuaFunction { type Value<'a> = crate::vm::value::function::LuaFunction<'a>; #[inline(always)] - unsafe fn to_lua_value<'a>(vm: &'a Vm, index: i32) -> Self::Value<'a> { + unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { unsafe { crate::vm::value::function::LuaFunction::from_lua_unchecked(vm, index) } } } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 10f2363..4eac4d6 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -38,7 +38,7 @@ use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; use crate::vm::value::FromLua; use crate::vm::Vm; -unsafe impl<'a> SimpleDrop for Table<'a> {} +unsafe impl SimpleDrop for Table<'_> {} impl<'a> FromParam<'a> for Table<'a> { #[inline(always)] diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 8b3c6b2..9a48625 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -67,6 +67,7 @@ impl PartialEq for Thread<'_> { impl Eq for Thread<'_> {} impl Display for Thread<'_> { + #[allow(clippy::missing_transmute_annotations)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "thread@{:X}", unsafe { std::mem::transmute::<_, usize>(self.vm.as_ptr()) @@ -80,7 +81,7 @@ impl Debug for Thread<'_> { } } -impl<'a> Thread<'a> { +impl Thread<'_> { #[inline(always)] pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadString) -> crate::vm::Result { self.vm.run_code(code) diff --git a/core/src/vm/userdata/case.rs b/core/src/vm/userdata/case.rs index d037135..ee4caf5 100644 --- a/core/src/vm/userdata/case.rs +++ b/core/src/vm/userdata/case.rs @@ -35,7 +35,7 @@ use std::ffi::{CStr, CString}; fn to_string_lossy(bytes: Cow<[u8]>) -> Cow { match bytes { Cow::Borrowed(v) => String::from_utf8_lossy(v), - Cow::Owned(v) => String::from(&*String::from_utf8_lossy(&*v)).into(), + Cow::Owned(v) => String::from(&*String::from_utf8_lossy(&v)).into(), } } @@ -67,8 +67,7 @@ impl NameConvert for Camel { } }) .map(to_string_lossy) - .join("") - .into(); + .join(""); CString::new(s).unwrap().into() } } @@ -86,8 +85,7 @@ impl NameConvert for Pascal { .split("_") .map(|v| v.as_bytes().capitalise_ascii()) .map(to_string_lossy) - .join("") - .into(); + .join(""); CString::new(s).unwrap().into() } } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index e0840eb..a9a8c90 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -182,14 +182,14 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { } } -impl<'a, T: UserData + LuaDrop, C: NameConvert> Registry<'a, T, C> { +impl Registry<'_, T, C> { pub fn add_gc_method_with_lua_drop(&self) { extern "C-unwind" fn run_lua_drop(l: State) -> i32 { unsafe { let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; lua_pushnil(l); lua_setmetatable(l, 1); - (&*udata).lua_drop(&Vm::from_raw(l)); + (*udata).lua_drop(&Vm::from_raw(l)); } 0 } @@ -198,7 +198,7 @@ impl<'a, T: UserData + LuaDrop, C: NameConvert> Registry<'a, T, C> { let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; lua_pushnil(l); lua_setmetatable(l, 1); - (&*udata).lua_drop(&Vm::from_raw(l)); + (*udata).lua_drop(&Vm::from_raw(l)); std::ptr::drop_in_place(udata); } 0 @@ -234,7 +234,7 @@ impl AddGcMethod for &AddGcMethodAuto { } } -impl<'a, T: UserData, C: NameConvert> Drop for Registry<'a, T, C> { +impl Drop for Registry<'_, T, C> { fn drop(&mut self) { if std::mem::needs_drop::() && self.has_gc.get().is_none() { warning!("No __gc method registered on a drop userdata type!"); diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 0742464..49dc135 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -31,12 +31,20 @@ use crate::vm::Vm; use std::borrow::Cow; use std::ffi::CStr; +/// This trait represents all types of UserData. An UserData is a type with a maximum alignment of 8 +/// with its memory tied to the Lua GC. pub trait UserData: Sized { const CLASS_NAME: &'static CStr; fn register(registry: &Registry) -> Result<(), Error>; } +/// This trait represents an UserData which is never borrowed mutably (excluding interior mutability +/// patterns). +/// +/// # Safety +/// +/// This is UB to implement on UserData types which may be borrowed mutably. pub unsafe trait UserDataImmutable: UserData {} pub trait LuaDrop { diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index ec2398a..ccf2107 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -138,7 +138,6 @@ unsafe impl IntoLua for T { impl FromLua<'_> for () { #[inline(always)] unsafe fn from_lua_unchecked(_: &'_ Vm, _: i32) -> Self { - () } #[inline(always)] diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index c237e4b..74456ce 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -102,7 +102,7 @@ unsafe impl IntoParam for LuaFunction<'_> { } } -impl<'a> LuaFunction<'a> { +impl LuaFunction<'_> { pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 51ef640..140f273 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -28,6 +28,9 @@ use crate::vm::Vm; +// WTF this is broken, if you do not indent then it becomes unreadable. I chose readability over +// un-readability. +#[allow(clippy::doc_overindented_list_items)] pub trait FromLua<'a>: Sized { /// Reads the value at the specified index in the given [Vm]. /// From 4ee216c9dc034f359eba6077eda39c94ac6b1e4d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 22:49:45 +0200 Subject: [PATCH 255/527] Removed SIGNATURE to avoid clippy warning --- core/src/ffi/lua.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 0544e39..64839e5 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -28,9 +28,6 @@ use std::ffi::{c_char, c_double, c_int, c_void}; -/* mark for precompiled code (`Lua') */ -pub const SIGNATURE: &[u8] = b"\033Lua"; - /// The maximum size of a lua chunkname. This is used by Vm::run_named_code for optimization. pub const IDSIZE: usize = 60; From e2943cf3e4eb78f60606a08fa73424872244e18f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 22:53:34 +0200 Subject: [PATCH 256/527] Added Safety section to Vm::from_raw --- core/src/vm/core/vm.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 21b69c1..ff8dbe5 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -50,6 +50,17 @@ pub struct Vm { } impl Vm { + /// Constructs a [Vm] by wrapping an existing lua [State]. + /// + /// # Arguments + /// + /// * `l`: the lua [State] to wrap. + /// + /// # Safety + /// + /// The given lua [State] must have been created from a [RootVm]. It is considered UB to wrap + /// a lua VM allocated outside bp3d-lua. It is also considered UB to wrap a VM which has been + /// allocated with a different set of patches. #[inline(always)] pub unsafe fn from_raw(l: State) -> Self { Self { l } From 9f30a771834d6566761be9ed7a2fe5006e704833 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 22:56:27 +0200 Subject: [PATCH 257/527] Use Option instead of Hook to remove the need for one transmute --- core/src/ffi/lua.rs | 2 +- core/src/vm/core/interrupt/unix.rs | 16 +++------------- 2 files changed, 4 insertions(+), 14 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 64839e5..35cfd29 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -240,5 +240,5 @@ extern "C" { pub fn lua_next(l: State, idx: c_int) -> c_int; pub fn lua_concat(l: State, n: c_int); - pub fn lua_sethook(l: State, hook: Hook, mask: c_int, count: c_int) -> c_int; + pub fn lua_sethook(l: State, hook: Option, mask: c_int, count: c_int) -> c_int; } diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 8d196a1..9e0e0ca 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, + lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::core::interrupt::Error; @@ -66,12 +66,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { } } unsafe { - lua_sethook( - l, - std::mem::transmute::<*const (), Hook>(std::ptr::null()), - 0, - 0, - ); + lua_sethook(l, None, 0, 0); lua_pushstring(l, c"interrupted".as_ptr()); lua_error(l); } @@ -89,12 +84,7 @@ extern "C" fn signal_handler(_: c_int) { } // Run the hook 1 instruction later. unsafe { - lua_sethook( - v.l, - lua_interrupt, - MASKCOUNT | MASKCALL | MASKLINE | MASKRET, - 1, - ) + lua_sethook(v.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1) }; v.return_chan.send(Ok(())).unwrap(); } From 7e337b21a7c53df13fc2e491164ea36536a38d7a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:05:52 +0200 Subject: [PATCH 258/527] Fixed build error under windows --- core/src/vm/core/interrupt/windows.rs | 21 +++------------------ core/src/vm/registry/interface.rs | 2 ++ 2 files changed, 5 insertions(+), 18 deletions(-) diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index 7fba3cc..d20daf4 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -52,12 +52,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { } } unsafe { - lua_sethook( - l, - std::mem::transmute::<*const (), Hook>(std::ptr::null()), - 0, - 0, - ); + lua_sethook(l, None, 0, 0); lua_pushstring(l, c"interrupted".as_ptr()); lua_error(l); } @@ -86,12 +81,7 @@ impl Signal { if self.th == unsafe { GetCurrentThread() } { // If somehow the system thread that ineterrupts the Vm is the same as the one which started the Vm, then directly set the hook. unsafe { - lua_sethook( - self.l, - lua_interrupt, - MASKCOUNT | MASKCALL | MASKLINE | MASKRET, - 1, - ); + lua_sethook(self.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); } } else { unsafe { @@ -105,12 +95,7 @@ impl Signal { if GetThreadContext(self.th, &mut ctx as _) == 0 { return Err(Error::Unknown); } - lua_sethook( - self.l, - lua_interrupt, - MASKCOUNT | MASKCALL | MASKLINE | MASKRET, - 1, - ); + lua_sethook(self.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); // Resume the thread. let _ = ResumeThread(self.th); } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index de01254..6b5e393 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -29,6 +29,8 @@ use crate::vm::registry::core::RegistryKey; use crate::vm::Vm; +//TODO: Try to find a better name. + pub trait RegistryValue: 'static { type Value<'a>; From daa168a1bdadc035a2e717c744161ff830db92b2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:06:42 +0200 Subject: [PATCH 259/527] Increased buffer time in instant test to avoid failure in CI builds --- core/tests/test_vm_libs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index ec203b3..095d86b 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -239,7 +239,7 @@ fn test_vm_lib_os_instant() { vm.run_code::<()>( c" local diff = instant:elapsed() - assert((diff - 0.5) < 0.1) + assert((diff - 0.5) < 0.2) ", ) .unwrap(); From 5b7bca93e99a4502e3c5fc4f0bf462db75f5181e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:07:28 +0200 Subject: [PATCH 260/527] Do not publish any crates for now --- codegen/Cargo.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 3ab6188..2d310e2 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -2,6 +2,7 @@ name = "bp3d-lua-codegen" version = "0.1.0" edition = "2021" #Due to gen be a reserved keyword! +publish = false [lib] proc-macro = true From 35458d2b77923a551b6c1fad5a8d7440fd75c0e3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:09:52 +0200 Subject: [PATCH 261/527] Removed unused Hook type under windows --- core/src/vm/core/interrupt/windows.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index d20daf4..350f961 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, Hook, State, MASKCALL, MASKCOUNT, MASKLINE, + lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::RootVm; From 2c1f499d3f91415372420ce73a3d26adbe3e1d0b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 21:12:06 +0000 Subject: [PATCH 262/527] Format Rust code using rustfmt --- core/src/vm/core/interrupt/unix.rs | 10 +++++++--- core/src/vm/core/interrupt/windows.rs | 14 ++++++++++++-- core/src/vm/value/core.rs | 3 +-- 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 9e0e0ca..96671d9 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -27,8 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, - MASKRET, + lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::core::interrupt::Error; use crate::vm::RootVm; @@ -84,7 +83,12 @@ extern "C" fn signal_handler(_: c_int) { } // Run the hook 1 instruction later. unsafe { - lua_sethook(v.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1) + lua_sethook( + v.l, + Some(lua_interrupt), + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ) }; v.return_chan.send(Ok(())).unwrap(); } diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index d20daf4..7deb5c3 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -81,7 +81,12 @@ impl Signal { if self.th == unsafe { GetCurrentThread() } { // If somehow the system thread that ineterrupts the Vm is the same as the one which started the Vm, then directly set the hook. unsafe { - lua_sethook(self.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); + lua_sethook( + self.l, + Some(lua_interrupt), + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ); } } else { unsafe { @@ -95,7 +100,12 @@ impl Signal { if GetThreadContext(self.th, &mut ctx as _) == 0 { return Err(Error::Unknown); } - lua_sethook(self.l, Some(lua_interrupt), MASKCOUNT | MASKCALL | MASKLINE | MASKRET, 1); + lua_sethook( + self.l, + Some(lua_interrupt), + MASKCOUNT | MASKCALL | MASKLINE | MASKRET, + 1, + ); // Resume the thread. let _ = ResumeThread(self.th); } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index ccf2107..3fb65ff 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -137,8 +137,7 @@ unsafe impl IntoLua for T { impl FromLua<'_> for () { #[inline(always)] - unsafe fn from_lua_unchecked(_: &'_ Vm, _: i32) -> Self { - } + unsafe fn from_lua_unchecked(_: &'_ Vm, _: i32) -> Self {} #[inline(always)] fn from_lua(_vm: &Vm, _: i32) -> crate::vm::Result<()> { From 98faa8a49786a7363d533fe127bafeb6bd675485 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 22 Apr 2025 21:22:25 +0000 Subject: [PATCH 263/527] Format Rust code using rustfmt --- core/src/vm/core/interrupt/windows.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index 96cadf3..9a4cb98 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -27,8 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, - MASKRET, + lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::RootVm; use bp3d_debug::{error, warning}; From bdd346479c8f23b82fa03f61b076f3ca9603b93d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:28:30 +0200 Subject: [PATCH 264/527] Attempt at fixing clippy warning --- core/src/vm/core/destructor.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 8b2ba53..392b352 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -104,7 +104,7 @@ impl Pool { /// # Safety /// /// The returned reference must not be aliased. - unsafe fn _from_vm(vm: &Vm) -> &mut Self { + unsafe fn _from_vm(vm: &Vm) -> *mut Self { let l = vm.as_ptr(); unsafe { lua_pushstring(l, c"__destructor_pool__".as_ptr()); @@ -112,12 +112,12 @@ impl Pool { let ptr = lua_touserdata(l, -1) as *mut Pool; assert!(!ptr.is_null()); lua_settop(l, -2); // Remove the pointer from the lua stack. - &mut *ptr + ptr } } pub fn from_vm(vm: &mut Vm) -> &mut Self { - unsafe { Self::_from_vm(vm) } + unsafe { &mut *Self::_from_vm(vm) } } pub fn attach(vm: &Vm, raw: R) -> R::Ptr @@ -125,7 +125,7 @@ impl Pool { R::Ptr: 'static, { let ptr = unsafe { Self::_from_vm(vm) }; - ptr.attach_mut(raw) + unsafe { (&mut *ptr).attach_mut(raw) } } pub fn attach_mut(&mut self, raw: R) -> R::Ptr From c540b14d55a482b6821056853ee8db98bede54c2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:35:46 +0200 Subject: [PATCH 265/527] Fixed clippy warning --- core/src/vm/core/destructor.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 392b352..d97b767 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -125,7 +125,7 @@ impl Pool { R::Ptr: 'static, { let ptr = unsafe { Self::_from_vm(vm) }; - unsafe { (&mut *ptr).attach_mut(raw) } + unsafe { (*ptr).attach_mut(raw) } } pub fn attach_mut(&mut self, raw: R) -> R::Ptr From a315071be717aea51fe8f2447e052fbfb83ed9dd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Apr 2025 23:40:08 +0200 Subject: [PATCH 266/527] Fixed most clippy warnings in testbin --- testbin/src/context.rs | 6 ++---- testbin/src/context_opt.rs | 6 ++---- testbin/src/main.rs | 18 ++++++++---------- 3 files changed, 12 insertions(+), 18 deletions(-) diff --git a/testbin/src/context.rs b/testbin/src/context.rs index 6955304..52a50ef 100644 --- a/testbin/src/context.rs +++ b/testbin/src/context.rs @@ -85,8 +85,7 @@ pub fn test_context_mlua() -> Duration { }) .unwrap(); } - let time = time.elapsed(); - time + time.elapsed() } pub fn test_context_vm() -> Duration { @@ -114,6 +113,5 @@ pub fn test_context_vm() -> Duration { vm.run_code::<()>(c"assert(context_pop() == nil)").unwrap(); } } - let time = time.elapsed(); - time + time.elapsed() } diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index e6ccf3b..caeb8a1 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -98,8 +98,7 @@ pub fn test_context_mlua() -> Duration { }) .unwrap(); } - let time = time.elapsed(); - time + time.elapsed() } pub fn test_context_vm() -> Duration { @@ -140,6 +139,5 @@ pub fn test_context_vm() -> Duration { part2.call::<()>(()).unwrap(); } } - let time = time.elapsed(); - time + time.elapsed() } diff --git a/testbin/src/main.rs b/testbin/src/main.rs index 3a62048..9268866 100644 --- a/testbin/src/main.rs +++ b/testbin/src/main.rs @@ -68,8 +68,7 @@ fn test_vm_destructor() -> Duration { assert_eq!(s, "Hello this is a test (0.42)"); vm.clear(); } - let time = time.elapsed(); - time + time.elapsed() } fn test_vm_mlua() -> Duration { @@ -98,8 +97,7 @@ fn test_vm_mlua() -> Duration { .unwrap(); assert_eq!(s, "Hello this is a test (0.42)"); } - let time = time.elapsed(); - time + time.elapsed() } fn main() { @@ -120,12 +118,12 @@ fn main() { ctx_mlua_opt += context_opt::test_context_mlua(); } - lua = lua / RUNS; - mlua = mlua / RUNS; - ctx_lua = ctx_lua / RUNS; - ctx_mlua = ctx_mlua / RUNS; - ctx_lua_opt = ctx_lua_opt / RUNS; - ctx_mlua_opt = ctx_mlua_opt / RUNS; + lua /= RUNS; + mlua /= RUNS; + ctx_lua /= RUNS; + ctx_mlua /= RUNS; + ctx_lua_opt /= RUNS; + ctx_mlua_opt /= RUNS; println!("average tools.lua (basic): {:?}", lua); println!("average mlua (basic): {:?}", mlua); From d5876b84606ead553c9089da9342fbc53fb23418 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 07:00:54 +0200 Subject: [PATCH 267/527] Moved testbin out of the workspace due to link issues under Linux --- Cargo.toml | 3 +-- testbin/.cargo/config | 3 +++ testbin/Cargo.toml | 5 ++++- 3 files changed, 8 insertions(+), 3 deletions(-) create mode 100644 testbin/.cargo/config diff --git a/Cargo.toml b/Cargo.toml index 111b8f7..2dd45a2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,6 +2,5 @@ members = [ "build", "codegen", - "core", - "testbin" + "core" ] diff --git a/testbin/.cargo/config b/testbin/.cargo/config new file mode 100644 index 0000000..1865a48 --- /dev/null +++ b/testbin/.cargo/config @@ -0,0 +1,3 @@ +[target.x86_64-unknown-linux-gnu.lua] +rustc-link-search = [] +rustc-link-lib = [] diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index 67a6fa1..9821bca 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -7,6 +7,9 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mlua = { version = "0.10.3", features = ["luajit", "vendored"] } +mlua = { version = "0.10.3", features = ["luajit"] } bp3d-lua = { version = "1.0.0-rc.1.0.0", path = "../core" } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } + +[workspace] +members = [] From 76c2cba443a3c77ffe11c3466ee0bbbea55eb53f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 07:08:28 +0200 Subject: [PATCH 268/527] Added aarch64 macos to testbin and fixed build error under windows --- core/Cargo.toml | 2 +- testbin/.cargo/config | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 6d3bfb3..06ec774 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,7 +23,7 @@ time = { version = "0.3.37", features = ["formatting"] } itertools = { version = "0.14.0" } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel"], optional = true } +windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics"], optional = true } [target.'cfg(unix)'.dependencies] libc = { version = "0.2.170", optional = true } diff --git a/testbin/.cargo/config b/testbin/.cargo/config index 1865a48..446973d 100644 --- a/testbin/.cargo/config +++ b/testbin/.cargo/config @@ -1,3 +1,7 @@ [target.x86_64-unknown-linux-gnu.lua] rustc-link-search = [] rustc-link-lib = [] + +[target.aarch64-apple-darwin.lua] +rustc-link-search = [] +rustc-link-lib = [] From 1fc816d435b495e368fb77c14ba06d463c85ef4c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 07:13:37 +0200 Subject: [PATCH 269/527] Added yet another stupid undocumented feature to windows-sys --- core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 06ec774..cbfd53c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -23,7 +23,7 @@ time = { version = "0.3.37", features = ["formatting"] } itertools = { version = "0.14.0" } [target.'cfg(windows)'.dependencies] -windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics"], optional = true } +windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug"], optional = true } [target.'cfg(unix)'.dependencies] libc = { version = "0.2.170", optional = true } From e09783ce4d0ba5e9bd1d4c24a8f5769918a36bba Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:01:59 +0200 Subject: [PATCH 270/527] Renamed LuaFunction to Function --- core/src/lib.rs | 9 ++++++++ core/src/libs/lua/call.rs | 4 ++-- core/src/libs/lua/load.rs | 6 +++--- core/src/vm/core/vm.rs | 6 +++--- core/src/vm/registry/types.rs | 10 ++++----- core/src/vm/value/any.rs | 4 ++-- core/src/vm/value/function.rs | 38 ++++++++++++++++----------------- core/tests/test_vm_backtrace.rs | 4 ++-- core/tests/test_vm_functions.rs | 6 +++--- testbin/src/context_opt.rs | 2 +- 10 files changed, 49 insertions(+), 40 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index ef0ef34..492fc66 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -28,6 +28,15 @@ //TODO: Support dynamic linking and modules by dynamic linking to luajit. //TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. +//TODO: Add support for LuaMethod to optimize calling methods on tables from registry (a function +// create to create from a LuaTable and an AnyStr matching the name of the method and a call method +// to call directly from registry). /!\ should be implemented as a higher-level helper like namespace, +// not a value /!\ => should probably refactor namespace into a new helper module outside the vm module. +//TODO: Add a LuaFunction helper as well to replace LuaFunction::call in registry. +//TODO: Probably rename LuaFunction as Function to match with Table vm component. +//TODO: Simplify API for get/set/geti/seti/get_field/set_field to accept a trait and implement for i32/string/etc. +//TODO: Remove LuaTable::call_method/call_function once the new LuaMethod exists. +//TODO: Attempt to implement custom __index on userdata. pub mod ffi; pub mod libs; diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index e5c227c..7d62261 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -32,10 +32,10 @@ use crate::vm::error::Error; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::function::LuaFunction; +use crate::vm::value::function::Function; decl_lib_func! { - fn pcall(vm: &Vm, func: LuaFunction) -> UncheckedAnyReturn { + fn pcall(vm: &Vm, func: Function) -> UncheckedAnyReturn { let top = vm.top(); true.into_param(vm); let ret = func.call::(()); diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index d7522c6..fa7ff6a 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -31,7 +31,7 @@ use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::namespace::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::function::LuaFunction; +use crate::vm::value::function::Function; use crate::{decl_closure, decl_lib_func}; use bp3d_util::simple_error; use std::path::{Path, PathBuf}; @@ -48,7 +48,7 @@ decl_lib_func! { } decl_lib_func! { - fn load_string<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (Option>, Option) { + fn load_string<'a>(vm: &Vm, s: &str, chunkname: Option<&str>) -> (Option>, Option) { match chunkname { None => match vm.load_code(s) { Ok(v) => (Some(v), None), @@ -86,7 +86,7 @@ fn parse_lua_path(chroot: &Path, path: &str) -> Result { } decl_closure! { - fn load_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> (Option>, Option) { + fn load_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> (Option>, Option) { let path = match parse_lua_path(chroot, path) { Ok(v) => v, Err(e) => return (None, Some(e.to_string())) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index ff8dbe5..cc9ed0d 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -38,7 +38,7 @@ use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; -use crate::vm::value::function::LuaFunction; +use crate::vm::value::function::Function; use crate::vm::value::{FromLua, IntoLua}; use bp3d_debug::debug; use std::cell::Cell; @@ -152,7 +152,7 @@ impl Vm { FromLua::from_lua(self, -(R::num_values() as i32)) } - pub fn load_code(&self, code: impl LoadString) -> crate::vm::Result { + pub fn load_code(&self, code: impl LoadString) -> crate::vm::Result { let l = self.as_ptr(); unsafe { // Push the lua code. @@ -177,7 +177,7 @@ impl Vm { FromLua::from_lua(self, -(R::num_values() as i32)) } - pub fn load(&self, obj: impl Load) -> crate::vm::Result { + pub fn load(&self, obj: impl Load) -> crate::vm::Result { let l = self.as_ptr(); let res = obj.load(l); unsafe { diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 680a94f..d4685f6 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -33,7 +33,7 @@ use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; pub struct Table; -pub struct LuaFunction; +pub struct Function; impl RegistryValue for Table { type Value<'a> = crate::vm::table::Table<'a>; @@ -44,16 +44,16 @@ impl RegistryValue for Table { } } -impl RegistryValue for LuaFunction { - type Value<'a> = crate::vm::value::function::LuaFunction<'a>; +impl RegistryValue for Function { + type Value<'a> = crate::vm::value::function::Function<'a>; #[inline(always)] unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { crate::vm::value::function::LuaFunction::from_lua_unchecked(vm, index) } + unsafe { crate::vm::value::function::Function::from_lua_unchecked(vm, index) } } } -impl RegistryKey { +impl RegistryKey { pub fn call<'a, T: IntoLua, R: FromLua<'a>>( &self, vm: &'a Vm, diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 043a469..25ce389 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -34,7 +34,7 @@ use crate::vm::table::Table; use crate::vm::thread::Thread; use crate::vm::userdata::AnyUserData; use crate::vm::util::{lua_rust_error, LuaType}; -use crate::vm::value::function::LuaFunction; +use crate::vm::value::function::Function; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::Display; @@ -47,7 +47,7 @@ pub enum AnyValue<'a> { Boolean(bool), String(&'a str), Buffer(&'a [u8]), - Function(LuaFunction<'a>), + Function(Function<'a>), Table(Table<'a>), UserData(AnyUserData<'a>), Thread(Thread<'a>), diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 74456ce..c288151 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -39,22 +39,22 @@ use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; -pub struct LuaFunction<'a> { +pub struct Function<'a> { vm: &'a Vm, index: i32, } -impl Clone for LuaFunction<'_> { +impl Clone for Function<'_> { fn clone(&self) -> Self { unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - LuaFunction { + Function { vm: self.vm, index: self.vm.top(), } } } -impl PartialEq for LuaFunction<'_> { +impl PartialEq for Function<'_> { fn eq(&self, other: &Self) -> bool { let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; @@ -62,9 +62,9 @@ impl PartialEq for LuaFunction<'_> { } } -impl Eq for LuaFunction<'_> {} +impl Eq for Function<'_> {} -impl Display for LuaFunction<'_> { +impl Display for Function<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!( f, @@ -74,20 +74,20 @@ impl Display for LuaFunction<'_> { } } -impl Debug for LuaFunction<'_> { +impl Debug for Function<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "LuaFunction({:?})", self.index) } } -unsafe impl SimpleDrop for LuaFunction<'_> {} +unsafe impl SimpleDrop for Function<'_> {} -impl LuaType for LuaFunction<'_> {} +impl LuaType for Function<'_> {} -impl<'a> FromParam<'a> for LuaFunction<'a> { +impl<'a> FromParam<'a> for Function<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { unsafe { luaL_checktype(vm.as_ptr(), index, Type::Function) }; - LuaFunction { vm, index } + Function { vm, index } } fn try_from_param(vm: &'a Vm, index: i32) -> Option { @@ -95,14 +95,14 @@ impl<'a> FromParam<'a> for LuaFunction<'a> { } } -unsafe impl IntoParam for LuaFunction<'_> { +unsafe impl IntoParam for Function<'_> { fn into_param(self, vm: &Vm) -> u16 { unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; 1 } } -impl LuaFunction<'_> { +impl Function<'_> { pub fn call<'b, R: FromLua<'b>>(&'b self, value: impl IntoLua) -> crate::vm::Result { let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; unsafe { @@ -114,10 +114,10 @@ impl LuaFunction<'_> { } } -impl<'a> FromLua<'a> for LuaFunction<'a> { +impl<'a> FromLua<'a> for Function<'a> { #[inline(always)] - unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> LuaFunction<'a> { - LuaFunction { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Function<'a> { + Function { vm, index: vm.get_absolute_index(index), } @@ -125,15 +125,15 @@ impl<'a> FromLua<'a> for LuaFunction<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { ensure_type_equals(vm, index, Type::Function)?; - Ok(LuaFunction { + Ok(Function { vm, index: vm.get_absolute_index(index), }) } } -impl Registry for LuaFunction<'_> { - type RegistryValue = crate::vm::registry::types::LuaFunction; +impl Registry for Function<'_> { + type RegistryValue = crate::vm::registry::types::Function; #[inline(always)] fn registry_put(self, vm: &Vm) -> RegistryKey { diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index c2066d7..620a952 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::value::function::Function; use bp3d_lua::vm::RootVm; use bp3d_util::simple_error; @@ -66,7 +66,7 @@ fn test_vm_backtrace() { ", ) .unwrap(); - let func: LuaFunction = vm.get_global(c"main").unwrap(); + let func: Function = vm.get_global(c"main").unwrap(); let err = func.call::<()>(()).unwrap_err().into_runtime().unwrap(); assert_eq!(err.msg(), "rust error: useless function called"); assert_eq!(err.backtrace(), "rust error: useless function called\nstack traceback:\n\t[C]: in function 'error_func'\n\t[string \"...\"]:3: in function 'raise'\n\t[string \"...\"]:7: in function 'a'\n\t[string \"...\"]:11: in function <[string \"...\"]:10>"); diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 113d6f5..b175acc 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::vm::table::Table; -use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::value::function::Function; use bp3d_lua::vm::RootVm; use std::ffi::CStr; @@ -35,7 +35,7 @@ use std::ffi::CStr; fn test_vm_function_1_arg() { let mut vm = RootVm::new(); let top = vm.top(); - let f: LuaFunction = vm + let f: Function = vm .run_code(c"return function(value) return 'this is a test ' .. value end") .unwrap(); let str: &str = f.call(42.42).unwrap(); @@ -50,7 +50,7 @@ fn test_vm_function_1_arg() { fn test_vm_function_2_args() { let mut vm = RootVm::new(); let top = vm.top(); - let f: LuaFunction = vm.run_code(c"return function(value, value2) return 'this ' .. value .. ' is a test ' .. tostring(value2) end").unwrap(); + let f: Function = vm.run_code(c"return function(value, value2) return 'this ' .. value .. ' is a test ' .. tostring(value2) end").unwrap(); let str: &str = f.call((42.42, false)).unwrap(); assert_eq!(str, "this 42.42 is a test false"); let str: &str = f.call((42, true)).unwrap(); diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index caeb8a1..e8d9cae 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; -use bp3d_lua::vm::value::function::LuaFunction; +use bp3d_lua::vm::value::function::Function as LuaFunction; use bp3d_lua::vm::RootVm; use mlua::{Function, Lua, UserDataMethods}; use std::time::Duration; From 1a47299ac3a10e41ab849e5a4cdaadae28d00891 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:06:38 +0200 Subject: [PATCH 271/527] Refactor: rename util to util::core --- codegen/src/gen/from_param.rs | 4 ++-- core/src/{util.rs => util/core.rs} | 0 core/src/util/mod.rs | 29 +++++++++++++++++++++++++++++ core/src/vm/closure/context.rs | 2 +- core/src/vm/closure/interface.rs | 2 +- core/src/vm/closure/rc.rs | 2 +- core/src/vm/core/vm.rs | 2 +- core/src/vm/function/core.rs | 2 +- core/src/vm/function/interface.rs | 2 +- core/src/vm/table/core.rs | 2 +- core/src/vm/table/interface.rs | 2 +- core/src/vm/thread.rs | 2 +- core/src/vm/value/any.rs | 2 +- core/src/vm/value/function.rs | 2 +- 14 files changed, 42 insertions(+), 13 deletions(-) rename core/src/{util.rs => util/core.rs} (100%) create mode 100644 core/src/util/mod.rs diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index f4b721c..4fb59f9 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -179,7 +179,7 @@ impl Parser for FromParam { } } - unsafe impl #generics bp3d_lua::util::SimpleDrop for #name #generics { } + unsafe impl #generics bp3d_lua::util::core::SimpleDrop for #name #generics { } } } @@ -207,7 +207,7 @@ impl Parser for FromParam { } } - unsafe impl #generics bp3d_lua::util::SimpleDrop for #name #generics { } + unsafe impl #generics bp3d_lua::util::core::SimpleDrop for #name #generics { } } } } diff --git a/core/src/util.rs b/core/src/util/core.rs similarity index 100% rename from core/src/util.rs rename to core/src/util/core.rs diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs new file mode 100644 index 0000000..f2a8a9a --- /dev/null +++ b/core/src/util/mod.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod core; diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 5b99795..8f72e6c 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -30,7 +30,7 @@ use crate::ffi::laux::luaL_error; use crate::ffi::lua::lua_newuserdata; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::registry::core::RawRegistryKey; use crate::vm::Vm; diff --git a/core/src/vm/closure/interface.rs b/core/src/vm/closure/interface.rs index 4c60d35..b68a477 100644 --- a/core/src/vm/closure/interface.rs +++ b/core/src/vm/closure/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::Vm; /// This trait represents a closure parameter. diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index ea8529f..4592e0a 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::Vm; use std::ops::Deref; diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index cc9ed0d..450eab6 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -31,7 +31,7 @@ use crate::ffi::lua::{ lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX, }; -use crate::util::AnyStr; +use crate::util::core::AnyStr; use crate::vm::core::destructor::Pool; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::core::{Load, LoadString}; diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 147b6f1..2a8180a 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -32,7 +32,7 @@ use crate::ffi::lua::{ lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_type, Integer, Number, Type, }; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; use crate::vm::util::{lua_rust_error, LuaType, TypeName}; diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 09521a1..8b8b7dd 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::util::LuaType; use crate::vm::Vm; diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 6ba2982..950a7df 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -31,7 +31,7 @@ use crate::ffi::lua::{ lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer, }; -use crate::util::AnyStr; +use crate::util::core::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; use crate::vm::value::util::ensure_single_into_lua; diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 4eac4d6..9f35106 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -28,7 +28,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::Registry; diff --git a/core/src/vm/thread.rs b/core/src/vm/thread.rs index 9a48625..e64a779 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread.rs @@ -28,7 +28,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::core::LoadString; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::FromParam; diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 25ce389..fa139d1 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::error::Error; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::table::Table; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index c288151..c5da923 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -28,7 +28,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; -use crate::util::SimpleDrop; +use crate::util::core::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::core::RegistryKey; From e944cdc6278699f70ea38b51019b12ca809c22fd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:08:58 +0200 Subject: [PATCH 272/527] Refactor: moved vm::namespace::Namespace to util::Namespace --- core/src/libs/interface.rs | 2 +- core/src/libs/lua/base.rs | 2 +- core/src/libs/lua/call.rs | 2 +- core/src/libs/lua/load.rs | 2 +- core/src/libs/lua/require.rs | 2 +- core/src/libs/os/compat.rs | 2 +- core/src/libs/os/instant.rs | 2 +- core/src/libs/os/time.rs | 2 +- core/src/libs/util/string.rs | 2 +- core/src/libs/util/table.rs | 2 +- core/src/libs/util/utf8.rs | 2 +- core/src/util/mod.rs | 3 +++ core/src/{vm => util}/namespace.rs | 0 core/src/vm/mod.rs | 1 - core/tests/test_vm_closures.rs | 2 +- 15 files changed, 15 insertions(+), 13 deletions(-) rename core/src/{vm => util}/namespace.rs (100%) diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index 5db7c26..a0a44e7 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::Vm; pub trait Lib { diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index aaefd09..d5d0ae6 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::libs::Lib; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table; const PATCH_LIST: &[&str] = &[ diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index 7d62261..e6fce77 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -30,7 +30,7 @@ use crate::decl_lib_func; use crate::libs::interface::Lib; use crate::vm::error::Error; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::Function; diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index fa7ff6a..0301233 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -29,7 +29,7 @@ use crate::libs::interface::Lib; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::function::Function; use crate::{decl_closure, decl_lib_func}; diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 3b150db..c18b7b9 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -29,7 +29,7 @@ use crate::decl_closure; use crate::libs::interface::Lib; use crate::vm::closure::rc::Rc; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; use bp3d_util::simple_error; diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index 0121ab4..7c9a37e 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -30,7 +30,7 @@ use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table; use crate::vm::Vm; use bp3d_os::time::{LocalUtcOffset, MonthExt}; diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index 5710787..2085cc4 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -28,7 +28,7 @@ use crate::libs::Lib; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::{decl_lib_func, decl_userdata}; struct Wrapper(bp3d_os::time::Instant); diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 84e25ff..b6e3a15 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -29,7 +29,7 @@ use crate::libs::Lib; use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table; use crate::vm::Vm; use crate::{decl_lib_func, decl_userdata}; diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 715c2c9..299af96 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -29,7 +29,7 @@ use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table; use bp3d_util::string::BufTools; use std::borrow::Cow; diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 4bc305e..b1d241d 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -29,7 +29,7 @@ use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table as LuaTable; use crate::vm::value::any::AnyValue; use crate::vm::Vm; diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index e8c2371..fb93c4c 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -29,7 +29,7 @@ use crate::decl_lib_func; use crate::libs::Lib; use crate::vm::function::types::RFunction; -use crate::vm::namespace::Namespace; +use crate::util::Namespace; use crate::vm::table::Table; use bp3d_util::string::StrTools; use std::borrow::Cow; diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index f2a8a9a..94da2e4 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -27,3 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod core; +mod namespace; + +pub use namespace::Namespace; diff --git a/core/src/vm/namespace.rs b/core/src/util/namespace.rs similarity index 100% rename from core/src/vm/namespace.rs rename to core/src/util/namespace.rs diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 0428faf..4cb5536 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -30,7 +30,6 @@ pub mod closure; pub mod core; pub mod error; pub mod function; -pub mod namespace; pub mod registry; pub mod table; pub mod thread; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 01c0fa5..1d60ac4 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -29,7 +29,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; -use bp3d_lua::vm::namespace::Namespace; +use bp3d_lua::util::namespace::Namespace; use bp3d_lua::vm::RootVm; struct TestContext { From 482385e7583b846aeb7aca51d20cbd7b0ba10f4a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:18:48 +0200 Subject: [PATCH 273/527] Added new LuaFunction utility in replacement of specialized impl on RegistryKey to support lua callbacks --- core/src/util/function.rs | 51 +++++++++++++++++++++++++++++++ core/src/util/mod.rs | 1 + core/src/util/namespace.rs | 4 +-- core/src/vm/registry/interface.rs | 16 ++-------- core/src/vm/registry/types.rs | 18 +---------- core/src/vm/table/core.rs | 2 +- core/src/vm/table/interface.rs | 16 ++++------ core/src/vm/value/function.rs | 16 ++++------ core/tests/test_vm_closures.rs | 2 +- 9 files changed, 71 insertions(+), 55 deletions(-) create mode 100644 core/src/util/function.rs diff --git a/core/src/util/function.rs b/core/src/util/function.rs new file mode 100644 index 0000000..9032100 --- /dev/null +++ b/core/src/util/function.rs @@ -0,0 +1,51 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::core::util::{pcall, push_error_handler}; +use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::types::Function; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; +use crate::vm::registry::Registry; + +/// This represents a Lua callback. +pub struct LuaFunction(RegistryKey); + +impl LuaFunction { + pub fn create(f: crate::vm::value::function::Function) -> Self { + Self(f.registry_put()) + } + + pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { + let pos = unsafe { push_error_handler(vm.as_ptr()) }; + unsafe { self.0.as_raw().push(vm) }; + let num_values = value.into_lua(vm); + unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; + R::from_lua(vm, -(R::num_values() as i32)) + } +} diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 94da2e4..3cb77d8 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -28,5 +28,6 @@ pub mod core; mod namespace; +mod function; pub use namespace::Namespace; diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 9fa2f0f..13416e3 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -43,7 +43,7 @@ impl<'a> Namespace<'a> { table: Table<'a>, names: impl Iterator, ) -> crate::vm::Result { - let mut key = table.registry_put(vm); + let mut key = table.registry_put(); let key = vm.scope(|vm| { for name in names { let mut table = key.push(vm); @@ -55,7 +55,7 @@ impl<'a> Namespace<'a> { table.get_field(name)? } }; - key = tab.registry_swap(vm, key); + key = tab.registry_swap(key); } Ok(key) })?; diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 6b5e393..c8dbecc 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -56,25 +56,13 @@ pub trait Registry: Sized { /// Register this value into the registry. /// - /// # Arguments - /// - /// * `vm`: the [Vm] to attach this value to. - /// /// returns: RegistryKey - fn registry_put(self, vm: &Vm) -> RegistryKey; + fn registry_put(self) -> RegistryKey; /// Swaps the value pointed by `old` in the registry to this value. /// /// # Arguments /// - /// * `vm`: the [Vm] to attach this value to. /// * `old`: the old registry key to be replaced. - fn registry_swap( - self, - vm: &Vm, - old: RegistryKey, - ) -> RegistryKey { - old.delete(vm); - self.registry_put(vm) - } + fn registry_swap(self, old: RegistryKey) -> RegistryKey; } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index d4685f6..437484b 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,10 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::core::util::{pcall, push_error_handler}; -use crate::vm::registry::core::RegistryKey; use crate::vm::registry::RegistryValue; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::FromLua; use crate::vm::Vm; pub struct Table; @@ -52,17 +50,3 @@ impl RegistryValue for Function { unsafe { crate::vm::value::function::Function::from_lua_unchecked(vm, index) } } } - -impl RegistryKey { - pub fn call<'a, T: IntoLua, R: FromLua<'a>>( - &self, - vm: &'a Vm, - value: T, - ) -> crate::vm::Result { - let pos = unsafe { push_error_handler(vm.as_ptr()) }; - unsafe { self.as_raw().push(vm) }; - let num_values = value.into_lua(vm); - unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; - R::from_lua(vm, -(R::num_values() as i32)) - } -} diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 950a7df..04dd991 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -40,7 +40,7 @@ use crate::vm::Vm; use std::fmt::{Debug, Display}; pub struct Table<'a> { - vm: &'a Vm, + pub(super) vm: &'a Vm, index: i32, } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 9f35106..6b165b1 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -83,21 +83,17 @@ impl Registry for Table<'_> { type RegistryValue = crate::vm::registry::types::Table; #[inline(always)] - fn registry_put(self, vm: &Vm) -> RegistryKey { + fn registry_put(self) -> RegistryKey { // If the table is not at the top of the stack, move it to the top. - ensure_value_top(vm, self.index()); - unsafe { RegistryKey::from_top(vm) } + ensure_value_top(self.vm, self.index()); + unsafe { RegistryKey::from_top(self.vm) } } #[inline(always)] - fn registry_swap( - self, - vm: &Vm, - old: RegistryKey, - ) -> RegistryKey { + fn registry_swap(self, old: RegistryKey) -> RegistryKey { // If the table is not at the top of the stack, move it to the top. - ensure_value_top(vm, self.index()); - unsafe { old.as_raw().replace(vm) }; + ensure_value_top(self.vm, self.index()); + unsafe { old.as_raw().replace(self.vm) }; old } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index c5da923..23089a6 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -136,21 +136,17 @@ impl Registry for Function<'_> { type RegistryValue = crate::vm::registry::types::Function; #[inline(always)] - fn registry_put(self, vm: &Vm) -> RegistryKey { + fn registry_put(self) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. - ensure_value_top(vm, self.index); - unsafe { RegistryKey::from_top(vm) } + ensure_value_top(self.vm, self.index); + unsafe { RegistryKey::from_top(self.vm) } } #[inline(always)] - fn registry_swap( - self, - vm: &Vm, - old: RegistryKey, - ) -> RegistryKey { + fn registry_swap(self, old: RegistryKey) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. - ensure_value_top(vm, self.index); - unsafe { old.as_raw().replace(vm) }; + ensure_value_top(self.vm, self.index); + unsafe { old.as_raw().replace(self.vm) }; old } } diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 1d60ac4..2c0345f 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -29,7 +29,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; -use bp3d_lua::util::namespace::Namespace; +use bp3d_lua::util::Namespace; use bp3d_lua::vm::RootVm; struct TestContext { From 91640ce79816de67ca7b04d1d8184dd2da45f49e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:19:50 +0200 Subject: [PATCH 274/527] Added export of LuaFunction --- core/src/util/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 3cb77d8..7abaddb 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -31,3 +31,4 @@ mod namespace; mod function; pub use namespace::Namespace; +pub use function::LuaFunction; From 8d2b83668e1ae30448cb5e063fab53ebcdb49f71 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:25:21 +0200 Subject: [PATCH 275/527] Renamed value::function::Function to value::Function --- core/src/libs/lua/call.rs | 2 +- core/src/libs/lua/load.rs | 2 +- core/src/util/function.rs | 2 +- core/src/vm/core/vm.rs | 2 +- core/src/vm/registry/types.rs | 4 ++-- core/src/vm/value/mod.rs | 3 ++- core/tests/test_vm_backtrace.rs | 2 +- core/tests/test_vm_functions.rs | 2 +- 8 files changed, 10 insertions(+), 9 deletions(-) diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index e6fce77..e2c5ee9 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -32,7 +32,7 @@ use crate::vm::error::Error; use crate::vm::function::types::RFunction; use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::function::Function; +use crate::vm::value::Function; decl_lib_func! { fn pcall(vm: &Vm, func: Function) -> UncheckedAnyReturn { diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 0301233..8146ced 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -31,7 +31,7 @@ use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::function::Function; +use crate::vm::value::Function; use crate::{decl_closure, decl_lib_func}; use bp3d_util::simple_error; use std::path::{Path, PathBuf}; diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 9032100..5bf972f 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -37,7 +37,7 @@ use crate::vm::registry::Registry; pub struct LuaFunction(RegistryKey); impl LuaFunction { - pub fn create(f: crate::vm::value::function::Function) -> Self { + pub fn create(f: crate::vm::value::Function) -> Self { Self(f.registry_put()) } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 450eab6..1a4cb45 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -38,7 +38,7 @@ use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; -use crate::vm::value::function::Function; +use crate::vm::value::Function; use crate::vm::value::{FromLua, IntoLua}; use bp3d_debug::debug; use std::cell::Cell; diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 437484b..19af682 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -43,10 +43,10 @@ impl RegistryValue for Table { } impl RegistryValue for Function { - type Value<'a> = crate::vm::value::function::Function<'a>; + type Value<'a> = crate::vm::value::Function<'a>; #[inline(always)] unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { crate::vm::value::function::Function::from_lua_unchecked(vm, index) } + unsafe { crate::vm::value::Function::from_lua_unchecked(vm, index) } } } diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 4985ac9..7619c75 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -28,8 +28,9 @@ pub mod any; mod core; -pub mod function; +mod function; mod interface; pub mod util; pub use interface::*; +pub use function::Function; diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index 620a952..db63378 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::value::function::Function; +use bp3d_lua::vm::value::Function; use bp3d_lua::vm::RootVm; use bp3d_util::simple_error; diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index b175acc..85917d5 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::vm::table::Table; -use bp3d_lua::vm::value::function::Function; +use bp3d_lua::vm::value::Function; use bp3d_lua::vm::RootVm; use std::ffi::CStr; From f7dece0ef405932c4932f699fb55f6e922fd82ff Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:25:51 +0200 Subject: [PATCH 276/527] Fixed build error in context_opt --- testbin/src/context_opt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index e8d9cae..8d9b139 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; -use bp3d_lua::vm::value::function::Function as LuaFunction; +use bp3d_lua::vm::value::Function as LuaFunction; use bp3d_lua::vm::RootVm; use mlua::{Function, Lua, UserDataMethods}; use std::time::Duration; From ad6c7e6168ae4ba604594a79f6b0fd799dab3aef Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:37:27 +0200 Subject: [PATCH 277/527] Added LuaMethod in replacement for member functions on Table --- core/src/util/function.rs | 6 +++- core/src/util/method.rs | 63 +++++++++++++++++++++++++++++++++ core/src/util/mod.rs | 2 ++ core/src/vm/table/core.rs | 26 -------------- core/tests/test_vm_functions.rs | 7 ++-- 5 files changed, 75 insertions(+), 29 deletions(-) create mode 100644 core/src/util/method.rs diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 5bf972f..fb56a14 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -41,11 +41,15 @@ impl LuaFunction { Self(f.registry_put()) } - pub fn call<'a, T: IntoLua, R: FromLua<'a>>(&self, vm: &'a Vm, value: T) -> crate::vm::Result { + pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.0.as_raw().push(vm) }; let num_values = value.into_lua(vm); unsafe { pcall(vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(vm, -(R::num_values() as i32)) } + + pub fn delete(self, vm: &Vm) { + self.0.delete(vm) + } } diff --git a/core/src/util/method.rs b/core/src/util/method.rs new file mode 100644 index 0000000..cbbd1c0 --- /dev/null +++ b/core/src/util/method.rs @@ -0,0 +1,63 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::util::core::AnyStr; +use crate::vm::core::util::{pcall, push_error_handler}; +use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::Registry; +use crate::vm::registry::types::{Function, Table}; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; + +pub struct LuaMethod { + obj: RegistryKey
, + method: RegistryKey +} + +impl LuaMethod { + pub fn create(obj: crate::vm::table::Table, method_name: impl AnyStr) -> crate::vm::Result { + let method: crate::vm::value::Function = obj.get_field(method_name)?; + let method = method.registry_put(); + let obj = obj.registry_put(); + Ok(Self { obj, method }) + } + + pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { + let pos = unsafe { push_error_handler(vm.as_ptr()) }; + unsafe { self.method.as_raw().push(vm) }; + unsafe { self.obj.as_raw().push(vm) }; + let num_values = value.into_lua(vm); + unsafe { pcall(vm, (num_values + 1) as _, R::num_values() as _, pos)? }; + R::from_lua(vm, -(R::num_values() as i32)) + } + + pub fn delete(self, vm: &Vm) { + self.method.delete(vm); + self.obj.delete(vm); + } +} diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 7abaddb..60ec1e6 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -29,6 +29,8 @@ pub mod core; mod namespace; mod function; +mod method; pub use namespace::Namespace; pub use function::LuaFunction; +pub use method::LuaMethod; diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 04dd991..ef3b15d 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -32,7 +32,6 @@ use crate::ffi::lua::{ lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer, }; use crate::util::core::AnyStr; -use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::table::iter::Iter; use crate::vm::value::util::ensure_single_into_lua; use crate::vm::value::{FromLua, IntoLua}; @@ -137,31 +136,6 @@ impl<'a> Table<'a> { self.len() == 0 } - pub fn call_function<'b, T: IntoLua, R: FromLua<'b>>( - &'b self, - name: impl AnyStr, - value: T, - ) -> crate::vm::Result { - let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - let num_values = value.into_lua(self.vm); - unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } - - pub fn call_method<'b, T: IntoLua, R: FromLua<'b>>( - &'b self, - name: impl AnyStr, - value: T, - ) -> crate::vm::Result { - let pos = unsafe { push_error_handler(self.vm.as_ptr()) }; - unsafe { lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()) }; - unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - let num_values = value.into_lua(self.vm); - unsafe { pcall(self.vm, (num_values + 1) as _, R::num_values() as _, pos)? }; - R::from_lua(self.vm, -(R::num_values() as i32)) - } - /// Creates a new iterator for this table. /// /// This function borrows mutably to avoid messing up the Lua stack while iterating. diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 85917d5..4fd967d 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -30,6 +30,7 @@ use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::Function; use bp3d_lua::vm::RootVm; use std::ffi::CStr; +use bp3d_lua::util::LuaMethod; #[test] fn test_vm_function_1_arg() { @@ -74,8 +75,10 @@ fn test_vm_function_method() { let mut vm = RootVm::new(); let top = vm.top(); let obj: Table = vm.run_code(METHODS).unwrap(); - let str: &str = obj.call_method(c"greeting", ()).unwrap(); + let method = LuaMethod::create(obj, c"greeting").unwrap(); + let str: &str = method.call(&vm, ()).unwrap(); assert_eq!(str, "Hello this is a test"); - assert_eq!(vm.top(), top + 2); // Table + 1 result + method.delete(&vm); + assert_eq!(vm.top(), top + 1); // 1 result vm.clear(); } From 6cca251fb8e79b0a855a58ecd77be924867bb7ff Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:40:01 +0200 Subject: [PATCH 278/527] Removed TODO comments --- core/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 492fc66..dc95cff 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -28,14 +28,7 @@ //TODO: Support dynamic linking and modules by dynamic linking to luajit. //TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. -//TODO: Add support for LuaMethod to optimize calling methods on tables from registry (a function -// create to create from a LuaTable and an AnyStr matching the name of the method and a call method -// to call directly from registry). /!\ should be implemented as a higher-level helper like namespace, -// not a value /!\ => should probably refactor namespace into a new helper module outside the vm module. -//TODO: Add a LuaFunction helper as well to replace LuaFunction::call in registry. -//TODO: Probably rename LuaFunction as Function to match with Table vm component. //TODO: Simplify API for get/set/geti/seti/get_field/set_field to accept a trait and implement for i32/string/etc. -//TODO: Remove LuaTable::call_method/call_function once the new LuaMethod exists. //TODO: Attempt to implement custom __index on userdata. pub mod ffi; From cbe66dada6ad5942be25dc0619590d6e295bdeec Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Apr 2025 22:41:08 +0200 Subject: [PATCH 279/527] Updated doc comment --- core/src/util/core.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/util/core.rs b/core/src/util/core.rs index ba76ecd..9b7f6e0 100644 --- a/core/src/util/core.rs +++ b/core/src/util/core.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//! Generic rust utilities module. +//! Core rust utilities module. use std::borrow::Cow; use std::ffi::{CStr, CString, OsStr}; From 43e91061035dd6c3e82dbf6e790601deedb001c2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 19:50:46 +0200 Subject: [PATCH 280/527] Renamed decl_from_param decl_from_param_unchecked as this macro is unsafe --- core/src/macro/closure.rs | 4 ++-- core/src/macro/lib_func.rs | 4 ++-- core/src/macro/mod.rs | 8 +++++--- core/src/macro/userdata_func.rs | 8 ++++---- core/src/vm/core/destructor.rs | 2 ++ core/src/vm/userdata/core.rs | 3 +++ 6 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/src/macro/closure.rs b/core/src/macro/closure.rs index 0f8a0e3..5076564 100644 --- a/core/src/macro/closure.rs +++ b/core/src/macro/closure.rs @@ -39,7 +39,7 @@ macro_rules! decl_closure { #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { let $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_> = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; - $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 1, $($arg_name: $arg_ty)*);)? let ret = _func(vm, $upvalue_name $(, $($arg_name),*)?); ret.into_param(vm) as _ } @@ -59,7 +59,7 @@ macro_rules! decl_closure { #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { let $upvalue_name: <$upvalue_ty as $crate::vm::closure::Upvalue>::From<'_> = unsafe { $crate::vm::closure::FromUpvalue::from_upvalue(vm, 1) }; - $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); + $crate::decl_from_param_unchecked!(vm, 1, $($arg_name: $arg_ty)*); let ret = _func($upvalue_name, $($arg_name),*); ret.into_param(vm) as _ } diff --git a/core/src/macro/lib_func.rs b/core/src/macro/lib_func.rs index 36e4e7c..860abd0 100644 --- a/core/src/macro/lib_func.rs +++ b/core/src/macro/lib_func.rs @@ -37,7 +37,7 @@ macro_rules! decl_lib_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 1, $($arg_name: $arg_ty)*);)? let ret = _func(vm $(, $($arg_name),*)?); ret.into_param(vm) as _ } @@ -53,7 +53,7 @@ macro_rules! decl_lib_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $crate::decl_from_param!(vm, 1, $($arg_name: $arg_ty)*); + $crate::decl_from_param_unchecked!(vm, 1, $($arg_name: $arg_ty)*); let ret = _func($($arg_name),*); ret.into_param(vm) as _ } diff --git a/core/src/macro/mod.rs b/core/src/macro/mod.rs index 95e9140..686f930 100644 --- a/core/src/macro/mod.rs +++ b/core/src/macro/mod.rs @@ -38,8 +38,10 @@ macro_rules! c_stringify { }; } +/// This macro is unsafe and should not be used from safe code directly. It is intended as a +/// building block for other macros. #[macro_export] -macro_rules! decl_from_param { +macro_rules! decl_from_param_unchecked { ( $vm: ident, $start_index: literal, ) => { @@ -57,7 +59,7 @@ macro_rules! decl_from_param { ) => { use $crate::vm::function::FromParam; let mut index = $start_index; - $crate::decl_from_param!(_from_param $vm, index, $(($arg_name: $arg_ty))*); + $crate::decl_from_param_unchecked!(_from_param $vm, index, $(($arg_name: $arg_ty))*); }; (_from_param $vm: ident, $index: ident, ) => { }; @@ -69,6 +71,6 @@ macro_rules! decl_from_param { (_from_param $vm: ident, $index: ident, ($arg_name: ident: $arg_ty: ty) $(($arg_name2: ident: $arg_ty2: ty))*) => { let $arg_name: $arg_ty = unsafe { FromParam::from_param($vm, $index) }; $index += 1; - $crate::decl_from_param!(_from_param $vm, $index, $(($arg_name2: $arg_ty2))*); + $crate::decl_from_param_unchecked!(_from_param $vm, $index, $(($arg_name2: $arg_ty2))*); }; } diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index b065e30..ad76a5e 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -40,7 +40,7 @@ macro_rules! decl_userdata_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 2, $($arg_name: $arg_ty)*);)? let ret = _func(unsafe { &mut *this_ptr }, vm $(, $($arg_name),*)?); ret.into_param(vm) as _ } @@ -66,7 +66,7 @@ macro_rules! decl_userdata_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 2, $($arg_name: $arg_ty)*);)? let ret = _func(unsafe { &mut *this_ptr } $(, $($arg_name),*)?); ret.into_param(vm) as _ } @@ -92,7 +92,7 @@ macro_rules! decl_userdata_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 2, $($arg_name: $arg_ty)*);)? let ret = _func(unsafe { &*this_ptr }, vm $(, $($arg_name),*)?); ret.into_param(vm) as _ } @@ -117,7 +117,7 @@ macro_rules! decl_userdata_func { let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param!(vm, 2, $($arg_name: $arg_ty)*);)? + $($crate::decl_from_param_unchecked!(vm, 2, $($arg_name: $arg_ty)*);)? let ret = _func(unsafe { &*this_ptr } $(, $($arg_name),*)?); ret.into_param(vm) as _ } diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index d97b767..6e25bb8 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -34,6 +34,8 @@ use crate::vm::Vm; use bp3d_debug::debug; use std::rc::Rc; +//TODO: Use rawgetp/rawsetp to manage the pointer to the destructor. + /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { type Ptr: Copy; diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index a9a8c90..3ce896c 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -40,6 +40,9 @@ use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; +//TODO: This should be a builder. +//TODO: The actual function structure should only contain name and CFunction. + pub struct Function { is_mutable: bool, args: Vec, From f74688d18076a4eae34400411afc41cc8efb2fce Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 24 Apr 2025 17:58:42 +0000 Subject: [PATCH 281/527] Format Rust code using rustfmt --- core/src/libs/lua/call.rs | 2 +- core/src/libs/lua/load.rs | 2 +- core/src/libs/lua/require.rs | 2 +- core/src/libs/os/compat.rs | 2 +- core/src/libs/os/instant.rs | 2 +- core/src/libs/os/time.rs | 2 +- core/src/libs/util/string.rs | 2 +- core/src/libs/util/table.rs | 2 +- core/src/libs/util/utf8.rs | 2 +- core/src/util/function.rs | 8 ++++++-- core/src/util/method.rs | 15 +++++++++++---- core/src/util/mod.rs | 4 ++-- core/src/vm/registry/interface.rs | 5 ++++- core/src/vm/table/interface.rs | 5 ++++- core/src/vm/value/function.rs | 5 ++++- core/src/vm/value/mod.rs | 2 +- core/tests/test_vm_closures.rs | 2 +- core/tests/test_vm_functions.rs | 2 +- 18 files changed, 43 insertions(+), 23 deletions(-) diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index e2c5ee9..fdc238e 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -28,9 +28,9 @@ use crate::decl_lib_func; use crate::libs::interface::Lib; +use crate::util::Namespace; use crate::vm::error::Error; use crate::vm::function::types::RFunction; -use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::Function; diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 8146ced..e13a4da 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::libs::interface::Lib; +use crate::util::Namespace; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; -use crate::util::Namespace; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::Function; use crate::{decl_closure, decl_lib_func}; diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index c18b7b9..475f6bb 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -28,8 +28,8 @@ use crate::decl_closure; use crate::libs::interface::Lib; -use crate::vm::closure::rc::Rc; use crate::util::Namespace; +use crate::vm::closure::rc::Rc; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; use bp3d_util::simple_error; diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index 7c9a37e..6697589 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -28,9 +28,9 @@ use crate::decl_lib_func; use crate::libs::Lib; +use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; -use crate::util::Namespace; use crate::vm::table::Table; use crate::vm::Vm; use bp3d_os::time::{LocalUtcOffset, MonthExt}; diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index 2085cc4..367149e 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -27,8 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::libs::Lib; -use crate::vm::function::types::RFunction; use crate::util::Namespace; +use crate::vm::function::types::RFunction; use crate::{decl_lib_func, decl_userdata}; struct Wrapper(bp3d_os::time::Instant); diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index b6e3a15..493e3d8 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::libs::Lib; +use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; -use crate::util::Namespace; use crate::vm::table::Table; use crate::vm::Vm; use crate::{decl_lib_func, decl_userdata}; diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 299af96..2befd61 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -28,8 +28,8 @@ use crate::decl_lib_func; use crate::libs::Lib; -use crate::vm::function::types::RFunction; use crate::util::Namespace; +use crate::vm::function::types::RFunction; use crate::vm::table::Table; use bp3d_util::string::BufTools; use std::borrow::Cow; diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index b1d241d..e157062 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -28,8 +28,8 @@ use crate::decl_lib_func; use crate::libs::Lib; -use crate::vm::function::types::RFunction; use crate::util::Namespace; +use crate::vm::function::types::RFunction; use crate::vm::table::Table as LuaTable; use crate::vm::value::any::AnyValue; use crate::vm::Vm; diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index fb93c4c..4c9761d 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -28,8 +28,8 @@ use crate::decl_lib_func; use crate::libs::Lib; -use crate::vm::function::types::RFunction; use crate::util::Namespace; +use crate::vm::function::types::RFunction; use crate::vm::table::Table; use bp3d_util::string::StrTools; use std::borrow::Cow; diff --git a/core/src/util/function.rs b/core/src/util/function.rs index fb56a14..30cfd25 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -29,9 +29,9 @@ use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; use crate::vm::registry::types::Function; +use crate::vm::registry::Registry; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; -use crate::vm::registry::Registry; /// This represents a Lua callback. pub struct LuaFunction(RegistryKey); @@ -41,7 +41,11 @@ impl LuaFunction { Self(f.registry_put()) } - pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { + pub fn call<'a, R: FromLua<'a>>( + &self, + vm: &'a Vm, + value: impl IntoLua, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.0.as_raw().push(vm) }; let num_values = value.into_lua(vm); diff --git a/core/src/util/method.rs b/core/src/util/method.rs index cbbd1c0..c67b767 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -29,25 +29,32 @@ use crate::util::core::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::RegistryKey; -use crate::vm::registry::Registry; use crate::vm::registry::types::{Function, Table}; +use crate::vm::registry::Registry; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; pub struct LuaMethod { obj: RegistryKey
, - method: RegistryKey + method: RegistryKey, } impl LuaMethod { - pub fn create(obj: crate::vm::table::Table, method_name: impl AnyStr) -> crate::vm::Result { + pub fn create( + obj: crate::vm::table::Table, + method_name: impl AnyStr, + ) -> crate::vm::Result { let method: crate::vm::value::Function = obj.get_field(method_name)?; let method = method.registry_put(); let obj = obj.registry_put(); Ok(Self { obj, method }) } - pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { + pub fn call<'a, R: FromLua<'a>>( + &self, + vm: &'a Vm, + value: impl IntoLua, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.method.as_raw().push(vm) }; unsafe { self.obj.as_raw().push(vm) }; diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 60ec1e6..f2807ba 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -27,10 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod core; -mod namespace; mod function; mod method; +mod namespace; -pub use namespace::Namespace; pub use function::LuaFunction; pub use method::LuaMethod; +pub use namespace::Namespace; diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index c8dbecc..bd59267 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -64,5 +64,8 @@ pub trait Registry: Sized { /// # Arguments /// /// * `old`: the old registry key to be replaced. - fn registry_swap(self, old: RegistryKey) -> RegistryKey; + fn registry_swap( + self, + old: RegistryKey, + ) -> RegistryKey; } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 6b165b1..60369e5 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -90,7 +90,10 @@ impl Registry for Table<'_> { } #[inline(always)] - fn registry_swap(self, old: RegistryKey) -> RegistryKey { + fn registry_swap( + self, + old: RegistryKey, + ) -> RegistryKey { // If the table is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index()); unsafe { old.as_raw().replace(self.vm) }; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 23089a6..0ae2f03 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -143,7 +143,10 @@ impl Registry for Function<'_> { } #[inline(always)] - fn registry_swap(self, old: RegistryKey) -> RegistryKey { + fn registry_swap( + self, + old: RegistryKey, + ) -> RegistryKey { // If the function is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index); unsafe { old.as_raw().replace(self.vm) }; diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 7619c75..5686c70 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -32,5 +32,5 @@ mod function; mod interface; pub mod util; -pub use interface::*; pub use function::Function; +pub use interface::*; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 2c0345f..ee9a15f 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; +use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; -use bp3d_lua::util::Namespace; use bp3d_lua::vm::RootVm; struct TestContext { diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 4fd967d..5a813e1 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_lua::util::LuaMethod; use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::Function; use bp3d_lua::vm::RootVm; use std::ffi::CStr; -use bp3d_lua::util::LuaMethod; #[test] fn test_vm_function_1_arg() { From e8ae8b6f1fd54d1500511bfe85b1f0d17b35c452 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 19:58:54 +0200 Subject: [PATCH 282/527] Renamed RegistryKey to Key and RawRegistryKey to RawKey --- core/src/util/function.rs | 4 ++-- core/src/util/method.rs | 6 +++--- core/src/vm/closure/context.rs | 6 +++--- core/src/vm/registry/core.rs | 30 +++++++++++++++--------------- core/src/vm/registry/interface.rs | 6 +++--- core/src/vm/table/interface.rs | 8 ++++---- core/src/vm/value/function.rs | 8 ++++---- 7 files changed, 34 insertions(+), 34 deletions(-) diff --git a/core/src/util/function.rs b/core/src/util/function.rs index fb56a14..14605a6 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -27,14 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::vm::core::util::{pcall, push_error_handler}; -use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::core::Key; use crate::vm::registry::types::Function; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use crate::vm::registry::Registry; /// This represents a Lua callback. -pub struct LuaFunction(RegistryKey); +pub struct LuaFunction(Key); impl LuaFunction { pub fn create(f: crate::vm::value::Function) -> Self { diff --git a/core/src/util/method.rs b/core/src/util/method.rs index cbbd1c0..b32b538 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -28,15 +28,15 @@ use crate::util::core::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; -use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::core::Key; use crate::vm::registry::Registry; use crate::vm::registry::types::{Function, Table}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; pub struct LuaMethod { - obj: RegistryKey
, - method: RegistryKey + obj: Key
, + method: Key } impl LuaMethod { diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 8f72e6c..e14c9b3 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -32,7 +32,7 @@ use crate::ffi::laux::luaL_error; use crate::ffi::lua::lua_newuserdata; use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; -use crate::vm::registry::core::RawRegistryKey; +use crate::vm::registry::core::RawKey; use crate::vm::Vm; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; @@ -74,7 +74,7 @@ impl CellMut { } pub struct Context { - key: RawRegistryKey, + key: RawKey, ptr: *mut *const T, } @@ -100,7 +100,7 @@ impl Context { pub fn new(vm: &Vm) -> Self { let (ptr, key) = unsafe { let ptr = lua_newuserdata(vm.as_ptr(), 8); - (ptr, RawRegistryKey::from_top(vm)) + (ptr, RawKey::from_top(vm)) }; Self { key, diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 9028b90..895402d 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -37,9 +37,9 @@ use std::marker::PhantomData; #[derive(Debug, Copy, Clone)] #[repr(transparent)] -pub struct RawRegistryKey(c_int); +pub struct RawKey(c_int); -impl RawRegistryKey { +impl RawKey { /// Returns the raw key. #[inline(always)] pub fn as_int(&self) -> c_int { @@ -48,8 +48,8 @@ impl RawRegistryKey { /// Wraps a raw integer as a registry key. #[inline(always)] - pub fn from_int(v: c_int) -> RawRegistryKey { - RawRegistryKey(v) + pub fn from_int(v: c_int) -> RawKey { + RawKey(v) } /// Pushes the lua value associated to this registry key on the lua stack. @@ -100,7 +100,7 @@ impl RawRegistryKey { lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); } - /// Creates a new [RawRegistryKey] from the top of the lua stack. + /// Creates a new [RawKey] from the top of the lua stack. /// /// # Arguments /// @@ -112,18 +112,18 @@ impl RawRegistryKey { /// /// This is UB to call if the stack is empty. #[inline(always)] - pub unsafe fn from_top(vm: &Vm) -> RawRegistryKey { + pub unsafe fn from_top(vm: &Vm) -> RawKey { let key = unsafe { luaL_ref(vm.as_ptr(), REGISTRYINDEX) }; - RawRegistryKey(key) + RawKey(key) } } -pub struct RegistryKey { - raw: RawRegistryKey, +pub struct Key { + raw: RawKey, useless: PhantomData<*const T>, } -impl RegistryKey { +impl Key { /// Pushes the lua value associated to this registry key on the lua stack. /// /// # Arguments @@ -145,7 +145,7 @@ impl RegistryKey { /// /// returns: ::Value #[inline(always)] - pub fn as_raw(&self) -> RawRegistryKey { + pub fn as_raw(&self) -> RawKey { self.raw } @@ -161,7 +161,7 @@ impl RegistryKey { unsafe { self.raw.delete(vm) }; } - /// Creates a new [RegistryKey] from the top of the lua stack. + /// Creates a new [Key] from the top of the lua stack. /// /// # Arguments /// @@ -173,9 +173,9 @@ impl RegistryKey { /// /// The type T must match the type of the value at the top of the stack. Additionally, the value /// at the top of the stack must not be referenced as it will be popped. - pub unsafe fn from_top(vm: &Vm) -> RegistryKey { - RegistryKey { - raw: RawRegistryKey::from_top(vm), + pub unsafe fn from_top(vm: &Vm) -> Key { + Key { + raw: RawKey::from_top(vm), useless: PhantomData, } } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index c8dbecc..76e0a96 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::core::Key; use crate::vm::Vm; //TODO: Try to find a better name. @@ -57,12 +57,12 @@ pub trait Registry: Sized { /// Register this value into the registry. /// /// returns: RegistryKey - fn registry_put(self) -> RegistryKey; + fn registry_put(self) -> Key; /// Swaps the value pointed by `old` in the registry to this value. /// /// # Arguments /// /// * `old`: the old registry key to be replaced. - fn registry_swap(self, old: RegistryKey) -> RegistryKey; + fn registry_swap(self, old: Key) -> Key; } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 6b165b1..f483c77 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -30,7 +30,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::core::Key; use crate::vm::registry::Registry; use crate::vm::table::Table; use crate::vm::util::LuaType; @@ -83,14 +83,14 @@ impl Registry for Table<'_> { type RegistryValue = crate::vm::registry::types::Table; #[inline(always)] - fn registry_put(self) -> RegistryKey { + fn registry_put(self) -> Key { // If the table is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index()); - unsafe { RegistryKey::from_top(self.vm) } + unsafe { Key::from_top(self.vm) } } #[inline(always)] - fn registry_swap(self, old: RegistryKey) -> RegistryKey { + fn registry_swap(self, old: Key) -> Key { // If the table is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index()); unsafe { old.as_raw().replace(self.vm) }; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 23089a6..9f887be 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -31,7 +31,7 @@ use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; use crate::util::core::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::registry::core::RegistryKey; +use crate::vm::registry::core::Key; use crate::vm::registry::Registry; use crate::vm::util::LuaType; use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; @@ -136,14 +136,14 @@ impl Registry for Function<'_> { type RegistryValue = crate::vm::registry::types::Function; #[inline(always)] - fn registry_put(self) -> RegistryKey { + fn registry_put(self) -> Key { // If the function is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index); - unsafe { RegistryKey::from_top(self.vm) } + unsafe { Key::from_top(self.vm) } } #[inline(always)] - fn registry_swap(self, old: RegistryKey) -> RegistryKey { + fn registry_swap(self, old: Key) -> Key { // If the function is not at the top of the stack, move it to the top. ensure_value_top(self.vm, self.index); unsafe { old.as_raw().replace(self.vm) }; From b9fd5cdfe19a637396186e64b3b60d5857d6fb93 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 20:19:40 +0200 Subject: [PATCH 283/527] Renamed RegistryValue to Value --- core/src/vm/registry/core.rs | 10 ++++++---- core/src/vm/registry/interface.rs | 6 +++--- core/src/vm/registry/mod.rs | 1 + core/src/vm/registry/types.rs | 10 +++++----- 4 files changed, 15 insertions(+), 12 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 895402d..f815efc 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -28,7 +28,7 @@ use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; -use crate::vm::registry::RegistryValue; +use crate::vm::registry::Value; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; @@ -123,7 +123,7 @@ pub struct Key { useless: PhantomData<*const T>, } -impl Key { +impl Key { /// Pushes the lua value associated to this registry key on the lua stack. /// /// # Arguments @@ -133,8 +133,10 @@ impl Key { /// returns: ::Value #[inline(always)] pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { - unsafe { self.raw.push(vm) }; - unsafe { T::to_lua_value(vm, -1) } + unsafe { + self.raw.push(vm); + T::from_lua(vm, -1) + } } /// Pushes the lua value associated to this registry key on the lua stack. diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 76e0a96..f1adfee 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -31,7 +31,7 @@ use crate::vm::Vm; //TODO: Try to find a better name. -pub trait RegistryValue: 'static { +pub trait Value: 'static { type Value<'a>; /// Reads the upvalue at the given location on the lua stack. @@ -47,12 +47,12 @@ pub trait RegistryValue: 'static { /// /// This function assumes the value at the top of the stack is of type `Self`. This function is /// UB otherwise. - unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_>; + unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_>; } /// A trait to produce registry values safely. pub trait Registry: Sized { - type RegistryValue: RegistryValue; + type RegistryValue: Value; /// Register this value into the registry. /// diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs index 0a53de9..b74383c 100644 --- a/core/src/vm/registry/mod.rs +++ b/core/src/vm/registry/mod.rs @@ -29,5 +29,6 @@ pub mod core; mod interface; pub mod types; +pub mod named; pub use interface::*; diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 19af682..d60875a 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,27 +26,27 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::registry::RegistryValue; +use crate::vm::registry::Value; use crate::vm::value::FromLua; use crate::vm::Vm; pub struct Table; pub struct Function; -impl RegistryValue for Table { +impl Value for Table { type Value<'a> = crate::vm::table::Table<'a>; #[inline(always)] - unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_> { unsafe { crate::vm::table::Table::from_lua_unchecked(vm, index) } } } -impl RegistryValue for Function { +impl Value for Function { type Value<'a> = crate::vm::value::Function<'a>; #[inline(always)] - unsafe fn to_lua_value(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_> { unsafe { crate::vm::value::Function::from_lua_unchecked(vm, index) } } } From deedf0a4f54d002e59b89be4209f850b0233414a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 20:35:20 +0200 Subject: [PATCH 284/527] Added WIP named registry key system --- core/src/vm/registry/core.rs | 3 +- core/src/vm/registry/interface.rs | 3 +- core/src/vm/registry/named.rs | 140 ++++++++++++++++++++++++++++++ 3 files changed, 144 insertions(+), 2 deletions(-) create mode 100644 core/src/vm/registry/named.rs diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index f815efc..edf8160 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -33,7 +33,8 @@ use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; -//TODO: Check if key can be a NonZeroI32 +//TODO: Check if possible to implement from_top as a safe function. +//TODO: Check if key can be a NonZeroI32. #[derive(Debug, Copy, Clone)] #[repr(transparent)] diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index f1adfee..9642916 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -27,12 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::vm::registry::core::Key; +use crate::vm::value::IntoLua; use crate::vm::Vm; //TODO: Try to find a better name. pub trait Value: 'static { - type Value<'a>; + type Value<'a>: IntoLua; /// Reads the upvalue at the given location on the lua stack. /// diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs new file mode 100644 index 0000000..291050a --- /dev/null +++ b/core/src/vm/registry/named.rs @@ -0,0 +1,140 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::c_void; +use std::marker::PhantomData; +use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, REGISTRYINDEX}; +use crate::vm::registry::Value; +use crate::vm::value::IntoLua; +use crate::vm::Vm; + +#[derive(Debug, Copy, Clone)] +pub struct RawKey(*const c_void); + +impl RawKey { + /// Sets the value for this key to the value on top of the given lua stack. + /// This function pops the value on top of the stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to manipulate. + /// + /// # Safety + /// + /// This function assumes that the value expected to be inserted in the registry is on top of + /// the stack. This function also assumes that the value on top of the stack does not have any + /// references to it anymore. + pub unsafe fn set(&self, vm: &Vm) { + let l = vm.as_ptr(); + lua_pushlightuserdata(l, self.0 as _); + lua_insert(l, -2); // Move key after value; + lua_rawset(l, REGISTRYINDEX); + } + + /// Pushes the value associated with this key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to manipulate. + /// + /// returns: () + pub fn push(&self, vm: &Vm) { + let l = vm.as_ptr(); + unsafe { + lua_pushlightuserdata(l, self.0 as _); + lua_rawget(l, REGISTRYINDEX); + } + } + + pub const fn new(name: &str) -> Self { + //TODO: implement hash function. + todo!() + } +} + +pub struct Key { + raw: RawKey, + useless: PhantomData<*const T>, +} + +impl Key { + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + #[inline(always)] + pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { + self.raw.push(vm); + unsafe { T::from_lua(vm, -1) } + } + + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + #[inline(always)] + pub fn as_raw(&self) -> RawKey { + self.raw + } + + /// Deletes this registry key from the specified [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to unregister from. + /// + /// returns: () + #[inline(always)] + pub fn delete(self, vm: &Vm) { + unsafe { + lua_pushnil(vm.as_ptr()); + self.raw.set(vm); + } + } + + /// Sets the value for this key. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to manipulate. + /// * `value`: the value to replace with. + /// + /// returns: () + pub fn set(&self, vm: &Vm, value: T::Value<'_>) { + value.into_lua(vm); + unsafe { self.raw.set(vm); } + } + + //TODO: new function +} From 29966fbf24c616de9706f62a10a6916c6d9b79b6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 22:28:37 +0200 Subject: [PATCH 285/527] Large refactor of registry system and added initial version of named registry key --- core/src/util/function.rs | 3 +- core/src/util/method.rs | 5 +-- core/src/util/namespace.rs | 6 +-- core/src/vm/registry/core.rs | 37 +++++++++++++--- core/src/vm/registry/interface.rs | 70 ++++++++++++++++++++++++------- core/src/vm/registry/named.rs | 65 +++++++++++++++------------- core/src/vm/registry/types.rs | 22 ---------- core/src/vm/table/interface.rs | 27 ++++++------ core/src/vm/value/function.rs | 33 ++++++++------- 9 files changed, 159 insertions(+), 109 deletions(-) diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 14605a6..43c916b 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -31,14 +31,13 @@ use crate::vm::registry::core::Key; use crate::vm::registry::types::Function; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; -use crate::vm::registry::Registry; /// This represents a Lua callback. pub struct LuaFunction(Key); impl LuaFunction { pub fn create(f: crate::vm::value::Function) -> Self { - Self(f.registry_put()) + Self(Key::new(f)) } pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { diff --git a/core/src/util/method.rs b/core/src/util/method.rs index b32b538..2be1e61 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -29,7 +29,6 @@ use crate::util::core::AnyStr; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::Key; -use crate::vm::registry::Registry; use crate::vm::registry::types::{Function, Table}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; @@ -42,9 +41,7 @@ pub struct LuaMethod { impl LuaMethod { pub fn create(obj: crate::vm::table::Table, method_name: impl AnyStr) -> crate::vm::Result { let method: crate::vm::value::Function = obj.get_field(method_name)?; - let method = method.registry_put(); - let obj = obj.registry_put(); - Ok(Self { obj, method }) + Ok(Self { method: Key::new(method), obj: Key::new(obj) }) } pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 13416e3..c1b957c 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::lua_settop; -use crate::vm::registry::Registry; +use crate::vm::registry::core::Key; use crate::vm::table::Table; use crate::vm::value::IntoLua; use crate::vm::Vm; @@ -43,7 +43,7 @@ impl<'a> Namespace<'a> { table: Table<'a>, names: impl Iterator, ) -> crate::vm::Result { - let mut key = table.registry_put(); + let key = Key::::new(table); let key = vm.scope(|vm| { for name in names { let mut table = key.push(vm); @@ -55,7 +55,7 @@ impl<'a> Namespace<'a> { table.get_field(name)? } }; - key = tab.registry_swap(key); + key.set(tab); } Ok(key) })?; diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index edf8160..e20b33c 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -28,11 +28,11 @@ use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; -use crate::vm::registry::Value; +use crate::vm::registry::{FromIndex, Set, Value}; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; - +use crate::vm::value::util::ensure_value_top; //TODO: Check if possible to implement from_top as a safe function. //TODO: Check if key can be a NonZeroI32. @@ -85,7 +85,7 @@ impl RawKey { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.0); } - /// Replaces the content of this key with the value on top of the stack. + /// Sets the content of this key with the value on top of the stack. /// /// # Arguments /// @@ -97,7 +97,7 @@ impl RawKey { /// /// This is UB to call if the key has already been deleted. #[inline(always)] - pub unsafe fn replace(&self, vm: &Vm) { + pub unsafe fn set(&self, vm: &Vm) { lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); } @@ -119,6 +119,20 @@ impl RawKey { } } +impl FromIndex for RawKey { + unsafe fn from_index(vm: &Vm, index: i32) -> Self { + ensure_value_top(vm, index); + Self::from_top(vm) + } +} + +impl Set for RawKey { + unsafe fn set(&self, vm: &Vm, index: i32) { + ensure_value_top(vm, index); + self.set(vm); + } +} + pub struct Key { raw: RawKey, useless: PhantomData<*const T>, @@ -136,7 +150,7 @@ impl Key { pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { unsafe { self.raw.push(vm); - T::from_lua(vm, -1) + T::from_registry(vm, -1) } } @@ -182,4 +196,17 @@ impl Key { useless: PhantomData, } } + + #[inline(always)] + pub fn new(value: T::Value<'_>) -> Key { + Key { + raw: T::push_registry(value), + useless: PhantomData + } + } + + #[inline(always)] + pub fn set(&self, value: T::Value<'_>) { + unsafe { T::set_registry(&self.raw, value) } + } } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 9642916..4a47b12 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -26,14 +26,50 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::registry::core::Key; -use crate::vm::value::IntoLua; use crate::vm::Vm; //TODO: Try to find a better name. +/// This trait represents a generic key which can be constructed from an index on the lua stack. +pub trait FromIndex { + /// Constructs a new instance of this generic key from the given vm and index. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to manipulate. + /// * `index`: the index of the value on the lua stack. + /// + /// returns: Self + /// + /// # Safety + /// + /// This function removes the value at index `index` and so assumes no more references exists + /// to it, failure to ensure this is UB. + unsafe fn from_index(vm: &Vm, index: i32) -> Self; + +} + +/// This trait represents a generic key which can be set from an index on the lua stack. +pub trait Set { + /// Sets the value of this generic key from the given vm and index. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to manipulate. + /// * `index`: the index of the value on the lua stack. + /// + /// returns: () + /// + /// # Safety + /// + /// This function removes the value at index `index` and so assumes no more references exists + /// to it, failure to ensure this is UB. The function also assumes this generic key still + /// exists in the registry table. + unsafe fn set(&self, vm: &Vm, index: i32); +} + pub trait Value: 'static { - type Value<'a>: IntoLua; + type Value<'a>; /// Reads the upvalue at the given location on the lua stack. /// @@ -48,22 +84,26 @@ pub trait Value: 'static { /// /// This function assumes the value at the top of the stack is of type `Self`. This function is /// UB otherwise. - unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_>; -} + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_>; -/// A trait to produce registry values safely. -pub trait Registry: Sized { - type RegistryValue: Value; - - /// Register this value into the registry. + /// Intializes a new generic key from the given value. /// - /// returns: RegistryKey - fn registry_put(self) -> Key; + /// This function should call R::from_index with a matching index and [Vm] instance. + fn push_registry(value: Self::Value<'_>) -> R; - /// Swaps the value pointed by `old` in the registry to this value. + /// Assign this value to the given generic registry key. + /// + /// This function should call key.set with a matching index and [Vm] instance. /// /// # Arguments /// - /// * `old`: the old registry key to be replaced. - fn registry_swap(self, old: Key) -> Key; + /// * `key`: the key to update. + /// * `value`: the new value. + /// + /// returns: () + /// + /// # Safety + /// + /// This function assumes the generic key still exists in the registry table. + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>); } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 291050a..9ebe6b6 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -29,33 +29,14 @@ use std::ffi::c_void; use std::marker::PhantomData; use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, REGISTRYINDEX}; -use crate::vm::registry::Value; -use crate::vm::value::IntoLua; +use crate::vm::registry::{Set, Value}; +use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; #[derive(Debug, Copy, Clone)] pub struct RawKey(*const c_void); impl RawKey { - /// Sets the value for this key to the value on top of the given lua stack. - /// This function pops the value on top of the stack. - /// - /// # Arguments - /// - /// * `vm`: the [Vm] instance to manipulate. - /// - /// # Safety - /// - /// This function assumes that the value expected to be inserted in the registry is on top of - /// the stack. This function also assumes that the value on top of the stack does not have any - /// references to it anymore. - pub unsafe fn set(&self, vm: &Vm) { - let l = vm.as_ptr(); - lua_pushlightuserdata(l, self.0 as _); - lua_insert(l, -2); // Move key after value; - lua_rawset(l, REGISTRYINDEX); - } - /// Pushes the value associated with this key on the lua stack. /// /// # Arguments @@ -72,8 +53,30 @@ impl RawKey { } pub const fn new(name: &str) -> Self { - //TODO: implement hash function. - todo!() + // This is a re-write of https://github.com/BPXFormat/bpx-rs/blob/develop/src/hash.rs + // in const context. + let mut val: u64 = 5381; + let bytes = name.as_bytes(); + // Unreadable algorithm: see the hash loop in bpx-rs for the readable variant. + let mut i = 0; + while i != bytes.len() { + let temp1 = val.wrapping_shl(5); + let temp2 = temp1.wrapping_add(val); + val = temp2.wrapping_add(bytes[i] as u64); + i += 1; + } + // And now a hack to turn u64 into ptr (btw, do NOT dereference it). + Self(val as usize as *const c_void) + } +} + +impl Set for RawKey { + unsafe fn set(&self, vm: &Vm, index: i32) { + let l = vm.as_ptr(); + ensure_value_top(vm, index); + lua_pushlightuserdata(l, self.0 as _); + lua_insert(l, -2); // Move key after value; + lua_rawset(l, REGISTRYINDEX); } } @@ -93,7 +96,7 @@ impl Key { #[inline(always)] pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { self.raw.push(vm); - unsafe { T::from_lua(vm, -1) } + unsafe { T::from_registry(vm, -1) } } /// Pushes the lua value associated to this registry key on the lua stack. @@ -119,7 +122,7 @@ impl Key { pub fn delete(self, vm: &Vm) { unsafe { lua_pushnil(vm.as_ptr()); - self.raw.set(vm); + self.raw.set(vm, -1); } } @@ -127,14 +130,16 @@ impl Key { /// /// # Arguments /// - /// * `vm`: the [Vm] instance to manipulate. /// * `value`: the value to replace with. /// /// returns: () - pub fn set(&self, vm: &Vm, value: T::Value<'_>) { - value.into_lua(vm); - unsafe { self.raw.set(vm); } + pub fn set(&self, value: T::Value<'_>) { + unsafe { T::set_registry(&self.raw, value) }; } - //TODO: new function + pub fn new(raw_key: RawKey, value: T::Value<'_>) -> Self { + let key = Key { raw: raw_key, useless: PhantomData }; + key.set(value); + key + } } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index d60875a..42e57e0 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,27 +26,5 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::vm::registry::Value; -use crate::vm::value::FromLua; -use crate::vm::Vm; - pub struct Table; pub struct Function; - -impl Value for Table { - type Value<'a> = crate::vm::table::Table<'a>; - - #[inline(always)] - unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { crate::vm::table::Table::from_lua_unchecked(vm, index) } - } -} - -impl Value for Function { - type Value<'a> = crate::vm::value::Function<'a>; - - #[inline(always)] - unsafe fn from_lua(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { crate::vm::value::Function::from_lua_unchecked(vm, index) } - } -} diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index f483c77..f503671 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -30,11 +30,10 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::registry::core::Key; -use crate::vm::registry::Registry; +use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::Table; use crate::vm::util::LuaType; -use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::value::util::ensure_type_equals; use crate::vm::value::FromLua; use crate::vm::Vm; @@ -79,21 +78,21 @@ unsafe impl IntoParam for Table<'_> { impl LuaType for Table<'_> {} -impl Registry for Table<'_> { - type RegistryValue = crate::vm::registry::types::Table; +impl crate::vm::registry::Value for crate::vm::registry::types::Table { + type Value<'a> = Table<'a>; #[inline(always)] - fn registry_put(self) -> Key { - // If the table is not at the top of the stack, move it to the top. - ensure_value_top(self.vm, self.index()); - unsafe { Key::from_top(self.vm) } + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe { Table::from_lua_unchecked(vm, index) } } #[inline(always)] - fn registry_swap(self, old: Key) -> Key { - // If the table is not at the top of the stack, move it to the top. - ensure_value_top(self.vm, self.index()); - unsafe { old.as_raw().replace(self.vm) }; - old + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index()) } + } + + #[inline(always)] + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index()) } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 9f887be..4f725b7 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -31,10 +31,9 @@ use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; use crate::util::core::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::registry::core::Key; -use crate::vm::registry::Registry; +use crate::vm::registry::{FromIndex, Set}; use crate::vm::util::LuaType; -use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::value::util::ensure_type_equals; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -112,6 +111,12 @@ impl Function<'_> { unsafe { pcall(self.vm, num_values as _, R::num_values() as _, pos)? }; R::from_lua(self.vm, -(R::num_values() as i32)) } + + /// Returns the absolute index of this function on the Lua stack. + #[inline(always)] + pub fn index(&self) -> i32 { + self.index + } } impl<'a> FromLua<'a> for Function<'a> { @@ -132,21 +137,21 @@ impl<'a> FromLua<'a> for Function<'a> { } } -impl Registry for Function<'_> { - type RegistryValue = crate::vm::registry::types::Function; +impl crate::vm::registry::Value for crate::vm::registry::types::Function { + type Value<'a> = Function<'a>; + + #[inline(always)] + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe { Function::from_lua_unchecked(vm, index) } + } #[inline(always)] - fn registry_put(self) -> Key { - // If the function is not at the top of the stack, move it to the top. - ensure_value_top(self.vm, self.index); - unsafe { Key::from_top(self.vm) } + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index()) } } #[inline(always)] - fn registry_swap(self, old: Key) -> Key { - // If the function is not at the top of the stack, move it to the top. - ensure_value_top(self.vm, self.index); - unsafe { old.as_raw().replace(self.vm) }; - old + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index()) } } From 2af2d1b3d88bcd85c40ed8aab58ae36854c58c38 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 22:30:16 +0200 Subject: [PATCH 286/527] Removed comments --- core/src/vm/registry/core.rs | 2 +- core/src/vm/registry/interface.rs | 2 -- core/src/vm/registry/named.rs | 1 + 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index e20b33c..57a86fe 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -33,7 +33,7 @@ use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; use crate::vm::value::util::ensure_value_top; -//TODO: Check if possible to implement from_top as a safe function. + //TODO: Check if key can be a NonZeroI32. #[derive(Debug, Copy, Clone)] diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 4a47b12..bbd89e1 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -28,8 +28,6 @@ use crate::vm::Vm; -//TODO: Try to find a better name. - /// This trait represents a generic key which can be constructed from an index on the lua stack. pub trait FromIndex { /// Constructs a new instance of this generic key from the given vm and index. diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 9ebe6b6..701f58a 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -133,6 +133,7 @@ impl Key { /// * `value`: the value to replace with. /// /// returns: () + #[inline(always)] pub fn set(&self, value: T::Value<'_>) { unsafe { T::set_registry(&self.raw, value) }; } From ba0d4f2cb5bd8eec64c4d9df24a232fce1be4c98 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 22:56:10 +0200 Subject: [PATCH 287/527] Added security to panic when a named key is already registered --- core/src/vm/registry/named.rs | 54 +++++++++++++++++++++++++++++++---- 1 file changed, 49 insertions(+), 5 deletions(-) diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 701f58a..942ae26 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -28,14 +28,17 @@ use std::ffi::c_void; use std::marker::PhantomData; -use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, REGISTRYINDEX}; +use crate::ffi::lua::{lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, lua_settop, lua_type, Type, REGISTRYINDEX}; use crate::vm::registry::{Set, Value}; use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct RawKey(*const c_void); +unsafe impl Send for RawKey {} +unsafe impl Sync for RawKey {} + impl RawKey { /// Pushes the value associated with this key on the lua stack. /// @@ -80,6 +83,48 @@ impl Set for RawKey { } } +struct InitKey(*const c_void); + +const USED_KEYS: RawKey = RawKey::new("__used_keys__"); + +unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { + if key == USED_KEYS.0 { + panic!("Attempt to use reserved named key __used_keys__"); + } + let l = vm.as_ptr(); + USED_KEYS.push(vm); + if lua_type(l, -1) != Type::Table { + lua_settop(l, -2); // Clear nil/none from the top of the stack. + lua_createtable(l, 0, 0); + USED_KEYS.set(vm, -1); // Pop the table and set it in the registry. + USED_KEYS.push(vm); // Re-push the table so that following code can use it. + } + lua_pushlightuserdata(l, key as _); + lua_rawget(l, -2); // Table is now at index -2 on the stack. + let ty = lua_type(l, -1); + lua_settop(l, -2); // Remove value from stack. + if ty == Type::Boolean { + // Key is already taken, this is bad. + panic!("Attempt to register an already used named key"); + } + lua_pushlightuserdata(l, key as _); + lua_pushboolean(l, 1); + lua_rawset(l, -3); // Table is now at -3 on the stack because we've just pushed a key and a + // value. + lua_settop(l, -2); // Clear the used keys table from the stack. +} + +impl Set for InitKey { + unsafe fn set(&self, vm: &Vm, index: i32) { + check_key_already_used(vm, self.0); + let l = vm.as_ptr(); + ensure_value_top(vm, index); + lua_pushlightuserdata(l, self.0 as _); + lua_insert(l, -2); // Move key after value; + lua_rawset(l, REGISTRYINDEX); + } +} + pub struct Key { raw: RawKey, useless: PhantomData<*const T>, @@ -139,8 +184,7 @@ impl Key { } pub fn new(raw_key: RawKey, value: T::Value<'_>) -> Self { - let key = Key { raw: raw_key, useless: PhantomData }; - key.set(value); - key + unsafe { T::set_registry(&InitKey(raw_key.0), value) }; + Key { raw: raw_key, useless: PhantomData } } } From d2a85bfd0d2698546bb051ef16e110b90cbf4a0f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 23:56:52 +0200 Subject: [PATCH 288/527] Fixed HUGE crash in context tool --- core/src/vm/closure/context.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index e14c9b3..cc92c0b 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -100,6 +100,7 @@ impl Context { pub fn new(vm: &Vm) -> Self { let (ptr, key) = unsafe { let ptr = lua_newuserdata(vm.as_ptr(), 8); + std::ptr::write(ptr as *mut u64, 0); (ptr, RawKey::from_top(vm)) }; Self { From 76d5127da2cd79953bd4a836c1fc0a6e56450cda Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 24 Apr 2025 23:57:54 +0200 Subject: [PATCH 289/527] Fixed various bugs with named Key/RawKey and use new RawKey API in destructor --- core/src/vm/core/destructor.rs | 34 +++++++++++++------------------ core/src/vm/registry/interface.rs | 3 ++- core/src/vm/registry/named.rs | 24 ++++++++++++++-------- core/src/vm/value/util.rs | 1 + 4 files changed, 33 insertions(+), 29 deletions(-) diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 6e25bb8..973e0a9 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -26,15 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{ - lua_gettable, lua_pushlightuserdata, lua_pushstring, lua_settable, lua_settop, lua_touserdata, - REGISTRYINDEX, -}; +use crate::ffi::lua::{lua_pushlightuserdata, lua_settop, lua_touserdata}; use crate::vm::Vm; use bp3d_debug::debug; use std::rc::Rc; - -//TODO: Use rawgetp/rawsetp to manage the pointer to the destructor. +use crate::vm::registry::named::RawKey; +use crate::vm::registry::Set; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { @@ -75,6 +72,8 @@ impl Raw for Rc { } } +const DESTRUCTOR_POOL: RawKey = RawKey::new("__destructor_pool__"); + #[derive(Default)] pub struct Pool { leaked: Vec>, @@ -91,14 +90,12 @@ impl Pool { /// /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. pub unsafe fn new_in_vm(vm: &mut Vm) { + DESTRUCTOR_POOL.register(vm); let l = vm.as_ptr(); let b = Box::leak(Box::new(Pool::new())); - unsafe { - lua_pushstring(l, c"__destructor_pool__".as_ptr()); - let ptr = b as *mut Pool as _; - lua_pushlightuserdata(l, ptr); - lua_settable(l, REGISTRYINDEX); - }; + let ptr = b as *mut Pool as _; + lua_pushlightuserdata(l, ptr); + DESTRUCTOR_POOL.set(vm, -1); } /// Extracts a destructor pool from the given [Vm]. @@ -108,14 +105,11 @@ impl Pool { /// The returned reference must not be aliased. unsafe fn _from_vm(vm: &Vm) -> *mut Self { let l = vm.as_ptr(); - unsafe { - lua_pushstring(l, c"__destructor_pool__".as_ptr()); - lua_gettable(l, REGISTRYINDEX); - let ptr = lua_touserdata(l, -1) as *mut Pool; - assert!(!ptr.is_null()); - lua_settop(l, -2); // Remove the pointer from the lua stack. - ptr - } + DESTRUCTOR_POOL.push(vm); + let ptr = lua_touserdata(l, -1) as *mut Pool; + assert!(!ptr.is_null()); + lua_settop(l, -2); // Remove the pointer from the lua stack. + ptr } pub fn from_vm(vm: &mut Vm) -> &mut Self { diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index bbd89e1..6abff38 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -62,7 +62,8 @@ pub trait Set { /// /// This function removes the value at index `index` and so assumes no more references exists /// to it, failure to ensure this is UB. The function also assumes this generic key still - /// exists in the registry table. + /// exists in the registry table. Finally, this assumes this key does not conflict with a + /// different one. unsafe fn set(&self, vm: &Vm, index: i32); } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 942ae26..1d42b04 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -28,7 +28,7 @@ use std::ffi::c_void; use std::marker::PhantomData; -use crate::ffi::lua::{lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, lua_settop, lua_type, Type, REGISTRYINDEX}; +use crate::ffi::lua::{lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; use crate::vm::registry::{Set, Value}; use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; @@ -55,6 +55,12 @@ impl RawKey { } } + /// Attempts to register this key with the given [Vm] instance. This function ensures the key + /// does not collide. + pub fn register(&self, vm: &Vm) { + unsafe { check_key_already_used(vm, self.0) }; + } + pub const fn new(name: &str) -> Self { // This is a re-write of https://github.com/BPXFormat/bpx-rs/blob/develop/src/hash.rs // in const context. @@ -73,13 +79,17 @@ impl RawKey { } } +unsafe fn rawsetp(l: State, idx: i32, key: *const c_void) { + lua_pushlightuserdata(l, key as _); + lua_insert(l, -2); // Move key after value; + lua_rawset(l, idx); +} + impl Set for RawKey { unsafe fn set(&self, vm: &Vm, index: i32) { let l = vm.as_ptr(); ensure_value_top(vm, index); - lua_pushlightuserdata(l, self.0 as _); - lua_insert(l, -2); // Move key after value; - lua_rawset(l, REGISTRYINDEX); + rawsetp(l, REGISTRYINDEX, self.0); } } @@ -94,7 +104,7 @@ unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { let l = vm.as_ptr(); USED_KEYS.push(vm); if lua_type(l, -1) != Type::Table { - lua_settop(l, -2); // Clear nil/none from the top of the stack. + lua_settop(l, -2); // Clear nil from the top of the stack. lua_createtable(l, 0, 0); USED_KEYS.set(vm, -1); // Pop the table and set it in the registry. USED_KEYS.push(vm); // Re-push the table so that following code can use it. @@ -119,9 +129,7 @@ impl Set for InitKey { check_key_already_used(vm, self.0); let l = vm.as_ptr(); ensure_value_top(vm, index); - lua_pushlightuserdata(l, self.0 as _); - lua_insert(l, -2); // Move key after value; - lua_rawset(l, REGISTRYINDEX); + rawsetp(l, REGISTRYINDEX, self.0); } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 92d2125..b238a15 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -51,6 +51,7 @@ pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Res /// replaces the original index by a nil value. #[inline(always)] pub fn ensure_value_top(vm: &Vm, index: i32) { + let index = vm.get_absolute_index(index); if index != vm.top() { let l = vm.as_ptr(); unsafe { From 96a899b972cadf3bc818613a86616068d1e0b125 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 25 Apr 2025 21:41:41 +0200 Subject: [PATCH 290/527] Redesign and simplified named registry key system --- core/src/util/function.rs | 1 - core/src/vm/registry/named.rs | 56 +++++++++++++++++++++++++++-------- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 826b50c..3c953c9 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -29,7 +29,6 @@ use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::registry::core::Key; use crate::vm::registry::types::Function; -use crate::vm::registry::Registry; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 1d42b04..24a47db 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::cell::Cell; use std::ffi::c_void; use std::marker::PhantomData; use crate::ffi::lua::{lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; @@ -47,7 +48,12 @@ impl RawKey { /// * `vm`: the [Vm] instance to manipulate. /// /// returns: () - pub fn push(&self, vm: &Vm) { + /// + /// # Safety + /// + /// This is UB to call if the key was not registered in the given [Vm] using + /// [register](RawKey::register). + pub unsafe fn push(&self, vm: &Vm) { let l = vm.as_ptr(); unsafe { lua_pushlightuserdata(l, self.0 as _); @@ -57,6 +63,11 @@ impl RawKey { /// Attempts to register this key with the given [Vm] instance. This function ensures the key /// does not collide. + /// + /// # Panic + /// + /// This function panics if this key is already registered in the given [Vm]. + #[inline(always)] pub fn register(&self, vm: &Vm) { unsafe { check_key_already_used(vm, self.0) }; } @@ -135,10 +146,19 @@ impl Set for InitKey { pub struct Key { raw: RawKey, + registered: Cell, useless: PhantomData<*const T>, } impl Key { + #[inline(always)] + fn ensure_registered(&self, vm: &Vm) { + if !self.registered.get() { + unsafe { check_key_already_used(vm, self.raw.0) }; + self.registered.set(true); + } + } + /// Pushes the lua value associated to this registry key on the lua stack. /// /// # Arguments @@ -146,10 +166,16 @@ impl Key { /// * `vm`: the [Vm] to attach the produced lua value to. /// /// returns: ::Value - #[inline(always)] - pub fn push<'a>(&self, vm: &'a Vm) -> T::Value<'a> { - self.raw.push(vm); - unsafe { T::from_registry(vm, -1) } + pub fn push<'a>(&self, vm: &'a Vm) -> Option> { + self.ensure_registered(vm); + unsafe { + self.raw.push(vm); + if lua_type(vm.as_ptr(), -1) == Type::Nil { + lua_settop(vm.as_ptr(), -2); + return None; + } + Some(T::from_registry(vm, -1)) + } } /// Pushes the lua value associated to this registry key on the lua stack. @@ -164,7 +190,7 @@ impl Key { self.raw } - /// Deletes this registry key from the specified [Vm]. + /// Resets the value pointed to by this registry key from the specified [Vm]. /// /// # Arguments /// @@ -172,7 +198,7 @@ impl Key { /// /// returns: () #[inline(always)] - pub fn delete(self, vm: &Vm) { + pub fn reset(&self, vm: &Vm) { unsafe { lua_pushnil(vm.as_ptr()); self.raw.set(vm, -1); @@ -186,13 +212,19 @@ impl Key { /// * `value`: the value to replace with. /// /// returns: () - #[inline(always)] pub fn set(&self, value: T::Value<'_>) { - unsafe { T::set_registry(&self.raw, value) }; + if self.registered.get() { + unsafe { T::set_registry(&InitKey(self.raw.0), value) }; + } else { + unsafe { T::set_registry(&self.raw, value) }; + } } - pub fn new(raw_key: RawKey, value: T::Value<'_>) -> Self { - unsafe { T::set_registry(&InitKey(raw_key.0), value) }; - Key { raw: raw_key, useless: PhantomData } + pub const fn new(name: &str) -> Self { + Key { + raw: RawKey::new(name), + registered: Cell::new(false), + useless: PhantomData + } } } From 008ca7141128bb4e24af57d0e1eee06d09afd5c2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 25 Apr 2025 21:46:40 +0200 Subject: [PATCH 291/527] Added Clone, Eq and PartialEq implementation to Key --- core/src/vm/registry/named.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 24a47db..f4b1587 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -144,12 +144,21 @@ impl Set for InitKey { } } +#[derive(Clone)] pub struct Key { raw: RawKey, registered: Cell, useless: PhantomData<*const T>, } +impl PartialEq for Key { + fn eq(&self, other: &Self) -> bool { + self.raw.eq(&other.raw) + } +} + +impl Eq for Key { } + impl Key { #[inline(always)] fn ensure_registered(&self, vm: &Vm) { From b2d3e44596a196e46e1fac43cc94ff0fc181a6c7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 25 Apr 2025 22:42:13 +0200 Subject: [PATCH 292/527] Refactored and simplified current userdata implementation --- core/src/macro/userdata.rs | 4 +-- core/src/macro/userdata_func.rs | 24 ++++++------- core/src/vm/userdata/core.rs | 61 ++++++++++++++++++++------------- 3 files changed, 51 insertions(+), 38 deletions(-) diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index b7d68ca..ce04c10 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -34,8 +34,8 @@ macro_rules! _impl_userdata { fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { $( - let (name, func) = unsafe { $obj_name::$fn_name().build()? }; - registry.add_method(name, func); + let f = $obj_name::$fn_name()?; + registry.add_method(f); )* use $crate::vm::userdata::AddGcMethod; (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index ad76a5e..5b169f9 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -32,7 +32,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::core::Function { + $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -46,11 +46,11 @@ macro_rules! decl_userdata_func { } _vmfunc(this_ptr, &vm) } - let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Builder::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? - f + unsafe { f.build() } } } }; @@ -58,7 +58,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::core::Function { + $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -72,11 +72,11 @@ macro_rules! decl_userdata_func { } _vmfunc(this_ptr, &vm) } - let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Builder::new($crate::c_stringify!($fn_name), _cfunc); f.mutable(); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? - f + unsafe { f.build() } } } }; @@ -84,7 +84,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::core::Function { + $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -98,10 +98,10 @@ macro_rules! decl_userdata_func { } _vmfunc(this_ptr, &vm) } - let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Builder::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? - f + unsafe { f.build() } } } }; @@ -109,7 +109,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> $crate::vm::userdata::core::Function { + $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -123,10 +123,10 @@ macro_rules! decl_userdata_func { } _vmfunc(this_ptr, &vm) } - let mut f = $crate::vm::userdata::core::Function::new($crate::c_stringify!($fn_name), _cfunc); + let mut f = $crate::vm::userdata::core::Builder::new($crate::c_stringify!($fn_name), _cfunc); f.arg::<&$obj_name>(); $($(f.arg::<$arg_ty>();)*)? - f + unsafe { f.build() } } } }; diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 3ce896c..2b1feed 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -40,23 +40,27 @@ use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; -//TODO: This should be a builder. -//TODO: The actual function structure should only contain name and CFunction. - +#[derive(Copy, Clone)] pub struct Function { + pub name: &'static CStr, + pub func: CFunction +} + +pub struct Builder { is_mutable: bool, args: Vec, - name: &'static CStr, - func: CFunction, + f: Function } -impl Function { - pub fn new(name: &'static CStr, func: CFunction) -> Function { - Function { +impl Builder { + pub fn new(name: &'static CStr, func: CFunction) -> Builder { + Builder { is_mutable: false, args: Vec::new(), - name, - func, + f: Function { + name, + func + } } } @@ -78,28 +82,28 @@ impl Function { /// /// All function arguments must be added through the arg function, if not calling this function /// is considered UB. - pub unsafe fn build(&self) -> Result<(&'static CStr, CFunction), Error> { + pub unsafe fn build(&self) -> Result { if self.args.is_empty() { return Err(Error::ArgsEmpty); } - if self.name == c"__gc" { + if self.f.name == c"__gc" { return Err(Error::Gc); } - if self.name == c"__index" { + if self.f.name == c"__index" { return Err(Error::Index); } - if self.name == c"__metatable" { + if self.f.name == c"__metatable" { return Err(Error::Metatable); } if self.is_mutable { let initial = &self.args[0]; for v in self.args.iter().skip(1) { if initial == v { - return Err(Error::MutViolation(self.name)); + return Err(Error::MutViolation(self.f.name)); } } } - Ok((self.name, self.func)) + Ok(self.f) } } @@ -156,13 +160,13 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { Ok(()) } - pub fn add_method(&self, name: &'static CStr, func: CFunction) { + pub fn add_method(&self, f: Function) { unsafe { - lua_pushcclosure(self.vm.as_ptr(), func, 0); - if &name.to_bytes()[..2] == b"__" { - lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); + lua_pushcclosure(self.vm.as_ptr(), f.func, 0); + if &f.name.to_bytes()[..2] == b"__" { + lua_setfield(self.vm.as_ptr(), -2, f.name.as_ptr()); } else { - lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); + lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(f.name).as_ptr()); } } } @@ -178,7 +182,10 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { } 0 } - self.add_method(c"__gc", run_drop::); + self.add_method(Function { + name: c"__gc", + func: run_drop:: + }); debug!({UD=?T::CLASS_NAME}, "Type registered with simple Drop"); } self.has_gc.set(()).unwrap(); @@ -207,10 +214,16 @@ impl Registry<'_, T, C> { 0 } if std::mem::needs_drop::() { - self.add_method(c"__gc", run_lua_drop_full::); + self.add_method(Function { + name: c"__gc", + func: run_lua_drop_full:: + }); debug!({UD=?T::CLASS_NAME}, "Type registered with Drop and LuaDrop"); } else { - self.add_method(c"__gc", run_lua_drop::); + self.add_method(Function { + name: c"__gc", + func: run_lua_drop:: + }); debug!({UD=?T::CLASS_NAME}, "Type registered with LuaDrop"); } self.has_gc.set(()).unwrap(); From 56bfb0c8e159a94a3d225d131dd7ade26acae9c9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 25 Apr 2025 21:24:53 +0000 Subject: [PATCH 293/527] Format Rust code using rustfmt --- core/src/util/method.rs | 18 ++++++++++++++---- core/src/vm/core/destructor.rs | 4 ++-- core/src/vm/registry/core.rs | 4 ++-- core/src/vm/registry/interface.rs | 1 - core/src/vm/registry/mod.rs | 2 +- core/src/vm/registry/named.rs | 17 ++++++++++------- core/src/vm/userdata/core.rs | 21 +++++++++++---------- 7 files changed, 40 insertions(+), 27 deletions(-) diff --git a/core/src/util/method.rs b/core/src/util/method.rs index 2be1e61..a605ee5 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -35,16 +35,26 @@ use crate::vm::Vm; pub struct LuaMethod { obj: Key
, - method: Key + method: Key, } impl LuaMethod { - pub fn create(obj: crate::vm::table::Table, method_name: impl AnyStr) -> crate::vm::Result { + pub fn create( + obj: crate::vm::table::Table, + method_name: impl AnyStr, + ) -> crate::vm::Result { let method: crate::vm::value::Function = obj.get_field(method_name)?; - Ok(Self { method: Key::new(method), obj: Key::new(obj) }) + Ok(Self { + method: Key::new(method), + obj: Key::new(obj), + }) } - pub fn call<'a, R: FromLua<'a>>(&self, vm: &'a Vm, value: impl IntoLua) -> crate::vm::Result { + pub fn call<'a, R: FromLua<'a>>( + &self, + vm: &'a Vm, + value: impl IntoLua, + ) -> crate::vm::Result { let pos = unsafe { push_error_handler(vm.as_ptr()) }; unsafe { self.method.as_raw().push(vm) }; unsafe { self.obj.as_raw().push(vm) }; diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 973e0a9..366a807 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -27,11 +27,11 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_pushlightuserdata, lua_settop, lua_touserdata}; +use crate::vm::registry::named::RawKey; +use crate::vm::registry::Set; use crate::vm::Vm; use bp3d_debug::debug; use std::rc::Rc; -use crate::vm::registry::named::RawKey; -use crate::vm::registry::Set; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 57a86fe..8df37f9 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -29,10 +29,10 @@ use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; use crate::vm::registry::{FromIndex, Set, Value}; +use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; -use crate::vm::value::util::ensure_value_top; //TODO: Check if key can be a NonZeroI32. @@ -201,7 +201,7 @@ impl Key { pub fn new(value: T::Value<'_>) -> Key { Key { raw: T::push_registry(value), - useless: PhantomData + useless: PhantomData, } } diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 6abff38..29fe9f8 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -44,7 +44,6 @@ pub trait FromIndex { /// This function removes the value at index `index` and so assumes no more references exists /// to it, failure to ensure this is UB. unsafe fn from_index(vm: &Vm, index: i32) -> Self; - } /// This trait represents a generic key which can be set from an index on the lua stack. diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs index b74383c..067427b 100644 --- a/core/src/vm/registry/mod.rs +++ b/core/src/vm/registry/mod.rs @@ -28,7 +28,7 @@ pub mod core; mod interface; -pub mod types; pub mod named; +pub mod types; pub use interface::*; diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index f4b1587..e449ac0 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -26,13 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; -use std::ffi::c_void; -use std::marker::PhantomData; -use crate::ffi::lua::{lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; +use crate::ffi::lua::{ + lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, + lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX, +}; use crate::vm::registry::{Set, Value}; use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; +use std::cell::Cell; +use std::ffi::c_void; +use std::marker::PhantomData; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct RawKey(*const c_void); @@ -131,7 +134,7 @@ unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { lua_pushlightuserdata(l, key as _); lua_pushboolean(l, 1); lua_rawset(l, -3); // Table is now at -3 on the stack because we've just pushed a key and a - // value. + // value. lua_settop(l, -2); // Clear the used keys table from the stack. } @@ -157,7 +160,7 @@ impl PartialEq for Key { } } -impl Eq for Key { } +impl Eq for Key {} impl Key { #[inline(always)] @@ -233,7 +236,7 @@ impl Key { Key { raw: RawKey::new(name), registered: Cell::new(false), - useless: PhantomData + useless: PhantomData, } } } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 2b1feed..819c072 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -43,13 +43,13 @@ use std::marker::PhantomData; #[derive(Copy, Clone)] pub struct Function { pub name: &'static CStr, - pub func: CFunction + pub func: CFunction, } pub struct Builder { is_mutable: bool, args: Vec, - f: Function + f: Function, } impl Builder { @@ -57,10 +57,7 @@ impl Builder { Builder { is_mutable: false, args: Vec::new(), - f: Function { - name, - func - } + f: Function { name, func }, } } @@ -166,7 +163,11 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { if &f.name.to_bytes()[..2] == b"__" { lua_setfield(self.vm.as_ptr(), -2, f.name.as_ptr()); } else { - lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(f.name).as_ptr()); + lua_setfield( + self.vm.as_ptr(), + -2, + self.case.name_convert(f.name).as_ptr(), + ); } } } @@ -184,7 +185,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { } self.add_method(Function { name: c"__gc", - func: run_drop:: + func: run_drop::, }); debug!({UD=?T::CLASS_NAME}, "Type registered with simple Drop"); } @@ -216,13 +217,13 @@ impl Registry<'_, T, C> { if std::mem::needs_drop::() { self.add_method(Function { name: c"__gc", - func: run_lua_drop_full:: + func: run_lua_drop_full::, }); debug!({UD=?T::CLASS_NAME}, "Type registered with Drop and LuaDrop"); } else { self.add_method(Function { name: c"__gc", - func: run_lua_drop:: + func: run_lua_drop::, }); debug!({UD=?T::CLASS_NAME}, "Type registered with LuaDrop"); } From 0b8f3b0343f0426a7ae67d4ad662ef2fcb1a7223 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 25 Apr 2025 23:31:01 +0200 Subject: [PATCH 294/527] Fixed memory leak in Vm --- core/src/vm/core/vm.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 1a4cb45..e8e8c77 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -237,7 +237,9 @@ impl DerefMut for RootVm { impl Drop for RootVm { fn drop(&mut self) { debug!("Deleting destructor pool"); - unsafe { std::ptr::drop_in_place(Pool::from_vm(self)) }; + unsafe { + drop(Box::from_raw(Pool::from_vm(self))); + } unsafe { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); From c818661aff11df99d4808a0b0aaa4762c0b664e0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Apr 2025 11:10:46 +0200 Subject: [PATCH 295/527] Added new GetTable and SetTable traits to greatly simplify get/set of values in tables --- codegen/src/gen/into_param.rs | 2 +- core/src/libs/lua/base.rs | 2 +- core/src/libs/os/compat.rs | 32 +++++++------- core/src/libs/os/time.rs | 38 ++++++++--------- core/src/libs/util/string.rs | 2 +- core/src/libs/util/table.rs | 10 ++--- core/src/libs/util/utf8.rs | 2 +- core/src/util/method.rs | 2 +- core/src/util/namespace.rs | 8 ++-- core/src/vm/table/core.rs | 41 +++++------------- core/src/vm/table/interface.rs | 41 +++++++++++++++++- core/src/vm/table/mod.rs | 1 + core/src/vm/table/traits.rs | 77 ++++++++++++++++++++++++++++++++++ core/tests/test_vm_tables.rs | 14 +++---- 14 files changed, 183 insertions(+), 89 deletions(-) create mode 100644 core/src/vm/table/traits.rs diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index c2e4efd..9d29adc 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -59,7 +59,7 @@ impl Parser for IntoParam { } else { let name = field.unique_name; quote! { - tbl.set_field(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); + tbl.set(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); } } } diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index d5d0ae6..dc83b8d 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -48,7 +48,7 @@ impl Lib for Base { let mut patches = Table::with_capacity(namespace.vm(), PATCH_LIST.len(), 0); for (i, name) in PATCH_LIST.iter().enumerate() { // Lua indices starts at 1 not 0. - patches.seti((i + 1) as _, *name)?; + patches.set(i + 1, *name)?; } namespace.add([("patches", patches)]) } diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index 6697589..4b6f871 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -102,15 +102,15 @@ decl_lib_func! { if format == "*t" { let std_offset = get_std_offset(); let mut table = Table::new(vm); - table.set_field(c"sec", time.second()).unwrap(); - table.set_field(c"min", time.minute()).unwrap(); - table.set_field(c"hour", time.hour()).unwrap(); - table.set_field(c"day", time.day()).unwrap(); - table.set_field(c"month", time.month() as u8).unwrap(); - table.set_field(c"year", time.year()).unwrap(); - table.set_field(c"wday", time.weekday() as u8 + 1).unwrap(); - table.set_field(c"yday", time.to_julian_day()).unwrap(); - table.set_field(c"isdst", time.offset() < std_offset).unwrap(); + table.set(c"sec", time.second()).unwrap(); + table.set(c"min", time.minute()).unwrap(); + table.set(c"hour", time.hour()).unwrap(); + table.set(c"day", time.day()).unwrap(); + table.set(c"month", time.month() as u8).unwrap(); + table.set(c"year", time.year()).unwrap(); + table.set(c"wday", time.weekday() as u8 + 1).unwrap(); + table.set(c"yday", time.to_julian_day()).unwrap(); + table.set(c"isdst", time.offset() < std_offset).unwrap(); Some(TableOrString::Table(table)) } else { let mut format = String::from(format); @@ -132,21 +132,21 @@ simple_error! { } fn get_time_from_table(table: Table) -> Result { - let year: i32 = table.get_field(c"year")?; - let month: u8 = table.get_field(c"month")?; - let day: u8 = table.get_field(c"day")?; + let year: i32 = table.get(c"year")?; + let month: u8 = table.get(c"month")?; + let day: u8 = table.get(c"day")?; let date = Date::from_calendar_date( year, Month::from_index(month).ok_or(TimeFormatError::InvalidMonthIndex(month))?, day, )?; - let hour: Option = table.get_field(c"hour")?; - let minute: Option = table.get_field(c"min")?; - let second: Option = table.get_field(c"sec")?; + let hour: Option = table.get(c"hour")?; + let minute: Option = table.get(c"min")?; + let second: Option = table.get(c"sec")?; let mut hour = hour.unwrap_or(12); let minute = minute.unwrap_or(0); let second = second.unwrap_or(0); - let dst: Option = table.get_field(c"isdst")?; + let dst: Option = table.get(c"isdst")?; let dst = dst.unwrap_or(false); // Consider DST to be always +1H, this may not always be true but is true in most countries. if dst { diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 493e3d8..921898a 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -82,25 +82,25 @@ decl_userdata! { fn get_date<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); - table.set_field(c"year", this.0.year())?; - table.set_field(c"month", this.0.month() as u8)?; - table.set_field(c"day", this.0.day())?; + table.set(c"year", this.0.year())?; + table.set(c"month", this.0.month() as u8)?; + table.set(c"day", this.0.day())?; Ok(table) } fn get_time<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); - table.set_field(c"hour", this.0.hour())?; - table.set_field(c"minute", this.0.minute())?; - table.set_field(c"second", this.0.second())?; + table.set(c"hour", this.0.hour())?; + table.set(c"minute", this.0.minute())?; + table.set(c"second", this.0.second())?; Ok(table) } fn get_offset<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); - table.set_field(c"hours", this.0.offset().whole_hours())?; - table.set_field(c"minutes", this.0.offset().whole_minutes())?; - table.set_field(c"seconds", this.0.offset().whole_seconds())?; + table.set(c"hours", this.0.offset().whole_hours())?; + table.set(c"minutes", this.0.offset().whole_minutes())?; + table.set(c"seconds", this.0.offset().whole_seconds())?; Ok(table) } } @@ -140,22 +140,22 @@ simple_error! { decl_lib_func! { fn new(table: Table) -> Result { - let year: i32 = table.get_field(c"year")?; - let month: u8 = table.get_field(c"month")?; - let day: u8 = table.get_field(c"day")?; + let year: i32 = table.get(c"year")?; + let month: u8 = table.get(c"month")?; + let day: u8 = table.get(c"day")?; let date = Date::from_calendar_date(year, Month::from_index(month).ok_or(DateTimeError::InvalidMonthIndex(month))?, day)?; - let hour: Option = table.get_field(c"hour")?; - let minute: Option = table.get_field(c"min")?; - let second: Option = table.get_field(c"sec")?; + let hour: Option = table.get(c"hour")?; + let minute: Option = table.get(c"min")?; + let second: Option = table.get(c"sec")?; let hour = hour.unwrap_or(12); let minute = minute.unwrap_or(0); let second = second.unwrap_or(0); let time = time::Time::from_hms(hour, minute, second)?; - let offset: Option
= table.get_field(c"offset")?; + let offset: Option
= table.get(c"offset")?; if let Some(offset) = offset { - let offset_hours: i8 = offset.get_field(c"hours")?; - let offset_minutes: i8 = offset.get_field(c"minutes")?; - let offset_seconds: i8 = offset.get_field(c"seconds")?; + let offset_hours: i8 = offset.get(c"hours")?; + let offset_minutes: i8 = offset.get(c"minutes")?; + let offset_seconds: i8 = offset.get(c"seconds")?; let offset = UtcOffset::from_hms(offset_hours, offset_minutes, offset_seconds)?; Ok(OffsetDateTime::new_in_offset(date, time, offset)) } else { diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 2befd61..4a8d4ee 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -49,7 +49,7 @@ decl_lib_func! { let mut tbl = Table::new(vm); for (i, v) in split.enumerate() { // Indices starts at 1 in lua. - tbl.seti((i + 1) as _, v)?; + tbl.set(i + 1, v)?; } Ok(tbl) } diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index e157062..7e9020f 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -40,18 +40,18 @@ fn update_rec(vm: &Vm, mut dst: LuaTable, mut src: LuaTable) -> crate::vm::Resul let (k, v) = res?; match v { AnyValue::Table(v) => vm.scope(|_| { - let dst1: Option = dst.get(k.clone())?; + let dst1: Option = dst.get_any(k.clone())?; match dst1 { None => { let tbl = LuaTable::new(vm); update_rec(vm, tbl.clone(), v)?; - dst.set(k, tbl)?; + dst.set_any(k, tbl)?; } Some(v1) => update_rec(vm, v1, v)?, } Ok(()) })?, - _ => dst.set(k, v)?, + _ => dst.set_any(k, v)?, } } Ok(()) @@ -155,8 +155,8 @@ decl_lib_func! { fn protect<'a>(vm: &Vm, src: LuaTable) -> crate::vm::Result> { let mut wrapper = LuaTable::new(vm); let mut metatable = LuaTable::new(vm); - metatable.set_field(c"__index", src)?; - metatable.set_field(c"__newindex", RFunction::wrap(__newindex))?; + metatable.set(c"__index", src)?; + metatable.set(c"__newindex", RFunction::wrap(__newindex))?; wrapper.set_metatable(metatable); Ok(wrapper) } diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index 4c9761d..82439da 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -46,7 +46,7 @@ decl_lib_func! { let mut tbl = Table::new(vm); for (i, v) in split.enumerate() { // Indices starts at 1 in lua. - tbl.seti((i + 1) as _, v)?; + tbl.set(i + 1, v)?; } Ok(tbl) } diff --git a/core/src/util/method.rs b/core/src/util/method.rs index a605ee5..b558084 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -43,7 +43,7 @@ impl LuaMethod { obj: crate::vm::table::Table, method_name: impl AnyStr, ) -> crate::vm::Result { - let method: crate::vm::value::Function = obj.get_field(method_name)?; + let method: crate::vm::value::Function = obj.get(method_name)?; Ok(Self { method: Key::new(method), obj: Key::new(obj), diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index c1b957c..80322e9 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -47,12 +47,12 @@ impl<'a> Namespace<'a> { let key = vm.scope(|vm| { for name in names { let mut table = key.push(vm); - let tbl: Option
= table.get_field(name)?; + let tbl: Option
= table.get(name)?; let tab = match tbl { Some(v) => v, None => { - table.set_field(name, Table::new(vm))?; - table.get_field(name)? + table.set(name, Table::new(vm))?; + table.get(name)? } }; key.set(tab); @@ -83,7 +83,7 @@ impl<'a> Namespace<'a> { items: impl IntoIterator, ) -> crate::vm::Result<()> { for (name, item) in items { - self.table.set_field(name, item)?; + self.table.set(name, item)?; } Ok(()) } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index ef3b15d..b5b0512 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,16 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{ - lua_createtable, lua_getfield, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, - lua_rawgeti, lua_rawseti, lua_setfield, lua_setmetatable, lua_settable, lua_topointer, -}; -use crate::util::core::AnyStr; +use crate::ffi::lua::{lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, lua_setmetatable, lua_settable, lua_topointer}; use crate::vm::table::iter::Iter; use crate::vm::value::util::ensure_single_into_lua; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; +use crate::vm::table::traits::{GetTable, SetTable}; pub struct Table<'a> { pub(super) vm: &'a Vm, @@ -143,43 +140,36 @@ impl<'a> Table<'a> { Iter::from_raw(self.vm, self.index) } - pub fn set_field(&mut self, name: impl AnyStr, value: impl IntoLua) -> crate::vm::Result<()> { - unsafe { - ensure_single_into_lua(self.vm, value)?; - lua_setfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); - } - Ok(()) - } - - pub fn get_field<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr) -> crate::vm::Result { + pub fn get<'b, T: FromLua<'b>>(&'b self, key: impl GetTable) -> crate::vm::Result { if T::num_values() != 1 { return Err(crate::vm::error::Error::MultiValue); } unsafe { - lua_getfield(self.vm.as_ptr(), self.index, name.to_str()?.as_ptr()); + key.get_table(self.vm.as_ptr(), self.index)?; T::from_lua(self.vm, -1) } } - pub fn seti(&mut self, i: i32, value: impl IntoLua) -> crate::vm::Result<()> { + pub fn set(&mut self, key: impl SetTable, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { ensure_single_into_lua(self.vm, value)?; - lua_rawseti(self.vm.as_ptr(), self.index, i); + key.set_table(self.vm.as_ptr(), self.index)?; } Ok(()) } - pub fn geti<'b, T: FromLua<'b>>(&'b self, i: i32) -> crate::vm::Result { + pub fn get_any<'b, T: FromLua<'b>>(&'b self, key: impl IntoLua) -> crate::vm::Result { if T::num_values() != 1 { return Err(crate::vm::error::Error::MultiValue); } unsafe { - lua_rawgeti(self.vm.as_ptr(), self.index, i); + ensure_single_into_lua(self.vm, key)?; + lua_gettable(self.vm.as_ptr(), self.index); T::from_lua(self.vm, -1) } } - pub fn set(&mut self, key: impl IntoLua, value: impl IntoLua) -> crate::vm::Result<()> { + pub fn set_any(&mut self, key: impl IntoLua, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { ensure_single_into_lua(self.vm, key)?; ensure_single_into_lua(self.vm, value)?; @@ -188,17 +178,6 @@ impl<'a> Table<'a> { Ok(()) } - pub fn get<'b, T: FromLua<'b>>(&'b self, key: impl IntoLua) -> crate::vm::Result { - if T::num_values() != 1 { - return Err(crate::vm::error::Error::MultiValue); - } - unsafe { - ensure_single_into_lua(self.vm, key)?; - lua_gettable(self.vm.as_ptr(), self.index); - T::from_lua(self.vm, -1) - } - } - pub fn push(&mut self, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let len = lua_objlen(self.vm.as_ptr(), self.index); diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index f503671..9d5013f 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -27,11 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_gettop, lua_pushvalue, lua_type, Type}; -use crate::util::core::SimpleDrop; +use crate::ffi::lua::{lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, State, Type}; +use crate::util::core::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::Table; +use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::util::LuaType; use crate::vm::value::util::ensure_type_equals; use crate::vm::value::FromLua; @@ -96,3 +97,39 @@ impl crate::vm::registry::Value for crate::vm::registry::types::Table { key.set(value.vm, value.index()) } } + +impl GetTable for T { + unsafe fn get_table(self, l: State, index: i32) -> crate::vm::Result<()> { + lua_getfield(l, index, self.to_str()?.as_ptr()); + Ok(()) + } +} + +macro_rules! impl_get_set_table { + ($($t: ty),*) => { + $( + impl GetTable for $t { + unsafe fn get_table(self, l: State, index: i32) -> crate::vm::Result<()> { + lua_rawgeti(l, index, self as _); + Ok(()) + } + } + + impl SetTable for $t { + unsafe fn set_table(self, l: State, index: i32) -> crate::vm::Result<()> { + lua_rawseti(l, index, self as _); + Ok(()) + } + } + )* + }; +} + +impl_get_set_table!(i8, i16, i32, i64, u8, u16, u32, u64, usize, isize); + +impl SetTable for T { + unsafe fn set_table(self, l: State, index: i32) -> crate::vm::Result<()> { + lua_setfield(l, index, self.to_str()?.as_ptr()); + Ok(()) + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 58c9677..41bedce 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -29,5 +29,6 @@ mod core; mod interface; mod iter; +pub mod traits; pub use core::Table; diff --git a/core/src/vm/table/traits.rs b/core/src/vm/table/traits.rs new file mode 100644 index 0000000..0602d33 --- /dev/null +++ b/core/src/vm/table/traits.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::State; + +pub trait GetTable { + /// Attempts to push the value identified by key `self` contained in table at index `index` onto + /// the lua stack. + /// + /// # Arguments + /// + /// * `l`: the lua state. + /// * `index`: the index of the table to read from. + /// + /// returns: () + /// + /// # Errors + /// + /// This returns an [Error](crate::vm::error::Error) if the key identified by `self` is not + /// valid for the table at index `index` on the lua stack. + /// + /// # Safety + /// + /// The lua state pointer `l` must not be NULL and must point to a valid [Vm], and the index + /// `index` must point to a valid [Table] on the lua stack. If any of these invariants are not + /// respected, this function is UB. + unsafe fn get_table(self, l: State, index: i32) -> crate::vm::Result<()>; +} + +pub trait SetTable { + /// Attempts to write the value on top of the lua stack into the key identified by `self` in + /// the table at index `index`. + /// + /// # Arguments + /// + /// * `l`: the lua state. + /// * `index`: the index of the table to read from. + /// + /// returns: () + /// + /// # Errors + /// + /// This returns an [Error](crate::vm::error::Error) if the key identified by `self` is not + /// valid for the table at index `index` on the lua stack. + /// + /// # Safety + /// + /// The lua state pointer `l` must not be NULL and must point to a valid [Vm], and the index + /// `index` must point to a valid [Table] on the lua stack. If any of these invariants are not + /// respected, this function is UB. + unsafe fn set_table(self, l: State, index: i32) -> crate::vm::Result<()>; +} diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index a97c915..b10b92f 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -35,13 +35,13 @@ fn tables() { let top = vm.top(); vm.scope(|vm| { let mut tbl = Table::new(vm); - tbl.set_field(c"a", 0.42)?; - tbl.set_field(c"b", "My great string")?; + tbl.set(c"a", 0.42)?; + tbl.set(c"b", "My great string")?; let mut new_table = Table::new(vm); - new_table.set_field(c"whatever", 42)?; - let s: &str = tbl.get_field(c"b")?; + new_table.set(c"whatever", 42)?; + let s: &str = tbl.get(c"b")?; assert_eq!(s, "My great string"); - tbl.set_field(c"sub", new_table)?; + tbl.set(c"sub", new_table)?; assert_eq!(tbl.len(), 3); vm.set_global(c"myTable", tbl) }) @@ -65,12 +65,12 @@ fn tables() { vm.scope(|vm| { let tbl: Table = vm.get_global("myTable")?; assert_eq!(tbl.len(), 3); - let v: f64 = tbl.get_field(c"a")?; + let v: f64 = tbl.get(c"a")?; assert_eq!(v, 0.42); let v = vm.run_code::<&str>(c"return myTable.b")?; assert_eq!(v, "My great string"); { - let v: f64 = tbl.get_field(c"a")?; + let v: f64 = tbl.get(c"a")?; assert_eq!(v, 0.42); } assert_eq!(v, "My great string"); From 788b7a21c4aaf45e2d8f243ecb61a6c8777213de Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Apr 2025 11:12:07 +0200 Subject: [PATCH 296/527] Removed TODO from main lib file --- core/src/lib.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index dc95cff..cc298b1 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -28,7 +28,6 @@ //TODO: Support dynamic linking and modules by dynamic linking to luajit. //TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. -//TODO: Simplify API for get/set/geti/seti/get_field/set_field to accept a trait and implement for i32/string/etc. //TODO: Attempt to implement custom __index on userdata. pub mod ffi; From 3bb49f12e1c434ce397ad193be927f69633ea89d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Apr 2025 11:39:02 +0200 Subject: [PATCH 297/527] Gated most tools behind feature flags to allow supporting dynamic modules --- core/Cargo.toml | 8 ++- core/src/libs/mod.rs | 5 ++ core/src/util/mod.rs | 6 ++ core/src/vm/core/mod.rs | 6 +- core/src/vm/core/root_vm.rs | 97 ++++++++++++++++++++++++++++ core/src/vm/core/vm.rs | 68 +------------------ core/src/vm/mod.rs | 5 +- core/tests/test_vm_backtrace.rs | 2 + core/tests/test_vm_closures.rs | 2 + core/tests/test_vm_custom_structs.rs | 2 + core/tests/test_vm_destructor.rs | 2 + core/tests/test_vm_functions.rs | 2 + core/tests/test_vm_libs.rs | 2 + core/tests/test_vm_multivalue.rs | 2 + core/tests/test_vm_run.rs | 2 + core/tests/test_vm_run_string.rs | 2 + core/tests/test_vm_tables.rs | 2 + core/tests/test_vm_userdata.rs | 2 + 18 files changed, 147 insertions(+), 70 deletions(-) create mode 100644 core/src/vm/core/root_vm.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index cbfd53c..0a788bf 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -32,5 +32,11 @@ libc = { version = "0.2.170", optional = true } bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } [features] -interrupt = ["libc", "windows-sys"] +interrupt = ["libc", "windows-sys", "root-vm"] dynamic = [] +root-vm = [] +util-method = [] +util-function = [] +util-namespace = [] +libs-core = ["util-namespace"] +libs = ["libs-core"] diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index 480fab6..fd4b885 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -27,18 +27,23 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. /// The bp3d-lua core library. +#[cfg(feature = "libs")] pub mod lua; /// Utility toolkit. +#[cfg(feature = "libs")] pub mod util; /// OS toolkit. +#[cfg(feature = "libs")] pub mod os; +#[cfg(feature = "libs-core")] mod interface; //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. //TODO: utf8 lib with string functions operating on UTF8-strings +#[cfg(feature = "libs-core")] pub use interface::*; diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index f2807ba..a1bdf35 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -27,10 +27,16 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod core; +#[cfg(feature = "util-function")] mod function; +#[cfg(feature = "util-method")] mod method; +#[cfg(feature = "util-namespace")] mod namespace; +#[cfg(feature = "util-function")] pub use function::LuaFunction; +#[cfg(feature = "util-method")] pub use method::LuaMethod; +#[cfg(feature = "util-namespace")] pub use namespace::Namespace; diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 064f836..c729dfb 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -32,9 +32,13 @@ pub mod iter; pub mod load; pub mod util; mod vm; +#[cfg(feature = "root-vm")] +mod root_vm; #[cfg(feature = "interrupt")] pub mod interrupt; pub use interface::*; -pub use vm::{RootVm, Vm}; +pub use vm::Vm; +#[cfg(feature = "root-vm")] +pub use root_vm::RootVm; diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs new file mode 100644 index 0000000..836b7a7 --- /dev/null +++ b/core/src/vm/core/root_vm.rs @@ -0,0 +1,97 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::Cell; +use std::ops::{Deref, DerefMut}; +use bp3d_debug::debug; +use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; +use crate::ffi::lua::lua_close; +use crate::vm::core::destructor::Pool; +use crate::vm::Vm; + +thread_local! { + // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! + static HAS_VM: Cell = const { Cell::new(false) }; +} + +pub struct RootVm { + vm: Vm +} + +impl Default for RootVm { + fn default() -> Self { + Self::new() + } +} + +impl RootVm { + pub fn new() -> RootVm { + if HAS_VM.get() { + panic!("A VM already exists for this thread.") + } + let l = unsafe { luaL_newstate() }; + unsafe { luaL_openlibs(l) }; + HAS_VM.set(true); + let mut vm = RootVm { + vm: unsafe { Vm::from_raw(l) }, + }; + unsafe { Pool::new_in_vm(&mut vm) }; + vm + } +} + +impl Deref for RootVm { + type Target = Vm; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.vm + } +} + +impl DerefMut for RootVm { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vm + } +} + +impl Drop for RootVm { + fn drop(&mut self) { + debug!("Deleting destructor pool"); + unsafe { + drop(Box::from_raw(Pool::from_vm(self))); + } + unsafe { + debug!("Closing Lua VM..."); + lua_close(self.vm.as_ptr()); + } + HAS_VM.set(false); + } +} + diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index e8e8c77..8fdcf54 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,13 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::{ - lua_close, lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, + lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX, }; use crate::util::core::AnyStr; -use crate::vm::core::destructor::Pool; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; @@ -40,9 +38,6 @@ use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::Function; use crate::vm::value::{FromLua, IntoLua}; -use bp3d_debug::debug; -use std::cell::Cell; -use std::ops::{Deref, DerefMut}; #[repr(transparent)] pub struct Vm { @@ -186,64 +181,3 @@ impl Vm { } } } - -thread_local! { - // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! - static HAS_VM: Cell = const { Cell::new(false) }; -} - -pub struct RootVm { - vm: Vm, -} - -impl Default for RootVm { - fn default() -> Self { - Self::new() - } -} - -impl RootVm { - pub fn new() -> RootVm { - if HAS_VM.get() { - panic!("A VM already exists for this thread.") - } - let l = unsafe { luaL_newstate() }; - unsafe { luaL_openlibs(l) }; - HAS_VM.set(true); - let mut vm = RootVm { - vm: unsafe { Vm::from_raw(l) }, - }; - unsafe { Pool::new_in_vm(&mut vm) }; - vm - } -} - -impl Deref for RootVm { - type Target = Vm; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.vm - } -} - -impl DerefMut for RootVm { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.vm - } -} - -impl Drop for RootVm { - fn drop(&mut self) { - debug!("Deleting destructor pool"); - unsafe { - drop(Box::from_raw(Pool::from_vm(self))); - } - unsafe { - debug!("Closing Lua VM..."); - lua_close(self.vm.as_ptr()); - } - HAS_VM.set(false); - } -} diff --git a/core/src/vm/mod.rs b/core/src/vm/mod.rs index 4cb5536..f254c9b 100644 --- a/core/src/vm/mod.rs +++ b/core/src/vm/mod.rs @@ -37,6 +37,9 @@ pub mod userdata; pub mod util; pub mod value; -pub use core::{RootVm, Vm}; +pub use core::Vm; + +#[cfg(feature = "root-vm")] +pub use core::RootVm; pub type Result = std::result::Result; diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index db63378..7cb1465 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::value::Function; diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index ee9a15f..5e0bf9c 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(all(feature = "root-vm", feature = "util-namespace"))] + use bp3d_lua::decl_closure; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; diff --git a/core/tests/test_vm_custom_structs.rs b/core/tests/test_vm_custom_structs.rs index e1a167d..46bf5c2 100644 --- a/core/tests/test_vm_custom_structs.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; diff --git a/core/tests/test_vm_destructor.rs b/core/tests/test_vm_destructor.rs index a87ee3c..2a9cb20 100644 --- a/core/tests/test_vm_destructor.rs +++ b/core/tests/test_vm_destructor.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index 5a813e1..f47bba4 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(all(feature = "root-vm", feature = "util-method"))] + use bp3d_lua::util::LuaMethod; use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::Function; diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 095d86b..36c5606 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(all(feature = "root-vm", feature = "libs"))] + use bp3d_lua::libs::lua::Lua; use bp3d_lua::libs::util::Util; use bp3d_lua::libs::Lib; diff --git a/core/tests/test_vm_multivalue.rs b/core/tests/test_vm_multivalue.rs index 8a1df6f..d217348 100644 --- a/core/tests/test_vm_multivalue.rs +++ b/core/tests/test_vm_multivalue.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::vm::RootVm; #[test] diff --git a/core/tests/test_vm_run.rs b/core/tests/test_vm_run.rs index 123422b..20fb07e 100644 --- a/core/tests/test_vm_run.rs +++ b/core/tests/test_vm_run.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::ffi::lua::{State, ThreadStatus}; use bp3d_lua::vm::core::load::{load_custom, Code, Script}; use bp3d_lua::vm::core::util::ChunkNameBuilder; diff --git a/core/tests/test_vm_run_string.rs b/core/tests/test_vm_run_string.rs index 61e434c..b5d83b7 100644 --- a/core/tests/test_vm_run_string.rs +++ b/core/tests/test_vm_run_string.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index b10b92f..aa8d7cc 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::vm::table::Table; use bp3d_lua::vm::RootVm; diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index d409bfa..7afe773 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#![cfg(feature = "root-vm")] + use bp3d_lua::ffi::lua::Number; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::LuaDrop; From ad49b40721cb70dba852f11a21445bdf97bb03f1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Apr 2025 21:32:59 +0200 Subject: [PATCH 298/527] Begin implement module system --- core/Cargo.toml | 2 ++ core/src/lib.rs | 2 +- core/src/module.rs | 38 +++++++++++++++++++++++++++++++ core/src/util/mod.rs | 2 ++ core/src/util/module.rs | 50 +++++++++++++++++++++++++++++++++++++++++ 5 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 core/src/module.rs create mode 100644 core/src/util/module.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 0a788bf..6e6bceb 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -34,9 +34,11 @@ bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } [features] interrupt = ["libc", "windows-sys", "root-vm"] dynamic = [] +module = ["dynamic"] root-vm = [] util-method = [] util-function = [] util-namespace = [] +util-module = [] libs-core = ["util-namespace"] libs = ["libs-core"] diff --git a/core/src/lib.rs b/core/src/lib.rs index cc298b1..925f3ef 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -27,7 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //TODO: Support dynamic linking and modules by dynamic linking to luajit. -//TODO: Use features to disable RootVm related stuff, such as destructors, interruption, etc. //TODO: Attempt to implement custom __index on userdata. pub mod ffi; @@ -35,3 +34,4 @@ pub mod libs; mod r#macro; pub mod util; pub mod vm; +mod module; diff --git a/core/src/module.rs b/core/src/module.rs new file mode 100644 index 0000000..f66754e --- /dev/null +++ b/core/src/module.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::State; +use crate::vm::Vm; + +extern "Rust" { fn bp3d_lua_nodule_main(vm: &Vm); } + +#[cfg(feature="dynamic")] +#[no_mangle] +extern "C" fn bp3d_lua_c_module_main(l: State) { + unsafe { bp3d_lua_nodule_main(&Vm::from_raw(l)) } +} diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index a1bdf35..8a9394a 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -33,6 +33,8 @@ mod function; mod method; #[cfg(feature = "util-namespace")] mod namespace; +#[cfg(feature = "util-module")] +pub mod module; #[cfg(feature = "util-function")] pub use function::LuaFunction; diff --git a/core/src/util/module.rs b/core/src/util/module.rs new file mode 100644 index 0000000..3a58f9b --- /dev/null +++ b/core/src/util/module.rs @@ -0,0 +1,50 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashMap; +use std::path::PathBuf; +use crate::vm::Vm; + +pub struct ModuleLoader { + map: HashMap, + paths: Vec +} + +impl ModuleLoader { + fn load_dynamic(&mut self, name: &str) { + + } + + pub fn load(&mut self, name: &str, vm: &Vm) { + + } + + pub fn add_search_path(&mut self, name: PathBuf) { + self.paths.push(name) + } +} From 8f1edb977db87d1fc989ff63b3fc69e6b4eeda37 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 7 May 2025 23:06:28 +0200 Subject: [PATCH 299/527] Use new bp3d_debug library version --- core/Cargo.toml | 2 +- core/src/vm/core/destructor.rs | 5 +---- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 6e6bceb..a426565 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -17,7 +17,7 @@ publish = false bp3d-util = { version = "2.0.1", features = ["simple-error", "format", "string"] } log = "0.4.26" bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } -bp3d-debug = "1.0.0-rc.6.1.0" +bp3d-debug = "1.0.0-rc.6.2.0" bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } time = { version = "0.3.37", features = ["formatting"] } itertools = { version = "0.14.0" } diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 366a807..a7da7d6 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -138,10 +138,7 @@ impl Pool { impl Drop for Pool { fn drop(&mut self) { - debug!( - { num = self.leaked.len() as u32 }, - "Deleting leaked pointers..." - ); + debug!({ num=self.leaked.len() }, "Deleting leaked pointers..."); let v = std::mem::take(&mut self.leaked); for f in v { f() From 3ec27717f1b48a5a3e2efb3457824be859773eaf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 8 May 2025 10:28:00 +0200 Subject: [PATCH 300/527] Tweaked dependencies configuration --- core/Cargo.toml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index a426565..173c7e9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,13 +15,14 @@ publish = false [dependencies] bp3d-util = { version = "2.0.1", features = ["simple-error", "format", "string"] } -log = "0.4.26" -bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs", "time"] } -time = { version = "0.3.37", features = ["formatting"] } +bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"], optional = true } +time = { version = "0.3.37", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } +[dev-dependencies] +bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } + [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug"], optional = true } @@ -41,4 +42,4 @@ util-function = [] util-namespace = [] util-module = [] libs-core = ["util-namespace"] -libs = ["libs-core"] +libs = ["libs-core", "time", "bp3d-os"] From 70523cf8ade92dbbd781340eec268d8fa4a27738 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 9 May 2025 16:16:28 +0200 Subject: [PATCH 301/527] Begin implement decl_lua_plugin codegen macro --- codegen/src/lib.rs | 17 +++++++++++++++++ core/Cargo.toml | 3 ++- core/src/lib.rs | 4 +++- core/src/module.rs | 12 ++---------- 4 files changed, 24 insertions(+), 12 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index c482b52..24d0783 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -32,6 +32,8 @@ mod parser; use crate::gen::{FromParam, IntoParam, LuaType}; use crate::parser::Parser; use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::quote; use syn::{parse_macro_input, DeriveInput}; #[proc_macro_derive(FromParam)] @@ -66,3 +68,18 @@ pub fn lua_type(input: TokenStream) -> TokenStream { } = parse_macro_input!(input); LuaType::new(ident, generics).parse(data).into() } + +#[proc_macro] +pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { + let ident = parse_macro_input!(input as Ident); + let func_name = String::from("bp3d_lua_plugin_") + ident.to_string().as_str(); + let func = Ident::new(&func_name, ident.span()); + let q = quote! { + #[no_mangle] + extern "Rust" fn #func(l: bp3d_lua::ffi::lua::State) -> bp3d_lua::vm::Result<()> { + let vm = unsafe { bp3d_lua::vm::Vm::from_raw(l) }; + #ident.register(&vm) + } + }; + q.into() +} diff --git a/core/Cargo.toml b/core/Cargo.toml index 173c7e9..d5e7a86 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,6 +19,7 @@ bp3d-debug = "1.0.0-rc.6.2.0" bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"], optional = true } time = { version = "0.3.37", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } +bp3d-lua-codegen = { version = "0.1.0", path = "../codegen", optional = true } [dev-dependencies] bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } @@ -35,7 +36,7 @@ bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } [features] interrupt = ["libc", "windows-sys", "root-vm"] dynamic = [] -module = ["dynamic"] +module = ["dynamic", "libs-core", "bp3d-lua-codegen"] root-vm = [] util-method = [] util-function = [] diff --git a/core/src/lib.rs b/core/src/lib.rs index 925f3ef..568f1b5 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -34,4 +34,6 @@ pub mod libs; mod r#macro; pub mod util; pub mod vm; -mod module; + +#[cfg(feature="module")] +pub mod module; diff --git a/core/src/module.rs b/core/src/module.rs index f66754e..b9db92a 100644 --- a/core/src/module.rs +++ b/core/src/module.rs @@ -26,13 +26,5 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::State; -use crate::vm::Vm; - -extern "Rust" { fn bp3d_lua_nodule_main(vm: &Vm); } - -#[cfg(feature="dynamic")] -#[no_mangle] -extern "C" fn bp3d_lua_c_module_main(l: State) { - unsafe { bp3d_lua_nodule_main(&Vm::from_raw(l)) } -} +pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +pub use bp3d_lua_codegen::decl_lua_plugin; From 2c2c727787d29bfa66c9ae61180265c7397f20cc Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 9 May 2025 14:23:37 +0000 Subject: [PATCH 302/527] Format Rust code using rustfmt --- core/src/lib.rs | 2 +- core/src/util/mod.rs | 4 ++-- core/src/util/module.rs | 12 ++++-------- core/src/vm/core/destructor.rs | 2 +- core/src/vm/core/mod.rs | 6 +++--- core/src/vm/core/root_vm.rs | 9 ++++----- core/src/vm/table/core.rs | 7 +++++-- core/src/vm/table/interface.rs | 7 +++++-- 8 files changed, 25 insertions(+), 24 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index 568f1b5..bf2b6b0 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -35,5 +35,5 @@ mod r#macro; pub mod util; pub mod vm; -#[cfg(feature="module")] +#[cfg(feature = "module")] pub mod module; diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index 8a9394a..f2a65c0 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -31,10 +31,10 @@ pub mod core; mod function; #[cfg(feature = "util-method")] mod method; -#[cfg(feature = "util-namespace")] -mod namespace; #[cfg(feature = "util-module")] pub mod module; +#[cfg(feature = "util-namespace")] +mod namespace; #[cfg(feature = "util-function")] pub use function::LuaFunction; diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 3a58f9b..da9ff24 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -26,23 +26,19 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::Vm; use std::collections::HashMap; use std::path::PathBuf; -use crate::vm::Vm; pub struct ModuleLoader { map: HashMap, - paths: Vec + paths: Vec, } impl ModuleLoader { - fn load_dynamic(&mut self, name: &str) { - - } - - pub fn load(&mut self, name: &str, vm: &Vm) { + fn load_dynamic(&mut self, name: &str) {} - } + pub fn load(&mut self, name: &str, vm: &Vm) {} pub fn add_search_path(&mut self, name: PathBuf) { self.paths.push(name) diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index a7da7d6..ad0e990 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -138,7 +138,7 @@ impl Pool { impl Drop for Pool { fn drop(&mut self) { - debug!({ num=self.leaked.len() }, "Deleting leaked pointers..."); + debug!({ num = self.leaked.len() }, "Deleting leaked pointers..."); let v = std::mem::take(&mut self.leaked); for f in v { f() diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index c729dfb..cf9c9f7 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -30,15 +30,15 @@ pub mod destructor; mod interface; pub mod iter; pub mod load; -pub mod util; -mod vm; #[cfg(feature = "root-vm")] mod root_vm; +pub mod util; +mod vm; #[cfg(feature = "interrupt")] pub mod interrupt; pub use interface::*; -pub use vm::Vm; #[cfg(feature = "root-vm")] pub use root_vm::RootVm; +pub use vm::Vm; diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index 836b7a7..8239a0b 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; -use std::ops::{Deref, DerefMut}; -use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::lua_close; use crate::vm::core::destructor::Pool; use crate::vm::Vm; +use bp3d_debug::debug; +use std::cell::Cell; +use std::ops::{Deref, DerefMut}; thread_local! { // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! @@ -40,7 +40,7 @@ thread_local! { } pub struct RootVm { - vm: Vm + vm: Vm, } impl Default for RootVm { @@ -94,4 +94,3 @@ impl Drop for RootVm { HAS_VM.set(false); } } - diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index b5b0512..e5bc701 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,13 +27,16 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, lua_setmetatable, lua_settable, lua_topointer}; +use crate::ffi::lua::{ + lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, + lua_setmetatable, lua_settable, lua_topointer, +}; use crate::vm::table::iter::Iter; +use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::value::util::ensure_single_into_lua; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; -use crate::vm::table::traits::{GetTable, SetTable}; pub struct Table<'a> { pub(super) vm: &'a Vm, diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 9d5013f..3e9986a 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -27,12 +27,15 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, State, Type}; +use crate::ffi::lua::{ + lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, + State, Type, +}; use crate::util::core::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; -use crate::vm::table::Table; use crate::vm::table::traits::{GetTable, SetTable}; +use crate::vm::table::Table; use crate::vm::util::LuaType; use crate::vm::value::util::ensure_type_equals; use crate::vm::value::FromLua; From 1d084cce05c3543b66e045b32e6aa9491a3d1875 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 9 May 2025 20:41:09 +0200 Subject: [PATCH 303/527] Fixed bug where the library may link dynamically when the static lib is chosen --- build/src/build/linux.rs | 3 ++- build/src/build/mac.rs | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs index 2c8d3af..b85c8bd 100644 --- a/build/src/build/linux.rs +++ b/build/src/build/linux.rs @@ -50,7 +50,8 @@ impl Build for Linux { let path_to_dylib = info.build_dir().join(&filename); std::fs::copy(&path_to_so, path_to_dylib)?; let path_to_dylib2 = info.target_dir().join(filename); - std::fs::copy(path_to_so, path_to_dylib2)?; + std::fs::copy(&path_to_so, path_to_dylib2)?; + std::fs::remove_file(path_to_so.join("libluajit.so"))?; Ok(()) } diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 9d83b9a..471d8df 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -64,6 +64,7 @@ impl Build for MacOS { std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib)?; let path_to_dylib2 = info.target_dir().join(filename); std::fs::copy(path_to_so.join("libluajit.so"), path_to_dylib2)?; + std::fs::remove_file(path_to_so.join("libluajit.so"))?; Ok(()) } From ddb73be7aa7d053674ae4155e20dd0886180e252 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 9 May 2025 21:56:22 +0200 Subject: [PATCH 304/527] Fixed build error due to missing feature --- testbin/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index 9821bca..de39a45 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] mlua = { version = "0.10.3", features = ["luajit"] } -bp3d-lua = { version = "1.0.0-rc.1.0.0", path = "../core" } +bp3d-lua = { version = "1.0.0-rc.1.0.0", path = "../core", features = ["root-vm"] } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } [workspace] From a37f965f513fa088902ee39d2faf5d80781a1bce Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 10 May 2025 20:27:02 +0200 Subject: [PATCH 305/527] Added initial versions of decl_lua_plugin and decl_lua_lib macros --- codegen/src/lib.rs | 26 +++++++++++++++++++++++++- core/Cargo.toml | 3 ++- core/build.rs | 2 ++ core/src/module.rs | 11 ++++++++++- 4 files changed, 39 insertions(+), 3 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 24d0783..4e7ddb7 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -72,7 +72,8 @@ pub fn lua_type(input: TokenStream) -> TokenStream { #[proc_macro] pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { let ident = parse_macro_input!(input as Ident); - let func_name = String::from("bp3d_lua_plugin_") + ident.to_string().as_str(); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let func_name = format!("bp3d_lua_{}_register_{}", crate_name.to_uppercase(), ident.to_string()); let func = Ident::new(&func_name, ident.span()); let q = quote! { #[no_mangle] @@ -83,3 +84,26 @@ pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { }; q.into() } + +#[proc_macro] +pub fn decl_lua_lib(input: TokenStream) -> TokenStream { + let ident = parse_macro_input!(input as Ident); + let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); + let rustc_const_name = format!("BP3D_LUA_{}_RUSTC_VERSION", crate_name.to_uppercase()); + let bp3d_lua_const_name = format!("BP3D_LUA_{}_ENGINE_VERSION", crate_name.to_uppercase()); + let const_name = format!("BP3D_LUA_{}_VERSION", crate_name.to_uppercase()); + let rustc_const = Ident::new(&rustc_const_name, ident.span()); + let bp3d_lua_const = Ident::new(&bp3d_lua_const_name, ident.span()); + let cons = Ident::new(&const_name, ident.span()); + let q = quote! { + #[no_mangle] + extern "C" const #rustc_const: *const std::ffi::c_char = bp3d_lua::module::RUSTC_VERSION.as_ptr() as _; + + #[no_mangle] + extern "C" const #bp3d_lua_const: *const std::ffi::c_char = bp3d_lua::module::VERSION.as_ptr() as _; + + #[no_mangle] + extern "C" const #cons: *const std::ffi::c_char = concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as _; + }; + q.into() +} diff --git a/core/Cargo.toml b/core/Cargo.toml index d5e7a86..ead9e5b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -32,11 +32,12 @@ libc = { version = "0.2.170", optional = true } [build-dependencies] bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } +rustc_version = { version = "0.4.1", optional = true } [features] interrupt = ["libc", "windows-sys", "root-vm"] dynamic = [] -module = ["dynamic", "libs-core", "bp3d-lua-codegen"] +module = ["dynamic", "libs-core", "bp3d-lua-codegen", "rustc_version"] root-vm = [] util-method = [] util-function = [] diff --git a/core/build.rs b/core/build.rs index 0b830ed..b8bf84b 100644 --- a/core/build.rs +++ b/core/build.rs @@ -91,4 +91,6 @@ fn main() { } else { println!("cargo:rustc-link-lib=static={}", lib.name); } + #[cfg(feature="module")] + println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version::version().unwrap()); } diff --git a/core/src/module.rs b/core/src/module.rs index b9db92a..3c67771 100644 --- a/core/src/module.rs +++ b/core/src/module.rs @@ -26,5 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -pub const VERSION: &str = env!("CARGO_PKG_VERSION"); +/// The BP3D Lua & LuaJIT version. +pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + +/// The rustc version being used. +pub const RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); + +/// The macro which generates a plugin entry point. pub use bp3d_lua_codegen::decl_lua_plugin; + +/// The macro which generates a library entry point. +pub use bp3d_lua_codegen::decl_lua_lib; From 71eeb251163ef96aab30dcd05a0fd4e02f145c24 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 11 May 2025 21:47:10 +0200 Subject: [PATCH 306/527] Re-design plugin system to use C compatible symbols in order to simplify integration with future C/C++ modules --- codegen/src/lib.rs | 4 +- core/Cargo.toml | 6 +- core/src/{module.rs => module/error.rs} | 90 +++++++++++++++++-- core/src/module/mod.rs | 114 ++++++++++++++++++++++++ 4 files changed, 201 insertions(+), 13 deletions(-) rename core/src/{module.rs => module/error.rs} (50%) create mode 100644 core/src/module/mod.rs diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4e7ddb7..46753b8 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -77,9 +77,9 @@ pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { let func = Ident::new(&func_name, ident.span()); let q = quote! { #[no_mangle] - extern "Rust" fn #func(l: bp3d_lua::ffi::lua::State) -> bp3d_lua::vm::Result<()> { + extern "C" fn #func(l: bp3d_lua::ffi::lua::State, error: *mut bp3d_lua::module::error::Error) -> bool { let vm = unsafe { bp3d_lua::vm::Vm::from_raw(l) }; - #ident.register(&vm) + unsafe { bp3d_lua::module::run_lua_register(&vm, #ident, *&mut error) } } }; q.into() diff --git a/core/Cargo.toml b/core/Cargo.toml index ead9e5b..93e1b1f 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -14,7 +14,7 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -bp3d-util = { version = "2.0.1", features = ["simple-error", "format", "string"] } +bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } bp3d-debug = "1.0.0-rc.6.2.0" bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"], optional = true } time = { version = "0.3.37", features = ["formatting"], optional = true } @@ -37,11 +37,11 @@ rustc_version = { version = "0.4.1", optional = true } [features] interrupt = ["libc", "windows-sys", "root-vm"] dynamic = [] -module = ["dynamic", "libs-core", "bp3d-lua-codegen", "rustc_version"] +module = ["libs-core", "bp3d-lua-codegen", "rustc_version"] root-vm = [] util-method = [] util-function = [] util-namespace = [] -util-module = [] +util-module = ["libc", "module"] libs-core = ["util-namespace"] libs = ["libs-core", "time", "bp3d-os"] diff --git a/core/src/module.rs b/core/src/module/error.rs similarity index 50% rename from core/src/module.rs rename to core/src/module/error.rs index 3c67771..fe219ec 100644 --- a/core/src/module.rs +++ b/core/src/module/error.rs @@ -26,14 +26,88 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -/// The BP3D Lua & LuaJIT version. -pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); +use libc::c_char; +use crate::ffi::lua::Type; -/// The rustc version being used. -pub const RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); +pub const STRING_BUF_LEN: usize = 4096; -/// The macro which generates a plugin entry point. -pub use bp3d_lua_codegen::decl_lua_plugin; +#[derive(Copy, Clone)] +#[repr(i32)] +pub enum ErrorType { + None = 0, + Utf8 = -1, + Type = -2, + Syntax = -3, + Runtime = -4, + Memory = -5, + Unknown = -6, + Error = -7, + Null = -8, + MultiValue = -9, + UnsupportedType = -10, + Loader = -11, + UserDataArgsEmpty = 1, + UserDataMutViolation = 2, + UserDataGc = 3, + UserDataIndex = 4, + UserDataMetatable = 5, + UserDataMultiValueField = 6, + UserDataAlreadyRegistered = 7, + UserDataAlignment = 8 +} -/// The macro which generates a library entry point. -pub use bp3d_lua_codegen::decl_lua_lib; +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Utf8Error { + pub ty: ErrorType, + pub valid_up_to: usize, + pub error_len: i16, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct TypeError { + pub ty: ErrorType, + pub expected: Type, + pub actual: Type, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct UnsupportedType { + pub ty: ErrorType, + pub actual: Type, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct String { + pub ty: ErrorType, + pub data: [u8; STRING_BUF_LEN], + pub len: usize +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct StaticString { + pub ty: ErrorType, + pub data: *const c_char, +} + +#[derive(Copy, Clone)] +#[repr(C)] +pub struct Alignment { + pub ty: ErrorType, + pub alignment: usize, +} + +#[repr(C)] +pub union Error { + pub ty: ErrorType, + pub string: String, + pub type_mismatch: TypeError, + pub utf8: Utf8Error, + pub unsupported_type: UnsupportedType, + pub static_string: StaticString, + pub alignment: Alignment +} diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs new file mode 100644 index 0000000..567e33e --- /dev/null +++ b/core/src/module/mod.rs @@ -0,0 +1,114 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod error; + +/// The BP3D Lua & LuaJIT version. +pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); + +/// The rustc version being used. +pub const RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); + +/// The macro which generates a plugin entry point. +pub use bp3d_lua_codegen::decl_lua_plugin; + +/// The macro which generates a library entry point. +pub use bp3d_lua_codegen::decl_lua_lib; + +/// Helper function to run the [register](crate::libs::Lib::register) function of a +/// [Lib](crate::libs::Lib). +/// +/// This function automatically translates the Rust result type to the C FFI compatible type. +pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: &mut error::Error) -> bool { + use crate::vm::error::Error; + use std::fmt::Write; + use bp3d_util::format::MemBufStr; + let res = lib.register(vm); + match res { + Ok(()) => true, + Err(e) => { + match e { + Error::InvalidUtf8(e) => { + error.ty = error::ErrorType::Utf8; + // Option is not FFI safe, so use i16. + error.utf8.error_len = e.error_len().map(|v| v as i16).unwrap_or(-1); + error.utf8.valid_up_to = e.valid_up_to(); + } + Error::Type(e) => { + error.ty = error::ErrorType::Type; + error.type_mismatch.actual = e.actual; + error.type_mismatch.expected = e.expected; + } + Error::Syntax(e) => { + error.ty = error::ErrorType::Syntax; + let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let _ = write!(msg, "{}", e); + } + Error::Runtime(e) => { + error.ty = error::ErrorType::Runtime; + let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let _ = write!(msg, "{}", e); + } + Error::Memory => error.ty = error::ErrorType::Memory, + Error::Unknown => error.ty = error::ErrorType::Unknown, + Error::Error => error.ty = error::ErrorType::Error, + Error::Null => error.ty = error::ErrorType::Null, + Error::MultiValue => error.ty = error::ErrorType::MultiValue, + Error::UserData(e) => match e { + crate::vm::userdata::Error::ArgsEmpty => error.ty = error::ErrorType::UserDataArgsEmpty, + crate::vm::userdata::Error::MutViolation(e) => { + error.ty = error::ErrorType::UserDataMutViolation; + error.static_string.data = e.as_ptr(); + } + crate::vm::userdata::Error::Gc => error.ty = error::ErrorType::UserDataGc, + crate::vm::userdata::Error::Index => error.ty = error::ErrorType::UserDataIndex, + crate::vm::userdata::Error::Metatable => error.ty = error::ErrorType::UserDataMetatable, + crate::vm::userdata::Error::MultiValueField => error.ty = error::ErrorType::UserDataMultiValueField, + crate::vm::userdata::Error::AlreadyRegistered(e) => { + error.ty = error::ErrorType::UserDataAlreadyRegistered; + error.static_string.data = e.as_ptr(); + } + crate::vm::userdata::Error::Alignment(e) => { + error.ty = error::ErrorType::UserDataAlignment; + error.alignment.alignment = e; + } + } + Error::UnsupportedType(e) => { + error.ty = error::ErrorType::UnsupportedType; + error.unsupported_type.actual = e; + } + Error::Loader(e) => { + error.ty = error::ErrorType::Loader; + let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let _ = write!(msg, "{}", e); + } + } + false + } + } +} From 942f721e7c36044e05884c4fa548009c6dc96c8d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 16 May 2025 04:42:33 +0200 Subject: [PATCH 307/527] Added support for generating list of dependencies --- codegen/Cargo.toml | 2 ++ codegen/src/lib.rs | 14 +++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 2d310e2..b8295f9 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -11,3 +11,5 @@ proc-macro = true quote = "1.0" syn = { version = "2.0.98", features = ["full"] } proc-macro2 = "1.0.26" +cargo-manifest = "0.19.1" +itertools = { version = "0.14.0" } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 46753b8..6cd2480 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -32,6 +32,8 @@ mod parser; use crate::gen::{FromParam, IntoParam, LuaType}; use crate::parser::Parser; use proc_macro::TokenStream; +use cargo_manifest::Manifest; +use itertools::Itertools; use proc_macro2::Ident; use quote::quote; use syn::{parse_macro_input, DeriveInput}; @@ -91,10 +93,16 @@ pub fn decl_lua_lib(input: TokenStream) -> TokenStream { let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); let rustc_const_name = format!("BP3D_LUA_{}_RUSTC_VERSION", crate_name.to_uppercase()); let bp3d_lua_const_name = format!("BP3D_LUA_{}_ENGINE_VERSION", crate_name.to_uppercase()); - let const_name = format!("BP3D_LUA_{}_VERSION", crate_name.to_uppercase()); + let deps_const_name = format!("BP3D_LUA_{}_DEPS", crate_name.to_uppercase()); let rustc_const = Ident::new(&rustc_const_name, ident.span()); let bp3d_lua_const = Ident::new(&bp3d_lua_const_name, ident.span()); - let cons = Ident::new(&const_name, ident.span()); + let deps_const = Ident::new(&deps_const_name, ident.span()); + let package = Manifest::from_path(std::env::var_os("CARGO_MANIFEST_PATH") + .expect("Failed to get CARGO_MANIFEST_PATH")) + .expect("Failed to read CARGO_MANIFEST_PATH"); + let mut deps_list = package.dependencies.map(|v| v.iter() + .map(|(k, v)| format!("{}={}", k, v.req())).join(",")).unwrap_or("".into()); + deps_list += "\0"; let q = quote! { #[no_mangle] extern "C" const #rustc_const: *const std::ffi::c_char = bp3d_lua::module::RUSTC_VERSION.as_ptr() as _; @@ -103,7 +111,7 @@ pub fn decl_lua_lib(input: TokenStream) -> TokenStream { extern "C" const #bp3d_lua_const: *const std::ffi::c_char = bp3d_lua::module::VERSION.as_ptr() as _; #[no_mangle] - extern "C" const #cons: *const std::ffi::c_char = concat!(env!("CARGO_PKG_VERSION"), "\0").as_ptr() as _; + extern "C" const #deps_const: *const std::ffi::c_char = #deps_list.as_ptr() as _; }; q.into() } From 2da2182a6d2b06dd44e58d680eb368bf8f9b08e7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 16 May 2025 04:45:09 +0200 Subject: [PATCH 308/527] Added version of time crate to module definitions --- core/src/module/mod.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index 567e33e..3577a6a 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -29,10 +29,13 @@ pub mod error; /// The BP3D Lua & LuaJIT version. -pub const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); +pub static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); /// The rustc version being used. -pub const RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); +pub static RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); + +/// The version of the time library used by bp3d-lua. +pub static TIME_VERSION: &str = "0.3.41"; /// The macro which generates a plugin entry point. pub use bp3d_lua_codegen::decl_lua_plugin; From d55756c3272b208c93f22d96866a7b26725c3b47 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 21 May 2025 19:33:30 +0200 Subject: [PATCH 309/527] Added new to_integer and to_number functions to any --- core/src/module/error.rs | 2 ++ core/src/module/mod.rs | 2 ++ core/src/vm/error.rs | 4 +++- core/src/vm/value/any.rs | 25 ++++++++++++++++++++++++- 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/core/src/module/error.rs b/core/src/module/error.rs index fe219ec..3b00630 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -46,6 +46,8 @@ pub enum ErrorType { MultiValue = -9, UnsupportedType = -10, Loader = -11, + ParseFloat = -12, + ParseInt = -13, UserDataArgsEmpty = 1, UserDataMutViolation = 2, UserDataGc = 3, diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index 3577a6a..eb00dcf 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -110,6 +110,8 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } + Error::ParseInt => error.ty = error::ErrorType::ParseInt, + Error::ParseFloat => error.ty = error::ErrorType::ParseFloat } false } diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index 7b8b58a..261c3bf 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -90,7 +90,9 @@ simple_error! { MultiValue => "only one value is supported by this API", UserData(crate::vm::userdata::Error) => "userdata: {}", UnsupportedType(Type) => "unsupported lua type: {:?}", - Loader(String) => "loader error: {}" + Loader(String) => "loader error: {}", + ParseFloat => "parse float error", + ParseInt => "parse int error" } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index fa139d1..dcc5417 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -28,7 +28,7 @@ use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; use crate::util::core::SimpleDrop; -use crate::vm::error::Error; +use crate::vm::error::{Error, TypeError}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::table::Table; use crate::vm::thread::Thread; @@ -38,6 +38,7 @@ use crate::vm::value::function::Function; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::Display; +use std::str::FromStr; #[derive(Debug, PartialEq, Clone)] pub enum AnyValue<'a> { @@ -87,6 +88,28 @@ impl AnyValue<'_> { AnyValue::Thread(_) => Type::Thread, } } + + pub fn to_number(&self) -> Result { + match self { + AnyValue::Number(v) => Ok(*v), + AnyValue::String(v) => crate::ffi::lua::Number::from_str(v).map_err(|_| Error::ParseFloat), + _ => Err(Error::Type(TypeError { + expected: Type::Number, + actual: self.ty() + })) + } + } + + pub fn to_integer(&self) -> Result { + match self { + AnyValue::Number(v) => Ok(*v as _), + AnyValue::String(v) => crate::ffi::lua::Integer::from_str(v).map_err(|_| Error::ParseInt), + _ => Err(Error::Type(TypeError { + expected: Type::Number, + actual: self.ty() + })) + } + } } unsafe impl IntoParam for AnyValue<'_> { From 1449361abddb5e0e9ce784d5cf76ac8804030642 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 21 May 2025 19:37:30 +0200 Subject: [PATCH 310/527] Refactor: moved Function to a new types module --- core/src/libs/lua/call.rs | 2 +- core/src/libs/lua/load.rs | 2 +- core/src/util/function.rs | 2 +- core/src/util/method.rs | 2 +- core/src/vm/core/vm.rs | 2 +- core/src/vm/value/mod.rs | 2 +- core/src/vm/value/types.rs | 29 +++++++++++++++++++++++++++++ core/tests/test_vm_backtrace.rs | 2 +- core/tests/test_vm_functions.rs | 2 +- 9 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 core/src/vm/value/types.rs diff --git a/core/src/libs/lua/call.rs b/core/src/libs/lua/call.rs index fdc238e..07ebad6 100644 --- a/core/src/libs/lua/call.rs +++ b/core/src/libs/lua/call.rs @@ -32,7 +32,7 @@ use crate::util::Namespace; use crate::vm::error::Error; use crate::vm::function::types::RFunction; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::Function; +use crate::vm::value::types::Function; decl_lib_func! { fn pcall(vm: &Vm, func: Function) -> UncheckedAnyReturn { diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index e13a4da..7e0cc0e 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -31,7 +31,7 @@ use crate::util::Namespace; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; -use crate::vm::value::Function; +use crate::vm::value::types::Function; use crate::{decl_closure, decl_lib_func}; use bp3d_util::simple_error; use std::path::{Path, PathBuf}; diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 3c953c9..2e94c37 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -36,7 +36,7 @@ use crate::vm::Vm; pub struct LuaFunction(Key); impl LuaFunction { - pub fn create(f: crate::vm::value::Function) -> Self { + pub fn create(f: crate::vm::value::types::Function) -> Self { Self(Key::new(f)) } diff --git a/core/src/util/method.rs b/core/src/util/method.rs index b558084..09bcdb5 100644 --- a/core/src/util/method.rs +++ b/core/src/util/method.rs @@ -43,7 +43,7 @@ impl LuaMethod { obj: crate::vm::table::Table, method_name: impl AnyStr, ) -> crate::vm::Result { - let method: crate::vm::value::Function = obj.get(method_name)?; + let method: crate::vm::value::types::Function = obj.get(method_name)?; Ok(Self { method: Key::new(method), obj: Key::new(obj), diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 8fdcf54..bc8bdb7 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -36,7 +36,7 @@ use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; -use crate::vm::value::Function; +use crate::vm::value::types::Function; use crate::vm::value::{FromLua, IntoLua}; #[repr(transparent)] diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 5686c70..50c0fec 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -31,6 +31,6 @@ mod core; mod function; mod interface; pub mod util; +pub mod types; -pub use function::Function; pub use interface::*; diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs new file mode 100644 index 0000000..e4ef03b --- /dev/null +++ b/core/src/vm/value/types.rs @@ -0,0 +1,29 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub use super::function::Function; diff --git a/core/tests/test_vm_backtrace.rs b/core/tests/test_vm_backtrace.rs index 7cb1465..4ddd9ee 100644 --- a/core/tests/test_vm_backtrace.rs +++ b/core/tests/test_vm_backtrace.rs @@ -30,7 +30,7 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::value::Function; +use bp3d_lua::vm::value::types::Function; use bp3d_lua::vm::RootVm; use bp3d_util::simple_error; diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index f47bba4..f3ab19f 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -30,7 +30,7 @@ use bp3d_lua::util::LuaMethod; use bp3d_lua::vm::table::Table; -use bp3d_lua::vm::value::Function; +use bp3d_lua::vm::value::types::Function; use bp3d_lua::vm::RootVm; use std::ffi::CStr; From 85a925b87de3bbcbe0bfa28b40f037142f1711b1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 21 May 2025 19:39:46 +0200 Subject: [PATCH 311/527] Refactor: rename ffi::Integer/Number to ffi::RawInteger/RawNumber --- core/src/ffi/ext.rs | 6 +++--- core/src/ffi/laux.rs | 10 +++++----- core/src/ffi/lua.rs | 12 ++++++------ core/src/vm/function/core.rs | 6 +++--- core/src/vm/value/any.rs | 8 ++++---- core/tests/test_vm_userdata.rs | 8 ++++---- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index 3c188ac..b216e42 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{Integer, Number, State}; +use crate::ffi::lua::{RawInteger, RawNumber, State}; use std::ffi::c_int; pub type MSize = u32; @@ -35,8 +35,8 @@ pub type MSize = u32; // Value reading //--------------- extern "C" { - pub fn lua_ext_fast_checknumber(l: State, numarg: c_int) -> Number; - pub fn lua_ext_fast_checkinteger(l: State, numarg: c_int) -> Integer; + pub fn lua_ext_fast_checknumber(l: State, numarg: c_int) -> RawNumber; + pub fn lua_ext_fast_checkinteger(l: State, numarg: c_int) -> RawInteger; } //------- diff --git a/core/src/ffi/laux.rs b/core/src/ffi/laux.rs index bdee28b..c8aee89 100644 --- a/core/src/ffi/laux.rs +++ b/core/src/ffi/laux.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{Integer, Number, State, ThreadStatus, Type}; +use crate::ffi::lua::{RawInteger, RawNumber, State, ThreadStatus, Type}; use std::ffi::{c_char, c_int, c_void}; //-------------------- @@ -60,11 +60,11 @@ extern "C" { len: *mut usize, ) -> *const c_char; - pub fn luaL_checknumber(l: State, numarg: c_int) -> Number; - pub fn luaL_optnumber(l: State, narg: c_int, def: Number) -> Number; + pub fn luaL_checknumber(l: State, numarg: c_int) -> RawNumber; + pub fn luaL_optnumber(l: State, narg: c_int, def: RawNumber) -> RawNumber; - pub fn luaL_checkinteger(l: State, numarg: c_int) -> Integer; - pub fn luaL_optinteger(l: State, narg: c_int, def: Integer) -> Integer; + pub fn luaL_checkinteger(l: State, numarg: c_int) -> RawInteger; + pub fn luaL_optinteger(l: State, narg: c_int, def: RawInteger) -> RawInteger; pub fn luaL_checkstack(l: State, sz: c_int, msg: *const c_char); pub fn luaL_checktype(l: State, narg: c_int, t: Type); diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 35cfd29..88249b5 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -75,8 +75,8 @@ pub enum Type { Thread = 8, } -pub type Number = c_double; -pub type Integer = isize; +pub type RawNumber = c_double; +pub type RawInteger = isize; //-------------------- // State manipulation @@ -119,8 +119,8 @@ extern "C" { pub fn lua_rawequal(l: State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_lessthan(l: State, idx1: c_int, idx2: c_int) -> c_int; - pub fn lua_tonumber(l: State, idx: c_int) -> Number; - pub fn lua_tointeger(l: State, idx: c_int) -> Integer; + pub fn lua_tonumber(l: State, idx: c_int) -> RawNumber; + pub fn lua_tointeger(l: State, idx: c_int) -> RawInteger; pub fn lua_toboolean(l: State, idx: c_int) -> c_int; pub fn lua_tolstring(l: State, idx: c_int, len: *mut usize) -> *const c_char; pub fn lua_objlen(l: State, idx: c_int) -> usize; @@ -135,8 +135,8 @@ extern "C" { //------------------------------- extern "C" { pub fn lua_pushnil(l: State); - pub fn lua_pushnumber(l: State, n: Number); - pub fn lua_pushinteger(l: State, n: Integer); + pub fn lua_pushnumber(l: State, n: RawNumber); + pub fn lua_pushinteger(l: State, n: RawInteger); pub fn lua_pushlstring(l: State, s: *const c_char, len: usize); pub fn lua_pushstring(l: State, s: *const c_char); //LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt, va_list argp); diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 2a8180a..c1e47cd 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -30,7 +30,7 @@ use crate::ffi::ext::{lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{ lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, - lua_pushnumber, lua_type, Integer, Number, Type, + lua_pushnumber, lua_type, RawInteger, RawNumber, Type, }; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; @@ -153,7 +153,7 @@ macro_rules! impl_integer { impl LuaType for $t { fn lua_type() -> Vec { - vec![TypeName::Some(std::any::type_name::())] + vec![TypeName::Some(std::any::type_name::())] } } @@ -194,7 +194,7 @@ macro_rules! impl_float { impl LuaType for $t { fn lua_type() -> Vec { - vec![TypeName::Some(std::any::type_name::())] + vec![TypeName::Some(std::any::type_name::())] } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index dcc5417..a85c2e1 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -89,10 +89,10 @@ impl AnyValue<'_> { } } - pub fn to_number(&self) -> Result { + pub fn to_number(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v), - AnyValue::String(v) => crate::ffi::lua::Number::from_str(v).map_err(|_| Error::ParseFloat), + AnyValue::String(v) => crate::ffi::lua::RawNumber::from_str(v).map_err(|_| Error::ParseFloat), _ => Err(Error::Type(TypeError { expected: Type::Number, actual: self.ty() @@ -100,10 +100,10 @@ impl AnyValue<'_> { } } - pub fn to_integer(&self) -> Result { + pub fn to_integer(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v as _), - AnyValue::String(v) => crate::ffi::lua::Integer::from_str(v).map_err(|_| Error::ParseInt), + AnyValue::String(v) => crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt), _ => Err(Error::Type(TypeError { expected: Type::Number, actual: self.ty() diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 7afe773..48965bd 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -28,7 +28,7 @@ #![cfg(feature = "root-vm")] -use bp3d_lua::ffi::lua::Number; +use bp3d_lua::ffi::lua::RawNumber; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::LuaDrop; use bp3d_lua::vm::{RootVm, Vm}; @@ -60,7 +60,7 @@ impl Drop for MyInt { decl_userdata! { impl MyInt { - fn tonumber(this: &MyInt) -> Number { + fn tonumber(this: &MyInt) -> RawNumber { this.0 as _ } @@ -210,11 +210,11 @@ fn test_vm_userdata_base(vm: &Vm) { "579" ); assert_eq!( - vm.run_code::(c"return (a + b):tonumber()").unwrap(), + vm.run_code::(c"return (a + b):tonumber()").unwrap(), 579.0 ); assert_eq!( - vm.run_code::(c"return a.tonumber(b)").unwrap(), + vm.run_code::(c"return a.tonumber(b)").unwrap(), 456.0 ); assert_eq!(top + 8, vm.top()); From feea51f71678901b478193b8625dffefb2b84808 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 21 May 2025 19:50:24 +0200 Subject: [PATCH 312/527] Added Integer and Number to types --- core/src/vm/value/types.rs | 99 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index e4ef03b..2d2815f 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -26,4 +26,103 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::laux::{luaL_checkinteger, luaL_checknumber}; +use crate::ffi::lua::{lua_isnumber, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type}; +use crate::util::core::SimpleDrop; +use crate::vm::error::{Error, TypeError}; +use crate::vm::function::FromParam; +use crate::vm::util::LuaType; +use crate::vm::value::FromLua; +use crate::vm::Vm; + pub use super::function::Function; + +#[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] +pub struct Number(pub RawNumber); + +unsafe impl SimpleDrop for Number {} + +impl LuaType for Number {} + +#[derive(Copy, Clone, PartialOrd, PartialEq, Debug, Eq, Ord, Hash)] +pub struct Integer(pub RawInteger); + +unsafe impl SimpleDrop for Integer {} + +impl LuaType for Integer {} + +impl<'a> FromParam<'a> for Number { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Self(luaL_checknumber(vm.as_ptr(), index)) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) == 1 { + Some(Self(lua_tonumber(l, index))) + } else { + None + } + } + } +} + +impl<'a> FromLua<'a> for Number { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Self(lua_tonumber(vm.as_ptr(), index)) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) != 1 { + return Err(Error::Type(TypeError { + expected: Type::Number, + actual: lua_type(l, index), + })); + } + Ok(Self(lua_tonumber(l, index))) + } + } +} + +impl<'a> FromParam<'a> for Integer { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Self(luaL_checkinteger(vm.as_ptr(), index)) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) == 1 { + Some(Self(lua_tointeger(l, index))) + } else { + None + } + } + } +} + +impl<'a> FromLua<'a> for Integer { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Self(lua_tointeger(vm.as_ptr(), index)) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) != 1 { + return Err(Error::Type(TypeError { + expected: Type::Number, + actual: lua_type(l, index), + })); + } + Ok(Self(lua_tointeger(l, index))) + } + } +} From 069668d0cb25edae792555010501972deed308a7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 21 May 2025 19:51:02 +0200 Subject: [PATCH 313/527] Fixed build error in testbin --- testbin/src/context_opt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index 8d9b139..e465335 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -28,7 +28,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; -use bp3d_lua::vm::value::Function as LuaFunction; +use bp3d_lua::vm::value::types::Function as LuaFunction; use bp3d_lua::vm::RootVm; use mlua::{Function, Lua, UserDataMethods}; use std::time::Duration; From 4e3d1fea9b718ebb4f34113c3d779bf8ffbeb0b4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 26 May 2025 11:15:15 +0200 Subject: [PATCH 314/527] Added @rpath to install_name --- build/src/build/mac.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 471d8df..4403ae3 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -54,10 +54,11 @@ impl Build for MacOS { fn post_build(info: &BuildInfo, runner: &CommandRunner) -> std::io::Result<()> { let filename = format!("libbp3d-luajit-{}.dylib", info.version()); + let filename2 = format!("@rpath/libbp3d-luajit-{}.dylib", info.version()); let path_to_so = info.build_dir().join("src"); runner.run( Command::new("install_name_tool") - .args(["-id", &filename, "libluajit.so"]) + .args(["-id", &filename2, "libluajit.so"]) .current_dir(&path_to_so), )?; let path_to_dylib = info.build_dir().join(&filename); From f904ed74b2a3f06d288fde997931f6aa11a4bf05 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:10:26 +0200 Subject: [PATCH 315/527] Use a new error type for UTF8 errors needed for the module system --- core/src/vm/error.rs | 44 ++++++++++++++++++++++++++++++++++++++- core/src/vm/value/core.rs | 2 +- 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index 261c3bf..e4f7fc1 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -29,7 +29,6 @@ use crate::ffi::lua::Type; use bp3d_util::simple_error; use std::fmt::{Display, Formatter}; -use std::str::Utf8Error; #[derive(Debug, Copy, Clone)] pub struct TypeError { @@ -77,6 +76,49 @@ impl Display for RuntimeError { } } +#[derive(Debug, Copy, Clone)] +pub struct Utf8Error { + // A re-usable error type is needed for modules so duplicate the one from std. + pub valid_up_to: usize, + pub error_len: Option +} + +impl From for Utf8Error { + fn from(value: std::str::Utf8Error) -> Self { + Utf8Error { + valid_up_to: value.valid_up_to(), + error_len: value.error_len().map(|v| v as u8), + } + } +} + +impl Utf8Error { + pub const fn valid_up_to(&self) -> usize { + self.valid_up_to + } + + pub const fn error_len(&self) -> Option { + match self.error_len { + Some(len) => Some(len as usize), + None => None, + } + } +} + +impl Display for Utf8Error { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + if let Some(error_len) = self.error_len { + write!( + f, + "invalid utf-8 sequence of {} bytes from index {}", + error_len, self.valid_up_to + ) + } else { + write!(f, "incomplete utf-8 byte sequence from index {}", self.valid_up_to) + } + } +} + simple_error! { pub Error { InvalidUtf8(Utf8Error) => "invalid UTF8 string: {}", diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 3fb65ff..fd3ebd3 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -55,7 +55,7 @@ impl<'a> FromLua<'a> for &'a str { let mut len: usize = 0; let s = lua_tolstring(l, index, &mut len as _); let slice = std::slice::from_raw_parts(s as _, len); - std::str::from_utf8(slice).map_err(Error::InvalidUtf8) + std::str::from_utf8(slice).map_err(|e| Error::InvalidUtf8(e.into())) } _ => Err(Error::Type(TypeError { expected: Type::String, From e52ae044fe7cf18ef452f4dd35ac53dae5fa8fe7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:13:37 +0200 Subject: [PATCH 316/527] Largely simplified module system now that the base is provided by bp3d-os --- codegen/Cargo.toml | 2 -- codegen/src/lib.rs | 33 +-------------------------------- core/Cargo.toml | 14 +++++++------- core/src/module/mod.rs | 8 +------- 4 files changed, 9 insertions(+), 48 deletions(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index b8295f9..2d310e2 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -11,5 +11,3 @@ proc-macro = true quote = "1.0" syn = { version = "2.0.98", features = ["full"] } proc-macro2 = "1.0.26" -cargo-manifest = "0.19.1" -itertools = { version = "0.14.0" } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 6cd2480..9a5467b 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -32,8 +32,6 @@ mod parser; use crate::gen::{FromParam, IntoParam, LuaType}; use crate::parser::Parser; use proc_macro::TokenStream; -use cargo_manifest::Manifest; -use itertools::Itertools; use proc_macro2::Ident; use quote::quote; use syn::{parse_macro_input, DeriveInput}; @@ -75,7 +73,7 @@ pub fn lua_type(input: TokenStream) -> TokenStream { pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { let ident = parse_macro_input!(input as Ident); let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let func_name = format!("bp3d_lua_{}_register_{}", crate_name.to_uppercase(), ident.to_string()); + let func_name = format!("bp3d_lua_{}_register_{}", crate_name.replace("-", "_"), ident.to_string()); let func = Ident::new(&func_name, ident.span()); let q = quote! { #[no_mangle] @@ -86,32 +84,3 @@ pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { }; q.into() } - -#[proc_macro] -pub fn decl_lua_lib(input: TokenStream) -> TokenStream { - let ident = parse_macro_input!(input as Ident); - let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let rustc_const_name = format!("BP3D_LUA_{}_RUSTC_VERSION", crate_name.to_uppercase()); - let bp3d_lua_const_name = format!("BP3D_LUA_{}_ENGINE_VERSION", crate_name.to_uppercase()); - let deps_const_name = format!("BP3D_LUA_{}_DEPS", crate_name.to_uppercase()); - let rustc_const = Ident::new(&rustc_const_name, ident.span()); - let bp3d_lua_const = Ident::new(&bp3d_lua_const_name, ident.span()); - let deps_const = Ident::new(&deps_const_name, ident.span()); - let package = Manifest::from_path(std::env::var_os("CARGO_MANIFEST_PATH") - .expect("Failed to get CARGO_MANIFEST_PATH")) - .expect("Failed to read CARGO_MANIFEST_PATH"); - let mut deps_list = package.dependencies.map(|v| v.iter() - .map(|(k, v)| format!("{}={}", k, v.req())).join(",")).unwrap_or("".into()); - deps_list += "\0"; - let q = quote! { - #[no_mangle] - extern "C" const #rustc_const: *const std::ffi::c_char = bp3d_lua::module::RUSTC_VERSION.as_ptr() as _; - - #[no_mangle] - extern "C" const #bp3d_lua_const: *const std::ffi::c_char = bp3d_lua::module::VERSION.as_ptr() as _; - - #[no_mangle] - extern "C" const #deps_const: *const std::ffi::c_char = #deps_list.as_ptr() as _; - }; - q.into() -} diff --git a/core/Cargo.toml b/core/Cargo.toml index 93e1b1f..7c565a9 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,20 +16,20 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"], optional = true } -time = { version = "0.3.37", features = ["formatting"], optional = true } +bp3d-os = { version = "1.0.0-rc.4.4.0", features = [], optional = true } +time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "0.1.0", path = "../codegen", optional = true } -[dev-dependencies] -bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } - [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug"], optional = true } [target.'cfg(unix)'.dependencies] libc = { version = "0.2.170", optional = true } +[dev-dependencies] +bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } + [build-dependencies] bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } rustc_version = { version = "0.4.1", optional = true } @@ -42,6 +42,6 @@ root-vm = [] util-method = [] util-function = [] util-namespace = [] -util-module = ["libc", "module"] +util-module = ["module", "bp3d-os/module"] libs-core = ["util-namespace"] -libs = ["libs-core", "time", "bp3d-os"] +libs = ["libs-core", "time", "bp3d-os/time"] diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index eb00dcf..13e791e 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -29,10 +29,7 @@ pub mod error; /// The BP3D Lua & LuaJIT version. -pub static VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0"); - -/// The rustc version being used. -pub static RUSTC_VERSION: &str = concat!(env!("RUSTC_VERSION"), "\0"); +pub static VERSION: &str = env!("CARGO_PKG_VERSION"); /// The version of the time library used by bp3d-lua. pub static TIME_VERSION: &str = "0.3.41"; @@ -40,9 +37,6 @@ pub static TIME_VERSION: &str = "0.3.41"; /// The macro which generates a plugin entry point. pub use bp3d_lua_codegen::decl_lua_plugin; -/// The macro which generates a library entry point. -pub use bp3d_lua_codegen::decl_lua_lib; - /// Helper function to run the [register](crate::libs::Lib::register) function of a /// [Lib](crate::libs::Lib). /// From 6c6869e414050fcd985593f04f148c73cd9fdf03 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:17:49 +0200 Subject: [PATCH 317/527] Added initial version of module loader --- core/src/util/module.rs | 145 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 137 insertions(+), 8 deletions(-) diff --git a/core/src/util/module.rs b/core/src/util/module.rs index da9ff24..23adc8c 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -27,20 +27,149 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::vm::Vm; -use std::collections::HashMap; +use std::collections::HashSet; +use std::ffi::CStr; use std::path::PathBuf; +use bp3d_debug::info; +use bp3d_os::module::library::Library; +use bp3d_os::module::{Module, ModuleLoader}; +use bp3d_os::module::library::types::VirtualLibrary; +use bp3d_util::simple_error; +use crate::module::{TIME_VERSION, VERSION}; +use crate::module::error::ErrorType; +use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; -pub struct ModuleLoader { - map: HashMap, - paths: Vec, +simple_error! { + pub Error { + PluginAlreadyLoaded(String) => "plugin {} is already loaded", + LibNotFound(String) => "library not found: {}", + PluginNotFound(String) => "plugin not found: {}", + (impl From)Vm(crate::vm::error::Error) => "vm error: {}", + (impl From)Module(bp3d_os::module::error::Error) => "module error: {}" + } +} + +pub type Result = std::result::Result; + +type PluginFunc = extern "C" fn(l: crate::ffi::lua::State, error: *mut crate::module::error::Error) -> bool; + +unsafe fn get_string(err: &crate::module::error::Error, f: impl FnOnce(String) -> crate::vm::error::Error) -> crate::vm::error::Error { + match std::str::from_utf8(&err.string.data[..err.string.len]) { + Ok(v) => f(v.into()), + Err(e) => crate::vm::error::Error::InvalidUtf8(e.into()) + } +} + +unsafe fn convert_module_error_to_vm_error(err: crate::module::error::Error) -> crate::vm::error::Error { + match err.ty { + ErrorType::Utf8 => crate::vm::error::Error::InvalidUtf8(Utf8Error { + valid_up_to: err.utf8.valid_up_to, + error_len: if err.utf8.error_len < 0 { None } else { Some(err.utf8.error_len as u8) }, + }), + ErrorType::Type => crate::vm::error::Error::Type(TypeError { + expected: err.type_mismatch.expected, + actual: err.type_mismatch.actual + }), + ErrorType::Syntax => get_string(&err, crate::vm::error::Error::Syntax), + ErrorType::Runtime => get_string(&err, |v| crate::vm::error::Error::Runtime(RuntimeError::new(v))), + ErrorType::Memory => crate::vm::error::Error::Memory, + ErrorType::Unknown => crate::vm::error::Error::Unknown, + ErrorType::Error => crate::vm::error::Error::Error, + ErrorType::Null => crate::vm::error::Error::Null, + ErrorType::MultiValue => crate::vm::error::Error::MultiValue, + ErrorType::UnsupportedType => crate::vm::error::Error::UnsupportedType(err.unsupported_type.actual), + ErrorType::Loader => get_string(&err, crate::vm::error::Error::Loader), + ErrorType::ParseFloat => crate::vm::error::Error::ParseFloat, + ErrorType::ParseInt => crate::vm::error::Error::ParseInt, + ErrorType::UserDataArgsEmpty => crate::vm::error::Error::UserData(crate::vm::userdata::Error::ArgsEmpty), + ErrorType::UserDataMutViolation => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data))), + ErrorType::UserDataGc => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Gc), + ErrorType::UserDataIndex => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index), + ErrorType::UserDataMetatable => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable), + ErrorType::UserDataMultiValueField => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField), + ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData(crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data))), + ErrorType::UserDataAlignment => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Alignment(err.alignment.alignment)), + _ => std::hint::unreachable_unchecked() + } +} + +pub struct ModuleManager { + set: HashSet, + loader: ModuleLoader } -impl ModuleLoader { - fn load_dynamic(&mut self, name: &str) {} +impl ModuleManager { + fn load_plugin(vm: &Vm, module: &Module, name: &str, lib: &str, plugin: &str) -> Result<()> { + let func_name = format!("bp3d_lua_{}_register_{}", lib, plugin); + let sym = unsafe { module.lib().load_symbol::(func_name) }? + .ok_or_else(|| Error::PluginNotFound(name.into()))?; + let mut err = crate::module::error::Error { + ty: ErrorType::None + }; + if !sym.call(vm.as_ptr(), &mut err) { + return Err(Error::Vm(unsafe { convert_module_error_to_vm_error(err) })) + } + Ok(()) + } + + fn load_dynamic(&mut self, lib: &str, plugin: &str, vm: &Vm) -> Result<()> { + let name = format!("{}::{}", lib, plugin); + if self.set.contains(&name) { + return Err(Error::PluginAlreadyLoaded(name)); + } + let module = unsafe { self.loader.load(lib) }?; + info!("Loaded dynamic module {:?}-{:?}", module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION")); + Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; + info!("Loaded plugin {}", name); + self.set.insert(name); + Ok(()) + } - pub fn load(&mut self, name: &str, vm: &Vm) {} + fn load_builtin(&mut self, lib: &str, plugin: &str, vm: &Vm) -> Result { + let name = format!("{}::{}", lib, plugin); + if self.set.contains(&name) { + return Err(Error::PluginAlreadyLoaded(name)); + } + let module = match unsafe { self.loader.load_builtin(lib) } { + Ok(v) => v, + Err(e) => return match e { + bp3d_os::module::error::Error::NotFound(_) => Ok(false), + e => Err(Error::Module(e)) + } + }; + info!("Loaded builtin module {:?}-{:?}", module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION")); + Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; + info!("Loaded plugin {}", name); + self.set.insert(name); + Ok(true) + } + + pub fn load(&mut self, lib: &str, plugin: &str, vm: &Vm) -> Result<()> { + if !self.load_builtin(lib, plugin, vm)? { + self.load_dynamic(lib, plugin, vm)?; + } + Ok(()) + } pub fn add_search_path(&mut self, name: PathBuf) { - self.paths.push(name) + self.loader.add_search_path(name) + } + + pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { + let mut loader = ModuleLoader::new(builtins); + loader.add_public_dependency("bp3d-lua", VERSION); + loader.add_public_dependency("time", TIME_VERSION); + Self { + set: Default::default(), + loader + } + } +} + +impl Default for ModuleManager { + fn default() -> Self { + Self::new(&[]) } } From 4c0e858191ad0a2bec967c059d1119a3117312e0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:40:59 +0200 Subject: [PATCH 318/527] Fixed more bugs in userdata macros --- core/src/macro/userdata.rs | 2 +- core/src/macro/userdata_func.rs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index ce04c10..ab1313a 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -32,7 +32,7 @@ macro_rules! _impl_userdata { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &$crate::vm::userdata::core::Registry) -> Result<(), $crate::vm::userdata::Error> { + fn register(registry: &$crate::vm::userdata::core::Registry) -> std::result::Result<(), $crate::vm::userdata::Error> { $( let f = $obj_name::$fn_name()?; registry.add_method(f); diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index 5b169f9..384af6a 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -32,7 +32,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { + $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -58,7 +58,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { + $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -84,7 +84,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { + $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; @@ -109,7 +109,7 @@ macro_rules! decl_userdata_func { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { impl $obj_name { - $vis fn $fn_name() -> Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { + $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; From bba97c1534fbf2a3b38d63d290dcb393c361eb37 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:41:11 +0200 Subject: [PATCH 319/527] Fixed possible name conflict in time lib --- core/src/libs/os/time.rs | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 921898a..f4859ac 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -46,41 +46,41 @@ simple_error! { } } -struct Wrapper(OffsetDateTime); +struct OffsetDateTimeWrapper(OffsetDateTime); decl_userdata! { - impl Wrapper { - fn format(this: &Wrapper, format: &str) -> Result { + impl OffsetDateTimeWrapper { + fn format(this: &OffsetDateTimeWrapper, format: &str) -> Result { let desc = parse(format)?; let str = this.0.format(&desc)?; Ok(str) } - fn __add(this: &Wrapper, duration: f64) -> Option { - this.0.checked_add(Duration::seconds_f64(duration)).map(Wrapper) + fn __add(this: &OffsetDateTimeWrapper, duration: f64) -> Option { + this.0.checked_add(Duration::seconds_f64(duration)).map(OffsetDateTimeWrapper) } - fn __sub(this: &Wrapper, other: &Wrapper) -> f64 { + fn __sub(this: &OffsetDateTimeWrapper, other: &OffsetDateTimeWrapper) -> f64 { (this.0 - other.0).as_seconds_f64() } - fn __gt(this: &Wrapper, other: &Wrapper) -> bool { + fn __gt(this: &OffsetDateTimeWrapper, other: &OffsetDateTimeWrapper) -> bool { this.0 > other.0 } - fn __ge(this: &Wrapper, other: &Wrapper) -> bool { + fn __ge(this: &OffsetDateTimeWrapper, other: &OffsetDateTimeWrapper) -> bool { this.0 >= other.0 } - fn __lt(this: &Wrapper, other: &Wrapper) -> bool { + fn __lt(this: &OffsetDateTimeWrapper, other: &OffsetDateTimeWrapper) -> bool { this.0 < other.0 } - fn __le(this: &Wrapper, other: &Wrapper) -> bool { + fn __le(this: &OffsetDateTimeWrapper, other: &OffsetDateTimeWrapper) -> bool { this.0 <= other.0 } - fn get_date<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + fn get_date<'a>(this: &OffsetDateTimeWrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); table.set(c"year", this.0.year())?; table.set(c"month", this.0.month() as u8)?; @@ -88,7 +88,7 @@ decl_userdata! { Ok(table) } - fn get_time<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + fn get_time<'a>(this: &OffsetDateTimeWrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); table.set(c"hour", this.0.hour())?; table.set(c"minute", this.0.minute())?; @@ -96,7 +96,7 @@ decl_userdata! { Ok(table) } - fn get_offset<'a>(this: &Wrapper, vm: &Vm) -> crate::vm::Result> { + fn get_offset<'a>(this: &OffsetDateTimeWrapper, vm: &Vm) -> crate::vm::Result> { let mut table = Table::with_capacity(vm, 0, 3); table.set(c"hours", this.0.offset().whole_hours())?; table.set(c"minutes", this.0.offset().whole_minutes())?; @@ -108,7 +108,7 @@ decl_userdata! { unsafe impl IntoParam for OffsetDateTime { fn into_param(self, vm: &Vm) -> u16 { - Wrapper(self).into_param(vm) + OffsetDateTimeWrapper(self).into_param(vm) } } @@ -172,7 +172,7 @@ impl Lib for Time { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace .vm() - .register_userdata::(crate::vm::userdata::case::Camel)?; + .register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([ ("nowUtc", RFunction::wrap(now_utc)), ("nowLocal", RFunction::wrap(now_local)), From 81ad34011a2ad51b70ebbe8159285804316dc141 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:41:42 +0200 Subject: [PATCH 320/527] Added module manager binding to lua --- core/src/libs/lua/mod.rs | 6 +++ core/src/libs/lua/module.rs | 82 +++++++++++++++++++++++++++++++++++++ core/src/vm/table/mod.rs | 2 + 3 files changed, 90 insertions(+) create mode 100644 core/src/libs/lua/module.rs diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 76af4aa..abc3827 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -32,9 +32,15 @@ mod load; mod options; pub mod require; +#[cfg(feature = "util-module")] +mod module; + pub use base::Base; pub use call::Call; pub use load::Load; pub use require::Require; pub use options::Lua; + +#[cfg(feature = "util-module")] +pub use module::Module; diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs new file mode 100644 index 0000000..ee03024 --- /dev/null +++ b/core/src/libs/lua/module.rs @@ -0,0 +1,82 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::PathBuf; +use bp3d_os::module::library::types::VirtualLibrary; +use crate::decl_userdata_mut; +use crate::libs::Lib; +use crate::util::module::ModuleManager; +use crate::util::module::Result; +use crate::util::Namespace; +use crate::vm::Vm; + +pub struct Module { + builtins: &'static [&'static VirtualLibrary], + search_paths: Vec +} + +impl Module { + pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { + Self { + builtins, + search_paths: Vec::new() + } + } + + pub fn add_search_path(&mut self, path: PathBuf) -> &mut Self { + self.search_paths.push(path); + self + } +} + +struct ModuleManagerWrapper(ModuleManager); + +decl_userdata_mut! { + impl ModuleManagerWrapper { + fn load(this: &mut ModuleManagerWrapper, vm: &Vm, lib: &str, plugin: &str) -> Result<()> { + this.0.load(lib, plugin, vm) + } + } +} + +impl Lib for Module { + const NAMESPACE: &'static str = ""; + + fn load(&self, _: &mut Namespace) -> crate::vm::Result<()> { + unreachable!() + } + + fn register(&self, vm: &Vm) -> crate::vm::Result<()> { + vm.register_userdata::(crate::vm::userdata::case::Camel)?; + let mut manager = ModuleManager::new(self.builtins); + for search_path in &self.search_paths { + manager.add_search_path(search_path.clone()); + } + vm.set_global(c"MODULES", ModuleManagerWrapper(manager)) + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 41bedce..07f84ba 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//TODO: Support collect function + mod core; mod interface; mod iter; From d110e41eec2a551f03090c85882ad2bb2ab8be94 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 13:45:24 +0200 Subject: [PATCH 321/527] Added initial test for new module manager --- core/tests/test_vm_libs.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 36c5606..2f66447 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -28,16 +28,17 @@ #![cfg(all(feature = "root-vm", feature = "libs"))] -use bp3d_lua::libs::lua::Lua; +use bp3d_lua::libs::lua::{Lua, Module}; use bp3d_lua::libs::util::Util; use bp3d_lua::libs::Lib; use bp3d_lua::vm::RootVm; #[test] fn test_vm_lib_lua() { - let mut vm = RootVm::new(); + let vm = RootVm::new(); let top = vm.top(); - Lua::new().build().register(&mut vm).unwrap(); + Lua::new().build().register(&vm).unwrap(); + Module::new(&[]).register(&vm).unwrap(); vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') @@ -71,6 +72,12 @@ fn test_vm_lib_lua() { ", ) .unwrap(); + let err = vm + .run_code::<()>(c"MODULES:load('broken', 'broken2')") + .unwrap_err() + .into_runtime() + .unwrap(); + assert_eq!(err.msg(), "rust error: module error: module not found (broken)"); assert_eq!(vm.top(), top); } From 4702b1ba5818485b74854276037416b00f13d37a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 14:03:49 +0200 Subject: [PATCH 322/527] Added more debug info to build script under Linux --- build/src/build/linux.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs index b85c8bd..82766fa 100644 --- a/build/src/build/linux.rs +++ b/build/src/build/linux.rs @@ -46,8 +46,11 @@ impl Build for Linux { fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { let filename = format!("libbp3d-luajit-{}.so", info.version()); + println!("File name: {}", filename); let path_to_so = info.build_dir().join("src").join("libluajit.so"); let path_to_dylib = info.build_dir().join(&filename); + println!("Path to SO: {}", path_to_so.display()); + println!("Path to DYLIB: {}", path_to_dylib.display()); std::fs::copy(&path_to_so, path_to_dylib)?; let path_to_dylib2 = info.target_dir().join(filename); std::fs::copy(&path_to_so, path_to_dylib2)?; From e44844b96f81a206efec9aa5f42919f9392358a7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 14:07:09 +0200 Subject: [PATCH 323/527] Fixed bug in build script under Linux --- build/src/build/linux.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/build/src/build/linux.rs b/build/src/build/linux.rs index 82766fa..d056c1b 100644 --- a/build/src/build/linux.rs +++ b/build/src/build/linux.rs @@ -46,15 +46,12 @@ impl Build for Linux { fn post_build(info: &BuildInfo, _: &CommandRunner) -> std::io::Result<()> { let filename = format!("libbp3d-luajit-{}.so", info.version()); - println!("File name: {}", filename); let path_to_so = info.build_dir().join("src").join("libluajit.so"); let path_to_dylib = info.build_dir().join(&filename); - println!("Path to SO: {}", path_to_so.display()); - println!("Path to DYLIB: {}", path_to_dylib.display()); std::fs::copy(&path_to_so, path_to_dylib)?; let path_to_dylib2 = info.target_dir().join(filename); std::fs::copy(&path_to_so, path_to_dylib2)?; - std::fs::remove_file(path_to_so.join("libluajit.so"))?; + std::fs::remove_file(path_to_so)?; Ok(()) } From 2985e2d1feefbd53d6cbd4c226acf222c382dc72 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 14:08:04 +0200 Subject: [PATCH 324/527] Updated version of bp3d-lua --- core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 7c565a9..64947c1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua" -version = "1.0.0-rc.1.0.0" +version = "1.0.0-rc.2.0.0" authors = ["Yuri Edward "] edition = "2021" description = "Lua wrapper and base library for BP3D." From a65ae11970fdb6a17214d66181791fe06ba6ffac Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 14:09:00 +0200 Subject: [PATCH 325/527] Fixed broken test --- core/tests/test_vm_libs.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 2f66447..0bf1b59 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -42,7 +42,7 @@ fn test_vm_lib_lua() { vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '1.0.0-rc.1.0.0') + assert(bp3d.lua.version == '1.0.0-rc.2.0.0') assert(#bp3d.lua.patches == 5) local func = bp3d.lua.loadString('return 1 + 1') assert(func) From 8721aca886704a475e6aabced6d18e4d41981315 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 7 Jun 2025 14:10:07 +0200 Subject: [PATCH 326/527] Updated version for testbin --- testbin/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index de39a45..b93341a 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -8,7 +8,7 @@ publish = false [dependencies] mlua = { version = "0.10.3", features = ["luajit"] } -bp3d-lua = { version = "1.0.0-rc.1.0.0", path = "../core", features = ["root-vm"] } +bp3d-lua = { version = "1.0.0-rc.2.0.0", path = "../core", features = ["root-vm"] } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } [workspace] From 1c4d029752497550f6d3a5d19f44d62e7f567404 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 09:07:27 +0200 Subject: [PATCH 327/527] Fixed build error under windows --- core/src/module/error.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/module/error.rs b/core/src/module/error.rs index 3b00630..9e34b2c 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use libc::c_char; +use std::ffi::c_char; use crate::ffi::lua::Type; pub const STRING_BUF_LEN: usize = 4096; From fcfc265628ec9a5c5022caaffe0d8e75c9f61e3e Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 07:12:11 +0000 Subject: [PATCH 328/527] Format Rust code using rustfmt --- codegen/src/lib.rs | 6 +- core/build.rs | 7 ++- core/src/libs/lua/module.rs | 8 +-- core/src/module/error.rs | 8 +-- core/src/module/mod.rs | 33 +++++++--- core/src/util/module.rs | 112 ++++++++++++++++++++++----------- core/src/vm/error.rs | 8 ++- core/src/vm/value/any.rs | 16 +++-- core/src/vm/value/mod.rs | 2 +- core/src/vm/value/types.rs | 4 +- core/tests/test_vm_libs.rs | 5 +- core/tests/test_vm_userdata.rs | 3 +- 12 files changed, 144 insertions(+), 68 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 9a5467b..d919531 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -73,7 +73,11 @@ pub fn lua_type(input: TokenStream) -> TokenStream { pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { let ident = parse_macro_input!(input as Ident); let crate_name = std::env::var("CARGO_PKG_NAME").unwrap(); - let func_name = format!("bp3d_lua_{}_register_{}", crate_name.replace("-", "_"), ident.to_string()); + let func_name = format!( + "bp3d_lua_{}_register_{}", + crate_name.replace("-", "_"), + ident.to_string() + ); let func = Ident::new(&func_name, ident.span()); let q = quote! { #[no_mangle] diff --git a/core/build.rs b/core/build.rs index b8bf84b..6fd8666 100644 --- a/core/build.rs +++ b/core/build.rs @@ -91,6 +91,9 @@ fn main() { } else { println!("cargo:rustc-link-lib=static={}", lib.name); } - #[cfg(feature="module")] - println!("cargo:rustc-env=RUSTC_VERSION={}", rustc_version::version().unwrap()); + #[cfg(feature = "module")] + println!( + "cargo:rustc-env=RUSTC_VERSION={}", + rustc_version::version().unwrap() + ); } diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index ee03024..16ddc2d 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -26,25 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::PathBuf; -use bp3d_os::module::library::types::VirtualLibrary; use crate::decl_userdata_mut; use crate::libs::Lib; use crate::util::module::ModuleManager; use crate::util::module::Result; use crate::util::Namespace; use crate::vm::Vm; +use bp3d_os::module::library::types::VirtualLibrary; +use std::path::PathBuf; pub struct Module { builtins: &'static [&'static VirtualLibrary], - search_paths: Vec + search_paths: Vec, } impl Module { pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { Self { builtins, - search_paths: Vec::new() + search_paths: Vec::new(), } } diff --git a/core/src/module/error.rs b/core/src/module/error.rs index 9e34b2c..7ceac58 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_char; use crate::ffi::lua::Type; +use std::ffi::c_char; pub const STRING_BUF_LEN: usize = 4096; @@ -55,7 +55,7 @@ pub enum ErrorType { UserDataMetatable = 5, UserDataMultiValueField = 6, UserDataAlreadyRegistered = 7, - UserDataAlignment = 8 + UserDataAlignment = 8, } #[derive(Copy, Clone)] @@ -86,7 +86,7 @@ pub struct UnsupportedType { pub struct String { pub ty: ErrorType, pub data: [u8; STRING_BUF_LEN], - pub len: usize + pub len: usize, } #[derive(Copy, Clone)] @@ -111,5 +111,5 @@ pub union Error { pub utf8: Utf8Error, pub unsupported_type: UnsupportedType, pub static_string: StaticString, - pub alignment: Alignment + pub alignment: Alignment, } diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index 13e791e..f301090 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -41,10 +41,14 @@ pub use bp3d_lua_codegen::decl_lua_plugin; /// [Lib](crate::libs::Lib). /// /// This function automatically translates the Rust result type to the C FFI compatible type. -pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: &mut error::Error) -> bool { +pub fn run_lua_register( + vm: &crate::vm::Vm, + lib: impl crate::libs::Lib, + error: &mut error::Error, +) -> bool { use crate::vm::error::Error; - use std::fmt::Write; use bp3d_util::format::MemBufStr; + use std::fmt::Write; let res = lib.register(vm); match res { Ok(()) => true, @@ -63,12 +67,14 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & } Error::Syntax(e) => { error.ty = error::ErrorType::Syntax; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::Runtime(e) => { error.ty = error::ErrorType::Runtime; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::Memory => error.ty = error::ErrorType::Memory, @@ -77,15 +83,21 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & Error::Null => error.ty = error::ErrorType::Null, Error::MultiValue => error.ty = error::ErrorType::MultiValue, Error::UserData(e) => match e { - crate::vm::userdata::Error::ArgsEmpty => error.ty = error::ErrorType::UserDataArgsEmpty, + crate::vm::userdata::Error::ArgsEmpty => { + error.ty = error::ErrorType::UserDataArgsEmpty + } crate::vm::userdata::Error::MutViolation(e) => { error.ty = error::ErrorType::UserDataMutViolation; error.static_string.data = e.as_ptr(); } crate::vm::userdata::Error::Gc => error.ty = error::ErrorType::UserDataGc, crate::vm::userdata::Error::Index => error.ty = error::ErrorType::UserDataIndex, - crate::vm::userdata::Error::Metatable => error.ty = error::ErrorType::UserDataMetatable, - crate::vm::userdata::Error::MultiValueField => error.ty = error::ErrorType::UserDataMultiValueField, + crate::vm::userdata::Error::Metatable => { + error.ty = error::ErrorType::UserDataMetatable + } + crate::vm::userdata::Error::MultiValueField => { + error.ty = error::ErrorType::UserDataMultiValueField + } crate::vm::userdata::Error::AlreadyRegistered(e) => { error.ty = error::ErrorType::UserDataAlreadyRegistered; error.static_string.data = e.as_ptr(); @@ -94,18 +106,19 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & error.ty = error::ErrorType::UserDataAlignment; error.alignment.alignment = e; } - } + }, Error::UnsupportedType(e) => { error.ty = error::ErrorType::UnsupportedType; error.unsupported_type.actual = e; } Error::Loader(e) => { error.ty = error::ErrorType::Loader; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::ParseInt => error.ty = error::ErrorType::ParseInt, - Error::ParseFloat => error.ty = error::ErrorType::ParseFloat + Error::ParseFloat => error.ty = error::ErrorType::ParseFloat, } false } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 23adc8c..ae84aa2 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::module::error::ErrorType; +use crate::module::{TIME_VERSION, VERSION}; +use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; use crate::vm::Vm; -use std::collections::HashSet; -use std::ffi::CStr; -use std::path::PathBuf; use bp3d_debug::info; +use bp3d_os::module::library::types::VirtualLibrary; use bp3d_os::module::library::Library; use bp3d_os::module::{Module, ModuleLoader}; -use bp3d_os::module::library::types::VirtualLibrary; use bp3d_util::simple_error; -use crate::module::{TIME_VERSION, VERSION}; -use crate::module::error::ErrorType; -use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; +use std::collections::HashSet; +use std::ffi::CStr; +use std::path::PathBuf; simple_error! { pub Error { @@ -51,63 +51,97 @@ simple_error! { pub type Result = std::result::Result; -type PluginFunc = extern "C" fn(l: crate::ffi::lua::State, error: *mut crate::module::error::Error) -> bool; +type PluginFunc = + extern "C" fn(l: crate::ffi::lua::State, error: *mut crate::module::error::Error) -> bool; -unsafe fn get_string(err: &crate::module::error::Error, f: impl FnOnce(String) -> crate::vm::error::Error) -> crate::vm::error::Error { +unsafe fn get_string( + err: &crate::module::error::Error, + f: impl FnOnce(String) -> crate::vm::error::Error, +) -> crate::vm::error::Error { match std::str::from_utf8(&err.string.data[..err.string.len]) { Ok(v) => f(v.into()), - Err(e) => crate::vm::error::Error::InvalidUtf8(e.into()) + Err(e) => crate::vm::error::Error::InvalidUtf8(e.into()), } } -unsafe fn convert_module_error_to_vm_error(err: crate::module::error::Error) -> crate::vm::error::Error { +unsafe fn convert_module_error_to_vm_error( + err: crate::module::error::Error, +) -> crate::vm::error::Error { match err.ty { ErrorType::Utf8 => crate::vm::error::Error::InvalidUtf8(Utf8Error { valid_up_to: err.utf8.valid_up_to, - error_len: if err.utf8.error_len < 0 { None } else { Some(err.utf8.error_len as u8) }, + error_len: if err.utf8.error_len < 0 { + None + } else { + Some(err.utf8.error_len as u8) + }, }), ErrorType::Type => crate::vm::error::Error::Type(TypeError { expected: err.type_mismatch.expected, - actual: err.type_mismatch.actual + actual: err.type_mismatch.actual, }), ErrorType::Syntax => get_string(&err, crate::vm::error::Error::Syntax), - ErrorType::Runtime => get_string(&err, |v| crate::vm::error::Error::Runtime(RuntimeError::new(v))), + ErrorType::Runtime => get_string(&err, |v| { + crate::vm::error::Error::Runtime(RuntimeError::new(v)) + }), ErrorType::Memory => crate::vm::error::Error::Memory, ErrorType::Unknown => crate::vm::error::Error::Unknown, ErrorType::Error => crate::vm::error::Error::Error, ErrorType::Null => crate::vm::error::Error::Null, ErrorType::MultiValue => crate::vm::error::Error::MultiValue, - ErrorType::UnsupportedType => crate::vm::error::Error::UnsupportedType(err.unsupported_type.actual), + ErrorType::UnsupportedType => { + crate::vm::error::Error::UnsupportedType(err.unsupported_type.actual) + } ErrorType::Loader => get_string(&err, crate::vm::error::Error::Loader), ErrorType::ParseFloat => crate::vm::error::Error::ParseFloat, ErrorType::ParseInt => crate::vm::error::Error::ParseInt, - ErrorType::UserDataArgsEmpty => crate::vm::error::Error::UserData(crate::vm::userdata::Error::ArgsEmpty), - ErrorType::UserDataMutViolation => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data))), + ErrorType::UserDataArgsEmpty => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::ArgsEmpty) + } + ErrorType::UserDataMutViolation => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data)), + ), ErrorType::UserDataGc => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Gc), - ErrorType::UserDataIndex => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index), - ErrorType::UserDataMetatable => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable), - ErrorType::UserDataMultiValueField => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField), - ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData(crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data))), - ErrorType::UserDataAlignment => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Alignment(err.alignment.alignment)), - _ => std::hint::unreachable_unchecked() + ErrorType::UserDataIndex => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index) + } + ErrorType::UserDataMetatable => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable) + } + ErrorType::UserDataMultiValueField => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField) + } + ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data)), + ), + ErrorType::UserDataAlignment => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::Alignment(err.alignment.alignment), + ), + _ => std::hint::unreachable_unchecked(), } } pub struct ModuleManager { set: HashSet, - loader: ModuleLoader + loader: ModuleLoader, } impl ModuleManager { - fn load_plugin(vm: &Vm, module: &Module, name: &str, lib: &str, plugin: &str) -> Result<()> { + fn load_plugin( + vm: &Vm, + module: &Module, + name: &str, + lib: &str, + plugin: &str, + ) -> Result<()> { let func_name = format!("bp3d_lua_{}_register_{}", lib, plugin); let sym = unsafe { module.lib().load_symbol::(func_name) }? .ok_or_else(|| Error::PluginNotFound(name.into()))?; let mut err = crate::module::error::Error { - ty: ErrorType::None + ty: ErrorType::None, }; if !sym.call(vm.as_ptr(), &mut err) { - return Err(Error::Vm(unsafe { convert_module_error_to_vm_error(err) })) + return Err(Error::Vm(unsafe { convert_module_error_to_vm_error(err) })); } Ok(()) } @@ -118,8 +152,11 @@ impl ModuleManager { return Err(Error::PluginAlreadyLoaded(name)); } let module = unsafe { self.loader.load(lib) }?; - info!("Loaded dynamic module {:?}-{:?}", module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION")); + info!( + "Loaded dynamic module {:?}-{:?}", + module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION") + ); Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); @@ -133,13 +170,18 @@ impl ModuleManager { } let module = match unsafe { self.loader.load_builtin(lib) } { Ok(v) => v, - Err(e) => return match e { - bp3d_os::module::error::Error::NotFound(_) => Ok(false), - e => Err(Error::Module(e)) + Err(e) => { + return match e { + bp3d_os::module::error::Error::NotFound(_) => Ok(false), + e => Err(Error::Module(e)), + } } }; - info!("Loaded builtin module {:?}-{:?}", module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION")); + info!( + "Loaded builtin module {:?}-{:?}", + module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION") + ); Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); @@ -163,7 +205,7 @@ impl ModuleManager { loader.add_public_dependency("time", TIME_VERSION); Self { set: Default::default(), - loader + loader, } } } diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index e4f7fc1..84b13a0 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -80,7 +80,7 @@ impl Display for RuntimeError { pub struct Utf8Error { // A re-usable error type is needed for modules so duplicate the one from std. pub valid_up_to: usize, - pub error_len: Option + pub error_len: Option, } impl From for Utf8Error { @@ -114,7 +114,11 @@ impl Display for Utf8Error { error_len, self.valid_up_to ) } else { - write!(f, "incomplete utf-8 byte sequence from index {}", self.valid_up_to) + write!( + f, + "incomplete utf-8 byte sequence from index {}", + self.valid_up_to + ) } } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index a85c2e1..4083df2 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -92,22 +92,26 @@ impl AnyValue<'_> { pub fn to_number(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v), - AnyValue::String(v) => crate::ffi::lua::RawNumber::from_str(v).map_err(|_| Error::ParseFloat), + AnyValue::String(v) => { + crate::ffi::lua::RawNumber::from_str(v).map_err(|_| Error::ParseFloat) + } _ => Err(Error::Type(TypeError { expected: Type::Number, - actual: self.ty() - })) + actual: self.ty(), + })), } } pub fn to_integer(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v as _), - AnyValue::String(v) => crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt), + AnyValue::String(v) => { + crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt) + } _ => Err(Error::Type(TypeError { expected: Type::Number, - actual: self.ty() - })) + actual: self.ty(), + })), } } } diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 50c0fec..51cffa6 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -30,7 +30,7 @@ pub mod any; mod core; mod function; mod interface; -pub mod util; pub mod types; +pub mod util; pub use interface::*; diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index 2d2815f..61195cf 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -27,7 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::{luaL_checkinteger, luaL_checknumber}; -use crate::ffi::lua::{lua_isnumber, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type}; +use crate::ffi::lua::{ + lua_isnumber, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type, +}; use crate::util::core::SimpleDrop; use crate::vm::error::{Error, TypeError}; use crate::vm::function::FromParam; diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 0bf1b59..90234a5 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -77,7 +77,10 @@ fn test_vm_lib_lua() { .unwrap_err() .into_runtime() .unwrap(); - assert_eq!(err.msg(), "rust error: module error: module not found (broken)"); + assert_eq!( + err.msg(), + "rust error: module error: module not found (broken)" + ); assert_eq!(vm.top(), top); } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 48965bd..caeab53 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -210,7 +210,8 @@ fn test_vm_userdata_base(vm: &Vm) { "579" ); assert_eq!( - vm.run_code::(c"return (a + b):tonumber()").unwrap(), + vm.run_code::(c"return (a + b):tonumber()") + .unwrap(), 579.0 ); assert_eq!( From ee0492c9e163245514bf17b2e3aff1e35a769939 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 11:00:45 +0200 Subject: [PATCH 329/527] Fixed bug in lj_disable_jit patch --- patch/lj_disable_jit.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patch/lj_disable_jit.patch b/patch/lj_disable_jit.patch index 7c90949..a3b1571 100644 --- a/patch/lj_disable_jit.patch +++ b/patch/lj_disable_jit.patch @@ -200,7 +200,7 @@ index fd8e585b..882f4425 100644 - return 2; - } - return 0; -+uint32_t lua_ext_getjitflags(lua_State *L, uint32_t flags) { ++uint32_t lua_ext_getjitflags(lua_State *L) { + return L2J(L)->flags; } From e1f0181376e9d782cafbc6180986dd6f1a31c6d6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 11:01:30 +0200 Subject: [PATCH 330/527] Added new lua_ext_ccatch_error patch --- core/build.rs | 2 + patch/lua_ext_ccatch_error.patch | 118 +++++++++++++++++++++++++++++++ 2 files changed, 120 insertions(+) create mode 100644 patch/lua_ext_ccatch_error.patch diff --git a/core/build.rs b/core/build.rs index b8bf84b..a69e164 100644 --- a/core/build.rs +++ b/core/build.rs @@ -42,6 +42,8 @@ const PATCH_LIST: &[&str] = &[ "lua_ext", // Ext library such as lua_ext_tab_len, etc. "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). "windows_set_lib_names", // Allow setting LJLIBNAME and LJDLLNAME from environment variables. + "lua_ext_ccatch_error" // Throw lua errors which cannot be catched from lua standard + // pcall/xpcall but only from lua_pcall C API. ]; fn apply_patches(out_path: &Path) -> std::io::Result> { diff --git a/patch/lua_ext_ccatch_error.patch b/patch/lua_ext_ccatch_error.patch new file mode 100644 index 0000000..b053521 --- /dev/null +++ b/patch/lua_ext_ccatch_error.patch @@ -0,0 +1,118 @@ +diff --git a/src/lj_err.c b/src/lj_err.c +index 03b5030b..8a1377e6 100644 +--- a/src/lj_err.c ++++ b/src/lj_err.c +@@ -175,7 +175,7 @@ static void *err_unwind(lua_State *L, void *stopcf, int errcode) + case FRAME_PCALLH: /* FF pcall() frame inside hook. */ + if (errcode) { + global_State *g; +- if (errcode == LUA_YIELD) { ++ if (errcode == LUA_YIELD || errcode == LUA_ERRCCATCH) { + frame = frame_prevd(frame); + break; + } +@@ -825,7 +825,7 @@ LJ_NOINLINE void lj_err_mem(lua_State *L) + } + + /* Find error function for runtime errors. Requires an extra stack traversal. */ +-static ptrdiff_t finderrfunc(lua_State *L) ++static ptrdiff_t finderrfunc(lua_State *L, int errcode) + { + cTValue *frame = L->base-1, *bot = tvref(L->stack)+LJ_FR2; + void *cf = L->cframe; +@@ -864,6 +864,10 @@ static ptrdiff_t finderrfunc(lua_State *L) + break; + case FRAME_PCALL: + case FRAME_PCALLH: ++ if (errcode == LUA_ERRCCATCH) { ++ frame = frame_prevd(frame); ++ break; ++ } + if (frame_func(frame_prevd(frame))->c.ffid == FF_xpcall) + return savestack(L, frame_prevd(frame)+1); /* xpcall's errorfunc. */ + return 0; +@@ -878,7 +882,7 @@ static ptrdiff_t finderrfunc(lua_State *L) + /* Runtime error. */ + LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) + { +- ptrdiff_t ef = (LJ_HASJIT && tvref(G(L)->jit_base)) ? 0 : finderrfunc(L); ++ ptrdiff_t ef = (LJ_HASJIT && tvref(G(L)->jit_base)) ? 0 : finderrfunc(L, LUA_ERRRUN); + if (ef) { + TValue *errfunc, *top; + lj_state_checkstack(L, LUA_MINSTACK * 2); /* Might raise new error. */ +@@ -899,6 +903,29 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_run(lua_State *L) + lj_err_throw(L, LUA_ERRRUN); + } + ++LJ_NOINLINE void LJ_FASTCALL lj_err_ccatch(lua_State *L) ++{ ++ ptrdiff_t ef = (LJ_HASJIT && tvref(G(L)->jit_base)) ? 0 : finderrfunc(L, LUA_ERRCCATCH); ++ if (ef) { ++ TValue *errfunc, *top; ++ lj_state_checkstack(L, LUA_MINSTACK * 2); /* Might raise new error. */ ++ lj_trace_abort(G(L)); ++ errfunc = restorestack(L, ef); ++ top = L->top; ++ if (!tvisfunc(errfunc) || L->status == LUA_ERRERR) { ++ setstrV(L, top-1, lj_err_str(L, LJ_ERR_ERRERR)); ++ lj_err_throw(L, LUA_ERRERR); ++ } ++ L->status = LUA_ERRERR; ++ copyTV(L, top+LJ_FR2, top-1); ++ copyTV(L, top-1, errfunc); ++ if (LJ_FR2) setnilV(top++); ++ L->top = top+1; ++ lj_vm_call(L, top, 1+1); /* Stack: |errfunc|msg| -> |msg| */ ++ } ++ lj_err_throw(L, LUA_ERRCCATCH); ++} ++ + /* Stack overflow error. */ + void LJ_FASTCALL lj_err_stkov(lua_State *L) + { +@@ -912,6 +939,8 @@ LJ_NOINLINE void LJ_FASTCALL lj_err_trace(lua_State *L, int errcode) + { + if (errcode == LUA_ERRRUN) + lj_err_run(L); ++ else if (errcode == LUA_ERRCCATCH) ++ lj_err_ccatch(L); + else + lj_err_throw(L, errcode); + } +@@ -1122,6 +1151,12 @@ LUA_API int lua_error(lua_State *L) + return 0; /* unreachable */ + } + ++LUA_API int lua_ext_ccatch_error(lua_State *L) ++{ ++ lj_err_ccatch(L); ++ return 0; /* unreachable */ ++} ++ + LUALIB_API int luaL_argerror(lua_State *L, int narg, const char *msg) + { + err_argmsg(L, narg, msg); +diff --git a/src/lj_err.h b/src/lj_err.h +index 0cb945b0..37b80a42 100644 +--- a/src/lj_err.h ++++ b/src/lj_err.h +@@ -25,6 +25,7 @@ LJ_FUNCA_NORET void LJ_FASTCALL lj_err_throw(lua_State *L, int errcode); + LJ_FUNC_NORET void lj_err_mem(lua_State *L); + LJ_FUNC_NORET void LJ_FASTCALL lj_err_stkov(lua_State *L); + LJ_FUNC_NORET void LJ_FASTCALL lj_err_run(lua_State *L); ++LJ_FUNC_NORET void LJ_FASTCALL lj_err_ccatch(lua_State *L); + #if LJ_HASJIT + LJ_FUNCA_NORET void LJ_FASTCALL lj_err_trace(lua_State *L, int errcode); + #endif +diff --git a/src/lua.h b/src/lua.h +index 6d1634d1..0eb0917b 100644 +--- a/src/lua.h ++++ b/src/lua.h +@@ -46,6 +46,7 @@ + #define LUA_ERRSYNTAX 3 + #define LUA_ERRMEM 4 + #define LUA_ERRERR 5 ++#define LUA_ERRCCATCH 6 + + + typedef struct lua_State lua_State; From 831f9725b3dc20f36cded79fe4e0a3011490a688 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 11:02:14 +0200 Subject: [PATCH 331/527] Use new lua_ext_ccatch_error in VM interrupt system --- core/src/ffi/ext.rs | 1 + core/src/vm/core/interrupt/unix.rs | 5 +++-- core/src/vm/core/interrupt/windows.rs | 6 +++--- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index b216e42..31df226 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -44,6 +44,7 @@ extern "C" { //------- extern "C" { pub fn lua_ext_tab_len(l: State, idx: c_int, outsize: *mut MSize) -> c_int; + pub fn lua_ext_ccatch_error(l: State) -> u32; } //----- diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 96671d9..96f27f6 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, + lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::core::interrupt::Error; use crate::vm::RootVm; @@ -37,6 +37,7 @@ use std::mem::MaybeUninit; use std::sync::{Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; +use crate::ffi::ext::lua_ext_ccatch_error; pub struct Signal { l: State, @@ -67,7 +68,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { unsafe { lua_sethook(l, None, 0, 0); lua_pushstring(l, c"interrupted".as_ptr()); - lua_error(l); + lua_ext_ccatch_error(l); } } diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index 9a4cb98..7357694 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_error, lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, + lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::RootVm; use bp3d_debug::{error, warning}; @@ -36,7 +36,7 @@ use std::time::Duration; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; use windows_sys::Win32::System::Threading::{GetCurrentThread, ResumeThread, SuspendThread}; - +use crate::ffi::ext::lua_ext_ccatch_error; use super::Error; static SIG_STATE: Mutex>> = Mutex::new(None); @@ -53,7 +53,7 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { unsafe { lua_sethook(l, None, 0, 0); lua_pushstring(l, c"interrupted".as_ptr()); - lua_error(l); + lua_ext_ccatch_error(l); } } From aae9b7a4ae0169d8dbe68b9426af45b757bb3f2b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 11:03:53 +0200 Subject: [PATCH 332/527] Added support for new ERRCCATCH error type --- core/src/ffi/lua.rs | 1 + core/src/module/error.rs | 1 + core/src/module/mod.rs | 7 ++++++- core/src/util/module.rs | 3 ++- core/src/vm/core/util.rs | 7 +++++++ core/src/vm/error.rs | 1 + 6 files changed, 18 insertions(+), 2 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 88249b5..d464745 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -48,6 +48,7 @@ pub enum ThreadStatus { ErrSyntax = 3, ErrMem = 4, ErrErr = 5, + ErrCcatch = 6 } #[repr(transparent)] diff --git a/core/src/module/error.rs b/core/src/module/error.rs index 9e34b2c..a41e312 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -48,6 +48,7 @@ pub enum ErrorType { Loader = -11, ParseFloat = -12, ParseInt = -13, + UncatchableRuntime = -14, UserDataArgsEmpty = 1, UserDataMutViolation = 2, UserDataGc = 3, diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index 13e791e..8c9aedf 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -105,7 +105,12 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & let _ = write!(msg, "{}", e); } Error::ParseInt => error.ty = error::ErrorType::ParseInt, - Error::ParseFloat => error.ty = error::ErrorType::ParseFloat + Error::ParseFloat => error.ty = error::ErrorType::ParseFloat, + Error::UncatchableRuntime(e) => { + error.ty = error::ErrorType::UncatchableRuntime; + let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let _ = write!(msg, "{}", e); + } } false } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 23adc8c..7c4e94c 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -72,6 +72,7 @@ unsafe fn convert_module_error_to_vm_error(err: crate::module::error::Error) -> }), ErrorType::Syntax => get_string(&err, crate::vm::error::Error::Syntax), ErrorType::Runtime => get_string(&err, |v| crate::vm::error::Error::Runtime(RuntimeError::new(v))), + ErrorType::UncatchableRuntime => get_string(&err, |v| crate::vm::error::Error::UncatchableRuntime(RuntimeError::new(v))), ErrorType::Memory => crate::vm::error::Error::Memory, ErrorType::Unknown => crate::vm::error::Error::Unknown, ErrorType::Error => crate::vm::error::Error::Error, @@ -89,7 +90,7 @@ unsafe fn convert_module_error_to_vm_error(err: crate::module::error::Error) -> ErrorType::UserDataMultiValueField => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField), ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData(crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data))), ErrorType::UserDataAlignment => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Alignment(err.alignment.alignment)), - _ => std::hint::unreachable_unchecked() + ErrorType::None => std::hint::unreachable_unchecked() } } diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index 54ffb31..87cd0a5 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -131,6 +131,13 @@ pub unsafe fn pcall( } ThreadStatus::ErrMem => Err(Error::Memory), ThreadStatus::ErrErr => Err(Error::Error), + ThreadStatus::ErrCcatch => { + // We've got a runtime error when executing the function so read the full stack + // trace produced by luaL_traceback and remove it from the stack. + let full_traceback: &str = FromLua::from_lua(vm, -1)?; + lua_remove(l, -1); + Err(Error::UncatchableRuntime(RuntimeError::new(full_traceback.into()))) + } _ => Err(Error::Unknown), } } diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index e4f7fc1..b970428 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -125,6 +125,7 @@ simple_error! { Type(TypeError) => "type error: {}", Syntax(String) => "syntax error: {}", Runtime(RuntimeError) => "runtime error: {}", + UncatchableRuntime(RuntimeError) => "uncatchable runtime error: {}", Memory => "memory allocation error", Unknown => "unknown error", Error => "error in error handler", From b03ce292cea478cfdc32e66861221ec366da7afa Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 16:54:37 +0200 Subject: [PATCH 333/527] Added new JitOptions binding --- core/src/ffi/jit.rs | 89 ++++++++++++ core/src/ffi/mod.rs | 1 + core/src/vm/core/jit.rs | 309 ++++++++++++++++++++++++++++++++++++++++ core/src/vm/core/mod.rs | 3 + 4 files changed, 402 insertions(+) create mode 100644 core/src/ffi/jit.rs create mode 100644 core/src/vm/core/jit.rs diff --git a/core/src/ffi/jit.rs b/core/src/ffi/jit.rs new file mode 100644 index 0000000..52d5d0e --- /dev/null +++ b/core/src/ffi/jit.rs @@ -0,0 +1,89 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ffi::c_int; + +pub const MODE_FLUSH: c_int = 0x0200; +pub const MODE_ON: c_int = 0x0100; +pub const MODE_OFF: c_int = 0x0000; + +pub const F_ON: u32 = 0x00000001; + +pub const F_CPU: u32 = 0x00000010; + +#[cfg(target_arch = "x86_64")] +mod x86_64 { + use crate::ffi::jit::F_CPU; + + pub const F_SSE3: u32 = F_CPU << 0; + pub const F_SSE4_1: u32 = F_CPU << 1; + pub const F_BMI2: u32 = F_CPU << 2; +} + +#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] +mod arm { + use crate::ffi::jit::F_CPU; + + pub const F_ARMV6_: u32 = F_CPU << 0; + pub const F_ARMV6T2_: u32 = F_CPU << 1; + pub const F_ARMV7: u32 = F_CPU << 2; + pub const F_ARMV8: u32 = F_CPU << 3; + pub const F_VFPV2: u32 = F_CPU << 4; + pub const F_VFPV3: u32 = F_CPU << 5; + + pub const F_ARMV6: u32 = F_ARMV6_|F_ARMV6T2_|F_ARMV7|F_ARMV8; + pub const F_ARMV6T2: u32 = F_ARMV6T2_|F_ARMV7|F_ARMV8; + pub const F_VFP: u32 = F_VFPV2|F_VFPV3; +} + +#[cfg(any(target_arch = "aarch64", target_arch = "arm"))] +pub use arm::*; + +#[cfg(target_arch = "x86_64")] +pub use x86_64::*; + +pub const F_OPT: u32 = 0x00010000; +pub const F_OPT_MASK: u32 = 0x0fff0000; + +pub const F_OPT_FOLD: u32 = F_OPT << 0; +pub const F_OPT_CSE: u32 = F_OPT << 1; +pub const F_OPT_DCE: u32 = F_OPT << 2; +pub const F_OPT_FWD: u32 = F_OPT << 3; +pub const F_OPT_DSE: u32 = F_OPT << 4; +pub const F_OPT_NARROW: u32 = F_OPT << 5; +pub const F_OPT_LOOP: u32 = F_OPT << 6; +pub const F_OPT_ABC: u32 = F_OPT << 7; +pub const F_OPT_SINK: u32 = F_OPT << 8; +pub const F_OPT_FUSE: u32 = F_OPT << 9; +pub const F_OPT_FMA: u32 = F_OPT << 10; + +pub const F_OPT_0: u32 = 0; +pub const F_OPT_1: u32 = F_OPT_FOLD | F_OPT_CSE | F_OPT_DCE; +pub const F_OPT_2: u32 = F_OPT_1 | F_OPT_NARROW | F_OPT_LOOP; +pub const F_OPT_3: u32 = F_OPT_2 | F_OPT_FWD | F_OPT_DSE | F_OPT_ABC | F_OPT_SINK | F_OPT_FUSE; +pub const F_OPT_DEFAULT: u32 = F_OPT_3; diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index 9ac5561..ec7e45f 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -29,3 +29,4 @@ pub mod ext; pub mod laux; pub mod lua; +pub mod jit; diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs new file mode 100644 index 0000000..6ca1d43 --- /dev/null +++ b/core/src/vm/core/jit.rs @@ -0,0 +1,309 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Display, Formatter}; +use libc::c_int; +use crate::ffi::ext::{lua_ext_getjitflags, lua_ext_setjitflags, lua_ext_setjitmode}; +use crate::ffi::jit; +use crate::vm::{RootVm, Vm}; + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct CpuArm { + pub v6: bool, + pub v6t2: bool, + pub v7: bool, + pub v8: bool, + pub vfpv2: bool, + pub vfpv3: bool, +} + +impl CpuArm { + pub fn has_vfp(&self) -> bool { + self.vfpv2 || self.vfpv3 + } +} + +impl Display for CpuArm { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "ARM CPU, features:")?; + if self.v6 { + write!(f, " ARMV6")?; + } + if self.v6t2 { + write!(f, " ARMV6T2")?; + } + if self.v7 { + write!(f, " ARMV7")?; + } + if self.v8 { + write!(f, " ARMV8")?; + } + if self.vfpv2 { + write!(f, " VFPV2")?; + } + if self.vfpv3 { + write!(f, " VFPV3")?; + } + Ok(()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct CpuX86 { + pub sse3: bool, + pub sse4_1: bool, + pub bmi2: bool, +} + +impl Display for CpuX86 { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "X86 CPU, features:")?; + if self.sse3 { + write!(f, " SSE3")?; + } + if self.sse4_1 { + write!(f, " SSE4_1")?; + } + if self.bmi2 { + write!(f, " BMI2")?; + } + Ok(()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum Cpu { + X86(CpuX86), + Arm(CpuArm), +} + +impl Display for Cpu { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + Cpu::X86(cpu) => cpu.fmt(f), + Cpu::Arm(cpu) => cpu.fmt(f), + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct Opts { + pub fold: bool, + pub cse: bool, + pub dce: bool, + pub fwd: bool, + pub dse: bool, + pub narrow: bool, + pub loop1: bool, + pub abc: bool, + pub sink: bool, + pub fuse: bool, + pub fma: bool +} + +impl Display for Opts { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + write!(f, "optimizations:")?; + if self.fold { + write!(f, " fold")?; + } + if self.cse { + write!(f, " cse")?; + } + if self.dce { + write!(f, " dce")?; + } + if self.fwd { + write!(f, " fwd")?; + } + if self.dse { + write!(f, " dse")?; + } + if self.narrow { + write!(f, " narrow")?; + } + if self.loop1 { + write!(f, " loop")?; + } + if self.abc { + write!(f, " abc")?; + } + if self.sink { + write!(f, " sink")?; + } + if self.fuse { + write!(f, " fuse")?; + } + if self.fma { + write!(f, " fma")?; + } + Ok(()) + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub enum OptLevel { + O0, + O1, + O2, + O3, + Unknown +} + +impl Default for OptLevel { + fn default() -> Self { + OptLevel::O3 + } +} + +impl Display for OptLevel { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match self { + OptLevel::O0 => write!(f, "O0"), + OptLevel::O1 => write!(f, "O1"), + OptLevel::O2 => write!(f, "O2"), + OptLevel::O3 => write!(f, "O3"), + OptLevel::Unknown => write!(f, "Unknown") + } + } +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct JitOptions { + cur_flag_set: u32, + mode: c_int, + opt_level_changed: bool +} + +impl JitOptions { + /// Read JIT options for the given [Vm](Vm). + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to read options for. + /// + /// returns: JitOptions + pub fn get(vm: &Vm) -> JitOptions { + Self { + cur_flag_set: unsafe { lua_ext_getjitflags(vm.as_ptr()) }, + mode: -1, + opt_level_changed: false, + } + } + + pub fn is_enabled(&self) -> bool { + (self.cur_flag_set & jit::F_ON) != 0 + } + + pub fn cpu(&self) -> Cpu { + #[cfg(target_arch = "x86_64")] + let cpu = Cpu::X86(CpuX86 { + sse3: (self.cur_flag_set & jit::F_SSE3) != 0, + sse4_1: (self.cur_flag_set & jit::SSE4_1) != 0, + bmi2: (self.cur_flag_set & jit::BMI2) != 0, + }); + #[cfg(any(target_arch = "aarch64", target_arch = "arm"))] + let cpu = Cpu::Arm(CpuArm { + v6: (self.cur_flag_set & jit::F_ARMV6_) != 0, + v6t2: (self.cur_flag_set & jit::F_ARMV6T2_) != 0, + v7: (self.cur_flag_set & jit::F_ARMV7) != 0, + v8: (self.cur_flag_set & jit::F_ARMV8) != 0, + vfpv2: (self.cur_flag_set & jit::F_VFPV2) != 0, + vfpv3: (self.cur_flag_set & jit::F_VFPV3) != 0, + }); + cpu + } + + pub fn opts(&self) -> Opts { + Opts { + fold: (self.cur_flag_set & jit::F_OPT_FOLD) != 0, + cse: (self.cur_flag_set & jit::F_OPT_CSE) != 0, + dce: (self.cur_flag_set & jit::F_OPT_DCE) != 0, + fwd: (self.cur_flag_set & jit::F_OPT_FWD) != 0, + dse: (self.cur_flag_set & jit::F_OPT_DSE) != 0, + narrow: (self.cur_flag_set & jit::F_OPT_NARROW) != 0, + loop1: (self.cur_flag_set & jit::F_OPT_LOOP) != 0, + abc: (self.cur_flag_set & jit::F_OPT_ABC) != 0, + sink: (self.cur_flag_set & jit::F_OPT_SINK) != 0, + fuse: (self.cur_flag_set & jit::F_OPT_FUSE) != 0, + fma: (self.cur_flag_set & jit::F_OPT_FMA) != 0, + } + } + + pub fn opt_level(&self) -> OptLevel { + if (self.cur_flag_set & jit::F_OPT_0) == jit::F_OPT_0 { + return OptLevel::O0; + } + if (self.cur_flag_set & jit::F_OPT_1) == jit::F_OPT_1 { + return OptLevel::O1; + } + if (self.cur_flag_set & jit::F_OPT_2) == jit::F_OPT_2 { + return OptLevel::O2; + } + if (self.cur_flag_set & jit::F_OPT_3) == jit::F_OPT_3 { + return OptLevel::O3; + } + OptLevel::Unknown + } + + pub fn flush(&mut self) { + self.mode = jit::MODE_FLUSH; + } + + pub fn disable(&mut self) { + if self.is_enabled() { + self.mode = jit::MODE_OFF; + } + } + + pub fn enable(&mut self) { + if !self.is_enabled() { + self.mode = jit::MODE_ON; + } + } + + pub fn set_opt_level(&mut self, level: OptLevel) { + self.cur_flag_set &= !jit::F_OPT_MASK; + match level { + OptLevel::O0 | OptLevel::Unknown => self.cur_flag_set |= jit::F_OPT_0, + OptLevel::O1 => self.cur_flag_set |= jit::F_OPT_1, + OptLevel::O2 => self.cur_flag_set |= jit::F_OPT_2, + OptLevel::O3 => self.cur_flag_set |= jit::F_OPT_3 + } + self.opt_level_changed = true; + } + + pub fn apply(self, vm: &mut RootVm) { + if self.mode != -1 { + assert_eq!(unsafe { lua_ext_setjitmode(vm.as_ptr(), self.mode) }, 0); + } + if self.opt_level_changed { + assert_eq!(unsafe { lua_ext_setjitflags(vm.as_ptr(), self.cur_flag_set) }, 0); + } + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index cf9c9f7..cf328a0 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -35,6 +35,9 @@ mod root_vm; pub mod util; mod vm; +#[cfg(feature = "root-vm")] +pub mod jit; + #[cfg(feature = "interrupt")] pub mod interrupt; From cd64787e78e105727ece0e6f8bbfbc01bd83a9db Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 17:12:00 +0200 Subject: [PATCH 334/527] Fixed bugs in JitOptions and added tests --- core/src/vm/core/jit.rs | 76 +++++++++++++++++-------------- core/tests/test_vm_jit_options.rs | 65 ++++++++++++++++++++++++++ 2 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 core/tests/test_vm_jit_options.rs diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index 6ca1d43..ebcc154 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -50,24 +50,28 @@ impl CpuArm { impl Display for CpuArm { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "ARM CPU, features:")?; - if self.v6 { - write!(f, " ARMV6")?; - } - if self.v6t2 { - write!(f, " ARMV6T2")?; - } - if self.v7 { - write!(f, " ARMV7")?; - } - if self.v8 { - write!(f, " ARMV8")?; - } - if self.vfpv2 { - write!(f, " VFPV2")?; - } - if self.vfpv3 { - write!(f, " VFPV3")?; + if !self.v6 && !self.v7 && !self.v8 && !self.v6t2 && !self.vfpv2 && !self.vfpv3 { + write!(f, "ARM CPU, no features")?; + } else { + write!(f, "ARM CPU, features:")?; + if self.v6 { + write!(f, " ARMV6")?; + } + if self.v6t2 { + write!(f, " ARMV6T2")?; + } + if self.v7 { + write!(f, " ARMV7")?; + } + if self.v8 { + write!(f, " ARMV8")?; + } + if self.vfpv2 { + write!(f, " VFPV2")?; + } + if self.vfpv3 { + write!(f, " VFPV3")?; + } } Ok(()) } @@ -82,15 +86,19 @@ pub struct CpuX86 { impl Display for CpuX86 { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - write!(f, "X86 CPU, features:")?; - if self.sse3 { - write!(f, " SSE3")?; - } - if self.sse4_1 { - write!(f, " SSE4_1")?; - } - if self.bmi2 { - write!(f, " BMI2")?; + if !self.sse3 && !self.sse4_1 && !self.bmi2 { + write!(f, "X86 CPU, no features")?; + } else { + write!(f, "X86 CPU, features:")?; + if self.sse3 { + write!(f, " SSE3")?; + } + if self.sse4_1 { + write!(f, " SSE4_1")?; + } + if self.bmi2 { + write!(f, " BMI2")?; + } } Ok(()) } @@ -256,17 +264,17 @@ impl JitOptions { } pub fn opt_level(&self) -> OptLevel { - if (self.cur_flag_set & jit::F_OPT_0) == jit::F_OPT_0 { - return OptLevel::O0; - } - if (self.cur_flag_set & jit::F_OPT_1) == jit::F_OPT_1 { - return OptLevel::O1; + if (self.cur_flag_set & jit::F_OPT_3) == jit::F_OPT_3 { + return OptLevel::O3; } if (self.cur_flag_set & jit::F_OPT_2) == jit::F_OPT_2 { return OptLevel::O2; } - if (self.cur_flag_set & jit::F_OPT_3) == jit::F_OPT_3 { - return OptLevel::O3; + if (self.cur_flag_set & jit::F_OPT_1) == jit::F_OPT_1 { + return OptLevel::O1; + } + if (self.cur_flag_set & jit::F_OPT_0) == jit::F_OPT_0 { + return OptLevel::O0; } OptLevel::Unknown } diff --git a/core/tests/test_vm_jit_options.rs b/core/tests/test_vm_jit_options.rs new file mode 100644 index 0000000..e51a4b8 --- /dev/null +++ b/core/tests/test_vm_jit_options.rs @@ -0,0 +1,65 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::core::jit::{JitOptions, OptLevel}; +use bp3d_lua::vm::RootVm; + +#[test] +fn test_vm_get_jit_options() { + let vm = RootVm::new(); + let jit = JitOptions::get(&vm); + let optimizations = jit.opts(); + let cpu = jit.cpu(); + let is_on = jit.is_enabled(); + assert_eq!(is_on, true); + assert_eq!(jit.opt_level(), OptLevel::default()); + println!("{} {}", cpu, optimizations); +} + +#[test] +fn test_vm_set_jit_options() { + let mut vm = RootVm::new(); + let mut jit = JitOptions::get(&vm); + assert_eq!(jit.opt_level(), OptLevel::default()); + jit.set_opt_level(OptLevel::O0); + assert_eq!(jit.opt_level(), OptLevel::O0); + jit.apply(&mut vm); + let jit = JitOptions::get(&vm); + assert_eq!(jit.opt_level(), OptLevel::O0); +} + +#[test] +fn test_vm_disable_jit() { + let mut vm = RootVm::new(); + let mut jit = JitOptions::get(&vm); + assert!(jit.is_enabled()); + jit.disable(); + jit.apply(&mut vm); + let jit = JitOptions::get(&vm); + assert!(!jit.is_enabled()); +} From 24cb89a8dd99e7098199a4ba2fbfc34e95cbf477 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 17:20:27 +0200 Subject: [PATCH 335/527] Updated crate version --- core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 64947c1..91777ff 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua" -version = "1.0.0-rc.2.0.0" +version = "1.0.0-rc.2.1.0" authors = ["Yuri Edward "] edition = "2021" description = "Lua wrapper and base library for BP3D." From 986ef4770f28a754be4eecc20cf57a4dcf1db1fb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 17:27:37 +0200 Subject: [PATCH 336/527] Fixed build error under X86/X64 --- core/src/vm/core/jit.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index ebcc154..b13321b 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -232,8 +232,8 @@ impl JitOptions { #[cfg(target_arch = "x86_64")] let cpu = Cpu::X86(CpuX86 { sse3: (self.cur_flag_set & jit::F_SSE3) != 0, - sse4_1: (self.cur_flag_set & jit::SSE4_1) != 0, - bmi2: (self.cur_flag_set & jit::BMI2) != 0, + sse4_1: (self.cur_flag_set & jit::F_SSE4_1) != 0, + bmi2: (self.cur_flag_set & jit::F_BMI2) != 0, }); #[cfg(any(target_arch = "aarch64", target_arch = "arm"))] let cpu = Cpu::Arm(CpuArm { From bf219b883e0dfa360b532eec2f82327934579f74 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 17:32:52 +0200 Subject: [PATCH 337/527] Fixed broken test (again) --- core/tests/test_vm_libs.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 90234a5..b34b994 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -39,10 +39,11 @@ fn test_vm_lib_lua() { let top = vm.top(); Lua::new().build().register(&vm).unwrap(); Module::new(&[]).register(&vm).unwrap(); + //FIXME: Find a way to write the version differently. vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '1.0.0-rc.2.0.0') + assert(bp3d.lua.version == '1.0.0-rc.2.1.0') assert(#bp3d.lua.patches == 5) local func = bp3d.lua.loadString('return 1 + 1') assert(func) From e1eb871838dd29559bd42d8f2cde6348d0a43985 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 17:57:01 +0200 Subject: [PATCH 338/527] Fixed broken import under windows --- core/src/vm/core/jit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index b13321b..b2000bf 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::c_int; use std::fmt::{Display, Formatter}; -use libc::c_int; use crate::ffi::ext::{lua_ext_getjitflags, lua_ext_setjitflags, lua_ext_setjitmode}; use crate::ffi::jit; use crate::vm::{RootVm, Vm}; From 51d65cd6635456ce7faaa6baae1dc88743a4df23 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 19:33:42 +0200 Subject: [PATCH 339/527] Added support for aarch64 windows and fixed link error under windows --- build/src/build/windows.rs | 4 ++++ build/src/target.rs | 1 + patch/lj_disable_jit.patch | 6 +++--- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs index 2dfbce7..aabd094 100644 --- a/build/src/build/windows.rs +++ b/build/src/build/windows.rs @@ -44,6 +44,10 @@ impl Build for Windows { for (k, v) in cl.env() { cmd.env(k, v); } + if info.target_name().contains("aarch64") { + cmd.env("VSCMD_ARG_HOST_ARCH", "arm64"); + cmd.env("VSCMD_ARG_TGT_ARCH", "arm64"); + } if !info.dynamic() { cmd.arg("static"); } diff --git a/build/src/target.rs b/build/src/target.rs index eebdf1d..a78d06c 100644 --- a/build/src/target.rs +++ b/build/src/target.rs @@ -42,6 +42,7 @@ static TARGET_MAP: phf::Map<&'static str, Target> = phf_map! { "aarch64-unknown-linux-gnu" => Target::Linux, "i686-pc-windows-msvc" => Target::Windows, "x86_64-pc-windows-msvc" => Target::Windows, + "aarch64-pc-windows-msvc" => Target::Windows, "x86_64-apple-darwin" => Target::MacAmd64, "x86_64-unknown-linux-gnu" => Target::Linux }; diff --git a/patch/lj_disable_jit.patch b/patch/lj_disable_jit.patch index a3b1571..27a4ef9 100644 --- a/patch/lj_disable_jit.patch +++ b/patch/lj_disable_jit.patch @@ -200,7 +200,7 @@ index fd8e585b..882f4425 100644 - return 2; - } - return 0; -+uint32_t lua_ext_getjitflags(lua_State *L) { ++LUA_API uint32_t lua_ext_getjitflags(lua_State *L) { + return L2J(L)->flags; } @@ -213,7 +213,7 @@ index fd8e585b..882f4425 100644 - if (idx < (ptrdiff_t)pt->sizekn) { - copyTV(L, L->top-1, proto_knumtv(pt, idx)); - return 1; -+int lua_ext_setjitflags(lua_State *L, uint32_t flags) { ++LUA_API int lua_ext_setjitflags(lua_State *L, uint32_t flags) { + jit_State *J = L2J(L); + uint32_t current = J->flags; + if ((current & JIT_F_ON) != (flags & JIT_F_ON)) { @@ -636,7 +636,7 @@ index 78608316..94f0077e 100644 } #endif -+int lua_ext_setjitmode(lua_State *L, int mode) { ++LUA_API int lua_ext_setjitmode(lua_State *L, int mode) { + global_State *g = G(L); + lj_trace_abort(g); /* Abort recording on any state change. */ + /* Avoid pulling the rug from under our own feet. */ From b8277c1eccb8e4f1df03ae9f3e590c2af5690dee Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 8 Jun 2025 17:40:02 +0000 Subject: [PATCH 340/527] Format Rust code using rustfmt --- core/build.rs | 2 +- core/src/ffi/jit.rs | 6 +- core/src/ffi/lua.rs | 2 +- core/src/ffi/mod.rs | 2 +- core/src/module/mod.rs | 34 +++++--- core/src/util/module.rs | 116 ++++++++++++++++++-------- core/src/vm/core/interrupt/unix.rs | 2 +- core/src/vm/core/interrupt/windows.rs | 4 +- core/src/vm/core/jit.rs | 19 +++-- core/src/vm/core/util.rs | 4 +- 10 files changed, 127 insertions(+), 64 deletions(-) diff --git a/core/build.rs b/core/build.rs index b0a1823..bf561a8 100644 --- a/core/build.rs +++ b/core/build.rs @@ -42,7 +42,7 @@ const PATCH_LIST: &[&str] = &[ "lua_ext", // Ext library such as lua_ext_tab_len, etc. "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). "windows_set_lib_names", // Allow setting LJLIBNAME and LJDLLNAME from environment variables. - "lua_ext_ccatch_error" // Throw lua errors which cannot be catched from lua standard + "lua_ext_ccatch_error", // Throw lua errors which cannot be catched from lua standard // pcall/xpcall but only from lua_pcall C API. ]; diff --git a/core/src/ffi/jit.rs b/core/src/ffi/jit.rs index 52d5d0e..5caa0d7 100644 --- a/core/src/ffi/jit.rs +++ b/core/src/ffi/jit.rs @@ -56,9 +56,9 @@ mod arm { pub const F_VFPV2: u32 = F_CPU << 4; pub const F_VFPV3: u32 = F_CPU << 5; - pub const F_ARMV6: u32 = F_ARMV6_|F_ARMV6T2_|F_ARMV7|F_ARMV8; - pub const F_ARMV6T2: u32 = F_ARMV6T2_|F_ARMV7|F_ARMV8; - pub const F_VFP: u32 = F_VFPV2|F_VFPV3; + pub const F_ARMV6: u32 = F_ARMV6_ | F_ARMV6T2_ | F_ARMV7 | F_ARMV8; + pub const F_ARMV6T2: u32 = F_ARMV6T2_ | F_ARMV7 | F_ARMV8; + pub const F_VFP: u32 = F_VFPV2 | F_VFPV3; } #[cfg(any(target_arch = "aarch64", target_arch = "arm"))] diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index d464745..a5307a3 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -48,7 +48,7 @@ pub enum ThreadStatus { ErrSyntax = 3, ErrMem = 4, ErrErr = 5, - ErrCcatch = 6 + ErrCcatch = 6, } #[repr(transparent)] diff --git a/core/src/ffi/mod.rs b/core/src/ffi/mod.rs index ec7e45f..e0b9bbd 100644 --- a/core/src/ffi/mod.rs +++ b/core/src/ffi/mod.rs @@ -27,6 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod ext; +pub mod jit; pub mod laux; pub mod lua; -pub mod jit; diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index 8c9aedf..c93b143 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -41,10 +41,14 @@ pub use bp3d_lua_codegen::decl_lua_plugin; /// [Lib](crate::libs::Lib). /// /// This function automatically translates the Rust result type to the C FFI compatible type. -pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: &mut error::Error) -> bool { +pub fn run_lua_register( + vm: &crate::vm::Vm, + lib: impl crate::libs::Lib, + error: &mut error::Error, +) -> bool { use crate::vm::error::Error; - use std::fmt::Write; use bp3d_util::format::MemBufStr; + use std::fmt::Write; let res = lib.register(vm); match res { Ok(()) => true, @@ -63,12 +67,14 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & } Error::Syntax(e) => { error.ty = error::ErrorType::Syntax; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::Runtime(e) => { error.ty = error::ErrorType::Runtime; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::Memory => error.ty = error::ErrorType::Memory, @@ -77,15 +83,21 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & Error::Null => error.ty = error::ErrorType::Null, Error::MultiValue => error.ty = error::ErrorType::MultiValue, Error::UserData(e) => match e { - crate::vm::userdata::Error::ArgsEmpty => error.ty = error::ErrorType::UserDataArgsEmpty, + crate::vm::userdata::Error::ArgsEmpty => { + error.ty = error::ErrorType::UserDataArgsEmpty + } crate::vm::userdata::Error::MutViolation(e) => { error.ty = error::ErrorType::UserDataMutViolation; error.static_string.data = e.as_ptr(); } crate::vm::userdata::Error::Gc => error.ty = error::ErrorType::UserDataGc, crate::vm::userdata::Error::Index => error.ty = error::ErrorType::UserDataIndex, - crate::vm::userdata::Error::Metatable => error.ty = error::ErrorType::UserDataMetatable, - crate::vm::userdata::Error::MultiValueField => error.ty = error::ErrorType::UserDataMultiValueField, + crate::vm::userdata::Error::Metatable => { + error.ty = error::ErrorType::UserDataMetatable + } + crate::vm::userdata::Error::MultiValueField => { + error.ty = error::ErrorType::UserDataMultiValueField + } crate::vm::userdata::Error::AlreadyRegistered(e) => { error.ty = error::ErrorType::UserDataAlreadyRegistered; error.static_string.data = e.as_ptr(); @@ -94,21 +106,23 @@ pub fn run_lua_register(vm: &crate::vm::Vm, lib: impl crate::libs::Lib, error: & error.ty = error::ErrorType::UserDataAlignment; error.alignment.alignment = e; } - } + }, Error::UnsupportedType(e) => { error.ty = error::ErrorType::UnsupportedType; error.unsupported_type.actual = e; } Error::Loader(e) => { error.ty = error::ErrorType::Loader; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } Error::ParseInt => error.ty = error::ErrorType::ParseInt, Error::ParseFloat => error.ty = error::ErrorType::ParseFloat, Error::UncatchableRuntime(e) => { error.ty = error::ErrorType::UncatchableRuntime; - let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; + let mut msg = + unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); } } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 7c4e94c..d386527 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::module::error::ErrorType; +use crate::module::{TIME_VERSION, VERSION}; +use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; use crate::vm::Vm; -use std::collections::HashSet; -use std::ffi::CStr; -use std::path::PathBuf; use bp3d_debug::info; +use bp3d_os::module::library::types::VirtualLibrary; use bp3d_os::module::library::Library; use bp3d_os::module::{Module, ModuleLoader}; -use bp3d_os::module::library::types::VirtualLibrary; use bp3d_util::simple_error; -use crate::module::{TIME_VERSION, VERSION}; -use crate::module::error::ErrorType; -use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; +use std::collections::HashSet; +use std::ffi::CStr; +use std::path::PathBuf; simple_error! { pub Error { @@ -51,64 +51,100 @@ simple_error! { pub type Result = std::result::Result; -type PluginFunc = extern "C" fn(l: crate::ffi::lua::State, error: *mut crate::module::error::Error) -> bool; +type PluginFunc = + extern "C" fn(l: crate::ffi::lua::State, error: *mut crate::module::error::Error) -> bool; -unsafe fn get_string(err: &crate::module::error::Error, f: impl FnOnce(String) -> crate::vm::error::Error) -> crate::vm::error::Error { +unsafe fn get_string( + err: &crate::module::error::Error, + f: impl FnOnce(String) -> crate::vm::error::Error, +) -> crate::vm::error::Error { match std::str::from_utf8(&err.string.data[..err.string.len]) { Ok(v) => f(v.into()), - Err(e) => crate::vm::error::Error::InvalidUtf8(e.into()) + Err(e) => crate::vm::error::Error::InvalidUtf8(e.into()), } } -unsafe fn convert_module_error_to_vm_error(err: crate::module::error::Error) -> crate::vm::error::Error { +unsafe fn convert_module_error_to_vm_error( + err: crate::module::error::Error, +) -> crate::vm::error::Error { match err.ty { ErrorType::Utf8 => crate::vm::error::Error::InvalidUtf8(Utf8Error { valid_up_to: err.utf8.valid_up_to, - error_len: if err.utf8.error_len < 0 { None } else { Some(err.utf8.error_len as u8) }, + error_len: if err.utf8.error_len < 0 { + None + } else { + Some(err.utf8.error_len as u8) + }, }), ErrorType::Type => crate::vm::error::Error::Type(TypeError { expected: err.type_mismatch.expected, - actual: err.type_mismatch.actual + actual: err.type_mismatch.actual, }), ErrorType::Syntax => get_string(&err, crate::vm::error::Error::Syntax), - ErrorType::Runtime => get_string(&err, |v| crate::vm::error::Error::Runtime(RuntimeError::new(v))), - ErrorType::UncatchableRuntime => get_string(&err, |v| crate::vm::error::Error::UncatchableRuntime(RuntimeError::new(v))), + ErrorType::Runtime => get_string(&err, |v| { + crate::vm::error::Error::Runtime(RuntimeError::new(v)) + }), + ErrorType::UncatchableRuntime => get_string(&err, |v| { + crate::vm::error::Error::UncatchableRuntime(RuntimeError::new(v)) + }), ErrorType::Memory => crate::vm::error::Error::Memory, ErrorType::Unknown => crate::vm::error::Error::Unknown, ErrorType::Error => crate::vm::error::Error::Error, ErrorType::Null => crate::vm::error::Error::Null, ErrorType::MultiValue => crate::vm::error::Error::MultiValue, - ErrorType::UnsupportedType => crate::vm::error::Error::UnsupportedType(err.unsupported_type.actual), + ErrorType::UnsupportedType => { + crate::vm::error::Error::UnsupportedType(err.unsupported_type.actual) + } ErrorType::Loader => get_string(&err, crate::vm::error::Error::Loader), ErrorType::ParseFloat => crate::vm::error::Error::ParseFloat, ErrorType::ParseInt => crate::vm::error::Error::ParseInt, - ErrorType::UserDataArgsEmpty => crate::vm::error::Error::UserData(crate::vm::userdata::Error::ArgsEmpty), - ErrorType::UserDataMutViolation => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data))), + ErrorType::UserDataArgsEmpty => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::ArgsEmpty) + } + ErrorType::UserDataMutViolation => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data)), + ), ErrorType::UserDataGc => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Gc), - ErrorType::UserDataIndex => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index), - ErrorType::UserDataMetatable => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable), - ErrorType::UserDataMultiValueField => crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField), - ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData(crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data))), - ErrorType::UserDataAlignment => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Alignment(err.alignment.alignment)), - ErrorType::None => std::hint::unreachable_unchecked() + ErrorType::UserDataIndex => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index) + } + ErrorType::UserDataMetatable => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable) + } + ErrorType::UserDataMultiValueField => { + crate::vm::error::Error::UserData(crate::vm::userdata::Error::MultiValueField) + } + ErrorType::UserDataAlreadyRegistered => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::AlreadyRegistered(CStr::from_ptr(err.static_string.data)), + ), + ErrorType::UserDataAlignment => crate::vm::error::Error::UserData( + crate::vm::userdata::Error::Alignment(err.alignment.alignment), + ), + ErrorType::None => std::hint::unreachable_unchecked(), } } pub struct ModuleManager { set: HashSet, - loader: ModuleLoader + loader: ModuleLoader, } impl ModuleManager { - fn load_plugin(vm: &Vm, module: &Module, name: &str, lib: &str, plugin: &str) -> Result<()> { + fn load_plugin( + vm: &Vm, + module: &Module, + name: &str, + lib: &str, + plugin: &str, + ) -> Result<()> { let func_name = format!("bp3d_lua_{}_register_{}", lib, plugin); let sym = unsafe { module.lib().load_symbol::(func_name) }? .ok_or_else(|| Error::PluginNotFound(name.into()))?; let mut err = crate::module::error::Error { - ty: ErrorType::None + ty: ErrorType::None, }; if !sym.call(vm.as_ptr(), &mut err) { - return Err(Error::Vm(unsafe { convert_module_error_to_vm_error(err) })) + return Err(Error::Vm(unsafe { convert_module_error_to_vm_error(err) })); } Ok(()) } @@ -119,8 +155,11 @@ impl ModuleManager { return Err(Error::PluginAlreadyLoaded(name)); } let module = unsafe { self.loader.load(lib) }?; - info!("Loaded dynamic module {:?}-{:?}", module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION")); + info!( + "Loaded dynamic module {:?}-{:?}", + module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION") + ); Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); @@ -134,13 +173,18 @@ impl ModuleManager { } let module = match unsafe { self.loader.load_builtin(lib) } { Ok(v) => v, - Err(e) => return match e { - bp3d_os::module::error::Error::NotFound(_) => Ok(false), - e => Err(Error::Module(e)) + Err(e) => { + return match e { + bp3d_os::module::error::Error::NotFound(_) => Ok(false), + e => Err(Error::Module(e)), + } } }; - info!("Loaded builtin module {:?}-{:?}", module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION")); + info!( + "Loaded builtin module {:?}-{:?}", + module.get_metadata_key("NAME"), + module.get_metadata_key("VERSION") + ); Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); @@ -164,7 +208,7 @@ impl ModuleManager { loader.add_public_dependency("time", TIME_VERSION); Self { set: Default::default(), - loader + loader, } } } diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 96f27f6..9e2c7c0 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; @@ -37,7 +38,6 @@ use std::mem::MaybeUninit; use std::sync::{Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; -use crate::ffi::ext::lua_ext_ccatch_error; pub struct Signal { l: State, diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index 7357694..a906e1e 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use super::Error; +use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; @@ -36,8 +38,6 @@ use std::time::Duration; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; use windows_sys::Win32::System::Threading::{GetCurrentThread, ResumeThread, SuspendThread}; -use crate::ffi::ext::lua_ext_ccatch_error; -use super::Error; static SIG_STATE: Mutex>> = Mutex::new(None); diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index b2000bf..f7d8d44 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_int; -use std::fmt::{Display, Formatter}; use crate::ffi::ext::{lua_ext_getjitflags, lua_ext_setjitflags, lua_ext_setjitmode}; use crate::ffi::jit; use crate::vm::{RootVm, Vm}; +use std::ffi::c_int; +use std::fmt::{Display, Formatter}; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub struct CpuArm { @@ -131,7 +131,7 @@ pub struct Opts { pub abc: bool, pub sink: bool, pub fuse: bool, - pub fma: bool + pub fma: bool, } impl Display for Opts { @@ -180,7 +180,7 @@ pub enum OptLevel { O1, O2, O3, - Unknown + Unknown, } impl Default for OptLevel { @@ -196,7 +196,7 @@ impl Display for OptLevel { OptLevel::O1 => write!(f, "O1"), OptLevel::O2 => write!(f, "O2"), OptLevel::O3 => write!(f, "O3"), - OptLevel::Unknown => write!(f, "Unknown") + OptLevel::Unknown => write!(f, "Unknown"), } } } @@ -205,7 +205,7 @@ impl Display for OptLevel { pub struct JitOptions { cur_flag_set: u32, mode: c_int, - opt_level_changed: bool + opt_level_changed: bool, } impl JitOptions { @@ -301,7 +301,7 @@ impl JitOptions { OptLevel::O0 | OptLevel::Unknown => self.cur_flag_set |= jit::F_OPT_0, OptLevel::O1 => self.cur_flag_set |= jit::F_OPT_1, OptLevel::O2 => self.cur_flag_set |= jit::F_OPT_2, - OptLevel::O3 => self.cur_flag_set |= jit::F_OPT_3 + OptLevel::O3 => self.cur_flag_set |= jit::F_OPT_3, } self.opt_level_changed = true; } @@ -311,7 +311,10 @@ impl JitOptions { assert_eq!(unsafe { lua_ext_setjitmode(vm.as_ptr(), self.mode) }, 0); } if self.opt_level_changed { - assert_eq!(unsafe { lua_ext_setjitflags(vm.as_ptr(), self.cur_flag_set) }, 0); + assert_eq!( + unsafe { lua_ext_setjitflags(vm.as_ptr(), self.cur_flag_set) }, + 0 + ); } } } diff --git a/core/src/vm/core/util.rs b/core/src/vm/core/util.rs index 87cd0a5..b61f0b3 100644 --- a/core/src/vm/core/util.rs +++ b/core/src/vm/core/util.rs @@ -136,7 +136,9 @@ pub unsafe fn pcall( // trace produced by luaL_traceback and remove it from the stack. let full_traceback: &str = FromLua::from_lua(vm, -1)?; lua_remove(l, -1); - Err(Error::UncatchableRuntime(RuntimeError::new(full_traceback.into()))) + Err(Error::UncatchableRuntime(RuntimeError::new( + full_traceback.into(), + ))) } _ => Err(Error::Unknown), } From a0ce80b5a00ef316b5b76a701fa419e108551b47 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 20:57:06 +0200 Subject: [PATCH 341/527] Fixed possible error in JitOptions --- core/src/vm/core/jit.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index b2000bf..bc6c5f0 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -273,7 +273,7 @@ impl JitOptions { if (self.cur_flag_set & jit::F_OPT_1) == jit::F_OPT_1 { return OptLevel::O1; } - if (self.cur_flag_set & jit::F_OPT_0) == jit::F_OPT_0 { + if (self.cur_flag_set & jit::F_OPT_MASK) == jit::F_OPT_0 { return OptLevel::O0; } OptLevel::Unknown From 3786f2d790289858865ce5356567860ddb377dcc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 8 Jun 2025 21:15:50 +0200 Subject: [PATCH 342/527] Fixed most clippy warnings --- build/src/build/mac.rs | 2 +- build/src/build/windows.rs | 2 +- build/src/info.rs | 6 +++--- build/src/util.rs | 2 +- codegen/src/lib.rs | 2 +- codegen/src/parser/enums.rs | 1 + core/src/ffi/jit.rs | 6 +++--- core/src/vm/core/jit.rs | 9 ++------- 8 files changed, 13 insertions(+), 17 deletions(-) diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 4403ae3..912460c 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -48,7 +48,7 @@ impl Build for MacOS { .env("MACOSX_DEPLOYMENT_TARGET", "10.11") .current_dir(info.build_dir()), ), - _ => Err(Error::new(ErrorKind::Other, "unsupported target")), + _ => Err(Error::other("unsupported target")), } } diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs index aabd094..f39fc28 100644 --- a/build/src/build/windows.rs +++ b/build/src/build/windows.rs @@ -40,7 +40,7 @@ impl Build for Windows { let mut cmd = Command::new("cmd"); cmd.arg("/C").arg("msvcbuild.bat"); let cl = cc::windows_registry::find_tool(info.target_name(), "cl.exe") - .ok_or(Error::new(ErrorKind::Other, "unable to find cl.exe"))?; + .ok_or(Error::other("unable to find cl.exe"))?; for (k, v) in cl.env() { cmd.env(k, v); } diff --git a/build/src/info.rs b/build/src/info.rs index f3c3ee3..10c5888 100644 --- a/build/src/info.rs +++ b/build/src/info.rs @@ -52,11 +52,11 @@ impl<'a> BuildInfo<'a> { let target_dir = base.build_dir.join("../../../.."); let start = manifest .find(VERSION) - .ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + .ok_or(Error::other("failed to find crate version"))?; let version = &manifest[start + VERSION.len()..]; let end = version .find("\"") - .ok_or(Error::new(ErrorKind::Other, "failed to find crate version"))?; + .ok_or(Error::other("failed to find crate version"))?; Ok(Self { base, target_dir, @@ -93,7 +93,7 @@ impl<'a> BuildInfo<'a> { Target::MacAmd64 | Target::MacAarch64 => MacOS::run(&self), Target::Linux => Linux::run(&self), Target::Windows => Windows::run(&self), - Target::Unsupported => Err(Error::new(ErrorKind::Other, "unsupported target")), + Target::Unsupported => Err(Error::other("unsupported target")), } } } diff --git a/build/src/util.rs b/build/src/util.rs index a678a94..a3069e0 100644 --- a/build/src/util.rs +++ b/build/src/util.rs @@ -43,7 +43,7 @@ impl CommandRunner { if stat.success() { Ok(()) } else { - Err(Error::new(ErrorKind::Other, self.msg)) + Err(Error::other(self.msg)) } } } diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index d919531..890713c 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -76,7 +76,7 @@ pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { let func_name = format!( "bp3d_lua_{}_register_{}", crate_name.replace("-", "_"), - ident.to_string() + ident ); let func = Ident::new(&func_name, ident.span()); let q = quote! { diff --git a/codegen/src/parser/enums.rs b/codegen/src/parser/enums.rs index b55a709..94b564d 100644 --- a/codegen/src/parser/enums.rs +++ b/codegen/src/parser/enums.rs @@ -40,6 +40,7 @@ pub struct EnumVariantMulti { pub fields: Vec, } +#[allow(clippy::large_enum_variant)] pub enum EnumVariant { SingleField(EnumVariantSingle), MultiField(EnumVariantMulti), diff --git a/core/src/ffi/jit.rs b/core/src/ffi/jit.rs index 52d5d0e..0b68d47 100644 --- a/core/src/ffi/jit.rs +++ b/core/src/ffi/jit.rs @@ -40,7 +40,7 @@ pub const F_CPU: u32 = 0x00000010; mod x86_64 { use crate::ffi::jit::F_CPU; - pub const F_SSE3: u32 = F_CPU << 0; + pub const F_SSE3: u32 = F_CPU; pub const F_SSE4_1: u32 = F_CPU << 1; pub const F_BMI2: u32 = F_CPU << 2; } @@ -49,7 +49,7 @@ mod x86_64 { mod arm { use crate::ffi::jit::F_CPU; - pub const F_ARMV6_: u32 = F_CPU << 0; + pub const F_ARMV6_: u32 = F_CPU; pub const F_ARMV6T2_: u32 = F_CPU << 1; pub const F_ARMV7: u32 = F_CPU << 2; pub const F_ARMV8: u32 = F_CPU << 3; @@ -70,7 +70,7 @@ pub use x86_64::*; pub const F_OPT: u32 = 0x00010000; pub const F_OPT_MASK: u32 = 0x0fff0000; -pub const F_OPT_FOLD: u32 = F_OPT << 0; +pub const F_OPT_FOLD: u32 = F_OPT; pub const F_OPT_CSE: u32 = F_OPT << 1; pub const F_OPT_DCE: u32 = F_OPT << 2; pub const F_OPT_FWD: u32 = F_OPT << 3; diff --git a/core/src/vm/core/jit.rs b/core/src/vm/core/jit.rs index bc6c5f0..18a0b9e 100644 --- a/core/src/vm/core/jit.rs +++ b/core/src/vm/core/jit.rs @@ -174,21 +174,16 @@ impl Display for Opts { } } -#[derive(Copy, Clone, Eq, PartialEq, Debug)] +#[derive(Copy, Clone, Eq, PartialEq, Debug, Default)] pub enum OptLevel { O0, O1, O2, + #[default] O3, Unknown } -impl Default for OptLevel { - fn default() -> Self { - OptLevel::O3 - } -} - impl Display for OptLevel { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { From ac8e522921e411b32c55af2ff1d437404ff897bd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 9 Jun 2025 20:52:49 +0200 Subject: [PATCH 343/527] Removed unused imports --- build/src/build/mac.rs | 2 +- build/src/build/windows.rs | 2 +- build/src/info.rs | 2 +- build/src/util.rs | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/build/src/build/mac.rs b/build/src/build/mac.rs index 912460c..26b0a97 100644 --- a/build/src/build/mac.rs +++ b/build/src/build/mac.rs @@ -30,7 +30,7 @@ use crate::build::Build; use crate::build::interface::Lib; use crate::util::CommandRunner; use crate::{BuildInfo, Target}; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::process::Command; pub struct MacOS; diff --git a/build/src/build/windows.rs b/build/src/build/windows.rs index f39fc28..0b551f7 100644 --- a/build/src/build/windows.rs +++ b/build/src/build/windows.rs @@ -30,7 +30,7 @@ use crate::BuildInfo; use crate::build::Build; use crate::build::interface::Lib; use crate::util::CommandRunner; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::process::Command; pub struct Windows; diff --git a/build/src/info.rs b/build/src/info.rs index 10c5888..54621f3 100644 --- a/build/src/info.rs +++ b/build/src/info.rs @@ -28,7 +28,7 @@ use crate::Target; use crate::build::{Build, Lib, Linux, MacOS, Windows}; -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::path::{Path, PathBuf}; pub struct BuildInfoBase<'a> { diff --git a/build/src/util.rs b/build/src/util.rs index a3069e0..ea0c5c6 100644 --- a/build/src/util.rs +++ b/build/src/util.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::io::{Error, ErrorKind}; +use std::io::Error; use std::process::Command; pub struct CommandRunner { From 5b469a0a2755c5060f58b51ca3caf0d257d02b48 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 9 Jun 2025 20:53:59 +0200 Subject: [PATCH 344/527] Removed unusable registry::named::Key and improved doc for set --- core/src/vm/registry/core.rs | 37 +--------- core/src/vm/registry/interface.rs | 6 ++ core/src/vm/registry/named.rs | 111 +----------------------------- 3 files changed, 9 insertions(+), 145 deletions(-) diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 8df37f9..8d01e20 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -85,22 +85,6 @@ impl RawKey { luaL_unref(vm.as_ptr(), REGISTRYINDEX, self.0); } - /// Sets the content of this key with the value on top of the stack. - /// - /// # Arguments - /// - /// * `vm`: the vm associated with this key. - /// - /// returns: () - /// - /// # Safety - /// - /// This is UB to call if the key has already been deleted. - #[inline(always)] - pub unsafe fn set(&self, vm: &Vm) { - lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); - } - /// Creates a new [RawKey] from the top of the lua stack. /// /// # Arguments @@ -129,7 +113,7 @@ impl FromIndex for RawKey { impl Set for RawKey { unsafe fn set(&self, vm: &Vm, index: i32) { ensure_value_top(vm, index); - self.set(vm); + lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); } } @@ -178,25 +162,6 @@ impl Key { unsafe { self.raw.delete(vm) }; } - /// Creates a new [Key] from the top of the lua stack. - /// - /// # Arguments - /// - /// * `vm`: the [Vm] instance representing the lua stack. - /// - /// returns: RegistryKey - /// - /// # Safety - /// - /// The type T must match the type of the value at the top of the stack. Additionally, the value - /// at the top of the stack must not be referenced as it will be popped. - pub unsafe fn from_top(vm: &Vm) -> Key { - Key { - raw: RawKey::from_top(vm), - useless: PhantomData, - } - } - #[inline(always)] pub fn new(value: T::Value<'_>) -> Key { Key { diff --git a/core/src/vm/registry/interface.rs b/core/src/vm/registry/interface.rs index 29fe9f8..c9f9f7b 100644 --- a/core/src/vm/registry/interface.rs +++ b/core/src/vm/registry/interface.rs @@ -43,6 +43,9 @@ pub trait FromIndex { /// /// This function removes the value at index `index` and so assumes no more references exists /// to it, failure to ensure this is UB. + /// + /// When using [Key](crate::vm::registry::core::Key), the type of the lua value at index `index` + /// must be the same as the type of key, if not this is UB. unsafe fn from_index(vm: &Vm, index: i32) -> Self; } @@ -63,6 +66,9 @@ pub trait Set { /// to it, failure to ensure this is UB. The function also assumes this generic key still /// exists in the registry table. Finally, this assumes this key does not conflict with a /// different one. + /// + /// When using [Key](crate::vm::registry::core::Key), the type of the lua value at index `index` + /// must be the same as the type of key, if not this is UB. unsafe fn set(&self, vm: &Vm, index: i32); } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index e449ac0..d6931ed 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -27,15 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{ - lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_pushnil, lua_rawget, + lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX, }; -use crate::vm::registry::{Set, Value}; +use crate::vm::registry::Set; use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; -use std::cell::Cell; use std::ffi::c_void; -use std::marker::PhantomData; #[derive(Debug, Copy, Clone, Eq, PartialEq)] pub struct RawKey(*const c_void); @@ -107,8 +105,6 @@ impl Set for RawKey { } } -struct InitKey(*const c_void); - const USED_KEYS: RawKey = RawKey::new("__used_keys__"); unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { @@ -137,106 +133,3 @@ unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { // value. lua_settop(l, -2); // Clear the used keys table from the stack. } - -impl Set for InitKey { - unsafe fn set(&self, vm: &Vm, index: i32) { - check_key_already_used(vm, self.0); - let l = vm.as_ptr(); - ensure_value_top(vm, index); - rawsetp(l, REGISTRYINDEX, self.0); - } -} - -#[derive(Clone)] -pub struct Key { - raw: RawKey, - registered: Cell, - useless: PhantomData<*const T>, -} - -impl PartialEq for Key { - fn eq(&self, other: &Self) -> bool { - self.raw.eq(&other.raw) - } -} - -impl Eq for Key {} - -impl Key { - #[inline(always)] - fn ensure_registered(&self, vm: &Vm) { - if !self.registered.get() { - unsafe { check_key_already_used(vm, self.raw.0) }; - self.registered.set(true); - } - } - - /// Pushes the lua value associated to this registry key on the lua stack. - /// - /// # Arguments - /// - /// * `vm`: the [Vm] to attach the produced lua value to. - /// - /// returns: ::Value - pub fn push<'a>(&self, vm: &'a Vm) -> Option> { - self.ensure_registered(vm); - unsafe { - self.raw.push(vm); - if lua_type(vm.as_ptr(), -1) == Type::Nil { - lua_settop(vm.as_ptr(), -2); - return None; - } - Some(T::from_registry(vm, -1)) - } - } - - /// Pushes the lua value associated to this registry key on the lua stack. - /// - /// # Arguments - /// - /// * `vm`: the [Vm] to attach the produced lua value to. - /// - /// returns: ::Value - #[inline(always)] - pub fn as_raw(&self) -> RawKey { - self.raw - } - - /// Resets the value pointed to by this registry key from the specified [Vm]. - /// - /// # Arguments - /// - /// * `vm`: the [Vm] to unregister from. - /// - /// returns: () - #[inline(always)] - pub fn reset(&self, vm: &Vm) { - unsafe { - lua_pushnil(vm.as_ptr()); - self.raw.set(vm, -1); - } - } - - /// Sets the value for this key. - /// - /// # Arguments - /// - /// * `value`: the value to replace with. - /// - /// returns: () - pub fn set(&self, value: T::Value<'_>) { - if self.registered.get() { - unsafe { T::set_registry(&InitKey(self.raw.0), value) }; - } else { - unsafe { T::set_registry(&self.raw, value) }; - } - } - - pub const fn new(name: &str) -> Self { - Key { - raw: RawKey::new(name), - registered: Cell::new(false), - useless: PhantomData, - } - } -} From 0f0bdcc63291f483ceaed3b71b1a5a19802a2ccd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 9 Jun 2025 22:21:35 +0200 Subject: [PATCH 345/527] Added safety flag to avoid binding a lua hook if the Vm has already terminated --- core/src/vm/core/interrupt/unix.rs | 10 ++++++++-- core/src/vm/core/interrupt/windows.rs | 10 ++++++++-- core/src/vm/core/root_vm.rs | 9 +++++++++ 3 files changed, 25 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 9e2c7c0..759ebc7 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -35,7 +35,8 @@ use crate::vm::RootVm; use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use std::mem::MaybeUninit; -use std::sync::{Mutex, Once}; +use std::sync::{Arc, Mutex, Once}; +use std::sync::atomic::AtomicBool; use std::thread::ThreadId; use std::time::Duration; @@ -43,6 +44,7 @@ pub struct Signal { l: State, thread: ThreadId, th: pthread_t, + alive: Arc } struct SigState { @@ -104,6 +106,7 @@ static SIG_BOUND: Once = Once::new(); impl Signal { pub fn create(vm: &mut RootVm) -> Self { + let alive = RootVm::get_alive(vm).clone(); let th = unsafe { pthread_self() }; let l = vm.as_ptr(); let thread = std::thread::current().id(); @@ -113,10 +116,13 @@ impl Signal { let ret = unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; assert_eq!(ret, 0); }); - Self { l, thread, th } + Self { l, thread, th, alive } } pub fn send(&self, duration: Duration) -> Result<(), Error> { + if !self.alive.load(std::sync::atomic::Ordering::SeqCst) { + return Ok(()); + } let (send, recv) = std::sync::mpsc::channel(); let (send2, recv2) = std::sync::mpsc::channel(); { diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index a906e1e..acd1988 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -33,7 +33,8 @@ use crate::ffi::lua::{ }; use crate::vm::RootVm; use bp3d_debug::{error, warning}; -use std::sync::Mutex; +use std::sync::{Arc, Mutex}; +use std::sync::atomic::AtomicBool; use std::time::Duration; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; @@ -60,16 +61,21 @@ extern "C-unwind" fn lua_interrupt(l: State, _: Debug) { pub struct Signal { l: State, th: HANDLE, + alive: Arc, } impl Signal { pub fn create(vm: &mut RootVm) -> Self { + let alive = RootVm::get_alive(vm).clone(); let th = unsafe { GetCurrentThread() }; let l = vm.as_ptr(); - Self { l, th } + Self { l, th, alive } } pub fn send(&self, duration: Duration) -> Result<(), Error> { + if !self.alive.load(std::sync::atomic::Ordering::SeqCst) { + return Ok(()); + } let (send2, recv2) = std::sync::mpsc::channel(); { let mut lock = SIG_STATE diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index 8239a0b..f7d356a 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -33,6 +33,8 @@ use crate::vm::Vm; use bp3d_debug::debug; use std::cell::Cell; use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use std::sync::atomic::{AtomicBool, Ordering}; thread_local! { // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! @@ -41,6 +43,7 @@ thread_local! { pub struct RootVm { vm: Vm, + alive: Arc } impl Default for RootVm { @@ -59,10 +62,15 @@ impl RootVm { HAS_VM.set(true); let mut vm = RootVm { vm: unsafe { Vm::from_raw(l) }, + alive: Arc::new(AtomicBool::new(true)), }; unsafe { Pool::new_in_vm(&mut vm) }; vm } + + pub fn get_alive(this: &Self) -> &Arc { + &this.alive + } } impl Deref for RootVm { @@ -91,6 +99,7 @@ impl Drop for RootVm { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); } + self.alive.store(false, Ordering::SeqCst); HAS_VM.set(false); } } From 95936cddbb0046730ec015fbb108e21268c48a5a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 9 Jun 2025 22:22:01 +0200 Subject: [PATCH 346/527] Added is_uncatachable flag to vm Error --- core/src/vm/error.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index 97d3660..c1add07 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -144,6 +144,10 @@ simple_error! { } impl Error { + pub fn is_uncatchable(&self) -> bool { + matches!(self, Error::UncatchableRuntime(_)) + } + pub fn into_runtime(self) -> Option { match self { Error::Runtime(e) => Some(e), From 86ba45ebfa4570e862740bd0454f8b0d5dd2d8e1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 15 Jun 2025 16:50:46 +0200 Subject: [PATCH 347/527] Added initial version of shell IPC protocol --- shell/proto/Cargo.toml | 10 +++++++ shell/proto/build.rs | 34 +++++++++++++++++++++ shell/proto/protoc.toml | 11 +++++++ shell/proto/src/lib.rs | 30 +++++++++++++++++++ shell/proto/src/recv.json5 | 59 +++++++++++++++++++++++++++++++++++++ shell/proto/src/send.json5 | 60 ++++++++++++++++++++++++++++++++++++++ 6 files changed, 204 insertions(+) create mode 100644 shell/proto/Cargo.toml create mode 100644 shell/proto/build.rs create mode 100644 shell/proto/protoc.toml create mode 100644 shell/proto/src/lib.rs create mode 100644 shell/proto/src/recv.json5 create mode 100644 shell/proto/src/send.json5 diff --git a/shell/proto/Cargo.toml b/shell/proto/Cargo.toml new file mode 100644 index 0000000..bc7b7e6 --- /dev/null +++ b/shell/proto/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "bp3d-lua-shell-proto" +version = "0.1.0" +edition = "2024" + +[dependencies] +bp3d-proto = { version = "1.0.0-rc.5.0.1", features = ["tokio"] } + +[build-dependencies] +bp3d-protoc = { version = "1.0.0-rc.5.0.0", features = ["api-rust"] } diff --git a/shell/proto/build.rs b/shell/proto/build.rs new file mode 100644 index 0000000..335b928 --- /dev/null +++ b/shell/proto/build.rs @@ -0,0 +1,34 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::Path; +use bp3d_protoc::api::generate_rust; + +fn main() { + generate_rust(Path::new("./protoc.toml")).unwrap(); +} diff --git a/shell/proto/protoc.toml b/shell/proto/protoc.toml new file mode 100644 index 0000000..1d6c08e --- /dev/null +++ b/shell/proto/protoc.toml @@ -0,0 +1,11 @@ +[package] +name = "" +path = "src" + +[recv] +write-async = true +union-set-discriminant = true + +[send] +write-async = true +union-set-discriminant = true diff --git a/shell/proto/src/lib.rs b/shell/proto/src/lib.rs new file mode 100644 index 0000000..4ee8ba5 --- /dev/null +++ b/shell/proto/src/lib.rs @@ -0,0 +1,30 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +include!(env!("BP3D_PROTOC_SEND")); +include!(env!("BP3D_PROTOC_RECV")); diff --git a/shell/proto/src/recv.json5 b/shell/proto/src/recv.json5 new file mode 100644 index 0000000..ae89310 --- /dev/null +++ b/shell/proto/src/recv.json5 @@ -0,0 +1,59 @@ +{ + name: "recv", + enums: [ + { + name: "Type", + variants: { + "End": 0, + "Log": 1 + } + } + ], + structs: [ + { + name: "Header", + fields: [ + { + name: "type", + raw: { + type: "unsigned", + bits: 8 + }, + view: { + type: "enum", + name: "Type" + } + } + ] + } + ], + messages: [ + { + name: "Log", + fields: [ + { + name: "source", + value: { + type: "string" + } + }, + { + name: "msg", + value: { + type: "string" + } + } + ] + } + ], + unions: [ + { + name: "Message", + discriminant: "Header.type", + cases: [ + { name: "End", case: "End" }, + { name: "Log", case: "Log", item_type: "Log" } + ] + } + ] +} diff --git a/shell/proto/src/send.json5 b/shell/proto/src/send.json5 new file mode 100644 index 0000000..8095056 --- /dev/null +++ b/shell/proto/src/send.json5 @@ -0,0 +1,60 @@ +{ + name: "send", + enums: [ + { + name: "Type", + variants: { + "Terminate": 0, + "RunCode": 1 + } + } + ], + structs: [ + { + name: "Header", + fields: [ + { + name: "type", + raw: { + type: "unsigned", + bits: 8 + }, + view: { + type: "enum", + name: "Type" + } + } + ] + } + ], + messages: [ + { + name: "RunCode", + fields: [ + { + name: "name", + optional: true, + value: { + type: "string" + } + }, + { + name: "code", + value: { + type: "string" + } + } + ] + } + ], + unions: [ + { + name: "Message", + discriminant: "Header.type", + cases: [ + { name: "Terminate", case: "Terminate" }, + { name: "RunCode", case: "RunCode", item_type: "RunCode" } + ] + } + ] +} From bd555439e8eb689844b13b82d8b02f06f483c065 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 15 Jun 2025 17:22:44 +0200 Subject: [PATCH 348/527] Fixed missing main message in IPC proto --- shell/proto/Cargo.toml | 3 ++- shell/proto/protoc.toml | 2 -- shell/proto/src/recv.json5 | 17 +++++++++++++++++ shell/proto/src/send.json5 | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) diff --git a/shell/proto/Cargo.toml b/shell/proto/Cargo.toml index bc7b7e6..619db21 100644 --- a/shell/proto/Cargo.toml +++ b/shell/proto/Cargo.toml @@ -2,9 +2,10 @@ name = "bp3d-lua-shell-proto" version = "0.1.0" edition = "2024" +publish = false [dependencies] -bp3d-proto = { version = "1.0.0-rc.5.0.1", features = ["tokio"] } +bp3d-proto = "1.0.0-rc.5.0.1" [build-dependencies] bp3d-protoc = { version = "1.0.0-rc.5.0.0", features = ["api-rust"] } diff --git a/shell/proto/protoc.toml b/shell/proto/protoc.toml index 1d6c08e..a89a993 100644 --- a/shell/proto/protoc.toml +++ b/shell/proto/protoc.toml @@ -3,9 +3,7 @@ name = "" path = "src" [recv] -write-async = true union-set-discriminant = true [send] -write-async = true union-set-discriminant = true diff --git a/shell/proto/src/recv.json5 b/shell/proto/src/recv.json5 index ae89310..27efc59 100644 --- a/shell/proto/src/recv.json5 +++ b/shell/proto/src/recv.json5 @@ -44,6 +44,23 @@ } } ] + }, + { + name: "Main", + fields: [ + { + name: "hdr", + item_type: "Header" + }, + { + name: "msg", + header: "hdr", + value: { + type: "union", + name: "Message" + } + } + ] } ], unions: [ diff --git a/shell/proto/src/send.json5 b/shell/proto/src/send.json5 index 8095056..7aaa934 100644 --- a/shell/proto/src/send.json5 +++ b/shell/proto/src/send.json5 @@ -45,6 +45,23 @@ } } ] + }, + { + name: "Main", + fields: [ + { + name: "hdr", + item_type: "Header" + }, + { + name: "msg", + header: "hdr", + value: { + type: "union", + name: "Message" + } + } + ] } ], unions: [ From fe5f50e99b7eaaf00d4970170bef9d865907a01c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 15 Jun 2025 20:18:40 +0200 Subject: [PATCH 349/527] Added support for running lua files --- shell/proto/src/send.json5 | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/shell/proto/src/send.json5 b/shell/proto/src/send.json5 index 7aaa934..a154ca4 100644 --- a/shell/proto/src/send.json5 +++ b/shell/proto/src/send.json5 @@ -5,7 +5,8 @@ name: "Type", variants: { "Terminate": 0, - "RunCode": 1 + "RunCode": 1, + "RunFile": 2 } } ], @@ -28,6 +29,17 @@ } ], messages: [ + { + name: "RunFile", + fields: [ + { + name: "path", + value: { + type: "string" + } + } + ] + }, { name: "RunCode", fields: [ @@ -70,7 +82,8 @@ discriminant: "Header.type", cases: [ { name: "Terminate", case: "Terminate" }, - { name: "RunCode", case: "RunCode", item_type: "RunCode" } + { name: "RunCode", case: "RunCode", item_type: "RunCode" }, + { name: "RunFile", case: "RunFile", item_type: "RunFile" } ] } ] From 60c2fb3b8ee8c6418677367d0b48c0d78bbfe7f4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 15 Jun 2025 20:20:39 +0200 Subject: [PATCH 350/527] Added initial version of core shell --- shell/core/Cargo.toml | 15 ++++ shell/core/src/core.rs | 92 +++++++++++++++++++++ shell/core/src/lua.rs | 183 +++++++++++++++++++++++++++++++++++++++++ shell/core/src/main.rs | 62 ++++++++++++++ 4 files changed, 352 insertions(+) create mode 100644 shell/core/Cargo.toml create mode 100644 shell/core/src/core.rs create mode 100644 shell/core/src/lua.rs create mode 100644 shell/core/src/main.rs diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml new file mode 100644 index 0000000..6c54862 --- /dev/null +++ b/shell/core/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "bp3d-lua-shell" +version = "0.1.0" +edition = "2024" +publish = false + +[dependencies] +bp3d-lua = { version = "1.0.0-rc.2.1.0", path = "../../core", features = ["root-vm", "util-module", "libs", "dynamic", "interrupt"] } +bp3d-debug = "1.0.0-rc.6.2.0" +bp3d-net = { version = "1.0.0-rc.2.1.1", features = ["ipc"] } +tokio = { version = "1.45.1", features = ["full"] } +bp3d-util = { version = "2.2.0", features = ["result"] } +bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } +bp3d-proto = "1.0.0-rc.5.0.1" +clap = { version = "4.5.4", features = ["derive"] } diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs new file mode 100644 index 0000000..38321e3 --- /dev/null +++ b/shell/core/src/core.rs @@ -0,0 +1,92 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +const MAX_SIZE: usize = 4096; + +use bp3d_debug::{debug, error, info}; +use bp3d_net::ipc::{Client, Server}; +use bp3d_net::ipc::util::Message; +use bp3d_proto::message::{FromBytes, WriteSelf}; +use crate::lua::{Args, Lua}; +use bp3d_util::result::ResultExt; +use bp3d_lua_shell_proto::recv; +use bp3d_lua_shell_proto::send; + +async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Result { + let mut msg = Message::new(MAX_SIZE); + loop { + tokio::select! { + res = client.recv(&mut msg) => { + res?; + if msg.is_empty() { + break; + } + let data: &[u8] = &msg; + //Nice weird broken syntax because Rust type inference is even more broken as ever. + let msg = ::from_bytes(data)?.into_inner(); + match msg.msg { + send::Message::Terminate => return Ok(true), + send::Message::RunCode(v) => match v.name { + Some(name) => lua.exec_with_name(name.into(), v.code.into()).await, + None => lua.exec(v.code.into()).await + }, + send::Message::RunFile(v) => lua.exec_file(v.path.into()).await + } + }, + Some((source, m)) = lua.next_log() => { + recv::Main { + hdr: recv::Header::new().to_ref(), + msg: recv::Log { source, msg: &m } + }.write_self(&mut msg)?; + } + } + } + client.close().await?; + Ok(false) +} + +pub async fn run(args: Args, name: &str) { + info!("starting lua VM"); + let mut lua = Lua::new(args); + info!("starting IPC server"); + let mut server = Server::create(name).await.expect_exit("Failed to create IPC server", 1); + while let Ok(client) = server.accept().await { + debug!("client connected"); + match client_task(&mut lua, client).await { + Err(e) => error!("client message error: {}", e), + Ok(flag) => { + debug!("client nominal exit"); + if flag { + break; + } + } + } + } + info!("terminating lua VM..."); + lua.exit().await; +} diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs new file mode 100644 index 0000000..c79cdbc --- /dev/null +++ b/shell/core/src/lua.rs @@ -0,0 +1,183 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::PathBuf; +use tokio::sync::mpsc; +use std::thread::JoinHandle; +use std::time::Duration; +use bp3d_lua::vm::core::interrupt::{spawn_interruptible, Signal}; +use bp3d_lua::libs; +use bp3d_lua::libs::Lib; +use bp3d_debug::{debug, error, info}; +use bp3d_lua::vm::core::jit::JitOptions; +use bp3d_lua::vm::core::load::{Code, Script}; +use bp3d_lua::vm::value::any::AnyValue; + +const CHANNEL_BUFFER: usize = 32; + +pub struct Args { + pub data: PathBuf, + pub lua: PathBuf, + pub modules: Vec +} + +pub enum Command { + Exit, + RunCode(String), + RunCodeWithName(String, String), + RunFile(String) +} + +pub struct Lua { + signal: Signal, + handle: JoinHandle<()>, + exec_queue: mpsc::Sender, + log_queue: mpsc::Receiver<(&'static str, String)>, +} + +impl Lua { + pub async fn exec(&self, code: String) { + self.exec_queue.send(Command::RunCode(code)).await.unwrap(); + } + + pub async fn exec_file(&self, name: String) { + self.exec_queue.send(Command::RunFile(name)).await.unwrap(); + } + + pub async fn exec_with_name(&self, name: String, code: String) { + self.exec_queue.send(Command::RunCodeWithName(name, code)).await.unwrap(); + } + + pub async fn exit(self) { + self.exec_queue.send(Command::Exit).await.unwrap(); + // Leave 50ms for the thread to terminate nominally before killing the VM. + tokio::time::sleep(Duration::from_millis(50)).await; + // This call will either immediately return because the thread is already dead (expected), + // otherwise it will pthread_kill and attempt to inject a lua hook which will later throw + // a lua uncatchable error which should bring the VM down. + let res = self.signal.send(Duration::from_secs(10)); + match res { + Ok(_) => self.handle.join().unwrap(), + Err(e) => error!("Error attempting to terminate VM thread: {}", e) + } + } + + pub async fn next_log(&mut self) -> Option<(&'static str, String)> { + self.log_queue.recv().await + } + + fn handle_value(res: bp3d_lua::vm::Result, logger: &mpsc::Sender<(&'static str, String)>) -> bool { + match res { + Ok(v) => { + logger.blocking_send(("output", v.to_string())).unwrap(); + false + }, + Err(e) => { + if e.is_uncatchable() { + logger.blocking_send(("kill", e.to_string())).unwrap(); + error!("Received VM termination error: {}", e); + true + } else { + logger.blocking_send(("error", e.to_string())).unwrap(); + error!("Failed to run code: {}", e); + false + } + } + } + } + + pub fn new(args: Args) -> Self { + let (exec_queue, mut receiver) = mpsc::channel(CHANNEL_BUFFER); + let (logger, log_queue) = mpsc::channel(CHANNEL_BUFFER); + let (signal, handle) = spawn_interruptible(move |vm| { + debug!("Loading VM libraries..."); + if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { + error!("Failed to load OS library: {}", e); + } + if let Err(e) = (libs::util::String, libs::util::Table, libs::util::Utf8).register(vm) { + error!("Failed to load util library: {}", e); + } + if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { + error!("Failed to load base library: {}", e); + } + let mut modules = libs::lua::Module::new(&[]); + for path in args.modules { + modules.add_search_path(path); + } + if let Err(e) = modules.register(vm) { + error!("Failed to load module manager: {}", e); + } + let jit = JitOptions::get(vm); + if jit.is_enabled() { + info!("JIT: ON ({})", jit.opt_level()); + info!("{}", jit.opts()); + info!("{}", jit.cpu()); + } else { + info!("JIT: OFF") + } + while let Some(command) = receiver.blocking_recv() { + match command { + Command::Exit => break, + Command::RunCode(code) => { + let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run_code(&*code), &logger))).unwrap(); + if ret { + break; + } + }, + Command::RunCodeWithName(name, code) => { + let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run(Code::new(&name, code.as_bytes())), &logger))).unwrap(); + if ret { + break; + } + }, + Command::RunFile(name) => { + let path = args.lua.join(name); + let script = match Script::from_path(path) { + Ok(script) => script, + Err(e) => { + error!("Error loading lua script: {}", e); + logger.blocking_send(("file", e.to_string())).unwrap(); + continue; + } + }; + let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run(script), &logger))).unwrap(); + if ret { + break; + } + } + } + } + }); + Self { + signal, + handle, + exec_queue, + log_queue + } + } +} diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs new file mode 100644 index 0000000..18567b9 --- /dev/null +++ b/shell/core/src/main.rs @@ -0,0 +1,62 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::PathBuf; +use clap::Parser; + +mod lua; +mod core; + +#[derive(Parser, Debug)] +#[command(version, about, long_about = None)] +struct Cli { + #[arg(short = 'n', long = "name", help = "Name of IPC server.")] + pub name: Option, + + #[arg(short = 'r', long = "root", help = "Path to lua root directory.")] + pub root: Option, + + #[arg(short = 'm', long = "modules", help = "Path to modules directory.")] + pub modules: Option +} + +#[tokio::main] +async fn main() { + let args = Cli::parse(); + let root = args.root.unwrap_or(PathBuf::from("./")); + let mut modules = Vec::new(); + if let Some(path) = args.modules { + modules.push(path); + } + modules.push(PathBuf::from("./target/debug")); + core::run(lua::Args { + data: root.join("data"), + lua: root.join("src"), + modules, + }, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; +} From 6dcee1105a23651fdf71a8ffb3129a4e0a85bf8c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 23 Jun 2025 20:57:06 +0200 Subject: [PATCH 351/527] Added support for setting rpath in built shell core binary --- shell/core/build.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 shell/core/build.rs diff --git a/shell/core/build.rs b/shell/core/build.rs new file mode 100644 index 0000000..c5bc1b2 --- /dev/null +++ b/shell/core/build.rs @@ -0,0 +1,32 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +fn main() { + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path"); + println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path/../Frameworks"); +} From 59a1397644f2bfc2fd083346f388c8d1993debff Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 28 Jun 2025 14:24:30 +0200 Subject: [PATCH 352/527] Updated bp3d-net --- shell/core/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index 6c54862..4fbaad7 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -7,7 +7,7 @@ publish = false [dependencies] bp3d-lua = { version = "1.0.0-rc.2.1.0", path = "../../core", features = ["root-vm", "util-module", "libs", "dynamic", "interrupt"] } bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-net = { version = "1.0.0-rc.2.1.1", features = ["ipc"] } +bp3d-net = { version = "1.0.0-rc.2.1.2", features = ["ipc"] } tokio = { version = "1.45.1", features = ["full"] } bp3d-util = { version = "2.2.0", features = ["result"] } bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } From 1a214caa0f723966121ea60c8cea11608c8f1cc8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 30 Jun 2025 21:05:18 +0200 Subject: [PATCH 353/527] Fixed log send --- shell/core/src/core.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index 38321e3..4f6f485 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -59,10 +59,12 @@ async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Resu } }, Some((source, m)) = lua.next_log() => { + msg.set_size(0); recv::Main { - hdr: recv::Header::new().to_ref(), + hdr: recv::Header::new().set_type(recv::Type::Log).to_ref(), msg: recv::Log { source, msg: &m } }.write_self(&mut msg)?; + client.send(&msg).await?; } } } From c43e20203e4c3962524dfd86c3249d992c503a5f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Jul 2025 09:58:39 +0200 Subject: [PATCH 354/527] Added shell proto and core --- Cargo.toml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 2dd45a2..dd2060f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -2,5 +2,7 @@ members = [ "build", "codegen", - "core" + "core", + "shell/core", + "shell/proto" ] From 9459058bd1d194a8c18e6fcde2ac9372ad26ab93 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Jul 2025 17:19:12 +0200 Subject: [PATCH 355/527] Added get_metatable to both Table and AnyUserData --- core/src/vm/table/core.rs | 22 +++++++++++++--------- core/src/vm/userdata/any.rs | 15 ++++++++++++--- core/src/vm/value/util.rs | 27 ++++++++++++++++++++++++++- 3 files changed, 51 insertions(+), 13 deletions(-) diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index e5bc701..022dc88 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,13 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{ - lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, - lua_setmetatable, lua_settable, lua_topointer, -}; +use crate::ffi::lua::{lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, lua_setmetatable, lua_settable, lua_topointer}; use crate::vm::table::iter::Iter; use crate::vm::table::traits::{GetTable, SetTable}; -use crate::vm::value::util::ensure_single_into_lua; +use crate::vm::value::util::{checked_get_metatable, ensure_single_into_lua}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -55,9 +52,7 @@ impl Clone for Table<'_> { impl PartialEq for Table<'_> { fn eq(&self, other: &Self) -> bool { - let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; - let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; - a == b + self.uid() == other.uid() } } @@ -68,7 +63,7 @@ impl Display for Table<'_> { write!( f, "table@{:X}", - unsafe { lua_topointer(self.vm.as_ptr(), self.index) } as usize + self.uid() ) } } @@ -99,6 +94,11 @@ impl<'a> Table<'a> { Self { vm, index } } + /// Returns a unique identifier to that table across the Vm it is attached to. + pub fn uid(&self) -> usize { + unsafe { lua_topointer(self.vm.as_ptr(), self.index) as _ } + } + pub fn new(vm: &'a Vm) -> Self { unsafe { lua_createtable(vm.as_ptr(), 0, 0) }; let index = unsafe { lua_gettop(vm.as_ptr()) }; @@ -125,6 +125,10 @@ impl<'a> Table<'a> { unsafe { lua_setmetatable(self.vm.as_ptr(), self.index) }; } + pub fn get_metatable(&self) -> Option
{ + checked_get_metatable(self.vm, self.index) + } + /// Returns the absolute index of this table on the Lua stack. #[inline(always)] pub fn index(&self) -> i32 { diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index e8a12a8..2b50b2e 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -33,6 +33,8 @@ use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::FromLua; use crate::vm::Vm; use std::fmt::{Debug, Display}; +use crate::vm::table::Table; +use crate::vm::value::util::checked_get_metatable; pub struct AnyUserData<'a> { vm: &'a Vm, @@ -51,9 +53,7 @@ impl Clone for AnyUserData<'_> { impl PartialEq for AnyUserData<'_> { fn eq(&self, other: &Self) -> bool { - let a = unsafe { lua_topointer(self.vm.as_ptr(), self.index) }; - let b = unsafe { lua_topointer(other.vm.as_ptr(), other.index) }; - a == b + self.uid() == other.uid() } } @@ -95,6 +95,11 @@ impl<'a> AnyUserData<'a> { Self { vm, index } } + /// Returns a unique identifier to that table across the Vm it is attached to. + pub fn uid(&self) -> usize { + unsafe { lua_topointer(self.vm.as_ptr(), self.index) as _ } + } + /// Returns a reference to this UserData value cast to `T`. #[inline(always)] pub fn get(&self) -> crate::vm::Result<&T> { @@ -120,6 +125,10 @@ impl<'a> AnyUserData<'a> { } Ok(unsafe { &mut *this_ptr }) } + + pub fn get_metatable(&self) -> Option
{ + checked_get_metatable(self.vm, self.index) + } } impl<'a> FromLua<'a> for AnyUserData<'a> { diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index b238a15..715be84 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushnil, lua_pushvalue, lua_replace, lua_settop, Type}; +use crate::ffi::lua::{lua_getmetatable, lua_pushnil, lua_pushvalue, lua_replace, lua_settop, lua_type, Type}; use crate::vm::error::{Error, TypeError}; +use crate::vm::table::Table; use crate::vm::value::IntoLua; use crate::vm::Vm; @@ -78,3 +79,27 @@ pub fn ensure_single_into_lua(vm: &Vm, value: impl IntoLua) -> crate::vm::Result } Ok(()) } + +/// Attempts to retrieve the metatable attached to the object at index `index`. +/// +/// If no metatable is found, this function automatically pops the value if needed and returns +/// [None]. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] instance to extract the metatable from. +/// * `index`: the object index inside the `vm` [Vm]. +/// +/// returns: Option
+pub fn checked_get_metatable(vm: &Vm, index: i32) -> Option
{ + unsafe { lua_getmetatable(vm.as_ptr(), index) }; + let ty = unsafe { lua_type(vm.as_ptr(), -1) }; + if ty == Type::Table { + return Some(unsafe { Table::from_raw(vm, vm.top()) }); + } + if ty != Type::None { + // A none type is special and implies no value was pushed on the stack. + unsafe { lua_settop(vm.as_ptr(), -2) }; // Pops the value from the stack. + } + None +} From 1ae736bb82b06a1f09076738453726faacd95ea1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 6 Jul 2025 17:31:50 +0200 Subject: [PATCH 356/527] Added FromParam impl for bool --- core/src/vm/function/core.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index c1e47cd..0fcaf7e 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -27,11 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; -use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_setmetatable, luaL_testudata}; -use crate::ffi::lua::{ - lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, - lua_pushnumber, lua_type, RawInteger, RawNumber, Type, -}; +use crate::ffi::laux::{luaL_checklstring, luaL_checktype, luaL_checkudata, luaL_setmetatable, luaL_testudata}; +use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_toboolean, lua_type, RawInteger, RawNumber, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; @@ -225,6 +222,19 @@ macro_rules! impl_float { impl_float!(f32, f64); +impl LuaType for bool { } + +impl FromParam<'_> for bool { + unsafe fn from_param(vm: &'_ Vm, index: i32) -> Self { + luaL_checktype(vm.as_ptr(), index, Type::Boolean); + lua_toboolean(vm.as_ptr(), index) != 0 + } + + fn try_from_param(vm: &'_ Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } +} + unsafe impl IntoParam for bool { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { From 303922d7bec44b0eb06f8944f500f05d2f3c5b20 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 15 Jul 2025 22:42:03 +0200 Subject: [PATCH 357/527] Rewrite shell command/response system --- shell/core/src/autocomplete.rs | 166 +++++++++++++++++++++++++++++++ shell/core/src/core.rs | 19 ++-- shell/core/src/data.rs | 43 ++++++++ shell/core/src/data_in.rs | 73 ++++++++++++++ shell/core/src/data_out.rs | 96 ++++++++++++++++++ shell/core/src/lua.rs | 112 ++++++++++----------- shell/core/src/main.rs | 4 + shell/proto/Cargo.toml | 4 +- shell/proto/protoc.toml | 7 +- shell/proto/src/completion.json5 | 105 +++++++++++++++++++ shell/proto/src/lib.rs | 1 + shell/proto/src/recv.json5 | 12 ++- 12 files changed, 564 insertions(+), 78 deletions(-) create mode 100644 shell/core/src/autocomplete.rs create mode 100644 shell/core/src/data.rs create mode 100644 shell/core/src/data_in.rs create mode 100644 shell/core/src/data_out.rs create mode 100644 shell/proto/src/completion.json5 diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs new file mode 100644 index 0000000..9fcbb6a --- /dev/null +++ b/shell/core/src/autocomplete.rs @@ -0,0 +1,166 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashSet; +use bp3d_lua::decl_closure; +use bp3d_lua::libs::Lib; +use bp3d_lua::util::Namespace; +use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::table::Table; +use bp3d_lua::vm::value::any::AnyValue; +use crate::data::DataOut; + +pub enum Mode { + AddUpdate(Vec), + Delete(Vec) +} + +#[derive(Debug, Eq, PartialEq)] +pub enum Type { + Function, + Attribute +} + +pub struct Item { + pub name: String, + pub ty: Type +} + +impl Item { + pub fn from_lua(name: &str, val: &AnyValue) -> Self { + match val { + AnyValue::Function(_) => Item { name: name.into(), ty: Type::Function }, + _ => Item { name: name.into(), ty: Type::Attribute } + } + } +} + +pub struct Completions { + pub path: String, + pub items: Vec +} + +fn get_capacity(val: &AnyValue) -> usize { + match val { + AnyValue::Function(_) => 0, + AnyValue::Table(v) => v.len(), + AnyValue::UserData(_) => 1, + _ => 0 + } +} + +fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: Table, metatables: bool) -> bp3d_lua::vm::Result<()> { + if set.contains(&value.uid()) { + return Ok(()); + } + for res in value.iter() { + let (k, v) = res?; + match k { + AnyValue::String(name) => { + let c = get_capacity(&v); + if c > 0 { + let mut path = path.clone(); + path.push(name.into()); + root.push(Completions { + path: path.join("."), + items: Vec::with_capacity(c) + }); + list_completions(set, path, root, v, metatables)?; + } else { + root.last_mut().unwrap().items.push(Item::from_lua(name, &v)); + } + } + _ => continue + } + } + if metatables { + if let Some(tbl) = value.get_metatable() { + list_table_completions(set, path, root, tbl, metatables)?; + } + } + set.insert(value.uid()); + Ok(()) +} + +fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: AnyValue, metatables: bool) -> bp3d_lua::vm::Result<()> { + match value { + AnyValue::Table(v) => list_table_completions(set, path, root, v, metatables), + AnyValue::UserData(v) => { + if let Some(tbl) = v.get_metatable() { + // We assume userdata have a single metatable (following current bp3d-lua pattern). + list_table_completions(set, path, root, tbl, false)?; + } + Ok(()) + } + _ => Ok(()) + } +} + +decl_closure! { + fn build_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { + let value: AnyValue = lua.get_global(name)?; + let mut root = Vec::new(); + let mut set = HashSet::new(); + list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; + ch.send(crate::data_out::Autocomplete(Mode::AddUpdate(root))); + Ok(()) + } +} + +decl_closure! { + fn delete_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { + let value: AnyValue = lua.get_global(name)?; + let mut root = Vec::new(); + let mut set = HashSet::new(); + list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; + let base = root.into_iter().map(|v| v.path); + ch.send(crate::data_out::Autocomplete(Mode::Delete(base.collect()))); + Ok(()) + } +} + +pub struct Autocomplete(std::rc::Rc); + +impl Autocomplete { + pub fn new(logger: DataOut) -> Autocomplete { + Autocomplete(logger.into()) + } +} + +impl Lib for Autocomplete { + const NAMESPACE: &'static str = "bp3d.lua.shell"; + + fn load(&self, namespace: &mut Namespace) -> bp3d_lua::vm::Result<()> { + let rc = Rc::from_rust(namespace.vm(), self.0.clone()); + let rc1 = Rc::from_rust(namespace.vm(), self.0.clone()); + namespace.add([ + ("buildCompletions", build_completions(rc)), + ("deleteCompletions", delete_completions(rc1)) + ]) + } +} diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index 4f6f485..b396ed2 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -31,10 +31,9 @@ const MAX_SIZE: usize = 4096; use bp3d_debug::{debug, error, info}; use bp3d_net::ipc::{Client, Server}; use bp3d_net::ipc::util::Message; -use bp3d_proto::message::{FromBytes, WriteSelf}; +use bp3d_proto::message::FromBytes; use crate::lua::{Args, Lua}; use bp3d_util::result::ResultExt; -use bp3d_lua_shell_proto::recv; use bp3d_lua_shell_proto::send; async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Result { @@ -47,23 +46,17 @@ async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Resu break; } let data: &[u8] = &msg; - //Nice weird broken syntax because Rust type inference is even more broken as ever. + //Nice weird broken syntax because Rust type inference is even more broken than ever. let msg = ::from_bytes(data)?.into_inner(); match msg.msg { send::Message::Terminate => return Ok(true), - send::Message::RunCode(v) => match v.name { - Some(name) => lua.exec_with_name(name.into(), v.code.into()).await, - None => lua.exec(v.code.into()).await - }, - send::Message::RunFile(v) => lua.exec_file(v.path.into()).await + send::Message::RunCode(v) => lua.send(v).await, + send::Message::RunFile(v) => lua.send(v).await } }, - Some((source, m)) = lua.next_log() => { + Some(b) = lua.next_msg() => { msg.set_size(0); - recv::Main { - hdr: recv::Header::new().set_type(recv::Type::Log).to_ref(), - msg: recv::Log { source, msg: &m } - }.write_self(&mut msg)?; + b.write(&mut msg)?; client.send(&msg).await?; } } diff --git a/shell/core/src/data.rs b/shell/core/src/data.rs new file mode 100644 index 0000000..eea86e8 --- /dev/null +++ b/shell/core/src/data.rs @@ -0,0 +1,43 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use tokio::sync::mpsc; +use crate::data_out::OutData; + +#[derive(Clone)] +pub struct DataOut(mpsc::Sender>); + +impl DataOut { + pub fn new(sender: mpsc::Sender>) -> Self { + Self(sender) + } + + pub fn send(&self, data: T) { + self.0.blocking_send(Box::new(data)).unwrap(); + } +} diff --git a/shell/core/src/data_in.rs b/shell/core/src/data_in.rs new file mode 100644 index 0000000..76f72b1 --- /dev/null +++ b/shell/core/src/data_in.rs @@ -0,0 +1,73 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::Vm; +use crate::data::DataOut; +use crate::lua::Args; + +pub trait InData: Send { + fn handle(&mut self, args: &Args, vm: &Vm, out: &DataOut) -> bool; +} + +pub trait NetInData { + fn to_in_data(self) -> Box; +} + +pub struct RunCode { + pub name: Option, + pub code: String, +} + +pub struct RunFile { + pub path: String, +} + +impl<'a> NetInData for bp3d_lua_shell_proto::send::RunFile<'a> { + fn to_in_data(self) -> Box { + Box::new(RunFile { + path: self.path.into() + }) + } +} + +impl<'a> NetInData for bp3d_lua_shell_proto::send::RunCode<'a> { + fn to_in_data(self) -> Box { + Box::new(RunCode { + name: self.name.map(|v| v.into()), + code: self.code.into() + }) + } +} + +pub struct Exit; + +impl InData for Exit { + fn handle(&mut self, _: &Args, _: &Vm, _: &DataOut) -> bool { + true + } +} diff --git a/shell/core/src/data_out.rs b/shell/core/src/data_out.rs new file mode 100644 index 0000000..6e11423 --- /dev/null +++ b/shell/core/src/data_out.rs @@ -0,0 +1,96 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_net::ipc::util::Message; +use bp3d_proto::message::WriteSelf; +use bp3d_lua_shell_proto::recv; +use bp3d_lua_shell_proto::completion; +use crate::autocomplete::{Mode, Type}; + +pub trait OutData: Send { + fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()>; +} + +pub struct Log(pub &'static str, pub String); + +impl OutData for Log { + fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()> { + recv::Main { + hdr: recv::Header::new().set_type(recv::Type::Log).to_ref(), + msg: recv::Log { source: self.0, msg: &self.1 } + }.write_self(msg) + } +} + +pub struct Autocomplete(pub Mode); + +impl OutData for Autocomplete { + fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()> { + match &self.0 { + Mode::AddUpdate(v) => { + let mut items = completion::AddUpdateItems::new(Vec::::new()); + for completion in v { + let mut items2 = completion::ListItems::new(Vec::::new()); + for item in &completion.items { + let ty = match item.ty { + Type::Function => completion::Type::Function, + Type::Attribute => completion::Type::Attribute + }; + items2.write_item(&completion::Item { + hdr: completion::Header::new().set_type(ty).to_ref(), + name: &item.name + })?; + } + items.write_item(&completion::List { + path: &completion.path, + items: items2.to_ref() + })?; + } + recv::Main { + hdr: recv::Header::new().set_type(recv::Type::AutocompleteAddUpdate).to_ref(), + msg: completion::AddUpdate { + items: items.to_ref() + } + }.write_self(msg)?; + } + Mode::Delete(v) => { + let mut items = completion::DeleteItems::new(Vec::::new()); + for path in v { + items.write_item(&completion::Path { path })?; + } + recv::Main { + hdr: recv::Header::new().set_type(recv::Type::AutocompleteDelete).to_ref(), + msg: completion::Delete { + items: items.to_ref() + } + }.write_self(msg)?; + } + } + Ok(()) + } +} diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index c79cdbc..b38743c 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -37,6 +37,11 @@ use bp3d_debug::{debug, error, info}; use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; use bp3d_lua::vm::value::any::AnyValue; +use bp3d_lua::vm::Vm; +use crate::autocomplete::Autocomplete; +use crate::data::DataOut; +use crate::data_in::{Exit, InData, NetInData, RunCode, RunFile}; +use crate::data_out::{Log, OutData}; const CHANNEL_BUFFER: usize = 32; @@ -46,35 +51,21 @@ pub struct Args { pub modules: Vec } -pub enum Command { - Exit, - RunCode(String), - RunCodeWithName(String, String), - RunFile(String) -} - pub struct Lua { signal: Signal, handle: JoinHandle<()>, - exec_queue: mpsc::Sender, - log_queue: mpsc::Receiver<(&'static str, String)>, + exec_queue: mpsc::Sender>, + out_queue: mpsc::Receiver>, } impl Lua { - pub async fn exec(&self, code: String) { - self.exec_queue.send(Command::RunCode(code)).await.unwrap(); - } - - pub async fn exec_file(&self, name: String) { - self.exec_queue.send(Command::RunFile(name)).await.unwrap(); - } - - pub async fn exec_with_name(&self, name: String, code: String) { - self.exec_queue.send(Command::RunCodeWithName(name, code)).await.unwrap(); + pub async fn send(&self, net_data: T) { + let data = net_data.to_in_data(); + self.exec_queue.send(data).await.unwrap(); } pub async fn exit(self) { - self.exec_queue.send(Command::Exit).await.unwrap(); + self.exec_queue.send(Box::new(Exit)).await.unwrap(); // Leave 50ms for the thread to terminate nominally before killing the VM. tokio::time::sleep(Duration::from_millis(50)).await; // This call will either immediately return because the thread is already dead (expected), @@ -87,23 +78,23 @@ impl Lua { } } - pub async fn next_log(&mut self) -> Option<(&'static str, String)> { - self.log_queue.recv().await + pub async fn next_msg(&mut self) -> Option> { + self.out_queue.recv().await } - fn handle_value(res: bp3d_lua::vm::Result, logger: &mpsc::Sender<(&'static str, String)>) -> bool { + fn handle_value(res: bp3d_lua::vm::Result, logger: &DataOut) -> bool { match res { Ok(v) => { - logger.blocking_send(("output", v.to_string())).unwrap(); + logger.send(Log("output", v.to_string())); false }, Err(e) => { if e.is_uncatchable() { - logger.blocking_send(("kill", e.to_string())).unwrap(); + logger.send(Log("kill", e.to_string())); error!("Received VM termination error: {}", e); true } else { - logger.blocking_send(("error", e.to_string())).unwrap(); + logger.send(Log("error", e.to_string())); error!("Failed to run code: {}", e); false } @@ -113,8 +104,9 @@ impl Lua { pub fn new(args: Args) -> Self { let (exec_queue, mut receiver) = mpsc::channel(CHANNEL_BUFFER); - let (logger, log_queue) = mpsc::channel(CHANNEL_BUFFER); + let (logger, out_queue) = mpsc::channel(CHANNEL_BUFFER); let (signal, handle) = spawn_interruptible(move |vm| { + let logger = DataOut::new(logger); debug!("Loading VM libraries..."); if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { error!("Failed to load OS library: {}", e); @@ -125,9 +117,12 @@ impl Lua { if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { error!("Failed to load base library: {}", e); } + if let Err(e) = Autocomplete::new(logger.clone()).register(vm) { + error!("Failed to register autocomplete library: {}", e); + } let mut modules = libs::lua::Module::new(&[]); - for path in args.modules { - modules.add_search_path(path); + for path in &args.modules { + modules.add_search_path(path.clone()); } if let Err(e) = modules.register(vm) { error!("Failed to load module manager: {}", e); @@ -141,35 +136,10 @@ impl Lua { info!("JIT: OFF") } while let Some(command) = receiver.blocking_recv() { - match command { - Command::Exit => break, - Command::RunCode(code) => { - let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run_code(&*code), &logger))).unwrap(); - if ret { - break; - } - }, - Command::RunCodeWithName(name, code) => { - let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run(Code::new(&name, code.as_bytes())), &logger))).unwrap(); - if ret { - break; - } - }, - Command::RunFile(name) => { - let path = args.lua.join(name); - let script = match Script::from_path(path) { - Ok(script) => script, - Err(e) => { - error!("Error loading lua script: {}", e); - logger.blocking_send(("file", e.to_string())).unwrap(); - continue; - } - }; - let ret = vm.scope(|vm| Ok(Self::handle_value(vm.run(script), &logger))).unwrap(); - if ret { - break; - } - } + // Nice type-inference breakage with this box. + let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); + if ret { + break; } } }); @@ -177,7 +147,31 @@ impl Lua { signal, handle, exec_queue, - log_queue + out_queue + } + } +} + +impl InData for RunCode { + fn handle(&mut self, _: &Args, vm: &Vm, out: &DataOut) -> bool { + match &self.name { + Some(name) => Lua::handle_value(vm.run(Code::new(name, self.code.as_bytes())), out), + None => Lua::handle_value(vm.run_code(&*self.code), out) } } } + +impl InData for RunFile { + fn handle(&mut self, args: &Args, vm: &Vm, out: &DataOut) -> bool { + let path = args.lua.join(&self.path); + let script = match Script::from_path(path) { + Ok(script) => script, + Err(e) => { + error!("Error loading lua script: {}", e); + out.send(Log("file", e.to_string())); + return false; + } + }; + Lua::handle_value(vm.run(script), out) + } +} diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index 18567b9..1af66e5 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -31,6 +31,10 @@ use clap::Parser; mod lua; mod core; +mod autocomplete; +mod data_out; +mod data_in; +mod data; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] diff --git a/shell/proto/Cargo.toml b/shell/proto/Cargo.toml index 619db21..dc029b2 100644 --- a/shell/proto/Cargo.toml +++ b/shell/proto/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" publish = false [dependencies] -bp3d-proto = "1.0.0-rc.5.0.1" +bp3d-proto = "1.0.0-rc.5.1.0" [build-dependencies] -bp3d-protoc = { version = "1.0.0-rc.5.0.0", features = ["api-rust"] } +bp3d-protoc = { version = "1.0.0-rc.5.0.1", features = ["api-rust"] } diff --git a/shell/proto/protoc.toml b/shell/proto/protoc.toml index a89a993..94ddf28 100644 --- a/shell/proto/protoc.toml +++ b/shell/proto/protoc.toml @@ -2,8 +2,11 @@ name = "" path = "src" -[recv] +[options.recv] union-set-discriminant = true -[send] +[options.send] union-set-discriminant = true + +[options.completion] +list-wrappers = true diff --git a/shell/proto/src/completion.json5 b/shell/proto/src/completion.json5 new file mode 100644 index 0000000..4a4e3b8 --- /dev/null +++ b/shell/proto/src/completion.json5 @@ -0,0 +1,105 @@ +{ + name: "completion", + enums: [ + { + name: "Type", + variants: { + "Function": 0, + "Attribute": 1, + "Subtree": 2 + } + } + ], + structs: [ + { + name: "Header", + fields: [ + { + name: "type", + raw: { + type: "unsigned", + bits: 8 + }, + view: { + type: "enum", + name: "Type" + } + } + ] + } + ], + messages: [ + { + name: "Item", + fields: [ + { + name: "hdr", + item_type: "Header" + }, + { + name: "name", + value: { + type: "string" + } + } + ] + }, + { + name: "Path", + fields: [ + { + name: "path", + value: { + type: "string" + } + } + ] + }, + { + name: "List", + fields: [ + { + name: "path", + value: { + type: "string" + } + }, + { + name: "items", + value: { + type: "list", + max_len: 255, + item_type: "Item", + nested: true + } + } + ] + }, + { + name: "AddUpdate", + fields: [ + { + name: "items", + value: { + type: "list", + max_len: 255, + item_type: "List" + } + } + ] + }, + { + name: "Delete", + fields: [ + { + name: "items", + value: { + type: "list", + max_len: 255, + item_type: "Path" + } + } + ] + } + ] +} diff --git a/shell/proto/src/lib.rs b/shell/proto/src/lib.rs index 4ee8ba5..4e2b475 100644 --- a/shell/proto/src/lib.rs +++ b/shell/proto/src/lib.rs @@ -28,3 +28,4 @@ include!(env!("BP3D_PROTOC_SEND")); include!(env!("BP3D_PROTOC_RECV")); +include!(env!("BP3D_PROTOC_COMPLETION")); diff --git a/shell/proto/src/recv.json5 b/shell/proto/src/recv.json5 index 27efc59..a86ab53 100644 --- a/shell/proto/src/recv.json5 +++ b/shell/proto/src/recv.json5 @@ -1,11 +1,17 @@ { name: "recv", + imports: [ + { protocol: "completion", type: "AddUpdate" }, + { protocol: "completion", type: "Delete" } + ], enums: [ { name: "Type", variants: { "End": 0, - "Log": 1 + "Log": 1, + "AutocompleteAddUpdate": 2, + "AutocompleteDelete": 3 } } ], @@ -69,7 +75,9 @@ discriminant: "Header.type", cases: [ { name: "End", case: "End" }, - { name: "Log", case: "Log", item_type: "Log" } + { name: "Log", case: "Log", item_type: "Log" }, + { name: "AutocompleteAddUpdate", case: "AutocompleteAddUpdate", item_type: "AddUpdate" }, + { name: "AutocompleteDelete", case: "AutocompleteDelete", item_type: "Delete" } ] } ] From a07ef0dc0c814de2062cc0f7c6c92bc7a8d5a349 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 17 Jul 2025 22:25:23 +0200 Subject: [PATCH 358/527] Re-write lua thread binding design --- core/src/ffi/lua.rs | 1 + core/src/vm/core/vm.rs | 16 ++- core/src/vm/registry/types.rs | 1 + core/src/vm/{thread.rs => thread/core.rs} | 106 ++++++++------------ core/src/vm/thread/interface.rs | 91 +++++++++++++++++ core/src/vm/thread/mod.rs | 31 ++++++ core/src/vm/thread/value.rs | 115 ++++++++++++++++++++++ core/src/vm/value/any.rs | 2 +- 8 files changed, 292 insertions(+), 71 deletions(-) rename core/src/vm/{thread.rs => thread/core.rs} (63%) create mode 100644 core/src/vm/thread/interface.rs create mode 100644 core/src/vm/thread/mod.rs create mode 100644 core/src/vm/thread/value.rs diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index a5307a3..c7871a8 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -195,6 +195,7 @@ extern "C" { // Coroutine functions //--------------------- extern "C" { + pub fn lua_isyieldable(l: State) -> c_int; pub fn lua_yield(l: State, nresults: c_int) -> c_int; pub fn lua_resume(l: State, narg: c_int) -> ThreadStatus; pub fn lua_status(l: State) -> ThreadStatus; diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index bc8bdb7..cd78b10 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,14 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{ - lua_getfield, lua_gettop, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, - ThreadStatus, GLOBALSINDEX, REGISTRYINDEX, -}; +use crate::ffi::lua::{lua_getfield, lua_gettop, lua_isyieldable, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::core::AnyStr; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::core::{Load, LoadString}; use crate::vm::error::Error; +use crate::vm::thread::core::Thread; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::types::Function; @@ -180,4 +178,14 @@ impl Vm { Ok(FromLua::from_lua_unchecked(self, -1)) } } + + /// Attempts to turn this Vm into a lua [Thread] if it is yieldable, otherwise returns None. + pub fn as_thread(&self) -> Option> { + let yieldable = unsafe { lua_isyieldable(self.as_ptr()) }; + if yieldable == 1 { + Some(unsafe { Thread::from_raw(self.as_ptr()) }) + } else { + None + } + } } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 42e57e0..216edc2 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -28,3 +28,4 @@ pub struct Table; pub struct Function; +pub struct Thread; diff --git a/core/src/vm/thread.rs b/core/src/vm/thread/core.rs similarity index 63% rename from core/src/vm/thread.rs rename to core/src/vm/thread/core.rs index e64a779..6600300 100644 --- a/core/src/vm/thread.rs +++ b/core/src/vm/thread/core.rs @@ -26,19 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_remove, lua_resume, lua_status, lua_tothread, ThreadStatus, Type}; -use crate::util::core::SimpleDrop; -use crate::vm::core::LoadString; +use std::fmt::{Debug, Display}; +use std::marker::PhantomData; +use crate::ffi::lua::{lua_remove, lua_resume, lua_status, ThreadStatus}; use crate::vm::error::{Error, RuntimeError}; -use crate::vm::function::FromParam; -use crate::vm::util::LuaType; -use crate::vm::value::util::ensure_type_equals; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; -use std::fmt::{Debug, Display}; -use std::marker::PhantomData; +//TODO: Support lua_yield through a custom IntoParam which yields (may cause additional stack unwind). + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum State { Yielded, Finished, @@ -46,32 +43,20 @@ pub enum State { pub struct Thread<'a> { vm: Vm, - useless: PhantomData<&'a ()>, -} - -impl Clone for Thread<'_> { - fn clone(&self) -> Self { - Self { - vm: unsafe { Vm::from_raw(self.vm.as_ptr()) }, - useless: PhantomData, - } - } + useless: PhantomData<&'a ()> } impl PartialEq for Thread<'_> { fn eq(&self, other: &Self) -> bool { - self.vm.as_ptr() == other.vm.as_ptr() + self.uid() == other.uid() } } impl Eq for Thread<'_> {} impl Display for Thread<'_> { - #[allow(clippy::missing_transmute_annotations)] fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!(f, "thread@{:X}", unsafe { - std::mem::transmute::<_, usize>(self.vm.as_ptr()) - }) + write!(f, "thread@{:X}", self.uid()) } } @@ -81,10 +66,36 @@ impl Debug for Thread<'_> { } } -impl Thread<'_> { +impl<'a> Thread<'a> { + /// Creates a thread object from an existing lua thread stack. + /// + /// # Arguments + /// + /// * `l`: the existing raw lua [State](crate::ffi::lua::State). + /// + /// returns: Thread + /// + /// # Safety + /// + /// Must ensure that l is a valid lua thread stack. If not, the resulting object is UB. + #[inline(always)] + pub unsafe fn from_raw(l: crate::ffi::lua::State) -> Self { + Self { + vm: Vm::from_raw(l), + useless: PhantomData + } + } + + #[inline(always)] + pub fn as_ptr(&self) -> crate::ffi::lua::State { + self.vm.as_ptr() + } + + /// Returns a unique identifier to that table across the Vm it is attached to. + #[allow(clippy::missing_transmute_annotations)] #[inline(always)] - pub fn run_code<'b, R: FromLua<'b>>(&'b self, code: impl LoadString) -> crate::vm::Result { - self.vm.run_code(code) + pub fn uid(&self) -> usize { + unsafe { std::mem::transmute(self.vm.as_ptr()) } } #[inline(always)] @@ -99,8 +110,8 @@ impl Thread<'_> { ThreadStatus::Ok => Ok(State::Finished), ThreadStatus::Yield => Ok(State::Yielded), ThreadStatus::ErrRun => { - // We've got a runtime error when executing the function so read the full stack - // trace produced by luaL_traceback and remove it from the stack. + // We've got a runtime error when executing the function. + // TODO: In the future, might be great to traceback the thread as well. let error_message: &str = FromLua::from_lua(&self.vm, -1)?; unsafe { lua_remove(self.vm.as_ptr(), -1) }; Err(Error::Runtime(RuntimeError::new( @@ -113,40 +124,3 @@ impl Thread<'_> { } } } - -impl<'a> FromLua<'a> for Thread<'a> { - #[inline(always)] - unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { - Thread { - vm: Vm::from_raw(lua_tothread(vm.as_ptr(), index)), - useless: PhantomData, - } - } - - fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::Thread)?; - Ok(Thread { - vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, - useless: PhantomData, - }) - } -} - -unsafe impl SimpleDrop for Thread<'_> {} - -impl LuaType for Thread<'_> {} - -impl<'a> FromParam<'a> for Thread<'a> { - unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { - luaL_checktype(vm.as_ptr(), index, Type::Thread); - Thread { - vm: unsafe { Vm::from_raw(lua_tothread(vm.as_ptr(), index)) }, - useless: PhantomData, - } - } - - #[inline(always)] - fn try_from_param(vm: &'a Vm, index: i32) -> Option { - FromLua::from_lua(vm, index).ok() - } -} diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs new file mode 100644 index 0000000..f9cde78 --- /dev/null +++ b/core/src/vm/thread/interface.rs @@ -0,0 +1,91 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::laux::luaL_checktype; +use crate::ffi::lua::{lua_gettop, lua_pushvalue, Type}; +use crate::util::core::SimpleDrop; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::registry::{FromIndex, Set}; +use crate::vm::thread::value::Value; +use crate::vm::util::LuaType; +use crate::vm::value::FromLua; +use crate::vm::value::util::ensure_type_equals; +use crate::vm::Vm; + +impl<'a> FromLua<'a> for Value<'a> { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Value::from_raw(vm, vm.get_absolute_index(index)) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + ensure_type_equals(vm, index, Type::Thread)?; + unsafe { Ok(Value::from_raw(vm, vm.get_absolute_index(index))) } + } +} + +unsafe impl SimpleDrop for Value<'_> {} + +impl LuaType for Value<'_> {} + +impl<'a> FromParam<'a> for Value<'a> { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + luaL_checktype(vm.as_ptr(), index, Type::Thread); + Value::from_raw(vm, vm.get_absolute_index(index)) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + Value::from_lua(vm, index).ok() + } +} + +unsafe impl IntoParam for Value<'_> { + fn into_param(self, vm: &Vm) -> u16 { + let top = unsafe { lua_gettop(vm.as_ptr()) }; + if top != self.index() { + unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; + } + 1 + } +} + +impl crate::vm::registry::Value for crate::vm::registry::types::Thread { + type Value<'a> = Value<'a>; + + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe { Value::from_lua_unchecked(vm, index) } + } + + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index()) } + } + + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index()) + } +} diff --git a/core/src/vm/thread/mod.rs b/core/src/vm/thread/mod.rs new file mode 100644 index 0000000..78e39c7 --- /dev/null +++ b/core/src/vm/thread/mod.rs @@ -0,0 +1,31 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +pub mod core; +pub mod value; +mod interface; diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs new file mode 100644 index 0000000..559a6c7 --- /dev/null +++ b/core/src/vm/thread/value.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Debug, Display}; +use crate::ffi::lua::{lua_newthread, lua_pushvalue, lua_tothread}; +use crate::vm::thread::core::Thread; +use crate::vm::Vm; + +/// Represents a thread object value on a lua stack. +pub struct Value<'a> { + pub(super) vm: &'a Vm, + index: i32, + thread: Thread<'static> +} + +impl Clone for Value<'_> { + fn clone(&self) -> Self { + unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; + Value { + vm: self.vm, + index: self.vm.top(), + thread: unsafe { Thread::from_raw(self.thread.as_ptr()) } + } + } +} + +impl PartialEq for Value<'_> { + fn eq(&self, other: &Self) -> bool { + self.thread.eq(&other.thread) + } +} + +impl Eq for Value<'_> {} + +impl Display for Value<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "thread@{:X}", self.thread.uid()) + } +} + +impl Debug for Value<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Thread({:?})", self.index) + } +} + +impl<'a> Value<'a> { + /// Creates a thread value from a raw Vm and index on `vm` stack. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a thread value and is absolute. If index is not absolute + /// then using the produced thread value is UB. If the index points to any other type then + /// using the produced thread value is also UB. + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self { + vm, + index, + thread: Thread::from_raw(lua_tothread(vm.as_ptr(), index)) + } + } + + pub fn new(vm: &'a Vm) -> Self { + let thread = unsafe { Thread::from_raw(lua_newthread(vm.as_ptr())) }; + Self { + vm, + index: vm.top(), + thread + } + } + + /// Returns the absolute index of this table on the Lua stack. + #[inline(always)] + pub fn index(&self) -> i32 { + self.index + } + + /// Returns the thread stack object attached to this thread value. + #[inline(always)] + pub fn as_thread(&self) -> &Thread { + &self.thread + } +} diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 4083df2..fb29c4a 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -31,7 +31,7 @@ use crate::util::core::SimpleDrop; use crate::vm::error::{Error, TypeError}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::table::Table; -use crate::vm::thread::Thread; +use crate::vm::thread::value::Value as Thread; use crate::vm::userdata::AnyUserData; use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::value::function::Function; From 4684705cbb9978aaaf6e69ca5821494129789d7e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 17 Jul 2025 22:33:27 +0200 Subject: [PATCH 359/527] Added new LuaThread helper tool --- core/Cargo.toml | 5 ++-- core/src/util/function.rs | 1 + core/src/util/mod.rs | 4 +++ core/src/util/thread.rs | 55 ++++++++++++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 2 +- 5 files changed, 64 insertions(+), 3 deletions(-) create mode 100644 core/src/util/thread.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 91777ff..b5d0510 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,8 +1,8 @@ [package] name = "bp3d-lua" -version = "1.0.0-rc.2.1.0" +version = "1.0.0-rc.3.0.0" authors = ["Yuri Edward "] -edition = "2021" +edition = "2021" # Possible very-little performance improvement with 2024 (2.35 vs 2.37/2.4) description = "Lua wrapper and base library for BP3D." license = "BSD-3-Clause" repository = "https://gitlab.com/bp3d/lua" @@ -42,6 +42,7 @@ root-vm = [] util-method = [] util-function = [] util-namespace = [] +util-thread = [] util-module = ["module", "bp3d-os/module"] libs-core = ["util-namespace"] libs = ["libs-core", "time", "bp3d-os/time"] diff --git a/core/src/util/function.rs b/core/src/util/function.rs index 2e94c37..9a30d16 100644 --- a/core/src/util/function.rs +++ b/core/src/util/function.rs @@ -52,6 +52,7 @@ impl LuaFunction { R::from_lua(vm, -(R::num_values() as i32)) } + #[inline(always)] pub fn delete(self, vm: &Vm) { self.0.delete(vm) } diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index f2a65c0..b5fa973 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -35,6 +35,8 @@ mod method; pub mod module; #[cfg(feature = "util-namespace")] mod namespace; +#[cfg(feature = "util-thread")] +pub mod thread; #[cfg(feature = "util-function")] pub use function::LuaFunction; @@ -42,3 +44,5 @@ pub use function::LuaFunction; pub use method::LuaMethod; #[cfg(feature = "util-namespace")] pub use namespace::Namespace; +#[cfg(feature = "util-thread")] +pub use thread::LuaThread; diff --git a/core/src/util/thread.rs b/core/src/util/thread.rs new file mode 100644 index 0000000..954c21e --- /dev/null +++ b/core/src/util/thread.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::vm::registry::core::Key; +use crate::vm::Vm; + +pub struct LuaThread { + key: Key, + thread: crate::vm::thread::core::Thread<'static> +} + +impl LuaThread { + pub fn create(value: crate::vm::thread::value::Value) -> Self { + let thread = unsafe { crate::vm::thread::core::Thread::from_raw(value.as_thread().as_ptr()) }; + Self { + key: Key::new(value), + thread + } + } + + #[inline(always)] + pub fn as_thread(&self) -> &crate::vm::thread::core::Thread { + &self.thread + } + + #[inline(always)] + pub fn delete(self, vm: &Vm) { + self.key.delete(vm) + } +} diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index b34b994..ee459d0 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -43,7 +43,7 @@ fn test_vm_lib_lua() { vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '1.0.0-rc.2.1.0') + assert(bp3d.lua.version == '1.0.0-rc.3.0.0') assert(#bp3d.lua.patches == 5) local func = bp3d.lua.loadString('return 1 + 1') assert(func) From 61572146b39296e6c461da8d60f39b07cf079167 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Jul 2025 13:25:48 +0200 Subject: [PATCH 360/527] Fundamental re-write of IntoLua/IntoParam machanism --- codegen/src/gen/into_lua.rs | 113 ++++++++++++++++++++ codegen/src/gen/mod.rs | 2 + codegen/src/lib.rs | 13 ++- core/src/vm/function/core.rs | 22 ++-- core/src/vm/function/interface.rs | 5 +- core/src/vm/function/mod.rs | 8 ++ core/src/vm/table/interface.rs | 9 +- core/src/vm/thread/core.rs | 21 +++- core/src/vm/thread/interface.rs | 9 +- core/src/vm/value/any.rs | 11 +- core/src/vm/value/core.rs | 150 ++++++++++++++++++++------- core/src/vm/value/function.rs | 8 ++ core/tests/test_vm_custom_structs.rs | 6 +- 13 files changed, 313 insertions(+), 64 deletions(-) create mode 100644 codegen/src/gen/into_lua.rs diff --git a/codegen/src/gen/into_lua.rs b/codegen/src/gen/into_lua.rs new file mode 100644 index 0000000..cc01ba3 --- /dev/null +++ b/codegen/src/gen/into_lua.rs @@ -0,0 +1,113 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::parser::enums::EnumVariant; +use crate::parser::structs::StructField; +use crate::parser::Parser; +use proc_macro2::{Ident, TokenStream}; +use quote::quote; +use syn::{Generics, Index}; + +pub struct IntoLua { + name: Ident, + generics: Generics, +} + +impl IntoLua { + pub fn new(name: Ident, generics: Generics) -> Self { + Self { name, generics } + } +} + +impl Parser for IntoLua { + type ParsedField = TokenStream; + type ParsedVariant = TokenStream; + + fn parse_field(&mut self, field: StructField) -> Self::ParsedField { + if field.unique_name_is_index { + let name_idx = Index::from(field.index); + // Table indices starts at 1 rather than 0 in Lua. + let index = (field.index + 1) as i32; + quote! { + tbl.set(#index, self.#name_idx).unwrap(); + } + } else { + let name = field.unique_name; + quote! { + tbl.set(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); + } + } + } + + fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant { + match variant { + EnumVariant::SingleField(v) => { + let name = v.unique_name; + let ty = v.field.ty; + quote! { + Self::#name(v) => <#ty as bp3d_lua::vm::value::IntoLua>::into_lua(v, vm), + } + } + EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), + EnumVariant::None(name) => { + let str = name.to_string(); + quote! { + Self::#name => <&str as bp3d_lua::vm::value::IntoLua>::into_lua(#str, vm), + } + } + } + } + + fn gen_struct(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + quote! { + unsafe impl #generics bp3d_lua::vm::value::IntoLua for #name #generics { + fn into_lua(self, vm: &bp3d_lua::vm::Vm) -> u16 { + let mut tbl = bp3d_lua::vm::table::Table::new(vm); + #(#parsed)* + 1 + } + } + } + } + + fn gen_enum(self, parsed: Vec) -> TokenStream { + let name = self.name; + let generics = self.generics; + quote! { + unsafe impl #generics bp3d_lua::vm::value::IntoLua for #name #generics { + fn into_lua(self, vm: &bp3d_lua::vm::Vm) -> u16 { + match self { + #(#parsed)* + } + } + } + } + } +} diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs index ddee92f..e7e16a1 100644 --- a/codegen/src/gen/mod.rs +++ b/codegen/src/gen/mod.rs @@ -29,7 +29,9 @@ mod from_param; mod into_param; mod lua_type; +mod into_lua; pub use from_param::FromParam; pub use into_param::IntoParam; +pub use into_lua::IntoLua; pub use lua_type::LuaType; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 890713c..161176c 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -29,7 +29,7 @@ mod gen; mod parser; -use crate::gen::{FromParam, IntoParam, LuaType}; +use crate::gen::{FromParam, IntoParam, IntoLua, LuaType}; use crate::parser::Parser; use proc_macro::TokenStream; use proc_macro2::Ident; @@ -58,6 +58,17 @@ pub fn into_param(input: TokenStream) -> TokenStream { IntoParam::new(ident, generics).parse(data).into() } +#[proc_macro_derive(IntoLua)] +pub fn into_lua(input: TokenStream) -> TokenStream { + let DeriveInput { + ident, + data, + generics, + .. + } = parse_macro_input!(input); + IntoLua::new(ident, generics).parse(data).into() +} + #[proc_macro_derive(LuaType)] pub fn lua_type(input: TokenStream) -> TokenStream { let DeriveInput { diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 0fcaf7e..1ba1592 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -27,13 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; -use crate::ffi::laux::{luaL_checklstring, luaL_checktype, luaL_checkudata, luaL_setmetatable, luaL_testudata}; -use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_toboolean, lua_type, RawInteger, RawNumber, Type}; +use crate::ffi::laux::{luaL_checklstring, luaL_checktype, luaL_checkudata, luaL_testudata}; +use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, lua_type, RawInteger, RawNumber, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; use crate::vm::util::{lua_rust_error, LuaType, TypeName}; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::borrow::Cow; use std::error::Error; @@ -101,20 +101,14 @@ impl<'a> FromParam<'a> for &'a [u8] { unsafe impl IntoParam for &str { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { - lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); - } - 1 + IntoLua::into_lua(self, vm) } } unsafe impl IntoParam for &[u8] { #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { - unsafe { - lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()); - } - 1 + IntoLua::into_lua(self, vm) } } @@ -126,6 +120,7 @@ unsafe impl IntoParam for String { } unsafe impl IntoParam for Vec { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { self.as_slice().into_param(vm) } @@ -301,10 +296,7 @@ impl<'a, T: UserData> FromParam<'a> for &'a T { unsafe impl IntoParam for T { fn into_param(self, vm: &Vm) -> u16 { - let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; - unsafe { userdata.write(self) }; - unsafe { luaL_setmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; - 1 + IntoLua::into_lua(self, vm) } } diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 8b8b7dd..5992599 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -41,13 +41,14 @@ use crate::vm::Vm; pub unsafe trait IntoParam: Sized { /// Turns self into a function return parameter. /// - /// This function returns the number of parameters pushed onto the lua stack. + /// This function returns the number of parameters pushed onto the lua stack. Returns -1 in + /// case of a lua_yield. /// /// # Arguments /// /// * `vm`: the [Vm] to push this value to. /// - /// returns: u16 + /// returns: i32 fn into_param(self, vm: &Vm) -> u16; } diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index 0f7fbf0..8f78c30 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -26,6 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// TODO: Support coercible boolean types (through dedicated type like Integer/Number) +// TODO: lua_ext_checkboolean +// TODO: Attempt to make Vm Send behind a cargo feature +// - Add checks to registry when the cargo feature is enabled +// - Remove the single Vm per thread rule when send feature is enabled. +// - Interrupt system should wrap RootVm in a special never Send and never Sync type to avoid +// specific safety issue. + mod core; mod interface; pub mod types; diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 3e9986a..0639651 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -38,7 +38,7 @@ use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::table::Table; use crate::vm::util::LuaType; use crate::vm::value::util::ensure_type_equals; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; unsafe impl SimpleDrop for Table<'_> {} @@ -71,7 +71,14 @@ impl<'a> FromLua<'a> for Table<'a> { } unsafe impl IntoParam for Table<'_> { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { + IntoLua::into_lua(self, vm) + } +} + +unsafe impl IntoLua for Table<'_> { + fn into_lua(self, vm: &Vm) -> u16 { let top = unsafe { lua_gettop(vm.as_ptr()) }; if top != self.index() { unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index 6600300..f262802 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -28,13 +28,13 @@ use std::fmt::{Debug, Display}; use std::marker::PhantomData; -use crate::ffi::lua::{lua_remove, lua_resume, lua_status, ThreadStatus}; +use crate::ffi::laux::luaL_error; +use crate::ffi::lua::{lua_isyieldable, lua_remove, lua_resume, lua_status, lua_yield, ThreadStatus}; use crate::vm::error::{Error, RuntimeError}; +use crate::vm::function::IntoParam; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; -//TODO: Support lua_yield through a custom IntoParam which yields (may cause additional stack unwind). - #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum State { Yielded, @@ -124,3 +124,18 @@ impl<'a> Thread<'a> { } } } + +pub struct Yield; + +unsafe impl IntoParam for Yield { + #[inline(always)] + fn into_param(self, vm: &Vm) -> u16 { + unsafe { + if lua_isyieldable(vm.as_ptr()) != 1 { + luaL_error(vm.as_ptr(), c"attempt to yield a non-thread stack object".as_ptr()); + } + lua_yield(vm.as_ptr(), 0); + 0 + } + } +} diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index f9cde78..7d06b89 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -33,7 +33,7 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::thread::value::Value; use crate::vm::util::LuaType; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_type_equals; use crate::vm::Vm; @@ -65,7 +65,14 @@ impl<'a> FromParam<'a> for Value<'a> { } unsafe impl IntoParam for Value<'_> { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { + IntoLua::into_lua(self, vm) + } +} + +unsafe impl IntoLua for Value<'_> { + fn into_lua(self, vm: &Vm) -> u16 { let top = unsafe { lua_gettop(vm.as_ptr()) }; if top != self.index() { unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index fb29c4a..44c189c 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -116,8 +116,8 @@ impl AnyValue<'_> { } } -unsafe impl IntoParam for AnyValue<'_> { - fn into_param(self, vm: &Vm) -> u16 { +unsafe impl IntoLua for AnyValue<'_> { + fn into_lua(self, vm: &Vm) -> u16 { match self { AnyValue::None => 0, AnyValue::Nil => { @@ -136,6 +136,13 @@ unsafe impl IntoParam for AnyValue<'_> { } } +unsafe impl IntoParam for AnyValue<'_> { + #[inline(always)] + fn into_param(self, vm: &Vm) -> u16 { + IntoLua::into_lua(self, vm) + } +} + impl<'a> FromLua<'a> for AnyValue<'a> { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index fd3ebd3..3714d90 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -26,14 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{ - lua_settop, lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_touserdata, - lua_type, Type, -}; +use std::borrow::Cow; +use crate::ffi::laux::{luaL_setmetatable, luaL_testudata}; +use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; -use crate::vm::function::IntoParam; -use crate::vm::userdata::UserDataImmutable; +use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::util::ensure_type_equals; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; @@ -95,7 +92,7 @@ impl<'a> FromLua<'a> for &'a [u8] { } macro_rules! impl_from_lua { - ($t: ty, $expected: ident, $func: ident, $($ret: tt)*) => { + ($t: ty, $expected: ident, $func: ident, $push_func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { #[inline(always)] unsafe fn from_lua_unchecked(vm: &Vm, index: i32) -> Self { @@ -107,33 +104,36 @@ macro_rules! impl_from_lua { Ok(unsafe { $func(vm.as_ptr(), index) $($ret)* }) } } + + unsafe impl IntoLua for $t { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { + $push_func(vm.as_ptr(), self as _); + 1 + } + } + } }; } #[cfg(target_pointer_width = "64")] -impl_from_lua!(i64, Number, lua_tointeger, as _); +impl_from_lua!(i64, Number, lua_tointeger, lua_pushinteger, as _); #[cfg(target_pointer_width = "64")] -impl_from_lua!(u64, Number, lua_tointeger, as _); - -impl_from_lua!(i8, Number, lua_tointeger, as _); -impl_from_lua!(u8, Number, lua_tointeger, as _); -impl_from_lua!(i16, Number, lua_tointeger, as _); -impl_from_lua!(u16, Number, lua_tointeger, as _); -impl_from_lua!(i32, Number, lua_tointeger, as _); -impl_from_lua!(u32, Number, lua_tointeger, as _); +impl_from_lua!(u64, Number, lua_tointeger, lua_pushinteger, as _); -impl_from_lua!(f32, Number, lua_tonumber, as _); -impl_from_lua!(f64, Number, lua_tonumber, as _); +impl_from_lua!(i8, Number, lua_tointeger, lua_pushinteger, as _); +impl_from_lua!(u8, Number, lua_tointeger, lua_pushinteger, as _); +impl_from_lua!(i16, Number, lua_tointeger, lua_pushinteger, as _); +impl_from_lua!(u16, Number, lua_tointeger, lua_pushinteger, as _); +impl_from_lua!(i32, Number, lua_tointeger, lua_pushinteger, as _); +impl_from_lua!(u32, Number, lua_tointeger, lua_pushinteger, as _); -impl_from_lua!(bool, Boolean, lua_toboolean, == 1); +impl_from_lua!(f32, Number, lua_tonumber, lua_pushnumber, as _); +impl_from_lua!(f64, Number, lua_tonumber, lua_pushnumber, as _); -unsafe impl IntoLua for T { - #[inline(always)] - fn into_lua(self, vm: &Vm) -> u16 { - self.into_param(vm) - } -} +impl_from_lua!(bool, Boolean, lua_toboolean, lua_pushboolean, == 1); impl FromLua<'_> for () { #[inline(always)] @@ -175,7 +175,7 @@ macro_rules! count_tts { } macro_rules! impl_from_lua_tuple { - ($($name: ident: $name2: ident),*) => { + ($($name: ident: $name2: ident ($name3: tt)),*) => { impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { #[inline(always)] fn num_values() -> i16 { @@ -192,6 +192,15 @@ macro_rules! impl_from_lua_tuple { Ok(($($name2),*)) } } + + unsafe impl<$($name: IntoLua),*> IntoLua for ($($name),*) { + fn into_lua(self, vm: &Vm) -> u16 { + $( + self.$name3.into_lua(vm); + )* + count_tts!($($name)*) + } + } }; (_from_lua_unchecked $vm: ident, $index: ident, $name2: ident: $name: ident) => { @@ -215,15 +224,15 @@ macro_rules! impl_from_lua_tuple { }; } -impl_from_lua_tuple!(T: t, T1: t1); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8); -impl_from_lua_tuple!(T: t, T1: t1, T2: t2, T3: t3, T4: t4, T5: t5, T6: t6, T7: t7, T8: t8, T9: t9); +impl_from_lua_tuple!(T: t (0), T1: t1 (1)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4), T5: t5 (5)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4), T5: t5 (5), T6: t6 (6)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4), T5: t5 (5), T6: t6 (6), T7: t7 (7)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4), T5: t5 (5), T6: t6 (6), T7: t7 (7), T8: t8 (8)); +impl_from_lua_tuple!(T: t (0), T1: t1 (1), T2: t2 (2), T3: t3 (3), T4: t4 (4), T5: t5 (5), T6: t6 (6), T7: t7 (7), T8: t8 (8), T9: t9 (9)); impl<'a, T: FromLua<'a>> FromLua<'a> for Option { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -252,3 +261,72 @@ impl<'a, T: FromLua<'a>> FromLua<'a> for Option { } } } + +unsafe impl IntoLua for T { + fn into_lua(self, vm: &Vm) -> u16 { + let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; + unsafe { userdata.write(self) }; + unsafe { luaL_setmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + 1 + } +} + +unsafe impl IntoLua for () { + #[inline(always)] + fn into_lua(self, _: &Vm) -> u16 { + 0 + } +} + +unsafe impl IntoLua for Option { + fn into_lua(self, vm: &Vm) -> u16 { + match self { + None => unsafe { + lua_pushnil(vm.as_ptr()); + 1 + }, + Some(v) => v.into_lua(vm), + } + } +} + +unsafe impl IntoLua for &str { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + self.as_bytes().into_lua(vm) + } +} + +unsafe impl IntoLua for &[u8] { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushlstring(vm.as_ptr(), self.as_ptr() as _, self.len()) }; + 1 + } +} + +unsafe impl IntoLua for String { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + (&*self).into_lua(vm) + } +} + +unsafe impl IntoLua for Vec { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + self.as_slice().into_lua(vm) + } +} + +unsafe impl<'a, T: IntoLua + Clone> IntoLua for Cow<'a, T> +where + &'a T: IntoLua, +{ + fn into_lua(self, vm: &Vm) -> u16 { + match self { + Cow::Borrowed(v) => v.into_lua(vm), + Cow::Owned(v) => v.into_lua(vm), + } + } +} diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 4f725b7..8d70167 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -95,7 +95,15 @@ impl<'a> FromParam<'a> for Function<'a> { } unsafe impl IntoParam for Function<'_> { + #[inline(always)] fn into_param(self, vm: &Vm) -> u16 { + IntoLua::into_lua(self, vm) + } +} + +unsafe impl IntoLua for Function<'_> { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; 1 } diff --git a/core/tests/test_vm_custom_structs.rs b/core/tests/test_vm_custom_structs.rs index 46bf5c2..5115fc9 100644 --- a/core/tests/test_vm_custom_structs.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -31,13 +31,13 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; -use bp3d_lua_codegen::LuaType; +use bp3d_lua_codegen::{IntoLua, LuaType}; use bp3d_lua_codegen::{FromParam, IntoParam}; #[derive(FromParam, LuaType, IntoParam)] struct Test1<'a>(&'a str, i32); -#[derive(FromParam, LuaType, IntoParam)] +#[derive(FromParam, LuaType, IntoParam, IntoLua)] struct Test2<'a> { name: &'a str, value: i32, @@ -69,7 +69,7 @@ decl_lib_func! { } #[test] -fn basic() { +fn test_custom_structs_basic() { let vm = RootVm::new(); let top = vm.top(); vm.set_global(c"test", RFunction::wrap(test)).unwrap(); From e20085ac4c6087c6adcfe04c5d3bce9412cb7e7c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Jul 2025 13:30:36 +0200 Subject: [PATCH 361/527] Updated cargo versions --- codegen/Cargo.toml | 2 +- core/Cargo.toml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 2d310e2..a0f31e1 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua-codegen" -version = "0.1.0" +version = "1.0.0-rc.1.0.0" edition = "2021" #Due to gen be a reserved keyword! publish = false diff --git a/core/Cargo.toml b/core/Cargo.toml index b5d0510..8d014b8 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -19,7 +19,7 @@ bp3d-debug = "1.0.0-rc.6.2.0" bp3d-os = { version = "1.0.0-rc.4.4.0", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } -bp3d-lua-codegen = { version = "0.1.0", path = "../codegen", optional = true } +bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32_System_Kernel", "Win32_System_Diagnostics", "Win32_System_Diagnostics_Debug"], optional = true } @@ -28,7 +28,7 @@ windows-sys = { version = "0.59.0", features = ["Win32_System_Threading", "Win32 libc = { version = "0.2.170", optional = true } [dev-dependencies] -bp3d-lua-codegen = { version = "0.1.0", path = "../codegen" } +bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen" } [build-dependencies] bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } From 5c4b0c033ff05aac794fa6d56063ee9e1dca8b6a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 19 Jul 2025 13:38:11 +0200 Subject: [PATCH 362/527] Re-design IntoParam to return i32 in order to support lua_yield --- codegen/src/gen/into_param.rs | 4 +- core/src/libs/os/compat.rs | 2 +- core/src/libs/os/time.rs | 2 +- core/src/vm/closure/core.rs | 2 +- core/src/vm/function/core.rs | 32 ++++----- core/src/vm/function/interface.rs | 2 +- core/src/vm/table/interface.rs | 4 +- core/src/vm/thread/core.rs | 4 +- core/src/vm/thread/interface.rs | 4 +- core/src/vm/value/any.rs | 12 ++-- core/src/vm/value/function.rs | 4 +- core/tests/test_vm_tables.rs | 2 +- core/tests/test_vm_threads.rs | 111 ++++++++++++++++++++++++++++++ 13 files changed, 148 insertions(+), 37 deletions(-) create mode 100644 core/tests/test_vm_threads.rs diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index 9d29adc..6451912 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -88,7 +88,7 @@ impl Parser for IntoParam { let generics = self.generics; quote! { unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { - fn into_param(self, vm: &bp3d_lua::vm::Vm) -> u16 { + fn into_param(self, vm: &bp3d_lua::vm::Vm) -> i32 { let mut tbl = bp3d_lua::vm::table::Table::new(vm); #(#parsed)* 1 @@ -102,7 +102,7 @@ impl Parser for IntoParam { let generics = self.generics; quote! { unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { - fn into_param(self, vm: &bp3d_lua::vm::Vm) -> u16 { + fn into_param(self, vm: &bp3d_lua::vm::Vm) -> i32 { match self { #(#parsed)* } diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index 4b6f871..a2fb44c 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -45,7 +45,7 @@ enum TableOrString<'a> { } unsafe impl IntoParam for TableOrString<'_> { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { match self { TableOrString::Table(t) => t.into_param(vm), TableOrString::String(s) => s.into_param(vm), diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index f4859ac..bc764e1 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -107,7 +107,7 @@ decl_userdata! { } unsafe impl IntoParam for OffsetDateTime { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { OffsetDateTimeWrapper(self).into_param(vm) } } diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index e23e25f..f2834a5 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -95,7 +95,7 @@ impl FromUpvalue<'_> for *const T { impl IntoUpvalue for T { #[inline(always)] fn into_upvalue(self, vm: &Vm) -> u16 { - self.into_param(vm) + self.into_param(vm) as _ } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 1ba1592..b488047 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -100,28 +100,28 @@ impl<'a> FromParam<'a> for &'a [u8] { unsafe impl IntoParam for &str { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } unsafe impl IntoParam for &[u8] { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } unsafe impl IntoParam for String { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { (&*self).into_param(vm) } } unsafe impl IntoParam for Vec { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { self.as_slice().into_param(vm) } } @@ -130,7 +130,7 @@ unsafe impl<'a, T: IntoParam + Clone> IntoParam for Cow<'a, T> where &'a T: IntoParam, { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { match self { Cow::Borrowed(v) => v.into_param(vm), Cow::Owned(v) => v.into_param(vm), @@ -163,7 +163,7 @@ macro_rules! impl_integer { unsafe impl IntoParam for $t { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { unsafe { lua_pushinteger(vm.as_ptr(), self as _); 1 @@ -204,7 +204,7 @@ macro_rules! impl_float { unsafe impl IntoParam for $t { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { unsafe { lua_pushnumber(vm.as_ptr(), self as _); 1 @@ -232,14 +232,14 @@ impl FromParam<'_> for bool { unsafe impl IntoParam for bool { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { unsafe { lua_pushboolean(vm.as_ptr(), if self { 1 } else { 0 }) }; 1 } } unsafe impl IntoParam for Result { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { match self { Ok(v) => v.into_param(vm), Err(e) => unsafe { @@ -250,7 +250,7 @@ unsafe impl IntoParam for Result { } unsafe impl IntoParam for Option { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { match self { None => unsafe { lua_pushnil(vm.as_ptr()); @@ -263,7 +263,7 @@ unsafe impl IntoParam for Option { unsafe impl IntoParam for () { #[inline(always)] - fn into_param(self, _: &Vm) -> u16 { + fn into_param(self, _: &Vm) -> i32 { 0 } } @@ -295,8 +295,8 @@ impl<'a, T: UserData> FromParam<'a> for &'a T { } unsafe impl IntoParam for T { - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } @@ -308,7 +308,7 @@ macro_rules! count_tts { macro_rules! impl_into_param_tuple { ($($name: ident: $name2: tt),*) => { unsafe impl<$($name: IntoParam),*> IntoParam for ($($name),*) { - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { $( self.$name2.into_param(vm); )* diff --git a/core/src/vm/function/interface.rs b/core/src/vm/function/interface.rs index 5992599..9538039 100644 --- a/core/src/vm/function/interface.rs +++ b/core/src/vm/function/interface.rs @@ -49,7 +49,7 @@ pub unsafe trait IntoParam: Sized { /// * `vm`: the [Vm] to push this value to. /// /// returns: i32 - fn into_param(self, vm: &Vm) -> u16; + fn into_param(self, vm: &Vm) -> i32; } /// This trait represents a function parameter. diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 0639651..812cc04 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -72,8 +72,8 @@ impl<'a> FromLua<'a> for Table<'a> { unsafe impl IntoParam for Table<'_> { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index f262802..21b34e5 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -129,13 +129,13 @@ pub struct Yield; unsafe impl IntoParam for Yield { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { + fn into_param(self, vm: &Vm) -> i32 { unsafe { if lua_isyieldable(vm.as_ptr()) != 1 { luaL_error(vm.as_ptr(), c"attempt to yield a non-thread stack object".as_ptr()); } lua_yield(vm.as_ptr(), 0); - 0 + -1 } } } diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 7d06b89..2c97d16 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -66,8 +66,8 @@ impl<'a> FromParam<'a> for Value<'a> { unsafe impl IntoParam for Value<'_> { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 44c189c..5dbbe7e 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -138,8 +138,8 @@ unsafe impl IntoLua for AnyValue<'_> { unsafe impl IntoParam for AnyValue<'_> { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } @@ -222,7 +222,7 @@ impl FromLua<'_> for AnyParam { } /// A raw primitive to return arbitrary count of values from a C function. -pub struct UncheckedAnyReturn(u16); +pub struct UncheckedAnyReturn(i32); impl UncheckedAnyReturn { /// Construct a [UncheckedAnyReturn]. @@ -235,7 +235,7 @@ impl UncheckedAnyReturn { /// /// It is UB to run any operation which may alter the lua stack after constructing this /// primitive. - pub unsafe fn new(vm: &Vm, count: u16) -> Self { + pub unsafe fn new(vm: &Vm, count: i32) -> Self { let top = vm.top(); if count > top as _ { panic!() @@ -246,7 +246,7 @@ impl UncheckedAnyReturn { unsafe impl IntoParam for UncheckedAnyReturn { #[inline(always)] - fn into_param(self, _: &Vm) -> u16 { - self.0 + fn into_param(self, _: &Vm) -> i32 { + self.0 as _ } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 8d70167..0b20608 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -96,8 +96,8 @@ impl<'a> FromParam<'a> for Function<'a> { unsafe impl IntoParam for Function<'_> { #[inline(always)] - fn into_param(self, vm: &Vm) -> u16 { - IntoLua::into_lua(self, vm) + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ } } diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index aa8d7cc..c6bc2f3 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -32,7 +32,7 @@ use bp3d_lua::vm::table::Table; use bp3d_lua::vm::RootVm; #[test] -fn tables() { +fn test_tables() { let mut vm = RootVm::new(); let top = vm.top(); vm.scope(|vm| { diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs new file mode 100644 index 0000000..6cff071 --- /dev/null +++ b/core/tests/test_vm_threads.rs @@ -0,0 +1,111 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::Cell; +use bp3d_lua::{decl_closure, decl_lib_func}; +use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::function::types::RFunction; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::thread::core::{State, Yield}; +use bp3d_lua::vm::thread::value::Value; + +decl_closure! { + fn increment |val: Rc>| () -> () { + val.set(val.get() + 1); + } +} + +#[test] +fn test_threads_yield_lua() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + let obj = std::rc::Rc::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + local value = coroutine.yield() + if (value == 42) then + increment() + end + end) + ").unwrap(); + let thread: Value = vm.get_global(c"CO").unwrap(); + assert_eq!(obj.get(), 0); + assert_eq!(thread.as_thread().resume(()).unwrap(), State::Yielded); + assert_eq!(obj.get(), 1); + assert_eq!(thread.as_thread().resume(42).unwrap(), State::Finished); + assert_eq!(obj.get(), 2); + // A finished thread will fail to resume. + assert!(thread.as_thread().resume(()).is_err()); + assert!(thread.as_thread().resume(true).is_err()); + assert!(thread.as_thread().resume(()).is_err()); +} + +decl_lib_func! { + fn my_yield() -> Yield { + Yield + } +} + +#[test] +fn test_threads_yield_rust_fail() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); + let res = vm.run_code::<()>(c"my_yield()").unwrap_err().into_runtime().unwrap(); + assert_eq!(res.msg(), "[string \"my_yield()\"]:1: attempt to yield a non-thread stack object"); +} + +#[test] +fn test_threads_yield_rust() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + let obj = std::rc::Rc::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + local value = my_yield() + if (value == 42) then + increment() + end + end) + ").unwrap(); + let thread: Value = vm.get_global(c"CO").unwrap(); + assert_eq!(obj.get(), 0); + assert_eq!(thread.as_thread().resume(()).unwrap(), State::Yielded); + assert_eq!(obj.get(), 1); + assert_eq!(thread.as_thread().resume(42).unwrap(), State::Finished); + assert_eq!(obj.get(), 2); + // A finished thread will fail to resume. + assert!(thread.as_thread().resume(()).is_err()); + assert!(thread.as_thread().resume(()).is_err()); + assert!(thread.as_thread().resume(()).is_err()); +} From d93f60a178af58bda89c8c4932f822977aa79dc3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 20 Jul 2025 18:01:43 +0200 Subject: [PATCH 363/527] Added support for extracting results from Thread::resume function --- core/src/vm/thread/core.rs | 26 +++++++++-- core/src/vm/value/core.rs | 26 ++++++++++- core/tests/test_vm_threads.rs | 81 ++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 14 deletions(-) diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index 21b34e5..d2fde87 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -41,6 +41,11 @@ pub enum State { Finished, } +pub struct Output { + pub state: State, + pub data: T +} + pub struct Thread<'a> { vm: Vm, useless: PhantomData<&'a ()> @@ -103,12 +108,26 @@ impl<'a> Thread<'a> { unsafe { lua_status(self.vm.as_ptr()) } } - pub fn resume(&self, args: impl IntoLua) -> crate::vm::Result { + pub fn resume<'b, T: FromLua<'b>>(&'b self, args: impl IntoLua) -> crate::vm::Result> + where T: 'static { let num = args.into_lua(&self.vm); + let top = self.vm.top(); let res = unsafe { lua_resume(self.vm.as_ptr(), num as _) }; match res { - ThreadStatus::Ok => Ok(State::Finished), - ThreadStatus::Yield => Ok(State::Yielded), + ThreadStatus::Ok => { + let data = T::from_lua(&self.vm, top)?; + Ok(Output { + state: State::Finished, + data + }) + }, + ThreadStatus::Yield => { + let data = T::from_lua(&self.vm, top)?; + Ok(Output { + state: State::Yielded, + data + }) + }, ThreadStatus::ErrRun => { // We've got a runtime error when executing the function. // TODO: In the future, might be great to traceback the thread as well. @@ -125,6 +144,7 @@ impl<'a> Thread<'a> { } } +//TODO: Support nreturns pub struct Yield; unsafe impl IntoParam for Yield { diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 3714d90..45c7a85 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -43,7 +43,7 @@ impl<'a> FromLua<'a> for &'a str { std::str::from_utf8_unchecked(slice) } - fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let l = vm.as_ptr(); unsafe { let ty = lua_type(l, index); @@ -91,6 +91,30 @@ impl<'a> FromLua<'a> for &'a [u8] { } } +impl FromLua<'_> for String { + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + let s: &str = FromLua::from_lua_unchecked(vm, index); + s.into() + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + let s: &str = FromLua::from_lua(vm, index)?; + Ok(s.into()) + } +} + +impl FromLua<'_> for Vec { + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + let bytes: &[u8] = FromLua::from_lua_unchecked(vm, index); + bytes.into() + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + let bytes: &[u8] = FromLua::from_lua(vm, index)?; + Ok(bytes.into()) + } +} + macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $push_func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index 6cff071..cff9f67 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -57,14 +57,14 @@ fn test_threads_yield_lua() { ").unwrap(); let thread: Value = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume(()).unwrap(), State::Yielded); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Yielded); assert_eq!(obj.get(), 1); - assert_eq!(thread.as_thread().resume(42).unwrap(), State::Finished); + assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); assert_eq!(obj.get(), 2); // A finished thread will fail to resume. - assert!(thread.as_thread().resume(()).is_err()); - assert!(thread.as_thread().resume(true).is_err()); - assert!(thread.as_thread().resume(()).is_err()); + assert!(thread.as_thread().resume::<()>(()).is_err()); + assert!(thread.as_thread().resume::<()>(true).is_err()); + assert!(thread.as_thread().resume::<()>(()).is_err()); } decl_lib_func! { @@ -100,12 +100,73 @@ fn test_threads_yield_rust() { ").unwrap(); let thread: Value = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume(()).unwrap(), State::Yielded); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Yielded); assert_eq!(obj.get(), 1); - assert_eq!(thread.as_thread().resume(42).unwrap(), State::Finished); + assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); assert_eq!(obj.get(), 2); // A finished thread will fail to resume. - assert!(thread.as_thread().resume(()).is_err()); - assert!(thread.as_thread().resume(()).is_err()); - assert!(thread.as_thread().resume(()).is_err()); + assert!(thread.as_thread().resume::<()>(()).is_err()); + assert!(thread.as_thread().resume::<()>(()).is_err()); + assert!(thread.as_thread().resume::<()>(()).is_err()); +} + +#[test] +fn test_threads_with_yield_value() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + let obj = std::rc::Rc::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + coroutine.yield(1) + increment() + return 42 + end) + ").unwrap(); + let thread: Value = vm.get_global(c"CO").unwrap(); + assert_eq!(obj.get(), 0); + assert_eq!(thread.as_thread().resume::(()).unwrap().data, 1); + assert_eq!(thread.as_thread().resume::(()).unwrap().data, 42); + assert_eq!(obj.get(), 2); +} + +#[test] +fn test_threads_with_yield_value_unsafe() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + let obj = std::rc::Rc::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + coroutine.yield(\"test\") + increment() + coroutine.yield(\"test2\") + increment() + collectgarbage() + return \"test3\" + end) + ").unwrap(); + let thread: Value = vm.get_global(c"CO").unwrap(); + assert_eq!(obj.get(), 0); + let s: String = thread.as_thread().resume(()).unwrap().data; + let s2: String = thread.as_thread().resume(()).unwrap().data; + let s3: String = thread.as_thread().resume(()).unwrap().data; + vm.run_code::<()>(c"CO = nil; collectgarbage()").unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + coroutine.yield(\"test\") + increment() + coroutine.yield(\"test2\") + increment() + collectgarbage() + return \"test3\" + end) + ").unwrap(); + assert_eq!(s, "test"); + assert_eq!(s2, "test2"); + assert_eq!(s3, "test3"); + assert_eq!(obj.get(), 3); } From 08c952ebf77b17d17bbbd21e594ea98605006710 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Jul 2025 22:33:00 +0200 Subject: [PATCH 364/527] Added support for yielding values from rust --- core/src/vm/thread/core.rs | 11 ++++++----- core/tests/test_vm_threads.rs | 34 +++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 8 deletions(-) diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index d2fde87..369c3c7 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -109,7 +109,8 @@ impl<'a> Thread<'a> { } pub fn resume<'b, T: FromLua<'b>>(&'b self, args: impl IntoLua) -> crate::vm::Result> - where T: 'static { + where T: 'static /* This clause ensures that a future call to collectgarbage or resume does + not free a lua value which would be borrowed by a previous call to resume */ { let num = args.into_lua(&self.vm); let top = self.vm.top(); let res = unsafe { lua_resume(self.vm.as_ptr(), num as _) }; @@ -144,17 +145,17 @@ impl<'a> Thread<'a> { } } -//TODO: Support nreturns -pub struct Yield; +pub struct Yield(pub T); -unsafe impl IntoParam for Yield { +unsafe impl IntoParam for Yield { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { unsafe { if lua_isyieldable(vm.as_ptr()) != 1 { luaL_error(vm.as_ptr(), c"attempt to yield a non-thread stack object".as_ptr()); } - lua_yield(vm.as_ptr(), 0); + let num = self.0.into_param(vm); + lua_yield(vm.as_ptr(), num); -1 } } diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index cff9f67..383c653 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -68,8 +68,14 @@ fn test_threads_yield_lua() { } decl_lib_func! { - fn my_yield() -> Yield { - Yield + fn my_yield() -> Yield<()> { + Yield(()) + } +} + +decl_lib_func! { + fn my_yield2(v: i32) -> Yield { + Yield(v) } } @@ -111,7 +117,7 @@ fn test_threads_yield_rust() { } #[test] -fn test_threads_with_yield_value() { +fn test_threads_with_yield_value_lua() { let vm = RootVm::new(); assert!(vm.as_thread().is_none()); let obj = std::rc::Rc::new(Cell::new(0)); @@ -131,6 +137,28 @@ fn test_threads_with_yield_value() { assert_eq!(obj.get(), 2); } +#[test] +fn test_threads_with_yield_value_rust() { + let vm = RootVm::new(); + assert!(vm.as_thread().is_none()); + let obj = std::rc::Rc::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.set_global(c"my_yield", RFunction::wrap(my_yield2)).unwrap(); + vm.run_code::<()>(c" + CO = coroutine.create(function() + increment() + my_yield(5) + increment() + return 42 + end) + ").unwrap(); + let thread: Value = vm.get_global(c"CO").unwrap(); + assert_eq!(obj.get(), 0); + assert_eq!(thread.as_thread().resume::(()).unwrap().data, 5); + assert_eq!(thread.as_thread().resume::(()).unwrap().data, 42); + assert_eq!(obj.get(), 2); +} + #[test] fn test_threads_with_yield_value_unsafe() { let vm = RootVm::new(); From 6f1f2504af2e3f9585fa3ba648a5872c6c5e663f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 21 Jul 2025 22:34:44 +0200 Subject: [PATCH 365/527] Removed unneeded TODO --- core/src/libs/mod.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index fd4b885..9c10592 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -43,7 +43,6 @@ mod interface; //TODO: maybe add a stack debug function which prints the content of the lua stack //TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) // make sure thread join is time-limited. -//TODO: utf8 lib with string functions operating on UTF8-strings #[cfg(feature = "libs-core")] pub use interface::*; From f338ac4916df12e2ae5582d88da11bca63a655b1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 22 Jul 2025 22:07:45 +0200 Subject: [PATCH 366/527] Added initial version of scheduler --- shell/core/Cargo.toml | 3 +- shell/core/src/lua.rs | 39 ++++++-- shell/core/src/main.rs | 8 +- shell/core/src/scheduler.rs | 156 ++++++++++++++++++++++++++++++++ shell/core/src/scheduler_api.rs | 67 ++++++++++++++ 5 files changed, 264 insertions(+), 9 deletions(-) create mode 100644 shell/core/src/scheduler.rs create mode 100644 shell/core/src/scheduler_api.rs diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index 4fbaad7..b0c030e 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" publish = false [dependencies] -bp3d-lua = { version = "1.0.0-rc.2.1.0", path = "../../core", features = ["root-vm", "util-module", "libs", "dynamic", "interrupt"] } +bp3d-lua = { version = "1.0.0-rc.2.1.0", path = "../../core", features = ["root-vm", "util-module", "util-thread", "libs", "dynamic", "interrupt"] } bp3d-debug = "1.0.0-rc.6.2.0" bp3d-net = { version = "1.0.0-rc.2.1.2", features = ["ipc"] } tokio = { version = "1.45.1", features = ["full"] } @@ -13,3 +13,4 @@ bp3d-util = { version = "2.2.0", features = ["result"] } bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } bp3d-proto = "1.0.0-rc.5.0.1" clap = { version = "4.5.4", features = ["derive"] } +bp3d-os = { version = "1.0.0-rc.4.4.0", features = ["time"] } \ No newline at end of file diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index b38743c..0fcb9be 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::path::PathBuf; +use std::rc::Rc; use tokio::sync::mpsc; use std::thread::JoinHandle; use std::time::Duration; @@ -42,13 +43,16 @@ use crate::autocomplete::Autocomplete; use crate::data::DataOut; use crate::data_in::{Exit, InData, NetInData, RunCode, RunFile}; use crate::data_out::{Log, OutData}; +use crate::scheduler::SchedulerPtr; +use crate::scheduler_api::SchedulerApi; const CHANNEL_BUFFER: usize = 32; pub struct Args { pub data: PathBuf, pub lua: PathBuf, - pub modules: Vec + pub modules: Vec, + pub main_script: Option } pub struct Lua { @@ -107,19 +111,28 @@ impl Lua { let (logger, out_queue) = mpsc::channel(CHANNEL_BUFFER); let (signal, handle) = spawn_interruptible(move |vm| { let logger = DataOut::new(logger); + let scheduler = Rc::new(SchedulerPtr::new()); debug!("Loading VM libraries..."); + debug!("Loading OS library..."); if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { error!("Failed to load OS library: {}", e); } + debug!("Loading string library..."); if let Err(e) = (libs::util::String, libs::util::Table, libs::util::Utf8).register(vm) { error!("Failed to load util library: {}", e); } + debug!("Loading lua library..."); if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { error!("Failed to load base library: {}", e); } + debug!("Loading bp3d-lua-shell::autocomplete library..."); if let Err(e) = Autocomplete::new(logger.clone()).register(vm) { error!("Failed to register autocomplete library: {}", e); } + debug!("Loading bp3d-lua-shell::scheduler library..."); + if let Err(e) = SchedulerApi::new(scheduler.clone()).register(vm) { + error!("Failed to register scheduler library: {}", e); + } let mut modules = libs::lua::Module::new(&[]); for path in &args.modules { modules.add_search_path(path.clone()); @@ -135,12 +148,22 @@ impl Lua { } else { info!("JIT: OFF") } - while let Some(command) = receiver.blocking_recv() { - // Nice type-inference breakage with this box. - let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); - if ret { - break; + if let Some(main_script) = &args.main_script { + vm.scope(|vm| Ok(RunFile { path: main_script.clone() }.handle(&args, vm, &logger))).unwrap(); + } + loop { + // First handle IPC events + while let Some(command) = receiver.try_recv().ok() { + // Nice type-inference breakage with this box. + let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); + if ret { + break; + } } + // Now run the scheduler + scheduler.step(vm, &logger); + // Wait for next cycle + std::thread::sleep(Duration::from_millis(1)); } }); Self { @@ -164,7 +187,8 @@ impl InData for RunCode { impl InData for RunFile { fn handle(&mut self, args: &Args, vm: &Vm, out: &DataOut) -> bool { let path = args.lua.join(&self.path); - let script = match Script::from_path(path) { + debug!("Loading script file: {:?}...", path); + let script = match Script::from_path(&path) { Ok(script) => script, Err(e) => { error!("Error loading lua script: {}", e); @@ -172,6 +196,7 @@ impl InData for RunFile { return false; } }; + debug!("Running script file: {:?}...", path); Lua::handle_value(vm.run(script), out) } } diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index 1af66e5..8aaafa1 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -35,6 +35,8 @@ mod autocomplete; mod data_out; mod data_in; mod data; +mod scheduler; +mod scheduler_api; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -46,7 +48,10 @@ struct Cli { pub root: Option, #[arg(short = 'm', long = "modules", help = "Path to modules directory.")] - pub modules: Option + pub modules: Option, + + #[arg(help = "Path to main script to start at Vm startup in the root directory.")] + pub main_script: Option } #[tokio::main] @@ -62,5 +67,6 @@ async fn main() { data: root.join("data"), lua: root.join("src"), modules, + main_script: args.main_script, }, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; } diff --git a/shell/core/src/scheduler.rs b/shell/core/src/scheduler.rs new file mode 100644 index 0000000..edcc3d1 --- /dev/null +++ b/shell/core/src/scheduler.rs @@ -0,0 +1,156 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::RefCell; +use std::cmp::Ordering; +use std::collections::BinaryHeap; +use bp3d_debug::{error, warning}; +use bp3d_os::time::Instant; +use bp3d_lua::util::LuaThread; +use bp3d_lua::vm::thread::core::State; +use bp3d_lua::vm::thread::value::Value; +use bp3d_lua::vm::Vm; +use crate::data::DataOut; +use crate::data_out::Log; + +struct Task { + at_ms: u64, + period_ms: Option, + thread: LuaThread +} + +impl Eq for Task {} + +impl Ord for Task { + fn cmp(&self, other: &Self) -> Ordering { + self.at_ms.cmp(&other.at_ms) + } +} + +impl PartialEq for Task { + fn eq(&self, other: &Self) -> bool { + self.thread.as_thread() == other.thread.as_thread() + } +} + +impl PartialOrd for Task { + fn partial_cmp(&self, other: &Self) -> Option { + other.at_ms.partial_cmp(&self.at_ms) + } +} + +struct Scheduler { + main: BinaryHeap, + instant: Instant +} + +impl Scheduler { + pub fn schedule_in(&mut self, value: Value, after_ms: u32) { + let task = Task { + at_ms: self.instant.elapsed().as_millis() as u64 + after_ms as u64, + period_ms: None, + thread: LuaThread::create(value) + }; + self.main.push(task); + } + + pub fn schedule_periodically(&mut self, value: Value, period_ms: u32) { + let task = Task { + at_ms: self.instant.elapsed().as_millis() as u64 + period_ms as u64, + period_ms: Some(period_ms), + thread: LuaThread::create(value) + }; + self.main.push(task); + } + + pub fn step(&mut self, vm: &Vm, logger: &DataOut) { + let time = self.instant.elapsed().as_millis() as u64; + while let Some(task) = self.main.peek() { + if time >= task.at_ms { + let mut task = unsafe { self.main.pop().unwrap_unchecked() }; + let out = match task.thread.as_thread().resume::>(()) { + Ok(v) => v, + Err(e) => { + error!("{}: Failed to schedule lua thread: {:?}", task.thread.as_thread(), e); + logger.send(Log("scheduler", e.to_string())); + task.thread.delete(vm); + continue; + } + }; + if let Some(new_time_ms) = out.data { + if out.state == State::Yielded { + if task.period_ms.is_some() { + task.period_ms = Some(new_time_ms); + } + task.at_ms = time + new_time_ms as u64; + self.main.push(task); + continue; + } else { + warning!("{}: Attempt to change period or time of terminated task.", task.thread.as_thread()); + task.thread.delete(vm); + continue; + } + } + if let Some(period_ms) = task.period_ms { + if out.state == State::Yielded { + task.at_ms = time + period_ms as u64; + self.main.push(task); + } else { + warning!("{}: Attempt to re-schedule terminated task.", task.thread.as_thread()); + task.thread.delete(vm); + } + } + } else { + break; + } + } + } +} + +pub struct SchedulerPtr(RefCell); + +impl SchedulerPtr { + pub fn new() -> Self { + Self(RefCell::new(Scheduler { main: BinaryHeap::new(), instant: Instant::now() })) + } + + pub fn schedule_in(&self, value: Value, after_ms: u32) { + let mut inner = self.0.borrow_mut(); + inner.schedule_in(value, after_ms); + } + + pub fn schedule_periodically(&self, value: Value, period_ms: u32) { + let mut inner = self.0.borrow_mut(); + inner.schedule_periodically(value, period_ms); + } + + pub fn step(&self, vm: &Vm, logger: &DataOut) { + let mut inner = self.0.borrow_mut(); + inner.step(vm, logger); + } +} diff --git a/shell/core/src/scheduler_api.rs b/shell/core/src/scheduler_api.rs new file mode 100644 index 0000000..773c3cc --- /dev/null +++ b/shell/core/src/scheduler_api.rs @@ -0,0 +1,67 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::decl_closure; +use bp3d_lua::libs::Lib; +use bp3d_lua::util::Namespace; +use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::thread::value::Value; +use crate::scheduler::SchedulerPtr; + +decl_closure! { + fn schedule_in |scheduler: Rc| (thread: Value, after_ms: u32) -> () { + scheduler.schedule_in(thread, after_ms); + } +} + +decl_closure! { + fn schedule_periodically |scheduler: Rc| (thread: Value, period_ms: u32) -> () { + scheduler.schedule_periodically(thread, period_ms); + } +} + +pub struct SchedulerApi(std::rc::Rc); + +impl SchedulerApi { + pub fn new(ptr: std::rc::Rc) -> Self { + Self(ptr) + } +} + +impl Lib for SchedulerApi { + const NAMESPACE: &'static str = "bp3d.lua.shell"; + + fn load(&self, namespace: &mut Namespace) -> bp3d_lua::vm::Result<()> { + let r1 = Rc::from_rust(namespace.vm(), self.0.clone()); + let r2 = Rc::from_rust(namespace.vm(), self.0.clone()); + namespace.add([ + ("scheduleIn", schedule_in(r1)), + ("schedulePeriodically", schedule_periodically(r2)) + ]) + } +} From 9db3a4354cf9b78087382a0c64f07f522af6ed27 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 23 Jul 2025 20:52:54 +0200 Subject: [PATCH 367/527] Fixed possible bug with Instant userdata object --- core/src/libs/os/instant.rs | 12 ++++++------ core/src/libs/os/time.rs | 7 +++++++ 2 files changed, 13 insertions(+), 6 deletions(-) diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index 367149e..d1c820c 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -31,19 +31,19 @@ use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::{decl_lib_func, decl_userdata}; -struct Wrapper(bp3d_os::time::Instant); +struct InstantWrapper(bp3d_os::time::Instant); decl_userdata! { - impl Wrapper { - fn elapsed(this: &Wrapper) -> f64 { + impl InstantWrapper { + fn elapsed(this: &InstantWrapper) -> f64 { this.0.elapsed().as_secs_f64() } } } decl_lib_func! { - fn now() -> Wrapper { - Wrapper(bp3d_os::time::Instant::now()) + fn now() -> InstantWrapper { + InstantWrapper(bp3d_os::time::Instant::now()) } } @@ -55,7 +55,7 @@ impl Lib for Instant { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace .vm() - .register_userdata::(crate::vm::userdata::case::Camel)?; + .register_userdata::(crate::vm::userdata::case::Camel)?; namespace.add([("now", RFunction::wrap(now))]) } } diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index bc764e1..1761d7f 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -38,6 +38,7 @@ use bp3d_util::simple_error; use time::error::{ComponentRange, Format, InvalidFormatDescription}; use time::format_description::parse; use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, UtcOffset}; +use crate::vm::value::IntoLua; simple_error! { FormatError { @@ -112,6 +113,12 @@ unsafe impl IntoParam for OffsetDateTime { } } +unsafe impl IntoLua for OffsetDateTime { + fn into_lua(self, vm: &Vm) -> u16 { + OffsetDateTimeWrapper(self).into_lua(vm) + } +} + decl_lib_func! { fn now_utc() -> OffsetDateTime { OffsetDateTime::now_utc() From 2b60bc9a6d0c1c1f9db7ea1d07601c2ef06dd986 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 26 Jul 2025 18:20:01 +0200 Subject: [PATCH 368/527] Added type definition files for base lua libs --- core/tests/lua/class.lua | 8 +- definitions/bp3d.lua.lua | 122 +++++++++++++++++++ definitions/bp3d.lua.shell.lua | 70 +++++++++++ definitions/bp3d.os.lua | 135 +++++++++++++++++++++ definitions/bp3d.util.lua | 213 +++++++++++++++++++++++++++++++++ 5 files changed, 545 insertions(+), 3 deletions(-) create mode 100644 definitions/bp3d.lua.lua create mode 100644 definitions/bp3d.lua.shell.lua create mode 100644 definitions/bp3d.os.lua create mode 100644 definitions/bp3d.util.lua diff --git a/core/tests/lua/class.lua b/core/tests/lua/class.lua index 22a9b08..076c4e7 100644 --- a/core/tests/lua/class.lua +++ b/core/tests/lua/class.lua @@ -1,5 +1,5 @@ --- @param name string ---- @param parent table +--- @param parent table? function AbstractClass(name, parent) local class = {} if not class.init then @@ -14,7 +14,7 @@ function AbstractClass(name, parent) end --- @param name string ---- @param parent table +--- @param parent table? function Class(name, parent) local class = {} if not class.init then @@ -35,13 +35,15 @@ function Class(name, parent) end --- @class Parent +--- @field init function local Parent = AbstractClass("Parent") function Parent:value() return 42 end ---- @class Child +--- @class Child: Parent +--- @field new function local Child = Class("Child", Parent) function Child:init(a) diff --git a/definitions/bp3d.lua.lua b/definitions/bp3d.lua.lua new file mode 100644 index 0000000..0e8f279 --- /dev/null +++ b/definitions/bp3d.lua.lua @@ -0,0 +1,122 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.lua + +--- @class _G +--- @field MODULES ModuleManager Global instance of the module manager available to the current LuaJIT interpreter. +_G = {} + +bp3d = {} + +--- @class bp3d.lua +--- @field name string The name of the Lua engine used. +--- @field version string The version string of the Lua engine following SemVer notation. +--- @field patches string[] The list of patches applied to the underlying LuaJIT interpreter. +bp3d.lua = {} + +--- Protect call to a Lua function and extract traceback information in case of error. +--- +--- @param func function the function to call in a protected environment. +--- @return [boolean, (any[] | string)] whatever true if the call succeeded, false otherwise followed by the error backtrace. +bp3d.lua.pcall = function(func) end + +--- Runs a Lua string and raises an error if the execution failed. +--- +--- @raises syntax or runtime error. +--- @param s string to run. +--- @param chunkname string? optional chunkname. +--- @return any[] whatever outputs returned by the function which was executed. +bp3d.lua.runString = function(s, chunkname) end + +--- Loads a Lua string and raises an error on broken syntax. +--- This function does not run the string. +--- +--- @raises syntax error. +--- @param s string to load/compile. +--- @param chunkname string? optional chunkname. +--- @return function whatever the compiled function. +bp3d.lua.loadString = function(s, chunkname) end + +--- Loads a Lua file and raises an error on broken syntax. +--- This function does not run the string. +--- +--- # Sandboxing +--- +--- This function uses a chroot to limit the location of the file which can loaded. +--- This avoids arbitrary code execution on files which the user does not whish to make visible to the Lua engine. +--- +--- @raises syntax error. +--- @param path string path to the file to load/compile. +--- @return function whatever the compiled function. +bp3d.lua.loadFile = function(path) end + +--- Runs a Lua file and raises an error if the execution failed. +--- +--- # Sandboxing +--- +--- This function uses a chroot to limit the location of the file which can loaded. +--- This avoids arbitrary code execution on files which the user does not whish to make visible to the Lua engine. +--- +--- @raises syntax or runtime error. +--- @param path string path to the file to load/compile and run. +--- @return any[] whatever outputs returned by the function which was executed. +bp3d.lua.runFile = function(path) end + +--- Runs a Lua file and raises an error if the execution failed. +--- Unlike runFile this function allows to omit script file extension and uses a search algorithm to be defined by the +--- host application instead of the bp3d-lua engine. +--- The search algorithm is optional, if no search algorithm is provided by the host application, user of the bp3d-lua +--- engine, then this function will be set to nil. +--- +--- # Sandboxing +--- +--- To control which directories are allowed to contain runnable lua files, the search algorithm is left to the host +--- application. +--- +--- @raises syntax or runtime error. +--- @param path string path to the script library file to load/compile and run. Path separator is '.' like standard +--- require function. +--- @return any[] whatever outputs returned by the function which was executed. +bp3d.lua.require = function(path) end + +--- @class ModuleManager +ModuleManager = {} + +--- Loads a native module into the current underlying LuaJIT interpreter. +--- File extensions and other platform specific prefixes are pre-applied by the underlying search algorithm and must +--- not be specified in the lib or plugin arguments to this function. +--- +--- # Sandboxing +--- +--- This function limits the modules search paths to a set of folders specified at the application level. +--- This limits arbitrary native code execution and essentially forbids arbitrary native code injection by Lua code. +--- +--- @param lib string the name of the library to load. +--- @param plugin string the name of the plugin in the native library to load. +function ModuleManager:load(lib, plugin) end diff --git a/definitions/bp3d.lua.shell.lua b/definitions/bp3d.lua.shell.lua new file mode 100644 index 0000000..8606804 --- /dev/null +++ b/definitions/bp3d.lua.shell.lua @@ -0,0 +1,70 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.lua.shell + +bp3d = {} +bp3d.lua = {} +bp3d.lua.shell = {} + +--- Looks for a value named `name` in _G and build autocompletions to be sent to the frontend for rendering purposes. +--- +--- @raises runtime error if the value with name `name` cannot be found. +--- @param name string the name of the key in _G to build autocompletions for. +--- @param metatable boolean true to enable listing recursive metatables, false otherwise. +bp3d.lua.shell.buildCompletions = function(name, metatable) end + +--- Removes all autocompletions which were added for `name`. +--- +--- @raises runtime error if the value with name `name` cannot be found. +--- @param name string the name of the key in _G to remove autocompletions for. +--- @param metatable boolean true to enable listing recursive metatables, false otherwise. +bp3d.lua.shell.deleteCompletions = function(name, metatable) end + +--- Schedules a lua thread to be resumed/started after a specified time as a whole number in milliseconds. +--- +---@param thread thread the thread to resume/start. +---@param after_ms integer 32 bits unsigned integer number of milliseconds after now at which to run the thread. +bp3d.lua.shell.scheduleIn = function(thread, after_ms) end + +--- Schedules a lua thread to be resumed/started at a regular time interval specified as a whole number in milliseconds. +--- +---@param thread thread the thread to resume/start. +---@param period_ms integer 32 bits unsigned integer interval in milliseconds at which to run the thread. +bp3d.lua.shell.schedulePeriodically = function(thread, period_ms) end + +--- Binds a Lua function to the given event name. +--- +---@param name string the name of the event to bind to. +---@param func function the lua function to run every time this event is raised. +bp3d.lua.shell.bindEvent = function(name, func) end + +--- Unbinds the Lua function from the given event name. +--- +---@param name string the name of the event to unbind the lua function from. +bp3d.lua.shell.unbindEvent = function(name) end diff --git a/definitions/bp3d.os.lua b/definitions/bp3d.os.lua new file mode 100644 index 0000000..f64c3f2 --- /dev/null +++ b/definitions/bp3d.os.lua @@ -0,0 +1,135 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.os + +bp3d = {} +bp3d.os = {} + +bp3d.os.instant = {} + +--- Creates a new instance of a Instant object. +--- @return Instant whatever a new Instant object. +bp3d.os.instant.now = function() end + +--- @class Instant +Instant = {} + +--- @return number whatever number of seconds since the creation of this object. +function Instant:elapsed() end + +bp3d.os.time = {} + +--- @return OffsetDateTime +bp3d.os.time.nowUtc = function() end + +--- @return OffsetDateTime +bp3d.os.time.nowLocal = function() end + +--- Constructs a new OffsetDateTime from a unix timestamp in seconds. +--- +--- @param timestamp integer the unix timestamp in seconds. +--- @return OffsetDateTime +bp3d.os.time.fromUnixTimestamp = function(timestamp) end + +--- @class Offset +--- @field hours integer +--- @field minutes integer +--- @field seconds integer +Offset = {} + +--- @class Time +--- @field hour integer +--- @field minute integer +--- @field second integer +Time = {} + +--- @class Date +--- @field year integer +--- @field month integer +--- @field day integer +Date = {} + +--- @class RawComponents +--- @field year integer +--- @field month integer +--- @field day integer +--- @field hour integer? +--- @field min integer? +--- @field sec integer? +--- @field offset Offset? +RawComponents = {} + +--- Creates a new OffsetDateTime from its raw components. +--- +--- @param table RawComponents manual definition of a date time with an optional offset. +--- @return OffsetDateTime +bp3d.os.time.new = function(table) end + +--- @class OffsetDateTime +OffsetDateTime = {} + +--- Formats this OffsetDateTime following a given format string. +--- The format string should match with the semantics of the Rust time crate. +--- +--- @param format string the format string. +--- @return string whatever formatted string. +function OffsetDateTime:format(format) end + +--- @param duration number duration in seconds. +--- @return OffsetDateTime +function OffsetDateTime:__add(duration) end + +--- @param other OffsetDateTime other operand to subtract with. +--- @return number whatever the dureation in seconds of (self - other). +function OffsetDateTime:__sub(other) end + +--- @param other OffsetDateTime other operand to compare with. +--- @return boolean whatever true if self > other, false otherwise. +function OffsetDateTime:__gt(other) end + +--- @param other OffsetDateTime other operand to compare with. +--- @return boolean whatever true if self >= other, false otherwise. +function OffsetDateTime:__ge(other) end + +--- @param other OffsetDateTime other operand to compare with. +--- @return boolean whatever true if self < other, false otherwise. +function OffsetDateTime:__lt(other) end + +--- @param other OffsetDateTime other operand to compare with. +--- @return boolean whatever true if self <= other, false otherwise. +function OffsetDateTime:__le(other) end + +--- @return Date whatever the date component of this OffsetDateTime. +function OffsetDateTime:getDate() end + +--- @return Time whatever the time component of this OffsetDateTime. +function OffsetDateTime:getTime() end + +--- @return Offset whatever the UTC offset component of this OffsetDateTime. +function OffsetDateTime:getOffset() end diff --git a/definitions/bp3d.util.lua b/definitions/bp3d.util.lua new file mode 100644 index 0000000..4d31653 --- /dev/null +++ b/definitions/bp3d.util.lua @@ -0,0 +1,213 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.util + +bp3d = {} +bp3d.util = {} +bp3d.util.string = {} +bp3d.util.table = {} +bp3d.util.utf8 = {} + +--- Checks if the given sub string `needle` can be found in `src`. +--- +--- @param src string the source string. +--- @param needle string the string to search for. +--- @return boolean whatever true if `needle` was found in `src`, false otherwise. +bp3d.util.string.contains = function(src, needle) end + +--- Splits the given string `src` using the separator `pattern`. +--- +--- @param src string input source string. +--- @param pattern integer separator character. +--- @return string[] +bp3d.util.string.split = function(src, pattern) end + +--- Capitalises the given string. +--- +--- Note: This function ignores UTF-8 characters. +--- +--- @param src string the string to capitalise. +--- @return string +bp3d.util.string.capitalise = function(src) end + +--- De-capitalises the given string. +--- +--- Note: This function ignores UTF-8 characters. +--- +--- @param src string the string to decapitalise. +--- @return string +bp3d.util.string.decapitalise = function(src) end + +--- Merges the keys of 1 table into another. +--- +--- @param dst table the destination table. +--- @param src table the source table to merge. +bp3d.util.table.update = function(dst, src) end + +--- Concatenates N source tables into a destination table. +--- +--- @param dst table the destination table which should have the keys of all source tables. +--- @param ... table a list of all source table to concatenate in `dst`. +bp3d.util.table.concat = function(dst, ...) end + +--- Deep-copies the table given as argument and return a new table. +--- +--- @param src table input table to deep-copy. +--- @return table +bp3d.util.table.copy = function(src) end + +--- @param src table input table to compute length of. +--- @return integer whatever the number of items in `src`. +--- This function is optimized to choose either the fast objlen method or a slow iterator based on if the table has +--- a hash component or not. +bp3d.util.table.count = function(src) end + +--- @param src table input table. +--- @return string whatever a string listing all key-value pairs in the table for quick display purposes. +bp3d.util.table.tostring = function(src) end + +--- @param src table input table. +--- @param value any value to search for. +--- @return boolean whatever true if value was found in the table, false otherwise. +bp3d.util.table.contains = function(src, value) end + +--- @param src table input table. +--- @param key any key to search for. +--- @return boolean whatever true if key was found in the table, false otherwise. +bp3d.util.table.containsKey = function(src, key) end + +--- Protects the given input table. A protected table is a read-only table where writing would result into a runtime +--- error. +--- +--- @param src table input table. +--- @return table +bp3d.util.table.protect = function(src) end + +--- Checks if the given sub string `needle` can be found in `src`. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string the source string. +--- @param needle string the string to search for. +--- @return boolean whatever true if `needle` was found in `src`, false otherwise. +bp3d.util.utf8.contains = function(src, needle) end + +--- Splits the given string `src` using the separator `pattern`. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string input source string. +--- @param pattern integer separator string. +--- @return string[] +bp3d.util.utf8.split = function(src, pattern) end + +--- Replace all instances of `pattern` in the given string `src` by the replacement string `replacement`. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string input source string. +--- @param pattern integer search string. +--- @param replacement string replacement string +bp3d.util.utf8.replace = function(src, pattern, replacement) end + +--- Count the number of unicode characters in the source string. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. This function does not handle unicode +--- ligatures. +--- +--- @param src string input source string. +--- @return integer +bp3d.util.utf8.count = function(src) end + +--- Extract a unicode character from the given source string. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. This function does not handle unicode +--- ligatures. +--- +--- @param src string input source string. +--- @param pos integer character index. +--- @return integer +bp3d.util.utf8.charAt = function(src, pos) end + +--- Checks if the given input string is valid UTF-8. +--- +--- @param src string input source string. +--- @return string? whatever nil if the input string contains invalud UTF-8 codes, otherwise returns `src`. +bp3d.util.utf8.fromString = function(src) end + +--- Converts the input string to a valid UTF-8 string by replacing all invalid UTF-8 codes by U+FFFD. +--- +--- @param src string input source string. +--- @return string +bp3d.util.utf8.fromStringLossy = function(src) end + +--- Capitalises the given string. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string the string to capitalise. +--- @return string +bp3d.util.utf8.capitalise = function(src) end + +--- De-capitalises the given string. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string the string to decapitalise. +--- @return string +bp3d.util.utf8.decapitalise = function(src) end + +--- Change case of all characters in the given input string to upper. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string the input string. +--- @return string +bp3d.util.utf8.upper = function(src) end + +--- Change case of all characters in the given input string to lower. +--- +--- Note: This function wil error if any of the inputs are not UTF-8 encoded. +--- +--- @param src string the input string. +--- @return string +bp3d.util.utf8.lower = function(src) end + +--- Extracts a sub-string from the given input source string. +--- +--- Note: This function wil error if `src` is not UTF-8 encoded. This function will never return a broken UTF-8 string. +--- In case `start` points inside a multi-byte UTF-8 sequence, the start position is advanced to the next valid UTF-8 +--- sequence. In case `end1` points inside a multi-byte UTF-8 sequence, the end position is moved backwards to the +--- previous valid UTF-8 sequence. +--- +--- @param src string the input string. +--- @param start integer start position in the input string. +--- @param end1 integer end position in the input string. +--- @return string +bp3d.util.utf8.sub = function(src, start, end1) end From 1ee39487fd92e5d67c1959cbcca289bf98532338 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 27 Jul 2025 23:09:51 +0200 Subject: [PATCH 369/527] Added re-write raw pointer handling and added support for more type-safe named registry key --- core/src/vm/closure/context.rs | 13 ++-- core/src/vm/closure/core.rs | 37 +++------ core/src/vm/closure/rc.rs | 7 +- core/src/vm/closure/types.rs | 10 +-- core/src/vm/core/destructor.rs | 28 +++---- core/src/vm/registry/named.rs | 136 ++++++++++++++++++++++----------- core/src/vm/registry/types.rs | 3 + core/src/vm/value/mod.rs | 1 + core/src/vm/value/raw_ptr.rs | 129 +++++++++++++++++++++++++++++++ core/src/vm/value/types.rs | 1 + 10 files changed, 263 insertions(+), 102 deletions(-) create mode 100644 core/src/vm/value/raw_ptr.rs diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index cc92c0b..385a0a4 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -36,6 +36,7 @@ use crate::vm::registry::core::RawKey; use crate::vm::Vm; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; +use crate::vm::value::types::RawPtr; pub struct Cell { ptr: *mut *const T, @@ -167,8 +168,8 @@ unsafe impl SimpleDrop for Mut<'_, T> {} impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: *mut *const T = FromUpvalue::from_upvalue(vm, index); - if (*ptr).is_null() { + let ptr: RawPtr<*const T> = FromUpvalue::from_upvalue(vm, index); + if (*ptr.as_ptr()).is_null() { luaL_error( vm.as_ptr(), c"Context is not available in this function.".as_ptr(), @@ -176,14 +177,14 @@ impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Ref(unsafe { &**ptr }) + Ref(unsafe { &**ptr.as_ptr() }) } } impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: *mut *mut T = FromUpvalue::from_upvalue(vm, index); - if (*ptr).is_null() { + let ptr: RawPtr<*mut T> = FromUpvalue::from_upvalue(vm, index); + if (*ptr.as_ptr()).is_null() { luaL_error( vm.as_ptr(), c"Context is not available in this function.".as_ptr(), @@ -191,7 +192,7 @@ impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Mut(unsafe { &mut **ptr }) + Mut(unsafe { &mut **ptr.as_ptr() }) } } diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index f2834a5..9aef356 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -26,13 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushlightuserdata, lua_topointer, GLOBALSINDEX}; +use crate::ffi::lua::{lua_topointer, GLOBALSINDEX}; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::ffi::OsStr; use std::path::Path; +use crate::vm::value::types::RawPtr; macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { @@ -78,17 +79,12 @@ impl_from_upvalue_using_from_lua_unchecked!(i64, u64); impl_from_upvalue_using_from_lua_unchecked!(i8, u8, i16, u16, i32, u32, f32, f64, bool); -impl FromUpvalue<'_> for *mut T { - #[inline(always)] - unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { - lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ - } -} +// Wrap this in a new RawPtr type... -impl FromUpvalue<'_> for *const T { +impl FromUpvalue<'_> for RawPtr { #[inline(always)] - unsafe fn from_upvalue(vm: &'_ Vm, index: i32) -> Self { - lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _ + unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { + RawPtr::new(lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _) } } @@ -99,26 +95,15 @@ impl IntoUpvalue for T { } } -impl IntoUpvalue for *mut T { - fn into_upvalue(self, vm: &Vm) -> u16 { - unsafe { lua_pushlightuserdata(vm.as_ptr(), self as _) }; - 1 - } -} - -impl IntoUpvalue for *const T { +impl IntoUpvalue for RawPtr { fn into_upvalue(self, vm: &Vm) -> u16 { - unsafe { lua_pushlightuserdata(vm.as_ptr(), self as _) }; + self.into_lua(vm); 1 } } -impl Upvalue for *mut T { - type From<'a> = *mut T; -} - -impl Upvalue for *const T { - type From<'a> = *const T; +impl Upvalue for RawPtr { + type From<'a> = RawPtr; } impl<'a> FromUpvalue<'a> for &'a OsStr { diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 4592e0a..7b166ad 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -30,6 +30,7 @@ use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::Vm; use std::ops::Deref; +use crate::vm::value::types::RawPtr; #[repr(transparent)] pub struct Rc(*const T); @@ -50,8 +51,8 @@ impl Deref for Ref<'_, T> { impl<'a, T> FromUpvalue<'a> for Ref<'a, T> { #[inline(always)] unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: *const T = FromUpvalue::from_upvalue(vm, index); - Ref(&*ptr) + let ptr: RawPtr = FromUpvalue::from_upvalue(vm, index); + Ref(&*ptr.as_ptr()) } } @@ -62,7 +63,7 @@ impl Upvalue for Rc { impl IntoUpvalue for Rc { #[inline(always)] fn into_upvalue(self, vm: &Vm) -> u16 { - self.0.into_upvalue(vm) + RawPtr::new(self.0 as *mut T).into_upvalue(vm) } } diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 9a4fe52..61e469d 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -31,7 +31,7 @@ use crate::vm::closure::{FromUpvalue, IntoUpvalue}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::value::IntoLua; use crate::vm::Vm; -use std::ffi::c_void; +use crate::vm::value::types::RawPtr; pub struct RClosure { func: CFunction, @@ -60,7 +60,7 @@ unsafe impl IntoLua for RClosure { } } -impl RClosure<*const c_void> { +impl RClosure> { pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self where for<'a> T: FromParam<'a>, @@ -73,11 +73,11 @@ impl RClosure<*const c_void> { R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; - let upvalue: *const F = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; let args: T = unsafe { FromParam::from_param(&vm, 1) }; - let res = unsafe { (*upvalue)(args) }; + let res = unsafe { (*upvalue.as_ptr())(args) }; res.into_param(&vm) as _ } - RClosure::new(_cfunc::, ptr as *const _) + RClosure::new(_cfunc::, RawPtr::new(ptr as _)) } } diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index ad0e990..ba57fb2 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -26,12 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushlightuserdata, lua_settop, lua_touserdata}; -use crate::vm::registry::named::RawKey; -use crate::vm::registry::Set; +use crate::ffi::lua::lua_settop; +use crate::vm::registry::named::Key; use crate::vm::Vm; use bp3d_debug::debug; use std::rc::Rc; +use crate::vm::registry::types::RawPtr; +use crate::vm::value::types::RawPtrRef; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { @@ -72,7 +73,7 @@ impl Raw for Rc { } } -const DESTRUCTOR_POOL: RawKey = RawKey::new("__destructor_pool__"); +static DESTRUCTOR_POOL: Key> = Key::new("__destructor_pool__"); #[derive(Default)] pub struct Pool { @@ -90,12 +91,9 @@ impl Pool { /// /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. pub unsafe fn new_in_vm(vm: &mut Vm) { - DESTRUCTOR_POOL.register(vm); - let l = vm.as_ptr(); let b = Box::leak(Box::new(Pool::new())); - let ptr = b as *mut Pool as _; - lua_pushlightuserdata(l, ptr); - DESTRUCTOR_POOL.set(vm, -1); + let ptr = crate::vm::value::types::RawPtr::new(b as *mut Pool); + DESTRUCTOR_POOL.set(RawPtrRef::from_ptr(vm, ptr)); } /// Extracts a destructor pool from the given [Vm]. @@ -103,17 +101,15 @@ impl Pool { /// # Safety /// /// The returned reference must not be aliased. - unsafe fn _from_vm(vm: &Vm) -> *mut Self { + unsafe fn _from_vm(vm: &Vm) -> crate::vm::value::types::RawPtr { let l = vm.as_ptr(); - DESTRUCTOR_POOL.push(vm); - let ptr = lua_touserdata(l, -1) as *mut Pool; - assert!(!ptr.is_null()); + let ptr = DESTRUCTOR_POOL.push(vm).unwrap(); lua_settop(l, -2); // Remove the pointer from the lua stack. - ptr + ptr.as_ptr() } pub fn from_vm(vm: &mut Vm) -> &mut Self { - unsafe { &mut *Self::_from_vm(vm) } + unsafe { &mut *Self::_from_vm(vm).as_mut_ptr() } } pub fn attach(vm: &Vm, raw: R) -> R::Ptr @@ -121,7 +117,7 @@ impl Pool { R::Ptr: 'static, { let ptr = unsafe { Self::_from_vm(vm) }; - unsafe { (*ptr).attach_mut(raw) } + unsafe { (*ptr.as_mut_ptr()).attach_mut(raw) } } pub fn attach_mut(&mut self, raw: R) -> R::Ptr diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index d6931ed..cd21e57 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -26,17 +26,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{ - lua_createtable, lua_insert, lua_pushboolean, lua_pushlightuserdata, lua_rawget, - lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX, -}; -use crate::vm::registry::Set; -use crate::vm::value::util::ensure_value_top; +use std::collections::BTreeSet; +use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, State, Type, REGISTRYINDEX}; +use crate::vm::registry::{Set, Value}; +use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; use crate::vm::Vm; use std::ffi::c_void; +use std::marker::PhantomData; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Mutex; -#[derive(Debug, Copy, Clone, Eq, PartialEq)] -pub struct RawKey(*const c_void); +#[derive(Debug)] +pub struct RawKey { + ptr: *const c_void, + registered: AtomicBool, + register_lock: Mutex +} unsafe impl Send for RawKey {} unsafe impl Sync for RawKey {} @@ -54,25 +59,15 @@ impl RawKey { /// /// This is UB to call if the key was not registered in the given [Vm] using /// [register](RawKey::register). - pub unsafe fn push(&self, vm: &Vm) { + pub fn push(&self, vm: &Vm) { + check_register_key_unique(self); let l = vm.as_ptr(); unsafe { - lua_pushlightuserdata(l, self.0 as _); + lua_pushlightuserdata(l, self.ptr as _); lua_rawget(l, REGISTRYINDEX); } } - /// Attempts to register this key with the given [Vm] instance. This function ensures the key - /// does not collide. - /// - /// # Panic - /// - /// This function panics if this key is already registered in the given [Vm]. - #[inline(always)] - pub fn register(&self, vm: &Vm) { - unsafe { check_key_already_used(vm, self.0) }; - } - pub const fn new(name: &str) -> Self { // This is a re-write of https://github.com/BPXFormat/bpx-rs/blob/develop/src/hash.rs // in const context. @@ -87,7 +82,11 @@ impl RawKey { i += 1; } // And now a hack to turn u64 into ptr (btw, do NOT dereference it). - Self(val as usize as *const c_void) + Self { + ptr: val as usize as *const c_void, + registered: AtomicBool::new(false), + register_lock: Mutex::new(false) + } } } @@ -99,37 +98,82 @@ unsafe fn rawsetp(l: State, idx: i32, key: *const c_void) { impl Set for RawKey { unsafe fn set(&self, vm: &Vm, index: i32) { + check_register_key_unique(self); let l = vm.as_ptr(); ensure_value_top(vm, index); - rawsetp(l, REGISTRYINDEX, self.0); + rawsetp(l, REGISTRYINDEX, self.ptr); } } -const USED_KEYS: RawKey = RawKey::new("__used_keys__"); +// Ideally this should be a HashSet, however Rust has decided otherwise; then give Rust whatever it +// wants. In call cases this not going to be used in a hot path. +//FIXME: This does not work across modules. +static KEY_REGISTRY: Mutex> = Mutex::new(BTreeSet::new()); -unsafe fn check_key_already_used(vm: &Vm, key: *const c_void) { - if key == USED_KEYS.0 { - panic!("Attempt to use reserved named key __used_keys__"); +fn check_register_key_unique(key: &RawKey) { + if key.registered.load(Ordering::Relaxed) { + return; } - let l = vm.as_ptr(); - USED_KEYS.push(vm); - if lua_type(l, -1) != Type::Table { - lua_settop(l, -2); // Clear nil from the top of the stack. - lua_createtable(l, 0, 0); - USED_KEYS.set(vm, -1); // Pop the table and set it in the registry. - USED_KEYS.push(vm); // Re-push the table so that following code can use it. + let mut registered = key.register_lock.lock().unwrap(); + if *registered { + return; } - lua_pushlightuserdata(l, key as _); - lua_rawget(l, -2); // Table is now at index -2 on the stack. - let ty = lua_type(l, -1); - lua_settop(l, -2); // Remove value from stack. - if ty == Type::Boolean { - // Key is already taken, this is bad. - panic!("Attempt to register an already used named key"); + let mut lock = KEY_REGISTRY.lock().unwrap(); + if lock.contains(&(key.ptr as _)) { + panic!("Attempt to register a duplicate key"); + } else { + lock.insert(key.ptr as _); + *registered = true; + key.registered.store(true, Ordering::Relaxed); + } +} + +pub struct Key { + raw: RawKey, + useless: PhantomData<*const T> +} + +unsafe impl Send for Key {} +unsafe impl Sync for Key {} + +impl Key { + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + pub fn push<'a>(&self, vm: &'a Vm) -> Option> { + unsafe { + self.raw.push(vm); + ensure_type_equals(vm, -1, Type::LightUserdata) + .map(|_| T::from_registry(vm, -1)).ok() + } + } + + /// Pushes the lua value associated to this registry key on the lua stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] to attach the produced lua value to. + /// + /// returns: ::Value + #[inline(always)] + pub fn as_raw(&self) -> &RawKey { + &self.raw + } + + #[inline(always)] + pub const fn new(name: &str) -> Key { + Key { + raw: RawKey::new(name), + useless: PhantomData, + } + } + + #[inline(always)] + pub fn set(&self, value: T::Value<'_>) { + unsafe { T::set_registry(&self.raw, value) } } - lua_pushlightuserdata(l, key as _); - lua_pushboolean(l, 1); - lua_rawset(l, -3); // Table is now at -3 on the stack because we've just pushed a key and a - // value. - lua_settop(l, -2); // Clear the used keys table from the stack. } diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 216edc2..94d69d0 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -26,6 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::marker::PhantomData; + pub struct Table; pub struct Function; pub struct Thread; +pub struct RawPtr(PhantomData); diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 51cffa6..8c8634f 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -32,5 +32,6 @@ mod function; mod interface; pub mod types; pub mod util; +mod raw_ptr; pub use interface::*; diff --git a/core/src/vm/value/raw_ptr.rs b/core/src/vm/value/raw_ptr.rs new file mode 100644 index 0000000..436fae1 --- /dev/null +++ b/core/src/vm/value/raw_ptr.rs @@ -0,0 +1,129 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_pushlightuserdata, lua_touserdata}; +use crate::util::core::SimpleDrop; +use crate::vm::registry::{FromIndex, Set}; +use crate::vm::value::IntoLua; +use crate::vm::Vm; + +#[derive(Debug)] +pub struct RawPtr(*const T); + +unsafe impl SimpleDrop for RawPtr { } + +impl Clone for RawPtr { + #[inline(always)] + fn clone(&self) -> Self { + Self(self.0) + } +} + +impl Copy for RawPtr { } + +impl RawPtr { + #[inline(always)] + pub fn new(ptr: *mut T) -> Self { + Self(ptr) + } + + /// Returns the raw underlying pointer. + #[inline(always)] + pub fn as_ptr(&self) -> *const T { + self.0 + } + + /// Returns the raw underlying pointer. + #[inline(always)] + pub fn as_mut_ptr(&self) -> *mut T { + self.0 as *mut T + } +} + +unsafe impl IntoLua for RawPtr { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushlightuserdata(vm.as_ptr(), self.0 as _) }; + 1 + } +} + +pub struct RawPtrRef<'a, T> { + vm: &'a Vm, + index: i32, + ptr: RawPtr, +} + +impl<'a, T> RawPtrRef<'a, T> { + /// Creates a [RawPtrRef] from a [Vm] and an index on the vm. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] instance to attach to. + /// * `index`: the index on the given [Vm] instance. + /// + /// returns: RawPtrRef<> + /// + /// # Safety + /// + /// This function assumes that `index` points to a valid light-userdata object of type `T` on + /// the stack represented by `vm`. Breaking any of these assumptions is UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self { vm, index, ptr: RawPtr::new(lua_touserdata(vm.as_ptr(), index) as _) } + } + + pub fn from_ptr(vm: &'a Vm, ptr: RawPtr) -> Self { + ptr.into_lua(vm); + Self { vm, index: vm.top(), ptr } + } + + #[inline(always)] + pub fn as_ptr(&self) -> RawPtr { + self.ptr + } +} + +impl crate::vm::registry::Value for crate::vm::registry::types::RawPtr { + type Value<'a> = RawPtrRef<'a, T>; + + #[inline(always)] + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + RawPtrRef::from_raw(vm, index) + } + + #[inline(always)] + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index) } + } + + #[inline(always)] + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index); + } +} diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index 61195cf..fb285a9 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -38,6 +38,7 @@ use crate::vm::value::FromLua; use crate::vm::Vm; pub use super::function::Function; +pub use super::raw_ptr::{RawPtr, RawPtrRef}; #[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] pub struct Number(pub RawNumber); From 7e1436365953100951a1a6ee2bd8f056e7ef7e71 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 10:22:42 +0200 Subject: [PATCH 370/527] Added more lua_ext functions to support fast boolean check and named key registry accross modules --- core/src/ffi/ext.rs | 13 ++++++++++++- patch/lua_ext.patch | 42 ++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index 31df226..faaf8fe 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{RawInteger, RawNumber, State}; -use std::ffi::c_int; +use std::ffi::{c_int, c_uint, c_void}; pub type MSize = u32; @@ -37,6 +37,7 @@ pub type MSize = u32; extern "C" { pub fn lua_ext_fast_checknumber(l: State, numarg: c_int) -> RawNumber; pub fn lua_ext_fast_checkinteger(l: State, numarg: c_int) -> RawInteger; + pub fn lua_ext_fast_checkboolean(l: State, numarg: c_int) -> c_int; } //------- @@ -60,3 +61,13 @@ extern "C" { /// Sets global JIT flags. pub fn lua_ext_setjitflags(l: State, flags: u32) -> c_int; } + +//--------------------- +// Named registry keys +//--------------------- +extern "C" { + pub fn lua_ext_keyreg_get() -> *const c_void; + pub fn lua_ext_keyreg_set(ptr: *const c_void); + pub fn lua_ext_keyreg_ref() -> c_uint; + pub fn lua_ext_keyreg_unref() -> c_uint; +} diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index 5cfa5bc..55abc6a 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -1,8 +1,17 @@ diff --git a/src/lj_api.c b/src/lj_api.c -index e9fc25b4..7e21f135 100644 +index e9fc25b4..747d1905 100644 --- a/src/lj_api.c +++ b/src/lj_api.c -@@ -1317,3 +1317,44 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +@@ -9,6 +9,8 @@ + #define lj_api_c + #define LUA_CORE + ++#include ++ + #include "lj_obj.h" + #include "lj_gc.h" + #include "lj_err.h" +@@ -1317,3 +1319,73 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) g->allocf = f; } @@ -47,3 +56,32 @@ index e9fc25b4..7e21f135 100644 + return lj_num2int(n); +#endif +} ++ ++LUALIB_API int lua_ext_fast_checkboolean(lua_State *L, int idx) ++{ ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tvisbool(o))) { ++ return (int)intV(o); ++ } else { ++ lj_err_argt(L, idx, LUA_TNUMBER); ++ } ++} ++ ++static atomic_intptr_t BP3D_LUA_EXT_KEYREG = 0; ++static atomic_uint_fast32_t BP3D_LUA_EXT_KEYREG_REFS = 0; ++ ++LUALIB_API void* lua_ext_keyreg_get() { ++ return (void*)BP3D_LUA_EXT_KEYREG; ++} ++ ++LUALIB_API void lua_ext_keyreg_set(void* ptr) { ++ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; ++} ++ ++LUALIB_API unsigned int lua_ext_keyreg_ref() { ++ return (unsigned int)++BP3D_LUA_EXT_KEYREG_REFS; ++} ++ ++LUALIB_API unsigned int lua_ext_keyreg_unref() { ++ return (unsigned int)--BP3D_LUA_EXT_KEYREG_REFS; ++} From 69940d219dea002dc4f8d2a013875263bcce9e0c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 10:22:52 +0200 Subject: [PATCH 371/527] Use new lua_ext_checkboolean --- core/src/vm/function/core.rs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index b488047..2f9138b 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::ext::{lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; -use crate::ffi::laux::{luaL_checklstring, luaL_checktype, luaL_checkudata, luaL_testudata}; -use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, lua_type, RawInteger, RawNumber, Type}; +use crate::ffi::ext::{lua_ext_fast_checkboolean, lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; +use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_testudata}; +use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_type, RawInteger, RawNumber, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; @@ -220,11 +220,12 @@ impl_float!(f32, f64); impl LuaType for bool { } impl FromParam<'_> for bool { + #[inline(always)] unsafe fn from_param(vm: &'_ Vm, index: i32) -> Self { - luaL_checktype(vm.as_ptr(), index, Type::Boolean); - lua_toboolean(vm.as_ptr(), index) != 0 + lua_ext_fast_checkboolean(vm.as_ptr(), index) != 0 } + #[inline(always)] fn try_from_param(vm: &'_ Vm, index: i32) -> Option { FromLua::from_lua(vm, index).ok() } From 9a98452560e058b64eea865111fc232d93ac3401 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 10:23:27 +0200 Subject: [PATCH 372/527] Added support for named key registry accross modules --- core/src/vm/core/root_vm.rs | 4 ++++ core/src/vm/registry/named.rs | 39 +++++++++++++++++++++++++++++------ 2 files changed, 37 insertions(+), 6 deletions(-) diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index f7d356a..8f3ccab 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -35,6 +35,7 @@ use std::cell::Cell; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use std::sync::atomic::{AtomicBool, Ordering}; +use crate::vm::registry::named::{handle_root_vm_init, handle_root_vm_uninit}; thread_local! { // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! @@ -64,6 +65,7 @@ impl RootVm { vm: unsafe { Vm::from_raw(l) }, alive: Arc::new(AtomicBool::new(true)), }; + handle_root_vm_init(); unsafe { Pool::new_in_vm(&mut vm) }; vm } @@ -95,6 +97,8 @@ impl Drop for RootVm { unsafe { drop(Box::from_raw(Pool::from_vm(self))); } + debug!("Closing named key registry..."); + handle_root_vm_uninit(); unsafe { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index cd21e57..6a0ddda 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -35,6 +35,7 @@ use std::ffi::c_void; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; +use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_set, lua_ext_keyreg_unref}; #[derive(Debug)] pub struct RawKey { @@ -105,11 +106,6 @@ impl Set for RawKey { } } -// Ideally this should be a HashSet, however Rust has decided otherwise; then give Rust whatever it -// wants. In call cases this not going to be used in a hot path. -//FIXME: This does not work across modules. -static KEY_REGISTRY: Mutex> = Mutex::new(BTreeSet::new()); - fn check_register_key_unique(key: &RawKey) { if key.registered.load(Ordering::Relaxed) { return; @@ -118,7 +114,8 @@ fn check_register_key_unique(key: &RawKey) { if *registered { return; } - let mut lock = KEY_REGISTRY.lock().unwrap(); + let registry = unsafe { voidp_to_ref(lua_ext_keyreg_get()) }; + let mut lock = registry.lock().unwrap(); if lock.contains(&(key.ptr as _)) { panic!("Attempt to register a duplicate key"); } else { @@ -128,6 +125,36 @@ fn check_register_key_unique(key: &RawKey) { } } +unsafe fn voidp_to_ref(p: *const c_void) -> &'static Mutex> +{ + unsafe { &*(p as *const Mutex>) } +} + +unsafe fn voidp_to_ptr(p: *const c_void) -> *mut Mutex> +{ + p as *mut Mutex> +} + +fn ref_to_voidp(r: &'static Mutex>) -> *const c_void +{ + r as *const Mutex> as *const c_void +} + +pub(crate) fn handle_root_vm_init() { + let refs = unsafe { lua_ext_keyreg_ref() }; + if refs == 1 { // First reference, initialize the key registry... + let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(BTreeSet::new())))); + unsafe { lua_ext_keyreg_set(ptr) }; + } +} + +pub(crate) fn handle_root_vm_uninit() { + let refs = unsafe { lua_ext_keyreg_unref() }; + if refs == 0 { + unsafe { drop(Box::from_raw(voidp_to_ptr(lua_ext_keyreg_get()))) } + } +} + pub struct Key { raw: RawKey, useless: PhantomData<*const T> From f2b46ff84f1be6af174eea55f9194b1bd251ac5b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 14:29:00 +0200 Subject: [PATCH 373/527] Added coercible boolean type --- core/src/ffi/lua.rs | 2 + core/src/vm/error.rs | 12 ++++- core/src/vm/function/core.rs | 76 ++++++++++++++++++++++++++++++- core/src/vm/function/mod.rs | 2 - core/src/vm/value/core.rs | 70 ++++++++++++++++++++++++++++- core/src/vm/value/types.rs | 86 +++--------------------------------- core/src/vm/value/util.rs | 2 +- 7 files changed, 162 insertions(+), 88 deletions(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index c7871a8..eccc689 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -121,7 +121,9 @@ extern "C" { pub fn lua_lessthan(l: State, idx1: c_int, idx2: c_int) -> c_int; pub fn lua_tonumber(l: State, idx: c_int) -> RawNumber; + pub fn lua_tonumberx(l: State, idx: c_int, ok: *mut c_int) -> RawNumber; pub fn lua_tointeger(l: State, idx: c_int) -> RawInteger; + pub fn lua_tointegerx(l: State, idx: c_int, ok: *mut c_int) -> RawInteger; pub fn lua_toboolean(l: State, idx: c_int) -> c_int; pub fn lua_tolstring(l: State, idx: c_int, len: *mut usize) -> *const c_char; pub fn lua_objlen(l: State, idx: c_int) -> usize; diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index c1add07..be8f5a4 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -26,9 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::Type; +use crate::ffi::lua::{lua_type, Type}; use bp3d_util::simple_error; use std::fmt::{Display, Formatter}; +use crate::vm::Vm; #[derive(Debug, Copy, Clone)] pub struct TypeError { @@ -36,6 +37,15 @@ pub struct TypeError { pub actual: Type, } +impl TypeError { + pub fn from_stack(expected: Type, vm: &Vm, index: i32) -> Error { + Error::Type(Self { + expected, + actual: unsafe { lua_type(vm.as_ptr(), index) }, + }) + } +} + impl Display for TypeError { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { write!(f, "expected {:?}, got {:?}", self.expected, self.actual) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 2f9138b..20eb484 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -27,8 +27,8 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_fast_checkboolean, lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; -use crate::ffi::laux::{luaL_checklstring, luaL_checkudata, luaL_testudata}; -use crate::ffi::lua::{lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_type, RawInteger, RawNumber, Type}; +use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_checkudata, luaL_testudata}; +use crate::ffi::lua::{lua_isnumber, lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; @@ -38,6 +38,7 @@ use crate::vm::Vm; use std::borrow::Cow; use std::error::Error; use std::slice; +use crate::vm::value::types::{Boolean, Integer, Number}; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { @@ -328,3 +329,74 @@ impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6); impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7); impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8); impl_into_param_tuple!(T: 0, T1: 1, T2: 2, T3: 3, T4: 4, T5: 5, T6: 6, T7: 7, T8: 8, T9: 9); + +impl<'a> FromParam<'a> for Number { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Self(luaL_checknumber(vm.as_ptr(), index)) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) == 1 { + Some(Self(lua_tonumber(l, index))) + } else { + None + } + } + } +} + +impl<'a> FromParam<'a> for Integer { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Self(luaL_checkinteger(vm.as_ptr(), index)) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let l = vm.as_ptr(); + unsafe { + if lua_isnumber(l, index) == 1 { + Some(Self(lua_tointeger(l, index))) + } else { + None + } + } + } +} + +impl<'a> FromParam<'a> for Boolean { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Self(lua_toboolean(vm.as_ptr(), index) == 1) + } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + unsafe { Some(Self::from_param(vm, index)) } + } +} + +unsafe impl IntoParam for Number { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + unsafe { lua_pushnumber(vm.as_ptr(), self.0) } + 1 + } +} + +unsafe impl IntoParam for Integer { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + unsafe { lua_pushinteger(vm.as_ptr(), self.0) } + 1 + } +} + +unsafe impl IntoParam for Boolean { + fn into_param(self, vm: &Vm) -> i32 { + unsafe { lua_pushboolean(vm.as_ptr(), if self.0 { 1 } else { 0 }) } + 1 + } +} diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index 8f78c30..4265e31 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -26,8 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO: Support coercible boolean types (through dedicated type like Integer/Number) -// TODO: lua_ext_checkboolean // TODO: Attempt to make Vm Send behind a cargo feature // - Add checks to registry when the cargo feature is enabled // - Remove the single Vm per thread rule when send feature is enabled. diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 45c7a85..8bb7b2d 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -28,11 +28,12 @@ use std::borrow::Cow; use crate::ffi::laux::{luaL_setmetatable, luaL_testudata}; -use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tolstring, lua_tonumber, lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tointegerx, lua_tolstring, lua_tonumber, lua_tonumberx, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::util::ensure_type_equals; use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::Vm; impl<'a> FromLua<'a> for &'a str { @@ -354,3 +355,70 @@ where } } } + +impl FromLua<'_> for Integer { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + Integer(lua_tointeger(vm.as_ptr(), index)) + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + let mut ok = 0; + let num = unsafe { lua_tointegerx(vm.as_ptr(), index, &mut ok) }; + if ok != 1 { + Err(TypeError::from_stack(Type::Number, vm, index)) + } else { + Ok(Integer(num)) + } + } +} + +impl FromLua<'_> for Number { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + Number(lua_tonumber(vm.as_ptr(), index)) + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + let mut ok = 0; + let num = unsafe { lua_tonumberx(vm.as_ptr(), index, &mut ok) }; + if ok != 1 { + Err(TypeError::from_stack(Type::Number, vm, index)) + } else { + Ok(Number(num)) + } + } +} + +impl FromLua<'_> for Boolean { + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + Boolean(unsafe { lua_toboolean(vm.as_ptr(), index) == 1 }) + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + Ok(Boolean(unsafe { lua_toboolean(vm.as_ptr(), index) == 1 })) + } +} + +unsafe impl IntoLua for Number { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushnumber(vm.as_ptr(), self.0) } + 1 + } +} + +unsafe impl IntoLua for Integer { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushinteger(vm.as_ptr(), self.0) } + 1 + } +} + +unsafe impl IntoLua for Boolean { + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushboolean(vm.as_ptr(), if self.0 { 1 } else { 0 }) } + 1 + } +} diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index fb285a9..8a76d62 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -26,16 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::laux::{luaL_checkinteger, luaL_checknumber}; -use crate::ffi::lua::{ - lua_isnumber, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type, -}; +use crate::ffi::lua::{RawInteger, RawNumber}; use crate::util::core::SimpleDrop; -use crate::vm::error::{Error, TypeError}; -use crate::vm::function::FromParam; use crate::vm::util::LuaType; -use crate::vm::value::FromLua; -use crate::vm::Vm; pub use super::function::Function; pub use super::raw_ptr::{RawPtr, RawPtrRef}; @@ -54,78 +47,9 @@ unsafe impl SimpleDrop for Integer {} impl LuaType for Integer {} -impl<'a> FromParam<'a> for Number { - #[inline(always)] - unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { - Self(luaL_checknumber(vm.as_ptr(), index)) - } - - fn try_from_param(vm: &'a Vm, index: i32) -> Option { - let l = vm.as_ptr(); - unsafe { - if lua_isnumber(l, index) == 1 { - Some(Self(lua_tonumber(l, index))) - } else { - None - } - } - } -} - -impl<'a> FromLua<'a> for Number { - #[inline(always)] - unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { - Self(lua_tonumber(vm.as_ptr(), index)) - } - - fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - let l = vm.as_ptr(); - unsafe { - if lua_isnumber(l, index) != 1 { - return Err(Error::Type(TypeError { - expected: Type::Number, - actual: lua_type(l, index), - })); - } - Ok(Self(lua_tonumber(l, index))) - } - } -} - -impl<'a> FromParam<'a> for Integer { - #[inline(always)] - unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { - Self(luaL_checkinteger(vm.as_ptr(), index)) - } - - fn try_from_param(vm: &'a Vm, index: i32) -> Option { - let l = vm.as_ptr(); - unsafe { - if lua_isnumber(l, index) == 1 { - Some(Self(lua_tointeger(l, index))) - } else { - None - } - } - } -} +#[derive(Copy, Clone, PartialOrd, PartialEq, Debug, Eq, Ord, Hash)] +pub struct Boolean(pub bool); -impl<'a> FromLua<'a> for Integer { - #[inline(always)] - unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { - Self(lua_tointeger(vm.as_ptr(), index)) - } +unsafe impl SimpleDrop for Boolean {} - fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - let l = vm.as_ptr(); - unsafe { - if lua_isnumber(l, index) != 1 { - return Err(Error::Type(TypeError { - expected: Type::Number, - actual: lua_type(l, index), - })); - } - Ok(Self(lua_tointeger(l, index))) - } - } -} +impl LuaType for Boolean {} diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 715be84..0b1e444 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -35,7 +35,7 @@ use crate::vm::Vm; /// Ensures the given lua value at index is of a specified type. #[inline(always)] pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { - let ty = unsafe { crate::ffi::lua::lua_type(vm.as_ptr(), index) }; + let ty = unsafe { lua_type(vm.as_ptr(), index) }; if ty == expected { //FIXME: likely branch Ok(()) From 6e1ec421487ef272650e91fd032bc0d0828949fa Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 17:20:15 +0200 Subject: [PATCH 374/527] Fixed random crashes in check_register_key_unique --- core/src/vm/core/root_vm.rs | 1 - core/src/vm/registry/named.rs | 9 ++++++++- patch/lua_ext.patch | 7 +++++-- 3 files changed, 13 insertions(+), 4 deletions(-) diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index 8f3ccab..4226293 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -97,7 +97,6 @@ impl Drop for RootVm { unsafe { drop(Box::from_raw(Pool::from_vm(self))); } - debug!("Closing named key registry..."); handle_root_vm_uninit(); unsafe { debug!("Closing Lua VM..."); diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 6a0ddda..2e4279c 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -35,6 +35,7 @@ use std::ffi::c_void; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; +use bp3d_debug::debug; use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_set, lua_ext_keyreg_unref}; #[derive(Debug)] @@ -142,7 +143,9 @@ fn ref_to_voidp(r: &'static Mutex>) -> *const c_void pub(crate) fn handle_root_vm_init() { let refs = unsafe { lua_ext_keyreg_ref() }; + debug!({refs}, "Init RootVM"); if refs == 1 { // First reference, initialize the key registry... + debug!("Setting up new named key registry..."); let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(BTreeSet::new())))); unsafe { lua_ext_keyreg_set(ptr) }; } @@ -151,7 +154,11 @@ pub(crate) fn handle_root_vm_init() { pub(crate) fn handle_root_vm_uninit() { let refs = unsafe { lua_ext_keyreg_unref() }; if refs == 0 { - unsafe { drop(Box::from_raw(voidp_to_ptr(lua_ext_keyreg_get()))) } + debug!("Closing named key registry..."); + unsafe { + drop(Box::from_raw(voidp_to_ptr(lua_ext_keyreg_get()))); + lua_ext_keyreg_set(std::ptr::null_mut()); + } } } diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index 55abc6a..a504bc8 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -1,5 +1,5 @@ diff --git a/src/lj_api.c b/src/lj_api.c -index e9fc25b4..747d1905 100644 +index e9fc25b4..9360f14e 100644 --- a/src/lj_api.c +++ b/src/lj_api.c @@ -9,6 +9,8 @@ @@ -11,7 +11,7 @@ index e9fc25b4..747d1905 100644 #include "lj_obj.h" #include "lj_gc.h" #include "lj_err.h" -@@ -1317,3 +1319,73 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +@@ -1317,3 +1319,76 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) g->allocf = f; } @@ -79,6 +79,9 @@ index e9fc25b4..747d1905 100644 +} + +LUALIB_API unsigned int lua_ext_keyreg_ref() { ++ if (BP3D_LUA_EXT_KEYREG == 0) { ++ BP3D_LUA_EXT_KEYREG_REFS = 0; ++ } + return (unsigned int)++BP3D_LUA_EXT_KEYREG_REFS; +} + From a449634e1f83bf7b473d01fd716abe19ea10eb79 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 3 Aug 2025 17:21:34 +0200 Subject: [PATCH 375/527] Added asserts to avoid possible UB when accessing the named key registry --- core/src/vm/registry/named.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 2e4279c..b703986 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -128,11 +128,13 @@ fn check_register_key_unique(key: &RawKey) { unsafe fn voidp_to_ref(p: *const c_void) -> &'static Mutex> { + assert!(!p.is_null()); unsafe { &*(p as *const Mutex>) } } unsafe fn voidp_to_ptr(p: *const c_void) -> *mut Mutex> { + assert!(!p.is_null()); p as *mut Mutex> } From 2bb65bb2b3e1d9d763ae8d0bf8ac4301f6d5580c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 5 Aug 2025 09:11:50 +0200 Subject: [PATCH 376/527] Added new InterruptibleRootVm in preparation of future attempt to make RootVm Send --- core/src/vm/core/interrupt/mod.rs | 8 ++- core/src/vm/core/interrupt/unix.rs | 7 ++- core/src/vm/core/interrupt/vm.rs | 75 +++++++++++++++++++++++++++ core/src/vm/core/interrupt/windows.rs | 7 ++- core/src/vm/core/root_vm.rs | 13 +---- 5 files changed, 89 insertions(+), 21 deletions(-) create mode 100644 core/src/vm/core/interrupt/vm.rs diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index 228c925..36c092a 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -28,12 +28,16 @@ //! This module contains tools to allow interrupting a root Vm. +mod vm; + #[cfg(unix)] mod unix; #[cfg(windows)] mod windows; +pub use vm::InterruptibleRootVm; + #[cfg(unix)] pub use unix::Signal; @@ -56,11 +60,11 @@ simple_error! { } pub fn spawn_interruptible( - f: impl FnOnce(&mut crate::vm::RootVm) -> R + Send + 'static, + f: impl FnOnce(&mut InterruptibleRootVm) -> R + Send + 'static, ) -> (Signal, JoinHandle) { let (send, recv) = std::sync::mpsc::channel(); let handle = std::thread::spawn(move || { - let mut vm = crate::vm::RootVm::new(); + let mut vm = InterruptibleRootVm::new(crate::vm::RootVm::new()); send.send(Signal::create(&mut vm)).unwrap(); f(&mut vm) }); diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 759ebc7..d7ca57d 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -30,8 +30,7 @@ use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; -use crate::vm::core::interrupt::Error; -use crate::vm::RootVm; +use crate::vm::core::interrupt::{Error, InterruptibleRootVm}; use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use std::mem::MaybeUninit; @@ -105,8 +104,8 @@ extern "C" fn signal_handler(_: c_int) { static SIG_BOUND: Once = Once::new(); impl Signal { - pub fn create(vm: &mut RootVm) -> Self { - let alive = RootVm::get_alive(vm).clone(); + pub fn create(vm: &mut InterruptibleRootVm) -> Self { + let alive = InterruptibleRootVm::get_alive(vm).clone(); let th = unsafe { pthread_self() }; let l = vm.as_ptr(); let thread = std::thread::current().id(); diff --git a/core/src/vm/core/interrupt/vm.rs b/core/src/vm/core/interrupt/vm.rs new file mode 100644 index 0000000..8eed617 --- /dev/null +++ b/core/src/vm/core/interrupt/vm.rs @@ -0,0 +1,75 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use std::ops::{Deref, DerefMut}; +use std::sync::Arc; +use std::sync::atomic::AtomicBool; +use crate::vm::{RootVm, Vm}; + +pub struct InterruptibleRootVm { + vm: RootVm, + alive: Arc, + useless: PhantomData<*const ()>, // This is to ensure InterruptibleVm is never Send nor Sync. +} + +impl InterruptibleRootVm { + pub fn new(vm: RootVm) -> Self { + Self { + vm, + alive: Arc::new(AtomicBool::new(true)), + useless: PhantomData, + } + } + + pub fn get_alive(this: &Self) -> &Arc { + &this.alive + } +} + +impl Deref for InterruptibleRootVm { + type Target = Vm; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.vm + } +} + +impl DerefMut for InterruptibleRootVm { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vm + } +} + +impl Drop for InterruptibleRootVm { + fn drop(&mut self) { + self.alive.store(false, std::sync::atomic::Ordering::SeqCst); + } +} diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index acd1988..5b98d3f 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -26,12 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use super::Error; +use super::{Error, InterruptibleRootVm}; use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; -use crate::vm::RootVm; use bp3d_debug::{error, warning}; use std::sync::{Arc, Mutex}; use std::sync::atomic::AtomicBool; @@ -65,8 +64,8 @@ pub struct Signal { } impl Signal { - pub fn create(vm: &mut RootVm) -> Self { - let alive = RootVm::get_alive(vm).clone(); + pub fn create(vm: &mut InterruptibleRootVm) -> Self { + let alive = InterruptibleRootVm::get_alive(vm).clone(); let th = unsafe { GetCurrentThread() }; let l = vm.as_ptr(); Self { l, th, alive } diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index 4226293..e8ff342 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -33,8 +33,6 @@ use crate::vm::Vm; use bp3d_debug::debug; use std::cell::Cell; use std::ops::{Deref, DerefMut}; -use std::sync::Arc; -use std::sync::atomic::{AtomicBool, Ordering}; use crate::vm::registry::named::{handle_root_vm_init, handle_root_vm_uninit}; thread_local! { @@ -43,8 +41,7 @@ thread_local! { } pub struct RootVm { - vm: Vm, - alive: Arc + vm: Vm } impl Default for RootVm { @@ -62,17 +59,12 @@ impl RootVm { unsafe { luaL_openlibs(l) }; HAS_VM.set(true); let mut vm = RootVm { - vm: unsafe { Vm::from_raw(l) }, - alive: Arc::new(AtomicBool::new(true)), + vm: unsafe { Vm::from_raw(l) } }; handle_root_vm_init(); unsafe { Pool::new_in_vm(&mut vm) }; vm } - - pub fn get_alive(this: &Self) -> &Arc { - &this.alive - } } impl Deref for RootVm { @@ -102,7 +94,6 @@ impl Drop for RootVm { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); } - self.alive.store(false, Ordering::SeqCst); HAS_VM.set(false); } } From 8fae47582d93b48c32c296aca6a0b601dc320577 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 5 Aug 2025 23:27:15 +0200 Subject: [PATCH 377/527] Added new LuaRef type to replace hacky RawPtr/RawPtrRef combination and expand the registry system to all possible FromLua/IntoLua types --- core/src/vm/closure/core.rs | 4 +- core/src/vm/core/destructor.rs | 18 ++-- core/src/vm/function/mod.rs | 4 +- core/src/vm/registry/lua_ref.rs | 157 ++++++++++++++++++++++++++++++++ core/src/vm/registry/mod.rs | 1 + core/src/vm/registry/types.rs | 2 +- core/src/vm/value/raw_ptr.rs | 63 +++---------- core/src/vm/value/types.rs | 2 +- 8 files changed, 184 insertions(+), 67 deletions(-) create mode 100644 core/src/vm/registry/lua_ref.rs diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index 9aef356..a3a6502 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_topointer, GLOBALSINDEX}; +use crate::ffi::lua::GLOBALSINDEX; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; use crate::vm::value::{FromLua, IntoLua}; @@ -84,7 +84,7 @@ impl_from_upvalue_using_from_lua_unchecked!(i8, u8, i16, u16, i32, u32, f32, f64 impl FromUpvalue<'_> for RawPtr { #[inline(always)] unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { - RawPtr::new(lua_topointer(vm.as_ptr(), GLOBALSINDEX - index) as _) + RawPtr::from_lua(vm, GLOBALSINDEX - index) } } diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index ba57fb2..1e95b50 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::lua_settop; use crate::vm::registry::named::Key; use crate::vm::Vm; use bp3d_debug::debug; use std::rc::Rc; -use crate::vm::registry::types::RawPtr; -use crate::vm::value::types::RawPtrRef; +use crate::vm::registry::types::LuaRef; +use crate::vm::value::types::RawPtr; +use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { @@ -73,7 +73,7 @@ impl Raw for Rc { } } -static DESTRUCTOR_POOL: Key> = Key::new("__destructor_pool__"); +static DESTRUCTOR_POOL: Key>> = Key::new("__destructor_pool__"); #[derive(Default)] pub struct Pool { @@ -92,8 +92,8 @@ impl Pool { /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. pub unsafe fn new_in_vm(vm: &mut Vm) { let b = Box::leak(Box::new(Pool::new())); - let ptr = crate::vm::value::types::RawPtr::new(b as *mut Pool); - DESTRUCTOR_POOL.set(RawPtrRef::from_ptr(vm, ptr)); + let ptr = RawPtr::new(b as *mut Pool); + DESTRUCTOR_POOL.set(LiveLuaRef::new(vm, ptr)); } /// Extracts a destructor pool from the given [Vm]. @@ -101,11 +101,9 @@ impl Pool { /// # Safety /// /// The returned reference must not be aliased. - unsafe fn _from_vm(vm: &Vm) -> crate::vm::value::types::RawPtr { - let l = vm.as_ptr(); + unsafe fn _from_vm(vm: &Vm) -> RawPtr { let ptr = DESTRUCTOR_POOL.push(vm).unwrap(); - lua_settop(l, -2); // Remove the pointer from the lua stack. - ptr.as_ptr() + ptr.get() } pub fn from_vm(vm: &mut Vm) -> &mut Self { diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index 4265e31..e40a03d 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -26,9 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO: Attempt to make Vm Send behind a cargo feature +// TODO: Attempt to make RootVm Send behind a cargo feature // - Add checks to registry when the cargo feature is enabled -// - Remove the single Vm per thread rule when send feature is enabled. +// - Remove the single RootVm per thread rule when send feature is enabled. // - Interrupt system should wrap RootVm in a special never Send and never Sync type to avoid // specific safety issue. diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs new file mode 100644 index 0000000..647b705 --- /dev/null +++ b/core/src/vm/registry/lua_ref.rs @@ -0,0 +1,157 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use crate::ffi::lua::lua_settop; +use crate::vm::registry::{FromIndex, Set}; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::types::RawPtr; +use crate::vm::Vm; + +/// Represents a simple registry value. +/// +/// # Safety +/// +/// This trait always assumes a single lua value is managed. If the underlying type manipulates +/// multiple stack indices on the given lua [Vm], the implementation is considered UB. +pub unsafe trait SimpleRegistryValue { + fn into_lua(self, vm: &Vm); + + /// Extracts an instance of `Self` from the given Lua [Vm] and index. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] where to read the object from. + /// * `index`: the index of the object on the stack. + /// + /// returns: Self + /// + /// # Safety + /// + /// This function assumes the given index is valid for [Vm] and that the object on the stack at + /// the given index is already of type `Self`. If any of these assumptions are broken, this + /// function is UB. + unsafe fn from_lua(vm: &Vm, index: i32) -> Self; +} + +pub struct LuaRef<'a, T> { + vm: &'a Vm, + index: i32, + useless: PhantomData, +} + +impl<'a, T: SimpleRegistryValue> LuaRef<'a, T> { + pub fn new(vm: &'a Vm, value: T) -> Self { + value.into_lua(vm); + Self { + vm, + index: vm.top(), + useless: PhantomData, + } + } + + /// Constructs a [LuaRef] from a raw index on a Lua [Vm]. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] where to read the lightuserdata from. + /// * `index`: the index of the lightuserdata pointer on the stack. + /// + /// # Safety + /// + /// Calling this function assumes the given index is absolute and is valid for [Vm] and that + /// the object on the stack at the given index is already of type `T`. If any of + /// these assumptions are not respected, this function is UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self { + vm, + index, + useless: PhantomData, + } + } + + #[inline(always)] + pub fn get(&self) -> T { + unsafe { T::from_lua(self.vm, self.index) } + } + + pub fn set(&self, value: T) { + //TODO: Implement + todo!() + } +} + +impl<'a, T> Drop for LuaRef<'a, T> { + fn drop(&mut self) { + // Remove the object from the lua stack if it is on top of the stack. + if self.index == self.vm.top() { + unsafe { lua_settop(self.vm.as_ptr(), -2); } + } + } +} + +impl super::Value for super::types::LuaRef { + type Value<'a> = LuaRef<'a, T>; + + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + LuaRef::from_raw(vm, vm.get_absolute_index(index)) + } + + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index) } + } + + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index); + } +} + +unsafe impl SimpleRegistryValue for T where for<'a> T: FromLua<'a> + IntoLua { + #[inline(always)] + fn into_lua(self, vm: &Vm) { + IntoLua::into_lua(self, vm); + } + + #[inline(always)] + unsafe fn from_lua(vm: &Vm, index: i32) -> Self { + unsafe { FromLua::from_lua_unchecked(vm, index) } + } +} + +unsafe impl SimpleRegistryValue for RawPtr { + #[inline(always)] + fn into_lua(self, vm: &Vm) { + IntoLua::into_lua(self, vm); + } + + #[inline(always)] + unsafe fn from_lua(vm: &Vm, index: i32) -> Self { + unsafe { RawPtr::from_lua(vm, index) } + } +} diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs index 067427b..a530fd5 100644 --- a/core/src/vm/registry/mod.rs +++ b/core/src/vm/registry/mod.rs @@ -30,5 +30,6 @@ pub mod core; mod interface; pub mod named; pub mod types; +pub mod lua_ref; pub use interface::*; diff --git a/core/src/vm/registry/types.rs b/core/src/vm/registry/types.rs index 94d69d0..6a0107a 100644 --- a/core/src/vm/registry/types.rs +++ b/core/src/vm/registry/types.rs @@ -31,4 +31,4 @@ use std::marker::PhantomData; pub struct Table; pub struct Function; pub struct Thread; -pub struct RawPtr(PhantomData); +pub struct LuaRef(PhantomData); diff --git a/core/src/vm/value/raw_ptr.rs b/core/src/vm/value/raw_ptr.rs index 436fae1..9ea9c36 100644 --- a/core/src/vm/value/raw_ptr.rs +++ b/core/src/vm/value/raw_ptr.rs @@ -28,7 +28,6 @@ use crate::ffi::lua::{lua_pushlightuserdata, lua_touserdata}; use crate::util::core::SimpleDrop; -use crate::vm::registry::{FromIndex, Set}; use crate::vm::value::IntoLua; use crate::vm::Vm; @@ -63,67 +62,29 @@ impl RawPtr { pub fn as_mut_ptr(&self) -> *mut T { self.0 as *mut T } -} - -unsafe impl IntoLua for RawPtr { - #[inline(always)] - fn into_lua(self, vm: &Vm) -> u16 { - unsafe { lua_pushlightuserdata(vm.as_ptr(), self.0 as _) }; - 1 - } -} - -pub struct RawPtrRef<'a, T> { - vm: &'a Vm, - index: i32, - ptr: RawPtr, -} -impl<'a, T> RawPtrRef<'a, T> { - /// Creates a [RawPtrRef] from a [Vm] and an index on the vm. + /// Extracts a [RawPtr] from the given Lua [Vm] index. /// /// # Arguments /// - /// * `vm`: the [Vm] instance to attach to. - /// * `index`: the index on the given [Vm] instance. - /// - /// returns: RawPtrRef<> + /// * `vm`: the [Vm] where to read the lightuserdata from. + /// * `index`: the index of the lightuserdata pointer on the stack. /// /// # Safety /// - /// This function assumes that `index` points to a valid light-userdata object of type `T` on - /// the stack represented by `vm`. Breaking any of these assumptions is UB. - #[inline(always)] - pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { - Self { vm, index, ptr: RawPtr::new(lua_touserdata(vm.as_ptr(), index) as _) } - } - - pub fn from_ptr(vm: &'a Vm, ptr: RawPtr) -> Self { - ptr.into_lua(vm); - Self { vm, index: vm.top(), ptr } - } - + /// Calling this function assumes the given index is valid for [Vm] and the lightuserdata + /// pointer points to an instance of T. If any of these assumptions are not respected, + /// this function is UB. #[inline(always)] - pub fn as_ptr(&self) -> RawPtr { - self.ptr + pub unsafe fn from_lua(vm: &Vm, index: i32) -> Self { + Self(lua_touserdata(vm.as_ptr(), index) as _) } } -impl crate::vm::registry::Value for crate::vm::registry::types::RawPtr { - type Value<'a> = RawPtrRef<'a, T>; - - #[inline(always)] - unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - RawPtrRef::from_raw(vm, index) - } - - #[inline(always)] - fn push_registry(value: Self::Value<'_>) -> R { - unsafe { R::from_index(value.vm, value.index) } - } - +unsafe impl IntoLua for RawPtr { #[inline(always)] - unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { - key.set(value.vm, value.index); + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { lua_pushlightuserdata(vm.as_ptr(), self.0 as _) }; + 1 } } diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index 8a76d62..d6663a4 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -31,7 +31,7 @@ use crate::util::core::SimpleDrop; use crate::vm::util::LuaType; pub use super::function::Function; -pub use super::raw_ptr::{RawPtr, RawPtrRef}; +pub use super::raw_ptr::RawPtr; #[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] pub struct Number(pub RawNumber); From fa563193cd5d85d5ceddc6cf6034a430aa0b1212 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 6 Aug 2025 09:38:38 +0200 Subject: [PATCH 378/527] Added support for set function to LuaRef --- core/src/vm/registry/lua_ref.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 647b705..94d4e9d 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::marker::PhantomData; -use crate::ffi::lua::lua_settop; +use crate::ffi::lua::{lua_replace, lua_settop}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::types::RawPtr; @@ -102,8 +102,8 @@ impl<'a, T: SimpleRegistryValue> LuaRef<'a, T> { } pub fn set(&self, value: T) { - //TODO: Implement - todo!() + value.into_lua(self.vm); + unsafe { lua_replace(self.vm.as_ptr(), self.index) }; } } From 21dc4f84c9b6b6f7a73fc42282f8d9e126ff3799 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 6 Aug 2025 12:55:43 +0200 Subject: [PATCH 379/527] Fixed more issues with named key registry --- core/src/ffi/ext.rs | 9 ++++----- core/src/vm/registry/named.rs | 36 ++++++++++++++++++----------------- patch/lua_ext.patch | 33 ++++++++++++++++++++------------ 3 files changed, 44 insertions(+), 34 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index faaf8fe..1ccda62 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{RawInteger, RawNumber, State}; -use std::ffi::{c_int, c_uint, c_void}; +use std::ffi::{c_int, c_void}; pub type MSize = u32; @@ -66,8 +66,7 @@ extern "C" { // Named registry keys //--------------------- extern "C" { - pub fn lua_ext_keyreg_get() -> *const c_void; - pub fn lua_ext_keyreg_set(ptr: *const c_void); - pub fn lua_ext_keyreg_ref() -> c_uint; - pub fn lua_ext_keyreg_unref() -> c_uint; + pub fn lua_ext_keyreg_get() -> *mut c_void; + pub fn lua_ext_keyreg_ref(ptr: *mut c_void) -> *mut c_void; + pub fn lua_ext_keyreg_unref() -> *mut c_void; } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index b703986..04ebc06 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -36,7 +36,7 @@ use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; use bp3d_debug::debug; -use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_set, lua_ext_keyreg_unref}; +use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_unref}; #[derive(Debug)] pub struct RawKey { @@ -126,41 +126,43 @@ fn check_register_key_unique(key: &RawKey) { } } -unsafe fn voidp_to_ref(p: *const c_void) -> &'static Mutex> +unsafe fn voidp_to_ref(p: *mut c_void) -> &'static Mutex> { assert!(!p.is_null()); unsafe { &*(p as *const Mutex>) } } -unsafe fn voidp_to_ptr(p: *const c_void) -> *mut Mutex> +unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut Mutex> { assert!(!p.is_null()); p as *mut Mutex> } -fn ref_to_voidp(r: &'static Mutex>) -> *const c_void +fn ref_to_voidp(r: &'static Mutex>) -> *mut c_void { - r as *const Mutex> as *const c_void + r as *const Mutex> as *mut c_void } pub(crate) fn handle_root_vm_init() { - let refs = unsafe { lua_ext_keyreg_ref() }; - debug!({refs}, "Init RootVM"); - if refs == 1 { // First reference, initialize the key registry... - debug!("Setting up new named key registry..."); - let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(BTreeSet::new())))); - unsafe { lua_ext_keyreg_set(ptr) }; + let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(BTreeSet::new())))); + // Pointer set in lua_ext_keyreg_ref to avoid TOCTOU. + let ptr = unsafe { lua_ext_keyreg_ref(ptr) }; + if ptr.is_null() { + debug!("Set up new named key registry..."); + } else { + debug!("Named key registry already exists"); + unsafe { drop(Box::from_raw(voidp_to_ptr(ptr))) }; } } pub(crate) fn handle_root_vm_uninit() { - let refs = unsafe { lua_ext_keyreg_unref() }; - if refs == 0 { + // Pointer reset to NULL in lua_ext_keyreg_unref to avoid TOCTOU. + let ptr = unsafe { lua_ext_keyreg_unref() }; + if !ptr.is_null() { debug!("Closing named key registry..."); - unsafe { - drop(Box::from_raw(voidp_to_ptr(lua_ext_keyreg_get()))); - lua_ext_keyreg_set(std::ptr::null_mut()); - } + unsafe { drop(Box::from_raw(voidp_to_ptr(ptr))) }; + } else { + debug!("Named key registry is still in use"); } } diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index a504bc8..6403726 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -1,5 +1,5 @@ diff --git a/src/lj_api.c b/src/lj_api.c -index e9fc25b4..9360f14e 100644 +index e9fc25b4..4559cdb5 100644 --- a/src/lj_api.c +++ b/src/lj_api.c @@ -9,6 +9,8 @@ @@ -11,7 +11,7 @@ index e9fc25b4..9360f14e 100644 #include "lj_obj.h" #include "lj_gc.h" #include "lj_err.h" -@@ -1317,3 +1319,76 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +@@ -1317,3 +1319,85 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) g->allocf = f; } @@ -74,17 +74,26 @@ index e9fc25b4..9360f14e 100644 + return (void*)BP3D_LUA_EXT_KEYREG; +} + -+LUALIB_API void lua_ext_keyreg_set(void* ptr) { -+ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; -+} -+ -+LUALIB_API unsigned int lua_ext_keyreg_ref() { -+ if (BP3D_LUA_EXT_KEYREG == 0) { -+ BP3D_LUA_EXT_KEYREG_REFS = 0; ++LUALIB_API void* lua_ext_keyreg_ref(void* ptr) { ++ int flag = 0; ++ if (BP3D_LUA_EXT_KEYREG_REFS == 0) { ++ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; ++ flag = 1; ++ } ++ (unsigned int)++BP3D_LUA_EXT_KEYREG_REFS; ++ if (flag == 1) { ++ return NULL; ++ } else { ++ return ptr; + } -+ return (unsigned int)++BP3D_LUA_EXT_KEYREG_REFS; +} + -+LUALIB_API unsigned int lua_ext_keyreg_unref() { -+ return (unsigned int)--BP3D_LUA_EXT_KEYREG_REFS; ++LUALIB_API void* lua_ext_keyreg_unref() { ++ unsigned int refs = (unsigned int)--BP3D_LUA_EXT_KEYREG_REFS; ++ if (refs == 0) { ++ void* ptr = (void*)BP3D_LUA_EXT_KEYREG; ++ BP3D_LUA_EXT_KEYREG = 0; ++ return ptr; ++ } ++ return NULL; +} From b648b40d3a5337e2d2ce932fefd8f813ba845e58 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 6 Aug 2025 12:56:17 +0200 Subject: [PATCH 380/527] Added initial version of RootVm send feature --- core/Cargo.toml | 1 + core/src/vm/core/root_vm.rs | 10 ++- core/src/vm/registry/core.rs | 16 +++- core/src/vm/registry/mod.rs | 3 + core/src/vm/registry/send_key.rs | 71 +++++++++++++++++ core/tests/test_multi_root_vms.rs | 128 ++++++++++++++++++++++++++++++ 6 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 core/src/vm/registry/send_key.rs create mode 100644 core/tests/test_multi_root_vms.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 8d014b8..2e91b5c 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -46,3 +46,4 @@ util-thread = [] util-module = ["module", "bp3d-os/module"] libs-core = ["util-namespace"] libs = ["libs-core", "time", "bp3d-os/time"] +send = [] diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm.rs index e8ff342..55fca5c 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm.rs @@ -31,19 +31,22 @@ use crate::ffi::lua::lua_close; use crate::vm::core::destructor::Pool; use crate::vm::Vm; use bp3d_debug::debug; -use std::cell::Cell; use std::ops::{Deref, DerefMut}; use crate::vm::registry::named::{handle_root_vm_init, handle_root_vm_uninit}; +#[cfg(not(feature = "send"))] thread_local! { // WTF?! The compiler should be smart enough to do this on its own! Another compiler defect! - static HAS_VM: Cell = const { Cell::new(false) }; + static HAS_VM: std::cell::Cell = const { std::cell::Cell::new(false) }; } pub struct RootVm { vm: Vm } +#[cfg(feature = "send")] +unsafe impl Send for RootVm { } + impl Default for RootVm { fn default() -> Self { Self::new() @@ -52,11 +55,13 @@ impl Default for RootVm { impl RootVm { pub fn new() -> RootVm { + #[cfg(not(feature = "send"))] if HAS_VM.get() { panic!("A VM already exists for this thread.") } let l = unsafe { luaL_newstate() }; unsafe { luaL_openlibs(l) }; + #[cfg(not(feature = "send"))] HAS_VM.set(true); let mut vm = RootVm { vm: unsafe { Vm::from_raw(l) } @@ -94,6 +99,7 @@ impl Drop for RootVm { debug!("Closing Lua VM..."); lua_close(self.vm.as_ptr()); } + #[cfg(not(feature = "send"))] HAS_VM.set(false); } } diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index 8d01e20..ea61768 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -33,6 +33,8 @@ use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; +#[cfg(feature = "send")] +use crate::vm::registry::send_key::VmCheckedRawKey; //TODO: Check if key can be a NonZeroI32. @@ -118,10 +120,16 @@ impl Set for RawKey { } pub struct Key { + #[cfg(feature = "send")] + raw: VmCheckedRawKey, + #[cfg(not(feature = "send"))] raw: RawKey, useless: PhantomData<*const T>, } +#[cfg(feature = "send")] +unsafe impl Send for Key {} + impl Key { /// Pushes the lua value associated to this registry key on the lua stack. /// @@ -147,7 +155,10 @@ impl Key { /// returns: ::Value #[inline(always)] pub fn as_raw(&self) -> RawKey { - self.raw + #[cfg(feature = "send")] + return self.raw.as_raw(); + #[cfg(not(feature = "send"))] + return self.raw; } /// Deletes this registry key from the specified [Vm]. @@ -159,6 +170,9 @@ impl Key { /// returns: () #[inline(always)] pub fn delete(self, vm: &Vm) { + #[cfg(feature = "send")] + self.raw.delete(vm); + #[cfg(not(feature = "send"))] unsafe { self.raw.delete(vm) }; } diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs index a530fd5..a707f82 100644 --- a/core/src/vm/registry/mod.rs +++ b/core/src/vm/registry/mod.rs @@ -32,4 +32,7 @@ pub mod named; pub mod types; pub mod lua_ref; +#[cfg(feature = "send")] +mod send_key; + pub use interface::*; diff --git a/core/src/vm/registry/send_key.rs b/core/src/vm/registry/send_key.rs new file mode 100644 index 0000000..fead15b --- /dev/null +++ b/core/src/vm/registry/send_key.rs @@ -0,0 +1,71 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::State; +use crate::vm::registry::core::RawKey; +use crate::vm::registry::{FromIndex, Set}; +use crate::vm::Vm; + +pub struct VmCheckedRawKey { + raw: RawKey, + vm: State +} + +impl VmCheckedRawKey { + pub fn push(&self, vm: &Vm) { + assert!(vm.as_ptr() == self.vm); + unsafe { self.raw.push(vm) } + } + + pub fn delete(self, vm: &Vm) { + assert!(vm.as_ptr() == self.vm); + unsafe { self.raw.delete(vm) } + } + + #[inline(always)] + pub fn as_raw(&self) -> RawKey { + self.raw + } +} + +impl FromIndex for VmCheckedRawKey { + unsafe fn from_index(vm: &Vm, index: i32) -> Self { + let raw = RawKey::from_index(vm, index); + Self { + vm: vm.as_ptr(), + raw + } + } +} + +impl Set for VmCheckedRawKey { + unsafe fn set(&self, vm: &Vm, index: i32) { + assert!(vm.as_ptr() == self.vm); + self.raw.set(vm, index); + } +} diff --git a/core/tests/test_multi_root_vms.rs b/core/tests/test_multi_root_vms.rs new file mode 100644 index 0000000..24b11b0 --- /dev/null +++ b/core/tests/test_multi_root_vms.rs @@ -0,0 +1,128 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#![cfg(feature = "send")] + +use bp3d_lua::vm::registry::core::Key; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::types::Function; +use bp3d_lua::vm::registry::types; + +#[test] +fn test_multi_root_vms_basic() { + let root1 = RootVm::new(); + let root2 = RootVm::new(); + let handle1 = std::thread::spawn(move || { + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + glb.call::<()>(()).unwrap(); + (Key::::new(glb), root1) + }); + let handle2 = std::thread::spawn(move || { + root2.run_code::<()>(c"function Test2() end").unwrap(); + let glb: Option = root2.get_global("Test").unwrap(); + assert!(glb.is_none()); + let glb: Function = root2.get_global("Test2").unwrap(); + glb.call::<()>(()).unwrap(); + (Key::::new(glb), root2) + }); + let (key1, root1) = handle1.join().unwrap(); + let (key2, root2) = handle2.join().unwrap(); + let fn1 = key1.push(&root1); + let fn2 = key2.push(&root2); + fn1.call::<()>(()).unwrap(); + fn2.call::<()>(()).unwrap(); +} + +#[test] +#[should_panic] +fn test_multi_root_vms_panic_1() { + let root1 = RootVm::new(); + let root2 = RootVm::new(); + let handle1 = std::thread::spawn(move || { + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + (Key::::new(glb), root1) + }); + let handle2 = std::thread::spawn(move || { + root2.run_code::<()>(c"function Test2() end").unwrap(); + let glb: Function = root2.get_global("Test2").unwrap(); + (Key::::new(glb), root2) + }); + let (key1, _) = handle1.join().unwrap(); + let (_, root2) = handle2.join().unwrap(); + key1.push(&root2); +} + +#[test] +#[should_panic] +fn test_multi_root_vms_panic_2() { + let root1 = RootVm::new(); + let root2 = RootVm::new(); + let handle1 = std::thread::spawn(move || { + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + (Key::::new(glb), root1) + }); + let handle2 = std::thread::spawn(move || { + root2.run_code::<()>(c"function Test2() end").unwrap(); + let glb: Function = root2.get_global("Test2").unwrap(); + (Key::::new(glb), root2) + }); + let (_, root1) = handle1.join().unwrap(); + let (key2, _) = handle2.join().unwrap(); + key2.push(&root1); +} + +#[test] +#[should_panic] +fn test_multi_root_vms_panic_3() { + let root1 = RootVm::new(); + let handle1 = std::thread::spawn(move || { + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + Key::::new(glb) + }); + let key = handle1.join().unwrap(); + let root2 = RootVm::new(); + key.push(&root2); +} + +#[test] +#[should_panic] +fn test_multi_root_vms_panic_4() { + let root1 = RootVm::new(); + let handle1 = std::thread::spawn(move || { + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + Key::::new(glb) + }); + let key = handle1.join().unwrap(); + let root2 = RootVm::new(); + key.delete(&root2); +} From c1b31ca676c13ca68536b7b5ef90f9ef9ae26b9a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 6 Aug 2025 13:05:13 +0200 Subject: [PATCH 381/527] Removed now completed TODOs --- core/src/lib.rs | 1 - core/src/vm/function/mod.rs | 6 ------ 2 files changed, 7 deletions(-) diff --git a/core/src/lib.rs b/core/src/lib.rs index bf2b6b0..f34fa31 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//TODO: Support dynamic linking and modules by dynamic linking to luajit. //TODO: Attempt to implement custom __index on userdata. pub mod ffi; diff --git a/core/src/vm/function/mod.rs b/core/src/vm/function/mod.rs index e40a03d..0f7fbf0 100644 --- a/core/src/vm/function/mod.rs +++ b/core/src/vm/function/mod.rs @@ -26,12 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -// TODO: Attempt to make RootVm Send behind a cargo feature -// - Add checks to registry when the cargo feature is enabled -// - Remove the single RootVm per thread rule when send feature is enabled. -// - Interrupt system should wrap RootVm in a special never Send and never Sync type to avoid -// specific safety issue. - mod core; mod interface; pub mod types; From dafacaa723e18f60d93efca482d5b96dc7bda02a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 6 Aug 2025 13:12:11 +0200 Subject: [PATCH 382/527] Updated version of bp3d-lua and updated version of mlua in performance test --- core/Cargo.toml | 2 +- core/tests/test_vm_libs.rs | 2 +- shell/core/Cargo.toml | 2 +- testbin/Cargo.toml | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 2e91b5c..5e18bde 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua" -version = "1.0.0-rc.3.0.0" +version = "1.0.0-rc.4.0.0" authors = ["Yuri Edward "] edition = "2021" # Possible very-little performance improvement with 2024 (2.35 vs 2.37/2.4) description = "Lua wrapper and base library for BP3D." diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index ee459d0..6d8dc84 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -43,7 +43,7 @@ fn test_vm_lib_lua() { vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '1.0.0-rc.3.0.0') + assert(bp3d.lua.version == '1.0.0-rc.4.0.0') assert(#bp3d.lua.patches == 5) local func = bp3d.lua.loadString('return 1 + 1') assert(func) diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index b0c030e..e37f5bb 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" publish = false [dependencies] -bp3d-lua = { version = "1.0.0-rc.2.1.0", path = "../../core", features = ["root-vm", "util-module", "util-thread", "libs", "dynamic", "interrupt"] } +bp3d-lua = { version = "1.0.0-rc.4.0.0", path = "../../core", features = ["root-vm", "util-module", "util-thread", "libs", "dynamic", "interrupt"] } bp3d-debug = "1.0.0-rc.6.2.0" bp3d-net = { version = "1.0.0-rc.2.1.2", features = ["ipc"] } tokio = { version = "1.45.1", features = ["full"] } diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index b93341a..60c7ced 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -7,8 +7,8 @@ publish = false # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -mlua = { version = "0.10.3", features = ["luajit"] } -bp3d-lua = { version = "1.0.0-rc.2.0.0", path = "../core", features = ["root-vm"] } +mlua = { version = "0.11.1", features = ["luajit"] } +bp3d-lua = { version = "1.0.0-rc.4.0.0", path = "../core", features = ["root-vm"] } bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } [workspace] From 6b954a297b936d06f3d292fb44e87c27cb8a1cdf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 10:03:56 +0200 Subject: [PATCH 383/527] Added Send requirement to userdata types if the send feature is enabled on the RootVm --- core/src/util/module.rs | 4 ++++ core/src/vm/userdata/interface.rs | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/core/src/util/module.rs b/core/src/util/module.rs index d386527..45511db 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -129,6 +129,10 @@ pub struct ModuleManager { loader: ModuleLoader, } +// This is safe because ModuleManager does not use thread locals or mutable globals of some kind. +// The hard work is mostly done by syscalls or static read-only memory. +unsafe impl Send for ModuleManager {} + impl ModuleManager { fn load_plugin( vm: &Vm, diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 49dc135..5055dba 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -33,6 +33,16 @@ use std::ffi::CStr; /// This trait represents all types of UserData. An UserData is a type with a maximum alignment of 8 /// with its memory tied to the Lua GC. +#[cfg(feature = "send")] +pub trait UserData: Send + Sized { + const CLASS_NAME: &'static CStr; + + fn register(registry: &Registry) -> Result<(), Error>; +} + +/// This trait represents all types of UserData. An UserData is a type with a maximum alignment of 8 +/// with its memory tied to the Lua GC. +#[cfg(not(feature = "send"))] pub trait UserData: Sized { const CLASS_NAME: &'static CStr; From 4c33054d7654bdb3a6e5e28e1eba593799c736d8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 10:05:57 +0200 Subject: [PATCH 384/527] Added Send requirment when the send feature is enabled to RClosure --- core/src/vm/closure/types.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 61e469d..152a442 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -61,16 +61,40 @@ unsafe impl IntoLua for RClosure { } impl RClosure> { + #[cfg(feature = "send")] pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self where for<'a> T: FromParam<'a>, R: IntoParam, + F: Send { let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam, + F: Send + { + let vm = unsafe { Vm::from_raw(l) }; + let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let res = unsafe { (*upvalue.as_ptr())(args) }; + res.into_param(&vm) as _ + } + RClosure::new(_cfunc::, RawPtr::new(ptr as _)) + } + + #[cfg(not(feature = "send"))] + pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + where + for<'a> T: FromParam<'a>, + R: IntoParam + { + let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); + extern "C-unwind" fn _cfunc R>(l: State) -> i32 + where + for<'a> T: FromParam<'a>, + R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; From 42942e7511b0cc73a0cf9ae6db050237032dcfeb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 11:19:05 +0200 Subject: [PATCH 385/527] Added new UnsafeRootVm and 2 versions of RootVm: SendRootVm and UnSendRootVm to allow using !Send closures even with the send feature enabled. --- core/src/libs/mod.rs | 2 +- core/src/vm/closure/types.rs | 2 +- core/src/vm/core/destructor.rs | 82 +++++++++++++++++-- core/src/vm/core/mod.rs | 4 +- .../vm/core/{root_vm.rs => root_vm/common.rs} | 51 +++--------- core/src/vm/core/root_vm/mod.rs | 49 +++++++++++ core/src/vm/core/root_vm/send.rs | 61 ++++++++++++++ core/src/vm/core/root_vm/unsend.rs | 59 +++++++++++++ 8 files changed, 258 insertions(+), 52 deletions(-) rename core/src/vm/core/{root_vm.rs => root_vm/common.rs} (77%) create mode 100644 core/src/vm/core/root_vm/mod.rs create mode 100644 core/src/vm/core/root_vm/send.rs create mode 100644 core/src/vm/core/root_vm/unsend.rs diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index 9c10592..d173b72 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -41,7 +41,7 @@ pub mod os; #[cfg(feature = "libs-core")] mod interface; //TODO: maybe add a stack debug function which prints the content of the lua stack -//TODO: os lib with basic function (mainly time and performance management) and threading (sandbox with max number of threads) +//TODO: threading (sandbox with max number of threads) // make sure thread join is time-limited. #[cfg(feature = "libs-core")] diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 152a442..30d4b04 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -68,7 +68,7 @@ impl RClosure> { R: IntoParam, F: Send { - let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); + let ptr = crate::vm::core::destructor::Pool::attach_send(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 1e95b50..3021567 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -29,11 +29,26 @@ use crate::vm::registry::named::Key; use crate::vm::Vm; use bp3d_debug::debug; -use std::rc::Rc; +use std::sync::Arc; use crate::vm::registry::types::LuaRef; use crate::vm::value::types::RawPtr; use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; +/// This trait represents a value which can be attached to a [Pool](Pool). +pub trait RawSend: Send { + type Ptr: Copy; + + fn into_raw(self) -> Self::Ptr; + + /// Deletes the raw pointer. + /// + /// # Safety + /// + /// This function must be called with the same pointer that originated from the same type using + /// the [into_raw](Raw::into_raw) method. + unsafe fn delete(ptr: Self::Ptr); +} + /// This trait represents a value which can be attached to a [Pool](Pool). pub trait Raw { type Ptr: Copy; @@ -49,6 +64,18 @@ pub trait Raw { unsafe fn delete(ptr: Self::Ptr); } +impl RawSend for T { + type Ptr = T::Ptr; + + fn into_raw(self) -> Self::Ptr { + T::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + T::delete(ptr) + } +} + impl Raw for Box { type Ptr = *mut T; @@ -61,28 +88,43 @@ impl Raw for Box { } } -impl Raw for Rc { +impl Raw for std::rc::Rc { type Ptr = *const T; fn into_raw(self) -> Self::Ptr { - Rc::into_raw(self) + std::rc::Rc::into_raw(self) } unsafe fn delete(ptr: Self::Ptr) { - drop(Rc::from_raw(ptr)) + drop(std::rc::Rc::from_raw(ptr)) + } +} + +impl RawSend for Arc { + type Ptr = *const T; + + fn into_raw(self) -> Self::Ptr { + Arc::into_raw(self) + } + + unsafe fn delete(ptr: Self::Ptr) { + drop(Arc::from_raw(ptr)) } } static DESTRUCTOR_POOL: Key>> = Key::new("__destructor_pool__"); -#[derive(Default)] pub struct Pool { leaked: Vec>, + is_send: bool } impl Pool { - pub fn new() -> Self { - Self::default() + pub fn new(is_send: bool) -> Self { + Self { + leaked: Vec::new(), + is_send + } } /// Inserts this pool in the given Vm. @@ -90,8 +132,8 @@ impl Pool { /// # Safety /// /// This is only safe to be called on [RootVm](crate::vm::RootVm) construction. - pub unsafe fn new_in_vm(vm: &mut Vm) { - let b = Box::leak(Box::new(Pool::new())); + pub unsafe fn new_in_vm(vm: &mut Vm, is_send: bool) { + let b = Box::leak(Box::new(Pool::new(is_send))); let ptr = RawPtr::new(b as *mut Pool); DESTRUCTOR_POOL.set(LiveLuaRef::new(vm, ptr)); } @@ -110,6 +152,14 @@ impl Pool { unsafe { &mut *Self::_from_vm(vm).as_mut_ptr() } } + pub fn attach_send(vm: &Vm, raw: R) -> R::Ptr + where + R::Ptr: 'static, + { + let ptr = unsafe { Self::_from_vm(vm) }; + unsafe { (*ptr.as_mut_ptr()).attach_mut_send(raw) } + } + pub fn attach(vm: &Vm, raw: R) -> R::Ptr where R::Ptr: 'static, @@ -118,10 +168,24 @@ impl Pool { unsafe { (*ptr.as_mut_ptr()).attach_mut(raw) } } + pub fn attach_mut_send(&mut self, raw: R) -> R::Ptr + where + R::Ptr: 'static, + { + let ptr = R::into_raw(raw); + self.leaked.push(Box::new(move || { + unsafe { R::delete(ptr) }; + })); + ptr + } + pub fn attach_mut(&mut self, raw: R) -> R::Ptr where R::Ptr: 'static, { + if self.is_send { + panic!("Attempt to attach !Send type to Send destructor Pool: this is forbidden!") + } let ptr = R::into_raw(raw); self.leaked.push(Box::new(move || { unsafe { R::delete(ptr) }; diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index cf328a0..c00420a 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -42,6 +42,8 @@ pub mod jit; pub mod interrupt; pub use interface::*; + #[cfg(feature = "root-vm")] -pub use root_vm::RootVm; +pub use root_vm::*; + pub use vm::Vm; diff --git a/core/src/vm/core/root_vm.rs b/core/src/vm/core/root_vm/common.rs similarity index 77% rename from core/src/vm/core/root_vm.rs rename to core/src/vm/core/root_vm/common.rs index 55fca5c..cf12241 100644 --- a/core/src/vm/core/root_vm.rs +++ b/core/src/vm/core/root_vm/common.rs @@ -26,13 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::lua_close; use crate::vm::core::destructor::Pool; -use crate::vm::Vm; -use bp3d_debug::debug; -use std::ops::{Deref, DerefMut}; use crate::vm::registry::named::{handle_root_vm_init, handle_root_vm_uninit}; +use crate::vm::Vm; #[cfg(not(feature = "send"))] thread_local! { @@ -40,21 +39,11 @@ thread_local! { static HAS_VM: std::cell::Cell = const { std::cell::Cell::new(false) }; } -pub struct RootVm { - vm: Vm -} - -#[cfg(feature = "send")] -unsafe impl Send for RootVm { } - -impl Default for RootVm { - fn default() -> Self { - Self::new() - } -} +#[repr(transparent)] +pub struct UnsafeRootVm(pub Vm); -impl RootVm { - pub fn new() -> RootVm { +impl UnsafeRootVm { + pub fn new(is_send: bool) -> UnsafeRootVm { #[cfg(not(feature = "send"))] if HAS_VM.get() { panic!("A VM already exists for this thread.") @@ -63,41 +52,23 @@ impl RootVm { unsafe { luaL_openlibs(l) }; #[cfg(not(feature = "send"))] HAS_VM.set(true); - let mut vm = RootVm { - vm: unsafe { Vm::from_raw(l) } - }; + let mut vm = UnsafeRootVm(unsafe { Vm::from_raw(l) }); handle_root_vm_init(); - unsafe { Pool::new_in_vm(&mut vm) }; + unsafe { Pool::new_in_vm(&mut vm.0, is_send) }; vm } } -impl Deref for RootVm { - type Target = Vm; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.vm - } -} - -impl DerefMut for RootVm { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.vm - } -} - -impl Drop for RootVm { +impl Drop for UnsafeRootVm { fn drop(&mut self) { debug!("Deleting destructor pool"); unsafe { - drop(Box::from_raw(Pool::from_vm(self))); + drop(Box::from_raw(Pool::from_vm(&mut self.0))); } handle_root_vm_uninit(); unsafe { debug!("Closing Lua VM..."); - lua_close(self.vm.as_ptr()); + lua_close(self.0.as_ptr()); } #[cfg(not(feature = "send"))] HAS_VM.set(false); diff --git a/core/src/vm/core/root_vm/mod.rs b/core/src/vm/core/root_vm/mod.rs new file mode 100644 index 0000000..88ffef9 --- /dev/null +++ b/core/src/vm/core/root_vm/mod.rs @@ -0,0 +1,49 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod common; + +#[cfg(feature = "send")] +mod send; + +mod unsend; + +// --> Static type aliases <-- + +pub type UnSendRootVm = unsend::RootVm; + +#[cfg(feature = "send")] +pub type SendRootVm = send::RootVm; + +// --> Automatic type aliases <-- + +#[cfg(not(feature = "send"))] +pub type RootVm = unsend::RootVm; + +#[cfg(feature = "send")] +pub type RootVm = send::RootVm; diff --git a/core/src/vm/core/root_vm/send.rs b/core/src/vm/core/root_vm/send.rs new file mode 100644 index 0000000..f366d42 --- /dev/null +++ b/core/src/vm/core/root_vm/send.rs @@ -0,0 +1,61 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ops::{Deref, DerefMut}; +use crate::vm::core::root_vm::common::UnsafeRootVm; +use crate::vm::Vm; + +pub struct RootVm { + vm: UnsafeRootVm +} + +impl RootVm { + pub fn new() -> RootVm { + RootVm { + vm: UnsafeRootVm::new(true) + } + } +} + +unsafe impl Send for RootVm { } + +impl Deref for RootVm { + type Target = Vm; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.vm.0 + } +} + +impl DerefMut for RootVm { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vm.0 + } +} diff --git a/core/src/vm/core/root_vm/unsend.rs b/core/src/vm/core/root_vm/unsend.rs new file mode 100644 index 0000000..f658432 --- /dev/null +++ b/core/src/vm/core/root_vm/unsend.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::ops::{Deref, DerefMut}; +use crate::vm::core::root_vm::common::UnsafeRootVm; +use crate::vm::Vm; + +pub struct RootVm { + vm: UnsafeRootVm +} + +impl RootVm { + pub fn new() -> RootVm { + RootVm { + vm: UnsafeRootVm::new(false) + } + } +} + +impl Deref for RootVm { + type Target = Vm; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.vm.0 + } +} + +impl DerefMut for RootVm { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.vm.0 + } +} From 2071a47d949cf13426262aa5327c671332472ef6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 11:21:16 +0200 Subject: [PATCH 386/527] Added closure Arc which is compatible with both Send RootVm and !Send RootVm --- core/src/vm/closure/arc.rs | 77 +++++++++++++++++++++++++++++++++++++ core/src/vm/closure/core.rs | 2 - core/src/vm/closure/mod.rs | 1 + core/src/vm/closure/rc.rs | 4 +- 4 files changed, 81 insertions(+), 3 deletions(-) create mode 100644 core/src/vm/closure/arc.rs diff --git a/core/src/vm/closure/arc.rs b/core/src/vm/closure/arc.rs new file mode 100644 index 0000000..dc3eb50 --- /dev/null +++ b/core/src/vm/closure/arc.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::util::core::SimpleDrop; +use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::Vm; +use std::ops::Deref; +use crate::vm::value::types::RawPtr; + +pub type Shared = std::sync::Arc; + +#[repr(transparent)] +pub struct Arc(*const T); + +#[repr(transparent)] +pub struct Ref<'a, T: Send + Sync>(&'a T); + +unsafe impl SimpleDrop for Ref<'_, T> {} + +impl Deref for Ref<'_, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} + +impl<'a, T: Send + Sync> FromUpvalue<'a> for Ref<'a, T> { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + let ptr: RawPtr = FromUpvalue::from_upvalue(vm, index); + Ref(&*ptr.as_ptr()) + } +} + +impl Upvalue for Arc { + type From<'a> = crate::vm::closure::rc::Ref<'a, T>; +} + +impl IntoUpvalue for Arc { + #[inline(always)] + fn into_upvalue(self, vm: &Vm) -> u16 { + RawPtr::new(self.0 as *mut T).into_upvalue(vm) + } +} + +impl Arc { + #[inline(always)] + pub fn from_rust(vm: &Vm, rc: Shared) -> Arc { + Arc(crate::vm::core::destructor::Pool::attach_send(vm, rc)) + } +} diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index a3a6502..9958c4d 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -79,8 +79,6 @@ impl_from_upvalue_using_from_lua_unchecked!(i64, u64); impl_from_upvalue_using_from_lua_unchecked!(i8, u8, i16, u16, i32, u32, f32, f64, bool); -// Wrap this in a new RawPtr type... - impl FromUpvalue<'_> for RawPtr { #[inline(always)] unsafe fn from_upvalue(vm: &Vm, index: i32) -> Self { diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 1d3cb24..71ddab8 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -31,5 +31,6 @@ mod core; mod interface; pub mod rc; pub mod types; +pub mod arc; pub use interface::*; diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 7b166ad..8e692af 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -32,6 +32,8 @@ use crate::vm::Vm; use std::ops::Deref; use crate::vm::value::types::RawPtr; +pub type Shared = std::rc::Rc; + #[repr(transparent)] pub struct Rc(*const T); @@ -69,7 +71,7 @@ impl IntoUpvalue for Rc { impl Rc { #[inline(always)] - pub fn from_rust(vm: &Vm, rc: std::rc::Rc) -> Rc { + pub fn from_rust(vm: &Vm, rc: Shared) -> Rc { Rc(crate::vm::core::destructor::Pool::attach(vm, rc)) } } From e6ad6c32105bd03407ecee225fd4bf067c02b176 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 11:23:27 +0200 Subject: [PATCH 387/527] Updated lua base lib to use new Arc closure type rather than Rc to allow use of the lib with both types of RootVm --- core/src/libs/lua/options.rs | 5 +++-- core/src/libs/lua/require.rs | 18 +++++++++--------- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index cd7446f..2e3cd5e 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -32,11 +32,12 @@ use crate::libs::lua::load::Load; use crate::libs::lua::require::{Provider, Require}; use crate::libs::Lib; use std::path::Path; +use crate::vm::closure::arc::Shared; #[derive(Default)] pub struct Lua<'a> { pub(super) load_chroot_path: Option<&'a Path>, - pub(super) provider: Option>, + pub(super) provider: Option>, } impl<'a> Lua<'a> { @@ -49,7 +50,7 @@ impl<'a> Lua<'a> { self } - pub fn provider(mut self, provider: std::rc::Rc) -> Self { + pub fn provider(mut self, provider: Shared) -> Self { self.provider = Some(provider); self } diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 475f6bb..ce17537 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -29,12 +29,12 @@ use crate::decl_closure; use crate::libs::interface::Lib; use crate::util::Namespace; -use crate::vm::closure::rc::Rc; +use crate::vm::closure::arc::{Arc, Shared}; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; use bp3d_util::simple_error; -use std::cell::RefCell; use std::collections::HashMap; +use std::sync::Mutex; simple_error! { pub Error { @@ -44,12 +44,12 @@ simple_error! { } } -pub trait Source { +pub trait Source: Send + Sync { fn run(&self, vm: &Vm, path: &str) -> crate::vm::Result; } #[derive(Default)] -pub struct Provider(RefCell>>); +pub struct Provider(Mutex>>); impl Provider { pub fn new() -> Self { @@ -57,14 +57,14 @@ impl Provider { } pub fn add_source(&self, name: String, source: S) { - let mut guard = self.0.borrow_mut(); + let mut guard = self.0.lock().unwrap(); guard.insert(name, Box::new(source)); } pub fn require(&self, vm: &Vm, path: &str) -> Result { let id = path.find('.').ok_or(Error::InvalidSyntax)?; let source = &path[..id]; - let guard = self.0.borrow(); + let guard = self.0.lock().unwrap(); let src = guard .get(source) .ok_or_else(|| Error::UnknownSource(source.into()))?; @@ -74,14 +74,14 @@ impl Provider { } decl_closure! { - fn require |provider: Rc| (vm: &Vm, path: &str) -> Result { + fn require |provider: Arc| (vm: &Vm, path: &str) -> Result { let top = vm.top(); provider.require(vm, path)?; unsafe { Ok(UncheckedAnyReturn::new(vm, (vm.top() - top) as _)) } } } -pub struct Require(pub std::rc::Rc); +pub struct Require(pub Shared); impl Lib for Require { const NAMESPACE: &'static str = "bp3d.lua"; @@ -91,7 +91,7 @@ impl Lib for Require { } fn register(&self, vm: &Vm) -> crate::vm::Result<()> { - let rc = Rc::from_rust(vm, self.0.clone()); + let rc = Arc::from_rust(vm, self.0.clone()); let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([("require", require(rc))]) } From 74a2b3ca0b73a96385341df7c55e47e685618842 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 11:24:12 +0200 Subject: [PATCH 388/527] Use UnSendRootVm in test_vm_threads to avoid use of atomics --- core/tests/test_vm_threads.rs | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index 383c653..74f2cbc 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -28,9 +28,9 @@ use std::cell::Cell; use bp3d_lua::{decl_closure, decl_lib_func}; -use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::closure::rc::{Rc, Shared}; +use bp3d_lua::vm::core::UnSendRootVm; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::thread::core::{State, Yield}; use bp3d_lua::vm::thread::value::Value; @@ -42,9 +42,9 @@ decl_closure! { #[test] fn test_threads_yield_lua() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - let obj = std::rc::Rc::new(Cell::new(0)); + let obj = Shared::new(Cell::new(0)); vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.run_code::<()>(c" CO = coroutine.create(function() @@ -81,7 +81,7 @@ decl_lib_func! { #[test] fn test_threads_yield_rust_fail() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); let res = vm.run_code::<()>(c"my_yield()").unwrap_err().into_runtime().unwrap(); @@ -90,9 +90,9 @@ fn test_threads_yield_rust_fail() { #[test] fn test_threads_yield_rust() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - let obj = std::rc::Rc::new(Cell::new(0)); + let obj = Shared::new(Cell::new(0)); vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); vm.run_code::<()>(c" @@ -118,9 +118,9 @@ fn test_threads_yield_rust() { #[test] fn test_threads_with_yield_value_lua() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - let obj = std::rc::Rc::new(Cell::new(0)); + let obj = Shared::new(Cell::new(0)); vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.run_code::<()>(c" CO = coroutine.create(function() @@ -139,9 +139,9 @@ fn test_threads_with_yield_value_lua() { #[test] fn test_threads_with_yield_value_rust() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - let obj = std::rc::Rc::new(Cell::new(0)); + let obj = Shared::new(Cell::new(0)); vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.set_global(c"my_yield", RFunction::wrap(my_yield2)).unwrap(); vm.run_code::<()>(c" @@ -161,9 +161,9 @@ fn test_threads_with_yield_value_rust() { #[test] fn test_threads_with_yield_value_unsafe() { - let vm = RootVm::new(); + let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - let obj = std::rc::Rc::new(Cell::new(0)); + let obj = Shared::new(Cell::new(0)); vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.run_code::<()>(c" CO = coroutine.create(function() From e2c98b6f73066dcfd75bdff2e3144a0cfa13ddda Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 7 Aug 2025 11:24:42 +0200 Subject: [PATCH 389/527] Updated shell to new simplified Shared shortcut --- shell/core/src/autocomplete.rs | 4 ++-- shell/core/src/scheduler_api.rs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 9fcbb6a..deaf147 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -30,7 +30,7 @@ use std::collections::HashSet; use bp3d_lua::decl_closure; use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; -use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::closure::rc::{Rc, Shared}; use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::any::AnyValue; use crate::data::DataOut; @@ -144,7 +144,7 @@ decl_closure! { } } -pub struct Autocomplete(std::rc::Rc); +pub struct Autocomplete(Shared); impl Autocomplete { pub fn new(logger: DataOut) -> Autocomplete { diff --git a/shell/core/src/scheduler_api.rs b/shell/core/src/scheduler_api.rs index 773c3cc..e20edd0 100644 --- a/shell/core/src/scheduler_api.rs +++ b/shell/core/src/scheduler_api.rs @@ -29,7 +29,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; -use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::closure::rc::{Rc, Shared}; use bp3d_lua::vm::thread::value::Value; use crate::scheduler::SchedulerPtr; @@ -45,10 +45,10 @@ decl_closure! { } } -pub struct SchedulerApi(std::rc::Rc); +pub struct SchedulerApi(Shared); impl SchedulerApi { - pub fn new(ptr: std::rc::Rc) -> Self { + pub fn new(ptr: Shared) -> Self { Self(ptr) } } From 64b9c2f2b52a4d7195753ca226597b754de62ec2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 8 Aug 2025 00:01:16 +0200 Subject: [PATCH 390/527] Added test for destructor safety --- core/tests/test_multi_root_vms.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/core/tests/test_multi_root_vms.rs b/core/tests/test_multi_root_vms.rs index 24b11b0..ec41634 100644 --- a/core/tests/test_multi_root_vms.rs +++ b/core/tests/test_multi_root_vms.rs @@ -28,6 +28,7 @@ #![cfg(feature = "send")] +use bp3d_lua::vm::core::destructor::Pool; use bp3d_lua::vm::registry::core::Key; use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::types::Function; @@ -126,3 +127,21 @@ fn test_multi_root_vms_panic_4() { let root2 = RootVm::new(); key.delete(&root2); } + +#[test] +#[should_panic] +fn test_multi_root_vms_not_send_destructor() { + let root1 = RootVm::new(); + Pool::attach(&root1, Box::new(())); +} + +#[test] +fn test_multi_root_vms_send_destructor() { + let root1 = RootVm::new(); + Pool::attach_send(&root1, Box::new(())); +} + +/*#[test] +fn test_multi_root_vms_not_send_build_error() { + todo!() +}*/ \ No newline at end of file From 39602d053563cdc23fa3cc8e6ef05c80d00e5984 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 8 Aug 2025 16:02:14 +0200 Subject: [PATCH 391/527] Improved LuaRef registry system to work with reference types and added tests arround it --- core/src/macro/mod.rs | 11 +++ core/src/vm/registry/lua_ref.rs | 74 +++++++++++++++---- core/tests/test_vm_functions.rs | 28 +------- core/tests/test_vm_registry.rs | 124 ++++++++++++++++++++++++++++++++ 4 files changed, 195 insertions(+), 42 deletions(-) create mode 100644 core/tests/test_vm_registry.rs diff --git a/core/src/macro/mod.rs b/core/src/macro/mod.rs index 686f930..0de5962 100644 --- a/core/src/macro/mod.rs +++ b/core/src/macro/mod.rs @@ -38,6 +38,17 @@ macro_rules! c_stringify { }; } +#[macro_export] +macro_rules! impl_simple_registry_value_static { + ($($(<$($generics: ident)*>)? ($t: ty) => $v: ty;)*) => { + $( + impl $(<$($generics),*>)? $crate::vm::registry::lua_ref::SimpleRegistryValue for $t { + type Value<'a> = $v; + } + )* + }; +} + /// This macro is unsafe and should not be used from safe code directly. It is intended as a /// building block for other macros. #[macro_export] diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 94d4e9d..f0c0250 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -28,19 +28,31 @@ use std::marker::PhantomData; use crate::ffi::lua::{lua_replace, lua_settop}; +use crate::impl_simple_registry_value_static; use crate::vm::registry::{FromIndex, Set}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::types::RawPtr; use crate::vm::Vm; -/// Represents a simple registry value. +/// Represents a simple value type which can be manipulated by [LuaRef]. +/// +/// # Notes +/// +/// * The definition of a simple type in bp3d-lua is a type which does not hold a reference to +/// the [Vm]. This is typically the case of primitives like strings, integers, numbers, etc. Types +/// such as tables or functions are called complex in bp3d-lua as they require constant interactions +/// with the Lua stack represented by a [Vm] in order to operate on them. +/// +/// * For complex types, no wrapper is needed as they already have a reference to the attached [Vm] +/// in their value type. Instead, complex types which can be saved in the registry directly +/// implements the registry [Value](crate::vm::registry::Value) trait. /// /// # Safety /// -/// This trait always assumes a single lua value is managed. If the underlying type manipulates -/// multiple stack indices on the given lua [Vm], the implementation is considered UB. -pub unsafe trait SimpleRegistryValue { - fn into_lua(self, vm: &Vm); +/// This trait always assumes a single lua value is managed. If the underlying [SimpleValue] +/// manipulates multiple stack indices on the given lua [Vm], the implementation is considered UB. +pub unsafe trait SimpleValue<'a> { + fn into_lua(self, vm: &'a Vm); /// Extracts an instance of `Self` from the given Lua [Vm] and index. /// @@ -56,7 +68,13 @@ pub unsafe trait SimpleRegistryValue { /// This function assumes the given index is valid for [Vm] and that the object on the stack at /// the given index is already of type `Self`. If any of these assumptions are broken, this /// function is UB. - unsafe fn from_lua(vm: &Vm, index: i32) -> Self; + unsafe fn from_lua(vm: &'a Vm, index: i32) -> Self; +} + +/// Marks a type as being compatible with the [LuaRef](crate::vm::registry::types::LuaRef) based +/// registry system for simple types. +pub trait SimpleRegistryValue { + type Value<'a>: SimpleValue<'a>; } pub struct LuaRef<'a, T> { @@ -65,7 +83,7 @@ pub struct LuaRef<'a, T> { useless: PhantomData, } -impl<'a, T: SimpleRegistryValue> LuaRef<'a, T> { +impl<'a, T: SimpleValue<'a>> LuaRef<'a, T> { pub fn new(vm: &'a Vm, value: T) -> Self { value.into_lua(vm); Self { @@ -117,34 +135,42 @@ impl<'a, T> Drop for LuaRef<'a, T> { } impl super::Value for super::types::LuaRef { - type Value<'a> = LuaRef<'a, T>; + type Value<'a> = LuaRef<'a, T::Value<'a>>; unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { LuaRef::from_raw(vm, vm.get_absolute_index(index)) } fn push_registry(value: Self::Value<'_>) -> R { - unsafe { R::from_index(value.vm, value.index) } + unsafe { + let r = R::from_index(value.vm, value.index); + // Avoid calling the destructor which may try to pop an already popped value. + std::mem::forget(value); + r + } } unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { key.set(value.vm, value.index); + // Avoid calling the destructor which may try to pop an already popped value. + std::mem::forget(value); } } -unsafe impl SimpleRegistryValue for T where for<'a> T: FromLua<'a> + IntoLua { +unsafe impl<'a, T> SimpleValue<'a> for T where T: FromLua<'a> + IntoLua { #[inline(always)] - fn into_lua(self, vm: &Vm) { - IntoLua::into_lua(self, vm); + fn into_lua(self, vm: &'a Vm) { + // This ensures the safety guarentee still holds. + assert_eq!(IntoLua::into_lua(self, vm), 1); } #[inline(always)] - unsafe fn from_lua(vm: &Vm, index: i32) -> Self { - unsafe { FromLua::from_lua_unchecked(vm, index) } + unsafe fn from_lua(vm: &'a Vm, index: i32) -> Self { + T::from_lua_unchecked(vm, index) } } -unsafe impl SimpleRegistryValue for RawPtr { +unsafe impl SimpleValue<'_> for RawPtr { #[inline(always)] fn into_lua(self, vm: &Vm) { IntoLua::into_lua(self, vm); @@ -155,3 +181,21 @@ unsafe impl SimpleRegistryValue for RawPtr { unsafe { RawPtr::from_lua(vm, index) } } } + +impl_simple_registry_value_static! { + (RawPtr) => RawPtr; + (&str) => &'a str; + (f32) => f32; + (f64) => f64; + (i8) => i8; + (i16) => i16; + (i32) => i32; + (i64) => i64; + (u8) => u8; + (u16) => u16; + (u32) => u32; + (u64) => u64; + (String) => String; + (&[u8]) => &'a [u8]; + (Vec) => Vec; +} diff --git a/core/tests/test_vm_functions.rs b/core/tests/test_vm_functions.rs index f3ab19f..9a26245 100644 --- a/core/tests/test_vm_functions.rs +++ b/core/tests/test_vm_functions.rs @@ -26,13 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![cfg(all(feature = "root-vm", feature = "util-method"))] +#![cfg(feature = "root-vm")] -use bp3d_lua::util::LuaMethod; -use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::types::Function; use bp3d_lua::vm::RootVm; -use std::ffi::CStr; #[test] fn test_vm_function_1_arg() { @@ -61,26 +58,3 @@ fn test_vm_function_2_args() { assert_eq!(vm.top(), top + 3); // Function + 2 results vm.clear(); } - -const METHODS: &CStr = c" -local obj = { ctx = 'this is a test' } - -function obj:greeting() - return 'Hello ' .. self.ctx -end - -return obj -"; - -#[test] -fn test_vm_function_method() { - let mut vm = RootVm::new(); - let top = vm.top(); - let obj: Table = vm.run_code(METHODS).unwrap(); - let method = LuaMethod::create(obj, c"greeting").unwrap(); - let str: &str = method.call(&vm, ()).unwrap(); - assert_eq!(str, "Hello this is a test"); - method.delete(&vm); - assert_eq!(vm.top(), top + 1); // 1 result - vm.clear(); -} diff --git a/core/tests/test_vm_registry.rs b/core/tests/test_vm_registry.rs new file mode 100644 index 0000000..c78baa4 --- /dev/null +++ b/core/tests/test_vm_registry.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#![cfg(all(feature = "root-vm", feature = "util-method", feature = "util-function"))] + +use std::ffi::CStr; +use bp3d_lua::util::{LuaFunction, LuaMethod}; +use bp3d_lua::vm::registry::core::Key; +use bp3d_lua::vm::registry::lua_ref::LuaRef as LiveLuaRef; +use bp3d_lua::vm::registry::types::LuaRef; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::table::Table; +use bp3d_lua::vm::value::types::Function; + +const METHODS: &CStr = c" +local obj = { ctx = 'this is a test' } + +function obj:greeting() + return 'Hello ' .. self.ctx +end + +return obj +"; + +#[test] +fn test_vm_registry_method() { + let mut vm = RootVm::new(); + let top = vm.top(); + let obj: Table = vm.run_code(METHODS).unwrap(); + let method = LuaMethod::create(obj, c"greeting").unwrap(); + let str: &str = method.call(&vm, ()).unwrap(); + assert_eq!(str, "Hello this is a test"); + method.delete(&vm); + assert_eq!(vm.top(), top + 1); // 1 result + vm.clear(); +} + +#[test] +fn test_vm_registry_function() { + let vm = RootVm::new(); + let top = vm.top(); + let obj: Function = vm.run_code(c"return function() return 'Hello world' end").unwrap(); + let f = LuaFunction::create(obj); + assert_eq!(vm.top(), top); // The function should have been popped from the stack following the + // call to LuaFunction + let str: &str = f.call(&vm, ()).unwrap(); + assert_eq!(str, "Hello world"); + assert_eq!(vm.top(), top + 1); // 1 result +} + +#[test] +fn test_vm_registry_string() { + let vm = RootVm::new(); + let top = vm.top(); + let r = LiveLuaRef::new(&vm, "this is a test"); + let key: Key> = Key::new(r); + assert_eq!(vm.top(), top); // The string should have been popped from the stack like any normal + // registry creation operation. + { + let value = key.push(&vm).get(); + assert_eq!(value, "this is a test"); + } + // LuaRef automatically pops from the stack on drop, as simple values which are stored in the + // registry cannot be de-allocated by luajit. + assert_eq!(vm.top(), top); +} + +#[test] +fn test_vm_registry_string_modify() { + let vm = RootVm::new(); + let top = vm.top(); + let r = LiveLuaRef::new(&vm, "this is a test"); + let key: Key> = Key::new(r); + assert_eq!(vm.top(), top); + let value = key.push(&vm); + assert_eq!(value.get(), "this is a test"); + value.set("one more test"); + assert_eq!(value.get(), "one more test"); + key.set(value); + assert_eq!(vm.top(), top); + { + let value = key.push(&vm).get(); + assert_eq!(value, "one more test"); + } + assert_eq!(vm.top(), top); +} + +#[test] +fn test_vm_registry_integer() { + let vm = RootVm::new(); + let top = vm.top(); + let r = LiveLuaRef::new(&vm, 42); + let key: Key> = Key::new(r); + { + let value = key.push(&vm).get(); + assert_eq!(value, 42); + } + assert_eq!(vm.top(), top); +} From 3119bac8b4cb97d40b973ffbd769857b62ef61a4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 8 Aug 2025 16:15:48 +0200 Subject: [PATCH 392/527] Added safety checks to most complex types which cannot be easily transferred between multiple VM instances. --- core/src/vm/table/interface.rs | 1 + core/src/vm/thread/interface.rs | 1 + core/src/vm/value/function.rs | 2 +- 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 812cc04..9291f9d 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -79,6 +79,7 @@ unsafe impl IntoParam for Table<'_> { unsafe impl IntoLua for Table<'_> { fn into_lua(self, vm: &Vm) -> u16 { + assert!(self.vm.as_ptr() == vm.as_ptr()); let top = unsafe { lua_gettop(vm.as_ptr()) }; if top != self.index() { unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 2c97d16..7ade396 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -73,6 +73,7 @@ unsafe impl IntoParam for Value<'_> { unsafe impl IntoLua for Value<'_> { fn into_lua(self, vm: &Vm) -> u16 { + assert!(self.vm.as_ptr() == vm.as_ptr()); let top = unsafe { lua_gettop(vm.as_ptr()) }; if top != self.index() { unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 0b20608..e2a3760 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -102,8 +102,8 @@ unsafe impl IntoParam for Function<'_> { } unsafe impl IntoLua for Function<'_> { - #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { + assert!(self.vm.as_ptr() == vm.as_ptr()); unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; 1 } From 4ce62824afbe8e88e29daa0fc1e78d348aa89cbc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 8 Aug 2025 23:23:33 +0200 Subject: [PATCH 393/527] Added new set_function API to thread Value and renamed Yielded to Suspended state. --- core/src/module/error.rs | 1 + core/src/module/mod.rs | 3 ++- core/src/util/module.rs | 1 + core/src/vm/error.rs | 3 ++- core/src/vm/thread/core.rs | 4 ++-- core/src/vm/thread/value.rs | 18 +++++++++++++++++- core/tests/test_vm_threads.rs | 34 ++++++++++++++++++++++++++-------- 7 files changed, 51 insertions(+), 13 deletions(-) diff --git a/core/src/module/error.rs b/core/src/module/error.rs index 95742c0..50a4381 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -49,6 +49,7 @@ pub enum ErrorType { ParseFloat = -12, ParseInt = -13, UncatchableRuntime = -14, + BadThreadState = -15, UserDataArgsEmpty = 1, UserDataMutViolation = 2, UserDataGc = 3, diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index c93b143..a641f5a 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -124,7 +124,8 @@ pub fn run_lua_register( let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); - } + }, + Error::BadThreadState => error.ty = error::ErrorType::BadThreadState } false } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 45511db..80c694a 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -121,6 +121,7 @@ unsafe fn convert_module_error_to_vm_error( crate::vm::userdata::Error::Alignment(err.alignment.alignment), ), ErrorType::None => std::hint::unreachable_unchecked(), + ErrorType::BadThreadState => crate::vm::error::Error::BadThreadState } } diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index be8f5a4..fe342c5 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -149,7 +149,8 @@ simple_error! { UnsupportedType(Type) => "unsupported lua type: {:?}", Loader(String) => "loader error: {}", ParseFloat => "parse float error", - ParseInt => "parse int error" + ParseInt => "parse int error", + BadThreadState => "attempt to set the function of a thread in suspended or dead state" } } diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index 369c3c7..c97bc94 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -37,7 +37,7 @@ use crate::vm::Vm; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum State { - Yielded, + Suspended, Finished, } @@ -125,7 +125,7 @@ impl<'a> Thread<'a> { ThreadStatus::Yield => { let data = T::from_lua(&self.vm, top)?; Ok(Output { - state: State::Yielded, + state: State::Suspended, data }) }, diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index 559a6c7..4eb9560 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -27,10 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::fmt::{Debug, Display}; -use crate::ffi::lua::{lua_newthread, lua_pushvalue, lua_tothread}; +use crate::ffi::lua::{lua_gettop, lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type}; use crate::vm::thread::core::Thread; +use crate::vm::value::types::Function; +use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; +//TODO: Rename to Thread and find a better name for core::Thread + /// Represents a thread object value on a lua stack. pub struct Value<'a> { pub(super) vm: &'a Vm, @@ -101,6 +105,18 @@ impl<'a> Value<'a> { } } + pub fn set_function(&self, function: Function<'a>) -> crate::vm::Result<()> { + if self.thread.status() != ThreadStatus::Ok { + return Err(crate::vm::error::Error::BadThreadState); + } + ensure_value_top(self.vm, function.index()); + unsafe { lua_xmove(self.vm.as_ptr(), self.thread.as_ptr(), 1); } + unsafe { + assert_eq!(lua_type(self.thread.as_ptr(), -1), Type::Function); + }; + Ok(()) + } + /// Returns the absolute index of this table on the Lua stack. #[inline(always)] pub fn index(&self) -> i32 { diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index 74f2cbc..73fb448 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -33,6 +33,7 @@ use bp3d_lua::vm::core::UnSendRootVm; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::thread::core::{State, Yield}; use bp3d_lua::vm::thread::value::Value; +use bp3d_lua::vm::value::types::Function; decl_closure! { fn increment |val: Rc>| () -> () { @@ -41,7 +42,7 @@ decl_closure! { } #[test] -fn test_threads_yield_lua() { +fn test_vm_threads_yield_lua() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); @@ -57,7 +58,7 @@ fn test_threads_yield_lua() { ").unwrap(); let thread: Value = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Yielded); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); assert_eq!(obj.get(), 1); assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); assert_eq!(obj.get(), 2); @@ -80,7 +81,7 @@ decl_lib_func! { } #[test] -fn test_threads_yield_rust_fail() { +fn test_vm_threads_yield_rust_fail() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); @@ -89,7 +90,7 @@ fn test_threads_yield_rust_fail() { } #[test] -fn test_threads_yield_rust() { +fn test_vm_threads_yield_rust() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); @@ -106,7 +107,7 @@ fn test_threads_yield_rust() { ").unwrap(); let thread: Value = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Yielded); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); assert_eq!(obj.get(), 1); assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); assert_eq!(obj.get(), 2); @@ -117,7 +118,7 @@ fn test_threads_yield_rust() { } #[test] -fn test_threads_with_yield_value_lua() { +fn test_vm_threads_with_yield_value_lua() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); @@ -138,7 +139,7 @@ fn test_threads_with_yield_value_lua() { } #[test] -fn test_threads_with_yield_value_rust() { +fn test_vm_threads_with_yield_value_rust() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); @@ -160,7 +161,7 @@ fn test_threads_with_yield_value_rust() { } #[test] -fn test_threads_with_yield_value_unsafe() { +fn test_vm_threads_with_yield_value_unsafe() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); @@ -198,3 +199,20 @@ fn test_threads_with_yield_value_unsafe() { assert_eq!(s3, "test3"); assert_eq!(obj.get(), 3); } + +#[test] +fn test_vm_threads_set_function() { + let vm = UnSendRootVm::new(); + let top = vm.top(); + assert!(vm.as_thread().is_none()); + let obj = Shared::new(Cell::new(0)); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); + vm.run_code::<()>(c"function ThreadMain() increment() coroutine.yield() increment() end").unwrap(); + let main_fn: Function = vm.get_global(c"ThreadMain").unwrap(); + let thread = Value::new(&vm); + thread.set_function(main_fn).unwrap(); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); + assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Finished); + assert_eq!(obj.get(), 2); + assert_eq!(vm.top() - top, 2) +} From 17dced60bd7acfd698d18ef24d3e6ff037df718c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 8 Aug 2025 23:23:57 +0200 Subject: [PATCH 394/527] Updated shell scheduler to renamed Suspended state --- shell/core/src/scheduler.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shell/core/src/scheduler.rs b/shell/core/src/scheduler.rs index edcc3d1..a265e0d 100644 --- a/shell/core/src/scheduler.rs +++ b/shell/core/src/scheduler.rs @@ -103,7 +103,7 @@ impl Scheduler { } }; if let Some(new_time_ms) = out.data { - if out.state == State::Yielded { + if out.state == State::Suspended { if task.period_ms.is_some() { task.period_ms = Some(new_time_ms); } @@ -117,7 +117,7 @@ impl Scheduler { } } if let Some(period_ms) = task.period_ms { - if out.state == State::Yielded { + if out.state == State::Suspended { task.at_ms = time + period_ms as u64; self.main.push(task); } else { From 19aea3977bab75811cf01c58ae030c9a57753759 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 10:18:16 +0200 Subject: [PATCH 395/527] Rename value::Value to value::Thread --- core/src/util/thread.rs | 2 +- core/src/vm/thread/interface.rs | 26 ++++++++++++------------- core/src/vm/thread/value.rs | 34 ++++++++++++++++----------------- core/src/vm/value/any.rs | 2 +- core/tests/test_vm_threads.rs | 14 +++++++------- shell/core/src/scheduler.rs | 10 +++++----- shell/core/src/scheduler_api.rs | 6 +++--- 7 files changed, 46 insertions(+), 48 deletions(-) diff --git a/core/src/util/thread.rs b/core/src/util/thread.rs index 954c21e..ac8ca47 100644 --- a/core/src/util/thread.rs +++ b/core/src/util/thread.rs @@ -35,7 +35,7 @@ pub struct LuaThread { } impl LuaThread { - pub fn create(value: crate::vm::thread::value::Value) -> Self { + pub fn create(value: crate::vm::thread::value::Thread) -> Self { let thread = unsafe { crate::vm::thread::core::Thread::from_raw(value.as_thread().as_ptr()) }; Self { key: Key::new(value), diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 7ade396..4adb265 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -31,47 +31,47 @@ use crate::ffi::lua::{lua_gettop, lua_pushvalue, Type}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; -use crate::vm::thread::value::Value; +use crate::vm::thread::value::Thread; use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::util::ensure_type_equals; use crate::vm::Vm; -impl<'a> FromLua<'a> for Value<'a> { +impl<'a> FromLua<'a> for Thread<'a> { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { - Value::from_raw(vm, vm.get_absolute_index(index)) + Thread::from_raw(vm, vm.get_absolute_index(index)) } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { ensure_type_equals(vm, index, Type::Thread)?; - unsafe { Ok(Value::from_raw(vm, vm.get_absolute_index(index))) } + unsafe { Ok(Thread::from_raw(vm, vm.get_absolute_index(index))) } } } -unsafe impl SimpleDrop for Value<'_> {} +unsafe impl SimpleDrop for Thread<'_> {} -impl LuaType for Value<'_> {} +impl LuaType for Thread<'_> {} -impl<'a> FromParam<'a> for Value<'a> { +impl<'a> FromParam<'a> for Thread<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { luaL_checktype(vm.as_ptr(), index, Type::Thread); - Value::from_raw(vm, vm.get_absolute_index(index)) + Thread::from_raw(vm, vm.get_absolute_index(index)) } fn try_from_param(vm: &'a Vm, index: i32) -> Option { - Value::from_lua(vm, index).ok() + Thread::from_lua(vm, index).ok() } } -unsafe impl IntoParam for Value<'_> { +unsafe impl IntoParam for Thread<'_> { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { IntoLua::into_lua(self, vm) as _ } } -unsafe impl IntoLua for Value<'_> { +unsafe impl IntoLua for Thread<'_> { fn into_lua(self, vm: &Vm) -> u16 { assert!(self.vm.as_ptr() == vm.as_ptr()); let top = unsafe { lua_gettop(vm.as_ptr()) }; @@ -83,10 +83,10 @@ unsafe impl IntoLua for Value<'_> { } impl crate::vm::registry::Value for crate::vm::registry::types::Thread { - type Value<'a> = Value<'a>; + type Value<'a> = Thread<'a>; unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { Value::from_lua_unchecked(vm, index) } + unsafe { Thread::from_lua_unchecked(vm, index) } } fn push_registry(value: Self::Value<'_>) -> R { diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index 4eb9560..a0a7168 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -27,53 +27,51 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::fmt::{Debug, Display}; -use crate::ffi::lua::{lua_gettop, lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type}; -use crate::vm::thread::core::Thread; +use crate::ffi::lua::{lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type}; +use crate::vm::thread::core; use crate::vm::value::types::Function; use crate::vm::value::util::ensure_value_top; use crate::vm::Vm; -//TODO: Rename to Thread and find a better name for core::Thread - /// Represents a thread object value on a lua stack. -pub struct Value<'a> { +pub struct Thread<'a> { pub(super) vm: &'a Vm, index: i32, - thread: Thread<'static> + thread: core::Thread<'static> } -impl Clone for Value<'_> { +impl Clone for Thread<'_> { fn clone(&self) -> Self { unsafe { lua_pushvalue(self.vm.as_ptr(), self.index) }; - Value { + Thread { vm: self.vm, index: self.vm.top(), - thread: unsafe { Thread::from_raw(self.thread.as_ptr()) } + thread: unsafe { core::Thread::from_raw(self.thread.as_ptr()) } } } } -impl PartialEq for Value<'_> { +impl PartialEq for Thread<'_> { fn eq(&self, other: &Self) -> bool { self.thread.eq(&other.thread) } } -impl Eq for Value<'_> {} +impl Eq for Thread<'_> {} -impl Display for Value<'_> { +impl Display for Thread<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "thread@{:X}", self.thread.uid()) } } -impl Debug for Value<'_> { +impl Debug for Thread<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { write!(f, "Thread({:?})", self.index) } } -impl<'a> Value<'a> { +impl<'a> Thread<'a> { /// Creates a thread value from a raw Vm and index on `vm` stack. /// /// # Arguments @@ -92,12 +90,12 @@ impl<'a> Value<'a> { Self { vm, index, - thread: Thread::from_raw(lua_tothread(vm.as_ptr(), index)) + thread: core::Thread::from_raw(lua_tothread(vm.as_ptr(), index)) } } pub fn new(vm: &'a Vm) -> Self { - let thread = unsafe { Thread::from_raw(lua_newthread(vm.as_ptr())) }; + let thread = unsafe { core::Thread::from_raw(lua_newthread(vm.as_ptr())) }; Self { vm, index: vm.top(), @@ -112,7 +110,7 @@ impl<'a> Value<'a> { ensure_value_top(self.vm, function.index()); unsafe { lua_xmove(self.vm.as_ptr(), self.thread.as_ptr(), 1); } unsafe { - assert_eq!(lua_type(self.thread.as_ptr(), -1), Type::Function); + assert_eq!(lua_type(self.thread.as_ptr(), -1), Type::Function); }; Ok(()) } @@ -125,7 +123,7 @@ impl<'a> Value<'a> { /// Returns the thread stack object attached to this thread value. #[inline(always)] - pub fn as_thread(&self) -> &Thread { + pub fn as_thread(&self) -> &core::Thread { &self.thread } } diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 5dbbe7e..88e1c98 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -31,7 +31,7 @@ use crate::util::core::SimpleDrop; use crate::vm::error::{Error, TypeError}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::table::Table; -use crate::vm::thread::value::Value as Thread; +use crate::vm::thread::value::Thread as Thread; use crate::vm::userdata::AnyUserData; use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::value::function::Function; diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index 73fb448..9d8f343 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -32,7 +32,7 @@ use bp3d_lua::vm::closure::rc::{Rc, Shared}; use bp3d_lua::vm::core::UnSendRootVm; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::thread::core::{State, Yield}; -use bp3d_lua::vm::thread::value::Value; +use bp3d_lua::vm::thread::value::Thread; use bp3d_lua::vm::value::types::Function; decl_closure! { @@ -56,7 +56,7 @@ fn test_vm_threads_yield_lua() { end end) ").unwrap(); - let thread: Value = vm.get_global(c"CO").unwrap(); + let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); assert_eq!(obj.get(), 1); @@ -105,7 +105,7 @@ fn test_vm_threads_yield_rust() { end end) ").unwrap(); - let thread: Value = vm.get_global(c"CO").unwrap(); + let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); assert_eq!(obj.get(), 1); @@ -131,7 +131,7 @@ fn test_vm_threads_with_yield_value_lua() { return 42 end) ").unwrap(); - let thread: Value = vm.get_global(c"CO").unwrap(); + let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 1); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 42); @@ -153,7 +153,7 @@ fn test_vm_threads_with_yield_value_rust() { return 42 end) ").unwrap(); - let thread: Value = vm.get_global(c"CO").unwrap(); + let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 5); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 42); @@ -177,7 +177,7 @@ fn test_vm_threads_with_yield_value_unsafe() { return \"test3\" end) ").unwrap(); - let thread: Value = vm.get_global(c"CO").unwrap(); + let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); let s: String = thread.as_thread().resume(()).unwrap().data; let s2: String = thread.as_thread().resume(()).unwrap().data; @@ -209,7 +209,7 @@ fn test_vm_threads_set_function() { vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); vm.run_code::<()>(c"function ThreadMain() increment() coroutine.yield() increment() end").unwrap(); let main_fn: Function = vm.get_global(c"ThreadMain").unwrap(); - let thread = Value::new(&vm); + let thread = Thread::new(&vm); thread.set_function(main_fn).unwrap(); assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Finished); diff --git a/shell/core/src/scheduler.rs b/shell/core/src/scheduler.rs index a265e0d..4b16669 100644 --- a/shell/core/src/scheduler.rs +++ b/shell/core/src/scheduler.rs @@ -33,7 +33,7 @@ use bp3d_debug::{error, warning}; use bp3d_os::time::Instant; use bp3d_lua::util::LuaThread; use bp3d_lua::vm::thread::core::State; -use bp3d_lua::vm::thread::value::Value; +use bp3d_lua::vm::thread::value::Thread; use bp3d_lua::vm::Vm; use crate::data::DataOut; use crate::data_out::Log; @@ -70,7 +70,7 @@ struct Scheduler { } impl Scheduler { - pub fn schedule_in(&mut self, value: Value, after_ms: u32) { + pub fn schedule_in(&mut self, value: Thread, after_ms: u32) { let task = Task { at_ms: self.instant.elapsed().as_millis() as u64 + after_ms as u64, period_ms: None, @@ -79,7 +79,7 @@ impl Scheduler { self.main.push(task); } - pub fn schedule_periodically(&mut self, value: Value, period_ms: u32) { + pub fn schedule_periodically(&mut self, value: Thread, period_ms: u32) { let task = Task { at_ms: self.instant.elapsed().as_millis() as u64 + period_ms as u64, period_ms: Some(period_ms), @@ -139,12 +139,12 @@ impl SchedulerPtr { Self(RefCell::new(Scheduler { main: BinaryHeap::new(), instant: Instant::now() })) } - pub fn schedule_in(&self, value: Value, after_ms: u32) { + pub fn schedule_in(&self, value: Thread, after_ms: u32) { let mut inner = self.0.borrow_mut(); inner.schedule_in(value, after_ms); } - pub fn schedule_periodically(&self, value: Value, period_ms: u32) { + pub fn schedule_periodically(&self, value: Thread, period_ms: u32) { let mut inner = self.0.borrow_mut(); inner.schedule_periodically(value, period_ms); } diff --git a/shell/core/src/scheduler_api.rs b/shell/core/src/scheduler_api.rs index e20edd0..bc259cb 100644 --- a/shell/core/src/scheduler_api.rs +++ b/shell/core/src/scheduler_api.rs @@ -30,17 +30,17 @@ use bp3d_lua::decl_closure; use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::rc::{Rc, Shared}; -use bp3d_lua::vm::thread::value::Value; +use bp3d_lua::vm::thread::value::Thread; use crate::scheduler::SchedulerPtr; decl_closure! { - fn schedule_in |scheduler: Rc| (thread: Value, after_ms: u32) -> () { + fn schedule_in |scheduler: Rc| (thread: Thread, after_ms: u32) -> () { scheduler.schedule_in(thread, after_ms); } } decl_closure! { - fn schedule_periodically |scheduler: Rc| (thread: Value, period_ms: u32) -> () { + fn schedule_periodically |scheduler: Rc| (thread: Thread, period_ms: u32) -> () { scheduler.schedule_periodically(thread, period_ms); } } From 810a9949338e5f84be51d19056e740b66475bbf8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 10:42:29 +0200 Subject: [PATCH 396/527] Added support to generate patch list dynamically at build-time --- build/src/patch.rs | 22 ++++++++++++++++++++-- core/build.rs | 14 ++++++++++---- core/src/libs/lua/base.rs | 8 +------- 3 files changed, 31 insertions(+), 13 deletions(-) diff --git a/build/src/patch.rs b/build/src/patch.rs index 84f9b68..6a76f2e 100644 --- a/build/src/patch.rs +++ b/build/src/patch.rs @@ -29,8 +29,26 @@ use crate::util::CommandRunner; use bp3d_os::fs::CopyOptions; use std::ffi::OsStr; +use std::fs::File; use std::path::{Path, PathBuf}; use std::process::Command; +use std::io::Write; + +pub struct Summary { + patches: Vec +} + +impl Summary { + pub fn write(&self, out: &Path) -> std::io::Result<()> { + let mut file = File::create(out)?; + writeln!(&mut file, "const PATCH_LIST: &[&str] = &[")?; + for patch in &self.patches { + writeln!(&mut file, " \"{}\",", patch)?; + } + writeln!(&mut file, "];")?; + Ok(()) + } +} pub struct Patch { src_path: PathBuf, @@ -75,7 +93,7 @@ impl Patch { mut self, patches: impl IntoIterator, out_path: &Path, - ) -> std::io::Result> { + ) -> std::io::Result { for patch in patches { self.apply(patch)?; } @@ -86,7 +104,7 @@ impl Patch { CopyOptions::new().exclude(OsStr::new(".git")), )?; } - Ok(self.get_patch_list().map(String::from).collect()) + Ok(Summary { patches: self.get_patch_list().map(String::from).collect() }) } } diff --git a/core/build.rs b/core/build.rs index bf561a8..98595dd 100644 --- a/core/build.rs +++ b/core/build.rs @@ -46,12 +46,18 @@ const PATCH_LIST: &[&str] = &[ // pcall/xpcall but only from lua_pcall C API. ]; -fn apply_patches(out_path: &Path) -> std::io::Result> { - Patch::new( +fn apply_patches(luajit_build_path: &Path, summary_path: &Path) -> std::io::Result<()> { + let summary = Patch::new( &Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"), )? - .apply_all(PATCH_LIST.iter().copied(), out_path) + .apply_all(PATCH_LIST.iter().copied(), luajit_build_path)?; + #[cfg(feature = "libs")] + { + summary.write(summary_path)?; + println!("cargo:rustc-env=BP3D_LUA_PATCH_SUMMARY_FILE={}", summary_path.display()); + } + Ok(()) } fn run_build(build_dir: &Path) -> std::io::Result { @@ -78,7 +84,7 @@ fn main() { let out_path = Path::new(&out).join("luajit-build"); // Apply patches to LuaJIT source code. - apply_patches(&out_path).expect("Failed to patch LuaJIT"); + apply_patches(&out_path, &Path::new(&out).join("patch_summary.rs")).expect("Failed to patch LuaJIT"); // Copy the source directory to the build directory. println!("Internal LuaJIT build directory: {}", out_path.display()); diff --git a/core/src/libs/lua/base.rs b/core/src/libs/lua/base.rs index dc83b8d..befb670 100644 --- a/core/src/libs/lua/base.rs +++ b/core/src/libs/lua/base.rs @@ -30,13 +30,7 @@ use crate::libs::Lib; use crate::util::Namespace; use crate::vm::table::Table; -const PATCH_LIST: &[&str] = &[ - "disable_lua_load", - "lib_init", - "lj_disable_jit", - "lua_ext", - "lua_load_no_bc", -]; +include!(env!("BP3D_LUA_PATCH_SUMMARY_FILE")); pub struct Base; From 053ae3e05027b0b927a3722f29da68e7acb64439 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 10:43:03 +0200 Subject: [PATCH 397/527] Refactored test_vm_libs to avoid depending on crate version. --- core/Cargo.toml | 2 +- core/tests/test_vm_libs.rs | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 5e18bde..7e98e0b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "bp3d-lua" -version = "1.0.0-rc.4.0.0" +version = "1.0.0-rc.5.0.0" authors = ["Yuri Edward "] edition = "2021" # Possible very-little performance improvement with 2024 (2.35 vs 2.37/2.4) description = "Lua wrapper and base library for BP3D." diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 6d8dc84..009c11c 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -26,11 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![cfg(all(feature = "root-vm", feature = "libs"))] +#![cfg(all(feature = "root-vm", feature = "libs", feature = "module"))] use bp3d_lua::libs::lua::{Lua, Module}; use bp3d_lua::libs::util::Util; use bp3d_lua::libs::Lib; +use bp3d_lua::module::VERSION; use bp3d_lua::vm::RootVm; #[test] @@ -39,12 +40,12 @@ fn test_vm_lib_lua() { let top = vm.top(); Lua::new().build().register(&vm).unwrap(); Module::new(&[]).register(&vm).unwrap(); - //FIXME: Find a way to write the version differently. + vm.set_global("BP3D_LUA_CRATE_VERSION", VERSION).unwrap(); vm.run_code::<()>( c" assert(bp3d.lua.name == 'bp3d-lua') - assert(bp3d.lua.version == '1.0.0-rc.4.0.0') - assert(#bp3d.lua.patches == 5) + assert(bp3d.lua.version == BP3D_LUA_CRATE_VERSION) + assert(#bp3d.lua.patches == 7) local func = bp3d.lua.loadString('return 1 + 1') assert(func) assert(func() == 2) From 2754609a2c3ad501ee3c806977b74d14a815375e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 11:35:07 +0200 Subject: [PATCH 398/527] Added initial set of safety tests --- core/Cargo.toml | 1 + .../safety/test_destructor_arc_not_send.rs | 41 ++++++++++++++++ .../test_destructor_arc_not_send.stderr | 22 +++++++++ .../safety/test_destructor_arc_not_sync.rs | 43 +++++++++++++++++ .../test_destructor_arc_not_sync.stderr | 22 +++++++++ .../safety/test_destructor_box_not_send.rs | 39 +++++++++++++++ .../test_destructor_box_not_send.stderr | 25 ++++++++++ .../safety/test_destructor_rc_not_send.rs | 41 ++++++++++++++++ .../safety/test_destructor_rc_not_send.stderr | 9 ++++ .../safety/test_multi_root_vms_not_send.rs | 37 ++++++++++++++ .../test_multi_root_vms_not_send.stderr | 48 +++++++++++++++++++ core/tests/safety/test_rclosure_not_send.rs | 41 ++++++++++++++++ .../safety/test_rclosure_not_send.stderr | 34 +++++++++++++ core/tests/safety/test_scope_safe_1.rs | 37 ++++++++++++++ core/tests/safety/test_scope_safe_1.stderr | 10 ++++ core/tests/safety/test_scope_safe_2.rs | 37 ++++++++++++++ core/tests/safety/test_scope_safe_2.stderr | 16 +++++++ core/tests/safety/test_scope_safe_3.rs | 41 ++++++++++++++++ core/tests/safety/test_scope_safe_3.stderr | 17 +++++++ .../safety/test_vm_threads_unsafe_type.rs | 38 +++++++++++++++ .../safety/test_vm_threads_unsafe_type.stderr | 26 ++++++++++ core/tests/test_multi_root_vms.rs | 5 -- core/tests/test_vm_closures.rs | 4 +- core/tests/test_vm_safety.rs | 33 +++++++++++++ 24 files changed, 660 insertions(+), 7 deletions(-) create mode 100644 core/tests/safety/test_destructor_arc_not_send.rs create mode 100644 core/tests/safety/test_destructor_arc_not_send.stderr create mode 100644 core/tests/safety/test_destructor_arc_not_sync.rs create mode 100644 core/tests/safety/test_destructor_arc_not_sync.stderr create mode 100644 core/tests/safety/test_destructor_box_not_send.rs create mode 100644 core/tests/safety/test_destructor_box_not_send.stderr create mode 100644 core/tests/safety/test_destructor_rc_not_send.rs create mode 100644 core/tests/safety/test_destructor_rc_not_send.stderr create mode 100644 core/tests/safety/test_multi_root_vms_not_send.rs create mode 100644 core/tests/safety/test_multi_root_vms_not_send.stderr create mode 100644 core/tests/safety/test_rclosure_not_send.rs create mode 100644 core/tests/safety/test_rclosure_not_send.stderr create mode 100644 core/tests/safety/test_scope_safe_1.rs create mode 100644 core/tests/safety/test_scope_safe_1.stderr create mode 100644 core/tests/safety/test_scope_safe_2.rs create mode 100644 core/tests/safety/test_scope_safe_2.stderr create mode 100644 core/tests/safety/test_scope_safe_3.rs create mode 100644 core/tests/safety/test_scope_safe_3.stderr create mode 100644 core/tests/safety/test_vm_threads_unsafe_type.rs create mode 100644 core/tests/safety/test_vm_threads_unsafe_type.stderr create mode 100644 core/tests/test_vm_safety.rs diff --git a/core/Cargo.toml b/core/Cargo.toml index 7e98e0b..be02058 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -29,6 +29,7 @@ libc = { version = "0.2.170", optional = true } [dev-dependencies] bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen" } +trybuild = "1.0.110" [build-dependencies] bp3d-lua-build = { version = "1.0.0-rc.1.0.0", path = "../build" } diff --git a/core/tests/safety/test_destructor_arc_not_send.rs b/core/tests/safety/test_destructor_arc_not_send.rs new file mode 100644 index 0000000..c2fd522 --- /dev/null +++ b/core/tests/safety/test_destructor_arc_not_send.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::closure::arc::Shared; + +// Should be !Send +#[derive(Debug)] +struct UnSendType(PhantomData<*const ()>); + +fn main() { + let vm = RootVm::new(); + let obj = Shared::new(UnSendType(PhantomData)); + bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); +} diff --git a/core/tests/safety/test_destructor_arc_not_send.stderr b/core/tests/safety/test_destructor_arc_not_send.stderr new file mode 100644 index 0000000..8958bcb --- /dev/null +++ b/core/tests/safety/test_destructor_arc_not_send.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `std::sync::Arc: RawSend` is not satisfied + --> tests/safety/test_destructor_arc_not_send.rs:40:60 + | +40 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); + | ------------------------------------------------- ^^^ the trait `RawSend` is not implemented for `std::sync::Arc` + | | + | required by a bound introduced by this call + | + = help: the trait `RawSend` is implemented for `std::sync::Arc` +note: required by a bound in `Pool::attach_send` + --> src/vm/core/destructor.rs + | + | pub fn attach_send(vm: &Vm, raw: R) -> R::Ptr + | ^^^^^^^ required by this bound in `Pool::attach_send` + +error[E0277]: the trait bound `std::sync::Arc: RawSend` is not satisfied + --> tests/safety/test_destructor_arc_not_send.rs:40:5 + | +40 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `RawSend` is not implemented for `std::sync::Arc` + | + = help: the trait `RawSend` is implemented for `std::sync::Arc` diff --git a/core/tests/safety/test_destructor_arc_not_sync.rs b/core/tests/safety/test_destructor_arc_not_sync.rs new file mode 100644 index 0000000..41fce9d --- /dev/null +++ b/core/tests/safety/test_destructor_arc_not_sync.rs @@ -0,0 +1,43 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::closure::arc::Shared; + +// Should be Send but !Sync +#[derive(Debug)] +struct UnSyncType(PhantomData<*const ()>); + +unsafe impl Send for UnSyncType {} + +fn main() { + let vm = RootVm::new(); + let obj = Shared::new(UnSyncType(PhantomData)); + bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); +} diff --git a/core/tests/safety/test_destructor_arc_not_sync.stderr b/core/tests/safety/test_destructor_arc_not_sync.stderr new file mode 100644 index 0000000..8f2fe57 --- /dev/null +++ b/core/tests/safety/test_destructor_arc_not_sync.stderr @@ -0,0 +1,22 @@ +error[E0277]: the trait bound `std::sync::Arc: RawSend` is not satisfied + --> tests/safety/test_destructor_arc_not_sync.rs:42:60 + | +42 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); + | ------------------------------------------------- ^^^ the trait `RawSend` is not implemented for `std::sync::Arc` + | | + | required by a bound introduced by this call + | + = help: the trait `RawSend` is implemented for `std::sync::Arc` +note: required by a bound in `Pool::attach_send` + --> src/vm/core/destructor.rs + | + | pub fn attach_send(vm: &Vm, raw: R) -> R::Ptr + | ^^^^^^^ required by this bound in `Pool::attach_send` + +error[E0277]: the trait bound `std::sync::Arc: RawSend` is not satisfied + --> tests/safety/test_destructor_arc_not_sync.rs:42:5 + | +42 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `RawSend` is not implemented for `std::sync::Arc` + | + = help: the trait `RawSend` is implemented for `std::sync::Arc` diff --git a/core/tests/safety/test_destructor_box_not_send.rs b/core/tests/safety/test_destructor_box_not_send.rs new file mode 100644 index 0000000..6acafd2 --- /dev/null +++ b/core/tests/safety/test_destructor_box_not_send.rs @@ -0,0 +1,39 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use bp3d_lua::vm::RootVm; + +// Should be !Send +#[derive(Debug)] +struct UnSendType(PhantomData<*const ()>); + +fn main() { + let vm = RootVm::new(); + bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, Box::new(UnSendType(PhantomData))); +} diff --git a/core/tests/safety/test_destructor_box_not_send.stderr b/core/tests/safety/test_destructor_box_not_send.stderr new file mode 100644 index 0000000..3d2c8ae --- /dev/null +++ b/core/tests/safety/test_destructor_box_not_send.stderr @@ -0,0 +1,25 @@ +error[E0277]: `*const ()` cannot be sent between threads safely + --> tests/safety/test_destructor_box_not_send.rs:38:5 + | +38 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, Box::new(UnSendType(PhantomData))); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const ()` cannot be sent between threads safely + | + = help: within `UnSendType`, the trait `Send` is not implemented for `*const ()` + = help: the trait `RawSend` is implemented for `std::sync::Arc` +note: required because it appears within the type `PhantomData<*const ()>` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `UnSendType` + --> tests/safety/test_destructor_box_not_send.rs:34:8 + | +34 | struct UnSendType(PhantomData<*const ()>); + | ^^^^^^^^^^ + = note: required for `Unique` to implement `Send` +note: required because it appears within the type `Box` + --> $RUST/alloc/src/boxed.rs + | + | pub struct Box< + | ^^^ + = note: required for `Box` to implement `RawSend` diff --git a/core/tests/safety/test_destructor_rc_not_send.rs b/core/tests/safety/test_destructor_rc_not_send.rs new file mode 100644 index 0000000..e359ba4 --- /dev/null +++ b/core/tests/safety/test_destructor_rc_not_send.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::closure::rc::Shared; + +// Should be !Send +#[derive(Debug)] +struct UnSendType(PhantomData<*const ()>); + +fn main() { + let vm = RootVm::new(); + let obj = Shared::new(UnSendType(PhantomData)); + bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); +} diff --git a/core/tests/safety/test_destructor_rc_not_send.stderr b/core/tests/safety/test_destructor_rc_not_send.stderr new file mode 100644 index 0000000..e18fa59 --- /dev/null +++ b/core/tests/safety/test_destructor_rc_not_send.stderr @@ -0,0 +1,9 @@ +error[E0277]: `std::rc::Rc` cannot be sent between threads safely + --> tests/safety/test_destructor_rc_not_send.rs:40:5 + | +40 | bp3d_lua::vm::core::destructor::Pool::attach_send(&vm, obj); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `std::rc::Rc` cannot be sent between threads safely + | + = help: the trait `Send` is not implemented for `std::rc::Rc` + = help: the trait `RawSend` is implemented for `std::sync::Arc` + = note: required for `std::rc::Rc` to implement `RawSend` diff --git a/core/tests/safety/test_multi_root_vms_not_send.rs b/core/tests/safety/test_multi_root_vms_not_send.rs new file mode 100644 index 0000000..10ab1ec --- /dev/null +++ b/core/tests/safety/test_multi_root_vms_not_send.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::core::UnSendRootVm; + +fn main() { + let root = UnSendRootVm::new(); + std::thread::spawn(move || { + // Should fail to build because UnSendRootVm is not supposed to be Send. + root.run_code::<()>(c"").unwrap(); + }); +} diff --git a/core/tests/safety/test_multi_root_vms_not_send.stderr b/core/tests/safety/test_multi_root_vms_not_send.stderr new file mode 100644 index 0000000..de1e116 --- /dev/null +++ b/core/tests/safety/test_multi_root_vms_not_send.stderr @@ -0,0 +1,48 @@ +error[E0277]: `*mut c_void` cannot be sent between threads safely + --> tests/safety/test_multi_root_vms_not_send.rs:33:24 + | +33 | std::thread::spawn(move || { + | ------------------ ^------ + | | | + | _____|__________________within this `{closure@$DIR/tests/safety/test_multi_root_vms_not_send.rs:33:24: 33:31}` + | | | + | | required by a bound introduced by this call +34 | | // Should fail to build because UnSendRootVm is not supposed to be Send. +35 | | root.run_code::<()>(c"").unwrap(); +36 | | }); + | |_____^ `*mut c_void` cannot be sent between threads safely + | + = help: within `{closure@$DIR/tests/safety/test_multi_root_vms_not_send.rs:33:24: 33:31}`, the trait `Send` is not implemented for `*mut c_void` +note: required because it appears within the type `bp3d_lua::ffi::lua::State` + --> src/ffi/lua.rs + | + | pub struct State(*mut c_void); + | ^^^^^ +note: required because it appears within the type `bp3d_lua::vm::Vm` + --> src/vm/core/vm.rs + | + | pub struct Vm { + | ^^ +note: required because it appears within the type `bp3d_lua::vm::core::root_vm::common::UnsafeRootVm` + --> src/vm/core/root_vm/common.rs + | + | pub struct UnsafeRootVm(pub Vm); + | ^^^^^^^^^^^^ +note: required because it appears within the type `bp3d_lua::vm::core::root_vm::unsend::RootVm` + --> src/vm/core/root_vm/unsend.rs + | + | pub struct RootVm { + | ^^^^^^ +note: required because it's used within this closure + --> tests/safety/test_multi_root_vms_not_send.rs:33:24 + | +33 | std::thread::spawn(move || { + | ^^^^^^^ +note: required by a bound in `spawn` + --> $RUST/std/src/thread/mod.rs + | + | pub fn spawn(f: F) -> JoinHandle + | ----- required by a bound in this function +... + | F: Send + 'static, + | ^^^^ required by this bound in `spawn` diff --git a/core/tests/safety/test_rclosure_not_send.rs b/core/tests/safety/test_rclosure_not_send.rs new file mode 100644 index 0000000..7138fda --- /dev/null +++ b/core/tests/safety/test_rclosure_not_send.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::closure::types::RClosure; + +// Should be !Send +#[derive(Debug)] +struct UnSendType(PhantomData<*const ()>); + +fn main() { + let ty = UnSendType(PhantomData); + let vm = RootVm::new(); + RClosure::from_rust(&vm, move |val: f32| format!("this is a test: {} - {:?}", val, ty)); +} diff --git a/core/tests/safety/test_rclosure_not_send.stderr b/core/tests/safety/test_rclosure_not_send.stderr new file mode 100644 index 0000000..9663153 --- /dev/null +++ b/core/tests/safety/test_rclosure_not_send.stderr @@ -0,0 +1,34 @@ +error[E0277]: `*const ()` cannot be sent between threads safely + --> tests/safety/test_rclosure_not_send.rs:40:30 + | +40 | RClosure::from_rust(&vm, move |val: f32| format!("this is a test: {} - {:?}", val, ty)); + | ------------------- ---------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | | | + | | `*const ()` cannot be sent between threads safely + | | within this `{closure@$DIR/tests/safety/test_rclosure_not_send.rs:40:30: 40:45}` + | required by a bound introduced by this call + | + = help: within `{closure@$DIR/tests/safety/test_rclosure_not_send.rs:40:30: 40:45}`, the trait `Send` is not implemented for `*const ()` +note: required because it appears within the type `PhantomData<*const ()>` + --> $RUST/core/src/marker.rs + | + | pub struct PhantomData; + | ^^^^^^^^^^^ +note: required because it appears within the type `UnSendType` + --> tests/safety/test_rclosure_not_send.rs:35:8 + | +35 | struct UnSendType(PhantomData<*const ()>); + | ^^^^^^^^^^ +note: required because it's used within this closure + --> tests/safety/test_rclosure_not_send.rs:40:30 + | +40 | RClosure::from_rust(&vm, move |val: f32| format!("this is a test: {} - {:?}", val, ty)); + | ^^^^^^^^^^^^^^^ +note: required by a bound in `RClosure::>::from_rust` + --> src/vm/closure/types.rs + | + | pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + | --------- required by a bound in this associated function +... + | F: Send + | ^^^^ required by this bound in `RClosure::>::from_rust` diff --git a/core/tests/safety/test_scope_safe_1.rs b/core/tests/safety/test_scope_safe_1.rs new file mode 100644 index 0000000..47cad54 --- /dev/null +++ b/core/tests/safety/test_scope_safe_1.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; + +fn main() { + let vm = RootVm::new(); + let _ = vm.scope(|vm| { + // Should not build as get_global is limited to vm.scope. + vm.get_global::<&str>(c"MY_GLOBAL") + }); +} diff --git a/core/tests/safety/test_scope_safe_1.stderr b/core/tests/safety/test_scope_safe_1.stderr new file mode 100644 index 0000000..102a19b --- /dev/null +++ b/core/tests/safety/test_scope_safe_1.stderr @@ -0,0 +1,10 @@ +error: lifetime may not live long enough + --> tests/safety/test_scope_safe_1.rs:35:9 + | +33 | let _ = vm.scope(|vm| { + | --- return type of closure is Result<&'2 str, bp3d_lua::vm::error::Error> + | | + | has type `&'1 bp3d_lua::vm::Vm` +34 | // Should not build as get_global is limited to vm.scope. +35 | vm.get_global::<&str>(c"MY_GLOBAL") + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2` diff --git a/core/tests/safety/test_scope_safe_2.rs b/core/tests/safety/test_scope_safe_2.rs new file mode 100644 index 0000000..fa71640 --- /dev/null +++ b/core/tests/safety/test_scope_safe_2.rs @@ -0,0 +1,37 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; + +fn main() { + let vm = RootVm::new(); + let _ = vm.scope(|_| { + // Should not build as get_global is limited to vm.scope. + vm.get_global::<&str>(c"MY_GLOBAL") + }); +} diff --git a/core/tests/safety/test_scope_safe_2.stderr b/core/tests/safety/test_scope_safe_2.stderr new file mode 100644 index 0000000..4cb3a04 --- /dev/null +++ b/core/tests/safety/test_scope_safe_2.stderr @@ -0,0 +1,16 @@ +error[E0597]: `vm` does not live long enough + --> tests/safety/test_scope_safe_2.rs:35:9 + | +32 | let vm = RootVm::new(); + | -- binding `vm` declared here +33 | let _ = vm.scope(|_| { + | --- value captured here +34 | // Should not build as get_global is limited to vm.scope. +35 | vm.get_global::<&str>(c"MY_GLOBAL") + | ^^--------------------------------- + | | + | borrowed value does not live long enough + | returning this value requires that `vm` is borrowed for `'static` +36 | }); +37 | } + | - `vm` dropped here while still borrowed diff --git a/core/tests/safety/test_scope_safe_3.rs b/core/tests/safety/test_scope_safe_3.rs new file mode 100644 index 0000000..b25d9a5 --- /dev/null +++ b/core/tests/safety/test_scope_safe_3.rs @@ -0,0 +1,41 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::types::Function; + +fn main() { + let vm = RootVm::new(); + let func: Function = vm.get_global(c"MY_GLOBAL").unwrap(); + let _ = vm.scope(|_| { + vm.set_global(c"MY_GLOBAL_2", func).unwrap(); + Ok(()) + }); + // Should fail because func was moved inside scope. + vm.set_global(c"MY_GLOBAL_3", func).unwrap(); +} diff --git a/core/tests/safety/test_scope_safe_3.stderr b/core/tests/safety/test_scope_safe_3.stderr new file mode 100644 index 0000000..37e2df8 --- /dev/null +++ b/core/tests/safety/test_scope_safe_3.stderr @@ -0,0 +1,17 @@ +error[E0382]: use of moved value: `func` + --> tests/safety/test_scope_safe_3.rs:40:35 + | +34 | let func: Function = vm.get_global(c"MY_GLOBAL").unwrap(); + | ---- move occurs because `func` has type `bp3d_lua::vm::value::types::Function<'_>`, which does not implement the `Copy` trait +35 | let _ = vm.scope(|_| { + | --- value moved into closure here +36 | vm.set_global(c"MY_GLOBAL_2", func).unwrap(); + | ---- variable moved due to use in closure +... +40 | vm.set_global(c"MY_GLOBAL_3", func).unwrap(); + | ^^^^ value used here after move + | +help: consider cloning the value if the performance cost is acceptable + | +36 | vm.set_global(c"MY_GLOBAL_2", func.clone()).unwrap(); + | ++++++++ diff --git a/core/tests/safety/test_vm_threads_unsafe_type.rs b/core/tests/safety/test_vm_threads_unsafe_type.rs new file mode 100644 index 0000000..3d37b64 --- /dev/null +++ b/core/tests/safety/test_vm_threads_unsafe_type.rs @@ -0,0 +1,38 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::core::UnSendRootVm; +use bp3d_lua::vm::thread::value::Thread; + +fn main() { + let vm = UnSendRootVm::new(); + let thread: Thread = vm.get_global(c"CO").unwrap(); + // Should fail to build because &str could be freed/overwritten by luajit after a subsequent + // resume. + let _: &str = thread.as_thread().resume(()).unwrap().data; +} diff --git a/core/tests/safety/test_vm_threads_unsafe_type.stderr b/core/tests/safety/test_vm_threads_unsafe_type.stderr new file mode 100644 index 0000000..850f173 --- /dev/null +++ b/core/tests/safety/test_vm_threads_unsafe_type.stderr @@ -0,0 +1,26 @@ +error[E0597]: `vm` does not live long enough + --> tests/safety/test_vm_threads_unsafe_type.rs:34:26 + | +33 | let vm = UnSendRootVm::new(); + | -- binding `vm` declared here +34 | let thread: Thread = vm.get_global(c"CO").unwrap(); + | ^^ borrowed value does not live long enough +... +37 | let _: &str = thread.as_thread().resume(()).unwrap().data; + | ----------------------------- argument requires that `vm` is borrowed for `'static` +38 | } + | - `vm` dropped here while still borrowed + +error[E0597]: `thread` does not live long enough + --> tests/safety/test_vm_threads_unsafe_type.rs:37:19 + | +34 | let thread: Thread = vm.get_global(c"CO").unwrap(); + | ------ binding `thread` declared here +... +37 | let _: &str = thread.as_thread().resume(()).unwrap().data; + | ^^^^^^----------------------- + | | + | borrowed value does not live long enough + | argument requires that `thread` is borrowed for `'static` +38 | } + | - `thread` dropped here while still borrowed diff --git a/core/tests/test_multi_root_vms.rs b/core/tests/test_multi_root_vms.rs index ec41634..bcaec03 100644 --- a/core/tests/test_multi_root_vms.rs +++ b/core/tests/test_multi_root_vms.rs @@ -140,8 +140,3 @@ fn test_multi_root_vms_send_destructor() { let root1 = RootVm::new(); Pool::attach_send(&root1, Box::new(())); } - -/*#[test] -fn test_multi_root_vms_not_send_build_error() { - todo!() -}*/ \ No newline at end of file diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 5e0bf9c..1c75e18 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -78,9 +78,9 @@ fn test_vm_fast_closure() { #[test] fn test_vm_rust_closure() { - let mut vm = RootVm::new(); + let vm = RootVm::new(); let top = vm.top(); - let closure = RClosure::from_rust(&mut vm, |val: f32| format!("this is a test: {}", val)); + let closure = RClosure::from_rust(&vm, |val: f32| format!("this is a test: {}", val)); vm.set_global(c"test", closure).unwrap(); assert_eq!(top, vm.top()); let s: &str = vm.run_code(c"return test(42.42)").unwrap(); diff --git a/core/tests/test_vm_safety.rs b/core/tests/test_vm_safety.rs new file mode 100644 index 0000000..63c5152 --- /dev/null +++ b/core/tests/test_vm_safety.rs @@ -0,0 +1,33 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#[test] +fn test_vm_safety() { + let t = trybuild::TestCases::new(); + t.compile_fail("tests/safety/*.rs"); +} From 6d27b1b0013ec8688061b04efc4c90726a54ccb4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 13:14:52 +0200 Subject: [PATCH 399/527] Rename AnyValue to Any --- core/src/libs/util/table.rs | 10 +-- core/src/vm/table/iter.rs | 8 +-- core/src/vm/value/any.rs | 108 ++++++++++++++++----------------- shell/core/src/autocomplete.rs | 26 ++++---- shell/core/src/lua.rs | 4 +- 5 files changed, 78 insertions(+), 78 deletions(-) diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 7e9020f..b049993 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -31,7 +31,7 @@ use crate::libs::Lib; use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::vm::table::Table as LuaTable; -use crate::vm::value::any::AnyValue; +use crate::vm::value::any::Any; use crate::vm::Vm; use bp3d_util::simple_error; @@ -39,7 +39,7 @@ fn update_rec(vm: &Vm, mut dst: LuaTable, mut src: LuaTable) -> crate::vm::Resul for res in src.iter() { let (k, v) = res?; match v { - AnyValue::Table(v) => vm.scope(|_| { + Any::Table(v) => vm.scope(|_| { let dst1: Option = dst.get_any(k.clone())?; match dst1 { None => { @@ -97,7 +97,7 @@ fn to_string_rec(prefix: String, mut table: LuaTable) -> crate::vm::Result { + Any::Table(v) => { lines.push(format!("{}:", k)); lines.extend(to_string_rec(prefix.clone() + " ", v)?); } @@ -114,7 +114,7 @@ decl_lib_func! { } decl_lib_func! { - fn contains(src: LuaTable, value: AnyValue) -> crate::vm::Result { + fn contains(src: LuaTable, value: Any) -> crate::vm::Result { let mut src = src; for res in src.iter() { let (_, v) = res?; @@ -127,7 +127,7 @@ decl_lib_func! { } decl_lib_func! { - fn contains_key(src: LuaTable, key: AnyValue) -> crate::vm::Result { + fn contains_key(src: LuaTable, key: Any) -> crate::vm::Result { let mut src = src; for res in src.iter() { let (k, _) = res?; diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 8a2c840..b0973a9 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_next, lua_pushnil, lua_settop}; -use crate::vm::value::any::AnyValue; +use crate::vm::value::any::Any; use crate::vm::value::FromLua; use crate::vm::Vm; @@ -54,7 +54,7 @@ impl<'a> Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = crate::vm::Result<(AnyValue<'a>, AnyValue<'a>)>; + type Item = crate::vm::Result<(Any<'a>, Any<'a>)>; fn next(&mut self) -> Option { if self.has_started { @@ -74,8 +74,8 @@ impl<'a> Iterator for Iter<'a> { self.last_top = self.vm.top(); self.has_started = true; if ret != 0 { - let value = AnyValue::from_lua(self.vm, -2); - let key = AnyValue::from_lua(self.vm, -1); + let value = Any::from_lua(self.vm, -2); + let key = Any::from_lua(self.vm, -1); Some(match (value, key) { (Ok(key), Ok(value)) => Ok((key, value)), (Ok(_), Err(e)) => Err(e), diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 88e1c98..de67f7a 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -41,7 +41,7 @@ use std::fmt::Display; use std::str::FromStr; #[derive(Debug, PartialEq, Clone)] -pub enum AnyValue<'a> { +pub enum Any<'a> { None, Nil, Number(f64), @@ -54,45 +54,45 @@ pub enum AnyValue<'a> { Thread(Thread<'a>), } -impl Eq for AnyValue<'_> {} +impl Eq for Any<'_> {} -impl Display for AnyValue<'_> { +impl Display for Any<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - AnyValue::None => f.write_str(""), - AnyValue::Nil => f.write_str("nil"), - AnyValue::Number(v) => write!(f, "{}", v), - AnyValue::Boolean(v) => write!(f, "{}", v), - AnyValue::String(v) => write!(f, "{}", v), - AnyValue::Buffer(v) => write!(f, "{:?}", v), - AnyValue::Function(v) => write!(f, "{}", v), - AnyValue::Table(v) => write!(f, "{}", v), - AnyValue::UserData(v) => write!(f, "{}", v), - AnyValue::Thread(v) => write!(f, "{}", v), + Any::None => f.write_str(""), + Any::Nil => f.write_str("nil"), + Any::Number(v) => write!(f, "{}", v), + Any::Boolean(v) => write!(f, "{}", v), + Any::String(v) => write!(f, "{}", v), + Any::Buffer(v) => write!(f, "{:?}", v), + Any::Function(v) => write!(f, "{}", v), + Any::Table(v) => write!(f, "{}", v), + Any::UserData(v) => write!(f, "{}", v), + Any::Thread(v) => write!(f, "{}", v), } } } -impl AnyValue<'_> { +impl Any<'_> { pub fn ty(&self) -> Type { match self { - AnyValue::None => Type::None, - AnyValue::Nil => Type::Nil, - AnyValue::Number(_) => Type::Number, - AnyValue::Boolean(_) => Type::Boolean, - AnyValue::String(_) => Type::String, - AnyValue::Buffer(_) => Type::String, - AnyValue::Function(_) => Type::Function, - AnyValue::Table(_) => Type::Table, - AnyValue::UserData(_) => Type::Userdata, - AnyValue::Thread(_) => Type::Thread, + Any::None => Type::None, + Any::Nil => Type::Nil, + Any::Number(_) => Type::Number, + Any::Boolean(_) => Type::Boolean, + Any::String(_) => Type::String, + Any::Buffer(_) => Type::String, + Any::Function(_) => Type::Function, + Any::Table(_) => Type::Table, + Any::UserData(_) => Type::Userdata, + Any::Thread(_) => Type::Thread, } } pub fn to_number(&self) -> Result { match self { - AnyValue::Number(v) => Ok(*v), - AnyValue::String(v) => { + Any::Number(v) => Ok(*v), + Any::String(v) => { crate::ffi::lua::RawNumber::from_str(v).map_err(|_| Error::ParseFloat) } _ => Err(Error::Type(TypeError { @@ -104,8 +104,8 @@ impl AnyValue<'_> { pub fn to_integer(&self) -> Result { match self { - AnyValue::Number(v) => Ok(*v as _), - AnyValue::String(v) => { + Any::Number(v) => Ok(*v as _), + Any::String(v) => { crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt) } _ => Err(Error::Type(TypeError { @@ -116,34 +116,34 @@ impl AnyValue<'_> { } } -unsafe impl IntoLua for AnyValue<'_> { +unsafe impl IntoLua for Any<'_> { fn into_lua(self, vm: &Vm) -> u16 { match self { - AnyValue::None => 0, - AnyValue::Nil => { + Any::None => 0, + Any::Nil => { unsafe { lua_pushnil(vm.as_ptr()) }; 1 } - AnyValue::Number(v) => v.into_lua(vm), - AnyValue::Boolean(v) => v.into_lua(vm), - AnyValue::String(v) => v.into_lua(vm), - AnyValue::Buffer(v) => v.into_lua(vm), - AnyValue::Function(v) => v.into_lua(vm), - AnyValue::Table(v) => v.into_lua(vm), - AnyValue::UserData(_) => 0, - AnyValue::Thread(_) => 0, + Any::Number(v) => v.into_lua(vm), + Any::Boolean(v) => v.into_lua(vm), + Any::String(v) => v.into_lua(vm), + Any::Buffer(v) => v.into_lua(vm), + Any::Function(v) => v.into_lua(vm), + Any::Table(v) => v.into_lua(vm), + Any::UserData(_) => 0, + Any::Thread(_) => 0, } } } -unsafe impl IntoParam for AnyValue<'_> { +unsafe impl IntoParam for Any<'_> { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { IntoLua::into_lua(self, vm) as _ } } -impl<'a> FromLua<'a> for AnyValue<'a> { +impl<'a> FromLua<'a> for Any<'a> { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self::from_lua(vm, index).unwrap_unchecked() @@ -152,41 +152,41 @@ impl<'a> FromLua<'a> for AnyValue<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let ty = unsafe { lua_type(vm.as_ptr(), index) }; match ty { - Type::None => Ok(AnyValue::None), - Type::Nil => Ok(AnyValue::Nil), + Type::None => Ok(Any::None), + Type::Nil => Ok(Any::Nil), Type::Boolean => { let value = unsafe { lua_toboolean(vm.as_ptr(), index) }; - Ok(AnyValue::Boolean(value == 1)) + Ok(Any::Boolean(value == 1)) } Type::LightUserdata => Err(Error::UnsupportedType(ty)), Type::Number => { let value = unsafe { lua_tonumber(vm.as_ptr(), index) }; - Ok(AnyValue::Number(value)) + Ok(Any::Number(value)) } Type::String => { let buffer: &[u8] = unsafe { FromLua::from_lua_unchecked(vm, index) }; match std::str::from_utf8(buffer) { - Ok(s) => Ok(AnyValue::String(s)), - Err(_) => Ok(AnyValue::Buffer(buffer)), + Ok(s) => Ok(Any::String(s)), + Err(_) => Ok(Any::Buffer(buffer)), } } - Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), + Type::Table => Ok(unsafe { Any::Table(FromLua::from_lua_unchecked(vm, index)) }), Type::Function => { - Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }) + Ok(unsafe { Any::Function(FromLua::from_lua_unchecked(vm, index)) }) } Type::Userdata => { - Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }) + Ok(unsafe { Any::UserData(FromLua::from_lua_unchecked(vm, index)) }) } - Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), + Type::Thread => Ok(unsafe { Any::Thread(FromLua::from_lua_unchecked(vm, index)) }), } } } -unsafe impl SimpleDrop for AnyValue<'_> {} +unsafe impl SimpleDrop for Any<'_> {} -impl LuaType for AnyValue<'_> {} +impl LuaType for Any<'_> {} -impl<'a> FromParam<'a> for AnyValue<'a> { +impl<'a> FromParam<'a> for Any<'a> { #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { match FromLua::from_lua(vm, index) { diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index deaf147..44ae75d 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -32,7 +32,7 @@ use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::rc::{Rc, Shared}; use bp3d_lua::vm::table::Table; -use bp3d_lua::vm::value::any::AnyValue; +use bp3d_lua::vm::value::any::Any; use crate::data::DataOut; pub enum Mode { @@ -52,9 +52,9 @@ pub struct Item { } impl Item { - pub fn from_lua(name: &str, val: &AnyValue) -> Self { + pub fn from_lua(name: &str, val: &Any) -> Self { match val { - AnyValue::Function(_) => Item { name: name.into(), ty: Type::Function }, + Any::Function(_) => Item { name: name.into(), ty: Type::Function }, _ => Item { name: name.into(), ty: Type::Attribute } } } @@ -65,11 +65,11 @@ pub struct Completions { pub items: Vec } -fn get_capacity(val: &AnyValue) -> usize { +fn get_capacity(val: &Any) -> usize { match val { - AnyValue::Function(_) => 0, - AnyValue::Table(v) => v.len(), - AnyValue::UserData(_) => 1, + Any::Function(_) => 0, + Any::Table(v) => v.len(), + Any::UserData(_) => 1, _ => 0 } } @@ -81,7 +81,7 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu for res in value.iter() { let (k, v) = res?; match k { - AnyValue::String(name) => { + Any::String(name) => { let c = get_capacity(&v); if c > 0 { let mut path = path.clone(); @@ -107,10 +107,10 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu Ok(()) } -fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: AnyValue, metatables: bool) -> bp3d_lua::vm::Result<()> { +fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: Any, metatables: bool) -> bp3d_lua::vm::Result<()> { match value { - AnyValue::Table(v) => list_table_completions(set, path, root, v, metatables), - AnyValue::UserData(v) => { + Any::Table(v) => list_table_completions(set, path, root, v, metatables), + Any::UserData(v) => { if let Some(tbl) = v.get_metatable() { // We assume userdata have a single metatable (following current bp3d-lua pattern). list_table_completions(set, path, root, tbl, false)?; @@ -123,7 +123,7 @@ fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec< decl_closure! { fn build_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { - let value: AnyValue = lua.get_global(name)?; + let value: Any = lua.get_global(name)?; let mut root = Vec::new(); let mut set = HashSet::new(); list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; @@ -134,7 +134,7 @@ decl_closure! { decl_closure! { fn delete_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { - let value: AnyValue = lua.get_global(name)?; + let value: Any = lua.get_global(name)?; let mut root = Vec::new(); let mut set = HashSet::new(); list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 0fcb9be..dd33247 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -37,7 +37,7 @@ use bp3d_lua::libs::Lib; use bp3d_debug::{debug, error, info}; use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; -use bp3d_lua::vm::value::any::AnyValue; +use bp3d_lua::vm::value::any::Any; use bp3d_lua::vm::Vm; use crate::autocomplete::Autocomplete; use crate::data::DataOut; @@ -86,7 +86,7 @@ impl Lua { self.out_queue.recv().await } - fn handle_value(res: bp3d_lua::vm::Result, logger: &DataOut) -> bool { + fn handle_value(res: bp3d_lua::vm::Result, logger: &DataOut) -> bool { match res { Ok(v) => { logger.send(Log("output", v.to_string())); From 5a4734b1283213c6185b41e754d6cd0787d5fa7c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 13:19:30 +0200 Subject: [PATCH 400/527] Fixed possible type safety issue --- core/src/vm/registry/lua_ref.rs | 2 +- core/tests/test_vm_registry.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index f0c0250..388d73f 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -119,7 +119,7 @@ impl<'a, T: SimpleValue<'a>> LuaRef<'a, T> { unsafe { T::from_lua(self.vm, self.index) } } - pub fn set(&self, value: T) { + pub fn set(&mut self, value: T) { value.into_lua(self.vm); unsafe { lua_replace(self.vm.as_ptr(), self.index) }; } diff --git a/core/tests/test_vm_registry.rs b/core/tests/test_vm_registry.rs index c78baa4..cf5d2ff 100644 --- a/core/tests/test_vm_registry.rs +++ b/core/tests/test_vm_registry.rs @@ -97,7 +97,7 @@ fn test_vm_registry_string_modify() { let r = LiveLuaRef::new(&vm, "this is a test"); let key: Key> = Key::new(r); assert_eq!(vm.top(), top); - let value = key.push(&vm); + let mut value = key.push(&vm); assert_eq!(value.get(), "this is a test"); value.set("one more test"); assert_eq!(value.get(), "one more test"); From 583fecaddc4d51cdf8e6443190dec62da52b670d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 13:26:39 +0200 Subject: [PATCH 401/527] Added Unknown type to represent an unknown reference on the lua stack identified by a single index --- core/src/vm/value/types.rs | 1 + core/src/vm/value/unknown.rs | 74 ++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 core/src/vm/value/unknown.rs diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index d6663a4..0976f5c 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -32,6 +32,7 @@ use crate::vm::util::LuaType; pub use super::function::Function; pub use super::raw_ptr::RawPtr; +pub use super::unknown::Unknown; #[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] pub struct Number(pub RawNumber); diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs new file mode 100644 index 0000000..b2a05a4 --- /dev/null +++ b/core/src/vm/value/unknown.rs @@ -0,0 +1,74 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::{Debug, Display}; +use crate::ffi::lua::{lua_replace, lua_type, Type}; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::any::Any; +use crate::vm::Vm; + +pub struct Unknown<'a> { + vm: &'a Vm, + index: i32 +} + +impl Debug for Unknown<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Table({:?})", self.index) + } +} + +impl<'a> Unknown<'a> { + pub fn from_vm(vm: &'a Vm, index: i32) -> Self { + Self { + vm, + index + } + } + + pub fn get>(&'a self) -> crate::vm::Result { + T::from_lua(self.vm, self.index) + } + + pub fn ty(&self) -> Type { + unsafe { lua_type(self.vm.as_ptr(), self.index) } + } + + pub fn to_any(self) -> crate::vm::Result> { + Any::from_lua(self.vm, self.index) + } + + pub fn set(&mut self, value: impl IntoLua) { + value.into_lua(self.vm); + unsafe { lua_replace(self.vm.as_ptr(), self.index) }; + } + + pub fn index(&self) -> i32 { + self.index + } +} From 0bf9d795462ccba56cea73d0f49bb8d2fde00c4e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 13:27:51 +0200 Subject: [PATCH 402/527] Fixed warning --- core/src/vm/value/mod.rs | 1 + core/src/vm/value/unknown.rs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 8c8634f..28e7196 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -33,5 +33,6 @@ mod interface; pub mod types; pub mod util; mod raw_ptr; +mod unknown; pub use interface::*; diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index b2a05a4..55e287a 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; +use std::fmt::Debug; use crate::ffi::lua::{lua_replace, lua_type, Type}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::any::Any; From 3140479df6b9d48bd7910391750344c5bdd3adbb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 9 Aug 2025 13:29:22 +0200 Subject: [PATCH 403/527] Use new Unknown type in iterator to avoid filtering types --- core/src/libs/util/table.rs | 22 ++++++++++++---------- core/src/vm/table/iter.rs | 16 +++++----------- shell/core/src/autocomplete.rs | 5 +++-- 3 files changed, 20 insertions(+), 23 deletions(-) diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index b049993..981a1b2 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -36,8 +36,9 @@ use crate::vm::Vm; use bp3d_util::simple_error; fn update_rec(vm: &Vm, mut dst: LuaTable, mut src: LuaTable) -> crate::vm::Result<()> { - for res in src.iter() { - let (k, v) = res?; + for (k, v) in src.iter() { + let k = k.to_any()?; + let v = v.to_any()?; match v { Any::Table(v) => vm.scope(|_| { let dst1: Option = dst.get_any(k.clone())?; @@ -69,8 +70,8 @@ decl_lib_func! { let iter = crate::vm::core::iter::start::(vm, 2); for res in iter { let mut src = res?; - for res in src.iter() { - let (_, v) = res?; + for (_, v) in src.iter() { + let v = v.to_any()?; dst.push(v)?; } } @@ -94,8 +95,9 @@ decl_lib_func! { fn to_string_rec(prefix: String, mut table: LuaTable) -> crate::vm::Result> { let mut lines = Vec::new(); - for res in table.iter() { - let (k, v) = res?; + for (k, v) in table.iter() { + let k = k.to_any()?; + let v = v.to_any()?; match v { Any::Table(v) => { lines.push(format!("{}:", k)); @@ -116,8 +118,8 @@ decl_lib_func! { decl_lib_func! { fn contains(src: LuaTable, value: Any) -> crate::vm::Result { let mut src = src; - for res in src.iter() { - let (_, v) = res?; + for (_, v) in src.iter() { + let v = v.to_any()?; if v == value { return Ok(true) } @@ -129,8 +131,8 @@ decl_lib_func! { decl_lib_func! { fn contains_key(src: LuaTable, key: Any) -> crate::vm::Result { let mut src = src; - for res in src.iter() { - let (k, _) = res?; + for (k, _) in src.iter() { + let k = k.to_any()?; if k == key { return Ok(true) } diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index b0973a9..021cfdc 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -27,8 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_next, lua_pushnil, lua_settop}; -use crate::vm::value::any::Any; -use crate::vm::value::FromLua; +use crate::vm::value::types::Unknown; use crate::vm::Vm; pub struct Iter<'a> { @@ -54,7 +53,7 @@ impl<'a> Iter<'a> { } impl<'a> Iterator for Iter<'a> { - type Item = crate::vm::Result<(Any<'a>, Any<'a>)>; + type Item = (Unknown<'a>, Unknown<'a>); fn next(&mut self) -> Option { if self.has_started { @@ -74,14 +73,9 @@ impl<'a> Iterator for Iter<'a> { self.last_top = self.vm.top(); self.has_started = true; if ret != 0 { - let value = Any::from_lua(self.vm, -2); - let key = Any::from_lua(self.vm, -1); - Some(match (value, key) { - (Ok(key), Ok(value)) => Ok((key, value)), - (Ok(_), Err(e)) => Err(e), - (Err(e), Ok(_)) => Err(e), - (Err(_), Err(e)) => Err(e), - }) + let value = Unknown::from_vm(self.vm, -2); + let key = Unknown::from_vm(self.vm, -1); + Some((value, key)) } else { self.has_ended = true; None diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 44ae75d..93df640 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -78,8 +78,9 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu if set.contains(&value.uid()) { return Ok(()); } - for res in value.iter() { - let (k, v) = res?; + for (k, v) in value.iter() { + let k = k.to_any()?; + let v = v.to_any()?; match k { Any::String(name) => { let c = get_capacity(&v); From 99822a0b7013dc637ed94d4bc2993d5cd9f98bf3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 13:08:09 +0200 Subject: [PATCH 404/527] Replaced Vec by Box<[u8]> for byte strings and added initial support for table collect into Vec, HashMap and BTreeMap --- core/src/libs/os/compat.rs | 4 +- core/src/vm/function/core.rs | 4 +- core/src/vm/registry/lua_ref.rs | 2 +- core/src/vm/table/core.rs | 4 ++ core/src/vm/table/interface.rs | 74 +++++++++++++++++++++++++++++++++ core/src/vm/table/mod.rs | 2 - core/src/vm/value/core.rs | 6 +-- core/src/vm/value/unknown.rs | 13 +++++- core/tests/test_vm_tables.rs | 32 +++++++++++++- 9 files changed, 129 insertions(+), 12 deletions(-) diff --git a/core/src/libs/os/compat.rs b/core/src/libs/os/compat.rs index a2fb44c..09d6312 100644 --- a/core/src/libs/os/compat.rs +++ b/core/src/libs/os/compat.rs @@ -185,8 +185,8 @@ decl_lib_func! { } decl_lib_func! { - fn getenv(key: &str) -> Option> { - std::env::var_os(key).map(|v| v.into_encoded_bytes()) + fn getenv(key: &str) -> Option> { + std::env::var_os(key).map(|v| v.into_encoded_bytes().into_boxed_slice()) } } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 20eb484..e4a4107 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -120,10 +120,10 @@ unsafe impl IntoParam for String { } } -unsafe impl IntoParam for Vec { +unsafe impl IntoParam for Box<[u8]> { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { - self.as_slice().into_param(vm) + self.as_ref().into_param(vm) } } diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 388d73f..80c6d6b 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -197,5 +197,5 @@ impl_simple_registry_value_static! { (u64) => u64; (String) => String; (&[u8]) => &'a [u8]; - (Vec) => Vec; + (Box<[u8]>) => Box<[u8]>; } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 022dc88..8bea72f 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -193,4 +193,8 @@ impl<'a> Table<'a> { } Ok(()) } + + pub fn collect>(self) -> crate::vm::Result { + T::from_lua(self.vm, self.index) + } } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 9291f9d..3fe13d6 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{ lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, @@ -144,3 +146,75 @@ impl SetTable for T { Ok(()) } } + +impl<'a, T: 'static> FromLua<'a> for Vec where for<'b> T: FromLua<'b> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let mut tbl = Table::from_lua_unchecked(vm, index); + let mut vec = Vec::new(); + for (_, v) in tbl.iter() { + let vv = v.get_unchecked(); + vec.push(vv); + } + vec + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let mut tbl = Table::from_lua(vm, index)?; + let mut vec = Vec::new(); + for (_, v) in tbl.iter() { + let vv = v.get()?; + vec.push(vv); + } + Ok(vec) + } +} + +impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap where for<'b> K: FromLua<'b> + Hash + Eq, + for<'b> V: FromLua<'b> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let mut tbl = Table::from_lua_unchecked(vm, index); + let mut map = HashMap::new(); + for (k, v) in tbl.iter() { + let kk = k.get_unchecked(); + let vv = v.get_unchecked(); + map.insert(kk, vv); + } + map + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let mut tbl = Table::from_lua(vm, index)?; + let mut map = HashMap::new(); + for (k, v) in tbl.iter() { + let kk = k.get()?; + let vv = v.get()?; + map.insert(kk, vv); + } + Ok(map) + } +} + +impl<'a, K: 'static, V: 'static> FromLua<'a> for BTreeMap where for<'b> K: FromLua<'b> + Ord, + for<'b> V: FromLua<'b> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let mut tbl = Table::from_lua_unchecked(vm, index); + let mut map = BTreeMap::new(); + for (k, v) in tbl.iter() { + let kk = k.get_unchecked(); + let vv = v.get_unchecked(); + map.insert(kk, vv); + } + map + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let mut tbl = Table::from_lua(vm, index)?; + let mut map = BTreeMap::new(); + for (k, v) in tbl.iter() { + let kk = k.get()?; + let vv = v.get()?; + map.insert(kk, vv); + } + Ok(map) + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 07f84ba..41bedce 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -26,8 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//TODO: Support collect function - mod core; mod interface; mod iter; diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 8bb7b2d..68928b5 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -104,7 +104,7 @@ impl FromLua<'_> for String { } } -impl FromLua<'_> for Vec { +impl FromLua<'_> for Box<[u8]> { unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { let bytes: &[u8] = FromLua::from_lua_unchecked(vm, index); bytes.into() @@ -337,10 +337,10 @@ unsafe impl IntoLua for String { } } -unsafe impl IntoLua for Vec { +unsafe impl IntoLua for Box<[u8]> { #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - self.as_slice().into_lua(vm) + self.as_ref().into_lua(vm) } } diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index 55e287a..aeaf6c5 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -51,10 +51,21 @@ impl<'a> Unknown<'a> { } } - pub fn get>(&'a self) -> crate::vm::Result { + /// Interprets the underlying reference on the lua stack as the specified Rust type. + pub fn get>(&self) -> crate::vm::Result { T::from_lua(self.vm, self.index) } + /// Interprets the underlying reference on the lua stack as the specified Rust type. + /// + /// # Safety + /// + /// This function assumes the type of the value at index `index` is already of the expected type, + /// if not, calling this function is UB. + pub unsafe fn get_unchecked>(&self) -> T { + T::from_lua_unchecked(self.vm, self.index) + } + pub fn ty(&self) -> Type { unsafe { lua_type(self.vm.as_ptr(), self.index) } } diff --git a/core/tests/test_vm_tables.rs b/core/tests/test_vm_tables.rs index c6bc2f3..d75b574 100644 --- a/core/tests/test_vm_tables.rs +++ b/core/tests/test_vm_tables.rs @@ -32,7 +32,7 @@ use bp3d_lua::vm::table::Table; use bp3d_lua::vm::RootVm; #[test] -fn test_tables() { +fn test_vm_tables() { let mut vm = RootVm::new(); let top = vm.top(); vm.scope(|vm| { @@ -81,3 +81,33 @@ fn test_tables() { .unwrap(); assert_eq!(vm.top(), new_top); } + +#[test] +fn test_vm_tables_collect_simple() { + let vm = RootVm::new(); + let top = vm.top(); + let mut tbl = Table::new(&vm); + tbl.push("Hello").unwrap(); + tbl.push("world").unwrap(); + let res: Vec = tbl.collect().unwrap(); + assert_eq!(res, vec!["Hello", "world"]); + assert_eq!(vm.top(), top + 1); // One value on the stack (the original table 'tbl') +} + +#[test] +fn test_vm_tables_collect_complex() { + let vm = RootVm::new(); + let top = vm.top(); + let mut tbl = Table::new(&vm); + let mut sub = Table::new(&vm); + sub.push("Hello").unwrap(); + sub.push("World").unwrap(); + tbl.push(sub).unwrap(); + let mut sub2 = Table::new(&vm); + sub2.push("Hello").unwrap(); + sub2.push("World").unwrap(); + tbl.push(sub2).unwrap(); + let res: Vec> = tbl.collect().unwrap(); + assert_eq!(res, vec![vec!["Hello", "World"], vec!["Hello", "World"]]); + assert_eq!(vm.top(), top + 1); // One value on the stack (the original table 'tbl') +} From e531678fbc0654137c07b0afcc572d80ea8009b0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 16:46:29 +0200 Subject: [PATCH 405/527] Fixed possible soundness bug with number of arguments returned by FromLua/IntoLua --- core/src/vm/function/core.rs | 9 ++------- core/src/vm/value/core.rs | 12 ++++-------- 2 files changed, 6 insertions(+), 15 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index e4a4107..6a758f3 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -302,19 +302,14 @@ unsafe impl IntoParam for T { } } -macro_rules! count_tts { - () => {0}; - ($_head:tt $($tail:tt)*) => {1 + count_tts!($($tail)*)}; -} - macro_rules! impl_into_param_tuple { ($($name: ident: $name2: tt),*) => { unsafe impl<$($name: IntoParam),*> IntoParam for ($($name),*) { fn into_param(self, vm: &Vm) -> i32 { $( - self.$name2.into_param(vm); + self.$name2.into_param(vm) + )* - count_tts!($($name)*) + 0 } } }; diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 68928b5..6e035f4 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -194,17 +194,13 @@ impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { } } -macro_rules! count_tts { - () => {0}; - ($_head:tt $($tail:tt)*) => {1 + count_tts!($($tail)*)}; -} - macro_rules! impl_from_lua_tuple { ($($name: ident: $name2: ident ($name3: tt)),*) => { impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { #[inline(always)] fn num_values() -> i16 { - count_tts!($($name)*) + $($name::num_values()+)* + 0 } unsafe fn from_lua_unchecked(vm: &'a Vm, mut index: i32) -> Self { @@ -221,9 +217,9 @@ macro_rules! impl_from_lua_tuple { unsafe impl<$($name: IntoLua),*> IntoLua for ($($name),*) { fn into_lua(self, vm: &Vm) -> u16 { $( - self.$name3.into_lua(vm); + self.$name3.into_lua(vm) + )* - count_tts!($($name)*) + 0 } } }; From 59b88c4dc3d06d0842029f551f4dbc4017718bca Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 16:50:41 +0200 Subject: [PATCH 406/527] Added support for calling arbitrary methods dynamically --- core/src/vm/userdata/any.rs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 2b50b2e..c173248 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -27,13 +27,16 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_pushvalue, lua_topointer, lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{lua_pushvalue, lua_settop, lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; -use crate::vm::value::FromLua; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; +use crate::util::core::AnyStr; +use crate::util::LuaFunction; use crate::vm::table::Table; +use crate::vm::value::types::Function; use crate::vm::value::util::checked_get_metatable; pub struct AnyUserData<'a> { @@ -129,6 +132,19 @@ impl<'a> AnyUserData<'a> { pub fn get_metatable(&self) -> Option
{ checked_get_metatable(self.vm, self.index) } + + pub fn call_method<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr, args: impl IntoLua) -> crate::vm::Result { + let tbl = self.get_metatable().ok_or(Error::Type(TypeError { + expected: Type::Table, + actual: Type::None, + }))?; + let f: Function = tbl.get(name)?; + let f = LuaFunction::create(f); + unsafe { lua_settop(self.vm.as_ptr(), -2) }; // Pop the metatatble from the stack. + let res: T = f.call(self.vm, (self, args))?; + f.delete(self.vm); + Ok(res) + } } impl<'a> FromLua<'a> for AnyUserData<'a> { @@ -149,3 +165,11 @@ impl<'a> FromLua<'a> for AnyUserData<'a> { } } } + +unsafe impl IntoLua for &AnyUserData<'_> { + fn into_lua(self, vm: &Vm) -> u16 { + assert!(self.vm.as_ptr() == vm.as_ptr()); + unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; + 1 + } +} From ab23972f4c18dfbcc7dbe0016b2fb77627868f9e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 17:11:57 +0200 Subject: [PATCH 407/527] Improved Display implementation of AnyUserData to try calling __tostring or tostring meta-methods --- core/src/vm/userdata/any.rs | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index c173248..22eee9b 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_pushvalue, lua_settop, lua_topointer, lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::{FromLua, IntoLua}; @@ -64,11 +64,25 @@ impl Eq for AnyUserData<'_> {} impl Display for AnyUserData<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "userdata@{:X}", - unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize - ) + let res = self.vm.scope(|_| { + let res: crate::vm::Result<&str> = self.call_method("__tostring", ()) + .or_else(|_| self.call_method("tostring", ())); + match res { + Ok(v) => { + let type_name = self.get_type_name()?; + Ok(write!(f, "{}({})", type_name, v)) + }, + Err(e) => Err(e) + } + }); + match res { + Ok(v) => v, + Err(_) => write!( + f, + "userdata@{:X}", + unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize + ) + } } } @@ -133,6 +147,17 @@ impl<'a> AnyUserData<'a> { checked_get_metatable(self.vm, self.index) } + pub fn get_type_name(&self) -> crate::vm::Result<&str> { + let tbl = self.get_metatable().ok_or(Error::Type(TypeError { + expected: Type::Table, + actual: Type::None, + }))?; + let value: &str = tbl.get(c"__metatable")?; + let value2: &'a str = unsafe { std::mem::transmute(value) }; + unsafe { lua_replace(self.vm.as_ptr(), -2) }; + Ok(value2) + } + pub fn call_method<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr, args: impl IntoLua) -> crate::vm::Result { let tbl = self.get_metatable().ok_or(Error::Type(TypeError { expected: Type::Table, From e896983e499875496e2b747e9536c7f69e7bd54d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 17:12:24 +0200 Subject: [PATCH 408/527] Added tests for both call_method and new better Display implementation --- core/tests/test_vm_userdata.rs | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index caeab53..1fbecd1 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -30,7 +30,7 @@ use bp3d_lua::ffi::lua::RawNumber; use bp3d_lua::vm::function::types::RFunction; -use bp3d_lua::vm::userdata::LuaDrop; +use bp3d_lua::vm::userdata::{AnyUserData, LuaDrop}; use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use std::sync::Mutex; @@ -308,3 +308,31 @@ fn test_vm_userdata_security5() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_call_method() { + let _guard = MUTEX.lock(); + let vm = RootVm::new(); + let top = vm.top(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.set_global("MY_INT", MyInt(123456)).unwrap(); + let ud: AnyUserData = vm.get_global("MY_INT").unwrap(); + let val: &str = ud.call_method("tostring", ()).unwrap(); + let val2: RawNumber = ud.call_method("tonumber", ()).unwrap(); + assert_eq!(val, "123456"); + assert_eq!(val2, 123456.0); + assert_eq!(vm.top(), top + 3); +} + +#[test] +fn test_vm_userdata_display() { + let _guard = MUTEX.lock(); + let vm = RootVm::new(); + let top = vm.top(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.set_global("MY_INT", MyInt(123456)).unwrap(); + let ud: AnyUserData = vm.get_global("MY_INT").unwrap(); + let s = ud.to_string(); + assert_eq!(s, "MyInt(123456)"); + assert_eq!(vm.top(), top + 1); +} From 2d16c1331be193a51938914e23603dfc978e6928 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 17:18:20 +0200 Subject: [PATCH 409/527] Refactored IntoLua implementation for function and userdata to new checked_push_value utility --- core/src/vm/userdata/any.rs | 7 +++---- core/src/vm/value/function.rs | 7 +++---- core/src/vm/value/util.rs | 14 ++++++++++++++ 3 files changed, 20 insertions(+), 8 deletions(-) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 22eee9b..bc9402d 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -37,7 +37,7 @@ use crate::util::core::AnyStr; use crate::util::LuaFunction; use crate::vm::table::Table; use crate::vm::value::types::Function; -use crate::vm::value::util::checked_get_metatable; +use crate::vm::value::util::{checked_get_metatable, checked_push_value}; pub struct AnyUserData<'a> { vm: &'a Vm, @@ -192,9 +192,8 @@ impl<'a> FromLua<'a> for AnyUserData<'a> { } unsafe impl IntoLua for &AnyUserData<'_> { + #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - assert!(self.vm.as_ptr() == vm.as_ptr()); - unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; - 1 + checked_push_value(self.vm, vm, self.index) } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index e2a3760..8a711e8 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -33,7 +33,7 @@ use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::util::LuaType; -use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::util::{checked_push_value, ensure_type_equals}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -102,10 +102,9 @@ unsafe impl IntoParam for Function<'_> { } unsafe impl IntoLua for Function<'_> { + #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - assert!(self.vm.as_ptr() == vm.as_ptr()); - unsafe { lua_pushvalue(vm.as_ptr(), self.index) }; - 1 + checked_push_value(self.vm, vm, self.index) } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 0b1e444..89c3193 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -103,3 +103,17 @@ pub fn checked_get_metatable(vm: &Vm, index: i32) -> Option
{ } None } + +/// Pushes the value at `index` to the top of the stack if both [Vm] objects points to the exact +/// same State. +/// +/// # Arguments +/// +/// * `src_vm`: the source [Vm] object. +/// * `dst_vm`: the target [Vm] object. +/// * `index`: the index on source [Vm]. +pub fn checked_push_value(src_vm: &Vm, dst_vm: &Vm, index: i32) -> u16 { + assert!(src_vm.as_ptr() == dst_vm.as_ptr()); + unsafe { lua_pushvalue(src_vm.as_ptr(), index) }; + 1 +} From 6cad919ebb797798107526d6785fb8ebacc3cad3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 17:21:54 +0200 Subject: [PATCH 410/527] Fixed warnings in build.rs --- core/build.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/build.rs b/core/build.rs index 98595dd..a1ad95d 100644 --- a/core/build.rs +++ b/core/build.rs @@ -46,16 +46,16 @@ const PATCH_LIST: &[&str] = &[ // pcall/xpcall but only from lua_pcall C API. ]; -fn apply_patches(luajit_build_path: &Path, summary_path: &Path) -> std::io::Result<()> { - let summary = Patch::new( +fn apply_patches(luajit_build_path: &Path, _summary_path: &Path) -> std::io::Result<()> { + let _summary = Patch::new( &Path::new("..").join("patch"), &Path::new("..").join("LuaJIT"), )? .apply_all(PATCH_LIST.iter().copied(), luajit_build_path)?; #[cfg(feature = "libs")] { - summary.write(summary_path)?; - println!("cargo:rustc-env=BP3D_LUA_PATCH_SUMMARY_FILE={}", summary_path.display()); + _summary.write(_summary_path)?; + println!("cargo:rustc-env=BP3D_LUA_PATCH_SUMMARY_FILE={}", _summary_path.display()); } Ok(()) } From 7ff5ec54ce5107bd757750d99ffc8b0430085f55 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 17:27:28 +0200 Subject: [PATCH 411/527] Removed util-function feature as now LuaFunction is required by AnyUserData --- core/Cargo.toml | 1 - core/src/util/mod.rs | 2 -- core/tests/test_vm_registry.rs | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index be02058..18d3dab 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -41,7 +41,6 @@ dynamic = [] module = ["libs-core", "bp3d-lua-codegen", "rustc_version"] root-vm = [] util-method = [] -util-function = [] util-namespace = [] util-thread = [] util-module = ["module", "bp3d-os/module"] diff --git a/core/src/util/mod.rs b/core/src/util/mod.rs index b5fa973..8952a04 100644 --- a/core/src/util/mod.rs +++ b/core/src/util/mod.rs @@ -27,7 +27,6 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod core; -#[cfg(feature = "util-function")] mod function; #[cfg(feature = "util-method")] mod method; @@ -38,7 +37,6 @@ mod namespace; #[cfg(feature = "util-thread")] pub mod thread; -#[cfg(feature = "util-function")] pub use function::LuaFunction; #[cfg(feature = "util-method")] pub use method::LuaMethod; diff --git a/core/tests/test_vm_registry.rs b/core/tests/test_vm_registry.rs index cf5d2ff..0f6b3ce 100644 --- a/core/tests/test_vm_registry.rs +++ b/core/tests/test_vm_registry.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#![cfg(all(feature = "root-vm", feature = "util-method", feature = "util-function"))] +#![cfg(all(feature = "root-vm", feature = "util-method"))] use std::ffi::CStr; use bp3d_lua::util::{LuaFunction, LuaMethod}; From 8f9be5baf0ef6befd57bc1f1d548f0188dfb2291 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 22:38:45 +0200 Subject: [PATCH 412/527] Large refactor: rename most value utilities --- codegen/src/gen/from_param.rs | 2 +- core/src/vm/registry/core.rs | 6 +++--- core/src/vm/registry/named.rs | 6 +++--- core/src/vm/table/core.rs | 14 +++++++------- core/src/vm/table/interface.rs | 4 ++-- core/src/vm/thread/interface.rs | 4 ++-- core/src/vm/thread/value.rs | 4 ++-- core/src/vm/userdata/any.rs | 6 +++--- core/src/vm/value/core.rs | 4 ++-- core/src/vm/value/function.rs | 8 ++++---- core/src/vm/value/util.rs | 10 +++++----- 11 files changed, 34 insertions(+), 34 deletions(-) diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index 4fb59f9..4da774d 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -162,7 +162,7 @@ impl Parser for FromParam { } fn try_from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Option { - bp3d_lua::vm::value::util::ensure_type_equals(vm, index, bp3d_lua::ffi::lua::Type::Table).ok()?; + bp3d_lua::vm::value::util::check_type_equals(vm, index, bp3d_lua::ffi::lua::Type::Table).ok()?; let mut top = vm.top(); let mut f = || -> Option { #(#try_from_params)* diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index ea61768..c5d4a72 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -29,7 +29,7 @@ use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; use crate::vm::registry::{FromIndex, Set, Value}; -use crate::vm::value::util::ensure_value_top; +use crate::vm::value::util::move_value_top; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; @@ -107,14 +107,14 @@ impl RawKey { impl FromIndex for RawKey { unsafe fn from_index(vm: &Vm, index: i32) -> Self { - ensure_value_top(vm, index); + move_value_top(vm, index); Self::from_top(vm) } } impl Set for RawKey { unsafe fn set(&self, vm: &Vm, index: i32) { - ensure_value_top(vm, index); + move_value_top(vm, index); lua_rawseti(vm.as_ptr(), REGISTRYINDEX, self.0); } } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 04ebc06..16ed625 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -29,7 +29,7 @@ use std::collections::BTreeSet; use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, State, Type, REGISTRYINDEX}; use crate::vm::registry::{Set, Value}; -use crate::vm::value::util::{ensure_type_equals, ensure_value_top}; +use crate::vm::value::util::{check_type_equals, move_value_top}; use crate::vm::Vm; use std::ffi::c_void; use std::marker::PhantomData; @@ -102,7 +102,7 @@ impl Set for RawKey { unsafe fn set(&self, vm: &Vm, index: i32) { check_register_key_unique(self); let l = vm.as_ptr(); - ensure_value_top(vm, index); + move_value_top(vm, index); rawsetp(l, REGISTRYINDEX, self.ptr); } } @@ -185,7 +185,7 @@ impl Key { pub fn push<'a>(&self, vm: &'a Vm) -> Option> { unsafe { self.raw.push(vm); - ensure_type_equals(vm, -1, Type::LightUserdata) + check_type_equals(vm, -1, Type::LightUserdata) .map(|_| T::from_registry(vm, -1)).ok() } } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 8bea72f..62a2951 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -30,7 +30,7 @@ use crate::ffi::ext::{lua_ext_tab_len, MSize}; use crate::ffi::lua::{lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, lua_setmetatable, lua_settable, lua_topointer}; use crate::vm::table::iter::Iter; use crate::vm::table::traits::{GetTable, SetTable}; -use crate::vm::value::util::{checked_get_metatable, ensure_single_into_lua}; +use crate::vm::value::util::{check_get_metatable, check_push_single}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -126,7 +126,7 @@ impl<'a> Table<'a> { } pub fn get_metatable(&self) -> Option
{ - checked_get_metatable(self.vm, self.index) + check_get_metatable(self.vm, self.index) } /// Returns the absolute index of this table on the Lua stack. @@ -159,7 +159,7 @@ impl<'a> Table<'a> { pub fn set(&mut self, key: impl SetTable, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - ensure_single_into_lua(self.vm, value)?; + check_push_single(self.vm, value)?; key.set_table(self.vm.as_ptr(), self.index)?; } Ok(()) @@ -170,7 +170,7 @@ impl<'a> Table<'a> { return Err(crate::vm::error::Error::MultiValue); } unsafe { - ensure_single_into_lua(self.vm, key)?; + check_push_single(self.vm, key)?; lua_gettable(self.vm.as_ptr(), self.index); T::from_lua(self.vm, -1) } @@ -178,8 +178,8 @@ impl<'a> Table<'a> { pub fn set_any(&mut self, key: impl IntoLua, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { - ensure_single_into_lua(self.vm, key)?; - ensure_single_into_lua(self.vm, value)?; + check_push_single(self.vm, key)?; + check_push_single(self.vm, value)?; lua_settable(self.vm.as_ptr(), self.index); } Ok(()) @@ -188,7 +188,7 @@ impl<'a> Table<'a> { pub fn push(&mut self, value: impl IntoLua) -> crate::vm::Result<()> { unsafe { let len = lua_objlen(self.vm.as_ptr(), self.index); - ensure_single_into_lua(self.vm, value)?; + check_push_single(self.vm, value)?; lua_rawseti(self.vm.as_ptr(), self.index, len as i32 + 1); } Ok(()) diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 3fe13d6..8a1ccf9 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -39,7 +39,7 @@ use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::table::Table; use crate::vm::util::LuaType; -use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::util::check_type_equals; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; @@ -67,7 +67,7 @@ impl<'a> FromLua<'a> for Table<'a> { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::Table)?; + check_type_equals(vm, index, Type::Table)?; Ok(unsafe { Table::from_raw(vm, vm.get_absolute_index(index)) }) } } diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 4adb265..ab6c484 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -34,7 +34,7 @@ use crate::vm::registry::{FromIndex, Set}; use crate::vm::thread::value::Thread; use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::util::check_type_equals; use crate::vm::Vm; impl<'a> FromLua<'a> for Thread<'a> { @@ -44,7 +44,7 @@ impl<'a> FromLua<'a> for Thread<'a> { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::Thread)?; + check_type_equals(vm, index, Type::Thread)?; unsafe { Ok(Thread::from_raw(vm, vm.get_absolute_index(index))) } } } diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index a0a7168..c09fd9d 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -30,7 +30,7 @@ use std::fmt::{Debug, Display}; use crate::ffi::lua::{lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type}; use crate::vm::thread::core; use crate::vm::value::types::Function; -use crate::vm::value::util::ensure_value_top; +use crate::vm::value::util::move_value_top; use crate::vm::Vm; /// Represents a thread object value on a lua stack. @@ -107,7 +107,7 @@ impl<'a> Thread<'a> { if self.thread.status() != ThreadStatus::Ok { return Err(crate::vm::error::Error::BadThreadState); } - ensure_value_top(self.vm, function.index()); + move_value_top(self.vm, function.index()); unsafe { lua_xmove(self.vm.as_ptr(), self.thread.as_ptr(), 1); } unsafe { assert_eq!(lua_type(self.thread.as_ptr(), -1), Type::Function); diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index bc9402d..327919f 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -37,7 +37,7 @@ use crate::util::core::AnyStr; use crate::util::LuaFunction; use crate::vm::table::Table; use crate::vm::value::types::Function; -use crate::vm::value::util::{checked_get_metatable, checked_push_value}; +use crate::vm::value::util::{check_get_metatable, check_push_value}; pub struct AnyUserData<'a> { vm: &'a Vm, @@ -144,7 +144,7 @@ impl<'a> AnyUserData<'a> { } pub fn get_metatable(&self) -> Option
{ - checked_get_metatable(self.vm, self.index) + check_get_metatable(self.vm, self.index) } pub fn get_type_name(&self) -> crate::vm::Result<&str> { @@ -194,6 +194,6 @@ impl<'a> FromLua<'a> for AnyUserData<'a> { unsafe impl IntoLua for &AnyUserData<'_> { #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - checked_push_value(self.vm, vm, self.index) + check_push_value(self.vm, vm, self.index) } } diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 6e035f4..68fabe7 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -31,7 +31,7 @@ use crate::ffi::laux::{luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tointegerx, lua_tolstring, lua_tonumber, lua_tonumberx, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; -use crate::vm::value::util::ensure_type_equals; +use crate::vm::value::util::check_type_equals; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::Vm; @@ -125,7 +125,7 @@ macro_rules! impl_from_lua { } fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::$expected)?; + check_type_equals(vm, index, Type::$expected)?; Ok(unsafe { $func(vm.as_ptr(), index) $($ret)* }) } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 8a711e8..7144c09 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -33,7 +33,7 @@ use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::util::LuaType; -use crate::vm::value::util::{checked_push_value, ensure_type_equals}; +use crate::vm::value::util::{check_push_value, check_type_equals}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -103,8 +103,8 @@ unsafe impl IntoParam for Function<'_> { unsafe impl IntoLua for Function<'_> { #[inline(always)] - fn into_lua(self, vm: &Vm) -> u16 { - checked_push_value(self.vm, vm, self.index) + fn into_lua(self, vm: &Vm) -> u16 { //TODO: This should be identical to Table + check_push_value(self.vm, vm, self.index) } } @@ -136,7 +136,7 @@ impl<'a> FromLua<'a> for Function<'a> { } fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { - ensure_type_equals(vm, index, Type::Function)?; + check_type_equals(vm, index, Type::Function)?; Ok(Function { vm, index: vm.get_absolute_index(index), diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 89c3193..61ffead 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -34,7 +34,7 @@ use crate::vm::Vm; /// Ensures the given lua value at index is of a specified type. #[inline(always)] -pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { +pub fn check_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Result<()> { let ty = unsafe { lua_type(vm.as_ptr(), index) }; if ty == expected { //FIXME: likely branch @@ -51,7 +51,7 @@ pub fn ensure_type_equals(vm: &Vm, index: i32, expected: Type) -> crate::vm::Res /// If the value at index is not at the top of the stack, this function moves it to the top and /// replaces the original index by a nil value. #[inline(always)] -pub fn ensure_value_top(vm: &Vm, index: i32) { +pub fn move_value_top(vm: &Vm, index: i32) { let index = vm.get_absolute_index(index); if index != vm.top() { let l = vm.as_ptr(); @@ -70,7 +70,7 @@ pub fn ensure_value_top(vm: &Vm, index: i32) { /// /// * `vm`: the vm to operate on. /// * `value`: the value to be placed on the lua stack. -pub fn ensure_single_into_lua(vm: &Vm, value: impl IntoLua) -> crate::vm::Result<()> { +pub fn check_push_single(vm: &Vm, value: impl IntoLua) -> crate::vm::Result<()> { let nums = value.into_lua(vm); if nums != 1 { // Clear the stack. @@ -91,7 +91,7 @@ pub fn ensure_single_into_lua(vm: &Vm, value: impl IntoLua) -> crate::vm::Result /// * `index`: the object index inside the `vm` [Vm]. /// /// returns: Option
-pub fn checked_get_metatable(vm: &Vm, index: i32) -> Option
{ +pub fn check_get_metatable(vm: &Vm, index: i32) -> Option
{ unsafe { lua_getmetatable(vm.as_ptr(), index) }; let ty = unsafe { lua_type(vm.as_ptr(), -1) }; if ty == Type::Table { @@ -112,7 +112,7 @@ pub fn checked_get_metatable(vm: &Vm, index: i32) -> Option
{ /// * `src_vm`: the source [Vm] object. /// * `dst_vm`: the target [Vm] object. /// * `index`: the index on source [Vm]. -pub fn checked_push_value(src_vm: &Vm, dst_vm: &Vm, index: i32) -> u16 { +pub fn check_push_value(src_vm: &Vm, dst_vm: &Vm, index: i32) -> u16 { assert!(src_vm.as_ptr() == dst_vm.as_ptr()); unsafe { lua_pushvalue(src_vm.as_ptr(), index) }; 1 From ad8e32d9e6f9b3080ab1f96bf42c8ace938f59c1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 22:52:44 +0200 Subject: [PATCH 413/527] Added new check_value_top utility to avoid some duplications --- core/src/vm/closure/arc.rs | 3 +-- core/src/vm/closure/rc.rs | 3 +-- core/src/vm/table/interface.rs | 12 ++++-------- core/src/vm/thread/interface.rs | 12 ++++-------- core/src/vm/value/function.rs | 6 +++--- core/src/vm/value/util.rs | 21 ++++++++++++++++++++- 6 files changed, 33 insertions(+), 24 deletions(-) diff --git a/core/src/vm/closure/arc.rs b/core/src/vm/closure/arc.rs index dc3eb50..39a66a2 100644 --- a/core/src/vm/closure/arc.rs +++ b/core/src/vm/closure/arc.rs @@ -53,8 +53,7 @@ impl Deref for Ref<'_, T> { impl<'a, T: Send + Sync> FromUpvalue<'a> for Ref<'a, T> { #[inline(always)] unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: RawPtr = FromUpvalue::from_upvalue(vm, index); - Ref(&*ptr.as_ptr()) + Ref(&*RawPtr::::from_upvalue(vm, index).as_ptr()) } } diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 8e692af..9693f8d 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -53,8 +53,7 @@ impl Deref for Ref<'_, T> { impl<'a, T> FromUpvalue<'a> for Ref<'a, T> { #[inline(always)] unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { - let ptr: RawPtr = FromUpvalue::from_upvalue(vm, index); - Ref(&*ptr.as_ptr()) + Ref(&*RawPtr::::from_upvalue(vm, index).as_ptr()) } } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 8a1ccf9..1332df7 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -30,7 +30,7 @@ use std::collections::{BTreeMap, HashMap}; use std::hash::Hash; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{ - lua_getfield, lua_gettop, lua_pushvalue, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, + lua_getfield, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, State, Type, }; use crate::util::core::{AnyStr, SimpleDrop}; @@ -39,7 +39,7 @@ use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::table::Table; use crate::vm::util::LuaType; -use crate::vm::value::util::check_type_equals; +use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; @@ -80,13 +80,9 @@ unsafe impl IntoParam for Table<'_> { } unsafe impl IntoLua for Table<'_> { + #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - assert!(self.vm.as_ptr() == vm.as_ptr()); - let top = unsafe { lua_gettop(vm.as_ptr()) }; - if top != self.index() { - unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; - } - 1 + check_value_top(self.vm, vm, self.index()) } } diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index ab6c484..ff44845 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -27,14 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_checktype; -use crate::ffi::lua::{lua_gettop, lua_pushvalue, Type}; +use crate::ffi::lua::Type; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::thread::value::Thread; use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; -use crate::vm::value::util::check_type_equals; +use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::Vm; impl<'a> FromLua<'a> for Thread<'a> { @@ -72,13 +72,9 @@ unsafe impl IntoParam for Thread<'_> { } unsafe impl IntoLua for Thread<'_> { + #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - assert!(self.vm.as_ptr() == vm.as_ptr()); - let top = unsafe { lua_gettop(vm.as_ptr()) }; - if top != self.index() { - unsafe { lua_pushvalue(vm.as_ptr(), self.index()) }; - } - 1 + check_value_top(self.vm, vm, self.index()) } } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 7144c09..3efa716 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -33,7 +33,7 @@ use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::util::LuaType; -use crate::vm::value::util::{check_push_value, check_type_equals}; +use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -103,8 +103,8 @@ unsafe impl IntoParam for Function<'_> { unsafe impl IntoLua for Function<'_> { #[inline(always)] - fn into_lua(self, vm: &Vm) -> u16 { //TODO: This should be identical to Table - check_push_value(self.vm, vm, self.index) + fn into_lua(self, vm: &Vm) -> u16 { + check_value_top(self.vm, vm, self.index()) } } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 61ffead..00b1d21 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_getmetatable, lua_pushnil, lua_pushvalue, lua_replace, lua_settop, lua_type, Type}; +use crate::ffi::lua::{lua_getmetatable, lua_gettop, lua_pushnil, lua_pushvalue, lua_replace, lua_settop, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::table::Table; use crate::vm::value::IntoLua; @@ -117,3 +117,22 @@ pub fn check_push_value(src_vm: &Vm, dst_vm: &Vm, index: i32) -> u16 { unsafe { lua_pushvalue(src_vm.as_ptr(), index) }; 1 } + +/// Pushes the value at `index` to the top of the stack if both [Vm] objects points to the exact +/// same State and if index is not already at the top of the stack. If `index` is already at the +/// top of the stack, this function doesn't perform any operations on the Lua stack identified by +/// `dst_vm`. +/// +/// # Arguments +/// +/// * `src_vm`: the source [Vm] object. +/// * `dst_vm`: the target [Vm] object. +/// * `index`: the index on source [Vm]. Must be absolute. +pub fn check_value_top(src_vm: &Vm, dst_vm: &Vm, index: i32) -> u16 { + assert!(src_vm.as_ptr() == dst_vm.as_ptr()); + let top = unsafe { lua_gettop(src_vm.as_ptr()) }; + if top != index { + unsafe { lua_pushvalue(src_vm.as_ptr(), index) }; + } + 1 +} From 4a06d847e729168ab9190654f3fbf1a4117397b1 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 23:10:40 +0200 Subject: [PATCH 414/527] Implemented codegen for IntoParam in function of IntoLua to avoid code duplication --- codegen/src/gen/into_lua.rs | 4 ++-- codegen/src/gen/into_param.rs | 32 +++++++++----------------------- 2 files changed, 11 insertions(+), 25 deletions(-) diff --git a/codegen/src/gen/into_lua.rs b/codegen/src/gen/into_lua.rs index cc01ba3..7e0a508 100644 --- a/codegen/src/gen/into_lua.rs +++ b/codegen/src/gen/into_lua.rs @@ -34,8 +34,8 @@ use quote::quote; use syn::{Generics, Index}; pub struct IntoLua { - name: Ident, - generics: Generics, + pub name: Ident, + pub generics: Generics, } impl IntoLua { diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index 6451912..70321d3 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -31,16 +31,14 @@ use crate::parser::structs::StructField; use crate::parser::Parser; use proc_macro2::{Ident, TokenStream}; use quote::quote; -use syn::{Generics, Index}; +use syn::Generics; +use crate::gen::IntoLua; -pub struct IntoParam { - name: Ident, - generics: Generics, -} +pub struct IntoParam(IntoLua); impl IntoParam { pub fn new(name: Ident, generics: Generics) -> Self { - Self { name, generics } + Self(IntoLua::new(name, generics)) } } @@ -49,19 +47,7 @@ impl Parser for IntoParam { type ParsedVariant = TokenStream; fn parse_field(&mut self, field: StructField) -> Self::ParsedField { - if field.unique_name_is_index { - let name_idx = Index::from(field.index); - // Table indices starts at 1 rather than 0 in Lua. - let index = (field.index + 1) as i32; - quote! { - tbl.set(#index, self.#name_idx).unwrap(); - } - } else { - let name = field.unique_name; - quote! { - tbl.set(bp3d_lua::c_stringify!(#name), self.#name).unwrap(); - } - } + self.0.parse_field(field) } fn parse_variant(&mut self, variant: EnumVariant) -> Self::ParsedVariant { @@ -84,8 +70,8 @@ impl Parser for IntoParam { } fn gen_struct(self, parsed: Vec) -> TokenStream { - let name = self.name; - let generics = self.generics; + let name = self.0.name; + let generics = self.0.generics; quote! { unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { fn into_param(self, vm: &bp3d_lua::vm::Vm) -> i32 { @@ -98,8 +84,8 @@ impl Parser for IntoParam { } fn gen_enum(self, parsed: Vec) -> TokenStream { - let name = self.name; - let generics = self.generics; + let name = self.0.name; + let generics = self.0.generics; quote! { unsafe impl #generics bp3d_lua::vm::function::IntoParam for #name #generics { fn into_param(self, vm: &bp3d_lua::vm::Vm) -> i32 { From 6d3c101eafea9598f08e64d9c0588180e1fc4dd8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 10 Aug 2025 23:21:41 +0200 Subject: [PATCH 415/527] Added new impl_registry_value macro to generate most of the registry glue code needed for complex registry types --- core/src/macro/mod.rs | 24 ++++++++++++++++++++++++ core/src/vm/registry/lua_ref.rs | 4 ++-- core/src/vm/table/interface.rs | 20 ++------------------ core/src/vm/thread/interface.rs | 17 ++--------------- core/src/vm/value/function.rs | 20 ++------------------ 5 files changed, 32 insertions(+), 53 deletions(-) diff --git a/core/src/macro/mod.rs b/core/src/macro/mod.rs index 0de5962..afc840c 100644 --- a/core/src/macro/mod.rs +++ b/core/src/macro/mod.rs @@ -49,6 +49,30 @@ macro_rules! impl_simple_registry_value_static { }; } +#[macro_export] +macro_rules! impl_registry_value { + ($reg_ty: ty => $value_ty: ident) => { + impl $crate::vm::registry::Value for $reg_ty { + type Value<'a> = $value_ty<'a>; + + #[inline(always)] + unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { + unsafe { $value_ty::from_lua_unchecked(vm, index) } + } + + #[inline(always)] + fn push_registry(value: Self::Value<'_>) -> R { + unsafe { R::from_index(value.vm, value.index()) } + } + + #[inline(always)] + unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { + key.set(value.vm, value.index()) + } + } + }; +} + /// This macro is unsafe and should not be used from safe code directly. It is intended as a /// building block for other macros. #[macro_export] diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 80c6d6b..7a11280 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -106,7 +106,7 @@ impl<'a, T: SimpleValue<'a>> LuaRef<'a, T> { /// the object on the stack at the given index is already of type `T`. If any of /// these assumptions are not respected, this function is UB. #[inline(always)] - pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + pub unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self { vm, index, @@ -138,7 +138,7 @@ impl super::Value for super::types::LuaRef type Value<'a> = LuaRef<'a, T::Value<'a>>; unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - LuaRef::from_raw(vm, vm.get_absolute_index(index)) + LuaRef::from_lua_unchecked(vm, vm.get_absolute_index(index)) } fn push_registry(value: Self::Value<'_>) -> R { diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index 1332df7..ea0950d 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -33,6 +33,7 @@ use crate::ffi::lua::{ lua_getfield, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, State, Type, }; +use crate::impl_registry_value; use crate::util::core::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; @@ -88,24 +89,7 @@ unsafe impl IntoLua for Table<'_> { impl LuaType for Table<'_> {} -impl crate::vm::registry::Value for crate::vm::registry::types::Table { - type Value<'a> = Table<'a>; - - #[inline(always)] - unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { Table::from_lua_unchecked(vm, index) } - } - - #[inline(always)] - fn push_registry(value: Self::Value<'_>) -> R { - unsafe { R::from_index(value.vm, value.index()) } - } - - #[inline(always)] - unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { - key.set(value.vm, value.index()) - } -} +impl_registry_value!(crate::vm::registry::types::Table => Table); impl GetTable for T { unsafe fn get_table(self, l: State, index: i32) -> crate::vm::Result<()> { diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index ff44845..3f45784 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -28,6 +28,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::Type; +use crate::impl_registry_value; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; @@ -78,18 +79,4 @@ unsafe impl IntoLua for Thread<'_> { } } -impl crate::vm::registry::Value for crate::vm::registry::types::Thread { - type Value<'a> = Thread<'a>; - - unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { Thread::from_lua_unchecked(vm, index) } - } - - fn push_registry(value: Self::Value<'_>) -> R { - unsafe { R::from_index(value.vm, value.index()) } - } - - unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { - key.set(value.vm, value.index()) - } -} +impl_registry_value!(crate::vm::registry::types::Thread => Thread); diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 3efa716..1bb2151 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -37,6 +37,7 @@ use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; +use crate::impl_registry_value; pub struct Function<'a> { vm: &'a Vm, @@ -144,21 +145,4 @@ impl<'a> FromLua<'a> for Function<'a> { } } -impl crate::vm::registry::Value for crate::vm::registry::types::Function { - type Value<'a> = Function<'a>; - - #[inline(always)] - unsafe fn from_registry(vm: &Vm, index: i32) -> Self::Value<'_> { - unsafe { Function::from_lua_unchecked(vm, index) } - } - - #[inline(always)] - fn push_registry(value: Self::Value<'_>) -> R { - unsafe { R::from_index(value.vm, value.index()) } - } - - #[inline(always)] - unsafe fn set_registry(key: &impl Set, value: Self::Value<'_>) { - key.set(value.vm, value.index()) - } -} +impl_registry_value!(crate::vm::registry::types::Function => Function); From f26f81be47fc8714360cc2bb4ff03ad9371f6e98 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 10:25:07 +0200 Subject: [PATCH 416/527] Added more log information --- core/src/libs/interface.rs | 2 ++ core/src/vm/core/vm.rs | 3 +++ core/src/vm/userdata/core.rs | 4 +++- shell/core/src/lua.rs | 8 ++------ 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index a0a44e7..0ce3a97 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_debug::info; use crate::util::Namespace; use crate::vm::Vm; @@ -35,6 +36,7 @@ pub trait Lib { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()>; fn register(&self, vm: &Vm) -> crate::vm::Result<()> { + info!("Adding lib '{}'...", Self::NAMESPACE); let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; self.load(&mut namespace)?; Ok(()) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index cd78b10..a12f6ce 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_debug::{info, warning}; use crate::ffi::lua::{lua_getfield, lua_gettop, lua_isyieldable, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; use crate::util::core::AnyStr; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; @@ -70,11 +71,13 @@ impl Vm { } pub fn register_userdata(&self, case: impl NameConvert) -> crate::vm::Result<()> { + info!("Adding userdata type {:?}", T::CLASS_NAME); let reg = unsafe { Registry::::new(self, case) }.map_err(Error::UserData)?; let res = T::register(®).map_err(Error::UserData); match res { Ok(_) => Ok(()), Err(e) => { + warning!("Failed to register userdata type {:?}: {}", T::CLASS_NAME, e); unsafe { lua_pushnil(self.l); lua_setfield(self.l, REGISTRYINDEX, T::CLASS_NAME.as_ptr()); diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 819c072..eae2bbf 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -188,6 +188,8 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { func: run_drop::, }); debug!({UD=?T::CLASS_NAME}, "Type registered with simple Drop"); + } else { + debug!({UD=?T::CLASS_NAME}, "Type does not need any drop behavior"); } self.has_gc.set(()).unwrap(); } @@ -254,7 +256,7 @@ impl AddGcMethod for &AddGcMethodAuto { impl Drop for Registry<'_, T, C> { fn drop(&mut self) { if std::mem::needs_drop::() && self.has_gc.get().is_none() { - warning!("No __gc method registered on a drop userdata type!"); + warning!("No __gc method registered on a userdata type which needs drop!"); // No __gc method found in object that needs it force add it before finishing it. self.add_gc_method(); } diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index dd33247..55be75d 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -112,24 +112,20 @@ impl Lua { let (signal, handle) = spawn_interruptible(move |vm| { let logger = DataOut::new(logger); let scheduler = Rc::new(SchedulerPtr::new()); - debug!("Loading VM libraries..."); - debug!("Loading OS library..."); + info!("Loading VM libraries..."); if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { error!("Failed to load OS library: {}", e); } - debug!("Loading string library..."); if let Err(e) = (libs::util::String, libs::util::Table, libs::util::Utf8).register(vm) { error!("Failed to load util library: {}", e); } - debug!("Loading lua library..."); if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { error!("Failed to load base library: {}", e); } - debug!("Loading bp3d-lua-shell::autocomplete library..."); + info!("Loading bp3d-lua-shell libraries..."); if let Err(e) = Autocomplete::new(logger.clone()).register(vm) { error!("Failed to register autocomplete library: {}", e); } - debug!("Loading bp3d-lua-shell::scheduler library..."); if let Err(e) = SchedulerApi::new(scheduler.clone()).register(vm) { error!("Failed to register scheduler library: {}", e); } From b90749bb0002c82aa95bfe6d819a4d628c0213a2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 11:23:03 +0200 Subject: [PATCH 417/527] Added support to add static fields and functions to userdata metatable --- core/src/macro/userdata.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index ab1313a..4e2574c 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -26,9 +26,19 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#[macro_export] +macro_rules! _impl_userdata_static { + ($registry: ident field $field_name: ident = $field_value: expr) => { + $registry.add_field($crate::c_stringify!($field_name), $field_value)?; + }; + ($registry: ident fn $function_name: ident) => { + $registry.add_field($crate::c_stringify!($function_name), $crate::vm::function::types::RFunction::wrap($function_name))?; + }; +} + #[macro_export] macro_rules! _impl_userdata { - ($obj_name: ident, $($fn_name: ident),*) => { + ($obj_name: ident, $($fn_name: ident),* { $([$($static_registry_tokens: tt)*];)* }) => { impl $crate::vm::userdata::UserData for $obj_name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); @@ -39,6 +49,9 @@ macro_rules! _impl_userdata { )* use $crate::vm::userdata::AddGcMethod; (&$crate::vm::userdata::core::AddGcMethodAuto::<$obj_name>::default()).add_gc_method(registry); + $( + $crate::_impl_userdata_static!(registry $($static_registry_tokens)*); + )* Ok(()) } } @@ -53,6 +66,8 @@ macro_rules! decl_userdata { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &$obj_name2: ident $($tokens: tt)*) -> $ret_ty: ty $code: block )* } + + $(static { $([$($static_registry_tokens: tt)*];)* })? ) => { $( $crate::decl_userdata_func! { @@ -60,7 +75,7 @@ macro_rules! decl_userdata { } )* - $crate::_impl_userdata!($obj_name, $($fn_name),*); + $crate::_impl_userdata!($obj_name, $($fn_name),* { $($([$($static_registry_tokens)*];)*)? }); unsafe impl $crate::vm::userdata::UserDataImmutable for $obj_name {} }; @@ -74,6 +89,8 @@ macro_rules! decl_userdata_mut { $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($($tokens: tt)*) -> $ret_ty: ty $code: block )* } + + $(static { $([$($static_registry_tokens: tt)*];)* })? ) => { $( $crate::decl_userdata_func! { @@ -81,6 +98,6 @@ macro_rules! decl_userdata_mut { } )* - $crate::_impl_userdata!($obj_name, $($fn_name),*); + $crate::_impl_userdata!($obj_name, $($fn_name),* { $($([$($static_registry_tokens)*];)*)? }); }; } From 4daebffaa0f5813b0ae1f0c6d974ed866ef5a8a2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 11:55:39 +0200 Subject: [PATCH 418/527] Fixed possible crash in Unknown value type --- core/src/vm/table/iter.rs | 4 ++-- core/src/vm/value/unknown.rs | 24 +++++++++++++++++++++++- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 021cfdc..7513989 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -73,8 +73,8 @@ impl<'a> Iterator for Iter<'a> { self.last_top = self.vm.top(); self.has_started = true; if ret != 0 { - let value = Unknown::from_vm(self.vm, -2); - let key = Unknown::from_vm(self.vm, -1); + let value = unsafe { Unknown::from_lua_unchecked(self.vm, self.last_top - 1) }; + let key = unsafe { Unknown::from_lua_unchecked(self.vm, self.last_top) }; Some((value, key)) } else { self.has_ended = true; diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index aeaf6c5..27d9398 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -30,6 +30,7 @@ use std::fmt::Debug; use crate::ffi::lua::{lua_replace, lua_type, Type}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::any::Any; +use crate::vm::value::util::check_value_top; use crate::vm::Vm; pub struct Unknown<'a> { @@ -44,7 +45,19 @@ impl Debug for Unknown<'_> { } impl<'a> Unknown<'a> { - pub fn from_vm(vm: &'a Vm, index: i32) -> Self { + /// Attempts to create an [Unknown] typed value from a specific index on the stack. + /// + /// # Arguments + /// + /// * `vm`: the [Vm] object this value is attached to. + /// * `index`: the index of the value on the stack. + /// + /// returns: Unknown + /// + /// # Safety + /// + /// The given stack index must be absolute, if not this is UB. + pub unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self { vm, index @@ -83,3 +96,12 @@ impl<'a> Unknown<'a> { self.index } } + +unsafe impl IntoLua for Unknown<'_> { + fn into_lua(self, vm: &Vm) -> u16 { + if self.ty() == Type::None { // None is not a value, do not operate the stack or UB. + return 0; // No value exists on the stack so IntoLua returns 0 values. + } + check_value_top(self.vm, vm, self.index) + } +} From db326895dfc550db4dc04662118f409702cc9597 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 11:55:56 +0200 Subject: [PATCH 419/527] Added new add_userdata function to Namespace --- core/src/util/namespace.rs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 80322e9..8512697 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -26,10 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::lua_settop; +use bp3d_debug::info; +use crate::ffi::lua::{lua_getfield, lua_settop, REGISTRYINDEX}; +use crate::util::core::AnyStr; use crate::vm::registry::core::Key; use crate::vm::table::Table; +use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::IntoLua; +use crate::vm::value::types::Unknown; use crate::vm::Vm; pub struct Namespace<'a> { @@ -88,6 +92,17 @@ impl<'a> Namespace<'a> { Ok(()) } + pub fn add_userdata(&mut self, name: impl AnyStr, case: impl NameConvert) -> crate::vm::Result<()> { + info!("Adding userdata type {:?} as {:?}", T::CLASS_NAME, name.to_str()?); + self.vm.register_userdata::(case)?; + let val = unsafe { + lua_getfield(self.vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + Unknown::from_lua_unchecked(self.vm, self.vm.top()) + }; + self.table.set(name, val)?; + Ok(()) + } + pub fn vm(&self) -> &'a Vm { self.vm } From cb145c9b7e738babd11b374db72addc22f2d7487 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 12:29:17 +0200 Subject: [PATCH 420/527] Rename from_lua_unchecked to from_raw --- core/src/util/namespace.rs | 2 +- core/src/vm/table/iter.rs | 4 ++-- core/src/vm/value/unknown.rs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 8512697..bd4f46a 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -97,7 +97,7 @@ impl<'a> Namespace<'a> { self.vm.register_userdata::(case)?; let val = unsafe { lua_getfield(self.vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); - Unknown::from_lua_unchecked(self.vm, self.vm.top()) + Unknown::from_raw(self.vm, self.vm.top()) }; self.table.set(name, val)?; Ok(()) diff --git a/core/src/vm/table/iter.rs b/core/src/vm/table/iter.rs index 7513989..6fd8bfe 100644 --- a/core/src/vm/table/iter.rs +++ b/core/src/vm/table/iter.rs @@ -73,8 +73,8 @@ impl<'a> Iterator for Iter<'a> { self.last_top = self.vm.top(); self.has_started = true; if ret != 0 { - let value = unsafe { Unknown::from_lua_unchecked(self.vm, self.last_top - 1) }; - let key = unsafe { Unknown::from_lua_unchecked(self.vm, self.last_top) }; + let value = unsafe { Unknown::from_raw(self.vm, self.last_top - 1) }; + let key = unsafe { Unknown::from_raw(self.vm, self.last_top) }; Some((value, key)) } else { self.has_ended = true; diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index 27d9398..bf44ce3 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -57,7 +57,7 @@ impl<'a> Unknown<'a> { /// # Safety /// /// The given stack index must be absolute, if not this is UB. - pub unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { Self { vm, index From 77f502bef0f375c70de1f6e2542dae6ffc60c87d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 13:31:14 +0200 Subject: [PATCH 421/527] Fixed new static field feature and added associated test --- core/src/libs/os/time.rs | 13 +++++++------ core/src/macro/userdata.rs | 2 +- core/src/util/namespace.rs | 4 +++- core/src/vm/userdata/core.rs | 19 +++++++++++++++---- core/tests/test_vm_libs.rs | 13 +++++++++++++ 5 files changed, 39 insertions(+), 12 deletions(-) diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index 1761d7f..c223189 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -105,6 +105,11 @@ decl_userdata! { Ok(table) } } + + static { + [fn new]; + [fn from_unix_timestamp]; + } } unsafe impl IntoParam for OffsetDateTime { @@ -177,14 +182,10 @@ impl Lib for Time { const NAMESPACE: &'static str = "bp3d.os.time"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace - .vm() - .register_userdata::(crate::vm::userdata::case::Camel)?; + namespace.add_userdata::(c"OffsetDateTime", crate::vm::userdata::case::Camel)?; namespace.add([ ("nowUtc", RFunction::wrap(now_utc)), - ("nowLocal", RFunction::wrap(now_local)), - ("fromUnixTimestamp", RFunction::wrap(from_unix_timestamp)), - ("new", RFunction::wrap(new)), + ("nowLocal", RFunction::wrap(now_local)) ]) } } diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index 4e2574c..86f79ef 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -32,7 +32,7 @@ macro_rules! _impl_userdata_static { $registry.add_field($crate::c_stringify!($field_name), $field_value)?; }; ($registry: ident fn $function_name: ident) => { - $registry.add_field($crate::c_stringify!($function_name), $crate::vm::function::types::RFunction::wrap($function_name))?; + $registry.add_static_field($crate::c_stringify!($function_name), $crate::vm::function::types::RFunction::wrap($function_name))?; }; } diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index bd4f46a..2544a77 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -27,7 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_debug::info; -use crate::ffi::lua::{lua_getfield, lua_settop, REGISTRYINDEX}; +use crate::ffi::lua::{lua_getfield, lua_replace, lua_settop, REGISTRYINDEX}; use crate::util::core::AnyStr; use crate::vm::registry::core::Key; use crate::vm::table::Table; @@ -97,6 +97,8 @@ impl<'a> Namespace<'a> { self.vm.register_userdata::(case)?; let val = unsafe { lua_getfield(self.vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + lua_getfield(self.vm.as_ptr(), -1, c"__static".as_ptr()); + lua_replace(self.vm.as_ptr(), -2); Unknown::from_raw(self.vm, self.vm.top()) }; self.table.set(name, val)?; diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index eae2bbf..74e7285 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -39,6 +39,7 @@ use bp3d_debug::{debug, warning}; use std::cell::OnceCell; use std::ffi::CStr; use std::marker::PhantomData; +use crate::vm::table::Table; #[derive(Copy, Clone)] pub struct Function { @@ -129,9 +130,11 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { if align_of::() > 8 { return Err(Error::Alignment(align_of::())); } + Table::new(vm); let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + // Pop the userdata metatable alongside its statics table from the stack. if res != 1 { - unsafe { lua_settop(vm.as_ptr(), -2) }; + unsafe { lua_settop(vm.as_ptr(), -3) }; return Err(Error::AlreadyRegistered(T::CLASS_NAME)); } let reg = Registry { @@ -145,7 +148,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { Ok(reg) } - pub fn add_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { + fn add_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { let num = value.into_lua(self.vm); if num > 1 { unsafe { lua_settop(self.vm.as_ptr(), -(num as i32) - 1) }; @@ -157,6 +160,12 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { Ok(()) } + pub fn add_static_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { + let mut static_table = unsafe { Table::from_raw(self.vm, -3) }; + static_table.set(&*self.case.name_convert(name), value).map_err(|_| Error::MultiValueField)?; + Ok(()) + } + pub fn add_method(&self, f: Function) { unsafe { lua_pushcclosure(self.vm.as_ptr(), f.func, 0); @@ -263,8 +272,10 @@ impl Drop for Registry<'_, T, C> { unsafe { lua_pushvalue(self.vm.as_ptr(), -1); lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); - // Pop the userdata metatable from the stack. - lua_settop(self.vm.as_ptr(), -2); + lua_pushvalue(self.vm.as_ptr(), -2); // Push the static table. + lua_setfield(self.vm.as_ptr(), -2, c"__static".as_ptr()); + // Pop the userdata metatable alongside its statics table from the stack. + lua_settop(self.vm.as_ptr(), -3); } } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 009c11c..35a0042 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -239,6 +239,19 @@ fn test_vm_lib_os_time() { .unwrap(); } +#[test] +fn test_vm_lib_os_time_2() { + let mut vm = RootVm::new(); + bp3d_lua::libs::os::Time.register(&mut vm).unwrap(); + Util.register(&mut vm).unwrap(); + vm.run_code::<()>(c" + local OffsetDateTime = bp3d.os.time.OffsetDateTime + local dt = OffsetDateTime.new({year = 1900, month = 12, day = 1}) + print(bp3d.util.table.tostring(dt:getDate())) + assert(bp3d.util.table.tostring(dt:getDate()) == 'month: 12\\nday: 1\\nyear: 1900') + ").unwrap(); +} + #[test] fn test_vm_lib_os_instant() { let mut vm = RootVm::new(); From 08390fc380fbedd7e1d0121c1b80477df03409d0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 13:46:37 +0200 Subject: [PATCH 422/527] Fixed OffsetDateTime.new test and added new userdata test --- core/tests/test_vm_libs.rs | 7 ++-- core/tests/test_vm_userdata.rs | 65 ++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+), 3 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 35a0042..2f47998 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -243,12 +243,13 @@ fn test_vm_lib_os_time() { fn test_vm_lib_os_time_2() { let mut vm = RootVm::new(); bp3d_lua::libs::os::Time.register(&mut vm).unwrap(); - Util.register(&mut vm).unwrap(); vm.run_code::<()>(c" local OffsetDateTime = bp3d.os.time.OffsetDateTime local dt = OffsetDateTime.new({year = 1900, month = 12, day = 1}) - print(bp3d.util.table.tostring(dt:getDate())) - assert(bp3d.util.table.tostring(dt:getDate()) == 'month: 12\\nday: 1\\nyear: 1900') + local date = dt:getDate() + assert(date.year == 1900) + assert(date.month == 12) + assert(date.day == 1) ").unwrap(); } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 1fbecd1..4ee689f 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -34,6 +34,7 @@ use bp3d_lua::vm::userdata::{AnyUserData, LuaDrop}; use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; use std::sync::Mutex; +use bp3d_lua::util::Namespace; static MUTEX: Mutex<()> = Mutex::new(()); @@ -84,6 +85,10 @@ decl_userdata! { MyInt(this.0 + other.0) } } + + static { + [fn new]; + } } #[derive(Debug)] @@ -133,6 +138,12 @@ decl_lib_func! { } } +decl_lib_func! { + fn new(i: i64) -> MyInt { + MyInt(i) + } +} + #[test] fn test_vm_userdata_forgot_reg() { let vm = RootVm::new(); @@ -172,6 +183,7 @@ fn test_vm_userdata_error_handling() { assert_eq!(msg, "userdata: __gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop"); assert_eq!(top, vm.top()); let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); + assert_eq!(top, vm.top()); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); assert_eq!( @@ -221,6 +233,41 @@ fn test_vm_userdata_base(vm: &Vm) { assert_eq!(top + 8, vm.top()); } +fn test_vm_userdata_base2(vm: &Vm) { + unsafe { + DROP_COUNTER = 0; + LUA_DROP_COUNTER = 0; + } + let top = vm.top(); + { + let mut namespace = Namespace::new(vm, "_G").unwrap(); + namespace.add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Snake).unwrap(); + } + assert_eq!(top, vm.top()); + vm.run_code::<()>(c"a = MyInt.new(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt.new(456)").unwrap(); + vm.run_code::<()>(c"c = MyInt.new(456)").unwrap(); + assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); + assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); + assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); + assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); + assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); + assert_eq!( + vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), + "579" + ); + assert_eq!( + vm.run_code::(c"return (a + b):tonumber()") + .unwrap(), + 579.0 + ); + assert_eq!( + vm.run_code::(c"return a.tonumber(b)").unwrap(), + 456.0 + ); + assert_eq!(top + 8, vm.top()); +} + #[test] fn test_vm_userdata() { let _guard = MUTEX.lock(); @@ -336,3 +383,21 @@ fn test_vm_userdata_display() { assert_eq!(s, "MyInt(123456)"); assert_eq!(vm.top(), top + 1); } + +#[test] +fn test_vm_userdata_statics() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base2(&vm); + vm.run_code::<()>(c" + MyInt.__gc = function() print(\"Lua has hacked Rust\") end + MyInt.__metatable = function() print(\"Lua has hacked Rust\") end + ").unwrap(); + let ud: AnyUserData = vm.get_global("a").unwrap(); + let s = ud.to_string(); + assert_eq!(s, "MyInt(123)"); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From 3080c1825219c4895e262ad55b853488a4d5048f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 13:55:05 +0200 Subject: [PATCH 423/527] Added more docs to avoid UB when using special return types to pass the metatable of UD to lua --- core/src/vm/value/any.rs | 2 +- core/src/vm/value/unknown.rs | 11 ++++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index de67f7a..66200c4 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -234,7 +234,7 @@ impl UncheckedAnyReturn { /// # Safety /// /// It is UB to run any operation which may alter the lua stack after constructing this - /// primitive. + /// primitive. Using this to return the metatable of an UserData is also UB. pub unsafe fn new(vm: &Vm, count: i32) -> Self { let top = vm.top(); if count > top as _ { diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index bf44ce3..cc16d0d 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -28,6 +28,7 @@ use std::fmt::Debug; use crate::ffi::lua::{lua_replace, lua_type, Type}; +use crate::vm::function::IntoParam; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::any::Any; use crate::vm::value::util::check_value_top; @@ -56,7 +57,8 @@ impl<'a> Unknown<'a> { /// /// # Safety /// - /// The given stack index must be absolute, if not this is UB. + /// The given stack index must be absolute, if not this is UB. Using this to return the + /// metatable of an UserData is also UB. pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { Self { vm, @@ -105,3 +107,10 @@ unsafe impl IntoLua for Unknown<'_> { check_value_top(self.vm, vm, self.index) } } + +unsafe impl IntoParam for Unknown<'_> { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ + } +} From c0e61854a697a23bc5fd057dfbc284a8821095e2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 14:29:14 +0200 Subject: [PATCH 424/527] Added support for adding custom logic to __index metamethod on userdata --- codegen/src/gen/mod.rs | 2 ++ core/src/lib.rs | 2 -- core/src/module/error.rs | 1 - core/src/module/mod.rs | 1 - core/src/util/module.rs | 3 --- core/src/vm/userdata/core.rs | 49 +++++++++++++++++++++++++++------- core/src/vm/userdata/error.rs | 1 - core/tests/test_vm_userdata.rs | 28 +++++++++++++++++-- 8 files changed, 67 insertions(+), 20 deletions(-) diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs index e7e16a1..008e8d0 100644 --- a/codegen/src/gen/mod.rs +++ b/codegen/src/gen/mod.rs @@ -26,6 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +//TODO: FromLua generator + mod from_param; mod into_param; mod lua_type; diff --git a/core/src/lib.rs b/core/src/lib.rs index f34fa31..2f1ff68 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -26,8 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -//TODO: Attempt to implement custom __index on userdata. - pub mod ffi; pub mod libs; mod r#macro; diff --git a/core/src/module/error.rs b/core/src/module/error.rs index 50a4381..ebb6b93 100644 --- a/core/src/module/error.rs +++ b/core/src/module/error.rs @@ -53,7 +53,6 @@ pub enum ErrorType { UserDataArgsEmpty = 1, UserDataMutViolation = 2, UserDataGc = 3, - UserDataIndex = 4, UserDataMetatable = 5, UserDataMultiValueField = 6, UserDataAlreadyRegistered = 7, diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index a641f5a..b799fa5 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -91,7 +91,6 @@ pub fn run_lua_register( error.static_string.data = e.as_ptr(); } crate::vm::userdata::Error::Gc => error.ty = error::ErrorType::UserDataGc, - crate::vm::userdata::Error::Index => error.ty = error::ErrorType::UserDataIndex, crate::vm::userdata::Error::Metatable => { error.ty = error::ErrorType::UserDataMetatable } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 80c694a..6e6d91b 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -105,9 +105,6 @@ unsafe fn convert_module_error_to_vm_error( crate::vm::userdata::Error::MutViolation(CStr::from_ptr(err.static_string.data)), ), ErrorType::UserDataGc => crate::vm::error::Error::UserData(crate::vm::userdata::Error::Gc), - ErrorType::UserDataIndex => { - crate::vm::error::Error::UserData(crate::vm::userdata::Error::Index) - } ErrorType::UserDataMetatable => { crate::vm::error::Error::UserData(crate::vm::userdata::Error::Metatable) } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 74e7285..1edf368 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -27,17 +27,14 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; -use crate::ffi::lua::{ - lua_pushcclosure, lua_pushnil, lua_pushvalue, lua_setfield, lua_setmetatable, lua_settop, - CFunction, State, -}; +use crate::ffi::lua::{lua_getmetatable, lua_pushcclosure, lua_pushlightuserdata, lua_pushnil, lua_pushvalue, lua_rawget, lua_setfield, lua_setmetatable, lua_settop, lua_touserdata, lua_type, CFunction, State, Type, GLOBALSINDEX}; use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, NameConvert, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; use crate::vm::Vm; use bp3d_debug::{debug, warning}; use std::cell::OnceCell; -use std::ffi::CStr; +use std::ffi::{c_void, CStr}; use std::marker::PhantomData; use crate::vm::table::Table; @@ -87,9 +84,6 @@ impl Builder { if self.f.name == c"__gc" { return Err(Error::Gc); } - if self.f.name == c"__index" { - return Err(Error::Index); - } if self.f.name == c"__metatable" { return Err(Error::Metatable); } @@ -109,6 +103,7 @@ pub struct Registry<'a, T: UserData, C: NameConvert> { vm: &'a Vm, useless: PhantomData, has_gc: OnceCell<()>, + has_index: OnceCell<()>, case: C, } @@ -141,6 +136,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { vm, useless: PhantomData, has_gc: OnceCell::new(), + has_index: OnceCell::new(), case, }; reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()) @@ -166,7 +162,37 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { Ok(()) } + fn add_index_metamethod(&self, f: CFunction) { + warning!({UD=?T::CLASS_NAME}, "Overriding __index on an UserData object may worsen performance of method calls by introducing an additional indirection on the __index metamethod"); + extern "C-unwind" fn __index(l: State) -> i32 { + unsafe { + lua_getmetatable(l, 1); + lua_pushvalue(l, 2); + lua_rawget(l, -2); + let ty = lua_type(l, -1); + if ty != Type::Nil { + return 1; + } + // Pop both the metatatble and the rawget value from the stack before running the + // custom __index method. + lua_settop(l, -3); + let f: CFunction = std::mem::transmute(lua_touserdata(l, GLOBALSINDEX - 1)); + f(l) + } + } + unsafe { + lua_pushlightuserdata(self.vm.as_ptr(), f as *mut c_void); + lua_pushcclosure(self.vm.as_ptr(), __index, 1); + lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); + } + self.has_index.set(()).unwrap(); + } + pub fn add_method(&self, f: Function) { + if &f.name.to_bytes() == b"__index" { + self.add_index_metamethod(f.func); + return; + } unsafe { lua_pushcclosure(self.vm.as_ptr(), f.func, 0); if &f.name.to_bytes()[..2] == b"__" { @@ -270,8 +296,11 @@ impl Drop for Registry<'_, T, C> { self.add_gc_method(); } unsafe { - lua_pushvalue(self.vm.as_ptr(), -1); - lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); + if self.has_index.get().is_none() { + lua_pushvalue(self.vm.as_ptr(), -1); + lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); + } + //TODO: The __static should only exist if at least 1 static was registered lua_pushvalue(self.vm.as_ptr(), -2); // Push the static table. lua_setfield(self.vm.as_ptr(), -2, c"__static".as_ptr()); // Pop the userdata metatable alongside its statics table from the stack. diff --git a/core/src/vm/userdata/error.rs b/core/src/vm/userdata/error.rs index 1b4dcba..a55343b 100644 --- a/core/src/vm/userdata/error.rs +++ b/core/src/vm/userdata/error.rs @@ -34,7 +34,6 @@ simple_error! { ArgsEmpty => "no arguments specified in function, please add at least one argument matching the type of self", MutViolation(&'static CStr) => "violation of the unique type rule for mutable method {:?}", Gc => "__gc meta-method is reserved for internal use, if you need Vm access in drop, please use LuaDrop", - Index => "__index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index", Metatable => "__metatable is set for security reasons and cannot be altered", MultiValueField => "multi-value fields are not supported", AlreadyRegistered(&'static CStr) => "class name {:?} has already been registered", diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 4ee689f..f2e5a8c 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -84,6 +84,14 @@ decl_userdata! { fn __add(this: &MyInt, other: &MyInt) -> MyInt { MyInt(this.0 + other.0) } + + fn __index(this: &MyInt, other: u8) -> Option { + if other == 0 { + Some(this.0 as _) + } else { + None + } + } } static { @@ -126,7 +134,7 @@ pub struct BrokenObject4; decl_userdata! { impl BrokenObject4 { - fn __index(this: &BrokenObject3) -> () { + fn __metatable(this: &BrokenObject3) -> () { println!("{:?}", this); } } @@ -194,7 +202,7 @@ fn test_vm_userdata_error_handling() { let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); - assert_eq!(msg, "userdata: __index meta-method is required to be surrendered to luaL_newmetatable, it is impossible to bind custom code to __index"); + assert_eq!(msg, "userdata: __metatable is set for security reasons and cannot be altered"); assert_eq!(top, vm.top()); } @@ -401,3 +409,19 @@ fn test_vm_userdata_statics() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_statics_2() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base2(&vm); + vm.run_code::<()>(c" + assert(a[0] == 123) + assert(a[1] == nil) + assert(b[0] == 456) + ").unwrap(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From 860922dc32a14f6957977a03a41a2ff8e5a85f17 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 18:36:51 +0200 Subject: [PATCH 425/527] Refactored and improved userdata static member system --- core/src/util/namespace.rs | 12 ++------ core/src/vm/userdata/core.rs | 10 ++++-- core/src/vm/userdata/mod.rs | 1 + core/src/vm/userdata/util.rs | 60 ++++++++++++++++++++++++++++++++++++ 4 files changed, 71 insertions(+), 12 deletions(-) create mode 100644 core/src/vm/userdata/util.rs diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 2544a77..d963f71 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -27,13 +27,13 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_debug::info; -use crate::ffi::lua::{lua_getfield, lua_replace, lua_settop, REGISTRYINDEX}; +use crate::ffi::lua::lua_settop; use crate::util::core::AnyStr; use crate::vm::registry::core::Key; use crate::vm::table::Table; use crate::vm::userdata::{NameConvert, UserData}; +use crate::vm::userdata::util::get_static_table; use crate::vm::value::IntoLua; -use crate::vm::value::types::Unknown; use crate::vm::Vm; pub struct Namespace<'a> { @@ -95,13 +95,7 @@ impl<'a> Namespace<'a> { pub fn add_userdata(&mut self, name: impl AnyStr, case: impl NameConvert) -> crate::vm::Result<()> { info!("Adding userdata type {:?} as {:?}", T::CLASS_NAME, name.to_str()?); self.vm.register_userdata::(case)?; - let val = unsafe { - lua_getfield(self.vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); - lua_getfield(self.vm.as_ptr(), -1, c"__static".as_ptr()); - lua_replace(self.vm.as_ptr(), -2); - Unknown::from_raw(self.vm, self.vm.top()) - }; - self.table.set(name, val)?; + self.table.set(name, get_static_table::(self.vm))?; Ok(()) } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 1edf368..36e3450 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -104,6 +104,7 @@ pub struct Registry<'a, T: UserData, C: NameConvert> { useless: PhantomData, has_gc: OnceCell<()>, has_index: OnceCell<()>, + has_static: OnceCell<()>, case: C, } @@ -137,6 +138,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { useless: PhantomData, has_gc: OnceCell::new(), has_index: OnceCell::new(), + has_static: OnceCell::new(), case, }; reg.add_field(c"__metatable", T::CLASS_NAME.to_str().unwrap_unchecked()) @@ -157,6 +159,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { } pub fn add_static_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { + let _ = self.has_static.set(()); let mut static_table = unsafe { Table::from_raw(self.vm, -3) }; static_table.set(&*self.case.name_convert(name), value).map_err(|_| Error::MultiValueField)?; Ok(()) @@ -300,9 +303,10 @@ impl Drop for Registry<'_, T, C> { lua_pushvalue(self.vm.as_ptr(), -1); lua_setfield(self.vm.as_ptr(), -2, c"__index".as_ptr()); } - //TODO: The __static should only exist if at least 1 static was registered - lua_pushvalue(self.vm.as_ptr(), -2); // Push the static table. - lua_setfield(self.vm.as_ptr(), -2, c"__static".as_ptr()); + if self.has_static.get().is_some() { + lua_pushvalue(self.vm.as_ptr(), -2); // Push the static table. + lua_setfield(self.vm.as_ptr(), -2, c"__static".as_ptr()); + } // Pop the userdata metatable alongside its statics table from the stack. lua_settop(self.vm.as_ptr(), -3); } diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs index d8f434a..9c83eb9 100644 --- a/core/src/vm/userdata/mod.rs +++ b/core/src/vm/userdata/mod.rs @@ -31,6 +31,7 @@ pub mod case; pub mod core; mod error; mod interface; +pub mod util; pub use any::AnyUserData; pub use error::Error; diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs new file mode 100644 index 0000000..07c3e81 --- /dev/null +++ b/core/src/vm/userdata/util.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::lua::{lua_getfield, lua_replace, lua_settop, lua_type, Type, REGISTRYINDEX}; +use crate::vm::table::Table; +use crate::vm::userdata::UserData; +use crate::vm::Vm; + +/// Returns the static table attached to the given UserData type. +/// +/// The static table contains all static fields and functions added to the type at registration +/// time. +/// +/// This function returns None when the UserData identified with type `T` does not have any static +/// members. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] the UserData type is attached to. +/// +/// returns: Option
+pub fn get_static_table(vm: &Vm) -> Option
{ + let val = unsafe { + lua_getfield(vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + lua_getfield(vm.as_ptr(), -1, c"__static".as_ptr()); + lua_replace(vm.as_ptr(), -2); + if lua_type(vm.as_ptr(), -1) == Type::Nil { + // No static table exists on the given userdata object, skip... + lua_settop(vm.as_ptr(), -2); + return None; + } + Table::from_raw(vm, vm.top()) + }; + Some(val) +} From 5c8e1ca4488ca99e48ce41163a3c564a6169f014 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 21:04:48 +0200 Subject: [PATCH 426/527] Updated type definitions for bp3d.os.time --- definitions/bp3d.os.lua | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/definitions/bp3d.os.lua b/definitions/bp3d.os.lua index f64c3f2..4312d12 100644 --- a/definitions/bp3d.os.lua +++ b/definitions/bp3d.os.lua @@ -51,12 +51,6 @@ bp3d.os.time.nowUtc = function() end --- @return OffsetDateTime bp3d.os.time.nowLocal = function() end ---- Constructs a new OffsetDateTime from a unix timestamp in seconds. ---- ---- @param timestamp integer the unix timestamp in seconds. ---- @return OffsetDateTime -bp3d.os.time.fromUnixTimestamp = function(timestamp) end - --- @class Offset --- @field hours integer --- @field minutes integer @@ -85,11 +79,19 @@ Date = {} --- @field offset Offset? RawComponents = {} +bp3d.os.time.OffsetDateTime = {} + --- Creates a new OffsetDateTime from its raw components. --- --- @param table RawComponents manual definition of a date time with an optional offset. --- @return OffsetDateTime -bp3d.os.time.new = function(table) end +bp3d.os.time.OffsetDateTime.new = function(table) end + +--- Constructs a new OffsetDateTime from a unix timestamp in seconds. +--- +--- @param timestamp integer the unix timestamp in seconds. +--- @return OffsetDateTime +bp3d.os.time.OffsetDateTime.fromUnixTimestamp = function(timestamp) end --- @class OffsetDateTime OffsetDateTime = {} From 351d09ba47ea57e6dd0e580bed2304e2f3225fab Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 21:06:16 +0200 Subject: [PATCH 427/527] Added initial version of new DebugRegistry --- core/src/libs/interface.rs | 2 + core/src/libs/lua/module.rs | 2 + core/src/libs/lua/require.rs | 2 + core/src/vm/core/debug.rs | 106 +++++++++++++++++++++++++++++++++++ core/src/vm/core/mod.rs | 1 + core/src/vm/core/vm.rs | 2 + 6 files changed, 115 insertions(+) create mode 100644 core/src/vm/core/debug.rs diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index 0ce3a97..9789184 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -28,6 +28,7 @@ use bp3d_debug::info; use crate::util::Namespace; +use crate::vm::core::debug::DebugRegistry; use crate::vm::Vm; pub trait Lib { @@ -37,6 +38,7 @@ pub trait Lib { fn register(&self, vm: &Vm) -> crate::vm::Result<()> { info!("Adding lib '{}'...", Self::NAMESPACE); + DebugRegistry::add::(vm); let mut namespace = Namespace::new(vm, Self::NAMESPACE)?; self.load(&mut namespace)?; Ok(()) diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index 16ddc2d..0db9be0 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -34,6 +34,7 @@ use crate::util::Namespace; use crate::vm::Vm; use bp3d_os::module::library::types::VirtualLibrary; use std::path::PathBuf; +use crate::vm::core::debug::DebugRegistry; pub struct Module { builtins: &'static [&'static VirtualLibrary], @@ -72,6 +73,7 @@ impl Lib for Module { } fn register(&self, vm: &Vm) -> crate::vm::Result<()> { + DebugRegistry::add::(vm); vm.register_userdata::(crate::vm::userdata::case::Camel)?; let mut manager = ModuleManager::new(self.builtins); for search_path in &self.search_paths { diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index ce17537..ede0790 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -35,6 +35,7 @@ use crate::vm::Vm; use bp3d_util::simple_error; use std::collections::HashMap; use std::sync::Mutex; +use crate::vm::core::debug::DebugRegistry; simple_error! { pub Error { @@ -91,6 +92,7 @@ impl Lib for Require { } fn register(&self, vm: &Vm) -> crate::vm::Result<()> { + DebugRegistry::add::(vm); let rc = Arc::from_rust(vm, self.0.clone()); let mut namespace = Namespace::new(vm, "bp3d.lua")?; namespace.add([("require", require(rc))]) diff --git a/core/src/vm/core/debug.rs b/core/src/vm/core/debug.rs new file mode 100644 index 0000000..6c63775 --- /dev/null +++ b/core/src/vm/core/debug.rs @@ -0,0 +1,106 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashMap; +use crate::vm::core::destructor::Pool; +use crate::vm::registry::named::Key; +use crate::vm::registry::types::LuaRef; +use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; +use crate::vm::userdata::UserData; +use crate::vm::value::types::RawPtr; +use crate::vm::Vm; + +pub trait DebugItemType { + const NAME: &'static str; +} + +pub struct Lib; +pub struct Class; + +impl DebugItemType for Lib { + const NAME: &'static str = "Lib"; +} + +impl DebugItemType for Class { + const NAME: &'static str = "Class"; +} + +pub trait DebugItem { + fn describe() -> String; +} + +#[cfg(feature="libs-core")] +impl DebugItem for T { + fn describe() -> String { + let lib_name = std::any::type_name::(); + let lib_namespace = T::NAMESPACE; + let desc = format!("{}: {}", lib_name, lib_namespace); + desc + } +} + +impl DebugItem for T { + fn describe() -> String { + T::CLASS_NAME.to_string_lossy().into() + } +} + +static DBG_REG: Key>> = Key::new("__debug_registry__"); + +pub struct DebugRegistry { + map: HashMap<&'static str, Vec> +} + +impl DebugRegistry { + fn add_internal + ?Sized, T: DebugItemType>(&mut self) { + self.map.entry(T::NAME).or_insert_with(Vec::new).push(D::describe()); + } + + fn list_internal(&self, _: T) -> Option> { + self.map.get(T::NAME).cloned() + } + + fn get(vm: &Vm) -> RawPtr { + if let None = DBG_REG.push(vm) { + let ptr = Pool::attach_send(vm, Box::new(DebugRegistry { map: HashMap::new() })); + DBG_REG.set(LiveLuaRef::new(vm, RawPtr::new(ptr))); + } + let ptr = DBG_REG.push(vm).unwrap().get(); + ptr + } + + pub fn add + ?Sized, T: DebugItemType>(vm: &Vm) { + let ptr = Self::get(vm); + unsafe { (*ptr.as_mut_ptr()).add_internal::() }; + } + + pub fn list(vm: &Vm, ty: impl DebugItemType) -> Option> { + let ptr = Self::get(vm); + unsafe { (*ptr.as_ptr()).list_internal(ty) } + } +} diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index c00420a..3f5fd6d 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -33,6 +33,7 @@ pub mod load; #[cfg(feature = "root-vm")] mod root_vm; pub mod util; +pub mod debug; mod vm; #[cfg(feature = "root-vm")] diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index a12f6ce..39f0f44 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -31,6 +31,7 @@ use crate::ffi::lua::{lua_getfield, lua_gettop, lua_isyieldable, lua_pushnil, lu use crate::util::core::AnyStr; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::core::{Load, LoadString}; +use crate::vm::core::debug::DebugRegistry; use crate::vm::error::Error; use crate::vm::thread::core::Thread; use crate::vm::userdata::core::Registry; @@ -72,6 +73,7 @@ impl Vm { pub fn register_userdata(&self, case: impl NameConvert) -> crate::vm::Result<()> { info!("Adding userdata type {:?}", T::CLASS_NAME); + DebugRegistry::add::(self); let reg = unsafe { Registry::::new(self, case) }.map_err(Error::UserData)?; let res = T::register(®).map_err(Error::UserData); match res { From fd2bf1ad60ec8e557b82c5b520b611968fb9996a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 21:06:34 +0200 Subject: [PATCH 428/527] Added initial version of debug library --- core/src/libs/lua/debug.rs | 90 ++++++++++++++++++++++++++++++++++++++ core/src/libs/lua/mod.rs | 2 + 2 files changed, 92 insertions(+) create mode 100644 core/src/libs/lua/debug.rs diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs new file mode 100644 index 0000000..73c2792 --- /dev/null +++ b/core/src/libs/lua/debug.rs @@ -0,0 +1,90 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::libs::Lib; +use crate::util::Namespace; +use crate::vm::core::debug::DebugRegistry; +use crate::vm::core::iter::start; +use crate::vm::function::types::RFunction; +use crate::vm::table::Table; +use crate::vm::value::any::Any; + +decl_lib_func! { + fn dump_stack(vm: &Vm, start_index: i32) -> crate::vm::Result
{ + let mut tbl = Table::new(vm); + let iter = start::(vm, start_index); + for value in iter { + match value { + Ok(v) => tbl.push(v.to_string())?, + Err(e) => tbl.push(e.to_string())?, + } + } + Ok(tbl) + } +} + +decl_lib_func! { + fn dump_libs(vm: &Vm) -> crate::vm::Result
{ + let mut tbl = Table::new(vm); + if let Some(vv) = DebugRegistry::list(vm, crate::vm::core::debug::Lib) { + for v in vv { + tbl.push(v)?; + } + } + Ok(tbl) + } +} + +decl_lib_func! { + fn dump_classes(vm: &Vm) -> crate::vm::Result
{ + let mut tbl = Table::new(vm); + if let Some(vv) = DebugRegistry::list(vm, crate::vm::core::debug::Class) { + for v in vv { + tbl.push(v)?; + } + } + Ok(tbl) + } +} + +//TODO: debugger to dump userdata metatable, static table and namespace content + +pub struct Debug; + +impl Lib for Debug { + const NAMESPACE: &'static str = "bp3d.lua.debug"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("dumpStack", RFunction::wrap(dump_stack)), + ("dumpLibs", RFunction::wrap(dump_libs)), + ("dumpClasses", RFunction::wrap(dump_classes)) + ]) + } +} diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index abc3827..04dd6c8 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -34,11 +34,13 @@ pub mod require; #[cfg(feature = "util-module")] mod module; +mod debug; pub use base::Base; pub use call::Call; pub use load::Load; pub use require::Require; +pub use debug::Debug; pub use options::Lua; From c3b9e2340d53e012c5ddb35fb48b5f6608c4b4e0 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 21:07:03 +0200 Subject: [PATCH 429/527] Added type definitions for debug lib --- definitions/bp3d.lua.debug.lua | 49 ++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 definitions/bp3d.lua.debug.lua diff --git a/definitions/bp3d.lua.debug.lua b/definitions/bp3d.lua.debug.lua new file mode 100644 index 0000000..cd8e5a8 --- /dev/null +++ b/definitions/bp3d.lua.debug.lua @@ -0,0 +1,49 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.lua.debug + +bp3d = {} +bp3d.lua = {} +bp3d.lua.debug = {} + +--- Dump the content of the Lua stack starting at a given index. +--- +--- @param startIndex number the index at which to start dumping the stack. +--- @return [string] whatever list of all variables on the stack. +bp3d.lua.debug.dumpStack = function(startIndex) end + +--- Dump all libs registered within the current Lua VM. +--- +--- @return [string] +bp3d.lua.debug.dumpLibs = function() end + +--- Dump all UserData classes registered within the current Lua VM. +--- +--- @return [string] +bp3d.lua.debug.dumpClasses = function() end From 1bc9c9abe2d0349b8d00a31d328c06ddb0637727 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 21:54:34 +0200 Subject: [PATCH 430/527] Fixed various bugs in Named registry key --- core/src/vm/registry/named.rs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 16ed625..44e00e6 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::collections::BTreeSet; -use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, State, Type, REGISTRYINDEX}; +use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; use crate::vm::registry::{Set, Value}; -use crate::vm::value::util::{check_type_equals, move_value_top}; +use crate::vm::value::util::move_value_top; use crate::vm::Vm; use std::ffi::c_void; use std::marker::PhantomData; @@ -185,8 +185,11 @@ impl Key { pub fn push<'a>(&self, vm: &'a Vm) -> Option> { unsafe { self.raw.push(vm); - check_type_equals(vm, -1, Type::LightUserdata) - .map(|_| T::from_registry(vm, -1)).ok() + if lua_type(vm.as_ptr(), -1) == Type::Nil { + lua_settop(vm.as_ptr(), -2); // Pop the nil from the stack. + return None; + } + Some(T::from_registry(vm, -1)) } } From 9c985787f406fdce0332b40408b095a781a5ce3f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 12 Aug 2025 22:06:47 +0200 Subject: [PATCH 431/527] Added test for new debug lib --- core/tests/test_vm_libs.rs | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 2f47998..3ac3d04 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -62,8 +62,7 @@ fn test_vm_lib_lua() { .into_runtime() .unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); - vm.run_code::<()>( - c" + vm.run_code::<()>(c" local function test() bp3d.lua.require \"not.existing.file\" end @@ -71,8 +70,7 @@ fn test_vm_lib_lua() { assert(not flag) print(err) assert(err ~= '') - ", - ) + ") .unwrap(); let err = vm .run_code::<()>(c"MODULES:load('broken', 'broken2')") @@ -284,21 +282,28 @@ fn test_vm_lib_os() { ) .unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); - vm.run_code::<()>( - c" + vm.run_code::<()>(c" local now = os.clock() assert((clock - now) < 0.1) - ", - ) - .unwrap(); - let s = vm - .run_code::<&str>( - c" + ").unwrap(); + let s = vm.run_code::<&str>(c" return os.date('!%H:%M:%S') - ", - ) - .unwrap(); + ").unwrap(); assert!(s.contains(":")); assert!(!s.contains("[")); assert!(!s.contains("]")); } + +#[test] +fn test_vm_lib_debug() { + let mut vm = RootVm::new(); + bp3d_lua::libs::lua::Debug.register(&mut vm).unwrap(); + vm.run_code::<()>(c" + local debug = bp3d.lua.debug + local libs = debug.dumpLibs(); + assert(#libs == 1) + assert(libs[1] == 'bp3d_lua::libs::lua::debug::Debug: bp3d.lua.debug') + local classes = debug.dumpClasses(); + assert(#classes == 0) + ").unwrap(); +} From 2824b515c00f8f9d80735ad23c256558a87283c9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:07:19 +0200 Subject: [PATCH 432/527] Added new ImmutableTable type to avoid possible UB when mutating UserData metatables --- core/src/util/namespace.rs | 3 +- core/src/vm/table/core.rs | 3 +- core/src/vm/table/immutable.rs | 134 +++++++++++++++++++++++++++++++++ core/src/vm/table/mod.rs | 3 + core/src/vm/userdata/any.rs | 6 +- core/src/vm/userdata/util.rs | 52 ++++++++++++- core/src/vm/value/util.rs | 7 +- 7 files changed, 198 insertions(+), 10 deletions(-) create mode 100644 core/src/vm/table/immutable.rs diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index d963f71..64e0739 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -95,7 +95,8 @@ impl<'a> Namespace<'a> { pub fn add_userdata(&mut self, name: impl AnyStr, case: impl NameConvert) -> crate::vm::Result<()> { info!("Adding userdata type {:?} as {:?}", T::CLASS_NAME, name.to_str()?); self.vm.register_userdata::(case)?; - self.table.set(name, get_static_table::(self.vm))?; + self.table.set(name, get_static_table::(self.vm) + .map(|v| unsafe { v.to_table() }))?; Ok(()) } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 62a2951..4e0db43 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -95,6 +95,7 @@ impl<'a> Table<'a> { } /// Returns a unique identifier to that table across the Vm it is attached to. + #[inline(always)] pub fn uid(&self) -> usize { unsafe { lua_topointer(self.vm.as_ptr(), self.index) as _ } } @@ -126,7 +127,7 @@ impl<'a> Table<'a> { } pub fn get_metatable(&self) -> Option
{ - check_get_metatable(self.vm, self.index) + unsafe { check_get_metatable(self.vm, self.index) } } /// Returns the absolute index of this table on the Lua stack. diff --git a/core/src/vm/table/immutable.rs b/core/src/vm/table/immutable.rs new file mode 100644 index 0000000..b21c0ab --- /dev/null +++ b/core/src/vm/table/immutable.rs @@ -0,0 +1,134 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fmt::Display; +use crate::vm::table::iter::Iter; +use crate::vm::table::Table; +use crate::vm::table::traits::GetTable; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ImmutableTable<'a>(Table<'a>); + +impl Display for ImmutableTable<'_> { + #[inline(always)] + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + Table::fmt(&self.0, f) + } +} + +impl<'a> From> for ImmutableTable<'a> { + #[inline(always)] + fn from(value: Table<'a>) -> Self { + Self(value) + } +} + +impl<'a> ImmutableTable<'a> { + /// Creates a table from a raw Vm and index. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a table and is absolute. If index is not absolute then + /// using the produced table is UB. If the index points to any other type then using the produced + /// table is also UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self(Table::from_raw(vm, index)) + } + + /// Returns a unique identifier to that table across the Vm it is attached to. + #[inline(always)] + pub fn uid(&self) -> usize { + self.0.uid() + } + + #[inline(always)] + pub fn len(&self) -> usize { + self.0.len() + } + + #[inline(always)] + pub fn get_metatable(&self) -> Option { + self.0.get_metatable().map(ImmutableTable) + } + + /// Returns the absolute index of this table on the Lua stack. + #[inline(always)] + pub fn index(&self) -> i32 { + self.0.index() + } + + #[inline(always)] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Creates a new iterator for this table. + /// + /// This function borrows mutably to avoid messing up the Lua stack while iterating. + #[inline(always)] + pub fn iter(&mut self) -> Iter { + self.0.iter() + } + + #[inline(always)] + pub fn get<'b, T: FromLua<'b>>(&'b self, key: impl GetTable) -> crate::vm::Result { + self.0.get(key) + } + + #[inline(always)] + pub fn get_any<'b, T: FromLua<'b>>(&'b self, key: impl IntoLua) -> crate::vm::Result { + self.0.get_any(key) + } + + #[inline(always)] + pub fn collect>(self) -> crate::vm::Result { + self.0.collect() + } + + /// Returns the underlying Lua Table. + /// + /// # Safety + /// + /// This function violates the contract of this type and is only intended to be used in order to + /// send the table to Lua. Note that you must not use this to expose a UserData metatable as + /// otherwise Lua could override functions like __gc and cause all kinds of UB. + #[inline(always)] + pub unsafe fn to_table(self) -> Table<'a> { + self.0 + } +} diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 41bedce..26dc601 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -30,5 +30,8 @@ mod core; mod interface; mod iter; pub mod traits; +mod immutable; pub use core::Table; + +pub use immutable::ImmutableTable; diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 327919f..5708d81 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -35,7 +35,7 @@ use crate::vm::Vm; use std::fmt::{Debug, Display}; use crate::util::core::AnyStr; use crate::util::LuaFunction; -use crate::vm::table::Table; +use crate::vm::table::ImmutableTable; use crate::vm::value::types::Function; use crate::vm::value::util::{check_get_metatable, check_push_value}; @@ -143,8 +143,8 @@ impl<'a> AnyUserData<'a> { Ok(unsafe { &mut *this_ptr }) } - pub fn get_metatable(&self) -> Option
{ - check_get_metatable(self.vm, self.index) + pub fn get_metatable(&self) -> Option { + unsafe { check_get_metatable(self.vm, self.index).map(ImmutableTable::from) } } pub fn get_type_name(&self) -> crate::vm::Result<&str> { diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs index 07c3e81..568df1c 100644 --- a/core/src/vm/userdata/util.rs +++ b/core/src/vm/userdata/util.rs @@ -26,8 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::CStr; use crate::ffi::lua::{lua_getfield, lua_replace, lua_settop, lua_type, Type, REGISTRYINDEX}; -use crate::vm::table::Table; +use crate::vm::table::ImmutableTable; use crate::vm::userdata::UserData; use crate::vm::Vm; @@ -44,9 +45,27 @@ use crate::vm::Vm; /// * `vm`: the [Vm] the UserData type is attached to. /// /// returns: Option
-pub fn get_static_table(vm: &Vm) -> Option
{ +pub fn get_static_table(vm: &Vm) -> Option { + get_static_table_by_name(vm, T::CLASS_NAME) +} + +/// Returns the static table attached to the given UserData type. +/// +/// The static table contains all static fields and functions added to the type at registration +/// time. +/// +/// This function returns None when the UserData identified with type `T` does not have any static +/// members. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] the UserData type is attached to. +/// * `name`: the name of the UserData type. +/// +/// returns: Option +pub fn get_static_table_by_name<'a>(vm: &'a Vm, name: &CStr) -> Option> { let val = unsafe { - lua_getfield(vm.as_ptr(), REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + lua_getfield(vm.as_ptr(), REGISTRYINDEX, name.as_ptr()); lua_getfield(vm.as_ptr(), -1, c"__static".as_ptr()); lua_replace(vm.as_ptr(), -2); if lua_type(vm.as_ptr(), -1) == Type::Nil { @@ -54,7 +73,32 @@ pub fn get_static_table(vm: &Vm) -> Option
{ lua_settop(vm.as_ptr(), -2); return None; } - Table::from_raw(vm, vm.top()) + ImmutableTable::from_raw(vm, vm.top()) + }; + Some(val) +} + +pub fn get_metatable(vm: &Vm) -> Option { + get_metatable_by_name(vm, T::CLASS_NAME) +} + +/// Returns the metatable attached to the given UserData type. +/// +/// # Arguments +/// +/// * `vm`: the [Vm] the UserData type is attached to. +/// * `name`: the name of the UserData type. +/// +/// returns: Option +pub fn get_metatable_by_name<'a>(vm: &'a Vm, name: &CStr) -> Option> { + let val = unsafe { + lua_getfield(vm.as_ptr(), REGISTRYINDEX, name.as_ptr()); + if lua_type(vm.as_ptr(), -1) == Type::Nil { + // No metatable exists on the given userdata object, skip... + lua_settop(vm.as_ptr(), -2); + return None; + } + ImmutableTable::from_raw(vm, vm.top()) }; Some(val) } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index 00b1d21..f7e546b 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -91,7 +91,12 @@ pub fn check_push_single(vm: &Vm, value: impl IntoLua) -> crate::vm::Result<()> /// * `index`: the object index inside the `vm` [Vm]. /// /// returns: Option
-pub fn check_get_metatable(vm: &Vm, index: i32) -> Option
{ +/// +/// # Safety +/// +/// This should never be used to pass the metatable of a [userdata](crate::vm::userdata) type to +/// Lua or even modify the returned metatable. If any of these are not maintained this is UB. +pub unsafe fn check_get_metatable(vm: &Vm, index: i32) -> Option
{ unsafe { lua_getmetatable(vm.as_ptr(), index) }; let ty = unsafe { lua_type(vm.as_ptr(), -1) }; if ty == Type::Table { From 259b911f3f2e32e21202f1ec863f4dd4b570fce3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:19:49 +0200 Subject: [PATCH 433/527] Added support for dumpMetaTable and dumpStaticTable to debug lib --- core/src/libs/lua/debug.rs | 34 ++++++++++++++++++++++++++++++++-- core/src/vm/userdata/util.rs | 8 ++++++++ core/tests/test_vm_libs.rs | 2 ++ 3 files changed, 42 insertions(+), 2 deletions(-) diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs index 73c2792..6bd420e 100644 --- a/core/src/libs/lua/debug.rs +++ b/core/src/libs/lua/debug.rs @@ -26,13 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::CString; +use std::str::FromStr; use crate::decl_lib_func; use crate::libs::Lib; use crate::util::Namespace; use crate::vm::core::debug::DebugRegistry; use crate::vm::core::iter::start; +use crate::vm::error::Error; use crate::vm::function::types::RFunction; use crate::vm::table::Table; +use crate::vm::userdata::util::{get_metatable_by_name, get_static_table_by_name}; use crate::vm::value::any::Any; decl_lib_func! { @@ -73,7 +77,31 @@ decl_lib_func! { } } -//TODO: debugger to dump userdata metatable, static table and namespace content +decl_lib_func! { + fn dump_static_table<'a>(vm: &Vm, class: &str) -> crate::vm::Result> { + let str = CString::from_str(class).map_err(|_| Error::Null)?; + let mut tbl = get_static_table_by_name(vm, &str).ok_or(Error::Unknown)?; + let mut out = Table::new(vm); + for (k, _) in tbl.iter() { + let name = k.get::<&str>()?; + out.push(name)?; + } + Ok(out) + } +} + +decl_lib_func! { + fn dump_meta_table<'a>(vm: &Vm, class: &str) -> crate::vm::Result> { + let str = CString::from_str(class).map_err(|_| Error::Null)?; + let mut tbl = get_metatable_by_name(vm, &str).ok_or(Error::Unknown)?; + let mut out = Table::new(vm); + for (k, _) in tbl.iter() { + let name = k.get::<&str>()?; + out.push(name)?; + } + Ok(out) + } +} pub struct Debug; @@ -84,7 +112,9 @@ impl Lib for Debug { namespace.add([ ("dumpStack", RFunction::wrap(dump_stack)), ("dumpLibs", RFunction::wrap(dump_libs)), - ("dumpClasses", RFunction::wrap(dump_classes)) + ("dumpClasses", RFunction::wrap(dump_classes)), + ("dumpStaticTable", RFunction::wrap(dump_static_table)), + ("dumpMetaTable", RFunction::wrap(dump_meta_table)), ]) } } diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs index 568df1c..d441872 100644 --- a/core/src/vm/userdata/util.rs +++ b/core/src/vm/userdata/util.rs @@ -78,6 +78,14 @@ pub fn get_static_table_by_name<'a>(vm: &'a Vm, name: &CStr) -> Option pub fn get_metatable(vm: &Vm) -> Option { get_metatable_by_name(vm, T::CLASS_NAME) } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 3ac3d04..588375d 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -305,5 +305,7 @@ fn test_vm_lib_debug() { assert(libs[1] == 'bp3d_lua::libs::lua::debug::Debug: bp3d.lua.debug') local classes = debug.dumpClasses(); assert(#classes == 0) + local stack = debug.dumpStack(0); + assert(#stack > 0) ").unwrap(); } From 78122548bde07f48637953339aa4b79e22bb420c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:20:52 +0200 Subject: [PATCH 434/527] Updated type definitions --- definitions/bp3d.lua.debug.lua | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/definitions/bp3d.lua.debug.lua b/definitions/bp3d.lua.debug.lua index cd8e5a8..fc10fdf 100644 --- a/definitions/bp3d.lua.debug.lua +++ b/definitions/bp3d.lua.debug.lua @@ -47,3 +47,15 @@ bp3d.lua.debug.dumpLibs = function() end --- --- @return [string] bp3d.lua.debug.dumpClasses = function() end + +--- Dump the static members of the given UserData type.. +--- +--- @param className string the UserData class name to dump. +--- @return [string] +bp3d.lua.debug.dumpStaticTable = function(className) end + +--- Dump the instance members of the given UserData type.. +--- +--- @param className string the UserData class name to dump. +--- @return [string] +bp3d.lua.debug.dumpMetaTable = function(className) end From 8fbcc5d61c6775beb16aaf976e5b2146aa0625d8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:22:38 +0200 Subject: [PATCH 435/527] Updated autocomplete shell library --- shell/core/src/autocomplete.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 93df640..4b95e7b 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -31,7 +31,7 @@ use bp3d_lua::decl_closure; use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::rc::{Rc, Shared}; -use bp3d_lua::vm::table::Table; +use bp3d_lua::vm::table::ImmutableTable; use bp3d_lua::vm::value::any::Any; use crate::data::DataOut; @@ -74,7 +74,7 @@ fn get_capacity(val: &Any) -> usize { } } -fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: Table, metatables: bool) -> bp3d_lua::vm::Result<()> { +fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: ImmutableTable, metatables: bool) -> bp3d_lua::vm::Result<()> { if set.contains(&value.uid()) { return Ok(()); } @@ -110,7 +110,7 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: Any, metatables: bool) -> bp3d_lua::vm::Result<()> { match value { - Any::Table(v) => list_table_completions(set, path, root, v, metatables), + Any::Table(v) => list_table_completions(set, path, root, v.into(), metatables), Any::UserData(v) => { if let Some(tbl) = v.get_metatable() { // We assume userdata have a single metatable (following current bp3d-lua pattern). From c85fb0d8ab45a001fcc04780a341a9a4414b1468 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:33:42 +0200 Subject: [PATCH 436/527] Fixed possible bug in dump_static_table when no static table exists for a given class --- core/src/libs/lua/debug.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs index 6bd420e..4f909ef 100644 --- a/core/src/libs/lua/debug.rs +++ b/core/src/libs/lua/debug.rs @@ -78,15 +78,18 @@ decl_lib_func! { } decl_lib_func! { - fn dump_static_table<'a>(vm: &Vm, class: &str) -> crate::vm::Result> { + fn dump_static_table<'a>(vm: &Vm, class: &str) -> crate::vm::Result>> { let str = CString::from_str(class).map_err(|_| Error::Null)?; - let mut tbl = get_static_table_by_name(vm, &str).ok_or(Error::Unknown)?; + let mut tbl = match get_static_table_by_name(vm, &str) { + Some(tbl) => tbl, + None => return Ok(None), + }; let mut out = Table::new(vm); for (k, _) in tbl.iter() { let name = k.get::<&str>()?; out.push(name)?; } - Ok(out) + Ok(Some(out)) } } From 224a0ff49cd46c8d1e6a9ff783e77cd7d7162edd Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 15:37:26 +0200 Subject: [PATCH 437/527] Added debug library to shell core --- shell/core/src/lua.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 55be75d..e19bd9d 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -113,6 +113,9 @@ impl Lua { let logger = DataOut::new(logger); let scheduler = Rc::new(SchedulerPtr::new()); info!("Loading VM libraries..."); + if let Err(e) = libs::lua::Debug.register(vm) { + error!("Failed to load debug library: {}", e); + } if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { error!("Failed to load OS library: {}", e); } From f610df7fb9550449ea5bbd5002ca900481b9edef Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 17:23:46 +0200 Subject: [PATCH 438/527] Added new marker trait ImmutableValue to prevent accessing mutable values from immutable values --- core/src/vm/table/immutable.rs | 8 +- core/src/vm/table/interface.rs | 37 ++++++++- core/src/vm/thread/interface.rs | 34 ++++++++- core/src/vm/thread/value.rs | 50 ++++++++++++ core/src/vm/userdata/any.rs | 130 ++++++++++++++++++++++++++------ core/src/vm/userdata/mod.rs | 2 +- core/src/vm/value/any.rs | 123 +++++++++++++++--------------- core/src/vm/value/core.rs | 26 ++++++- core/src/vm/value/function.rs | 4 +- core/src/vm/value/interface.rs | 3 + core/src/vm/value/raw_ptr.rs | 4 +- 11 files changed, 329 insertions(+), 92 deletions(-) diff --git a/core/src/vm/table/immutable.rs b/core/src/vm/table/immutable.rs index b21c0ab..32fd8f5 100644 --- a/core/src/vm/table/immutable.rs +++ b/core/src/vm/table/immutable.rs @@ -30,7 +30,7 @@ use std::fmt::Display; use crate::vm::table::iter::Iter; use crate::vm::table::Table; use crate::vm::table::traits::GetTable; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; #[derive(Debug, Clone, PartialEq, Eq)] @@ -106,17 +106,17 @@ impl<'a> ImmutableTable<'a> { } #[inline(always)] - pub fn get<'b, T: FromLua<'b>>(&'b self, key: impl GetTable) -> crate::vm::Result { + pub fn get<'b, T: FromLua<'b> + ImmutableValue>(&'b self, key: impl GetTable) -> crate::vm::Result { self.0.get(key) } #[inline(always)] - pub fn get_any<'b, T: FromLua<'b>>(&'b self, key: impl IntoLua) -> crate::vm::Result { + pub fn get_any<'b, T: FromLua<'b> + ImmutableValue>(&'b self, key: impl IntoLua) -> crate::vm::Result { self.0.get_any(key) } #[inline(always)] - pub fn collect>(self) -> crate::vm::Result { + pub fn collect + ImmutableValue>(self) -> crate::vm::Result { self.0.collect() } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index ea0950d..d9a391b 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -38,10 +38,10 @@ use crate::util::core::{AnyStr, SimpleDrop}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::traits::{GetTable, SetTable}; -use crate::vm::table::Table; +use crate::vm::table::{ImmutableTable, Table}; use crate::vm::util::LuaType; use crate::vm::value::util::{check_type_equals, check_value_top}; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; unsafe impl SimpleDrop for Table<'_> {} @@ -73,6 +73,33 @@ impl<'a> FromLua<'a> for Table<'a> { } } +impl LuaType for ImmutableTable<'_> {} +unsafe impl SimpleDrop for ImmutableTable<'_> {} +unsafe impl ImmutableValue for ImmutableTable<'_> {} + +impl<'a> FromLua<'a> for ImmutableTable<'a> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Self::from_raw(vm, vm.get_absolute_index(index)) + } + + #[inline(always)] + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + Table::from_lua(vm, index).map(Into::into) + } +} + +impl<'a> FromParam<'a> for ImmutableTable<'a> { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Table::from_param(vm, index).into() + } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + Table::try_from_param(vm, index).map(Into::into) + } +} + unsafe impl IntoParam for Table<'_> { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { @@ -149,6 +176,8 @@ impl<'a, T: 'static> FromLua<'a> for Vec where for<'b> T: FromLua<'b> { } } +unsafe impl ImmutableValue for Vec {} + impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap where for<'b> K: FromLua<'b> + Hash + Eq, for<'b> V: FromLua<'b> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -174,6 +203,8 @@ impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap where for<'b> K: } } +unsafe impl ImmutableValue for HashMap {} + impl<'a, K: 'static, V: 'static> FromLua<'a> for BTreeMap where for<'b> K: FromLua<'b> + Ord, for<'b> V: FromLua<'b> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -198,3 +229,5 @@ impl<'a, K: 'static, V: 'static> FromLua<'a> for BTreeMap where for<'b> K: Ok(map) } } + +unsafe impl ImmutableValue for BTreeMap {} diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 3f45784..04de112 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -32,12 +32,25 @@ use crate::impl_registry_value; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; -use crate::vm::thread::value::Thread; +use crate::vm::thread::value::{ImmutableThread, Thread}; use crate::vm::util::LuaType; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::Vm; +unsafe impl ImmutableValue for ImmutableThread<'_> {} + +impl<'a> FromLua<'a> for ImmutableThread<'a> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + ImmutableThread::from_raw(vm, vm.get_absolute_index(index)) + } + + #[inline(always)] + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + Thread::from_lua(vm, index).map(Into::into) + } +} + impl<'a> FromLua<'a> for Thread<'a> { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -54,17 +67,34 @@ unsafe impl SimpleDrop for Thread<'_> {} impl LuaType for Thread<'_> {} +unsafe impl SimpleDrop for ImmutableThread<'_> {} + +impl LuaType for ImmutableThread<'_> {} + impl<'a> FromParam<'a> for Thread<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { luaL_checktype(vm.as_ptr(), index, Type::Thread); Thread::from_raw(vm, vm.get_absolute_index(index)) } + #[inline(always)] fn try_from_param(vm: &'a Vm, index: i32) -> Option { Thread::from_lua(vm, index).ok() } } +impl<'a> FromParam<'a> for ImmutableThread<'a> { + #[inline(always)] + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + Thread::from_param(vm, index).into() + } + + #[inline(always)] + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + ImmutableThread::from_lua(vm, index).ok() + } +} + unsafe impl IntoParam for Thread<'_> { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index c09fd9d..91b3f0f 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -127,3 +127,53 @@ impl<'a> Thread<'a> { &self.thread } } + +/// Represents a thread object value on a lua stack. +#[derive(Clone, Eq, PartialEq, Debug)] +pub struct ImmutableThread<'a>(Thread<'a>); + +impl Display for ImmutableThread<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "thread@{:X}", self.0.thread.uid()) + } +} + +impl<'a> From> for ImmutableThread<'a> { + #[inline(always)] + fn from(value: Thread<'a>) -> Self { + Self(value) + } +} + +impl<'a> ImmutableThread<'a> { + /// Creates a thread value from a raw Vm and index on `vm` stack. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a thread value and is absolute. If index is not absolute + /// then using the produced thread value is UB. If the index points to any other type then + /// using the produced thread value is also UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self(Thread::from_raw(vm, index)) + } + + /// Returns the absolute index of this table on the Lua stack. + #[inline(always)] + pub fn index(&self) -> i32 { + self.0.index + } + + /// Returns the thread stack object attached to this thread value. + #[inline(always)] + pub fn as_thread(&self) -> &core::Thread { + &self.0.thread + } +} diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 5708d81..6d6c0b8 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -30,12 +30,13 @@ use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; -use crate::util::core::AnyStr; +use crate::util::core::{AnyStr, SimpleDrop}; use crate::util::LuaFunction; use crate::vm::table::ImmutableTable; +use crate::vm::util::LuaType; use crate::vm::value::types::Function; use crate::vm::value::util::{check_get_metatable, check_push_value}; @@ -62,27 +63,32 @@ impl PartialEq for AnyUserData<'_> { impl Eq for AnyUserData<'_> {} -impl Display for AnyUserData<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let res = self.vm.scope(|_| { - let res: crate::vm::Result<&str> = self.call_method("__tostring", ()) - .or_else(|_| self.call_method("tostring", ())); - match res { - Ok(v) => { - let type_name = self.get_type_name()?; - Ok(write!(f, "{}({})", type_name, v)) - }, - Err(e) => Err(e) - } - }); +//Stupid fmt name in Rust which causes conflicts... +fn internal_display(ud: &AnyUserData, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + let res = ud.vm.scope(|_| { + let res: crate::vm::Result<&str> = ud.call_method("__tostring", ()) + .or_else(|_| ud.call_method("tostring", ())); match res { - Ok(v) => v, - Err(_) => write!( - f, - "userdata@{:X}", - unsafe { lua_touserdata(self.vm.as_ptr(), self.index) } as usize - ) + Ok(v) => { + let type_name = ud.get_type_name()?; + Ok(write!(f, "{}({})", type_name, v)) + }, + Err(e) => Err(e) } + }); + match res { + Ok(v) => v, + Err(_) => write!( + f, + "userdata@{:X}", + unsafe { lua_touserdata(ud.vm.as_ptr(), ud.index) } as usize + ) + } +} + +impl Display for AnyUserData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + internal_display(self, f) } } @@ -197,3 +203,85 @@ unsafe impl IntoLua for &AnyUserData<'_> { check_push_value(self.vm, vm, self.index) } } + +#[derive(Debug, Clone, Eq, PartialEq)] +pub struct ImmutableAnyUserData<'a>(AnyUserData<'a>); + +impl Display for ImmutableAnyUserData<'_> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + internal_display(&self.0, f) + } +} + +impl<'a> From> for ImmutableAnyUserData<'a> { + #[inline(always)] + fn from(value: AnyUserData<'a>) -> Self { + Self(value) + } +} + +impl<'a> ImmutableAnyUserData<'a> { + /// Creates an AnyUserData from a raw Vm and index. + /// + /// # Arguments + /// + /// * `vm`: the vm to link to. + /// * `index`: the index on the lua stack. + /// + /// returns: Table + /// + /// # Safety + /// + /// Must ensure that index points to a UserData and is absolute. If index is not absolute then + /// using the produced object is UB. If the index points to any other type then using the produced + /// object is also UB. + #[inline(always)] + pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { + Self(AnyUserData::from_raw(vm, index)) + } + + /// Returns a unique identifier to that table across the Vm it is attached to. + #[inline(always)] + pub fn uid(&self) -> usize { + self.0.uid() + } + + /// Returns a reference to this UserData value cast to `T`. + #[inline(always)] + pub fn get(&self) -> crate::vm::Result<&T> { + self.0.get() + } + + #[inline(always)] + pub fn get_metatable(&self) -> Option { + self.0.get_metatable() + } + + #[inline(always)] + pub fn get_type_name(&self) -> crate::vm::Result<&str> { + self.0.get_type_name() + } + + #[inline(always)] + pub fn call_method<'b, T: FromLua<'b> + ImmutableValue>(&'b self, name: impl AnyStr, args: impl IntoLua) -> crate::vm::Result { + self.0.call_method(name, args) + } +} + +unsafe impl ImmutableValue for ImmutableAnyUserData<'_> {} + +impl<'a> FromLua<'a> for ImmutableAnyUserData<'a> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + Self::from_raw(vm, vm.get_absolute_index(index)) + } + + #[inline(always)] + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + AnyUserData::from_lua(vm, index).map(Into::into) + } +} + +unsafe impl SimpleDrop for ImmutableAnyUserData<'_> {} +unsafe impl SimpleDrop for AnyUserData<'_> {} +impl LuaType for AnyUserData<'_> {} +impl LuaType for ImmutableAnyUserData<'_> {} diff --git a/core/src/vm/userdata/mod.rs b/core/src/vm/userdata/mod.rs index 9c83eb9..07ccc3d 100644 --- a/core/src/vm/userdata/mod.rs +++ b/core/src/vm/userdata/mod.rs @@ -33,6 +33,6 @@ mod error; mod interface; pub mod util; -pub use any::AnyUserData; +pub use any::{AnyUserData, ImmutableAnyUserData}; pub use error::Error; pub use interface::*; diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 66200c4..43527fa 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -30,18 +30,21 @@ use crate::ffi::lua::{lua_pushnil, lua_toboolean, lua_tonumber, lua_type, Type}; use crate::util::core::SimpleDrop; use crate::vm::error::{Error, TypeError}; use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::table::Table; -use crate::vm::thread::value::Thread as Thread; -use crate::vm::userdata::AnyUserData; +use crate::vm::table::{ImmutableTable, Table}; +use crate::vm::thread::value::{ImmutableThread, Thread as Thread}; +use crate::vm::userdata::{AnyUserData, ImmutableAnyUserData}; use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::value::function::Function; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; use std::fmt::Display; use std::str::FromStr; +pub type Any<'a> = AnyValue<'a, Table<'a>, AnyUserData<'a>, Thread<'a>>; +pub type AnyImmutable<'a> = AnyValue<'a, ImmutableTable<'a>, ImmutableAnyUserData<'a>, ImmutableThread<'a>>; + #[derive(Debug, PartialEq, Clone)] -pub enum Any<'a> { +pub enum AnyValue<'a, T, U, R> { None, Nil, Number(f64), @@ -49,50 +52,50 @@ pub enum Any<'a> { String(&'a str), Buffer(&'a [u8]), Function(Function<'a>), - Table(Table<'a>), - UserData(AnyUserData<'a>), - Thread(Thread<'a>), + Table(T), + UserData(U), + Thread(R), } -impl Eq for Any<'_> {} +impl Eq for AnyValue<'_, T, U, R> {} -impl Display for Any<'_> { +impl Display for AnyValue<'_, T, U, R> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { - Any::None => f.write_str(""), - Any::Nil => f.write_str("nil"), - Any::Number(v) => write!(f, "{}", v), - Any::Boolean(v) => write!(f, "{}", v), - Any::String(v) => write!(f, "{}", v), - Any::Buffer(v) => write!(f, "{:?}", v), - Any::Function(v) => write!(f, "{}", v), - Any::Table(v) => write!(f, "{}", v), - Any::UserData(v) => write!(f, "{}", v), - Any::Thread(v) => write!(f, "{}", v), + AnyValue::None => f.write_str(""), + AnyValue::Nil => f.write_str("nil"), + AnyValue::Number(v) => write!(f, "{}", v), + AnyValue::Boolean(v) => write!(f, "{}", v), + AnyValue::String(v) => write!(f, "{}", v), + AnyValue::Buffer(v) => write!(f, "{:?}", v), + AnyValue::Function(v) => write!(f, "{}", v), + AnyValue::Table(v) => write!(f, "{}", v), + AnyValue::UserData(v) => write!(f, "{}", v), + AnyValue::Thread(v) => write!(f, "{}", v), } } } -impl Any<'_> { +impl AnyValue<'_, T, U, R> { pub fn ty(&self) -> Type { match self { - Any::None => Type::None, - Any::Nil => Type::Nil, - Any::Number(_) => Type::Number, - Any::Boolean(_) => Type::Boolean, - Any::String(_) => Type::String, - Any::Buffer(_) => Type::String, - Any::Function(_) => Type::Function, - Any::Table(_) => Type::Table, - Any::UserData(_) => Type::Userdata, - Any::Thread(_) => Type::Thread, + AnyValue::None => Type::None, + AnyValue::Nil => Type::Nil, + AnyValue::Number(_) => Type::Number, + AnyValue::Boolean(_) => Type::Boolean, + AnyValue::String(_) => Type::String, + AnyValue::Buffer(_) => Type::String, + AnyValue::Function(_) => Type::Function, + AnyValue::Table(_) => Type::Table, + AnyValue::UserData(_) => Type::Userdata, + AnyValue::Thread(_) => Type::Thread, } } pub fn to_number(&self) -> Result { match self { - Any::Number(v) => Ok(*v), - Any::String(v) => { + AnyValue::Number(v) => Ok(*v), + AnyValue::String(v) => { crate::ffi::lua::RawNumber::from_str(v).map_err(|_| Error::ParseFloat) } _ => Err(Error::Type(TypeError { @@ -104,8 +107,8 @@ impl Any<'_> { pub fn to_integer(&self) -> Result { match self { - Any::Number(v) => Ok(*v as _), - Any::String(v) => { + AnyValue::Number(v) => Ok(*v as _), + AnyValue::String(v) => { crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt) } _ => Err(Error::Type(TypeError { @@ -119,19 +122,19 @@ impl Any<'_> { unsafe impl IntoLua for Any<'_> { fn into_lua(self, vm: &Vm) -> u16 { match self { - Any::None => 0, - Any::Nil => { + AnyValue::None => 0, + AnyValue::Nil => { unsafe { lua_pushnil(vm.as_ptr()) }; 1 } - Any::Number(v) => v.into_lua(vm), - Any::Boolean(v) => v.into_lua(vm), - Any::String(v) => v.into_lua(vm), - Any::Buffer(v) => v.into_lua(vm), - Any::Function(v) => v.into_lua(vm), - Any::Table(v) => v.into_lua(vm), - Any::UserData(_) => 0, - Any::Thread(_) => 0, + AnyValue::Number(v) => v.into_lua(vm), + AnyValue::Boolean(v) => v.into_lua(vm), + AnyValue::String(v) => v.into_lua(vm), + AnyValue::Buffer(v) => v.into_lua(vm), + AnyValue::Function(v) => v.into_lua(vm), + AnyValue::Table(v) => v.into_lua(vm), + AnyValue::UserData(_) => 0, + AnyValue::Thread(_) => 0, } } } @@ -143,7 +146,7 @@ unsafe impl IntoParam for Any<'_> { } } -impl<'a> FromLua<'a> for Any<'a> { +impl<'a, T: FromLua<'a>, U: FromLua<'a>, R: FromLua<'a>> FromLua<'a> for AnyValue<'a, T, U, R> { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { Self::from_lua(vm, index).unwrap_unchecked() @@ -152,41 +155,41 @@ impl<'a> FromLua<'a> for Any<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let ty = unsafe { lua_type(vm.as_ptr(), index) }; match ty { - Type::None => Ok(Any::None), - Type::Nil => Ok(Any::Nil), + Type::None => Ok(AnyValue::None), + Type::Nil => Ok(AnyValue::Nil), Type::Boolean => { let value = unsafe { lua_toboolean(vm.as_ptr(), index) }; - Ok(Any::Boolean(value == 1)) + Ok(AnyValue::Boolean(value == 1)) } Type::LightUserdata => Err(Error::UnsupportedType(ty)), Type::Number => { let value = unsafe { lua_tonumber(vm.as_ptr(), index) }; - Ok(Any::Number(value)) + Ok(AnyValue::Number(value)) } Type::String => { let buffer: &[u8] = unsafe { FromLua::from_lua_unchecked(vm, index) }; match std::str::from_utf8(buffer) { - Ok(s) => Ok(Any::String(s)), - Err(_) => Ok(Any::Buffer(buffer)), + Ok(s) => Ok(AnyValue::String(s)), + Err(_) => Ok(AnyValue::Buffer(buffer)), } } - Type::Table => Ok(unsafe { Any::Table(FromLua::from_lua_unchecked(vm, index)) }), + Type::Table => Ok(unsafe { AnyValue::Table(FromLua::from_lua_unchecked(vm, index)) }), Type::Function => { - Ok(unsafe { Any::Function(FromLua::from_lua_unchecked(vm, index)) }) + Ok(unsafe { AnyValue::Function(FromLua::from_lua_unchecked(vm, index)) }) } Type::Userdata => { - Ok(unsafe { Any::UserData(FromLua::from_lua_unchecked(vm, index)) }) + Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }) } - Type::Thread => Ok(unsafe { Any::Thread(FromLua::from_lua_unchecked(vm, index)) }), + Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), } } } -unsafe impl SimpleDrop for Any<'_> {} +unsafe impl SimpleDrop for AnyValue<'_, T, U, R> {} -impl LuaType for Any<'_> {} +impl LuaType for AnyValue<'_, T, U, R> {} -impl<'a> FromParam<'a> for Any<'a> { +impl<'a, T: FromLua<'a> + SimpleDrop + LuaType, U: FromLua<'a> + SimpleDrop + LuaType, R: FromLua<'a> + SimpleDrop + LuaType> FromParam<'a> for AnyValue<'a, T, U, R> { #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { match FromLua::from_lua(vm, index) { @@ -201,6 +204,8 @@ impl<'a> FromParam<'a> for Any<'a> { } } +unsafe impl ImmutableValue for AnyImmutable<'_> {} + /// A marker struct to run lua code which may return any number of values on the stack. pub struct AnyParam; diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 68fabe7..d6d1f70 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -32,7 +32,7 @@ use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pus use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::value::util::check_type_equals; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::Vm; @@ -64,6 +64,8 @@ impl<'a> FromLua<'a> for &'a str { } } +unsafe impl ImmutableValue for &str {} + impl<'a> FromLua<'a> for &'a [u8] { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let mut len: usize = 0; @@ -92,6 +94,8 @@ impl<'a> FromLua<'a> for &'a [u8] { } } +unsafe impl ImmutableValue for &[u8] {} + impl FromLua<'_> for String { unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { let s: &str = FromLua::from_lua_unchecked(vm, index); @@ -104,6 +108,8 @@ impl FromLua<'_> for String { } } +unsafe impl ImmutableValue for String {} + impl FromLua<'_> for Box<[u8]> { unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { let bytes: &[u8] = FromLua::from_lua_unchecked(vm, index); @@ -116,6 +122,8 @@ impl FromLua<'_> for Box<[u8]> { } } +unsafe impl ImmutableValue for Box<[u8]> {} + macro_rules! impl_from_lua { ($t: ty, $expected: ident, $func: ident, $push_func: ident, $($ret: tt)*) => { impl FromLua<'_> for $t { @@ -139,6 +147,8 @@ macro_rules! impl_from_lua { } } } + + unsafe impl ImmutableValue for $t {} }; } @@ -175,6 +185,8 @@ impl FromLua<'_> for () { } } +unsafe impl ImmutableValue for () {} + impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -194,8 +206,12 @@ impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { } } +unsafe impl ImmutableValue for &T {} + macro_rules! impl_from_lua_tuple { ($($name: ident: $name2: ident ($name3: tt)),*) => { + unsafe impl<$($name: ImmutableValue),*> ImmutableValue for ($($name),*) {} + impl<'a, $($name: FromLua<'a>),*> FromLua<'a> for ($($name),*) { #[inline(always)] fn num_values() -> i16 { @@ -283,6 +299,8 @@ impl<'a, T: FromLua<'a>> FromLua<'a> for Option { } } +unsafe impl ImmutableValue for Option {} + unsafe impl IntoLua for T { fn into_lua(self, vm: &Vm) -> u16 { let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; @@ -369,6 +387,8 @@ impl FromLua<'_> for Integer { } } +unsafe impl ImmutableValue for Integer {} + impl FromLua<'_> for Number { #[inline(always)] unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { @@ -386,6 +406,8 @@ impl FromLua<'_> for Number { } } +unsafe impl ImmutableValue for Number {} + impl FromLua<'_> for Boolean { unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { Boolean(unsafe { lua_toboolean(vm.as_ptr(), index) == 1 }) @@ -396,6 +418,8 @@ impl FromLua<'_> for Boolean { } } +unsafe impl ImmutableValue for Boolean {} + unsafe impl IntoLua for Number { #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 1bb2151..98229e9 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -34,7 +34,7 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::util::LuaType; use crate::vm::value::util::{check_type_equals, check_value_top}; -use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; use crate::impl_registry_value; @@ -146,3 +146,5 @@ impl<'a> FromLua<'a> for Function<'a> { } impl_registry_value!(crate::vm::registry::types::Function => Function); + +unsafe impl ImmutableValue for Function<'_> {} diff --git a/core/src/vm/value/interface.rs b/core/src/vm/value/interface.rs index 140f273..918d5ac 100644 --- a/core/src/vm/value/interface.rs +++ b/core/src/vm/value/interface.rs @@ -87,3 +87,6 @@ pub unsafe trait IntoLua: Sized { /// returns: u16 number of elements pushed onto the Lua stack. fn into_lua(self, vm: &Vm) -> u16; } + +/// Marker trait that represents a value which cannot be mutated. +pub unsafe trait ImmutableValue {} diff --git a/core/src/vm/value/raw_ptr.rs b/core/src/vm/value/raw_ptr.rs index 9ea9c36..2e9ffaa 100644 --- a/core/src/vm/value/raw_ptr.rs +++ b/core/src/vm/value/raw_ptr.rs @@ -28,7 +28,7 @@ use crate::ffi::lua::{lua_pushlightuserdata, lua_touserdata}; use crate::util::core::SimpleDrop; -use crate::vm::value::IntoLua; +use crate::vm::value::{ImmutableValue, IntoLua}; use crate::vm::Vm; #[derive(Debug)] @@ -88,3 +88,5 @@ unsafe impl IntoLua for RawPtr { 1 } } + +unsafe impl ImmutableValue for RawPtr {} From 0e0633966e255eb644641a6ae6264e8193fae220 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 17:57:55 +0200 Subject: [PATCH 439/527] Added support for new interactive mode --- shell/core/src/autocomplete.rs | 3 +++ shell/core/src/core.rs | 31 +++++++++++++++++++++++++++++++ shell/core/src/data_in.rs | 6 +++++- shell/core/src/data_out.rs | 5 ++++- shell/core/src/lua.rs | 8 ++++++-- shell/core/src/main.rs | 12 ++++++++++-- 6 files changed, 59 insertions(+), 6 deletions(-) diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 4b95e7b..1a70484 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -35,6 +35,7 @@ use bp3d_lua::vm::table::ImmutableTable; use bp3d_lua::vm::value::any::Any; use crate::data::DataOut; +#[derive(Debug)] pub enum Mode { AddUpdate(Vec), Delete(Vec) @@ -46,6 +47,7 @@ pub enum Type { Attribute } +#[derive(Debug)] pub struct Item { pub name: String, pub ty: Type @@ -60,6 +62,7 @@ impl Item { } } +#[derive(Debug)] pub struct Completions { pub path: String, pub items: Vec diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index b396ed2..6079298 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -34,6 +34,7 @@ use bp3d_net::ipc::util::Message; use bp3d_proto::message::FromBytes; use crate::lua::{Args, Lua}; use bp3d_util::result::ResultExt; +use tokio::io::{AsyncBufReadExt, BufReader}; use bp3d_lua_shell_proto::send; async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Result { @@ -85,3 +86,33 @@ pub async fn run(args: Args, name: &str) { info!("terminating lua VM..."); lua.exit().await; } + +pub async fn run_interactive(args: Args) { + info!("starting lua VM"); + let mut lua = Lua::new(args); + let mut reader = BufReader::new(tokio::io::stdin()).lines(); + loop { + tokio::select! { + res = reader.next_line() => { + match res { + Err(e) => { + error!("error reading from stdin: {}", e); + break; + }, + Ok(line) => match line { + Some(v) => lua.send(send::RunCode { + name: None, + code: &v + }).await, + None => break + } + } + } + Some(b) = lua.next_msg() => { + debug!("{:?}", b); + } + } + } + info!("terminating lua VM..."); + lua.exit().await; +} diff --git a/shell/core/src/data_in.rs b/shell/core/src/data_in.rs index 76f72b1..dadf683 100644 --- a/shell/core/src/data_in.rs +++ b/shell/core/src/data_in.rs @@ -26,11 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::Debug; use bp3d_lua::vm::Vm; use crate::data::DataOut; use crate::lua::Args; -pub trait InData: Send { +pub trait InData: Send + Debug { fn handle(&mut self, args: &Args, vm: &Vm, out: &DataOut) -> bool; } @@ -38,11 +39,13 @@ pub trait NetInData { fn to_in_data(self) -> Box; } +#[derive(Debug)] pub struct RunCode { pub name: Option, pub code: String, } +#[derive(Debug)] pub struct RunFile { pub path: String, } @@ -64,6 +67,7 @@ impl<'a> NetInData for bp3d_lua_shell_proto::send::RunCode<'a> { } } +#[derive(Debug)] pub struct Exit; impl InData for Exit { diff --git a/shell/core/src/data_out.rs b/shell/core/src/data_out.rs index 6e11423..12aa325 100644 --- a/shell/core/src/data_out.rs +++ b/shell/core/src/data_out.rs @@ -26,16 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fmt::Debug; use bp3d_net::ipc::util::Message; use bp3d_proto::message::WriteSelf; use bp3d_lua_shell_proto::recv; use bp3d_lua_shell_proto::completion; use crate::autocomplete::{Mode, Type}; -pub trait OutData: Send { +pub trait OutData: Send + Debug { fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()>; } +#[derive(Debug)] pub struct Log(pub &'static str, pub String); impl OutData for Log { @@ -47,6 +49,7 @@ impl OutData for Log { } } +#[derive(Debug)] pub struct Autocomplete(pub Mode); impl OutData for Autocomplete { diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index e19bd9d..c552295 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -34,7 +34,7 @@ use std::time::Duration; use bp3d_lua::vm::core::interrupt::{spawn_interruptible, Signal}; use bp3d_lua::libs; use bp3d_lua::libs::Lib; -use bp3d_debug::{debug, error, info}; +use bp3d_debug::{debug, error, info, trace}; use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; use bp3d_lua::vm::value::any::Any; @@ -150,12 +150,16 @@ impl Lua { if let Some(main_script) = &args.main_script { vm.scope(|vm| Ok(RunFile { path: main_script.clone() }.handle(&args, vm, &logger))).unwrap(); } - loop { + let mut running = true; + while running { // First handle IPC events while let Some(command) = receiver.try_recv().ok() { // Nice type-inference breakage with this box. + trace!("received command: {:?}", command); let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); + trace!({ret}, "command handled"); if ret { + running = false; break; } } diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index 8aaafa1..e4b886b 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -50,6 +50,9 @@ struct Cli { #[arg(short = 'm', long = "modules", help = "Path to modules directory.")] pub modules: Option, + #[arg(long = "it", help = "Run in interactive mode.")] + pub interactive: bool, + #[arg(help = "Path to main script to start at Vm startup in the root directory.")] pub main_script: Option } @@ -63,10 +66,15 @@ async fn main() { modules.push(path); } modules.push(PathBuf::from("./target/debug")); - core::run(lua::Args { + let largs = lua::Args { data: root.join("data"), lua: root.join("src"), modules, main_script: args.main_script, - }, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; + }; + if args.interactive { + core::run_interactive(largs).await; + } else { + core::run(largs, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; + } } From bca81eb2e2897821f56febfc360b9f6f279c0987 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 13 Aug 2025 21:57:14 +0200 Subject: [PATCH 440/527] Fixed bug with spawn_interruptible --- core/src/vm/core/interrupt/mod.rs | 4 ++-- core/src/vm/core/interrupt/unix.rs | 4 +++- core/src/vm/core/interrupt/vm.rs | 16 ++++++++-------- core/src/vm/core/interrupt/windows.rs | 4 +++- 4 files changed, 16 insertions(+), 12 deletions(-) diff --git a/core/src/vm/core/interrupt/mod.rs b/core/src/vm/core/interrupt/mod.rs index 36c092a..c660f2e 100644 --- a/core/src/vm/core/interrupt/mod.rs +++ b/core/src/vm/core/interrupt/mod.rs @@ -60,11 +60,11 @@ simple_error! { } pub fn spawn_interruptible( - f: impl FnOnce(&mut InterruptibleRootVm) -> R + Send + 'static, + f: impl FnOnce(&mut InterruptibleRootVm) -> R + Send + 'static, ) -> (Signal, JoinHandle) { let (send, recv) = std::sync::mpsc::channel(); let handle = std::thread::spawn(move || { - let mut vm = InterruptibleRootVm::new(crate::vm::RootVm::new()); + let mut vm = InterruptibleRootVm::new(crate::vm::core::UnSendRootVm::new()); send.send(Signal::create(&mut vm)).unwrap(); f(&mut vm) }); diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index d7ca57d..534af55 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -34,10 +34,12 @@ use crate::vm::core::interrupt::{Error, InterruptibleRootVm}; use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use std::mem::MaybeUninit; +use std::ops::Deref; use std::sync::{Arc, Mutex, Once}; use std::sync::atomic::AtomicBool; use std::thread::ThreadId; use std::time::Duration; +use crate::vm::Vm; pub struct Signal { l: State, @@ -104,7 +106,7 @@ extern "C" fn signal_handler(_: c_int) { static SIG_BOUND: Once = Once::new(); impl Signal { - pub fn create(vm: &mut InterruptibleRootVm) -> Self { + pub fn create>(vm: &mut InterruptibleRootVm) -> Self { let alive = InterruptibleRootVm::get_alive(vm).clone(); let th = unsafe { pthread_self() }; let l = vm.as_ptr(); diff --git a/core/src/vm/core/interrupt/vm.rs b/core/src/vm/core/interrupt/vm.rs index 8eed617..b8457fc 100644 --- a/core/src/vm/core/interrupt/vm.rs +++ b/core/src/vm/core/interrupt/vm.rs @@ -30,16 +30,16 @@ use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; use std::sync::Arc; use std::sync::atomic::AtomicBool; -use crate::vm::{RootVm, Vm}; +use crate::vm::Vm; -pub struct InterruptibleRootVm { - vm: RootVm, +pub struct InterruptibleRootVm { + vm: T, alive: Arc, useless: PhantomData<*const ()>, // This is to ensure InterruptibleVm is never Send nor Sync. } -impl InterruptibleRootVm { - pub fn new(vm: RootVm) -> Self { +impl InterruptibleRootVm { + pub fn new(vm: T) -> Self { Self { vm, alive: Arc::new(AtomicBool::new(true)), @@ -52,7 +52,7 @@ impl InterruptibleRootVm { } } -impl Deref for InterruptibleRootVm { +impl> Deref for InterruptibleRootVm { type Target = Vm; #[inline(always)] @@ -61,14 +61,14 @@ impl Deref for InterruptibleRootVm { } } -impl DerefMut for InterruptibleRootVm { +impl> DerefMut for InterruptibleRootVm { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { &mut self.vm } } -impl Drop for InterruptibleRootVm { +impl Drop for InterruptibleRootVm { fn drop(&mut self) { self.alive.store(false, std::sync::atomic::Ordering::SeqCst); } diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index 5b98d3f..aa33c4b 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ops::Deref; use super::{Error, InterruptibleRootVm}; use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ @@ -38,6 +39,7 @@ use std::time::Duration; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; use windows_sys::Win32::System::Threading::{GetCurrentThread, ResumeThread, SuspendThread}; +use crate::vm::Vm; static SIG_STATE: Mutex>> = Mutex::new(None); @@ -64,7 +66,7 @@ pub struct Signal { } impl Signal { - pub fn create(vm: &mut InterruptibleRootVm) -> Self { + pub fn create>(vm: &mut InterruptibleRootVm) -> Self { let alive = InterruptibleRootVm::get_alive(vm).clone(); let th = unsafe { GetCurrentThread() }; let l = vm.as_ptr(); From 6076c84b397a8dc19a93129c59f41663faa05f14 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 14 Aug 2025 05:33:01 +0200 Subject: [PATCH 441/527] Cleaned up comment --- core/src/libs/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index d173b72..d982220 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -40,7 +40,7 @@ pub mod os; #[cfg(feature = "libs-core")] mod interface; -//TODO: maybe add a stack debug function which prints the content of the lua stack + //TODO: threading (sandbox with max number of threads) // make sure thread join is time-limited. From bad1ec22e13df4f9b75024aa4f9af6c2435e25ce Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 15 Aug 2025 11:37:49 +0200 Subject: [PATCH 442/527] Added support for exiting the Lua shell from lua code --- shell/core/src/autocomplete.rs | 108 ------------------- shell/core/src/lib1/autocomplete_api.rs | 115 +++++++++++++++++++++ shell/core/src/lib1/mod.rs | 85 +++++++++++++++ shell/core/src/{ => lib1}/scheduler_api.rs | 29 +----- shell/core/src/lua.rs | 19 ++-- shell/core/src/main.rs | 4 +- 6 files changed, 213 insertions(+), 147 deletions(-) create mode 100644 shell/core/src/lib1/autocomplete_api.rs create mode 100644 shell/core/src/lib1/mod.rs rename shell/core/src/{ => lib1}/scheduler_api.rs (67%) diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 1a70484..616a91e 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -26,14 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::HashSet; -use bp3d_lua::decl_closure; -use bp3d_lua::libs::Lib; -use bp3d_lua::util::Namespace; -use bp3d_lua::vm::closure::rc::{Rc, Shared}; -use bp3d_lua::vm::table::ImmutableTable; use bp3d_lua::vm::value::any::Any; -use crate::data::DataOut; #[derive(Debug)] pub enum Mode { @@ -67,104 +60,3 @@ pub struct Completions { pub path: String, pub items: Vec } - -fn get_capacity(val: &Any) -> usize { - match val { - Any::Function(_) => 0, - Any::Table(v) => v.len(), - Any::UserData(_) => 1, - _ => 0 - } -} - -fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: ImmutableTable, metatables: bool) -> bp3d_lua::vm::Result<()> { - if set.contains(&value.uid()) { - return Ok(()); - } - for (k, v) in value.iter() { - let k = k.to_any()?; - let v = v.to_any()?; - match k { - Any::String(name) => { - let c = get_capacity(&v); - if c > 0 { - let mut path = path.clone(); - path.push(name.into()); - root.push(Completions { - path: path.join("."), - items: Vec::with_capacity(c) - }); - list_completions(set, path, root, v, metatables)?; - } else { - root.last_mut().unwrap().items.push(Item::from_lua(name, &v)); - } - } - _ => continue - } - } - if metatables { - if let Some(tbl) = value.get_metatable() { - list_table_completions(set, path, root, tbl, metatables)?; - } - } - set.insert(value.uid()); - Ok(()) -} - -fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: Any, metatables: bool) -> bp3d_lua::vm::Result<()> { - match value { - Any::Table(v) => list_table_completions(set, path, root, v.into(), metatables), - Any::UserData(v) => { - if let Some(tbl) = v.get_metatable() { - // We assume userdata have a single metatable (following current bp3d-lua pattern). - list_table_completions(set, path, root, tbl, false)?; - } - Ok(()) - } - _ => Ok(()) - } -} - -decl_closure! { - fn build_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { - let value: Any = lua.get_global(name)?; - let mut root = Vec::new(); - let mut set = HashSet::new(); - list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; - ch.send(crate::data_out::Autocomplete(Mode::AddUpdate(root))); - Ok(()) - } -} - -decl_closure! { - fn delete_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { - let value: Any = lua.get_global(name)?; - let mut root = Vec::new(); - let mut set = HashSet::new(); - list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; - let base = root.into_iter().map(|v| v.path); - ch.send(crate::data_out::Autocomplete(Mode::Delete(base.collect()))); - Ok(()) - } -} - -pub struct Autocomplete(Shared); - -impl Autocomplete { - pub fn new(logger: DataOut) -> Autocomplete { - Autocomplete(logger.into()) - } -} - -impl Lib for Autocomplete { - const NAMESPACE: &'static str = "bp3d.lua.shell"; - - fn load(&self, namespace: &mut Namespace) -> bp3d_lua::vm::Result<()> { - let rc = Rc::from_rust(namespace.vm(), self.0.clone()); - let rc1 = Rc::from_rust(namespace.vm(), self.0.clone()); - namespace.add([ - ("buildCompletions", build_completions(rc)), - ("deleteCompletions", delete_completions(rc1)) - ]) - } -} diff --git a/shell/core/src/lib1/autocomplete_api.rs b/shell/core/src/lib1/autocomplete_api.rs new file mode 100644 index 0000000..0bac743 --- /dev/null +++ b/shell/core/src/lib1/autocomplete_api.rs @@ -0,0 +1,115 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::collections::HashSet; +use bp3d_lua::decl_closure; +use bp3d_lua::vm::closure::rc::Rc; +use bp3d_lua::vm::table::ImmutableTable; +use bp3d_lua::vm::value::any::Any; +use crate::autocomplete::{Completions, Item, Mode}; +use crate::data::DataOut; + +fn get_capacity(val: &Any) -> usize { + match val { + Any::Function(_) => 0, + Any::Table(v) => v.len(), + Any::UserData(_) => 1, + _ => 0 + } +} + +fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: ImmutableTable, metatables: bool) -> bp3d_lua::vm::Result<()> { + if set.contains(&value.uid()) { + return Ok(()); + } + for (k, v) in value.iter() { + let k = k.to_any()?; + let v = v.to_any()?; + match k { + Any::String(name) => { + let c = get_capacity(&v); + if c > 0 { + let mut path = path.clone(); + path.push(name.into()); + root.push(Completions { + path: path.join("."), + items: Vec::with_capacity(c) + }); + list_completions(set, path, root, v, metatables)?; + } else { + root.last_mut().unwrap().items.push(Item::from_lua(name, &v)); + } + } + _ => continue + } + } + if metatables { + if let Some(tbl) = value.get_metatable() { + list_table_completions(set, path, root, tbl, metatables)?; + } + } + set.insert(value.uid()); + Ok(()) +} + +fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: Any, metatables: bool) -> bp3d_lua::vm::Result<()> { + match value { + Any::Table(v) => list_table_completions(set, path, root, v.into(), metatables), + Any::UserData(v) => { + if let Some(tbl) = v.get_metatable() { + // We assume userdata have a single metatable (following current bp3d-lua pattern). + list_table_completions(set, path, root, tbl, false)?; + } + Ok(()) + } + _ => Ok(()) + } +} + +decl_closure! { + pub fn build_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { + let value: Any = lua.get_global(name)?; + let mut root = Vec::new(); + let mut set = HashSet::new(); + list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; + ch.send(crate::data_out::Autocomplete(Mode::AddUpdate(root))); + Ok(()) + } +} + +decl_closure! { + pub fn delete_completions |ch: Rc| (lua: &Vm, name: &str, metatables: bool) -> bp3d_lua::vm::Result<()> { + let value: Any = lua.get_global(name)?; + let mut root = Vec::new(); + let mut set = HashSet::new(); + list_completions(&mut set, vec![name.into()], &mut root, value, metatables)?; + let base = root.into_iter().map(|v| v.path); + ch.send(crate::data_out::Autocomplete(Mode::Delete(base.collect()))); + Ok(()) + } +} diff --git a/shell/core/src/lib1/mod.rs b/shell/core/src/lib1/mod.rs new file mode 100644 index 0000000..f07a54a --- /dev/null +++ b/shell/core/src/lib1/mod.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::cell::Cell; +use bp3d_debug::info; +use bp3d_lua::decl_closure; +use bp3d_lua::libs::Lib; +use bp3d_lua::util::Namespace; +use bp3d_lua::vm::closure::rc::{Rc, Shared}; +use crate::data::DataOut; +use crate::lib1::autocomplete_api::{build_completions, delete_completions}; +use crate::lib1::scheduler_api::{schedule_in, schedule_periodically}; +use crate::scheduler::SchedulerPtr; + +mod autocomplete_api; +mod scheduler_api; + +decl_closure! { + fn request_exit |running: Rc>| () -> () { + info!("Lua has requested exit"); + running.set(false); + } +} + +pub struct Shell { + log_ch: Shared, + scheduler: Shared, + running: Shared> +} + +impl Shell { + pub fn new(log_ch: DataOut, scheduler: Shared, running: Shared>) -> Shell { + Self { + log_ch: log_ch.into(), + scheduler, + running + } + } +} + +impl Lib for Shell { + const NAMESPACE: &'static str = "bp3d.lua.shell"; + + fn load(&self, namespace: &mut Namespace) -> bp3d_lua::vm::Result<()> { + let rc = Rc::from_rust(namespace.vm(), self.log_ch.clone()); + let rc1 = Rc::from_rust(namespace.vm(), self.log_ch.clone()); + let r1 = Rc::from_rust(namespace.vm(), self.scheduler.clone()); + let r2 = Rc::from_rust(namespace.vm(), self.scheduler.clone()); + let running = Rc::from_rust(namespace.vm(), self.running.clone()); + namespace.add([ + ("buildCompletions", build_completions(rc)), + ("deleteCompletions", delete_completions(rc1)) + ])?; + namespace.add([ + ("scheduleIn", schedule_in(r1)), + ("schedulePeriodically", schedule_periodically(r2)) + ])?; + namespace.add([("requestExit", request_exit(running))]) + } +} diff --git a/shell/core/src/scheduler_api.rs b/shell/core/src/lib1/scheduler_api.rs similarity index 67% rename from shell/core/src/scheduler_api.rs rename to shell/core/src/lib1/scheduler_api.rs index bc259cb..7f194b9 100644 --- a/shell/core/src/scheduler_api.rs +++ b/shell/core/src/lib1/scheduler_api.rs @@ -27,41 +27,18 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::decl_closure; -use bp3d_lua::libs::Lib; -use bp3d_lua::util::Namespace; -use bp3d_lua::vm::closure::rc::{Rc, Shared}; +use bp3d_lua::vm::closure::rc::Rc; use bp3d_lua::vm::thread::value::Thread; use crate::scheduler::SchedulerPtr; decl_closure! { - fn schedule_in |scheduler: Rc| (thread: Thread, after_ms: u32) -> () { + pub fn schedule_in |scheduler: Rc| (thread: Thread, after_ms: u32) -> () { scheduler.schedule_in(thread, after_ms); } } decl_closure! { - fn schedule_periodically |scheduler: Rc| (thread: Thread, period_ms: u32) -> () { + pub fn schedule_periodically |scheduler: Rc| (thread: Thread, period_ms: u32) -> () { scheduler.schedule_periodically(thread, period_ms); } } - -pub struct SchedulerApi(Shared); - -impl SchedulerApi { - pub fn new(ptr: Shared) -> Self { - Self(ptr) - } -} - -impl Lib for SchedulerApi { - const NAMESPACE: &'static str = "bp3d.lua.shell"; - - fn load(&self, namespace: &mut Namespace) -> bp3d_lua::vm::Result<()> { - let r1 = Rc::from_rust(namespace.vm(), self.0.clone()); - let r2 = Rc::from_rust(namespace.vm(), self.0.clone()); - namespace.add([ - ("scheduleIn", schedule_in(r1)), - ("schedulePeriodically", schedule_periodically(r2)) - ]) - } -} diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index c552295..12f2d18 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::cell::Cell; use std::path::PathBuf; use std::rc::Rc; use tokio::sync::mpsc; @@ -39,12 +40,11 @@ use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; use bp3d_lua::vm::value::any::Any; use bp3d_lua::vm::Vm; -use crate::autocomplete::Autocomplete; use crate::data::DataOut; use crate::data_in::{Exit, InData, NetInData, RunCode, RunFile}; use crate::data_out::{Log, OutData}; +use crate::lib1::Shell; use crate::scheduler::SchedulerPtr; -use crate::scheduler_api::SchedulerApi; const CHANNEL_BUFFER: usize = 32; @@ -112,6 +112,7 @@ impl Lua { let (signal, handle) = spawn_interruptible(move |vm| { let logger = DataOut::new(logger); let scheduler = Rc::new(SchedulerPtr::new()); + let running = Rc::new(Cell::new(true)); info!("Loading VM libraries..."); if let Err(e) = libs::lua::Debug.register(vm) { error!("Failed to load debug library: {}", e); @@ -125,12 +126,9 @@ impl Lua { if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { error!("Failed to load base library: {}", e); } - info!("Loading bp3d-lua-shell libraries..."); - if let Err(e) = Autocomplete::new(logger.clone()).register(vm) { - error!("Failed to register autocomplete library: {}", e); - } - if let Err(e) = SchedulerApi::new(scheduler.clone()).register(vm) { - error!("Failed to register scheduler library: {}", e); + info!("Loading bp3d-lua-shell library..."); + if let Err(e) = Shell::new(logger.clone(), scheduler.clone(), running.clone()).register(vm) { + error!("Failed to load shell library: {}", e); } let mut modules = libs::lua::Module::new(&[]); for path in &args.modules { @@ -150,8 +148,7 @@ impl Lua { if let Some(main_script) = &args.main_script { vm.scope(|vm| Ok(RunFile { path: main_script.clone() }.handle(&args, vm, &logger))).unwrap(); } - let mut running = true; - while running { + while running.get() { // First handle IPC events while let Some(command) = receiver.try_recv().ok() { // Nice type-inference breakage with this box. @@ -159,7 +156,7 @@ impl Lua { let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); trace!({ret}, "command handled"); if ret { - running = false; + running.set(false); break; } } diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index e4b886b..4a381fa 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -31,12 +31,12 @@ use clap::Parser; mod lua; mod core; -mod autocomplete; mod data_out; mod data_in; mod data; mod scheduler; -mod scheduler_api; +mod lib1; +mod autocomplete; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] From cdfe558861404b00446b7e2f765787e2f7b50870 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 15 Aug 2025 11:38:22 +0200 Subject: [PATCH 443/527] Added new requestExit function to Lua definition file --- definitions/bp3d.lua.shell.lua | 3 +++ 1 file changed, 3 insertions(+) diff --git a/definitions/bp3d.lua.shell.lua b/definitions/bp3d.lua.shell.lua index 8606804..08c34db 100644 --- a/definitions/bp3d.lua.shell.lua +++ b/definitions/bp3d.lua.shell.lua @@ -68,3 +68,6 @@ bp3d.lua.shell.bindEvent = function(name, func) end --- ---@param name string the name of the event to unbind the lua function from. bp3d.lua.shell.unbindEvent = function(name) end + +--- Requests exit of the shell application from lua code. +bp3d.lua.shell.requestExit = function() end From 7364359f7c845702ed4c7b77be03dd3b50712f18 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 16 Aug 2025 18:41:25 +0200 Subject: [PATCH 444/527] Added initial support to prematurely exit interactive shell application on Lua thread request --- shell/core/src/core.rs | 5 ++++- shell/core/src/data_out.rs | 15 +++++++++++++++ shell/core/src/lua.rs | 11 ++++++++--- shell/core/src/main.rs | 2 ++ 4 files changed, 29 insertions(+), 4 deletions(-) diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index 6079298..9ae4379 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -110,9 +110,12 @@ pub async fn run_interactive(args: Args) { } Some(b) = lua.next_msg() => { debug!("{:?}", b); + if b.has_exited() { + break; + } } } } - info!("terminating lua VM..."); + info!("Terminating lua VM..."); lua.exit().await; } diff --git a/shell/core/src/data_out.rs b/shell/core/src/data_out.rs index 12aa325..f56930c 100644 --- a/shell/core/src/data_out.rs +++ b/shell/core/src/data_out.rs @@ -35,6 +35,21 @@ use crate::autocomplete::{Mode, Type}; pub trait OutData: Send + Debug { fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()>; + fn has_exited(&self) -> bool { false } +} + +#[derive(Debug)] +pub struct End; + +impl OutData for End { + fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()> { + recv::Main { + hdr: recv::Header::new().set_type(recv::Type::End).to_ref(), + msg: recv::Message::End + }.write_self(msg) + } + + fn has_exited(&self) -> bool { true } } #[derive(Debug)] diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 12f2d18..af21cb1 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -35,14 +35,14 @@ use std::time::Duration; use bp3d_lua::vm::core::interrupt::{spawn_interruptible, Signal}; use bp3d_lua::libs; use bp3d_lua::libs::Lib; -use bp3d_debug::{debug, error, info, trace}; +use bp3d_debug::{debug, error, info, trace, warning}; use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; use bp3d_lua::vm::value::any::Any; use bp3d_lua::vm::Vm; use crate::data::DataOut; use crate::data_in::{Exit, InData, NetInData, RunCode, RunFile}; -use crate::data_out::{Log, OutData}; +use crate::data_out::{End, Log, OutData}; use crate::lib1::Shell; use crate::scheduler::SchedulerPtr; @@ -69,7 +69,11 @@ impl Lua { } pub async fn exit(self) { - self.exec_queue.send(Box::new(Exit)).await.unwrap(); + if let Err(_) = self.exec_queue.send(Box::new(Exit)).await { + self.handle.join().unwrap(); + warning!("Attempt to exit already exited Lua thread"); + return; + } // Leave 50ms for the thread to terminate nominally before killing the VM. tokio::time::sleep(Duration::from_millis(50)).await; // This call will either immediately return because the thread is already dead (expected), @@ -165,6 +169,7 @@ impl Lua { // Wait for next cycle std::thread::sleep(Duration::from_millis(1)); } + logger.send(End); }); Self { signal, diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index 4a381fa..e2f87b2 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::path::PathBuf; +use bp3d_debug::trace; use clap::Parser; mod lua; @@ -77,4 +78,5 @@ async fn main() { } else { core::run(largs, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; } + trace!("Application end"); } From f20d8be15068d92ca221fb11db5b89cfa04fc5ed Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 16 Aug 2025 20:02:42 +0200 Subject: [PATCH 445/527] Fixed safety tests with new compiler version --- core/tests/safety/test_destructor_box_not_send.stderr | 2 +- core/tests/safety/test_rclosure_not_send.stderr | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/tests/safety/test_destructor_box_not_send.stderr b/core/tests/safety/test_destructor_box_not_send.stderr index 3d2c8ae..6e9a9fb 100644 --- a/core/tests/safety/test_destructor_box_not_send.stderr +++ b/core/tests/safety/test_destructor_box_not_send.stderr @@ -9,7 +9,7 @@ error[E0277]: `*const ()` cannot be sent between threads safely note: required because it appears within the type `PhantomData<*const ()>` --> $RUST/core/src/marker.rs | - | pub struct PhantomData; + | pub struct PhantomData; | ^^^^^^^^^^^ note: required because it appears within the type `UnSendType` --> tests/safety/test_destructor_box_not_send.rs:34:8 diff --git a/core/tests/safety/test_rclosure_not_send.stderr b/core/tests/safety/test_rclosure_not_send.stderr index 9663153..894cba8 100644 --- a/core/tests/safety/test_rclosure_not_send.stderr +++ b/core/tests/safety/test_rclosure_not_send.stderr @@ -12,7 +12,7 @@ error[E0277]: `*const ()` cannot be sent between threads safely note: required because it appears within the type `PhantomData<*const ()>` --> $RUST/core/src/marker.rs | - | pub struct PhantomData; + | pub struct PhantomData; | ^^^^^^^^^^^ note: required because it appears within the type `UnSendType` --> tests/safety/test_rclosure_not_send.rs:35:8 From dcf58c8033b9e78847411a2697c7adf9a4fcb192 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 17 Aug 2025 19:59:39 +0200 Subject: [PATCH 446/527] Fixed elided lifetimes deprecation warnings --- core/src/libs/lua/debug.rs | 6 +++--- core/src/libs/util/string.rs | 4 ++-- core/src/libs/util/utf8.rs | 8 ++++---- core/src/util/core.rs | 6 +++--- core/src/util/thread.rs | 3 ++- core/src/vm/core/vm.rs | 4 ++-- core/src/vm/table/core.rs | 4 ++-- core/src/vm/table/immutable.rs | 4 ++-- core/src/vm/thread/core.rs | 4 ++-- core/src/vm/thread/value.rs | 8 +++++--- core/src/vm/userdata/any.rs | 4 ++-- core/src/vm/userdata/util.rs | 4 ++-- core/src/vm/value/util.rs | 2 +- core/tests/test_vm_custom_structs.rs | 2 +- 14 files changed, 33 insertions(+), 30 deletions(-) diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs index 4f909ef..3154b64 100644 --- a/core/src/libs/lua/debug.rs +++ b/core/src/libs/lua/debug.rs @@ -40,7 +40,7 @@ use crate::vm::userdata::util::{get_metatable_by_name, get_static_table_by_name} use crate::vm::value::any::Any; decl_lib_func! { - fn dump_stack(vm: &Vm, start_index: i32) -> crate::vm::Result
{ + fn dump_stack(vm: &Vm, start_index: i32) -> crate::vm::Result> { let mut tbl = Table::new(vm); let iter = start::(vm, start_index); for value in iter { @@ -54,7 +54,7 @@ decl_lib_func! { } decl_lib_func! { - fn dump_libs(vm: &Vm) -> crate::vm::Result
{ + fn dump_libs(vm: &Vm) -> crate::vm::Result> { let mut tbl = Table::new(vm); if let Some(vv) = DebugRegistry::list(vm, crate::vm::core::debug::Lib) { for v in vv { @@ -66,7 +66,7 @@ decl_lib_func! { } decl_lib_func! { - fn dump_classes(vm: &Vm) -> crate::vm::Result
{ + fn dump_classes(vm: &Vm) -> crate::vm::Result> { let mut tbl = Table::new(vm); if let Some(vv) = DebugRegistry::list(vm, crate::vm::core::debug::Class) { for v in vv { diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index 4a8d4ee..a611ad2 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -56,13 +56,13 @@ decl_lib_func! { } decl_lib_func! { - fn capitalise(src: &[u8]) -> Cow<[u8]> { + fn capitalise(src: &[u8]) -> Cow<'_, [u8]> { src.capitalise_ascii() } } decl_lib_func! { - fn decapitalise(src: &[u8]) -> Cow<[u8]> { + fn decapitalise(src: &[u8]) -> Cow<'_, [u8]> { src.decapitalise_ascii() } } diff --git a/core/src/libs/util/utf8.rs b/core/src/libs/util/utf8.rs index 82439da..68942a2 100644 --- a/core/src/libs/util/utf8.rs +++ b/core/src/libs/util/utf8.rs @@ -71,25 +71,25 @@ decl_lib_func! { } decl_lib_func! { - fn from_string<'a>(src: &'a [u8]) -> Option<&'a str> { + fn from_string(src: &[u8]) -> Option<&str> { std::str::from_utf8(src).ok() } } decl_lib_func! { - fn from_string_lossy(src: &[u8]) -> Cow { + fn from_string_lossy(src: &[u8]) -> Cow<'_, str> { String::from_utf8_lossy(src) } } decl_lib_func! { - fn capitalise(src: &str) -> Cow { + fn capitalise(src: &str) -> Cow<'_, str> { src.capitalise() } } decl_lib_func! { - fn decapitalise(src: &str) -> Cow { + fn decapitalise(src: &str) -> Cow<'_, str> { src.decapitalise() } } diff --git a/core/src/util/core.rs b/core/src/util/core.rs index 9b7f6e0..66cff27 100644 --- a/core/src/util/core.rs +++ b/core/src/util/core.rs @@ -33,11 +33,11 @@ use std::ffi::{CStr, CString, OsStr}; use std::path::Path; pub trait AnyStr { - fn to_str(&self) -> crate::vm::Result>; + fn to_str(&self) -> crate::vm::Result>; } impl AnyStr for &str { - fn to_str(&self) -> crate::vm::Result> { + fn to_str(&self) -> crate::vm::Result> { Ok(Cow::Owned( CString::new(&**self).map_err(|_| crate::vm::error::Error::Null)?, )) @@ -46,7 +46,7 @@ impl AnyStr for &str { impl AnyStr for &CStr { #[inline(always)] - fn to_str(&self) -> crate::vm::Result> { + fn to_str(&self) -> crate::vm::Result> { Ok(Cow::Borrowed(&**self)) } } diff --git a/core/src/util/thread.rs b/core/src/util/thread.rs index ac8ca47..df5ac1b 100644 --- a/core/src/util/thread.rs +++ b/core/src/util/thread.rs @@ -43,8 +43,9 @@ impl LuaThread { } } + //TODO: Check if this is indeed safe. #[inline(always)] - pub fn as_thread(&self) -> &crate::vm::thread::core::Thread { + pub fn as_thread(&self) -> &crate::vm::thread::core::Thread<'static> { &self.thread } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 39f0f44..11fc254 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -150,7 +150,7 @@ impl Vm { FromLua::from_lua(self, -(R::num_values() as i32)) } - pub fn load_code(&self, code: impl LoadString) -> crate::vm::Result { + pub fn load_code(&self, code: impl LoadString) -> crate::vm::Result> { let l = self.as_ptr(); unsafe { // Push the lua code. @@ -175,7 +175,7 @@ impl Vm { FromLua::from_lua(self, -(R::num_values() as i32)) } - pub fn load(&self, obj: impl Load) -> crate::vm::Result { + pub fn load(&self, obj: impl Load) -> crate::vm::Result> { let l = self.as_ptr(); let res = obj.load(l); unsafe { diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index 4e0db43..a6aa5c0 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -126,7 +126,7 @@ impl<'a> Table<'a> { unsafe { lua_setmetatable(self.vm.as_ptr(), self.index) }; } - pub fn get_metatable(&self) -> Option
{ + pub fn get_metatable(&self) -> Option> { unsafe { check_get_metatable(self.vm, self.index) } } @@ -144,7 +144,7 @@ impl<'a> Table<'a> { /// Creates a new iterator for this table. /// /// This function borrows mutably to avoid messing up the Lua stack while iterating. - pub fn iter(&mut self) -> Iter { + pub fn iter(&mut self) -> Iter<'_> { Iter::from_raw(self.vm, self.index) } diff --git a/core/src/vm/table/immutable.rs b/core/src/vm/table/immutable.rs index 32fd8f5..e13a104 100644 --- a/core/src/vm/table/immutable.rs +++ b/core/src/vm/table/immutable.rs @@ -82,7 +82,7 @@ impl<'a> ImmutableTable<'a> { } #[inline(always)] - pub fn get_metatable(&self) -> Option { + pub fn get_metatable(&self) -> Option> { self.0.get_metatable().map(ImmutableTable) } @@ -101,7 +101,7 @@ impl<'a> ImmutableTable<'a> { /// /// This function borrows mutably to avoid messing up the Lua stack while iterating. #[inline(always)] - pub fn iter(&mut self) -> Iter { + pub fn iter(&mut self) -> Iter<'_> { self.0.iter() } diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index c97bc94..acd778a 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -60,13 +60,13 @@ impl PartialEq for Thread<'_> { impl Eq for Thread<'_> {} impl Display for Thread<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "thread@{:X}", self.uid()) } } impl Debug for Thread<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str("Thread") } } diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index 91b3f0f..2be3605 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -123,7 +123,9 @@ impl<'a> Thread<'a> { /// Returns the thread stack object attached to this thread value. #[inline(always)] - pub fn as_thread(&self) -> &core::Thread { + pub fn as_thread(&self) -> &core::Thread<'a> { + //TODO: Check if this is safe as thread lifetime duration should be as long as thread value + // on the Lua stack; the same goes for ImmutableThread. &self.thread } } @@ -133,7 +135,7 @@ impl<'a> Thread<'a> { pub struct ImmutableThread<'a>(Thread<'a>); impl Display for ImmutableThread<'_> { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "thread@{:X}", self.0.thread.uid()) } } @@ -173,7 +175,7 @@ impl<'a> ImmutableThread<'a> { /// Returns the thread stack object attached to this thread value. #[inline(always)] - pub fn as_thread(&self) -> &core::Thread { + pub fn as_thread(&self) -> &core::Thread<'a> { &self.0.thread } } diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 6d6c0b8..474ca42 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -149,7 +149,7 @@ impl<'a> AnyUserData<'a> { Ok(unsafe { &mut *this_ptr }) } - pub fn get_metatable(&self) -> Option { + pub fn get_metatable(&self) -> Option> { unsafe { check_get_metatable(self.vm, self.index).map(ImmutableTable::from) } } @@ -253,7 +253,7 @@ impl<'a> ImmutableAnyUserData<'a> { } #[inline(always)] - pub fn get_metatable(&self) -> Option { + pub fn get_metatable(&self) -> Option> { self.0.get_metatable() } diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs index d441872..ca5ea19 100644 --- a/core/src/vm/userdata/util.rs +++ b/core/src/vm/userdata/util.rs @@ -45,7 +45,7 @@ use crate::vm::Vm; /// * `vm`: the [Vm] the UserData type is attached to. /// /// returns: Option
-pub fn get_static_table(vm: &Vm) -> Option { +pub fn get_static_table(vm: &Vm) -> Option> { get_static_table_by_name(vm, T::CLASS_NAME) } @@ -86,7 +86,7 @@ pub fn get_static_table_by_name<'a>(vm: &'a Vm, name: &CStr) -> Option -pub fn get_metatable(vm: &Vm) -> Option { +pub fn get_metatable(vm: &Vm) -> Option> { get_metatable_by_name(vm, T::CLASS_NAME) } diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index f7e546b..d7eb8d1 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -96,7 +96,7 @@ pub fn check_push_single(vm: &Vm, value: impl IntoLua) -> crate::vm::Result<()> /// /// This should never be used to pass the metatable of a [userdata](crate::vm::userdata) type to /// Lua or even modify the returned metatable. If any of these are not maintained this is UB. -pub unsafe fn check_get_metatable(vm: &Vm, index: i32) -> Option
{ +pub unsafe fn check_get_metatable(vm: &Vm, index: i32) -> Option> { unsafe { lua_getmetatable(vm.as_ptr(), index) }; let ty = unsafe { lua_type(vm.as_ptr(), -1) }; if ty == Type::Table { diff --git a/core/tests/test_vm_custom_structs.rs b/core/tests/test_vm_custom_structs.rs index 5115fc9..9c6f637 100644 --- a/core/tests/test_vm_custom_structs.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -56,7 +56,7 @@ decl_lib_func! { } decl_lib_func! { - fn test2(name: &str) -> Test2 { + fn test2(name: &str) -> Test2<'_> { Test2 { name, value: 42 } } } From b5e8045a52097569549b24d80e5ed33eb7deac2c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 17 Aug 2025 20:10:27 +0200 Subject: [PATCH 447/527] Added 1 additional userdata safety test --- core/tests/test_vm_userdata.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index f2e5a8c..2d78766 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -425,3 +425,16 @@ fn test_vm_userdata_statics_2() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_security6() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"a.__index.__gc = function() print(\"Lua has hacked Rust\") end") + .unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From 88e50d8667a18ae6bd3cc293b1d2a39d96015d3e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 18 Aug 2025 20:20:10 +0200 Subject: [PATCH 448/527] Use new shell module of bp3d-os in bp3d-lua-shell --- shell/core/Cargo.toml | 2 +- shell/core/src/core.rs | 39 ++++++++++++++++++++++++++++----------- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index e37f5bb..022dc13 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -13,4 +13,4 @@ bp3d-util = { version = "2.2.0", features = ["result"] } bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } bp3d-proto = "1.0.0-rc.5.0.1" clap = { version = "4.5.4", features = ["derive"] } -bp3d-os = { version = "1.0.0-rc.4.4.0", features = ["time"] } \ No newline at end of file +bp3d-os = { version = "1.0.0-rc.4.5.1", features = ["time", "shell"] } \ No newline at end of file diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index 9ae4379..0d75192 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -31,11 +31,12 @@ const MAX_SIZE: usize = 4096; use bp3d_debug::{debug, error, info}; use bp3d_net::ipc::{Client, Server}; use bp3d_net::ipc::util::Message; +use bp3d_os::shell::{Event, SendChannel, Shell}; use bp3d_proto::message::FromBytes; use crate::lua::{Args, Lua}; use bp3d_util::result::ResultExt; -use tokio::io::{AsyncBufReadExt, BufReader}; use bp3d_lua_shell_proto::send; +use tokio::sync::mpsc; async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Result { let mut msg = Message::new(MAX_SIZE); @@ -87,24 +88,38 @@ pub async fn run(args: Args, name: &str) { lua.exit().await; } +struct ChannelWrapper(mpsc::Sender); + +impl SendChannel for ChannelWrapper { + fn send(&self, event: Event) { + self.0.blocking_send(event).unwrap(); + } +} + pub async fn run_interactive(args: Args) { info!("starting lua VM"); let mut lua = Lua::new(args); - let mut reader = BufReader::new(tokio::io::stdin()).lines(); + let (tx, mut rx) = mpsc::channel::(64); + let app = Shell::new("lua> ", ChannelWrapper(tx)); loop { tokio::select! { - res = reader.next_line() => { + res = rx.recv() => { match res { - Err(e) => { - error!("error reading from stdin: {}", e); + None => { + error!("Shell application has prematurely closed"); break; }, - Ok(line) => match line { - Some(v) => lua.send(send::RunCode { - name: None, - code: &v - }).await, - None => break + Some(event) => match event { + Event::CommandReceived(str) => { + lua.send(send::RunCode { + name: None, + code: &str + }).await; + }, + Event::ExitRequested => { + debug!("exit requested"); + break; + } } } } @@ -118,4 +133,6 @@ pub async fn run_interactive(args: Args) { } info!("Terminating lua VM..."); lua.exit().await; + info!("Terminating shell application..."); + app.exit(); } From 669ceb14d2b555a9ec3c5d9bc7919e00042ddff3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 18 Aug 2025 20:44:11 +0200 Subject: [PATCH 449/527] Fixed generation bug in codegen --- codegen/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 161176c..be72853 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -94,7 +94,7 @@ pub fn decl_lua_plugin(input: TokenStream) -> TokenStream { #[no_mangle] extern "C" fn #func(l: bp3d_lua::ffi::lua::State, error: *mut bp3d_lua::module::error::Error) -> bool { let vm = unsafe { bp3d_lua::vm::Vm::from_raw(l) }; - unsafe { bp3d_lua::module::run_lua_register(&vm, #ident, *&mut error) } + unsafe { bp3d_lua::module::run_lua_register(&vm, #ident, &mut *error) } } }; q.into() From c789d95b72259ec8619b50e0e78cda489c7e466b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 19 Aug 2025 19:43:08 +0200 Subject: [PATCH 450/527] Fixed bp3d-lua bugs with named key registry across modules --- core/Cargo.toml | 2 +- core/src/vm/registry/named.rs | 44 ++++++++++++++++++++++------------- 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 18d3dab..b32e1e0 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,7 +16,7 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-os = { version = "1.0.0-rc.4.4.0", features = [], optional = true } +bp3d-os = { version = "1.0.0-rc.4.5.2", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 44e00e6..c6726c8 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::BTreeSet; +use std::collections::HashMap; use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; use crate::vm::registry::{Set, Value}; use crate::vm::value::util::move_value_top; @@ -41,6 +41,10 @@ use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_unr #[derive(Debug)] pub struct RawKey { ptr: *const c_void, + // This may not always work, but unfortunately Rust TypeId is broken across modules. + // Fortunately, the generic type which is used with this is always a static lifetime + // which must implement the Value trait which limits the number of possible types. + ty: fn() -> &'static str, registered: AtomicBool, register_lock: Mutex } @@ -70,7 +74,11 @@ impl RawKey { } } - pub const fn new(name: &str) -> Self { + pub fn ty(&self) -> &'static str { + (self.ty)() + } + + pub const fn new(name: &str, ty: fn() -> &'static str) -> Self { // This is a re-write of https://github.com/BPXFormat/bpx-rs/blob/develop/src/hash.rs // in const context. let mut val: u64 = 5381; @@ -86,6 +94,7 @@ impl RawKey { // And now a hack to turn u64 into ptr (btw, do NOT dereference it). Self { ptr: val as usize as *const c_void, + ty, registered: AtomicBool::new(false), register_lock: Mutex::new(false) } @@ -117,34 +126,37 @@ fn check_register_key_unique(key: &RawKey) { } let registry = unsafe { voidp_to_ref(lua_ext_keyreg_get()) }; let mut lock = registry.lock().unwrap(); - if lock.contains(&(key.ptr as _)) { - panic!("Attempt to register a duplicate key"); - } else { - lock.insert(key.ptr as _); - *registered = true; - key.registered.store(true, Ordering::Relaxed); + if let Some(ty) = lock.get(&(key.ptr as _)) { + if *ty != key.ty() { + panic!("Attempt to register a duplicate key"); + } } + lock.insert(key.ptr as _, key.ty()); + *registered = true; + key.registered.store(true, Ordering::Relaxed); } -unsafe fn voidp_to_ref(p: *mut c_void) -> &'static Mutex> +type NamedKeyRegistry = Mutex>; + +unsafe fn voidp_to_ref(p: *mut c_void) -> &'static NamedKeyRegistry { assert!(!p.is_null()); - unsafe { &*(p as *const Mutex>) } + unsafe { &*(p as *const NamedKeyRegistry) } } -unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut Mutex> +unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut NamedKeyRegistry { assert!(!p.is_null()); - p as *mut Mutex> + p as *mut NamedKeyRegistry } -fn ref_to_voidp(r: &'static Mutex>) -> *mut c_void +fn ref_to_voidp(r: &'static NamedKeyRegistry) -> *mut c_void { - r as *const Mutex> as *mut c_void + r as *const NamedKeyRegistry as *mut c_void } pub(crate) fn handle_root_vm_init() { - let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(BTreeSet::new())))); + let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(HashMap::new())))); // Pointer set in lua_ext_keyreg_ref to avoid TOCTOU. let ptr = unsafe { lua_ext_keyreg_ref(ptr) }; if ptr.is_null() { @@ -208,7 +220,7 @@ impl Key { #[inline(always)] pub const fn new(name: &str) -> Key { Key { - raw: RawKey::new(name), + raw: RawKey::new(name, std::any::type_name::), useless: PhantomData, } } From b8453660aa77f08bbf14ecb6aee267fa7f1b67eb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 19 Aug 2025 21:36:06 +0200 Subject: [PATCH 451/527] Fixed bug with userdata case rename feature --- core/src/vm/userdata/core.rs | 5 +-- core/tests/test_vm_userdata.rs | 61 ++++++++++++++++++++++++++++++++++ 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 36e3450..f0f6d59 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -32,7 +32,7 @@ use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, NameConvert, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; use crate::vm::Vm; -use bp3d_debug::{debug, warning}; +use bp3d_debug::{debug, trace, warning}; use std::cell::OnceCell; use std::ffi::{c_void, CStr}; use std::marker::PhantomData; @@ -147,13 +147,14 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { } fn add_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { + trace!("Set metatable field {:?}", name); let num = value.into_lua(self.vm); if num > 1 { unsafe { lua_settop(self.vm.as_ptr(), -(num as i32) - 1) }; return Err(Error::MultiValueField); } unsafe { - lua_setfield(self.vm.as_ptr(), -2, self.case.name_convert(name).as_ptr()); + lua_setfield(self.vm.as_ptr(), -2, name.as_ptr()); } Ok(()) } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 2d78766..feb7ed8 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -276,6 +276,41 @@ fn test_vm_userdata_base2(vm: &Vm) { assert_eq!(top + 8, vm.top()); } +fn test_vm_userdata_base3(vm: &Vm) { + unsafe { + DROP_COUNTER = 0; + LUA_DROP_COUNTER = 0; + } + let top = vm.top(); + { + let mut namespace = Namespace::new(vm, "_G").unwrap(); + namespace.add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Camel).unwrap(); + } + assert_eq!(top, vm.top()); + vm.run_code::<()>(c"a = MyInt.new(123)").unwrap(); + vm.run_code::<()>(c"b = MyInt.new(456)").unwrap(); + vm.run_code::<()>(c"c = MyInt.new(456)").unwrap(); + assert_eq!(vm.run_code::(c"return a == b").unwrap(), false); + assert_eq!(vm.run_code::(c"return b == c").unwrap(), true); + assert_eq!(vm.run_code::(c"return a < b").unwrap(), true); + assert_eq!(vm.run_code::(c"return b > a").unwrap(), true); + assert_eq!(vm.run_code::<&MyInt>(c"return a + b").unwrap().0, 579); + assert_eq!( + vm.run_code::<&str>(c"return (a + b):tostring()").unwrap(), + "579" + ); + assert_eq!( + vm.run_code::(c"return (a + b):tonumber()") + .unwrap(), + 579.0 + ); + assert_eq!( + vm.run_code::(c"return a.tonumber(b)").unwrap(), + 456.0 + ); + assert_eq!(top + 8, vm.top()); +} + #[test] fn test_vm_userdata() { let _guard = MUTEX.lock(); @@ -438,3 +473,29 @@ fn test_vm_userdata_security6() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_security7() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base2(&vm); + vm.run_code::<()>(c"getmetatable(a).__gc = function() print(\"Lua has hacked Rust\") end") + .unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security8() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base3(&vm); + vm.run_code::<()>(c"getmetatable(a).__gc = function() error(\"Lua has hacked Rust\") end") + .unwrap_err(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From 64662bd03633207512163b6f29ae2dc7ce1808cf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 19 Aug 2025 21:40:53 +0200 Subject: [PATCH 452/527] Fixed warnings when root-vm feature is disabled --- core/src/vm/registry/named.rs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index c6726c8..9fb621d 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -35,8 +35,7 @@ use std::ffi::c_void; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; -use bp3d_debug::debug; -use crate::ffi::ext::{lua_ext_keyreg_get, lua_ext_keyreg_ref, lua_ext_keyreg_unref}; +use crate::ffi::ext::lua_ext_keyreg_get; #[derive(Debug)] pub struct RawKey { @@ -144,18 +143,23 @@ unsafe fn voidp_to_ref(p: *mut c_void) -> &'static NamedKeyRegistry unsafe { &*(p as *const NamedKeyRegistry) } } +#[cfg(feature="root-vm")] unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut NamedKeyRegistry { assert!(!p.is_null()); p as *mut NamedKeyRegistry } +#[cfg(feature="root-vm")] fn ref_to_voidp(r: &'static NamedKeyRegistry) -> *mut c_void { r as *const NamedKeyRegistry as *mut c_void } +#[cfg(feature="root-vm")] pub(crate) fn handle_root_vm_init() { + use bp3d_debug::debug; + use crate::ffi::ext::lua_ext_keyreg_ref; let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(HashMap::new())))); // Pointer set in lua_ext_keyreg_ref to avoid TOCTOU. let ptr = unsafe { lua_ext_keyreg_ref(ptr) }; @@ -167,7 +171,10 @@ pub(crate) fn handle_root_vm_init() { } } +#[cfg(feature="root-vm")] pub(crate) fn handle_root_vm_uninit() { + use bp3d_debug::debug; + use crate::ffi::ext::lua_ext_keyreg_unref; // Pointer reset to NULL in lua_ext_keyreg_unref to avoid TOCTOU. let ptr = unsafe { lua_ext_keyreg_unref() }; if !ptr.is_null() { From aa37c1d287cb091a3588e97a234291596c2ab595 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 23 Aug 2025 16:59:00 +0200 Subject: [PATCH 453/527] Added support for cdata and 64 bits integers to Lua VM --- core/src/ffi/ext.rs | 14 ++++ core/src/ffi/lua.rs | 1 + patch/lua_ext.patch | 157 ++++++++++++++++++++++++++++++++++++++------ 3 files changed, 151 insertions(+), 21 deletions(-) diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index 1ccda62..ec65a35 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -40,6 +40,20 @@ extern "C" { pub fn lua_ext_fast_checkboolean(l: State, numarg: c_int) -> c_int; } +//----------------- +// 64 bit integers +//----------------- +extern "C" { + pub fn lua_ext_checkinteger64(l: State, numarg: c_int) -> i64; + pub fn lua_ext_checkuinteger64(l: State, numarg: c_int) -> u64; + pub fn lua_ext_getinteger64(l: State, numarg: c_int, out: *mut i64) -> c_int; + pub fn lua_ext_getuinteger64(l: State, numarg: c_int, out: *mut u64) -> c_int; + pub fn lua_ext_pushinteger64(l: State, value: i64) -> c_int; + pub fn lua_ext_pushuinteger64(l: State, value: u64) -> c_int; + pub fn lua_ext_tointeger64(l: State, numarg: c_int) -> i64; + pub fn lua_ext_touinteger64(l: State, numarg: c_int) -> u64; +} + //------- // Other //------- diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index eccc689..04abe6e 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -74,6 +74,7 @@ pub enum Type { Function = 6, Userdata = 7, Thread = 8, + Cdata = 10 } pub type RawNumber = c_double; diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index 6403726..b90be66 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -1,5 +1,5 @@ diff --git a/src/lj_api.c b/src/lj_api.c -index e9fc25b4..4559cdb5 100644 +index e9fc25b4..68996ee0 100644 --- a/src/lj_api.c +++ b/src/lj_api.c @@ -9,6 +9,8 @@ @@ -11,7 +11,16 @@ index e9fc25b4..4559cdb5 100644 #include "lj_obj.h" #include "lj_gc.h" #include "lj_err.h" -@@ -1317,3 +1319,85 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) +@@ -25,6 +27,8 @@ + #include "lj_vm.h" + #include "lj_strscan.h" + #include "lj_strfmt.h" ++#include "lj_cdata.h" ++#include "lualib.h" + + /* -- Common helper functions --------------------------------------------- */ + +@@ -1317,3 +1321,191 @@ LUA_API void lua_setallocf(lua_State *L, lua_Alloc f, void *ud) g->allocf = f; } @@ -67,33 +76,139 @@ index e9fc25b4..4559cdb5 100644 + } +} + -+static atomic_intptr_t BP3D_LUA_EXT_KEYREG = 0; -+static atomic_uint_fast32_t BP3D_LUA_EXT_KEYREG_REFS = 0; ++#if LJ_64 ++LUALIB_API int64_t lua_ext_checkinteger64(lua_State *L, int idx) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_INT64)) { ++ return (int64_t)cdataptr(cd); ++ } ++ } ++ return (int64_t)lua_ext_fast_checkinteger(L, idx); ++} + -+LUALIB_API void* lua_ext_keyreg_get() { -+ return (void*)BP3D_LUA_EXT_KEYREG; ++LUALIB_API uint64_t lua_ext_checkuinteger64(lua_State *L, int idx) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_UINT64)) { ++ return (int64_t)cdataptr(cd); ++ } ++ } ++ return (uint64_t)lua_ext_fast_checkinteger(L, idx); +} + -+LUALIB_API void* lua_ext_keyreg_ref(void* ptr) { -+ int flag = 0; -+ if (BP3D_LUA_EXT_KEYREG_REFS == 0) { -+ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; -+ flag = 1; ++LUALIB_API int lua_ext_getinteger64(lua_State *L, int idx, int64_t *out) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_INT64)) { ++ *out = *(int64_t*)cdataptr(cd); ++ return 1; ++ } else { ++ return 0; + } -+ (unsigned int)++BP3D_LUA_EXT_KEYREG_REFS; -+ if (flag == 1) { -+ return NULL; ++ } else if (LJ_LIKELY(tvisint(o))) { ++ *out = (int64_t)intV(o); ++ return 1; ++ } else if (LJ_LIKELY(tvisnum(o))) { ++ *out = (int64_t)numV(o); ++ return 1; ++ } ++ return 0; ++} ++ ++LUALIB_API int lua_ext_getuinteger64(lua_State *L, int idx, uint64_t *out) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_UINT64)) { ++ *out = *(uint64_t*)cdataptr(cd); ++ return 1; + } else { -+ return ptr; ++ return 0; + } ++ } else if (LJ_LIKELY(tvisint(o))) { ++ *out = (uint64_t)intV(o); ++ return 1; ++ } else if (LJ_LIKELY(tvisnum(o))) { ++ *out = (uint64_t)numV(o); ++ return 1; ++ } ++ return 0; +} + -+LUALIB_API void* lua_ext_keyreg_unref() { -+ unsigned int refs = (unsigned int)--BP3D_LUA_EXT_KEYREG_REFS; -+ if (refs == 0) { -+ void* ptr = (void*)BP3D_LUA_EXT_KEYREG; -+ BP3D_LUA_EXT_KEYREG = 0; -+ return ptr; ++LUALIB_API int64_t lua_ext_tointeger64(lua_State *L, int idx) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_INT64)) { ++ return *(int64_t*)cdataptr(cd); + } ++ } ++ return (int64_t)lua_tointeger(L, idx); ++} ++ ++LUALIB_API uint64_t lua_ext_touinteger64(lua_State *L, int idx) { ++ cTValue *o = index2adr(L, idx); ++ if (LJ_LIKELY(tviscdata(o))) { ++ GCcdata *cd = cdataV(o); ++ if (LJ_LIKELY(cd->ctypeid == CTID_UINT64)) { ++ return *(uint64_t*)cdataptr(cd); ++ } ++ } ++ return (uint64_t)lua_tointeger(L, idx); ++} ++ ++LUALIB_API int lua_ext_pushinteger64(lua_State *L, int64_t in) { ++ ctype_loadffi(L); ++ GCcdata *cd = lj_cdata_new_(L, CTID_INT64, 8); ++ *(int64_t*)cdataptr(cd) = in; ++ TValue *o = L->top; ++ setcdataV(L, o, cd); ++ incr_top(L); ++ return 1; ++} ++ ++LUALIB_API int lua_ext_pushuinteger64(lua_State *L, uint64_t in) { ++ ctype_loadffi(L); ++ GCcdata *cd = lj_cdata_new_(L, CTID_UINT64, 8); ++ *(uint64_t*)cdataptr(cd) = in; ++ TValue *o = L->top; ++ setcdataV(L, o, cd); ++ incr_top(L); ++ return 1; ++} ++#endif ++ ++static atomic_intptr_t BP3D_LUA_EXT_KEYREG = 0; ++static atomic_uint_fast32_t BP3D_LUA_EXT_KEYREG_REFS = 0; ++ ++LUALIB_API void* lua_ext_keyreg_get() { ++ return (void*)BP3D_LUA_EXT_KEYREG; ++} ++ ++LUALIB_API void* lua_ext_keyreg_ref(void* ptr) { ++ int flag = 0; ++ if (BP3D_LUA_EXT_KEYREG_REFS == 0) { ++ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; ++ flag = 1; ++ } ++ ++BP3D_LUA_EXT_KEYREG_REFS; ++ if (flag == 1) { + return NULL; ++ } else { ++ return ptr; ++ } ++} ++ ++LUALIB_API void* lua_ext_keyreg_unref() { ++ unsigned int refs = (unsigned int)--BP3D_LUA_EXT_KEYREG_REFS; ++ if (refs == 0) { ++ void* ptr = (void*)BP3D_LUA_EXT_KEYREG; ++ BP3D_LUA_EXT_KEYREG = 0; ++ return ptr; ++ } ++ return NULL; +} From e523b596946b481fc647b84cf7ecfa4aaf065eb2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 23 Aug 2025 16:59:53 +0200 Subject: [PATCH 454/527] Use 32 bits unsigned in bp3d.util.table.count --- core/src/libs/util/table.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/libs/util/table.rs b/core/src/libs/util/table.rs index 981a1b2..b10fc8a 100644 --- a/core/src/libs/util/table.rs +++ b/core/src/libs/util/table.rs @@ -88,7 +88,7 @@ decl_lib_func! { } decl_lib_func! { - fn count(src: LuaTable) -> u64 { + fn count(src: LuaTable) -> u32 { src.len() as _ } } From 03c87a19b85749b20e127e4e5ee09e25854e38e7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 23 Aug 2025 17:00:49 +0200 Subject: [PATCH 455/527] Added support for 64 bits integers and usize/isize to APIs --- core/src/vm/function/core.rs | 49 +++++++++++++++++++++++++++++++++--- core/src/vm/value/core.rs | 45 ++++++++++++++++++++++++++++++--- 2 files changed, 86 insertions(+), 8 deletions(-) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 6a758f3..69d51da 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::ext::{lua_ext_fast_checkboolean, lua_ext_fast_checkinteger, lua_ext_fast_checknumber}; +use crate::ffi::ext::{lua_ext_checkinteger64, lua_ext_checkuinteger64, lua_ext_fast_checkboolean, lua_ext_fast_checkinteger, lua_ext_fast_checknumber, lua_ext_pushinteger64, lua_ext_pushuinteger64}; use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_checkudata, luaL_testudata}; use crate::ffi::lua::{lua_isnumber, lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type}; use crate::util::core::SimpleDrop; @@ -139,7 +139,7 @@ where } } -macro_rules! impl_integer { +macro_rules! impl_integer_ty { ($($t: ty),*) => { $( unsafe impl SimpleDrop for $t {} @@ -149,7 +149,14 @@ macro_rules! impl_integer { vec![TypeName::Some(std::any::type_name::())] } } + )* + }; +} +macro_rules! impl_integer { + ($($t: ty),*) => { + impl_integer_ty!($($t),*); + $( impl FromParam<'_> for $t { #[inline(always)] unsafe fn from_param(vm: &Vm, index: i32) -> Self { @@ -175,8 +182,42 @@ macro_rules! impl_integer { }; } -#[cfg(target_pointer_width = "64")] -impl_integer!(i64, u64); +macro_rules! impl_integer_64 { + ($t: ty, $func: ident, $push_func: ident) => { + #[cfg(target_pointer_width = "64")] + impl_integer_ty!($t); + + #[cfg(target_pointer_width = "64")] + impl FromParam<'_> for $t { + #[inline(always)] + unsafe fn from_param(vm: &'_ Vm, index: i32) -> Self { + $func(vm.as_ptr(), index) as _ + } + + #[inline(always)] + fn try_from_param(vm: &'_ Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } + } + + #[cfg(target_pointer_width = "64")] + unsafe impl IntoParam for $t { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + unsafe { $push_func(vm.as_ptr(), self as _) } + } + } + } +} + +impl_integer_64!(i64, lua_ext_checkinteger64, lua_ext_pushinteger64); +impl_integer_64!(u64, lua_ext_checkuinteger64, lua_ext_pushuinteger64); + +impl_integer_64!(isize, lua_ext_checkinteger64, lua_ext_pushinteger64); +impl_integer_64!(usize, lua_ext_checkuinteger64, lua_ext_pushuinteger64); + +#[cfg(target_pointer_width = "32")] +impl_integer!(isize, usize); impl_integer!(i8, u8, i16, u16, i32, u32); diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index d6d1f70..c4920ed 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::borrow::Cow; +use crate::ffi::ext::{lua_ext_getinteger64, lua_ext_getuinteger64, lua_ext_pushinteger64, lua_ext_pushuinteger64, lua_ext_tointeger64, lua_ext_touinteger64}; use crate::ffi::laux::{luaL_setmetatable, luaL_testudata}; use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tointegerx, lua_tolstring, lua_tonumber, lua_tonumberx, lua_touserdata, lua_type, Type}; use crate::vm::error::{Error, TypeError}; @@ -152,11 +153,47 @@ macro_rules! impl_from_lua { }; } -#[cfg(target_pointer_width = "64")] -impl_from_lua!(i64, Number, lua_tointeger, lua_pushinteger, as _); +macro_rules! impl_from_lua_64 { + ($t: ty, $get_func: ident, $func: ident, $push_func: ident) => { + #[cfg(target_pointer_width = "64")] + impl FromLua<'_> for $t { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + $func(vm.as_ptr(), index) as _ + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + let mut out = 0; + let val = unsafe { $get_func(vm.as_ptr(), index, &mut out) }; + if val == 1 { + Ok(out as _) + } else { + Err(TypeError::from_stack(Type::Number, vm, index)) + } + } + } + + #[cfg(target_pointer_width = "64")] + unsafe impl IntoLua for $t { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { $push_func(vm.as_ptr(), self as _) as _ } + } + } + } +} + +impl_from_lua_64!(i64, lua_ext_getinteger64, lua_ext_tointeger64, lua_ext_pushinteger64); +impl_from_lua_64!(u64, lua_ext_getuinteger64, lua_ext_touinteger64, lua_ext_pushuinteger64); + +impl_from_lua_64!(isize, lua_ext_getinteger64, lua_ext_tointeger64, lua_ext_pushinteger64); +impl_from_lua_64!(usize, lua_ext_getuinteger64, lua_ext_touinteger64, lua_ext_pushuinteger64); + +#[cfg(target_pointer_width = "32")] +impl_from_lua!(isize, Number, lua_tointeger, lua_pushinteger, as _); -#[cfg(target_pointer_width = "64")] -impl_from_lua!(u64, Number, lua_tointeger, lua_pushinteger, as _); +#[cfg(target_pointer_width = "32")] +impl_from_lua!(usize, Number, lua_tointeger, lua_pushinteger, as _); impl_from_lua!(i8, Number, lua_tointeger, lua_pushinteger, as _); impl_from_lua!(u8, Number, lua_tointeger, lua_pushinteger, as _); From 8e0daa598dc27721f9b0e7b5cd57e6d80cdfa75a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 23 Aug 2025 17:09:16 +0200 Subject: [PATCH 456/527] Added initial set of tests for u64 and i64 --- core/tests/test_vm_integer64.rs | 68 +++++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 core/tests/test_vm_integer64.rs diff --git a/core/tests/test_vm_integer64.rs b/core/tests/test_vm_integer64.rs new file mode 100644 index 0000000..289be06 --- /dev/null +++ b/core/tests/test_vm_integer64.rs @@ -0,0 +1,68 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::ffi::lua::Type; +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::util::check_type_equals; + +#[test] +fn test_vm_u64() { + let vm = RootVm::new(); + let top = vm.top(); + let val: u64 = vm.run_code(c"return 2ULL^64ULL-1ULL").unwrap(); + assert!(check_type_equals(&vm, -1, Type::Cdata).is_ok()); + assert_eq!(val, u64::MAX); + vm.set_global(c"UINT64_MAX", u64::MAX).unwrap(); + assert_eq!(vm.run_code::<&str>(c"return tostring(UINT64_MAX)").unwrap(), "18446744073709551615ULL"); + vm.run_code::<()>(c"assert(UINT64_MAX == 2ULL^64ULL-1ULL)").unwrap(); + assert_eq!(top + 2, vm.top()); +} + +#[test] +fn test_vm_i64() { + let vm = RootVm::new(); + let top = vm.top(); + let val: i64 = vm.run_code(c"return 2LL^63LL-1LL").unwrap(); + assert!(check_type_equals(&vm, -1, Type::Cdata).is_ok()); + assert_eq!(val, i64::MAX); + let val: i64 = vm.run_code(c"return -2LL^63LL").unwrap(); + assert!(check_type_equals(&vm, -1, Type::Cdata).is_ok()); + assert_eq!(val, i64::MIN); + vm.set_global(c"INT64_MAX", i64::MAX).unwrap(); + vm.set_global(c"INT64_MIN", i64::MIN).unwrap(); + assert_eq!(vm.run_code::<&str>(c"return tostring(INT64_MAX)").unwrap(), "9223372036854775807LL"); + assert_eq!(vm.run_code::<&str>(c"return tostring(INT64_MIN)").unwrap(), "-9223372036854775808LL"); + vm.run_code::<()>(c"assert(INT64_MAX == 2LL^63LL-1LL)").unwrap(); + vm.run_code::<()>(c"assert(INT64_MIN == -2LL^63LL)").unwrap(); + assert_eq!(top + 4, vm.top()); +} + +#[test] +fn test_vm_i64_any() { + +} From 6e9a068e76e12bebf49dac064b7e68a4c5c6c011 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 24 Aug 2025 17:19:48 +0200 Subject: [PATCH 457/527] Introduced new FULL_TYPE constant on all userdata types to mitigate a possible safety bug where 2 userdatas with the name would exist in 2 distinct modules --- core/src/libs/lua/module.rs | 10 ++++--- core/src/libs/os/instant.rs | 6 ++--- core/src/libs/os/time.rs | 6 ++--- core/src/macro/userdata.rs | 44 ++++++++++++++++++++++++++++--- core/src/macro/userdata_func.rs | 8 +++--- core/src/vm/core/vm.rs | 2 +- core/src/vm/function/core.rs | 6 ++--- core/src/vm/userdata/any.rs | 2 +- core/src/vm/userdata/core.rs | 8 +++--- core/src/vm/userdata/interface.rs | 13 ++++----- core/src/vm/userdata/util.rs | 4 +-- core/src/vm/value/core.rs | 4 +-- core/tests/test_vm_userdata.rs | 34 ++++++++++++++---------- 13 files changed, 98 insertions(+), 49 deletions(-) diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index 0db9be0..7707819 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -26,7 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::decl_userdata_mut; +use crate::{decl_userdata, impl_userdata_mut}; use crate::libs::Lib; use crate::util::module::ModuleManager; use crate::util::module::Result; @@ -55,9 +55,9 @@ impl Module { } } -struct ModuleManagerWrapper(ModuleManager); +decl_userdata!(struct ModuleManagerWrapper(ModuleManager)); -decl_userdata_mut! { +impl_userdata_mut! { impl ModuleManagerWrapper { fn load(this: &mut ModuleManagerWrapper, vm: &Vm, lib: &str, plugin: &str) -> Result<()> { this.0.load(lib, plugin, vm) @@ -65,6 +65,10 @@ decl_userdata_mut! { } } +//TODO: Re-write as a global to avoid safety bug where multiple instances of the module manager +// exists for the application. +//TODO: Put libs as global to allow threads and maybe future unload. + impl Lib for Module { const NAMESPACE: &'static str = ""; diff --git a/core/src/libs/os/instant.rs b/core/src/libs/os/instant.rs index d1c820c..cea5b7f 100644 --- a/core/src/libs/os/instant.rs +++ b/core/src/libs/os/instant.rs @@ -29,11 +29,11 @@ use crate::libs::Lib; use crate::util::Namespace; use crate::vm::function::types::RFunction; -use crate::{decl_lib_func, decl_userdata}; +use crate::{decl_lib_func, decl_userdata, impl_userdata}; -struct InstantWrapper(bp3d_os::time::Instant); +decl_userdata!(struct InstantWrapper(bp3d_os::time::Instant)); -decl_userdata! { +impl_userdata! { impl InstantWrapper { fn elapsed(this: &InstantWrapper) -> f64 { this.0.elapsed().as_secs_f64() diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index c223189..c8457b0 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -32,7 +32,7 @@ use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; use crate::vm::table::Table; use crate::vm::Vm; -use crate::{decl_lib_func, decl_userdata}; +use crate::{decl_lib_func, decl_userdata, impl_userdata}; use bp3d_os::time::{LocalOffsetDateTime, MonthExt}; use bp3d_util::simple_error; use time::error::{ComponentRange, Format, InvalidFormatDescription}; @@ -47,9 +47,9 @@ simple_error! { } } -struct OffsetDateTimeWrapper(OffsetDateTime); +decl_userdata!(struct OffsetDateTimeWrapper(OffsetDateTime)); -decl_userdata! { +impl_userdata! { impl OffsetDateTimeWrapper { fn format(this: &OffsetDateTimeWrapper, format: &str) -> Result { let desc = parse(format)?; diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index 86f79ef..7ba8f90 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -40,8 +40,6 @@ macro_rules! _impl_userdata_static { macro_rules! _impl_userdata { ($obj_name: ident, $($fn_name: ident),* { $([$($static_registry_tokens: tt)*];)* }) => { impl $crate::vm::userdata::UserData for $obj_name { - const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($obj_name); - fn register(registry: &$crate::vm::userdata::core::Registry) -> std::result::Result<(), $crate::vm::userdata::Error> { $( let f = $obj_name::$fn_name()?; @@ -60,6 +58,46 @@ macro_rules! _impl_userdata { #[macro_export] macro_rules! decl_userdata { + ( + $(#[$meta: meta])* + $vis: vis struct $name:ident + ) => { + $(#[$meta])* + $vis struct $name; + + impl $crate::vm::userdata::UserDataType for $name { + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); + const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; + } + }; + ( + $(#[$meta: meta])* + $vis: vis struct $name:ident { $($struct_decl: tt)* } + ) => { + $(#[$meta])* + $vis struct $name { $($struct_decl)* } + + impl $crate::vm::userdata::UserDataType for $name { + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); + const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; + } + }; + ( + $(#[$meta: meta])* + $vis: vis struct $name:ident($($struct_decl: tt)*) + ) => { + $(#[$meta])* + $vis struct $name($($struct_decl)*); + + impl $crate::vm::userdata::UserDataType for $name { + const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); + const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; + } + }; +} + +#[macro_export] +macro_rules! impl_userdata { ( impl $obj_name: ident { $( @@ -82,7 +120,7 @@ macro_rules! decl_userdata { } #[macro_export] -macro_rules! decl_userdata_mut { +macro_rules! impl_userdata_mut { ( impl $obj_name: ident { $( diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index 384af6a..3d3b6f1 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -36,7 +36,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *mut $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -62,7 +62,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &mut $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *mut $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *mut $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -88,7 +88,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *const $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { @@ -113,7 +113,7 @@ macro_rules! decl_userdata_func { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserData>::CLASS_NAME.as_ptr()) } as *const $obj_name; + let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; #[inline(always)] extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *const $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 11fc254..b7161ee 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -82,7 +82,7 @@ impl Vm { warning!("Failed to register userdata type {:?}: {}", T::CLASS_NAME, e); unsafe { lua_pushnil(self.l); - lua_setfield(self.l, REGISTRYINDEX, T::CLASS_NAME.as_ptr()); + lua_setfield(self.l, REGISTRYINDEX, T::FULL_TYPE.as_ptr()); } Err(e) } diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 69d51da..2495e49 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -314,7 +314,7 @@ unsafe impl IntoParam for () { impl LuaType for &T { fn lua_type() -> Vec { vec![TypeName::Some(unsafe { - T::CLASS_NAME.to_str().unwrap_unchecked() + T::FULL_TYPE.to_str().unwrap_unchecked() })] } } @@ -322,13 +322,13 @@ impl LuaType for &T { impl<'a, T: UserData> FromParam<'a> for &'a T { #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> &'a T { - let obj_ptr = luaL_checkudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) as *const T; + let obj_ptr = luaL_checkudata(vm.as_ptr(), index, T::FULL_TYPE.as_ptr()) as *const T; unsafe { &*obj_ptr } } #[inline(always)] fn try_from_param(vm: &'a Vm, index: i32) -> Option { - let ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + let ptr = unsafe { luaL_testudata(vm.as_ptr(), index, T::FULL_TYPE.as_ptr()) } as *const T; if ptr.is_null() { None } else { diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 474ca42..a80f67a 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -138,7 +138,7 @@ impl<'a> AnyUserData<'a> { /// code otherwise using this function is UB. pub unsafe fn get_mut(&mut self) -> crate::vm::Result<&mut T> { let this_ptr = - unsafe { luaL_testudata(self.vm.as_ptr(), self.index, T::CLASS_NAME.as_ptr()) } + unsafe { luaL_testudata(self.vm.as_ptr(), self.index, T::FULL_TYPE.as_ptr()) } as *mut T; if this_ptr.is_null() { return Err(Error::Type(TypeError { diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index f0f6d59..3063757 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -127,7 +127,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { return Err(Error::Alignment(align_of::())); } Table::new(vm); - let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + let res = unsafe { luaL_newmetatable(vm.as_ptr(), T::FULL_TYPE.as_ptr()) }; // Pop the userdata metatable alongside its statics table from the stack. if res != 1 { unsafe { lua_settop(vm.as_ptr(), -3) }; @@ -215,7 +215,7 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { if std::mem::needs_drop::() { extern "C-unwind" fn run_drop(l: State) -> i32 { unsafe { - let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + let udata = luaL_checkudata(l, 1, T::FULL_TYPE.as_ptr()) as *mut T; lua_pushnil(l); lua_setmetatable(l, 1); std::ptr::drop_in_place(udata); @@ -238,7 +238,7 @@ impl Registry<'_, T, C> { pub fn add_gc_method_with_lua_drop(&self) { extern "C-unwind" fn run_lua_drop(l: State) -> i32 { unsafe { - let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + let udata = luaL_checkudata(l, 1, T::FULL_TYPE.as_ptr()) as *mut T; lua_pushnil(l); lua_setmetatable(l, 1); (*udata).lua_drop(&Vm::from_raw(l)); @@ -247,7 +247,7 @@ impl Registry<'_, T, C> { } extern "C-unwind" fn run_lua_drop_full(l: State) -> i32 { unsafe { - let udata = luaL_checkudata(l, 1, T::CLASS_NAME.as_ptr()) as *mut T; + let udata = luaL_checkudata(l, 1, T::FULL_TYPE.as_ptr()) as *mut T; lua_pushnil(l); lua_setmetatable(l, 1); (*udata).lua_drop(&Vm::from_raw(l)); diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index 5055dba..c36fa21 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -34,21 +34,22 @@ use std::ffi::CStr; /// This trait represents all types of UserData. An UserData is a type with a maximum alignment of 8 /// with its memory tied to the Lua GC. #[cfg(feature = "send")] -pub trait UserData: Send + Sized { - const CLASS_NAME: &'static CStr; - +pub trait UserData: Send + Sized + UserDataType { fn register(registry: &Registry) -> Result<(), Error>; } /// This trait represents all types of UserData. An UserData is a type with a maximum alignment of 8 /// with its memory tied to the Lua GC. #[cfg(not(feature = "send"))] -pub trait UserData: Sized { - const CLASS_NAME: &'static CStr; - +pub trait UserData: Sized + UserDataType { fn register(registry: &Registry) -> Result<(), Error>; } +pub trait UserDataType { + const CLASS_NAME: &'static CStr; + const FULL_TYPE: &'static CStr; +} + /// This trait represents an UserData which is never borrowed mutably (excluding interior mutability /// patterns). /// diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs index ca5ea19..89199e4 100644 --- a/core/src/vm/userdata/util.rs +++ b/core/src/vm/userdata/util.rs @@ -46,7 +46,7 @@ use crate::vm::Vm; /// /// returns: Option
pub fn get_static_table(vm: &Vm) -> Option> { - get_static_table_by_name(vm, T::CLASS_NAME) + get_static_table_by_name(vm, T::FULL_TYPE) } /// Returns the static table attached to the given UserData type. @@ -87,7 +87,7 @@ pub fn get_static_table_by_name<'a>(vm: &'a Vm, name: &CStr) -> Option pub fn get_metatable(vm: &Vm) -> Option> { - get_metatable_by_name(vm, T::CLASS_NAME) + get_metatable_by_name(vm, T::FULL_TYPE) } /// Returns the metatable attached to the given UserData type. diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index c4920ed..be7eb4e 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -232,7 +232,7 @@ impl<'a, T: UserDataImmutable> FromLua<'a> for &'a T { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let this_ptr = - unsafe { luaL_testudata(vm.as_ptr(), index, T::CLASS_NAME.as_ptr()) } as *const T; + unsafe { luaL_testudata(vm.as_ptr(), index, T::FULL_TYPE.as_ptr()) } as *const T; if this_ptr.is_null() { return Err(Error::Type(TypeError { expected: Type::Userdata, @@ -342,7 +342,7 @@ unsafe impl IntoLua for T { fn into_lua(self, vm: &Vm) -> u16 { let userdata = unsafe { lua_newuserdata(vm.as_ptr(), size_of::()) } as *mut T; unsafe { userdata.write(self) }; - unsafe { luaL_setmetatable(vm.as_ptr(), T::CLASS_NAME.as_ptr()) }; + unsafe { luaL_setmetatable(vm.as_ptr(), T::FULL_TYPE.as_ptr()) }; 1 } } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index feb7ed8..73d2d32 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -32,7 +32,7 @@ use bp3d_lua::ffi::lua::RawNumber; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::{AnyUserData, LuaDrop}; use bp3d_lua::vm::{RootVm, Vm}; -use bp3d_lua::{decl_lib_func, decl_userdata, decl_userdata_mut}; +use bp3d_lua::{decl_lib_func, decl_userdata, impl_userdata, impl_userdata_mut}; use std::sync::Mutex; use bp3d_lua::util::Namespace; @@ -41,7 +41,7 @@ static MUTEX: Mutex<()> = Mutex::new(()); static mut DROP_COUNTER: i32 = 0; static mut LUA_DROP_COUNTER: i32 = 0; -pub struct MyInt(i64); +decl_userdata!(pub struct MyInt(i64)); impl LuaDrop for MyInt { fn lua_drop(&self, _: &Vm) { @@ -59,7 +59,7 @@ impl Drop for MyInt { } } -decl_userdata! { +impl_userdata! { impl MyInt { fn tonumber(this: &MyInt) -> RawNumber { this.0 as _ @@ -99,10 +99,12 @@ decl_userdata! { } } -#[derive(Debug)] -pub struct BrokenObject; +decl_userdata! { + #[derive(Debug)] + pub struct BrokenObject +} -decl_userdata_mut! { +impl_userdata_mut! { impl BrokenObject { // this should blow up at init time fn replace(this: &mut BrokenObject, other: &BrokenObject) -> () { @@ -111,17 +113,19 @@ decl_userdata_mut! { } } -pub struct BrokenObject2(pub u128); +decl_userdata!(pub struct BrokenObject2(pub u128)); -decl_userdata! { +impl_userdata! { impl BrokenObject2 { } } -#[derive(Debug)] -pub struct BrokenObject3; - decl_userdata! { + #[derive(Debug)] + pub struct BrokenObject3 +} + +impl_userdata! { impl BrokenObject3 { fn __gc(this: &BrokenObject3) -> () { println!("{:?}", this); @@ -129,10 +133,12 @@ decl_userdata! { } } -#[derive(Debug)] -pub struct BrokenObject4; - decl_userdata! { + #[derive(Debug)] + pub struct BrokenObject4 +} + +impl_userdata! { impl BrokenObject4 { fn __metatable(this: &BrokenObject3) -> () { println!("{:?}", this); From 9d1d56d88bb4c071aa606e3e8d26fd30f66ebd3f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 24 Aug 2025 17:23:48 +0200 Subject: [PATCH 458/527] Added unsafe to UserDataType in case of manual implementation --- core/src/macro/userdata.rs | 6 +++--- core/src/vm/userdata/interface.rs | 8 +++++++- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index 7ba8f90..c757d5f 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -65,7 +65,7 @@ macro_rules! decl_userdata { $(#[$meta])* $vis struct $name; - impl $crate::vm::userdata::UserDataType for $name { + unsafe impl $crate::vm::userdata::UserDataType for $name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; } @@ -77,7 +77,7 @@ macro_rules! decl_userdata { $(#[$meta])* $vis struct $name { $($struct_decl)* } - impl $crate::vm::userdata::UserDataType for $name { + unsafe impl $crate::vm::userdata::UserDataType for $name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; } @@ -89,7 +89,7 @@ macro_rules! decl_userdata { $(#[$meta])* $vis struct $name($($struct_decl)*); - impl $crate::vm::userdata::UserDataType for $name { + unsafe impl $crate::vm::userdata::UserDataType for $name { const CLASS_NAME: &'static std::ffi::CStr = $crate::c_stringify!($name); const FULL_TYPE: &'static std::ffi::CStr = unsafe { std::ffi::CStr::from_ptr(concat!(module_path!(), "::", stringify!($name), "\0").as_ptr() as _) }; } diff --git a/core/src/vm/userdata/interface.rs b/core/src/vm/userdata/interface.rs index c36fa21..69baa96 100644 --- a/core/src/vm/userdata/interface.rs +++ b/core/src/vm/userdata/interface.rs @@ -45,7 +45,13 @@ pub trait UserData: Sized + UserDataType { fn register(registry: &Registry) -> Result<(), Error>; } -pub trait UserDataType { +/// This trait represents the type identification of an userdata in a Lua VM. +/// +/// # Safety +/// +/// This is UB if FULL_TYPE is duplicated with another userdata type. On the other hand no +/// additional restriction is imposed on CLASS_NAME. +pub unsafe trait UserDataType { const CLASS_NAME: &'static CStr; const FULL_TYPE: &'static CStr; } From 75991bd771acdcd7ceee6733848f2dc30bc83eab Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 24 Aug 2025 17:33:21 +0200 Subject: [PATCH 459/527] Fixed bug with debug registry and latest userdata refactor as well as added utility method to dump the class name of an userdata --- core/src/libs/lua/debug.rs | 10 ++++++++++ core/src/vm/core/debug.rs | 2 +- definitions/bp3d.lua.debug.lua | 18 ++++++++++++------ 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs index 3154b64..1a0c22a 100644 --- a/core/src/libs/lua/debug.rs +++ b/core/src/libs/lua/debug.rs @@ -106,6 +106,15 @@ decl_lib_func! { } } +decl_lib_func! { + fn dump_class_name(vm: &Vm, class: &str) -> crate::vm::Result { + let str = CString::from_str(class).map_err(|_| Error::Null)?; + let tbl = get_metatable_by_name(vm, &str).ok_or(Error::Unknown)?; + let class_name: &str = tbl.get(c"__metatable")?; + Ok(class_name.into()) + } +} + pub struct Debug; impl Lib for Debug { @@ -118,6 +127,7 @@ impl Lib for Debug { ("dumpClasses", RFunction::wrap(dump_classes)), ("dumpStaticTable", RFunction::wrap(dump_static_table)), ("dumpMetaTable", RFunction::wrap(dump_meta_table)), + ("dumpClassName", RFunction::wrap(dump_class_name)) ]) } } diff --git a/core/src/vm/core/debug.rs b/core/src/vm/core/debug.rs index 6c63775..24ab251 100644 --- a/core/src/vm/core/debug.rs +++ b/core/src/vm/core/debug.rs @@ -66,7 +66,7 @@ impl DebugItem for T { impl DebugItem for T { fn describe() -> String { - T::CLASS_NAME.to_string_lossy().into() + T::FULL_TYPE.to_string_lossy().into() } } diff --git a/definitions/bp3d.lua.debug.lua b/definitions/bp3d.lua.debug.lua index fc10fdf..8589298 100644 --- a/definitions/bp3d.lua.debug.lua +++ b/definitions/bp3d.lua.debug.lua @@ -48,14 +48,20 @@ bp3d.lua.debug.dumpLibs = function() end --- @return [string] bp3d.lua.debug.dumpClasses = function() end ---- Dump the static members of the given UserData type.. +--- Dump the static members of the given UserData class.. --- ---- @param className string the UserData class name to dump. +--- @param class string the UserData class to dump. --- @return [string] -bp3d.lua.debug.dumpStaticTable = function(className) end +bp3d.lua.debug.dumpStaticTable = function(class) end ---- Dump the instance members of the given UserData type.. +--- Dump the instance members of the given UserData class.. --- ---- @param className string the UserData class name to dump. +--- @param class string the UserData class to dump. --- @return [string] -bp3d.lua.debug.dumpMetaTable = function(className) end +bp3d.lua.debug.dumpMetaTable = function(class) end + +--- Dump the class name of the given UserData class.. +--- +--- @param class string the UserData class to dump. +--- @return [string] +bp3d.lua.debug.dumpClassName = function(class) end From 5dab70bfb11263353b069fe6243d417bf5a2a23a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 24 Aug 2025 22:47:41 +0200 Subject: [PATCH 460/527] Use u32 rather than u64 for performance --- testbin/src/context_opt.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index e465335..74720fa 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -34,18 +34,18 @@ use mlua::{Function, Lua, UserDataMethods}; use std::time::Duration; struct TestContext { - value3: Vec, + value3: Vec, } decl_closure! { - fn context_push |ctx: ContextMut| (val: u64) -> () { + fn context_push |ctx: ContextMut| (val: u32) -> () { let mut ctx = ctx; ctx.value3.push(val); } } decl_closure! { - fn context_pop |ctx: ContextMut| () -> Option { + fn context_pop |ctx: ContextMut| () -> Option { let mut ctx = ctx; ctx.value3.pop() } @@ -54,7 +54,7 @@ decl_closure! { pub fn test_context_mlua() -> Duration { let lua = Lua::new(); lua.register_userdata_type::(|reg| { - reg.add_method_mut("push", |_, this, val: u64| { + reg.add_method_mut("push", |_, this, val: u32| { this.value3.push(val); Ok(()) }); From 59d5966085bb837c0825517a77176cae22f23745 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 26 Aug 2025 21:27:48 +0200 Subject: [PATCH 461/527] Added support for 64 bits integers to Any type --- core/src/vm/value/any.rs | 13 +++++++++++++ core/tests/test_vm_integer64.rs | 11 ++++++++++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 43527fa..0529baa 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -48,6 +48,8 @@ pub enum AnyValue<'a, T, U, R> { None, Nil, Number(f64), + Int64(i64), + UInt64(u64), Boolean(bool), String(&'a str), Buffer(&'a [u8]), @@ -72,6 +74,8 @@ impl Display for AnyValue<'_, T, U, R> { AnyValue::Table(v) => write!(f, "{}", v), AnyValue::UserData(v) => write!(f, "{}", v), AnyValue::Thread(v) => write!(f, "{}", v), + AnyValue::Int64(v) => write!(f, "{}", v), + AnyValue::UInt64(v) => write!(f, "{}", v) } } } @@ -89,6 +93,8 @@ impl AnyValue<'_, T, U, R> { AnyValue::Table(_) => Type::Table, AnyValue::UserData(_) => Type::Userdata, AnyValue::Thread(_) => Type::Thread, + AnyValue::Int64(_) => Type::Cdata, + AnyValue::UInt64(_) => Type::Cdata } } @@ -135,6 +141,8 @@ unsafe impl IntoLua for Any<'_> { AnyValue::Table(v) => v.into_lua(vm), AnyValue::UserData(_) => 0, AnyValue::Thread(_) => 0, + AnyValue::Int64(v) => v.into_lua(vm), + AnyValue::UInt64(v) => v.into_lua(vm) } } } @@ -181,6 +189,11 @@ impl<'a, T: FromLua<'a>, U: FromLua<'a>, R: FromLua<'a>> FromLua<'a> for AnyValu Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }) } Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), + Type::Cdata => { + i64::from_lua(vm, index).map(AnyValue::Int64) + .or_else(|_| u64::from_lua(vm, index).map(AnyValue::UInt64)) + .map_err(|_| Error::UnsupportedType(ty)) + }, } } } diff --git a/core/tests/test_vm_integer64.rs b/core/tests/test_vm_integer64.rs index 289be06..cf129ea 100644 --- a/core/tests/test_vm_integer64.rs +++ b/core/tests/test_vm_integer64.rs @@ -28,6 +28,7 @@ use bp3d_lua::ffi::lua::Type; use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::any::Any; use bp3d_lua::vm::value::util::check_type_equals; #[test] @@ -64,5 +65,13 @@ fn test_vm_i64() { #[test] fn test_vm_i64_any() { - + let vm = RootVm::new(); + let top = vm.top(); + let val: Any = vm.run_code(c"return 2ULL^64ULL-1ULL").unwrap(); + let val2: Any = vm.run_code(c"return 2LL^63LL-1LL").unwrap(); + assert_eq!(val.ty(), Type::Cdata); + assert_eq!(val, Any::UInt64(u64::MAX)); + assert_eq!(val2.ty(), Type::Cdata); + assert_eq!(val2, Any::Int64(i64::MAX)); + assert_eq!(top + 2, vm.top()); } From 8daafedd6309c3f2a610c1ebcde94c05256cbc1e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 27 Aug 2025 22:03:52 +0200 Subject: [PATCH 462/527] Added back ImmutableType to i64 and u64 --- core/src/vm/value/core.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index be7eb4e..89306c8 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -180,6 +180,8 @@ macro_rules! impl_from_lua_64 { unsafe { $push_func(vm.as_ptr(), self as _) as _ } } } + + unsafe impl ImmutableValue for $t {} } } From 68bf3e809606c235b8088e0ac7b8df91eec044e3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 27 Aug 2025 22:05:01 +0200 Subject: [PATCH 463/527] Added new Int53 and UInt53 types to bring back old behavior --- core/src/util/core.rs | 14 +++ core/src/vm/value/integer53.rs | 209 +++++++++++++++++++++++++++++++++ core/src/vm/value/mod.rs | 1 + core/src/vm/value/types.rs | 2 + 4 files changed, 226 insertions(+) create mode 100644 core/src/vm/value/integer53.rs diff --git a/core/src/util/core.rs b/core/src/util/core.rs index 66cff27..0d90764 100644 --- a/core/src/util/core.rs +++ b/core/src/util/core.rs @@ -28,8 +28,11 @@ //! Core rust utilities module. +use core::fmt; use std::borrow::Cow; +use std::error::Error; use std::ffi::{CStr, CString, OsStr}; +use std::fmt::Display; use std::path::Path; pub trait AnyStr { @@ -68,3 +71,14 @@ unsafe impl SimpleDrop for &T {} unsafe impl SimpleDrop for &[u8] {} unsafe impl SimpleDrop for &OsStr {} unsafe impl SimpleDrop for &Path {} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub struct TryFromIntError; + +impl Display for TryFromIntError { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + fmt.write_str("out of range integral type conversion attempted") + } +} + +impl Error for TryFromIntError { } diff --git a/core/src/vm/value/integer53.rs b/core/src/vm/value/integer53.rs new file mode 100644 index 0000000..101421d --- /dev/null +++ b/core/src/vm/value/integer53.rs @@ -0,0 +1,209 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::ffi::ext::lua_ext_fast_checkinteger; +use crate::ffi::lua::{lua_pushinteger, lua_tointeger, RawInteger, Type}; +use crate::util::core::{SimpleDrop, TryFromIntError}; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::util::{LuaType, TypeName}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; +use crate::vm::value::util::check_type_equals; +use crate::vm::Vm; + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone, Debug)] +pub struct Int53(i64); + +#[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone, Debug)] +pub struct UInt53(u64); + +impl Int53 { + pub const MIN: Int53 = Int53(-2^52); + pub const MAX: Int53 = Int53(2^52 - 1); + + #[inline(always)] + pub const fn to_i64(self) -> i64 { + self.0 + } +} + +impl UInt53 { + pub const MIN: UInt53 = UInt53(0); + pub const MAX: UInt53 = UInt53(2^53 - 1); + + #[inline(always)] + pub const fn to_u64(self) -> u64 { + self.0 + } +} + +impl From for i64 { + #[inline(always)] + fn from(val: Int53) -> i64 { + val.to_i64() + } +} + +impl From for u64 { + #[inline(always)] + fn from(val: UInt53) -> u64 { + val.to_u64() + } +} + +impl TryFrom for UInt53 { + type Error = TryFromIntError; + + fn try_from(value: Int53) -> Result { + if value.0 < 0 { + Err(TryFromIntError) + } else { + Ok(UInt53(value.0 as u64)) + } + } +} + +impl TryFrom for Int53 { + type Error = TryFromIntError; + + fn try_from(value: UInt53) -> Result { + if value.0 > Int53::MAX.0 as _ { + Err(TryFromIntError) + } else { + Ok(Int53(value.0 as i64)) + } + } +} + +impl TryFrom for Int53 { + type Error = TryFromIntError; + + fn try_from(value: i64) -> Result { + if value > Int53::MAX.0 || value < Int53::MIN.0 { + Err(TryFromIntError) + } else { + Ok(Int53(value)) + } + } +} + +impl TryFrom for Int53 { + type Error = TryFromIntError; + + fn try_from(value: u64) -> Result { + if value > Int53::MAX.0 as _ { + Err(TryFromIntError) + } else { + Ok(Int53(value as _)) + } + } +} + +impl TryFrom for UInt53 { + type Error = TryFromIntError; + + fn try_from(value: i64) -> Result { + if value < 0 || value > UInt53::MAX.0 as _ { + Err(TryFromIntError) + } else { + Ok(UInt53(value as _)) + } + } +} + +impl TryFrom for UInt53 { + type Error = TryFromIntError; + + fn try_from(value: u64) -> Result { + if value > UInt53::MAX.0 as _ { + Err(TryFromIntError) + } else { + Ok(UInt53(value as _)) + } + } +} + +macro_rules! impl_from_lua { + ($t: ty, $expected: ident, $func: ident, $push_func: ident) => { + unsafe impl SimpleDrop for $t {} + + impl LuaType for $t { + fn lua_type() -> Vec { + vec![TypeName::Some(std::any::type_name::())] + } + } + + impl FromLua<'_> for $t { + #[inline(always)] + unsafe fn from_lua_unchecked(vm: &Vm, index: i32) -> Self { + Self($func(vm.as_ptr(), index) as _) + } + + fn from_lua(vm: &Vm, index: i32) -> crate::vm::Result { + check_type_equals(vm, index, Type::$expected)?; + Ok(Self(unsafe { $func(vm.as_ptr(), index) as _ })) + } + } + + unsafe impl IntoLua for $t { + #[inline(always)] + fn into_lua(self, vm: &Vm) -> u16 { + unsafe { + $push_func(vm.as_ptr(), self.0 as _); + 1 + } + } + } + + unsafe impl ImmutableValue for $t {} + + impl FromParam<'_> for $t { + #[inline(always)] + unsafe fn from_param(vm: &Vm, index: i32) -> Self { + Self(lua_ext_fast_checkinteger(vm.as_ptr(), index) as _) + } + + #[inline(always)] + fn try_from_param(vm: &Vm, index: i32) -> Option { + FromLua::from_lua(vm, index).ok() + } + } + + unsafe impl IntoParam for $t { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + unsafe { + lua_pushinteger(vm.as_ptr(), self.0 as _); + 1 + } + } + } + }; +} + +impl_from_lua!(Int53, Number, lua_tointeger, lua_pushinteger); +impl_from_lua!(UInt53, Number, lua_tointeger, lua_pushinteger); diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index 28e7196..c06f834 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -34,5 +34,6 @@ pub mod types; pub mod util; mod raw_ptr; mod unknown; +mod integer53; pub use interface::*; diff --git a/core/src/vm/value/types.rs b/core/src/vm/value/types.rs index 0976f5c..7ad5eaa 100644 --- a/core/src/vm/value/types.rs +++ b/core/src/vm/value/types.rs @@ -34,6 +34,8 @@ pub use super::function::Function; pub use super::raw_ptr::RawPtr; pub use super::unknown::Unknown; +pub use super::integer53::*; + #[derive(Copy, Clone, PartialOrd, PartialEq, Debug)] pub struct Number(pub RawNumber); From 0926adc7a128d0401dd60ae776cb189c55eaabbe Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 27 Aug 2025 22:44:33 +0200 Subject: [PATCH 464/527] Fixed bug in integer53 --- core/src/vm/value/integer53.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/vm/value/integer53.rs b/core/src/vm/value/integer53.rs index 101421d..e63a646 100644 --- a/core/src/vm/value/integer53.rs +++ b/core/src/vm/value/integer53.rs @@ -42,8 +42,8 @@ pub struct Int53(i64); pub struct UInt53(u64); impl Int53 { - pub const MIN: Int53 = Int53(-2^52); - pub const MAX: Int53 = Int53(2^52 - 1); + pub const MIN: Int53 = Int53(-(2 << 51)); + pub const MAX: Int53 = Int53((2 << 51) - 1); #[inline(always)] pub const fn to_i64(self) -> i64 { @@ -53,7 +53,7 @@ impl Int53 { impl UInt53 { pub const MIN: UInt53 = UInt53(0); - pub const MAX: UInt53 = UInt53(2^53 - 1); + pub const MAX: UInt53 = UInt53((2 << 52) - 1); #[inline(always)] pub const fn to_u64(self) -> u64 { From 4f8b0a90e05b2e2fe4f9100bd218da04796373ba Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 28 Aug 2025 21:45:53 +0200 Subject: [PATCH 465/527] Added support for cdata to to_integer and added new to_uinteger --- core/src/vm/value/any.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index 0529baa..ddbd477 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -111,12 +111,29 @@ impl AnyValue<'_, T, U, R> { } } - pub fn to_integer(&self) -> Result { + pub fn to_integer(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v as _), AnyValue::String(v) => { - crate::ffi::lua::RawInteger::from_str(v).map_err(|_| Error::ParseInt) + i64::from_str(v).map_err(|_| Error::ParseInt) } + AnyValue::Int64(v) => Ok(*v), + AnyValue::UInt64(v) => Ok(*v as _), + _ => Err(Error::Type(TypeError { + expected: Type::Number, + actual: self.ty(), + })), + } + } + + pub fn to_uinteger(&self) -> Result { + match self { + AnyValue::Number(v) => Ok(*v as _), + AnyValue::String(v) => { + u64::from_str(v).map_err(|_| Error::ParseInt) + } + AnyValue::Int64(v) => Ok(*v as _), + AnyValue::UInt64(v) => Ok(*v), _ => Err(Error::Type(TypeError { expected: Type::Number, actual: self.ty(), From 400f89e3c39dea2310a001788d61fd331f225148 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 28 Aug 2025 22:01:23 +0200 Subject: [PATCH 466/527] Added new bp3d.util.num base library --- core/src/libs/util/mod.rs | 4 +- core/src/libs/util/num.rs | 77 ++++++++++++++++++++++++++++++++++++++ core/tests/test_vm_libs.rs | 18 +++++++++ 3 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 core/src/libs/util/num.rs diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index 4db407f..c8cc8a5 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -29,11 +29,13 @@ mod string; mod table; mod utf8; +mod num; pub use string::String; pub use table::Table; pub use utf8::Utf8; +pub use num::Num; // Workaround for language defect #22259. #[allow(non_upper_case_globals)] -pub const Util: (Table, String, Utf8) = (Table, String, Utf8); +pub const Util: (Table, String, Utf8, Num) = (Table, String, Utf8, Num); diff --git a/core/src/libs/util/num.rs b/core/src/libs/util/num.rs new file mode 100644 index 0000000..efc2ab1 --- /dev/null +++ b/core/src/libs/util/num.rs @@ -0,0 +1,77 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use crate::decl_lib_func; +use crate::libs::Lib; +use crate::util::Namespace; +use crate::vm::function::types::RFunction; +use crate::vm::value::any::Any; +use crate::vm::value::types::{Int53, UInt53}; + +decl_lib_func! { + fn toistring(val: Any) -> crate::vm::Result { + let val = val.to_integer()?; + Ok(val.to_string()) + } +} + +decl_lib_func! { + fn toustring(val: Any) -> crate::vm::Result { + let val = val.to_uinteger()?; + Ok(val.to_string()) + } +} + +pub struct Num; + +impl Lib for Num { + const NAMESPACE: &'static str = "bp3d.util.num"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add([ + ("UINT53_MAX", UInt53::MAX), + ("UINT53_MIN", UInt53::MIN) + ])?; + namespace.add([ + ("INT53_MAX", Int53::MAX), + ("INT53_MIN", Int53::MIN) + ])?; + namespace.add([ + ("UINT64_MAX", u64::MAX), + ("UINT64_MIN", u64::MIN) + ])?; + namespace.add([ + ("INT64_MAX", i64::MAX), + ("INT64_MIN", i64::MIN) + ])?; + namespace.add([ + ("toistring", RFunction::wrap(toistring)), + ("toustring", RFunction::wrap(toustring)) + ]) + } +} diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 588375d..4fef503 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -309,3 +309,21 @@ fn test_vm_lib_debug() { assert(#stack > 0) ").unwrap(); } + +#[test] +fn test_vm_lib_util_num() { + let mut vm = RootVm::new(); + Util.register(&mut vm).unwrap(); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MAX)").unwrap(); + assert_eq!(val, "4503599627370495"); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MIN)").unwrap(); + assert_eq!(val, "-4503599627370496"); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT53_MAX)").unwrap(); + assert_eq!(val, "9007199254740991"); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MIN)").unwrap(); + assert_eq!(val, "-9223372036854775808"); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MAX)").unwrap(); + assert_eq!(val, "9223372036854775807"); + let val = vm.run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT64_MAX)").unwrap(); + assert_eq!(val, "18446744073709551615"); +} From a6e3a1b4732d2361f3d077e0e7c3f449b829f37a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 28 Aug 2025 22:07:43 +0200 Subject: [PATCH 467/527] Added type definitions for new bp3d.util.num base library --- definitions/bp3d.util.lua | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/definitions/bp3d.util.lua b/definitions/bp3d.util.lua index 4d31653..67e0d0c 100644 --- a/definitions/bp3d.util.lua +++ b/definitions/bp3d.util.lua @@ -33,6 +33,7 @@ bp3d.util = {} bp3d.util.string = {} bp3d.util.table = {} bp3d.util.utf8 = {} +bp3d.util.num = {} --- Checks if the given sub string `needle` can be found in `src`. --- @@ -211,3 +212,25 @@ bp3d.util.utf8.lower = function(src) end --- @param end1 integer end position in the input string. --- @return string bp3d.util.utf8.sub = function(src, start, end1) end + +--- @class bp3d.util.num +--- @field INT53_MIN number -2^52 +--- @field INT53_MAX number 2^52-1 +--- @field UINT53_MIN number 0 +--- @field UINT53_MAX number 2^53-1 +--- @field INT64_MIN number -2^63; this is actually not a number but a special LuaJIT cdata type +--- @field INT64_MAX number 2^63-1; this is actually not a number but a special LuaJIT cdata type +--- @field UINT64_MIN number 0; this is actually not a number but a special LuaJIT cdata type +--- @field UINT64_MAX number 2^64-1; this is actually not a number but a special LuaJIT cdata type + +--- Converts the input value to an integer string. +--- +--- @param value any +--- @return string +bp3d.util.num.toistring = function(value) end + +--- Converts the input value to an unsigned integer string. +--- +--- @param value any +--- @return string +bp3d.util.num.toustring = function(value) end From 4370608e8b2271cc50ff10c63c59f29cec4acc45 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 28 Aug 2025 22:08:02 +0200 Subject: [PATCH 468/527] Added new tests for integer53 --- core/tests/test_vm_integer53.rs | 59 +++++++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 core/tests/test_vm_integer53.rs diff --git a/core/tests/test_vm_integer53.rs b/core/tests/test_vm_integer53.rs new file mode 100644 index 0000000..e6534f8 --- /dev/null +++ b/core/tests/test_vm_integer53.rs @@ -0,0 +1,59 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_lua::vm::RootVm; +use bp3d_lua::vm::value::types::{Int53, UInt53}; + +#[test] +fn test_vm_u53() { + let vm = RootVm::new(); + let top = vm.top(); + let val: UInt53 = vm.run_code(c"return 2^53-1").unwrap(); + assert_eq!(val, UInt53::MAX); + vm.set_global(c"UINT53_MAX", UInt53::MAX).unwrap(); + assert_eq!(vm.run_code::<&str>(c"return tostring(UINT53_MAX)").unwrap(), "9.007199254741e+15"); + vm.run_code::<()>(c"assert(UINT53_MAX == 2^53-1)").unwrap(); + assert_eq!(top + 2, vm.top()); +} + +#[test] +fn test_vm_i53() { + let vm = RootVm::new(); + let top = vm.top(); + let val: Int53 = vm.run_code(c"return 2^52-1").unwrap(); + assert_eq!(val, Int53::MAX); + let val: Int53 = vm.run_code(c"return -2^52").unwrap(); + assert_eq!(val, Int53::MIN); + vm.set_global(c"INT53_MAX", Int53::MAX).unwrap(); + vm.set_global(c"INT53_MIN", Int53::MIN).unwrap(); + assert_eq!(vm.run_code::<&str>(c"return tostring(INT53_MAX)").unwrap(), "4.5035996273705e+15"); + assert_eq!(vm.run_code::<&str>(c"return tostring(INT53_MIN)").unwrap(), "-4.5035996273705e+15"); + vm.run_code::<()>(c"assert(INT53_MAX == 2^52-1)").unwrap(); + vm.run_code::<()>(c"assert(INT53_MIN == -2^52)").unwrap(); + assert_eq!(top + 4, vm.top()); +} From 968cca4e956598a15976dc768ab1d04303d3c42a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 29 Aug 2025 22:31:06 +0200 Subject: [PATCH 469/527] Added from_i64_lossy and from_u64_lossy --- core/src/vm/value/integer53.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/core/src/vm/value/integer53.rs b/core/src/vm/value/integer53.rs index e63a646..b0d5b08 100644 --- a/core/src/vm/value/integer53.rs +++ b/core/src/vm/value/integer53.rs @@ -45,6 +45,16 @@ impl Int53 { pub const MIN: Int53 = Int53(-(2 << 51)); pub const MAX: Int53 = Int53((2 << 51) - 1); + pub const fn from_i64_lossy(value: i64) -> Int53 { + if value < Self::MIN.0 { + Self::MIN + } else if value > Self::MAX.0 { + Self::MAX + } else { + Int53(value) + } + } + #[inline(always)] pub const fn to_i64(self) -> i64 { self.0 @@ -55,6 +65,11 @@ impl UInt53 { pub const MIN: UInt53 = UInt53(0); pub const MAX: UInt53 = UInt53((2 << 52) - 1); + #[inline(always)] + pub const fn from_u64_lossy(value: u64) -> UInt53 { + UInt53(value & Self::MAX.0) + } + #[inline(always)] pub const fn to_u64(self) -> u64 { self.0 From 0a2684731198808e7465da80da76abae4f07b3b2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 30 Aug 2025 10:24:45 +0200 Subject: [PATCH 470/527] Added full util library --- shell/core/src/lua.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index af21cb1..7c2703a 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -124,7 +124,7 @@ impl Lua { if let Err(e) = (libs::os::Compat, libs::os::Instant, libs::os::Time).register(vm) { error!("Failed to load OS library: {}", e); } - if let Err(e) = (libs::util::String, libs::util::Table, libs::util::Utf8).register(vm) { + if let Err(e) = libs::util::Util.register(vm) { error!("Failed to load util library: {}", e); } if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { From 03b9be5e18c9d604fee667c134bdb15657641f03 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 31 Aug 2025 22:06:02 +0200 Subject: [PATCH 471/527] Updated bp3d-os for debug support and added more security tests to userdata --- core/Cargo.toml | 2 +- core/tests/test_vm_userdata.rs | 26 ++++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index b32e1e0..da25b20 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,7 +16,7 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-os = { version = "1.0.0-rc.4.5.2", features = [], optional = true } +bp3d-os = { version = "1.0.0-rc.4.6.0", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index 73d2d32..feaf68f 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -505,3 +505,29 @@ fn test_vm_userdata_security8() { assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); } + +#[test] +fn test_vm_userdata_security9() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"a:__gc()") + .unwrap(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} + +#[test] +fn test_vm_userdata_security10() { + let _guard = MUTEX.lock(); + { + let vm = RootVm::new(); + test_vm_userdata_base(&vm); + vm.run_code::<()>(c"a.__gc(a)") + .unwrap(); + } + assert_eq!(unsafe { DROP_COUNTER }, 6); + assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); +} From aef31e8d544dfab2177516bd500653d640f65532 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 13 Sep 2025 13:54:59 +0200 Subject: [PATCH 472/527] Use new versions of bp3d-debug and bp3d-os --- core/Cargo.toml | 4 ++-- core/src/util/module.rs | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index da25b20..08ce738 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,8 +15,8 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } -bp3d-debug = "1.0.0-rc.6.2.0" -bp3d-os = { version = "1.0.0-rc.4.6.0", features = [], optional = true } +bp3d-debug = "1.0.0" +bp3d-os = { version = "1.0.1", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 6e6d91b..e299f53 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -206,8 +206,11 @@ impl ModuleManager { pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { let mut loader = ModuleLoader::new(builtins); - loader.add_public_dependency("bp3d-lua", VERSION); - loader.add_public_dependency("time", TIME_VERSION); + #[cfg(feature = "send")] + loader.add_public_dependency("bp3d-lua", VERSION, ["send"]); + #[cfg(not(feature = "send"))] + loader.add_public_dependency("bp3d-lua", VERSION, ["-send"]); + loader.add_public_dependency("time", TIME_VERSION, ["*"]); Self { set: Default::default(), loader, From 4083780b34eae0d868e8407bc1b1161a45349379 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 13 Sep 2025 14:06:38 +0200 Subject: [PATCH 473/527] Attempt at fixing build error in lua_ext patch --- patch/lua_ext.patch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index b90be66..dd04e42 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -192,7 +192,7 @@ index e9fc25b4..68996ee0 100644 +LUALIB_API void* lua_ext_keyreg_ref(void* ptr) { + int flag = 0; + if (BP3D_LUA_EXT_KEYREG_REFS == 0) { -+ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)ptr; ++ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)(uintptr_t)ptr; + flag = 1; + } + ++BP3D_LUA_EXT_KEYREG_REFS; From efdeebcfd75d66ef0d9db626d3caa22de8bfe05e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 13 Sep 2025 14:18:32 +0200 Subject: [PATCH 474/527] Attempt at fixing build error in lua_ext patch v2 --- patch/lua_ext.patch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patch/lua_ext.patch b/patch/lua_ext.patch index dd04e42..8fa4016 100644 --- a/patch/lua_ext.patch +++ b/patch/lua_ext.patch @@ -182,7 +182,7 @@ index e9fc25b4..68996ee0 100644 +} +#endif + -+static atomic_intptr_t BP3D_LUA_EXT_KEYREG = 0; ++static void* _Atomic BP3D_LUA_EXT_KEYREG = NULL; +static atomic_uint_fast32_t BP3D_LUA_EXT_KEYREG_REFS = 0; + +LUALIB_API void* lua_ext_keyreg_get() { @@ -192,7 +192,7 @@ index e9fc25b4..68996ee0 100644 +LUALIB_API void* lua_ext_keyreg_ref(void* ptr) { + int flag = 0; + if (BP3D_LUA_EXT_KEYREG_REFS == 0) { -+ BP3D_LUA_EXT_KEYREG = (atomic_intptr_t)(uintptr_t)ptr; ++ BP3D_LUA_EXT_KEYREG = ptr; + flag = 1; + } + ++BP3D_LUA_EXT_KEYREG_REFS; From 6340f5aef74a1b8008df43bdb1e31bfd06144241 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 13 Sep 2025 14:38:28 +0200 Subject: [PATCH 475/527] Attempt at fixing build error in lua_ext patch v3 --- patch/windows_set_lib_names.patch | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/patch/windows_set_lib_names.patch b/patch/windows_set_lib_names.patch index 38b48df..03df124 100644 --- a/patch/windows_set_lib_names.patch +++ b/patch/windows_set_lib_names.patch @@ -1,7 +1,16 @@ diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat -index 69c0c61a..0665eaa3 100644 +index 69c0c61a..f06df5e9 100644 --- a/src/msvcbuild.bat +++ b/src/msvcbuild.bat +@@ -16,7 +16,7 @@ + @setlocal + @rem Add more debug flags here, e.g. DEBUGCFLAGS=/DLUA_USE_ASSERT + @set DEBUGCFLAGS= +-@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline ++@set LJCOMPILE=cl /nologo /c /std:c11 /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline + @set LJDYNBUILD=/DLUA_BUILD_AS_DLL /MD + @set LJDYNBUILD_DEBUG=/DLUA_BUILD_AS_DLL /MDd + @set LJCOMPILETARGET=/Zi @@ -29,8 +29,6 @@ @set DASMDIR=..\dynasm @set DASM=%DASMDIR%\dynasm.lua From fe3914a124a721fa917e9e4ee621bd30e2259314 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 14 Sep 2025 16:51:28 +0200 Subject: [PATCH 476/527] Attempt at fixing test error for vm_test_safety --- .github/workflows/development.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/development.yml b/.github/workflows/development.yml index a5ed8d4..7eea48f 100644 --- a/.github/workflows/development.yml +++ b/.github/workflows/development.yml @@ -7,6 +7,8 @@ on: jobs: Test: uses: BlockProject3D/workflows/.github/workflows/Build_Test.yml@main + with: + rust_components: rust-src Analyze: uses: BlockProject3D/workflows/.github/workflows/Analyze.yml@main @@ -14,6 +16,8 @@ jobs: Coverage: uses: BlockProject3D/workflows/.github/workflows/Coverage.yml@main + with: + rust_components: rust-src Release: uses: BlockProject3D/workflows/.github/workflows/Release.yml@main From c2b820c6e28b441e82c7620ba910e416544a2ccb Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 14 Sep 2025 17:13:05 +0200 Subject: [PATCH 477/527] Attempt at fixing build error in lua_ext patch v4 --- patch/windows_set_lib_names.patch | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/patch/windows_set_lib_names.patch b/patch/windows_set_lib_names.patch index 03df124..c2a56b7 100644 --- a/patch/windows_set_lib_names.patch +++ b/patch/windows_set_lib_names.patch @@ -1,17 +1,17 @@ -diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat -index 69c0c61a..f06df5e9 100644 ---- a/src/msvcbuild.bat -+++ b/src/msvcbuild.bat -@@ -16,7 +16,7 @@ +diff --git a/src/msvcbuild.bat b/src/msvcbuild.bat +index 69c0c61a..f06df5e9 100644 +--- a/src/msvcbuild.bat ++++ b/src/msvcbuild.bat +@@ -16,7 +16,7 @@ @setlocal @rem Add more debug flags here, e.g. DEBUGCFLAGS=/DLUA_USE_ASSERT @set DEBUGCFLAGS= -@set LJCOMPILE=cl /nologo /c /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline -+@set LJCOMPILE=cl /nologo /c /std:c11 /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline ++@set LJCOMPILE=cl /nologo /c /std:c11 /experimental:c11atomics /O2 /W3 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_STDIO_INLINE=__declspec(dllexport)__inline @set LJDYNBUILD=/DLUA_BUILD_AS_DLL /MD @set LJDYNBUILD_DEBUG=/DLUA_BUILD_AS_DLL /MDd @set LJCOMPILETARGET=/Zi -@@ -29,8 +29,6 @@ +@@ -29,8 +29,6 @@ @set DASMDIR=..\dynasm @set DASM=%DASMDIR%\dynasm.lua @set DASC=vm_x64.dasc From 78e67d1b60381a767d985da7a2dbca6b05d4be9e Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 15 Sep 2025 21:38:42 +0200 Subject: [PATCH 478/527] Added provenance to lua_State which should mitigate test failures under linux and windows --- core/build.rs | 1 + core/src/ffi/ext.rs | 2 ++ core/src/vm/registry/send_key.rs | 12 ++++++------ core/tests/test_multi_root_vms.rs | 15 +++++++++++++++ core/tests/test_vm_libs.rs | 2 +- patch/lua_ext_provenance.patch | 31 +++++++++++++++++++++++++++++++ 6 files changed, 56 insertions(+), 7 deletions(-) create mode 100644 patch/lua_ext_provenance.patch diff --git a/core/build.rs b/core/build.rs index a1ad95d..d0da7e8 100644 --- a/core/build.rs +++ b/core/build.rs @@ -44,6 +44,7 @@ const PATCH_LIST: &[&str] = &[ "windows_set_lib_names", // Allow setting LJLIBNAME and LJDLLNAME from environment variables. "lua_ext_ccatch_error", // Throw lua errors which cannot be catched from lua standard // pcall/xpcall but only from lua_pcall C API. + "lua_ext_provenance", // lua_ext_getprovenance for registry key safety. ]; fn apply_patches(luajit_build_path: &Path, _summary_path: &Path) -> std::io::Result<()> { diff --git a/core/src/ffi/ext.rs b/core/src/ffi/ext.rs index ec65a35..c159845 100644 --- a/core/src/ffi/ext.rs +++ b/core/src/ffi/ext.rs @@ -60,6 +60,8 @@ extern "C" { extern "C" { pub fn lua_ext_tab_len(l: State, idx: c_int, outsize: *mut MSize) -> c_int; pub fn lua_ext_ccatch_error(l: State) -> u32; + + pub fn lua_ext_getprovenance(l: State) -> u64; } //----- diff --git a/core/src/vm/registry/send_key.rs b/core/src/vm/registry/send_key.rs index fead15b..a5c184f 100644 --- a/core/src/vm/registry/send_key.rs +++ b/core/src/vm/registry/send_key.rs @@ -26,24 +26,24 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::State; +use crate::ffi::ext::lua_ext_getprovenance; use crate::vm::registry::core::RawKey; use crate::vm::registry::{FromIndex, Set}; use crate::vm::Vm; pub struct VmCheckedRawKey { raw: RawKey, - vm: State + provenance: u64, } impl VmCheckedRawKey { pub fn push(&self, vm: &Vm) { - assert!(vm.as_ptr() == self.vm); + assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); unsafe { self.raw.push(vm) } } pub fn delete(self, vm: &Vm) { - assert!(vm.as_ptr() == self.vm); + assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); unsafe { self.raw.delete(vm) } } @@ -57,7 +57,7 @@ impl FromIndex for VmCheckedRawKey { unsafe fn from_index(vm: &Vm, index: i32) -> Self { let raw = RawKey::from_index(vm, index); Self { - vm: vm.as_ptr(), + provenance: unsafe { lua_ext_getprovenance(vm.as_ptr()) }, raw } } @@ -65,7 +65,7 @@ impl FromIndex for VmCheckedRawKey { impl Set for VmCheckedRawKey { unsafe fn set(&self, vm: &Vm, index: i32) { - assert!(vm.as_ptr() == self.vm); + assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); self.raw.set(vm, index); } } diff --git a/core/tests/test_multi_root_vms.rs b/core/tests/test_multi_root_vms.rs index bcaec03..9df1786 100644 --- a/core/tests/test_multi_root_vms.rs +++ b/core/tests/test_multi_root_vms.rs @@ -128,6 +128,21 @@ fn test_multi_root_vms_panic_4() { key.delete(&root2); } +#[test] +#[should_panic] +fn test_multi_root_vms_panic_5() { + let key = { + let root1 = RootVm::new(); + root1.run_code::<()>(c"function Test() end").unwrap(); + let glb: Function = root1.get_global("Test").unwrap(); + Key::::new(glb) + }; + { + let root2 = RootVm::new(); + key.delete(&root2); + } +} + #[test] #[should_panic] fn test_multi_root_vms_not_send_destructor() { diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 4fef503..7cf0c1e 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -45,7 +45,7 @@ fn test_vm_lib_lua() { c" assert(bp3d.lua.name == 'bp3d-lua') assert(bp3d.lua.version == BP3D_LUA_CRATE_VERSION) - assert(#bp3d.lua.patches == 7) + assert(#bp3d.lua.patches == 8) local func = bp3d.lua.loadString('return 1 + 1') assert(func) assert(func() == 2) diff --git a/patch/lua_ext_provenance.patch b/patch/lua_ext_provenance.patch new file mode 100644 index 0000000..ef86497 --- /dev/null +++ b/patch/lua_ext_provenance.patch @@ -0,0 +1,31 @@ +diff --git a/src/lj_obj.h b/src/lj_obj.h +index 855727bf..351cbcb7 100644 +--- a/src/lj_obj.h ++++ b/src/lj_obj.h +@@ -700,6 +700,7 @@ struct lua_State { + GCRef env; /* Thread environment (table of globals). */ + void *cframe; /* End of C stack frame chain. */ + MSize stacksize; /* True stack size (incl. LJ_STACK_EXTRA). */ ++ uint64_t provenance; + }; + + #define G(L) (mref(L->glref, global_State)) +diff --git a/src/lj_state.c b/src/lj_state.c +index d8fc545a..99fdb804 100644 +--- a/src/lj_state.c ++++ b/src/lj_state.c +@@ -264,6 +264,7 @@ LUA_API lua_State *lua_newstate(lua_Alloc allocf, void *allocd) + memset(GG, 0, sizeof(GG_State)); + L = &GG->L; + g = &GG->g; ++ L->provenance = prng.u[0]; + L->gct = ~LJ_TTHREAD; + L->marked = LJ_GC_WHITE0 | LJ_GC_FIXED | LJ_GC_SFIXED; /* Prevent free. */ + L->dummy_ffid = FF_C; +@@ -380,3 +381,6 @@ void LJ_FASTCALL lj_state_free(global_State *g, lua_State *L) + lj_mem_freet(g, L); + } + ++LUA_API uint64_t lua_ext_getprovenance(lua_State *L) { ++ return L->provenance; ++} From c7929c264f78acfc858a802a3a1e5b3758f342a5 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Mon, 15 Sep 2025 19:45:14 +0000 Subject: [PATCH 479/527] Format Rust code using rustfmt --- build/src/patch.rs | 8 +- codegen/src/gen/into_param.rs | 2 +- codegen/src/gen/mod.rs | 4 +- codegen/src/lib.rs | 2 +- core/build.rs | 12 ++- core/src/ffi/lua.rs | 2 +- core/src/libs/interface.rs | 2 +- core/src/libs/lua/debug.rs | 6 +- core/src/libs/lua/mod.rs | 4 +- core/src/libs/lua/module.rs | 4 +- core/src/libs/lua/options.rs | 2 +- core/src/libs/lua/require.rs | 2 +- core/src/libs/os/time.rs | 9 +- core/src/libs/util/mod.rs | 4 +- core/src/libs/util/num.rs | 22 +---- core/src/macro/userdata.rs | 5 +- core/src/module/mod.rs | 4 +- core/src/util/core.rs | 2 +- core/src/util/module.rs | 2 +- core/src/util/namespace.rs | 22 +++-- core/src/util/thread.rs | 7 +- core/src/vm/closure/arc.rs | 2 +- core/src/vm/closure/context.rs | 2 +- core/src/vm/closure/core.rs | 2 +- core/src/vm/closure/mod.rs | 2 +- core/src/vm/closure/rc.rs | 2 +- core/src/vm/closure/types.rs | 14 +-- core/src/vm/core/debug.rs | 20 ++-- core/src/vm/core/destructor.rs | 10 +- core/src/vm/core/interrupt/unix.rs | 13 ++- core/src/vm/core/interrupt/vm.rs | 4 +- core/src/vm/core/interrupt/windows.rs | 6 +- core/src/vm/core/mod.rs | 2 +- core/src/vm/core/root_vm/common.rs | 2 +- core/src/vm/core/root_vm/send.rs | 8 +- core/src/vm/core/root_vm/unsend.rs | 6 +- core/src/vm/core/vm.rs | 15 ++- core/src/vm/error.rs | 2 +- core/src/vm/function/core.rs | 21 +++-- core/src/vm/registry/core.rs | 8 +- core/src/vm/registry/lua_ref.rs | 13 ++- core/src/vm/registry/mod.rs | 2 +- core/src/vm/registry/named.rs | 36 ++++---- core/src/vm/registry/send_key.rs | 17 +++- core/src/vm/table/core.rs | 11 +-- core/src/vm/table/immutable.rs | 14 ++- core/src/vm/table/interface.rs | 26 ++++-- core/src/vm/table/mod.rs | 2 +- core/src/vm/thread/core.rs | 33 ++++--- core/src/vm/thread/interface.rs | 2 +- core/src/vm/thread/mod.rs | 2 +- core/src/vm/thread/value.rs | 18 ++-- core/src/vm/userdata/any.rs | 35 ++++--- core/src/vm/userdata/core.rs | 12 ++- core/src/vm/userdata/util.rs | 2 +- core/src/vm/value/any.rs | 36 ++++---- core/src/vm/value/core.rs | 47 ++++++++-- core/src/vm/value/function.rs | 2 +- core/src/vm/value/integer53.rs | 2 +- core/src/vm/value/mod.rs | 6 +- core/src/vm/value/raw_ptr.rs | 4 +- core/src/vm/value/unknown.rs | 14 ++- core/src/vm/value/util.rs | 5 +- core/tests/test_multi_root_vms.rs | 4 +- core/tests/test_vm_custom_structs.rs | 2 +- core/tests/test_vm_integer53.rs | 17 +++- core/tests/test_vm_integer64.rs | 26 ++++-- core/tests/test_vm_libs.rs | 59 ++++++++---- core/tests/test_vm_registry.rs | 12 ++- core/tests/test_vm_threads.rs | 117 +++++++++++++++++------- core/tests/test_vm_userdata.rs | 41 ++++++--- shell/core/src/autocomplete.rs | 18 ++-- shell/core/src/core.rs | 10 +- shell/core/src/data.rs | 2 +- shell/core/src/data_in.rs | 8 +- shell/core/src/data_out.rs | 57 +++++++----- shell/core/src/lib1/autocomplete_api.rs | 35 +++++-- shell/core/src/lib1/mod.rs | 24 +++-- shell/core/src/lib1/scheduler_api.rs | 2 +- shell/core/src/lua.rs | 62 ++++++++----- shell/core/src/main.rs | 20 ++-- shell/core/src/scheduler.rs | 43 ++++++--- shell/proto/build.rs | 2 +- 83 files changed, 743 insertions(+), 427 deletions(-) diff --git a/build/src/patch.rs b/build/src/patch.rs index 6a76f2e..98bf3c9 100644 --- a/build/src/patch.rs +++ b/build/src/patch.rs @@ -30,12 +30,12 @@ use crate::util::CommandRunner; use bp3d_os::fs::CopyOptions; use std::ffi::OsStr; use std::fs::File; +use std::io::Write; use std::path::{Path, PathBuf}; use std::process::Command; -use std::io::Write; pub struct Summary { - patches: Vec + patches: Vec, } impl Summary { @@ -104,7 +104,9 @@ impl Patch { CopyOptions::new().exclude(OsStr::new(".git")), )?; } - Ok(Summary { patches: self.get_patch_list().map(String::from).collect() }) + Ok(Summary { + patches: self.get_patch_list().map(String::from).collect(), + }) } } diff --git a/codegen/src/gen/into_param.rs b/codegen/src/gen/into_param.rs index 70321d3..f7507a3 100644 --- a/codegen/src/gen/into_param.rs +++ b/codegen/src/gen/into_param.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::gen::IntoLua; use crate::parser::enums::EnumVariant; use crate::parser::structs::StructField; use crate::parser::Parser; use proc_macro2::{Ident, TokenStream}; use quote::quote; use syn::Generics; -use crate::gen::IntoLua; pub struct IntoParam(IntoLua); diff --git a/codegen/src/gen/mod.rs b/codegen/src/gen/mod.rs index 008e8d0..9f18191 100644 --- a/codegen/src/gen/mod.rs +++ b/codegen/src/gen/mod.rs @@ -29,11 +29,11 @@ //TODO: FromLua generator mod from_param; +mod into_lua; mod into_param; mod lua_type; -mod into_lua; pub use from_param::FromParam; -pub use into_param::IntoParam; pub use into_lua::IntoLua; +pub use into_param::IntoParam; pub use lua_type::LuaType; diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index be72853..8221c41 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -29,7 +29,7 @@ mod gen; mod parser; -use crate::gen::{FromParam, IntoParam, IntoLua, LuaType}; +use crate::gen::{FromParam, IntoLua, IntoParam, LuaType}; use crate::parser::Parser; use proc_macro::TokenStream; use proc_macro2::Ident; diff --git a/core/build.rs b/core/build.rs index d0da7e8..e32df9d 100644 --- a/core/build.rs +++ b/core/build.rs @@ -43,8 +43,8 @@ const PATCH_LIST: &[&str] = &[ "lua_load_no_bc", // Treat all inputs as strings (no bytecode allowed). "windows_set_lib_names", // Allow setting LJLIBNAME and LJDLLNAME from environment variables. "lua_ext_ccatch_error", // Throw lua errors which cannot be catched from lua standard - // pcall/xpcall but only from lua_pcall C API. - "lua_ext_provenance", // lua_ext_getprovenance for registry key safety. + // pcall/xpcall but only from lua_pcall C API. + "lua_ext_provenance", // lua_ext_getprovenance for registry key safety. ]; fn apply_patches(luajit_build_path: &Path, _summary_path: &Path) -> std::io::Result<()> { @@ -56,7 +56,10 @@ fn apply_patches(luajit_build_path: &Path, _summary_path: &Path) -> std::io::Res #[cfg(feature = "libs")] { _summary.write(_summary_path)?; - println!("cargo:rustc-env=BP3D_LUA_PATCH_SUMMARY_FILE={}", _summary_path.display()); + println!( + "cargo:rustc-env=BP3D_LUA_PATCH_SUMMARY_FILE={}", + _summary_path.display() + ); } Ok(()) } @@ -85,7 +88,8 @@ fn main() { let out_path = Path::new(&out).join("luajit-build"); // Apply patches to LuaJIT source code. - apply_patches(&out_path, &Path::new(&out).join("patch_summary.rs")).expect("Failed to patch LuaJIT"); + apply_patches(&out_path, &Path::new(&out).join("patch_summary.rs")) + .expect("Failed to patch LuaJIT"); // Copy the source directory to the build directory. println!("Internal LuaJIT build directory: {}", out_path.display()); diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 04abe6e..0613b8b 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -74,7 +74,7 @@ pub enum Type { Function = 6, Userdata = 7, Thread = 8, - Cdata = 10 + Cdata = 10, } pub type RawNumber = c_double; diff --git a/core/src/libs/interface.rs b/core/src/libs/interface.rs index 9789184..49078ee 100644 --- a/core/src/libs/interface.rs +++ b/core/src/libs/interface.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_debug::info; use crate::util::Namespace; use crate::vm::core::debug::DebugRegistry; use crate::vm::Vm; +use bp3d_debug::info; pub trait Lib { const NAMESPACE: &'static str; diff --git a/core/src/libs/lua/debug.rs b/core/src/libs/lua/debug.rs index 1a0c22a..79d495c 100644 --- a/core/src/libs/lua/debug.rs +++ b/core/src/libs/lua/debug.rs @@ -26,8 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::CString; -use std::str::FromStr; use crate::decl_lib_func; use crate::libs::Lib; use crate::util::Namespace; @@ -38,6 +36,8 @@ use crate::vm::function::types::RFunction; use crate::vm::table::Table; use crate::vm::userdata::util::{get_metatable_by_name, get_static_table_by_name}; use crate::vm::value::any::Any; +use std::ffi::CString; +use std::str::FromStr; decl_lib_func! { fn dump_stack(vm: &Vm, start_index: i32) -> crate::vm::Result> { @@ -127,7 +127,7 @@ impl Lib for Debug { ("dumpClasses", RFunction::wrap(dump_classes)), ("dumpStaticTable", RFunction::wrap(dump_static_table)), ("dumpMetaTable", RFunction::wrap(dump_meta_table)), - ("dumpClassName", RFunction::wrap(dump_class_name)) + ("dumpClassName", RFunction::wrap(dump_class_name)), ]) } } diff --git a/core/src/libs/lua/mod.rs b/core/src/libs/lua/mod.rs index 04dd6c8..1a4569d 100644 --- a/core/src/libs/lua/mod.rs +++ b/core/src/libs/lua/mod.rs @@ -32,15 +32,15 @@ mod load; mod options; pub mod require; +mod debug; #[cfg(feature = "util-module")] mod module; -mod debug; pub use base::Base; pub use call::Call; +pub use debug::Debug; pub use load::Load; pub use require::Require; -pub use debug::Debug; pub use options::Lua; diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index 7707819..e6cce45 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::{decl_userdata, impl_userdata_mut}; use crate::libs::Lib; use crate::util::module::ModuleManager; use crate::util::module::Result; use crate::util::Namespace; +use crate::vm::core::debug::DebugRegistry; use crate::vm::Vm; +use crate::{decl_userdata, impl_userdata_mut}; use bp3d_os::module::library::types::VirtualLibrary; use std::path::PathBuf; -use crate::vm::core::debug::DebugRegistry; pub struct Module { builtins: &'static [&'static VirtualLibrary], diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index 2e3cd5e..4edad58 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -31,8 +31,8 @@ use crate::libs::lua::call::Call; use crate::libs::lua::load::Load; use crate::libs::lua::require::{Provider, Require}; use crate::libs::Lib; -use std::path::Path; use crate::vm::closure::arc::Shared; +use std::path::Path; #[derive(Default)] pub struct Lua<'a> { diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index ede0790..1a93eb6 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -30,12 +30,12 @@ use crate::decl_closure; use crate::libs::interface::Lib; use crate::util::Namespace; use crate::vm::closure::arc::{Arc, Shared}; +use crate::vm::core::debug::DebugRegistry; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; use bp3d_util::simple_error; use std::collections::HashMap; use std::sync::Mutex; -use crate::vm::core::debug::DebugRegistry; simple_error! { pub Error { diff --git a/core/src/libs/os/time.rs b/core/src/libs/os/time.rs index c8457b0..35bac99 100644 --- a/core/src/libs/os/time.rs +++ b/core/src/libs/os/time.rs @@ -31,6 +31,7 @@ use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::vm::function::IntoParam; use crate::vm::table::Table; +use crate::vm::value::IntoLua; use crate::vm::Vm; use crate::{decl_lib_func, decl_userdata, impl_userdata}; use bp3d_os::time::{LocalOffsetDateTime, MonthExt}; @@ -38,7 +39,6 @@ use bp3d_util::simple_error; use time::error::{ComponentRange, Format, InvalidFormatDescription}; use time::format_description::parse; use time::{Date, Duration, Month, OffsetDateTime, PrimitiveDateTime, UtcOffset}; -use crate::vm::value::IntoLua; simple_error! { FormatError { @@ -182,10 +182,13 @@ impl Lib for Time { const NAMESPACE: &'static str = "bp3d.os.time"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.add_userdata::(c"OffsetDateTime", crate::vm::userdata::case::Camel)?; + namespace.add_userdata::( + c"OffsetDateTime", + crate::vm::userdata::case::Camel, + )?; namespace.add([ ("nowUtc", RFunction::wrap(now_utc)), - ("nowLocal", RFunction::wrap(now_local)) + ("nowLocal", RFunction::wrap(now_local)), ]) } } diff --git a/core/src/libs/util/mod.rs b/core/src/libs/util/mod.rs index c8cc8a5..85b4870 100644 --- a/core/src/libs/util/mod.rs +++ b/core/src/libs/util/mod.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +mod num; mod string; mod table; mod utf8; -mod num; +pub use num::Num; pub use string::String; pub use table::Table; pub use utf8::Utf8; -pub use num::Num; // Workaround for language defect #22259. #[allow(non_upper_case_globals)] diff --git a/core/src/libs/util/num.rs b/core/src/libs/util/num.rs index efc2ab1..8298d89 100644 --- a/core/src/libs/util/num.rs +++ b/core/src/libs/util/num.rs @@ -53,25 +53,13 @@ impl Lib for Num { const NAMESPACE: &'static str = "bp3d.util.num"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { - namespace.add([ - ("UINT53_MAX", UInt53::MAX), - ("UINT53_MIN", UInt53::MIN) - ])?; - namespace.add([ - ("INT53_MAX", Int53::MAX), - ("INT53_MIN", Int53::MIN) - ])?; - namespace.add([ - ("UINT64_MAX", u64::MAX), - ("UINT64_MIN", u64::MIN) - ])?; - namespace.add([ - ("INT64_MAX", i64::MAX), - ("INT64_MIN", i64::MIN) - ])?; + namespace.add([("UINT53_MAX", UInt53::MAX), ("UINT53_MIN", UInt53::MIN)])?; + namespace.add([("INT53_MAX", Int53::MAX), ("INT53_MIN", Int53::MIN)])?; + namespace.add([("UINT64_MAX", u64::MAX), ("UINT64_MIN", u64::MIN)])?; + namespace.add([("INT64_MAX", i64::MAX), ("INT64_MIN", i64::MIN)])?; namespace.add([ ("toistring", RFunction::wrap(toistring)), - ("toustring", RFunction::wrap(toustring)) + ("toustring", RFunction::wrap(toustring)), ]) } } diff --git a/core/src/macro/userdata.rs b/core/src/macro/userdata.rs index c757d5f..c926c87 100644 --- a/core/src/macro/userdata.rs +++ b/core/src/macro/userdata.rs @@ -32,7 +32,10 @@ macro_rules! _impl_userdata_static { $registry.add_field($crate::c_stringify!($field_name), $field_value)?; }; ($registry: ident fn $function_name: ident) => { - $registry.add_static_field($crate::c_stringify!($function_name), $crate::vm::function::types::RFunction::wrap($function_name))?; + $registry.add_static_field( + $crate::c_stringify!($function_name), + $crate::vm::function::types::RFunction::wrap($function_name), + )?; }; } diff --git a/core/src/module/mod.rs b/core/src/module/mod.rs index b799fa5..dda8fc0 100644 --- a/core/src/module/mod.rs +++ b/core/src/module/mod.rs @@ -123,8 +123,8 @@ pub fn run_lua_register( let mut msg = unsafe { MemBufStr::wrap(&mut error.string.len, &mut error.string.data) }; let _ = write!(msg, "{}", e); - }, - Error::BadThreadState => error.ty = error::ErrorType::BadThreadState + } + Error::BadThreadState => error.ty = error::ErrorType::BadThreadState, } false } diff --git a/core/src/util/core.rs b/core/src/util/core.rs index 0d90764..4a7ba0a 100644 --- a/core/src/util/core.rs +++ b/core/src/util/core.rs @@ -81,4 +81,4 @@ impl Display for TryFromIntError { } } -impl Error for TryFromIntError { } +impl Error for TryFromIntError {} diff --git a/core/src/util/module.rs b/core/src/util/module.rs index e299f53..bedbc37 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -118,7 +118,7 @@ unsafe fn convert_module_error_to_vm_error( crate::vm::userdata::Error::Alignment(err.alignment.alignment), ), ErrorType::None => std::hint::unreachable_unchecked(), - ErrorType::BadThreadState => crate::vm::error::Error::BadThreadState + ErrorType::BadThreadState => crate::vm::error::Error::BadThreadState, } } diff --git a/core/src/util/namespace.rs b/core/src/util/namespace.rs index 64e0739..bbe0c60 100644 --- a/core/src/util/namespace.rs +++ b/core/src/util/namespace.rs @@ -26,15 +26,15 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_debug::info; use crate::ffi::lua::lua_settop; use crate::util::core::AnyStr; use crate::vm::registry::core::Key; use crate::vm::table::Table; -use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::userdata::util::get_static_table; +use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::IntoLua; use crate::vm::Vm; +use bp3d_debug::info; pub struct Namespace<'a> { vm: &'a Vm, @@ -92,11 +92,21 @@ impl<'a> Namespace<'a> { Ok(()) } - pub fn add_userdata(&mut self, name: impl AnyStr, case: impl NameConvert) -> crate::vm::Result<()> { - info!("Adding userdata type {:?} as {:?}", T::CLASS_NAME, name.to_str()?); + pub fn add_userdata( + &mut self, + name: impl AnyStr, + case: impl NameConvert, + ) -> crate::vm::Result<()> { + info!( + "Adding userdata type {:?} as {:?}", + T::CLASS_NAME, + name.to_str()? + ); self.vm.register_userdata::(case)?; - self.table.set(name, get_static_table::(self.vm) - .map(|v| unsafe { v.to_table() }))?; + self.table.set( + name, + get_static_table::(self.vm).map(|v| unsafe { v.to_table() }), + )?; Ok(()) } diff --git a/core/src/util/thread.rs b/core/src/util/thread.rs index df5ac1b..e7847d9 100644 --- a/core/src/util/thread.rs +++ b/core/src/util/thread.rs @@ -31,15 +31,16 @@ use crate::vm::Vm; pub struct LuaThread { key: Key, - thread: crate::vm::thread::core::Thread<'static> + thread: crate::vm::thread::core::Thread<'static>, } impl LuaThread { pub fn create(value: crate::vm::thread::value::Thread) -> Self { - let thread = unsafe { crate::vm::thread::core::Thread::from_raw(value.as_thread().as_ptr()) }; + let thread = + unsafe { crate::vm::thread::core::Thread::from_raw(value.as_thread().as_ptr()) }; Self { key: Key::new(value), - thread + thread, } } diff --git a/core/src/vm/closure/arc.rs b/core/src/vm/closure/arc.rs index 39a66a2..4061c6b 100644 --- a/core/src/vm/closure/arc.rs +++ b/core/src/vm/closure/arc.rs @@ -28,9 +28,9 @@ use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::value::types::RawPtr; use crate::vm::Vm; use std::ops::Deref; -use crate::vm::value::types::RawPtr; pub type Shared = std::sync::Arc; diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 385a0a4..d983411 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -33,10 +33,10 @@ use crate::ffi::lua::lua_newuserdata; use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::registry::core::RawKey; +use crate::vm::value::types::RawPtr; use crate::vm::Vm; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use crate::vm::value::types::RawPtr; pub struct Cell { ptr: *mut *const T, diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index 9958c4d..a067080 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -29,11 +29,11 @@ use crate::ffi::lua::GLOBALSINDEX; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; +use crate::vm::value::types::RawPtr; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::ffi::OsStr; use std::path::Path; -use crate::vm::value::types::RawPtr; macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 71ddab8..00aa348 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod arc; pub mod context; mod core; mod interface; pub mod rc; pub mod types; -pub mod arc; pub use interface::*; diff --git a/core/src/vm/closure/rc.rs b/core/src/vm/closure/rc.rs index 9693f8d..8a87d78 100644 --- a/core/src/vm/closure/rc.rs +++ b/core/src/vm/closure/rc.rs @@ -28,9 +28,9 @@ use crate::util::core::SimpleDrop; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; +use crate::vm::value::types::RawPtr; use crate::vm::Vm; use std::ops::Deref; -use crate::vm::value::types::RawPtr; pub type Shared = std::rc::Rc; diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 30d4b04..5525af2 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -29,9 +29,9 @@ use crate::ffi::lua::{lua_pushcclosure, CFunction, State}; use crate::vm::closure::{FromUpvalue, IntoUpvalue}; use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::value::types::RawPtr; use crate::vm::value::IntoLua; use crate::vm::Vm; -use crate::vm::value::types::RawPtr; pub struct RClosure { func: CFunction, @@ -66,14 +66,14 @@ impl RClosure> { where for<'a> T: FromParam<'a>, R: IntoParam, - F: Send + F: Send, { let ptr = crate::vm::core::destructor::Pool::attach_send(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where for<'a> T: FromParam<'a>, R: IntoParam, - F: Send + F: Send, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; @@ -87,14 +87,14 @@ impl RClosure> { #[cfg(not(feature = "send"))] pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self where - for<'a> T: FromParam<'a>, - R: IntoParam + for<'a> T: FromParam<'a>, + R: IntoParam, { let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); extern "C-unwind" fn _cfunc R>(l: State) -> i32 where - for<'a> T: FromParam<'a>, - R: IntoParam, + for<'a> T: FromParam<'a>, + R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; diff --git a/core/src/vm/core/debug.rs b/core/src/vm/core/debug.rs index 24ab251..16ccea2 100644 --- a/core/src/vm/core/debug.rs +++ b/core/src/vm/core/debug.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::HashMap; use crate::vm::core::destructor::Pool; +use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; use crate::vm::registry::named::Key; use crate::vm::registry::types::LuaRef; -use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; use crate::vm::userdata::UserData; use crate::vm::value::types::RawPtr; use crate::vm::Vm; +use std::collections::HashMap; pub trait DebugItemType { const NAME: &'static str; @@ -54,7 +54,7 @@ pub trait DebugItem { fn describe() -> String; } -#[cfg(feature="libs-core")] +#[cfg(feature = "libs-core")] impl DebugItem for T { fn describe() -> String { let lib_name = std::any::type_name::(); @@ -73,12 +73,15 @@ impl DebugItem for T { static DBG_REG: Key>> = Key::new("__debug_registry__"); pub struct DebugRegistry { - map: HashMap<&'static str, Vec> + map: HashMap<&'static str, Vec>, } impl DebugRegistry { fn add_internal + ?Sized, T: DebugItemType>(&mut self) { - self.map.entry(T::NAME).or_insert_with(Vec::new).push(D::describe()); + self.map + .entry(T::NAME) + .or_insert_with(Vec::new) + .push(D::describe()); } fn list_internal(&self, _: T) -> Option> { @@ -87,7 +90,12 @@ impl DebugRegistry { fn get(vm: &Vm) -> RawPtr { if let None = DBG_REG.push(vm) { - let ptr = Pool::attach_send(vm, Box::new(DebugRegistry { map: HashMap::new() })); + let ptr = Pool::attach_send( + vm, + Box::new(DebugRegistry { + map: HashMap::new(), + }), + ); DBG_REG.set(LiveLuaRef::new(vm, RawPtr::new(ptr))); } let ptr = DBG_REG.push(vm).unwrap().get(); diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 3021567..7c9dab4 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; use crate::vm::registry::named::Key; +use crate::vm::registry::types::LuaRef; +use crate::vm::value::types::RawPtr; use crate::vm::Vm; use bp3d_debug::debug; use std::sync::Arc; -use crate::vm::registry::types::LuaRef; -use crate::vm::value::types::RawPtr; -use crate::vm::registry::lua_ref::LuaRef as LiveLuaRef; /// This trait represents a value which can be attached to a [Pool](Pool). pub trait RawSend: Send { @@ -116,14 +116,14 @@ static DESTRUCTOR_POOL: Key>> = Key::new("__destructor_pool_ pub struct Pool { leaked: Vec>, - is_send: bool + is_send: bool, } impl Pool { pub fn new(is_send: bool) -> Self { Self { leaked: Vec::new(), - is_send + is_send, } } diff --git a/core/src/vm/core/interrupt/unix.rs b/core/src/vm/core/interrupt/unix.rs index 534af55..42f745d 100644 --- a/core/src/vm/core/interrupt/unix.rs +++ b/core/src/vm/core/interrupt/unix.rs @@ -31,21 +31,21 @@ use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; use crate::vm::core::interrupt::{Error, InterruptibleRootVm}; +use crate::vm::Vm; use bp3d_debug::{error, warning}; use libc::{c_int, pthread_kill, pthread_self, pthread_t, SIGUSR1}; use std::mem::MaybeUninit; use std::ops::Deref; -use std::sync::{Arc, Mutex, Once}; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex, Once}; use std::thread::ThreadId; use std::time::Duration; -use crate::vm::Vm; pub struct Signal { l: State, thread: ThreadId, th: pthread_t, - alive: Arc + alive: Arc, } struct SigState { @@ -117,7 +117,12 @@ impl Signal { let ret = unsafe { libc::sigaction(SIGUSR1, &sig as _, std::ptr::null_mut()) }; assert_eq!(ret, 0); }); - Self { l, thread, th, alive } + Self { + l, + thread, + th, + alive, + } } pub fn send(&self, duration: Duration) -> Result<(), Error> { diff --git a/core/src/vm/core/interrupt/vm.rs b/core/src/vm/core/interrupt/vm.rs index b8457fc..a5c602e 100644 --- a/core/src/vm/core/interrupt/vm.rs +++ b/core/src/vm/core/interrupt/vm.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::Vm; use std::marker::PhantomData; use std::ops::{Deref, DerefMut}; -use std::sync::Arc; use std::sync::atomic::AtomicBool; -use crate::vm::Vm; +use std::sync::Arc; pub struct InterruptibleRootVm { vm: T, diff --git a/core/src/vm/core/interrupt/windows.rs b/core/src/vm/core/interrupt/windows.rs index aa33c4b..b758f57 100644 --- a/core/src/vm/core/interrupt/windows.rs +++ b/core/src/vm/core/interrupt/windows.rs @@ -26,20 +26,20 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ops::Deref; use super::{Error, InterruptibleRootVm}; use crate::ffi::ext::lua_ext_ccatch_error; use crate::ffi::lua::{ lua_pushstring, lua_sethook, Debug, State, MASKCALL, MASKCOUNT, MASKLINE, MASKRET, }; +use crate::vm::Vm; use bp3d_debug::{error, warning}; -use std::sync::{Arc, Mutex}; +use std::ops::Deref; use std::sync::atomic::AtomicBool; +use std::sync::{Arc, Mutex}; use std::time::Duration; use windows_sys::Win32::Foundation::HANDLE; use windows_sys::Win32::System::Diagnostics::Debug::{GetThreadContext, CONTEXT}; use windows_sys::Win32::System::Threading::{GetCurrentThread, ResumeThread, SuspendThread}; -use crate::vm::Vm; static SIG_STATE: Mutex>> = Mutex::new(None); diff --git a/core/src/vm/core/mod.rs b/core/src/vm/core/mod.rs index 3f5fd6d..18f6b2f 100644 --- a/core/src/vm/core/mod.rs +++ b/core/src/vm/core/mod.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +pub mod debug; pub mod destructor; mod interface; pub mod iter; @@ -33,7 +34,6 @@ pub mod load; #[cfg(feature = "root-vm")] mod root_vm; pub mod util; -pub mod debug; mod vm; #[cfg(feature = "root-vm")] diff --git a/core/src/vm/core/root_vm/common.rs b/core/src/vm/core/root_vm/common.rs index cf12241..7cb41b7 100644 --- a/core/src/vm/core/root_vm/common.rs +++ b/core/src/vm/core/root_vm/common.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_debug::debug; use crate::ffi::laux::{luaL_newstate, luaL_openlibs}; use crate::ffi::lua::lua_close; use crate::vm::core::destructor::Pool; use crate::vm::registry::named::{handle_root_vm_init, handle_root_vm_uninit}; use crate::vm::Vm; +use bp3d_debug::debug; #[cfg(not(feature = "send"))] thread_local! { diff --git a/core/src/vm/core/root_vm/send.rs b/core/src/vm/core/root_vm/send.rs index f366d42..758deb9 100644 --- a/core/src/vm/core/root_vm/send.rs +++ b/core/src/vm/core/root_vm/send.rs @@ -26,23 +26,23 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ops::{Deref, DerefMut}; use crate::vm::core::root_vm::common::UnsafeRootVm; use crate::vm::Vm; +use std::ops::{Deref, DerefMut}; pub struct RootVm { - vm: UnsafeRootVm + vm: UnsafeRootVm, } impl RootVm { pub fn new() -> RootVm { RootVm { - vm: UnsafeRootVm::new(true) + vm: UnsafeRootVm::new(true), } } } -unsafe impl Send for RootVm { } +unsafe impl Send for RootVm {} impl Deref for RootVm { type Target = Vm; diff --git a/core/src/vm/core/root_vm/unsend.rs b/core/src/vm/core/root_vm/unsend.rs index f658432..e983bfa 100644 --- a/core/src/vm/core/root_vm/unsend.rs +++ b/core/src/vm/core/root_vm/unsend.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ops::{Deref, DerefMut}; use crate::vm::core::root_vm::common::UnsafeRootVm; use crate::vm::Vm; +use std::ops::{Deref, DerefMut}; pub struct RootVm { - vm: UnsafeRootVm + vm: UnsafeRootVm, } impl RootVm { pub fn new() -> RootVm { RootVm { - vm: UnsafeRootVm::new(false) + vm: UnsafeRootVm::new(false), } } } diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index b7161ee..1fbc768 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -26,18 +26,21 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_debug::{info, warning}; -use crate::ffi::lua::{lua_getfield, lua_gettop, lua_isyieldable, lua_pushnil, lua_remove, lua_setfield, lua_settop, State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX}; +use crate::ffi::lua::{ + lua_getfield, lua_gettop, lua_isyieldable, lua_pushnil, lua_remove, lua_setfield, lua_settop, + State, ThreadStatus, GLOBALSINDEX, REGISTRYINDEX, +}; use crate::util::core::AnyStr; +use crate::vm::core::debug::DebugRegistry; use crate::vm::core::util::{handle_syntax_error, pcall, push_error_handler}; use crate::vm::core::{Load, LoadString}; -use crate::vm::core::debug::DebugRegistry; use crate::vm::error::Error; use crate::vm::thread::core::Thread; use crate::vm::userdata::core::Registry; use crate::vm::userdata::{NameConvert, UserData}; use crate::vm::value::types::Function; use crate::vm::value::{FromLua, IntoLua}; +use bp3d_debug::{info, warning}; #[repr(transparent)] pub struct Vm { @@ -79,7 +82,11 @@ impl Vm { match res { Ok(_) => Ok(()), Err(e) => { - warning!("Failed to register userdata type {:?}: {}", T::CLASS_NAME, e); + warning!( + "Failed to register userdata type {:?}: {}", + T::CLASS_NAME, + e + ); unsafe { lua_pushnil(self.l); lua_setfield(self.l, REGISTRYINDEX, T::FULL_TYPE.as_ptr()); diff --git a/core/src/vm/error.rs b/core/src/vm/error.rs index fe342c5..688a837 100644 --- a/core/src/vm/error.rs +++ b/core/src/vm/error.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::lua::{lua_type, Type}; +use crate::vm::Vm; use bp3d_util::simple_error; use std::fmt::{Display, Formatter}; -use crate::vm::Vm; #[derive(Debug, Copy, Clone)] pub struct TypeError { diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 2495e49..26c7ceb 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -26,19 +26,28 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::ext::{lua_ext_checkinteger64, lua_ext_checkuinteger64, lua_ext_fast_checkboolean, lua_ext_fast_checkinteger, lua_ext_fast_checknumber, lua_ext_pushinteger64, lua_ext_pushuinteger64}; -use crate::ffi::laux::{luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_checkudata, luaL_testudata}; -use crate::ffi::lua::{lua_isnumber, lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type}; +use crate::ffi::ext::{ + lua_ext_checkinteger64, lua_ext_checkuinteger64, lua_ext_fast_checkboolean, + lua_ext_fast_checkinteger, lua_ext_fast_checknumber, lua_ext_pushinteger64, + lua_ext_pushuinteger64, +}; +use crate::ffi::laux::{ + luaL_checkinteger, luaL_checklstring, luaL_checknumber, luaL_checkudata, luaL_testudata, +}; +use crate::ffi::lua::{ + lua_isnumber, lua_pushboolean, lua_pushinteger, lua_pushnil, lua_pushnumber, lua_toboolean, + lua_tointeger, lua_tonumber, lua_type, RawInteger, RawNumber, Type, +}; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::UserData; use crate::vm::util::{lua_rust_error, LuaType, TypeName}; +use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::borrow::Cow; use std::error::Error; use std::slice; -use crate::vm::value::types::{Boolean, Integer, Number}; impl<'a, T: FromParam<'a> + SimpleDrop> FromParam<'a> for Option { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { @@ -207,7 +216,7 @@ macro_rules! impl_integer_64 { unsafe { $push_func(vm.as_ptr(), self as _) } } } - } + }; } impl_integer_64!(i64, lua_ext_checkinteger64, lua_ext_pushinteger64); @@ -259,7 +268,7 @@ macro_rules! impl_float { impl_float!(f32, f64); -impl LuaType for bool { } +impl LuaType for bool {} impl FromParam<'_> for bool { #[inline(always)] diff --git a/core/src/vm/registry/core.rs b/core/src/vm/registry/core.rs index c5d4a72..6f37f2a 100644 --- a/core/src/vm/registry/core.rs +++ b/core/src/vm/registry/core.rs @@ -28,13 +28,13 @@ use crate::ffi::laux::{luaL_ref, luaL_unref}; use crate::ffi::lua::{lua_rawgeti, lua_rawseti, REGISTRYINDEX}; +#[cfg(feature = "send")] +use crate::vm::registry::send_key::VmCheckedRawKey; use crate::vm::registry::{FromIndex, Set, Value}; use crate::vm::value::util::move_value_top; use crate::vm::Vm; use std::ffi::c_int; use std::marker::PhantomData; -#[cfg(feature = "send")] -use crate::vm::registry::send_key::VmCheckedRawKey; //TODO: Check if key can be a NonZeroI32. @@ -173,7 +173,9 @@ impl Key { #[cfg(feature = "send")] self.raw.delete(vm); #[cfg(not(feature = "send"))] - unsafe { self.raw.delete(vm) }; + unsafe { + self.raw.delete(vm) + }; } #[inline(always)] diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 7a11280..385129b 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::marker::PhantomData; use crate::ffi::lua::{lua_replace, lua_settop}; use crate::impl_simple_registry_value_static; use crate::vm::registry::{FromIndex, Set}; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::types::RawPtr; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::marker::PhantomData; /// Represents a simple value type which can be manipulated by [LuaRef]. /// @@ -129,7 +129,9 @@ impl<'a, T> Drop for LuaRef<'a, T> { fn drop(&mut self) { // Remove the object from the lua stack if it is on top of the stack. if self.index == self.vm.top() { - unsafe { lua_settop(self.vm.as_ptr(), -2); } + unsafe { + lua_settop(self.vm.as_ptr(), -2); + } } } } @@ -157,7 +159,10 @@ impl super::Value for super::types::LuaRef } } -unsafe impl<'a, T> SimpleValue<'a> for T where T: FromLua<'a> + IntoLua { +unsafe impl<'a, T> SimpleValue<'a> for T +where + T: FromLua<'a> + IntoLua, +{ #[inline(always)] fn into_lua(self, vm: &'a Vm) { // This ensures the safety guarentee still holds. diff --git a/core/src/vm/registry/mod.rs b/core/src/vm/registry/mod.rs index a707f82..cee6be6 100644 --- a/core/src/vm/registry/mod.rs +++ b/core/src/vm/registry/mod.rs @@ -28,9 +28,9 @@ pub mod core; mod interface; +pub mod lua_ref; pub mod named; pub mod types; -pub mod lua_ref; #[cfg(feature = "send")] mod send_key; diff --git a/core/src/vm/registry/named.rs b/core/src/vm/registry/named.rs index 9fb621d..75a3616 100644 --- a/core/src/vm/registry/named.rs +++ b/core/src/vm/registry/named.rs @@ -26,16 +26,19 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::HashMap; -use crate::ffi::lua::{lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, REGISTRYINDEX}; +use crate::ffi::ext::lua_ext_keyreg_get; +use crate::ffi::lua::{ + lua_insert, lua_pushlightuserdata, lua_rawget, lua_rawset, lua_settop, lua_type, State, Type, + REGISTRYINDEX, +}; use crate::vm::registry::{Set, Value}; use crate::vm::value::util::move_value_top; use crate::vm::Vm; +use std::collections::HashMap; use std::ffi::c_void; use std::marker::PhantomData; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Mutex; -use crate::ffi::ext::lua_ext_keyreg_get; #[derive(Debug)] pub struct RawKey { @@ -45,7 +48,7 @@ pub struct RawKey { // which must implement the Value trait which limits the number of possible types. ty: fn() -> &'static str, registered: AtomicBool, - register_lock: Mutex + register_lock: Mutex, } unsafe impl Send for RawKey {} @@ -95,7 +98,7 @@ impl RawKey { ptr: val as usize as *const c_void, ty, registered: AtomicBool::new(false), - register_lock: Mutex::new(false) + register_lock: Mutex::new(false), } } } @@ -137,29 +140,26 @@ fn check_register_key_unique(key: &RawKey) { type NamedKeyRegistry = Mutex>; -unsafe fn voidp_to_ref(p: *mut c_void) -> &'static NamedKeyRegistry -{ +unsafe fn voidp_to_ref(p: *mut c_void) -> &'static NamedKeyRegistry { assert!(!p.is_null()); unsafe { &*(p as *const NamedKeyRegistry) } } -#[cfg(feature="root-vm")] -unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut NamedKeyRegistry -{ +#[cfg(feature = "root-vm")] +unsafe fn voidp_to_ptr(p: *mut c_void) -> *mut NamedKeyRegistry { assert!(!p.is_null()); p as *mut NamedKeyRegistry } -#[cfg(feature="root-vm")] -fn ref_to_voidp(r: &'static NamedKeyRegistry) -> *mut c_void -{ +#[cfg(feature = "root-vm")] +fn ref_to_voidp(r: &'static NamedKeyRegistry) -> *mut c_void { r as *const NamedKeyRegistry as *mut c_void } -#[cfg(feature="root-vm")] +#[cfg(feature = "root-vm")] pub(crate) fn handle_root_vm_init() { - use bp3d_debug::debug; use crate::ffi::ext::lua_ext_keyreg_ref; + use bp3d_debug::debug; let ptr = ref_to_voidp(Box::leak(Box::new(Mutex::new(HashMap::new())))); // Pointer set in lua_ext_keyreg_ref to avoid TOCTOU. let ptr = unsafe { lua_ext_keyreg_ref(ptr) }; @@ -171,10 +171,10 @@ pub(crate) fn handle_root_vm_init() { } } -#[cfg(feature="root-vm")] +#[cfg(feature = "root-vm")] pub(crate) fn handle_root_vm_uninit() { - use bp3d_debug::debug; use crate::ffi::ext::lua_ext_keyreg_unref; + use bp3d_debug::debug; // Pointer reset to NULL in lua_ext_keyreg_unref to avoid TOCTOU. let ptr = unsafe { lua_ext_keyreg_unref() }; if !ptr.is_null() { @@ -187,7 +187,7 @@ pub(crate) fn handle_root_vm_uninit() { pub struct Key { raw: RawKey, - useless: PhantomData<*const T> + useless: PhantomData<*const T>, } unsafe impl Send for Key {} diff --git a/core/src/vm/registry/send_key.rs b/core/src/vm/registry/send_key.rs index a5c184f..50bbc4a 100644 --- a/core/src/vm/registry/send_key.rs +++ b/core/src/vm/registry/send_key.rs @@ -38,12 +38,18 @@ pub struct VmCheckedRawKey { impl VmCheckedRawKey { pub fn push(&self, vm: &Vm) { - assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); + assert_eq!( + unsafe { lua_ext_getprovenance(vm.as_ptr()) }, + self.provenance + ); unsafe { self.raw.push(vm) } } pub fn delete(self, vm: &Vm) { - assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); + assert_eq!( + unsafe { lua_ext_getprovenance(vm.as_ptr()) }, + self.provenance + ); unsafe { self.raw.delete(vm) } } @@ -58,14 +64,17 @@ impl FromIndex for VmCheckedRawKey { let raw = RawKey::from_index(vm, index); Self { provenance: unsafe { lua_ext_getprovenance(vm.as_ptr()) }, - raw + raw, } } } impl Set for VmCheckedRawKey { unsafe fn set(&self, vm: &Vm, index: i32) { - assert_eq!(unsafe { lua_ext_getprovenance(vm.as_ptr()) }, self.provenance); + assert_eq!( + unsafe { lua_ext_getprovenance(vm.as_ptr()) }, + self.provenance + ); self.raw.set(vm, index); } } diff --git a/core/src/vm/table/core.rs b/core/src/vm/table/core.rs index a6aa5c0..5781a6e 100644 --- a/core/src/vm/table/core.rs +++ b/core/src/vm/table/core.rs @@ -27,7 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::ext::{lua_ext_tab_len, MSize}; -use crate::ffi::lua::{lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, lua_setmetatable, lua_settable, lua_topointer}; +use crate::ffi::lua::{ + lua_createtable, lua_gettable, lua_gettop, lua_objlen, lua_pushvalue, lua_rawseti, + lua_setmetatable, lua_settable, lua_topointer, +}; use crate::vm::table::iter::Iter; use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::value::util::{check_get_metatable, check_push_single}; @@ -60,11 +63,7 @@ impl Eq for Table<'_> {} impl Display for Table<'_> { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "table@{:X}", - self.uid() - ) + write!(f, "table@{:X}", self.uid()) } } diff --git a/core/src/vm/table/immutable.rs b/core/src/vm/table/immutable.rs index e13a104..6075404 100644 --- a/core/src/vm/table/immutable.rs +++ b/core/src/vm/table/immutable.rs @@ -26,12 +26,12 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Display; use crate::vm::table::iter::Iter; -use crate::vm::table::Table; use crate::vm::table::traits::GetTable; +use crate::vm::table::Table; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; +use std::fmt::Display; #[derive(Debug, Clone, PartialEq, Eq)] pub struct ImmutableTable<'a>(Table<'a>); @@ -106,12 +106,18 @@ impl<'a> ImmutableTable<'a> { } #[inline(always)] - pub fn get<'b, T: FromLua<'b> + ImmutableValue>(&'b self, key: impl GetTable) -> crate::vm::Result { + pub fn get<'b, T: FromLua<'b> + ImmutableValue>( + &'b self, + key: impl GetTable, + ) -> crate::vm::Result { self.0.get(key) } #[inline(always)] - pub fn get_any<'b, T: FromLua<'b> + ImmutableValue>(&'b self, key: impl IntoLua) -> crate::vm::Result { + pub fn get_any<'b, T: FromLua<'b> + ImmutableValue>( + &'b self, + key: impl IntoLua, + ) -> crate::vm::Result { self.0.get_any(key) } diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index d9a391b..aada139 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -26,12 +26,9 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::{BTreeMap, HashMap}; -use std::hash::Hash; use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{ - lua_getfield, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, - State, Type, + lua_getfield, lua_rawgeti, lua_rawseti, lua_setfield, lua_type, State, Type, }; use crate::impl_registry_value; use crate::util::core::{AnyStr, SimpleDrop}; @@ -43,6 +40,8 @@ use crate::vm::util::LuaType; use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; +use std::collections::{BTreeMap, HashMap}; +use std::hash::Hash; unsafe impl SimpleDrop for Table<'_> {} @@ -154,7 +153,10 @@ impl SetTable for T { } } -impl<'a, T: 'static> FromLua<'a> for Vec where for<'b> T: FromLua<'b> { +impl<'a, T: 'static> FromLua<'a> for Vec +where + for<'b> T: FromLua<'b>, +{ unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let mut tbl = Table::from_lua_unchecked(vm, index); let mut vec = Vec::new(); @@ -178,8 +180,11 @@ impl<'a, T: 'static> FromLua<'a> for Vec where for<'b> T: FromLua<'b> { unsafe impl ImmutableValue for Vec {} -impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap where for<'b> K: FromLua<'b> + Hash + Eq, - for<'b> V: FromLua<'b> { +impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap +where + for<'b> K: FromLua<'b> + Hash + Eq, + for<'b> V: FromLua<'b>, +{ unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let mut tbl = Table::from_lua_unchecked(vm, index); let mut map = HashMap::new(); @@ -205,8 +210,11 @@ impl<'a, K: 'static, V: 'static> FromLua<'a> for HashMap where for<'b> K: unsafe impl ImmutableValue for HashMap {} -impl<'a, K: 'static, V: 'static> FromLua<'a> for BTreeMap where for<'b> K: FromLua<'b> + Ord, - for<'b> V: FromLua<'b> { +impl<'a, K: 'static, V: 'static> FromLua<'a> for BTreeMap +where + for<'b> K: FromLua<'b> + Ord, + for<'b> V: FromLua<'b>, +{ unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let mut tbl = Table::from_lua_unchecked(vm, index); let mut map = BTreeMap::new(); diff --git a/core/src/vm/table/mod.rs b/core/src/vm/table/mod.rs index 26dc601..1d10f79 100644 --- a/core/src/vm/table/mod.rs +++ b/core/src/vm/table/mod.rs @@ -27,10 +27,10 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod core; +mod immutable; mod interface; mod iter; pub mod traits; -mod immutable; pub use core::Table; diff --git a/core/src/vm/thread/core.rs b/core/src/vm/thread/core.rs index acd778a..b3f0d46 100644 --- a/core/src/vm/thread/core.rs +++ b/core/src/vm/thread/core.rs @@ -26,14 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; -use std::marker::PhantomData; use crate::ffi::laux::luaL_error; -use crate::ffi::lua::{lua_isyieldable, lua_remove, lua_resume, lua_status, lua_yield, ThreadStatus}; +use crate::ffi::lua::{ + lua_isyieldable, lua_remove, lua_resume, lua_status, lua_yield, ThreadStatus, +}; use crate::vm::error::{Error, RuntimeError}; use crate::vm::function::IntoParam; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::{Debug, Display}; +use std::marker::PhantomData; #[derive(Copy, Clone, Eq, PartialEq, Debug)] pub enum State { @@ -43,12 +45,12 @@ pub enum State { pub struct Output { pub state: State, - pub data: T + pub data: T, } pub struct Thread<'a> { vm: Vm, - useless: PhantomData<&'a ()> + useless: PhantomData<&'a ()>, } impl PartialEq for Thread<'_> { @@ -87,7 +89,7 @@ impl<'a> Thread<'a> { pub unsafe fn from_raw(l: crate::ffi::lua::State) -> Self { Self { vm: Vm::from_raw(l), - useless: PhantomData + useless: PhantomData, } } @@ -109,8 +111,10 @@ impl<'a> Thread<'a> { } pub fn resume<'b, T: FromLua<'b>>(&'b self, args: impl IntoLua) -> crate::vm::Result> - where T: 'static /* This clause ensures that a future call to collectgarbage or resume does - not free a lua value which would be borrowed by a previous call to resume */ { + where + T: 'static, /* This clause ensures that a future call to collectgarbage or resume does + not free a lua value which would be borrowed by a previous call to resume */ + { let num = args.into_lua(&self.vm); let top = self.vm.top(); let res = unsafe { lua_resume(self.vm.as_ptr(), num as _) }; @@ -119,16 +123,16 @@ impl<'a> Thread<'a> { let data = T::from_lua(&self.vm, top)?; Ok(Output { state: State::Finished, - data + data, }) - }, + } ThreadStatus::Yield => { let data = T::from_lua(&self.vm, top)?; Ok(Output { state: State::Suspended, - data + data, }) - }, + } ThreadStatus::ErrRun => { // We've got a runtime error when executing the function. // TODO: In the future, might be great to traceback the thread as well. @@ -152,7 +156,10 @@ unsafe impl IntoParam for Yield { fn into_param(self, vm: &Vm) -> i32 { unsafe { if lua_isyieldable(vm.as_ptr()) != 1 { - luaL_error(vm.as_ptr(), c"attempt to yield a non-thread stack object".as_ptr()); + luaL_error( + vm.as_ptr(), + c"attempt to yield a non-thread stack object".as_ptr(), + ); } let num = self.0.into_param(vm); lua_yield(vm.as_ptr(), num); diff --git a/core/src/vm/thread/interface.rs b/core/src/vm/thread/interface.rs index 04de112..b36d3f7 100644 --- a/core/src/vm/thread/interface.rs +++ b/core/src/vm/thread/interface.rs @@ -34,8 +34,8 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::thread::value::{ImmutableThread, Thread}; use crate::vm::util::LuaType; -use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::value::util::{check_type_equals, check_value_top}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; unsafe impl ImmutableValue for ImmutableThread<'_> {} diff --git a/core/src/vm/thread/mod.rs b/core/src/vm/thread/mod.rs index 78e39c7..40f4740 100644 --- a/core/src/vm/thread/mod.rs +++ b/core/src/vm/thread/mod.rs @@ -27,5 +27,5 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. pub mod core; -pub mod value; mod interface; +pub mod value; diff --git a/core/src/vm/thread/value.rs b/core/src/vm/thread/value.rs index 2be3605..c451384 100644 --- a/core/src/vm/thread/value.rs +++ b/core/src/vm/thread/value.rs @@ -26,18 +26,20 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::{Debug, Display}; -use crate::ffi::lua::{lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type}; +use crate::ffi::lua::{ + lua_newthread, lua_pushvalue, lua_tothread, lua_type, lua_xmove, ThreadStatus, Type, +}; use crate::vm::thread::core; use crate::vm::value::types::Function; use crate::vm::value::util::move_value_top; use crate::vm::Vm; +use std::fmt::{Debug, Display}; /// Represents a thread object value on a lua stack. pub struct Thread<'a> { pub(super) vm: &'a Vm, index: i32, - thread: core::Thread<'static> + thread: core::Thread<'static>, } impl Clone for Thread<'_> { @@ -46,7 +48,7 @@ impl Clone for Thread<'_> { Thread { vm: self.vm, index: self.vm.top(), - thread: unsafe { core::Thread::from_raw(self.thread.as_ptr()) } + thread: unsafe { core::Thread::from_raw(self.thread.as_ptr()) }, } } } @@ -90,7 +92,7 @@ impl<'a> Thread<'a> { Self { vm, index, - thread: core::Thread::from_raw(lua_tothread(vm.as_ptr(), index)) + thread: core::Thread::from_raw(lua_tothread(vm.as_ptr(), index)), } } @@ -99,7 +101,7 @@ impl<'a> Thread<'a> { Self { vm, index: vm.top(), - thread + thread, } } @@ -108,7 +110,9 @@ impl<'a> Thread<'a> { return Err(crate::vm::error::Error::BadThreadState); } move_value_top(self.vm, function.index()); - unsafe { lua_xmove(self.vm.as_ptr(), self.thread.as_ptr(), 1); } + unsafe { + lua_xmove(self.vm.as_ptr(), self.thread.as_ptr(), 1); + } unsafe { assert_eq!(lua_type(self.thread.as_ptr(), -1), Type::Function); }; diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index a80f67a..e12f170 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -27,18 +27,20 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::luaL_testudata; -use crate::ffi::lua::{lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type}; -use crate::vm::error::{Error, TypeError}; -use crate::vm::userdata::{UserData, UserDataImmutable}; -use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; -use crate::vm::Vm; -use std::fmt::{Debug, Display}; +use crate::ffi::lua::{ + lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type, +}; use crate::util::core::{AnyStr, SimpleDrop}; use crate::util::LuaFunction; +use crate::vm::error::{Error, TypeError}; use crate::vm::table::ImmutableTable; +use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::util::LuaType; use crate::vm::value::types::Function; use crate::vm::value::util::{check_get_metatable, check_push_value}; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; +use crate::vm::Vm; +use std::fmt::{Debug, Display}; pub struct AnyUserData<'a> { vm: &'a Vm, @@ -66,14 +68,15 @@ impl Eq for AnyUserData<'_> {} //Stupid fmt name in Rust which causes conflicts... fn internal_display(ud: &AnyUserData, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { let res = ud.vm.scope(|_| { - let res: crate::vm::Result<&str> = ud.call_method("__tostring", ()) + let res: crate::vm::Result<&str> = ud + .call_method("__tostring", ()) .or_else(|_| ud.call_method("tostring", ())); match res { Ok(v) => { let type_name = ud.get_type_name()?; Ok(write!(f, "{}({})", type_name, v)) - }, - Err(e) => Err(e) + } + Err(e) => Err(e), } }); match res { @@ -82,7 +85,7 @@ fn internal_display(ud: &AnyUserData, f: &mut std::fmt::Formatter<'_>) -> std::f f, "userdata@{:X}", unsafe { lua_touserdata(ud.vm.as_ptr(), ud.index) } as usize - ) + ), } } @@ -164,7 +167,11 @@ impl<'a> AnyUserData<'a> { Ok(value2) } - pub fn call_method<'b, T: FromLua<'b>>(&'b self, name: impl AnyStr, args: impl IntoLua) -> crate::vm::Result { + pub fn call_method<'b, T: FromLua<'b>>( + &'b self, + name: impl AnyStr, + args: impl IntoLua, + ) -> crate::vm::Result { let tbl = self.get_metatable().ok_or(Error::Type(TypeError { expected: Type::Table, actual: Type::None, @@ -263,7 +270,11 @@ impl<'a> ImmutableAnyUserData<'a> { } #[inline(always)] - pub fn call_method<'b, T: FromLua<'b> + ImmutableValue>(&'b self, name: impl AnyStr, args: impl IntoLua) -> crate::vm::Result { + pub fn call_method<'b, T: FromLua<'b> + ImmutableValue>( + &'b self, + name: impl AnyStr, + args: impl IntoLua, + ) -> crate::vm::Result { self.0.call_method(name, args) } } diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 3063757..d8de4a9 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -27,7 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::ffi::laux::{luaL_checkudata, luaL_newmetatable}; -use crate::ffi::lua::{lua_getmetatable, lua_pushcclosure, lua_pushlightuserdata, lua_pushnil, lua_pushvalue, lua_rawget, lua_setfield, lua_setmetatable, lua_settop, lua_touserdata, lua_type, CFunction, State, Type, GLOBALSINDEX}; +use crate::ffi::lua::{ + lua_getmetatable, lua_pushcclosure, lua_pushlightuserdata, lua_pushnil, lua_pushvalue, + lua_rawget, lua_setfield, lua_setmetatable, lua_settop, lua_touserdata, lua_type, CFunction, + State, Type, GLOBALSINDEX, +}; +use crate::vm::table::Table; use crate::vm::userdata::{AddGcMethod, Error, LuaDrop, NameConvert, UserData}; use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::IntoLua; @@ -36,7 +41,6 @@ use bp3d_debug::{debug, trace, warning}; use std::cell::OnceCell; use std::ffi::{c_void, CStr}; use std::marker::PhantomData; -use crate::vm::table::Table; #[derive(Copy, Clone)] pub struct Function { @@ -162,7 +166,9 @@ impl<'a, T: UserData, C: NameConvert> Registry<'a, T, C> { pub fn add_static_field(&self, name: &'static CStr, value: impl IntoLua) -> Result<(), Error> { let _ = self.has_static.set(()); let mut static_table = unsafe { Table::from_raw(self.vm, -3) }; - static_table.set(&*self.case.name_convert(name), value).map_err(|_| Error::MultiValueField)?; + static_table + .set(&*self.case.name_convert(name), value) + .map_err(|_| Error::MultiValueField)?; Ok(()) } diff --git a/core/src/vm/userdata/util.rs b/core/src/vm/userdata/util.rs index 89199e4..099eecf 100644 --- a/core/src/vm/userdata/util.rs +++ b/core/src/vm/userdata/util.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::CStr; use crate::ffi::lua::{lua_getfield, lua_replace, lua_settop, lua_type, Type, REGISTRYINDEX}; use crate::vm::table::ImmutableTable; use crate::vm::userdata::UserData; use crate::vm::Vm; +use std::ffi::CStr; /// Returns the static table attached to the given UserData type. /// diff --git a/core/src/vm/value/any.rs b/core/src/vm/value/any.rs index ddbd477..f615637 100644 --- a/core/src/vm/value/any.rs +++ b/core/src/vm/value/any.rs @@ -31,7 +31,7 @@ use crate::util::core::SimpleDrop; use crate::vm::error::{Error, TypeError}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::table::{ImmutableTable, Table}; -use crate::vm::thread::value::{ImmutableThread, Thread as Thread}; +use crate::vm::thread::value::{ImmutableThread, Thread}; use crate::vm::userdata::{AnyUserData, ImmutableAnyUserData}; use crate::vm::util::{lua_rust_error, LuaType}; use crate::vm::value::function::Function; @@ -41,7 +41,8 @@ use std::fmt::Display; use std::str::FromStr; pub type Any<'a> = AnyValue<'a, Table<'a>, AnyUserData<'a>, Thread<'a>>; -pub type AnyImmutable<'a> = AnyValue<'a, ImmutableTable<'a>, ImmutableAnyUserData<'a>, ImmutableThread<'a>>; +pub type AnyImmutable<'a> = + AnyValue<'a, ImmutableTable<'a>, ImmutableAnyUserData<'a>, ImmutableThread<'a>>; #[derive(Debug, PartialEq, Clone)] pub enum AnyValue<'a, T, U, R> { @@ -75,7 +76,7 @@ impl Display for AnyValue<'_, T, U, R> { AnyValue::UserData(v) => write!(f, "{}", v), AnyValue::Thread(v) => write!(f, "{}", v), AnyValue::Int64(v) => write!(f, "{}", v), - AnyValue::UInt64(v) => write!(f, "{}", v) + AnyValue::UInt64(v) => write!(f, "{}", v), } } } @@ -94,7 +95,7 @@ impl AnyValue<'_, T, U, R> { AnyValue::UserData(_) => Type::Userdata, AnyValue::Thread(_) => Type::Thread, AnyValue::Int64(_) => Type::Cdata, - AnyValue::UInt64(_) => Type::Cdata + AnyValue::UInt64(_) => Type::Cdata, } } @@ -114,9 +115,7 @@ impl AnyValue<'_, T, U, R> { pub fn to_integer(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v as _), - AnyValue::String(v) => { - i64::from_str(v).map_err(|_| Error::ParseInt) - } + AnyValue::String(v) => i64::from_str(v).map_err(|_| Error::ParseInt), AnyValue::Int64(v) => Ok(*v), AnyValue::UInt64(v) => Ok(*v as _), _ => Err(Error::Type(TypeError { @@ -129,9 +128,7 @@ impl AnyValue<'_, T, U, R> { pub fn to_uinteger(&self) -> Result { match self { AnyValue::Number(v) => Ok(*v as _), - AnyValue::String(v) => { - u64::from_str(v).map_err(|_| Error::ParseInt) - } + AnyValue::String(v) => u64::from_str(v).map_err(|_| Error::ParseInt), AnyValue::Int64(v) => Ok(*v as _), AnyValue::UInt64(v) => Ok(*v), _ => Err(Error::Type(TypeError { @@ -159,7 +156,7 @@ unsafe impl IntoLua for Any<'_> { AnyValue::UserData(_) => 0, AnyValue::Thread(_) => 0, AnyValue::Int64(v) => v.into_lua(vm), - AnyValue::UInt64(v) => v.into_lua(vm) + AnyValue::UInt64(v) => v.into_lua(vm), } } } @@ -206,11 +203,10 @@ impl<'a, T: FromLua<'a>, U: FromLua<'a>, R: FromLua<'a>> FromLua<'a> for AnyValu Ok(unsafe { AnyValue::UserData(FromLua::from_lua_unchecked(vm, index)) }) } Type::Thread => Ok(unsafe { AnyValue::Thread(FromLua::from_lua_unchecked(vm, index)) }), - Type::Cdata => { - i64::from_lua(vm, index).map(AnyValue::Int64) - .or_else(|_| u64::from_lua(vm, index).map(AnyValue::UInt64)) - .map_err(|_| Error::UnsupportedType(ty)) - }, + Type::Cdata => i64::from_lua(vm, index) + .map(AnyValue::Int64) + .or_else(|_| u64::from_lua(vm, index).map(AnyValue::UInt64)) + .map_err(|_| Error::UnsupportedType(ty)), } } } @@ -219,7 +215,13 @@ unsafe impl SimpleDrop for AnyValue impl LuaType for AnyValue<'_, T, U, R> {} -impl<'a, T: FromLua<'a> + SimpleDrop + LuaType, U: FromLua<'a> + SimpleDrop + LuaType, R: FromLua<'a> + SimpleDrop + LuaType> FromParam<'a> for AnyValue<'a, T, U, R> { +impl< + 'a, + T: FromLua<'a> + SimpleDrop + LuaType, + U: FromLua<'a> + SimpleDrop + LuaType, + R: FromLua<'a> + SimpleDrop + LuaType, + > FromParam<'a> for AnyValue<'a, T, U, R> +{ #[inline(always)] unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { match FromLua::from_lua(vm, index) { diff --git a/core/src/vm/value/core.rs b/core/src/vm/value/core.rs index 89306c8..d0d4e33 100644 --- a/core/src/vm/value/core.rs +++ b/core/src/vm/value/core.rs @@ -26,16 +26,23 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use crate::ffi::ext::{lua_ext_getinteger64, lua_ext_getuinteger64, lua_ext_pushinteger64, lua_ext_pushuinteger64, lua_ext_tointeger64, lua_ext_touinteger64}; +use crate::ffi::ext::{ + lua_ext_getinteger64, lua_ext_getuinteger64, lua_ext_pushinteger64, lua_ext_pushuinteger64, + lua_ext_tointeger64, lua_ext_touinteger64, +}; use crate::ffi::laux::{luaL_setmetatable, luaL_testudata}; -use crate::ffi::lua::{lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tointegerx, lua_tolstring, lua_tonumber, lua_tonumberx, lua_touserdata, lua_type, Type}; +use crate::ffi::lua::{ + lua_newuserdata, lua_pushboolean, lua_pushinteger, lua_pushlstring, lua_pushnil, + lua_pushnumber, lua_settop, lua_toboolean, lua_tointeger, lua_tointegerx, lua_tolstring, + lua_tonumber, lua_tonumberx, lua_touserdata, lua_type, Type, +}; use crate::vm::error::{Error, TypeError}; use crate::vm::userdata::{UserData, UserDataImmutable}; +use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::value::util::check_type_equals; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; -use crate::vm::value::types::{Boolean, Integer, Number}; use crate::vm::Vm; +use std::borrow::Cow; impl<'a> FromLua<'a> for &'a str { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { @@ -182,14 +189,34 @@ macro_rules! impl_from_lua_64 { } unsafe impl ImmutableValue for $t {} - } + }; } -impl_from_lua_64!(i64, lua_ext_getinteger64, lua_ext_tointeger64, lua_ext_pushinteger64); -impl_from_lua_64!(u64, lua_ext_getuinteger64, lua_ext_touinteger64, lua_ext_pushuinteger64); - -impl_from_lua_64!(isize, lua_ext_getinteger64, lua_ext_tointeger64, lua_ext_pushinteger64); -impl_from_lua_64!(usize, lua_ext_getuinteger64, lua_ext_touinteger64, lua_ext_pushuinteger64); +impl_from_lua_64!( + i64, + lua_ext_getinteger64, + lua_ext_tointeger64, + lua_ext_pushinteger64 +); +impl_from_lua_64!( + u64, + lua_ext_getuinteger64, + lua_ext_touinteger64, + lua_ext_pushuinteger64 +); + +impl_from_lua_64!( + isize, + lua_ext_getinteger64, + lua_ext_tointeger64, + lua_ext_pushinteger64 +); +impl_from_lua_64!( + usize, + lua_ext_getuinteger64, + lua_ext_touinteger64, + lua_ext_pushuinteger64 +); #[cfg(target_pointer_width = "32")] impl_from_lua!(isize, Number, lua_tointeger, lua_pushinteger, as _); diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index 98229e9..b34dd97 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -28,6 +28,7 @@ use crate::ffi::laux::luaL_checktype; use crate::ffi::lua::{lua_pushvalue, lua_topointer, Type}; +use crate::impl_registry_value; use crate::util::core::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; @@ -37,7 +38,6 @@ use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; -use crate::impl_registry_value; pub struct Function<'a> { vm: &'a Vm, diff --git a/core/src/vm/value/integer53.rs b/core/src/vm/value/integer53.rs index b0d5b08..ebf3642 100644 --- a/core/src/vm/value/integer53.rs +++ b/core/src/vm/value/integer53.rs @@ -31,8 +31,8 @@ use crate::ffi::lua::{lua_pushinteger, lua_tointeger, RawInteger, Type}; use crate::util::core::{SimpleDrop, TryFromIntError}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::util::{LuaType, TypeName}; -use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::value::util::check_type_equals; +use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; #[derive(Eq, PartialEq, Ord, PartialOrd, Hash, Copy, Clone, Debug)] diff --git a/core/src/vm/value/mod.rs b/core/src/vm/value/mod.rs index c06f834..a1fb77e 100644 --- a/core/src/vm/value/mod.rs +++ b/core/src/vm/value/mod.rs @@ -29,11 +29,11 @@ pub mod any; mod core; mod function; +mod integer53; mod interface; -pub mod types; -pub mod util; mod raw_ptr; +pub mod types; mod unknown; -mod integer53; +pub mod util; pub use interface::*; diff --git a/core/src/vm/value/raw_ptr.rs b/core/src/vm/value/raw_ptr.rs index 2e9ffaa..ad5ca65 100644 --- a/core/src/vm/value/raw_ptr.rs +++ b/core/src/vm/value/raw_ptr.rs @@ -34,7 +34,7 @@ use crate::vm::Vm; #[derive(Debug)] pub struct RawPtr(*const T); -unsafe impl SimpleDrop for RawPtr { } +unsafe impl SimpleDrop for RawPtr {} impl Clone for RawPtr { #[inline(always)] @@ -43,7 +43,7 @@ impl Clone for RawPtr { } } -impl Copy for RawPtr { } +impl Copy for RawPtr {} impl RawPtr { #[inline(always)] diff --git a/core/src/vm/value/unknown.rs b/core/src/vm/value/unknown.rs index cc16d0d..1092315 100644 --- a/core/src/vm/value/unknown.rs +++ b/core/src/vm/value/unknown.rs @@ -26,17 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Debug; use crate::ffi::lua::{lua_replace, lua_type, Type}; use crate::vm::function::IntoParam; -use crate::vm::value::{FromLua, IntoLua}; use crate::vm::value::any::Any; use crate::vm::value::util::check_value_top; +use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use std::fmt::Debug; pub struct Unknown<'a> { vm: &'a Vm, - index: i32 + index: i32, } impl Debug for Unknown<'_> { @@ -60,10 +60,7 @@ impl<'a> Unknown<'a> { /// The given stack index must be absolute, if not this is UB. Using this to return the /// metatable of an UserData is also UB. pub unsafe fn from_raw(vm: &'a Vm, index: i32) -> Self { - Self { - vm, - index - } + Self { vm, index } } /// Interprets the underlying reference on the lua stack as the specified Rust type. @@ -101,7 +98,8 @@ impl<'a> Unknown<'a> { unsafe impl IntoLua for Unknown<'_> { fn into_lua(self, vm: &Vm) -> u16 { - if self.ty() == Type::None { // None is not a value, do not operate the stack or UB. + if self.ty() == Type::None { + // None is not a value, do not operate the stack or UB. return 0; // No value exists on the stack so IntoLua returns 0 values. } check_value_top(self.vm, vm, self.index) diff --git a/core/src/vm/value/util.rs b/core/src/vm/value/util.rs index d7eb8d1..cc22808 100644 --- a/core/src/vm/value/util.rs +++ b/core/src/vm/value/util.rs @@ -26,7 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_getmetatable, lua_gettop, lua_pushnil, lua_pushvalue, lua_replace, lua_settop, lua_type, Type}; +use crate::ffi::lua::{ + lua_getmetatable, lua_gettop, lua_pushnil, lua_pushvalue, lua_replace, lua_settop, lua_type, + Type, +}; use crate::vm::error::{Error, TypeError}; use crate::vm::table::Table; use crate::vm::value::IntoLua; diff --git a/core/tests/test_multi_root_vms.rs b/core/tests/test_multi_root_vms.rs index 9df1786..3851fcf 100644 --- a/core/tests/test_multi_root_vms.rs +++ b/core/tests/test_multi_root_vms.rs @@ -30,9 +30,9 @@ use bp3d_lua::vm::core::destructor::Pool; use bp3d_lua::vm::registry::core::Key; -use bp3d_lua::vm::RootVm; -use bp3d_lua::vm::value::types::Function; use bp3d_lua::vm::registry::types; +use bp3d_lua::vm::value::types::Function; +use bp3d_lua::vm::RootVm; #[test] fn test_multi_root_vms_basic() { diff --git a/core/tests/test_vm_custom_structs.rs b/core/tests/test_vm_custom_structs.rs index 9c6f637..d30085d 100644 --- a/core/tests/test_vm_custom_structs.rs +++ b/core/tests/test_vm_custom_structs.rs @@ -31,8 +31,8 @@ use bp3d_lua::decl_lib_func; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::RootVm; -use bp3d_lua_codegen::{IntoLua, LuaType}; use bp3d_lua_codegen::{FromParam, IntoParam}; +use bp3d_lua_codegen::{IntoLua, LuaType}; #[derive(FromParam, LuaType, IntoParam)] struct Test1<'a>(&'a str, i32); diff --git a/core/tests/test_vm_integer53.rs b/core/tests/test_vm_integer53.rs index e6534f8..fd39960 100644 --- a/core/tests/test_vm_integer53.rs +++ b/core/tests/test_vm_integer53.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::types::{Int53, UInt53}; +use bp3d_lua::vm::RootVm; #[test] fn test_vm_u53() { @@ -36,7 +36,10 @@ fn test_vm_u53() { let val: UInt53 = vm.run_code(c"return 2^53-1").unwrap(); assert_eq!(val, UInt53::MAX); vm.set_global(c"UINT53_MAX", UInt53::MAX).unwrap(); - assert_eq!(vm.run_code::<&str>(c"return tostring(UINT53_MAX)").unwrap(), "9.007199254741e+15"); + assert_eq!( + vm.run_code::<&str>(c"return tostring(UINT53_MAX)").unwrap(), + "9.007199254741e+15" + ); vm.run_code::<()>(c"assert(UINT53_MAX == 2^53-1)").unwrap(); assert_eq!(top + 2, vm.top()); } @@ -51,8 +54,14 @@ fn test_vm_i53() { assert_eq!(val, Int53::MIN); vm.set_global(c"INT53_MAX", Int53::MAX).unwrap(); vm.set_global(c"INT53_MIN", Int53::MIN).unwrap(); - assert_eq!(vm.run_code::<&str>(c"return tostring(INT53_MAX)").unwrap(), "4.5035996273705e+15"); - assert_eq!(vm.run_code::<&str>(c"return tostring(INT53_MIN)").unwrap(), "-4.5035996273705e+15"); + assert_eq!( + vm.run_code::<&str>(c"return tostring(INT53_MAX)").unwrap(), + "4.5035996273705e+15" + ); + assert_eq!( + vm.run_code::<&str>(c"return tostring(INT53_MIN)").unwrap(), + "-4.5035996273705e+15" + ); vm.run_code::<()>(c"assert(INT53_MAX == 2^52-1)").unwrap(); vm.run_code::<()>(c"assert(INT53_MIN == -2^52)").unwrap(); assert_eq!(top + 4, vm.top()); diff --git a/core/tests/test_vm_integer64.rs b/core/tests/test_vm_integer64.rs index cf129ea..65ce2cf 100644 --- a/core/tests/test_vm_integer64.rs +++ b/core/tests/test_vm_integer64.rs @@ -27,9 +27,9 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use bp3d_lua::ffi::lua::Type; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::value::any::Any; use bp3d_lua::vm::value::util::check_type_equals; +use bp3d_lua::vm::RootVm; #[test] fn test_vm_u64() { @@ -39,8 +39,12 @@ fn test_vm_u64() { assert!(check_type_equals(&vm, -1, Type::Cdata).is_ok()); assert_eq!(val, u64::MAX); vm.set_global(c"UINT64_MAX", u64::MAX).unwrap(); - assert_eq!(vm.run_code::<&str>(c"return tostring(UINT64_MAX)").unwrap(), "18446744073709551615ULL"); - vm.run_code::<()>(c"assert(UINT64_MAX == 2ULL^64ULL-1ULL)").unwrap(); + assert_eq!( + vm.run_code::<&str>(c"return tostring(UINT64_MAX)").unwrap(), + "18446744073709551615ULL" + ); + vm.run_code::<()>(c"assert(UINT64_MAX == 2ULL^64ULL-1ULL)") + .unwrap(); assert_eq!(top + 2, vm.top()); } @@ -56,10 +60,18 @@ fn test_vm_i64() { assert_eq!(val, i64::MIN); vm.set_global(c"INT64_MAX", i64::MAX).unwrap(); vm.set_global(c"INT64_MIN", i64::MIN).unwrap(); - assert_eq!(vm.run_code::<&str>(c"return tostring(INT64_MAX)").unwrap(), "9223372036854775807LL"); - assert_eq!(vm.run_code::<&str>(c"return tostring(INT64_MIN)").unwrap(), "-9223372036854775808LL"); - vm.run_code::<()>(c"assert(INT64_MAX == 2LL^63LL-1LL)").unwrap(); - vm.run_code::<()>(c"assert(INT64_MIN == -2LL^63LL)").unwrap(); + assert_eq!( + vm.run_code::<&str>(c"return tostring(INT64_MAX)").unwrap(), + "9223372036854775807LL" + ); + assert_eq!( + vm.run_code::<&str>(c"return tostring(INT64_MIN)").unwrap(), + "-9223372036854775808LL" + ); + vm.run_code::<()>(c"assert(INT64_MAX == 2LL^63LL-1LL)") + .unwrap(); + vm.run_code::<()>(c"assert(INT64_MIN == -2LL^63LL)") + .unwrap(); assert_eq!(top + 4, vm.top()); } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 7cf0c1e..20e43e8 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -62,7 +62,8 @@ fn test_vm_lib_lua() { .into_runtime() .unwrap(); assert_eq!(err.msg(), "rust error: unknown source name not"); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local function test() bp3d.lua.require \"not.existing.file\" end @@ -70,7 +71,8 @@ fn test_vm_lib_lua() { assert(not flag) print(err) assert(err ~= '') - ") + ", + ) .unwrap(); let err = vm .run_code::<()>(c"MODULES:load('broken', 'broken2')") @@ -241,14 +243,17 @@ fn test_vm_lib_os_time() { fn test_vm_lib_os_time_2() { let mut vm = RootVm::new(); bp3d_lua::libs::os::Time.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local OffsetDateTime = bp3d.os.time.OffsetDateTime local dt = OffsetDateTime.new({year = 1900, month = 12, day = 1}) local date = dt:getDate() assert(date.year == 1900) assert(date.month == 12) assert(date.day == 1) - ").unwrap(); + ", + ) + .unwrap(); } #[test] @@ -282,13 +287,20 @@ fn test_vm_lib_os() { ) .unwrap(); std::thread::sleep(std::time::Duration::from_millis(500)); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local now = os.clock() assert((clock - now) < 0.1) - ").unwrap(); - let s = vm.run_code::<&str>(c" + ", + ) + .unwrap(); + let s = vm + .run_code::<&str>( + c" return os.date('!%H:%M:%S') - ").unwrap(); + ", + ) + .unwrap(); assert!(s.contains(":")); assert!(!s.contains("[")); assert!(!s.contains("]")); @@ -298,7 +310,8 @@ fn test_vm_lib_os() { fn test_vm_lib_debug() { let mut vm = RootVm::new(); bp3d_lua::libs::lua::Debug.register(&mut vm).unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" local debug = bp3d.lua.debug local libs = debug.dumpLibs(); assert(#libs == 1) @@ -307,23 +320,37 @@ fn test_vm_lib_debug() { assert(#classes == 0) local stack = debug.dumpStack(0); assert(#stack > 0) - ").unwrap(); + ", + ) + .unwrap(); } #[test] fn test_vm_lib_util_num() { let mut vm = RootVm::new(); Util.register(&mut vm).unwrap(); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MAX)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MAX)") + .unwrap(); assert_eq!(val, "4503599627370495"); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MIN)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT53_MIN)") + .unwrap(); assert_eq!(val, "-4503599627370496"); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT53_MAX)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT53_MAX)") + .unwrap(); assert_eq!(val, "9007199254740991"); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MIN)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MIN)") + .unwrap(); assert_eq!(val, "-9223372036854775808"); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MAX)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toistring(bp3d.util.num.INT64_MAX)") + .unwrap(); assert_eq!(val, "9223372036854775807"); - let val = vm.run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT64_MAX)").unwrap(); + let val = vm + .run_code::<&str>(c"return bp3d.util.num.toustring(bp3d.util.num.UINT64_MAX)") + .unwrap(); assert_eq!(val, "18446744073709551615"); } diff --git a/core/tests/test_vm_registry.rs b/core/tests/test_vm_registry.rs index 0f6b3ce..29bfdd0 100644 --- a/core/tests/test_vm_registry.rs +++ b/core/tests/test_vm_registry.rs @@ -28,14 +28,14 @@ #![cfg(all(feature = "root-vm", feature = "util-method"))] -use std::ffi::CStr; use bp3d_lua::util::{LuaFunction, LuaMethod}; use bp3d_lua::vm::registry::core::Key; use bp3d_lua::vm::registry::lua_ref::LuaRef as LiveLuaRef; use bp3d_lua::vm::registry::types::LuaRef; -use bp3d_lua::vm::RootVm; use bp3d_lua::vm::table::Table; use bp3d_lua::vm::value::types::Function; +use bp3d_lua::vm::RootVm; +use std::ffi::CStr; const METHODS: &CStr = c" local obj = { ctx = 'this is a test' } @@ -64,10 +64,12 @@ fn test_vm_registry_method() { fn test_vm_registry_function() { let vm = RootVm::new(); let top = vm.top(); - let obj: Function = vm.run_code(c"return function() return 'Hello world' end").unwrap(); + let obj: Function = vm + .run_code(c"return function() return 'Hello world' end") + .unwrap(); let f = LuaFunction::create(obj); assert_eq!(vm.top(), top); // The function should have been popped from the stack following the - // call to LuaFunction + // call to LuaFunction let str: &str = f.call(&vm, ()).unwrap(); assert_eq!(str, "Hello world"); assert_eq!(vm.top(), top + 1); // 1 result @@ -80,7 +82,7 @@ fn test_vm_registry_string() { let r = LiveLuaRef::new(&vm, "this is a test"); let key: Key> = Key::new(r); assert_eq!(vm.top(), top); // The string should have been popped from the stack like any normal - // registry creation operation. + // registry creation operation. { let value = key.push(&vm).get(); assert_eq!(value, "this is a test"); diff --git a/core/tests/test_vm_threads.rs b/core/tests/test_vm_threads.rs index 9d8f343..b4bbbfc 100644 --- a/core/tests/test_vm_threads.rs +++ b/core/tests/test_vm_threads.rs @@ -26,14 +26,14 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; -use bp3d_lua::{decl_closure, decl_lib_func}; use bp3d_lua::vm::closure::rc::{Rc, Shared}; use bp3d_lua::vm::core::UnSendRootVm; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::thread::core::{State, Yield}; use bp3d_lua::vm::thread::value::Thread; use bp3d_lua::vm::value::types::Function; +use bp3d_lua::{decl_closure, decl_lib_func}; +use std::cell::Cell; decl_closure! { fn increment |val: Rc>| () -> () { @@ -46,8 +46,10 @@ fn test_vm_threads_yield_lua() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() local value = coroutine.yield() @@ -55,12 +57,20 @@ fn test_vm_threads_yield_lua() { increment() end end) - ").unwrap(); + ", + ) + .unwrap(); let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); + assert_eq!( + thread.as_thread().resume::<()>(()).unwrap().state, + State::Suspended + ); assert_eq!(obj.get(), 1); - assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); + assert_eq!( + thread.as_thread().resume::<()>(42).unwrap().state, + State::Finished + ); assert_eq!(obj.get(), 2); // A finished thread will fail to resume. assert!(thread.as_thread().resume::<()>(()).is_err()); @@ -84,9 +94,17 @@ decl_lib_func! { fn test_vm_threads_yield_rust_fail() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); - vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); - let res = vm.run_code::<()>(c"my_yield()").unwrap_err().into_runtime().unwrap(); - assert_eq!(res.msg(), "[string \"my_yield()\"]:1: attempt to yield a non-thread stack object"); + vm.set_global(c"my_yield", RFunction::wrap(my_yield)) + .unwrap(); + let res = vm + .run_code::<()>(c"my_yield()") + .unwrap_err() + .into_runtime() + .unwrap(); + assert_eq!( + res.msg(), + "[string \"my_yield()\"]:1: attempt to yield a non-thread stack object" + ); } #[test] @@ -94,9 +112,12 @@ fn test_vm_threads_yield_rust() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.set_global(c"my_yield", RFunction::wrap(my_yield)).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.set_global(c"my_yield", RFunction::wrap(my_yield)) + .unwrap(); + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() local value = my_yield() @@ -104,12 +125,20 @@ fn test_vm_threads_yield_rust() { increment() end end) - ").unwrap(); + ", + ) + .unwrap(); let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); + assert_eq!( + thread.as_thread().resume::<()>(()).unwrap().state, + State::Suspended + ); assert_eq!(obj.get(), 1); - assert_eq!(thread.as_thread().resume::<()>(42).unwrap().state, State::Finished); + assert_eq!( + thread.as_thread().resume::<()>(42).unwrap().state, + State::Finished + ); assert_eq!(obj.get(), 2); // A finished thread will fail to resume. assert!(thread.as_thread().resume::<()>(()).is_err()); @@ -122,15 +151,19 @@ fn test_vm_threads_with_yield_value_lua() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() coroutine.yield(1) increment() return 42 end) - ").unwrap(); + ", + ) + .unwrap(); let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 1); @@ -143,16 +176,21 @@ fn test_vm_threads_with_yield_value_rust() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.set_global(c"my_yield", RFunction::wrap(my_yield2)).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.set_global(c"my_yield", RFunction::wrap(my_yield2)) + .unwrap(); + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() my_yield(5) increment() return 42 end) - ").unwrap(); + ", + ) + .unwrap(); let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); assert_eq!(thread.as_thread().resume::(()).unwrap().data, 5); @@ -165,8 +203,10 @@ fn test_vm_threads_with_yield_value_unsafe() { let vm = UnSendRootVm::new(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.run_code::<()>(c" + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() coroutine.yield(\"test\") @@ -176,14 +216,17 @@ fn test_vm_threads_with_yield_value_unsafe() { collectgarbage() return \"test3\" end) - ").unwrap(); + ", + ) + .unwrap(); let thread: Thread = vm.get_global(c"CO").unwrap(); assert_eq!(obj.get(), 0); let s: String = thread.as_thread().resume(()).unwrap().data; let s2: String = thread.as_thread().resume(()).unwrap().data; let s3: String = thread.as_thread().resume(()).unwrap().data; vm.run_code::<()>(c"CO = nil; collectgarbage()").unwrap(); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" CO = coroutine.create(function() increment() coroutine.yield(\"test\") @@ -193,7 +236,9 @@ fn test_vm_threads_with_yield_value_unsafe() { collectgarbage() return \"test3\" end) - ").unwrap(); + ", + ) + .unwrap(); assert_eq!(s, "test"); assert_eq!(s2, "test2"); assert_eq!(s3, "test3"); @@ -206,13 +251,21 @@ fn test_vm_threads_set_function() { let top = vm.top(); assert!(vm.as_thread().is_none()); let obj = Shared::new(Cell::new(0)); - vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))).unwrap(); - vm.run_code::<()>(c"function ThreadMain() increment() coroutine.yield() increment() end").unwrap(); + vm.set_global(c"increment", increment(Rc::from_rust(&vm, obj.clone()))) + .unwrap(); + vm.run_code::<()>(c"function ThreadMain() increment() coroutine.yield() increment() end") + .unwrap(); let main_fn: Function = vm.get_global(c"ThreadMain").unwrap(); let thread = Thread::new(&vm); thread.set_function(main_fn).unwrap(); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Suspended); - assert_eq!(thread.as_thread().resume::<()>(()).unwrap().state, State::Finished); + assert_eq!( + thread.as_thread().resume::<()>(()).unwrap().state, + State::Suspended + ); + assert_eq!( + thread.as_thread().resume::<()>(()).unwrap().state, + State::Finished + ); assert_eq!(obj.get(), 2); assert_eq!(vm.top() - top, 2) } diff --git a/core/tests/test_vm_userdata.rs b/core/tests/test_vm_userdata.rs index feaf68f..a603292 100644 --- a/core/tests/test_vm_userdata.rs +++ b/core/tests/test_vm_userdata.rs @@ -29,12 +29,12 @@ #![cfg(feature = "root-vm")] use bp3d_lua::ffi::lua::RawNumber; +use bp3d_lua::util::Namespace; use bp3d_lua::vm::function::types::RFunction; use bp3d_lua::vm::userdata::{AnyUserData, LuaDrop}; use bp3d_lua::vm::{RootVm, Vm}; use bp3d_lua::{decl_lib_func, decl_userdata, impl_userdata, impl_userdata_mut}; use std::sync::Mutex; -use bp3d_lua::util::Namespace; static MUTEX: Mutex<()> = Mutex::new(()); @@ -208,7 +208,10 @@ fn test_vm_userdata_error_handling() { let res = vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake); assert!(res.is_err()); let msg = res.unwrap_err().to_string(); - assert_eq!(msg, "userdata: __metatable is set for security reasons and cannot be altered"); + assert_eq!( + msg, + "userdata: __metatable is set for security reasons and cannot be altered" + ); assert_eq!(top, vm.top()); } @@ -255,7 +258,9 @@ fn test_vm_userdata_base2(vm: &Vm) { let top = vm.top(); { let mut namespace = Namespace::new(vm, "_G").unwrap(); - namespace.add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Snake).unwrap(); + namespace + .add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Snake) + .unwrap(); } assert_eq!(top, vm.top()); vm.run_code::<()>(c"a = MyInt.new(123)").unwrap(); @@ -290,7 +295,9 @@ fn test_vm_userdata_base3(vm: &Vm) { let top = vm.top(); { let mut namespace = Namespace::new(vm, "_G").unwrap(); - namespace.add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Camel).unwrap(); + namespace + .add_userdata::("MyInt", bp3d_lua::vm::userdata::case::Camel) + .unwrap(); } assert_eq!(top, vm.top()); vm.run_code::<()>(c"a = MyInt.new(123)").unwrap(); @@ -410,7 +417,8 @@ fn test_vm_userdata_call_method() { let _guard = MUTEX.lock(); let vm = RootVm::new(); let top = vm.top(); - vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake) + .unwrap(); vm.set_global("MY_INT", MyInt(123456)).unwrap(); let ud: AnyUserData = vm.get_global("MY_INT").unwrap(); let val: &str = ud.call_method("tostring", ()).unwrap(); @@ -425,7 +433,8 @@ fn test_vm_userdata_display() { let _guard = MUTEX.lock(); let vm = RootVm::new(); let top = vm.top(); - vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake).unwrap(); + vm.register_userdata::(bp3d_lua::vm::userdata::case::Snake) + .unwrap(); vm.set_global("MY_INT", MyInt(123456)).unwrap(); let ud: AnyUserData = vm.get_global("MY_INT").unwrap(); let s = ud.to_string(); @@ -439,10 +448,13 @@ fn test_vm_userdata_statics() { { let vm = RootVm::new(); test_vm_userdata_base2(&vm); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" MyInt.__gc = function() print(\"Lua has hacked Rust\") end MyInt.__metatable = function() print(\"Lua has hacked Rust\") end - ").unwrap(); + ", + ) + .unwrap(); let ud: AnyUserData = vm.get_global("a").unwrap(); let s = ud.to_string(); assert_eq!(s, "MyInt(123)"); @@ -457,11 +469,14 @@ fn test_vm_userdata_statics_2() { { let vm = RootVm::new(); test_vm_userdata_base2(&vm); - vm.run_code::<()>(c" + vm.run_code::<()>( + c" assert(a[0] == 123) assert(a[1] == nil) assert(b[0] == 456) - ").unwrap(); + ", + ) + .unwrap(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); @@ -512,8 +527,7 @@ fn test_vm_userdata_security9() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c"a:__gc()") - .unwrap(); + vm.run_code::<()>(c"a:__gc()").unwrap(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); @@ -525,8 +539,7 @@ fn test_vm_userdata_security10() { { let vm = RootVm::new(); test_vm_userdata_base(&vm); - vm.run_code::<()>(c"a.__gc(a)") - .unwrap(); + vm.run_code::<()>(c"a.__gc(a)").unwrap(); } assert_eq!(unsafe { DROP_COUNTER }, 6); assert_eq!(unsafe { LUA_DROP_COUNTER }, 6); diff --git a/shell/core/src/autocomplete.rs b/shell/core/src/autocomplete.rs index 616a91e..73ad3ac 100644 --- a/shell/core/src/autocomplete.rs +++ b/shell/core/src/autocomplete.rs @@ -31,26 +31,32 @@ use bp3d_lua::vm::value::any::Any; #[derive(Debug)] pub enum Mode { AddUpdate(Vec), - Delete(Vec) + Delete(Vec), } #[derive(Debug, Eq, PartialEq)] pub enum Type { Function, - Attribute + Attribute, } #[derive(Debug)] pub struct Item { pub name: String, - pub ty: Type + pub ty: Type, } impl Item { pub fn from_lua(name: &str, val: &Any) -> Self { match val { - Any::Function(_) => Item { name: name.into(), ty: Type::Function }, - _ => Item { name: name.into(), ty: Type::Attribute } + Any::Function(_) => Item { + name: name.into(), + ty: Type::Function, + }, + _ => Item { + name: name.into(), + ty: Type::Attribute, + }, } } } @@ -58,5 +64,5 @@ impl Item { #[derive(Debug)] pub struct Completions { pub path: String, - pub items: Vec + pub items: Vec, } diff --git a/shell/core/src/core.rs b/shell/core/src/core.rs index 0d75192..f45a0d0 100644 --- a/shell/core/src/core.rs +++ b/shell/core/src/core.rs @@ -28,14 +28,14 @@ const MAX_SIZE: usize = 4096; +use crate::lua::{Args, Lua}; use bp3d_debug::{debug, error, info}; -use bp3d_net::ipc::{Client, Server}; +use bp3d_lua_shell_proto::send; use bp3d_net::ipc::util::Message; +use bp3d_net::ipc::{Client, Server}; use bp3d_os::shell::{Event, SendChannel, Shell}; use bp3d_proto::message::FromBytes; -use crate::lua::{Args, Lua}; use bp3d_util::result::ResultExt; -use bp3d_lua_shell_proto::send; use tokio::sync::mpsc; async fn client_task(lua: &mut Lua, client: Client) -> bp3d_proto::message::Result { @@ -71,7 +71,9 @@ pub async fn run(args: Args, name: &str) { info!("starting lua VM"); let mut lua = Lua::new(args); info!("starting IPC server"); - let mut server = Server::create(name).await.expect_exit("Failed to create IPC server", 1); + let mut server = Server::create(name) + .await + .expect_exit("Failed to create IPC server", 1); while let Ok(client) = server.accept().await { debug!("client connected"); match client_task(&mut lua, client).await { diff --git a/shell/core/src/data.rs b/shell/core/src/data.rs index eea86e8..a0a1705 100644 --- a/shell/core/src/data.rs +++ b/shell/core/src/data.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use tokio::sync::mpsc; use crate::data_out::OutData; +use tokio::sync::mpsc; #[derive(Clone)] pub struct DataOut(mpsc::Sender>); diff --git a/shell/core/src/data_in.rs b/shell/core/src/data_in.rs index dadf683..e18106e 100644 --- a/shell/core/src/data_in.rs +++ b/shell/core/src/data_in.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Debug; -use bp3d_lua::vm::Vm; use crate::data::DataOut; use crate::lua::Args; +use bp3d_lua::vm::Vm; +use std::fmt::Debug; pub trait InData: Send + Debug { fn handle(&mut self, args: &Args, vm: &Vm, out: &DataOut) -> bool; @@ -53,7 +53,7 @@ pub struct RunFile { impl<'a> NetInData for bp3d_lua_shell_proto::send::RunFile<'a> { fn to_in_data(self) -> Box { Box::new(RunFile { - path: self.path.into() + path: self.path.into(), }) } } @@ -62,7 +62,7 @@ impl<'a> NetInData for bp3d_lua_shell_proto::send::RunCode<'a> { fn to_in_data(self) -> Box { Box::new(RunCode { name: self.name.map(|v| v.into()), - code: self.code.into() + code: self.code.into(), }) } } diff --git a/shell/core/src/data_out.rs b/shell/core/src/data_out.rs index f56930c..90a2b87 100644 --- a/shell/core/src/data_out.rs +++ b/shell/core/src/data_out.rs @@ -26,16 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fmt::Debug; +use crate::autocomplete::{Mode, Type}; +use bp3d_lua_shell_proto::completion; +use bp3d_lua_shell_proto::recv; use bp3d_net::ipc::util::Message; use bp3d_proto::message::WriteSelf; -use bp3d_lua_shell_proto::recv; -use bp3d_lua_shell_proto::completion; -use crate::autocomplete::{Mode, Type}; +use std::fmt::Debug; pub trait OutData: Send + Debug { fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()>; - fn has_exited(&self) -> bool { false } + fn has_exited(&self) -> bool { + false + } } #[derive(Debug)] @@ -45,11 +47,14 @@ impl OutData for End { fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()> { recv::Main { hdr: recv::Header::new().set_type(recv::Type::End).to_ref(), - msg: recv::Message::End - }.write_self(msg) + msg: recv::Message::End, + } + .write_self(msg) } - fn has_exited(&self) -> bool { true } + fn has_exited(&self) -> bool { + true + } } #[derive(Debug)] @@ -59,8 +64,12 @@ impl OutData for Log { fn write(&self, msg: &mut Message) -> bp3d_proto::message::Result<()> { recv::Main { hdr: recv::Header::new().set_type(recv::Type::Log).to_ref(), - msg: recv::Log { source: self.0, msg: &self.1 } - }.write_self(msg) + msg: recv::Log { + source: self.0, + msg: &self.1, + }, + } + .write_self(msg) } } @@ -77,24 +86,27 @@ impl OutData for Autocomplete { for item in &completion.items { let ty = match item.ty { Type::Function => completion::Type::Function, - Type::Attribute => completion::Type::Attribute + Type::Attribute => completion::Type::Attribute, }; items2.write_item(&completion::Item { hdr: completion::Header::new().set_type(ty).to_ref(), - name: &item.name + name: &item.name, })?; } items.write_item(&completion::List { path: &completion.path, - items: items2.to_ref() + items: items2.to_ref(), })?; } recv::Main { - hdr: recv::Header::new().set_type(recv::Type::AutocompleteAddUpdate).to_ref(), + hdr: recv::Header::new() + .set_type(recv::Type::AutocompleteAddUpdate) + .to_ref(), msg: completion::AddUpdate { - items: items.to_ref() - } - }.write_self(msg)?; + items: items.to_ref(), + }, + } + .write_self(msg)?; } Mode::Delete(v) => { let mut items = completion::DeleteItems::new(Vec::::new()); @@ -102,11 +114,14 @@ impl OutData for Autocomplete { items.write_item(&completion::Path { path })?; } recv::Main { - hdr: recv::Header::new().set_type(recv::Type::AutocompleteDelete).to_ref(), + hdr: recv::Header::new() + .set_type(recv::Type::AutocompleteDelete) + .to_ref(), msg: completion::Delete { - items: items.to_ref() - } - }.write_self(msg)?; + items: items.to_ref(), + }, + } + .write_self(msg)?; } } Ok(()) diff --git a/shell/core/src/lib1/autocomplete_api.rs b/shell/core/src/lib1/autocomplete_api.rs index 0bac743..e16c0d1 100644 --- a/shell/core/src/lib1/autocomplete_api.rs +++ b/shell/core/src/lib1/autocomplete_api.rs @@ -26,24 +26,30 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::collections::HashSet; +use crate::autocomplete::{Completions, Item, Mode}; +use crate::data::DataOut; use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::rc::Rc; use bp3d_lua::vm::table::ImmutableTable; use bp3d_lua::vm::value::any::Any; -use crate::autocomplete::{Completions, Item, Mode}; -use crate::data::DataOut; +use std::collections::HashSet; fn get_capacity(val: &Any) -> usize { match val { Any::Function(_) => 0, Any::Table(v) => v.len(), Any::UserData(_) => 1, - _ => 0 + _ => 0, } } -fn list_table_completions(set: &mut HashSet, path: Vec, root: &mut Vec, mut value: ImmutableTable, metatables: bool) -> bp3d_lua::vm::Result<()> { +fn list_table_completions( + set: &mut HashSet, + path: Vec, + root: &mut Vec, + mut value: ImmutableTable, + metatables: bool, +) -> bp3d_lua::vm::Result<()> { if set.contains(&value.uid()) { return Ok(()); } @@ -58,14 +64,17 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu path.push(name.into()); root.push(Completions { path: path.join("."), - items: Vec::with_capacity(c) + items: Vec::with_capacity(c), }); list_completions(set, path, root, v, metatables)?; } else { - root.last_mut().unwrap().items.push(Item::from_lua(name, &v)); + root.last_mut() + .unwrap() + .items + .push(Item::from_lua(name, &v)); } } - _ => continue + _ => continue, } } if metatables { @@ -77,7 +86,13 @@ fn list_table_completions(set: &mut HashSet, path: Vec, root: &mu Ok(()) } -fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec, value: Any, metatables: bool) -> bp3d_lua::vm::Result<()> { +fn list_completions( + set: &mut HashSet, + path: Vec, + root: &mut Vec, + value: Any, + metatables: bool, +) -> bp3d_lua::vm::Result<()> { match value { Any::Table(v) => list_table_completions(set, path, root, v.into(), metatables), Any::UserData(v) => { @@ -87,7 +102,7 @@ fn list_completions(set: &mut HashSet, path: Vec, root: &mut Vec< } Ok(()) } - _ => Ok(()) + _ => Ok(()), } } diff --git a/shell/core/src/lib1/mod.rs b/shell/core/src/lib1/mod.rs index f07a54a..3c725a6 100644 --- a/shell/core/src/lib1/mod.rs +++ b/shell/core/src/lib1/mod.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; +use crate::data::DataOut; +use crate::lib1::autocomplete_api::{build_completions, delete_completions}; +use crate::lib1::scheduler_api::{schedule_in, schedule_periodically}; +use crate::scheduler::SchedulerPtr; use bp3d_debug::info; use bp3d_lua::decl_closure; use bp3d_lua::libs::Lib; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::rc::{Rc, Shared}; -use crate::data::DataOut; -use crate::lib1::autocomplete_api::{build_completions, delete_completions}; -use crate::lib1::scheduler_api::{schedule_in, schedule_periodically}; -use crate::scheduler::SchedulerPtr; +use std::cell::Cell; mod autocomplete_api; mod scheduler_api; @@ -50,15 +50,19 @@ decl_closure! { pub struct Shell { log_ch: Shared, scheduler: Shared, - running: Shared> + running: Shared>, } impl Shell { - pub fn new(log_ch: DataOut, scheduler: Shared, running: Shared>) -> Shell { + pub fn new( + log_ch: DataOut, + scheduler: Shared, + running: Shared>, + ) -> Shell { Self { log_ch: log_ch.into(), scheduler, - running + running, } } } @@ -74,11 +78,11 @@ impl Lib for Shell { let running = Rc::from_rust(namespace.vm(), self.running.clone()); namespace.add([ ("buildCompletions", build_completions(rc)), - ("deleteCompletions", delete_completions(rc1)) + ("deleteCompletions", delete_completions(rc1)), ])?; namespace.add([ ("scheduleIn", schedule_in(r1)), - ("schedulePeriodically", schedule_periodically(r2)) + ("schedulePeriodically", schedule_periodically(r2)), ])?; namespace.add([("requestExit", request_exit(running))]) } diff --git a/shell/core/src/lib1/scheduler_api.rs b/shell/core/src/lib1/scheduler_api.rs index 7f194b9..0d430d6 100644 --- a/shell/core/src/lib1/scheduler_api.rs +++ b/shell/core/src/lib1/scheduler_api.rs @@ -26,10 +26,10 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::scheduler::SchedulerPtr; use bp3d_lua::decl_closure; use bp3d_lua::vm::closure::rc::Rc; use bp3d_lua::vm::thread::value::Thread; -use crate::scheduler::SchedulerPtr; decl_closure! { pub fn schedule_in |scheduler: Rc| (thread: Thread, after_ms: u32) -> () { diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 7c2703a..9f087bd 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -26,25 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::Cell; -use std::path::PathBuf; -use std::rc::Rc; -use tokio::sync::mpsc; -use std::thread::JoinHandle; -use std::time::Duration; -use bp3d_lua::vm::core::interrupt::{spawn_interruptible, Signal}; -use bp3d_lua::libs; -use bp3d_lua::libs::Lib; -use bp3d_debug::{debug, error, info, trace, warning}; -use bp3d_lua::vm::core::jit::JitOptions; -use bp3d_lua::vm::core::load::{Code, Script}; -use bp3d_lua::vm::value::any::Any; -use bp3d_lua::vm::Vm; use crate::data::DataOut; use crate::data_in::{Exit, InData, NetInData, RunCode, RunFile}; use crate::data_out::{End, Log, OutData}; use crate::lib1::Shell; use crate::scheduler::SchedulerPtr; +use bp3d_debug::{debug, error, info, trace, warning}; +use bp3d_lua::libs; +use bp3d_lua::libs::Lib; +use bp3d_lua::vm::Vm; +use bp3d_lua::vm::core::interrupt::{Signal, spawn_interruptible}; +use bp3d_lua::vm::core::jit::JitOptions; +use bp3d_lua::vm::core::load::{Code, Script}; +use bp3d_lua::vm::value::any::Any; +use std::cell::Cell; +use std::path::PathBuf; +use std::rc::Rc; +use std::thread::JoinHandle; +use std::time::Duration; +use tokio::sync::mpsc; const CHANNEL_BUFFER: usize = 32; @@ -52,7 +52,7 @@ pub struct Args { pub data: PathBuf, pub lua: PathBuf, pub modules: Vec, - pub main_script: Option + pub main_script: Option, } pub struct Lua { @@ -82,7 +82,7 @@ impl Lua { let res = self.signal.send(Duration::from_secs(10)); match res { Ok(_) => self.handle.join().unwrap(), - Err(e) => error!("Error attempting to terminate VM thread: {}", e) + Err(e) => error!("Error attempting to terminate VM thread: {}", e), } } @@ -95,7 +95,7 @@ impl Lua { Ok(v) => { logger.send(Log("output", v.to_string())); false - }, + } Err(e) => { if e.is_uncatchable() { logger.send(Log("kill", e.to_string())); @@ -127,11 +127,17 @@ impl Lua { if let Err(e) = libs::util::Util.register(vm) { error!("Failed to load util library: {}", e); } - if let Err(e) = libs::lua::Lua::new().load_chroot_path(&args.data).build().register(vm) { + if let Err(e) = libs::lua::Lua::new() + .load_chroot_path(&args.data) + .build() + .register(vm) + { error!("Failed to load base library: {}", e); } info!("Loading bp3d-lua-shell library..."); - if let Err(e) = Shell::new(logger.clone(), scheduler.clone(), running.clone()).register(vm) { + if let Err(e) = + Shell::new(logger.clone(), scheduler.clone(), running.clone()).register(vm) + { error!("Failed to load shell library: {}", e); } let mut modules = libs::lua::Module::new(&[]); @@ -150,15 +156,23 @@ impl Lua { info!("JIT: OFF") } if let Some(main_script) = &args.main_script { - vm.scope(|vm| Ok(RunFile { path: main_script.clone() }.handle(&args, vm, &logger))).unwrap(); + vm.scope(|vm| { + Ok(RunFile { + path: main_script.clone(), + } + .handle(&args, vm, &logger)) + }) + .unwrap(); } while running.get() { // First handle IPC events while let Some(command) = receiver.try_recv().ok() { // Nice type-inference breakage with this box. trace!("received command: {:?}", command); - let ret = vm.scope(|vm| Ok((command as Box).handle(&args, vm, &logger))).unwrap(); - trace!({ret}, "command handled"); + let ret = vm + .scope(|vm| Ok((command as Box).handle(&args, vm, &logger))) + .unwrap(); + trace!({ ret }, "command handled"); if ret { running.set(false); break; @@ -175,7 +189,7 @@ impl Lua { signal, handle, exec_queue, - out_queue + out_queue, } } } @@ -184,7 +198,7 @@ impl InData for RunCode { fn handle(&mut self, _: &Args, vm: &Vm, out: &DataOut) -> bool { match &self.name { Some(name) => Lua::handle_value(vm.run(Code::new(name, self.code.as_bytes())), out), - None => Lua::handle_value(vm.run_code(&*self.code), out) + None => Lua::handle_value(vm.run_code(&*self.code), out), } } } diff --git a/shell/core/src/main.rs b/shell/core/src/main.rs index e2f87b2..c69bcfd 100644 --- a/shell/core/src/main.rs +++ b/shell/core/src/main.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::PathBuf; use bp3d_debug::trace; use clap::Parser; +use std::path::PathBuf; -mod lua; +mod autocomplete; mod core; -mod data_out; -mod data_in; mod data; -mod scheduler; +mod data_in; +mod data_out; mod lib1; -mod autocomplete; +mod lua; +mod scheduler; #[derive(Parser, Debug)] #[command(version, about, long_about = None)] @@ -55,7 +55,7 @@ struct Cli { pub interactive: bool, #[arg(help = "Path to main script to start at Vm startup in the root directory.")] - pub main_script: Option + pub main_script: Option, } #[tokio::main] @@ -76,7 +76,11 @@ async fn main() { if args.interactive { core::run_interactive(largs).await; } else { - core::run(largs, args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell")).await; + core::run( + largs, + args.name.as_ref().map(|v| &**v).unwrap_or("bp3d-lua-shell"), + ) + .await; } trace!("Application end"); } diff --git a/shell/core/src/scheduler.rs b/shell/core/src/scheduler.rs index 4b16669..757f40b 100644 --- a/shell/core/src/scheduler.rs +++ b/shell/core/src/scheduler.rs @@ -26,22 +26,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::cell::RefCell; -use std::cmp::Ordering; -use std::collections::BinaryHeap; +use crate::data::DataOut; +use crate::data_out::Log; use bp3d_debug::{error, warning}; -use bp3d_os::time::Instant; use bp3d_lua::util::LuaThread; +use bp3d_lua::vm::Vm; use bp3d_lua::vm::thread::core::State; use bp3d_lua::vm::thread::value::Thread; -use bp3d_lua::vm::Vm; -use crate::data::DataOut; -use crate::data_out::Log; +use bp3d_os::time::Instant; +use std::cell::RefCell; +use std::cmp::Ordering; +use std::collections::BinaryHeap; struct Task { at_ms: u64, period_ms: Option, - thread: LuaThread + thread: LuaThread, } impl Eq for Task {} @@ -66,7 +66,7 @@ impl PartialOrd for Task { struct Scheduler { main: BinaryHeap, - instant: Instant + instant: Instant, } impl Scheduler { @@ -74,7 +74,7 @@ impl Scheduler { let task = Task { at_ms: self.instant.elapsed().as_millis() as u64 + after_ms as u64, period_ms: None, - thread: LuaThread::create(value) + thread: LuaThread::create(value), }; self.main.push(task); } @@ -83,7 +83,7 @@ impl Scheduler { let task = Task { at_ms: self.instant.elapsed().as_millis() as u64 + period_ms as u64, period_ms: Some(period_ms), - thread: LuaThread::create(value) + thread: LuaThread::create(value), }; self.main.push(task); } @@ -96,7 +96,11 @@ impl Scheduler { let out = match task.thread.as_thread().resume::>(()) { Ok(v) => v, Err(e) => { - error!("{}: Failed to schedule lua thread: {:?}", task.thread.as_thread(), e); + error!( + "{}: Failed to schedule lua thread: {:?}", + task.thread.as_thread(), + e + ); logger.send(Log("scheduler", e.to_string())); task.thread.delete(vm); continue; @@ -111,7 +115,10 @@ impl Scheduler { self.main.push(task); continue; } else { - warning!("{}: Attempt to change period or time of terminated task.", task.thread.as_thread()); + warning!( + "{}: Attempt to change period or time of terminated task.", + task.thread.as_thread() + ); task.thread.delete(vm); continue; } @@ -121,7 +128,10 @@ impl Scheduler { task.at_ms = time + period_ms as u64; self.main.push(task); } else { - warning!("{}: Attempt to re-schedule terminated task.", task.thread.as_thread()); + warning!( + "{}: Attempt to re-schedule terminated task.", + task.thread.as_thread() + ); task.thread.delete(vm); } } @@ -136,7 +146,10 @@ pub struct SchedulerPtr(RefCell); impl SchedulerPtr { pub fn new() -> Self { - Self(RefCell::new(Scheduler { main: BinaryHeap::new(), instant: Instant::now() })) + Self(RefCell::new(Scheduler { + main: BinaryHeap::new(), + instant: Instant::now(), + })) } pub fn schedule_in(&self, value: Thread, after_ms: u32) { diff --git a/shell/proto/build.rs b/shell/proto/build.rs index 335b928..4f33577 100644 --- a/shell/proto/build.rs +++ b/shell/proto/build.rs @@ -26,8 +26,8 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::Path; use bp3d_protoc::api::generate_rust; +use std::path::Path; fn main() { generate_rust(Path::new("./protoc.toml")).unwrap(); From e6805fa9587bc109ccf0b99d2de502fbb089dbe5 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 15 Sep 2025 22:26:01 +0200 Subject: [PATCH 480/527] Added initial support for post close functions --- core/src/vm/core/destructor.rs | 15 +++++++++++++++ core/src/vm/core/root_vm/common.rs | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/core/src/vm/core/destructor.rs b/core/src/vm/core/destructor.rs index 7c9dab4..fcf5011 100644 --- a/core/src/vm/core/destructor.rs +++ b/core/src/vm/core/destructor.rs @@ -116,6 +116,7 @@ static DESTRUCTOR_POOL: Key>> = Key::new("__destructor_pool_ pub struct Pool { leaked: Vec>, + post_close: Vec>, is_send: bool, } @@ -123,6 +124,7 @@ impl Pool { pub fn new(is_send: bool) -> Self { Self { leaked: Vec::new(), + post_close: Vec::new(), is_send, } } @@ -192,6 +194,19 @@ impl Pool { })); ptr } + + pub fn attach_post_close(vm: &Vm, f: impl FnOnce() + Send + 'static) { + let ptr = unsafe { Self::_from_vm(vm) }; + unsafe { (*ptr.as_mut_ptr()).attach_mut_post_close(f) } + } + + pub fn attach_mut_post_close(&mut self, f: impl FnOnce() + Send + 'static) { + self.post_close.push(Box::new(f)); + } + + pub fn extract_post_close(vm: &mut Vm) -> Vec> { + std::mem::replace(&mut Self::from_vm(vm).post_close, Vec::new()) + } } impl Drop for Pool { diff --git a/core/src/vm/core/root_vm/common.rs b/core/src/vm/core/root_vm/common.rs index 7cb41b7..d66933c 100644 --- a/core/src/vm/core/root_vm/common.rs +++ b/core/src/vm/core/root_vm/common.rs @@ -62,6 +62,7 @@ impl UnsafeRootVm { impl Drop for UnsafeRootVm { fn drop(&mut self) { debug!("Deleting destructor pool"); + let funcs = Pool::extract_post_close(&mut self.0); unsafe { drop(Box::from_raw(Pool::from_vm(&mut self.0))); } @@ -72,5 +73,9 @@ impl Drop for UnsafeRootVm { } #[cfg(not(feature = "send"))] HAS_VM.set(false); + debug!("Running post VM hooks..."); + for func in funcs { + func() + } } } From a4b5e4431cc4aa2a8d2dd47b99072be7f59346c6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 16 Sep 2025 07:12:28 +0200 Subject: [PATCH 481/527] Added new utility functions to bp3d.util.num --- core/src/libs/util/num.rs | 39 +++++++++++++++++++++++++++++++++++++++ definitions/bp3d.util.lua | 37 +++++++++++++++++++++++++++++++++++++ 2 files changed, 76 insertions(+) diff --git a/core/src/libs/util/num.rs b/core/src/libs/util/num.rs index 8298d89..aefad79 100644 --- a/core/src/libs/util/num.rs +++ b/core/src/libs/util/num.rs @@ -27,12 +27,46 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::decl_lib_func; +use crate::ffi::lua::RawNumber; use crate::libs::Lib; use crate::util::Namespace; use crate::vm::function::types::RFunction; use crate::vm::value::any::Any; use crate::vm::value::types::{Int53, UInt53}; +decl_lib_func! { + fn eq(a: RawNumber, b: RawNumber, epsilon: RawNumber) -> bool { + (a - b).abs() <= epsilon + } +} + +decl_lib_func! { + fn parsenumber(value: &str) -> (Option, Option) { + match value.parse() { + Ok(n) => (Some(n), None), + Err(e) => (None, Some(e.to_string())) + } + } +} + +decl_lib_func! { + fn parseint64(value: &str) -> (Option, Option) { + match value.parse() { + Ok(n) => (Some(n), None), + Err(e) => (None, Some(e.to_string())) + } + } +} + +decl_lib_func! { + fn parseuint64(value: &str) -> (Option, Option) { + match value.parse() { + Ok(n) => (Some(n), None), + Err(e) => (None, Some(e.to_string())) + } + } +} + decl_lib_func! { fn toistring(val: Any) -> crate::vm::Result { let val = val.to_integer()?; @@ -57,9 +91,14 @@ impl Lib for Num { namespace.add([("INT53_MAX", Int53::MAX), ("INT53_MIN", Int53::MIN)])?; namespace.add([("UINT64_MAX", u64::MAX), ("UINT64_MIN", u64::MIN)])?; namespace.add([("INT64_MAX", i64::MAX), ("INT64_MIN", i64::MIN)])?; + namespace.add([("NAN", f64::NAN), ("EPSILON", f64::EPSILON)])?; namespace.add([ ("toistring", RFunction::wrap(toistring)), ("toustring", RFunction::wrap(toustring)), + ("eq", RFunction::wrap(eq)), + ("parsenumber", RFunction::wrap(parsenumber)), + ("parseint64", RFunction::wrap(parseint64)), + ("parseuint64", RFunction::wrap(parseuint64)) ]) } } diff --git a/definitions/bp3d.util.lua b/definitions/bp3d.util.lua index 67e0d0c..7f31291 100644 --- a/definitions/bp3d.util.lua +++ b/definitions/bp3d.util.lua @@ -222,6 +222,8 @@ bp3d.util.utf8.sub = function(src, start, end1) end --- @field INT64_MAX number 2^63-1; this is actually not a number but a special LuaJIT cdata type --- @field UINT64_MIN number 0; this is actually not a number but a special LuaJIT cdata type --- @field UINT64_MAX number 2^64-1; this is actually not a number but a special LuaJIT cdata type +--- @field NAN number nan +--- @field EPSILON number represents the maximum possible precision of a Lua floating-point --- Converts the input value to an integer string. --- @@ -234,3 +236,38 @@ bp3d.util.num.toistring = function(value) end --- @param value any --- @return string bp3d.util.num.toustring = function(value) end + +--- Tests equality between two floating-point numbers. +--- +--- @param a number first operand +--- @param b number second operand +--- @param epsilon number maximum difference between a and b +--- @return boolean +bp3d.util.num.eq = function(a, b, epsilon) end + +--- Parse a Lua floating-point number from the input string. +--- +--- If the number was successfully parsed, returns the number and nil, +--- otherwise returns nil and a string error message. +--- +--- @param str string +--- @return [number?, string?] +bp3d.util.num.parsenumber = function(str) end + +--- Parse a LuaJIT true 64 bits integer from the input string. +--- +--- If the number was successfully parsed, returns the integer and nil, +--- otherwise returns nil and a string error message. +--- +--- @param str string +--- @return [number?, string?] +bp3d.util.num.parseint64 = function(str) end + +--- Parse a LuaJIT true 64 bits unsigned integer from the input string. +--- +--- If the number was successfully parsed, returns the integer and nil, +--- otherwise returns nil and a string error message. +--- +--- @param str string +--- @return [number?, string?] +bp3d.util.num.parseuint64 = function(str) end From 98958a756d14097fb9f1d9ab3c787a648e6f9552 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 16 Sep 2025 07:37:54 +0200 Subject: [PATCH 482/527] Fixed bug where bp3d-lua is constantly re-built under windows --- core/build.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.rs b/core/build.rs index e32df9d..3e39500 100644 --- a/core/build.rs +++ b/core/build.rs @@ -81,7 +81,7 @@ fn run_build(build_dir: &Path) -> std::io::Result { fn main() { // Rerun this script if any of the patch files changed. println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-changed=patch"); + println!("cargo:rerun-if-changed=../patch"); // Create build directory. let out = std::env::var_os("OUT_DIR").expect("Failed to acquire output directory"); From 3f834c1c3556ed161eda2c6bef4afb9a7943f439 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 16 Sep 2025 07:47:57 +0200 Subject: [PATCH 483/527] Fixed output of safety test --- core/tests/safety/test_rclosure_not_send.stderr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/tests/safety/test_rclosure_not_send.stderr b/core/tests/safety/test_rclosure_not_send.stderr index 894cba8..bdde115 100644 --- a/core/tests/safety/test_rclosure_not_send.stderr +++ b/core/tests/safety/test_rclosure_not_send.stderr @@ -30,5 +30,5 @@ note: required by a bound in `RClosure::>::from_rust` | pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self | --------- required by a bound in this associated function ... - | F: Send + | F: Send, | ^^^^ required by this bound in `RClosure::>::from_rust` From 0bfe567f3197fccecbb708a55cdfbd6198246192 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 16 Sep 2025 05:54:29 +0000 Subject: [PATCH 484/527] Format Rust code using rustfmt --- core/src/libs/util/num.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/libs/util/num.rs b/core/src/libs/util/num.rs index aefad79..45a8223 100644 --- a/core/src/libs/util/num.rs +++ b/core/src/libs/util/num.rs @@ -98,7 +98,7 @@ impl Lib for Num { ("eq", RFunction::wrap(eq)), ("parsenumber", RFunction::wrap(parsenumber)), ("parseint64", RFunction::wrap(parseint64)), - ("parseuint64", RFunction::wrap(parseuint64)) + ("parseuint64", RFunction::wrap(parseuint64)), ]) } } From 0683adb742a4b011c9ec426e9f493f4184730195 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 19 Oct 2025 21:26:42 +0200 Subject: [PATCH 485/527] Updated to bp3d-os 2.1.0 to fix most safety issues arround modules --- core/Cargo.toml | 2 +- core/src/libs/lua/module.rs | 73 ++++++++++++++++--------------------- core/src/util/module.rs | 62 ++++++++++++++++++------------- core/tests/test_vm_libs.rs | 4 +- definitions/bp3d.lua.lua | 9 +---- shell/core/Cargo.toml | 2 +- shell/core/src/lua.rs | 13 +++++-- 7 files changed, 82 insertions(+), 83 deletions(-) diff --git a/core/Cargo.toml b/core/Cargo.toml index 08ce738..f3906a1 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -16,7 +16,7 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } bp3d-debug = "1.0.0" -bp3d-os = { version = "1.0.1", features = [], optional = true } +bp3d-os = { version = "2.1.0", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index e6cce45..0322e4b 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -27,62 +27,51 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use crate::libs::Lib; -use crate::util::module::ModuleManager; +use crate::util::module::{Error, ModuleManager}; use crate::util::module::Result; use crate::util::Namespace; -use crate::vm::core::debug::DebugRegistry; use crate::vm::Vm; -use crate::{decl_userdata, impl_userdata_mut}; -use bp3d_os::module::library::types::VirtualLibrary; -use std::path::PathBuf; +use crate::decl_lib_func; +use crate::vm::core::destructor::Pool; +use crate::vm::function::types::RFunction; +use crate::vm::registry::named::Key; +use crate::vm::registry::types::LuaRef; +use crate::vm::value::types::RawPtr; -pub struct Module { - builtins: &'static [&'static VirtualLibrary], - search_paths: Vec, -} - -impl Module { - pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { - Self { - builtins, - search_paths: Vec::new(), - } - } +static KEY: Key>> = Key::new("__module_manager__"); - pub fn add_search_path(&mut self, path: PathBuf) -> &mut Self { - self.search_paths.push(path); - self +fn register_module_manager(vm: &Vm) { + let value = KEY.push(vm); + if value.is_some() { + panic!("A ModuleManager is already registered in the current Vm"); } + let manager = ModuleManager::new(); + let value = Pool::attach_send(vm, Box::new(manager)); + let value = crate::vm::registry::lua_ref::LuaRef::new(vm, RawPtr::new(value)); + KEY.set(value); } -decl_userdata!(struct ModuleManagerWrapper(ModuleManager)); - -impl_userdata_mut! { - impl ModuleManagerWrapper { - fn load(this: &mut ModuleManagerWrapper, vm: &Vm, lib: &str, plugin: &str) -> Result<()> { - this.0.load(lib, plugin, vm) +decl_lib_func! { + fn load_module(vm: &Vm, lib: &str, plugin: &str) -> Result<()> { + let manager = KEY.push(vm); + if let Some(manager) = manager { + unsafe { (&mut *manager.get().as_mut_ptr()).load(lib, plugin, vm) }?; + Ok(()) + } else { + Err(Error::NotRegistered) } } } -//TODO: Re-write as a global to avoid safety bug where multiple instances of the module manager -// exists for the application. -//TODO: Put libs as global to allow threads and maybe future unload. +pub struct Module; impl Lib for Module { - const NAMESPACE: &'static str = ""; - - fn load(&self, _: &mut Namespace) -> crate::vm::Result<()> { - unreachable!() - } + const NAMESPACE: &'static str = "bp3d.lua.module"; - fn register(&self, vm: &Vm) -> crate::vm::Result<()> { - DebugRegistry::add::(vm); - vm.register_userdata::(crate::vm::userdata::case::Camel)?; - let mut manager = ModuleManager::new(self.builtins); - for search_path in &self.search_paths { - manager.add_search_path(search_path.clone()); - } - vm.set_global(c"MODULES", ModuleManagerWrapper(manager)) + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + register_module_manager(namespace.vm()); + namespace.add([ + ("load", RFunction::wrap(load_module)) + ]) } } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index bedbc37..71576f3 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -26,24 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use bp3d_os::module::loader::ModuleHandle; use crate::module::error::ErrorType; use crate::module::{TIME_VERSION, VERSION}; use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; use crate::vm::Vm; -use bp3d_debug::info; -use bp3d_os::module::library::types::VirtualLibrary; +use bp3d_debug::{error, info}; use bp3d_os::module::library::Library; -use bp3d_os::module::{Module, ModuleLoader}; +use bp3d_os::module::Module; use bp3d_util::simple_error; use std::collections::HashSet; use std::ffi::CStr; -use std::path::PathBuf; +use bp3d_os::module::loader::ModuleLoader; simple_error! { pub Error { PluginAlreadyLoaded(String) => "plugin {} is already loaded", LibNotFound(String) => "library not found: {}", PluginNotFound(String) => "plugin not found: {}", + NotRegistered => "ModuleManager not registered", (impl From)Vm(crate::vm::error::Error) => "vm error: {}", (impl From)Module(bp3d_os::module::error::Error) => "module error: {}" } @@ -123,8 +124,7 @@ unsafe fn convert_module_error_to_vm_error( } pub struct ModuleManager { - set: HashSet, - loader: ModuleLoader, + set: HashSet } // This is safe because ModuleManager does not use thread locals or mutable globals of some kind. @@ -156,13 +156,14 @@ impl ModuleManager { if self.set.contains(&name) { return Err(Error::PluginAlreadyLoaded(name)); } - let module = unsafe { self.loader.load(lib) }?; + let mut lock = ModuleLoader::lock(); + let module = unsafe { lock.load(lib) }?; info!( "Loaded dynamic module {:?}-{:?}", - module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION") + module.get().get_metadata_key("NAME"), + module.get().get_metadata_key("VERSION") ); - Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; + Self::load_plugin(vm, module.get(), &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); Ok(()) @@ -173,7 +174,8 @@ impl ModuleManager { if self.set.contains(&name) { return Err(Error::PluginAlreadyLoaded(name)); } - let module = match unsafe { self.loader.load_builtin(lib) } { + let mut lock = ModuleLoader::lock(); + let module = match unsafe { lock.load_builtin(lib) } { Ok(v) => v, Err(e) => { return match e { @@ -184,10 +186,10 @@ impl ModuleManager { }; info!( "Loaded builtin module {:?}-{:?}", - module.get_metadata_key("NAME"), - module.get_metadata_key("VERSION") + module.get().get_metadata_key("NAME"), + module.get().get_metadata_key("VERSION") ); - Self::load_plugin(vm, module, &name, &lib.replace("-", "_"), plugin)?; + Self::load_plugin(vm, module.get(), &name, &lib.replace("-", "_"), plugin)?; info!("Loaded plugin {}", name); self.set.insert(name); Ok(true) @@ -200,26 +202,34 @@ impl ModuleManager { Ok(()) } - pub fn add_search_path(&mut self, name: PathBuf) { - self.loader.add_search_path(name) - } - - pub fn new(builtins: &'static [&'static VirtualLibrary]) -> Self { - let mut loader = ModuleLoader::new(builtins); + pub fn new() -> Self { + let mut lock = ModuleLoader::lock(); #[cfg(feature = "send")] - loader.add_public_dependency("bp3d-lua", VERSION, ["send"]); + lock.add_public_dependency("bp3d-lua", VERSION, ["send"]); #[cfg(not(feature = "send"))] - loader.add_public_dependency("bp3d-lua", VERSION, ["-send"]); - loader.add_public_dependency("time", TIME_VERSION, ["*"]); + lock.add_public_dependency("bp3d-lua", VERSION, ["-send"]); + lock.add_public_dependency("time", TIME_VERSION, ["*"]); Self { - set: Default::default(), - loader, + set: Default::default() } } } impl Default for ModuleManager { fn default() -> Self { - Self::new(&[]) + Self::new() + } +} + +impl Drop for ModuleManager { + fn drop(&mut self) { + for plugin in self.set.drain() { + let mut it = plugin.split("::"); + let lib = it.next().unwrap(); + let plugin = it.next().unwrap(); + if let Err(e) = ModuleLoader::lock().unload(lib) { + error!("Failed to unload plugin {}::{}: {}", lib, plugin, e); + } + } } } diff --git a/core/tests/test_vm_libs.rs b/core/tests/test_vm_libs.rs index 20e43e8..c8ce303 100644 --- a/core/tests/test_vm_libs.rs +++ b/core/tests/test_vm_libs.rs @@ -39,7 +39,7 @@ fn test_vm_lib_lua() { let vm = RootVm::new(); let top = vm.top(); Lua::new().build().register(&vm).unwrap(); - Module::new(&[]).register(&vm).unwrap(); + Module.register(&vm).unwrap(); vm.set_global("BP3D_LUA_CRATE_VERSION", VERSION).unwrap(); vm.run_code::<()>( c" @@ -75,7 +75,7 @@ fn test_vm_lib_lua() { ) .unwrap(); let err = vm - .run_code::<()>(c"MODULES:load('broken', 'broken2')") + .run_code::<()>(c"bp3d.lua.module.load('broken', 'broken2')") .unwrap_err() .into_runtime() .unwrap(); diff --git a/definitions/bp3d.lua.lua b/definitions/bp3d.lua.lua index 0e8f279..7559cde 100644 --- a/definitions/bp3d.lua.lua +++ b/definitions/bp3d.lua.lua @@ -28,10 +28,6 @@ --- @meta bp3d.lua ---- @class _G ---- @field MODULES ModuleManager Global instance of the module manager available to the current LuaJIT interpreter. -_G = {} - bp3d = {} --- @class bp3d.lua @@ -105,8 +101,7 @@ bp3d.lua.runFile = function(path) end --- @return any[] whatever outputs returned by the function which was executed. bp3d.lua.require = function(path) end ---- @class ModuleManager -ModuleManager = {} +bp3d.lua.module = {} --- Loads a native module into the current underlying LuaJIT interpreter. --- File extensions and other platform specific prefixes are pre-applied by the underlying search algorithm and must @@ -119,4 +114,4 @@ ModuleManager = {} --- --- @param lib string the name of the library to load. --- @param plugin string the name of the plugin in the native library to load. -function ModuleManager:load(lib, plugin) end +bp3d.lua.module.load = function(lib, plugin) end diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index 022dc13..de11c3a 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -13,4 +13,4 @@ bp3d-util = { version = "2.2.0", features = ["result"] } bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } bp3d-proto = "1.0.0-rc.5.0.1" clap = { version = "4.5.4", features = ["derive"] } -bp3d-os = { version = "1.0.0-rc.4.5.1", features = ["time", "shell"] } \ No newline at end of file +bp3d-os = { version = "2.1.0", features = ["time", "shell"] } diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 9f087bd..6fdd0cd 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -44,7 +44,9 @@ use std::path::PathBuf; use std::rc::Rc; use std::thread::JoinHandle; use std::time::Duration; +use bp3d_os::module::loader::ModuleLoader; use tokio::sync::mpsc; +use bp3d_lua::libs::lua::Module; const CHANNEL_BUFFER: usize = 32; @@ -140,11 +142,14 @@ impl Lua { { error!("Failed to load shell library: {}", e); } - let mut modules = libs::lua::Module::new(&[]); - for path in &args.modules { - modules.add_search_path(path.clone()); + { + let mut loader = ModuleLoader::lock(); + for path in &args.modules { + trace!("Adding search path: {:?}", path); + loader.add_search_path(path.clone()); + } } - if let Err(e) = modules.register(vm) { + if let Err(e) = Module.register(vm) { error!("Failed to load module manager: {}", e); } let jit = JitOptions::get(vm); From 618d3967d8d19fa38c7f40530686b80e0833aead Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 19 Oct 2025 21:26:54 +0200 Subject: [PATCH 486/527] Added luajit.patch --- patch/luajit.patch | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 patch/luajit.patch diff --git a/patch/luajit.patch b/patch/luajit.patch new file mode 100644 index 0000000..c5d883f --- /dev/null +++ b/patch/luajit.patch @@ -0,0 +1,32 @@ +diff --git a/src/luajit.c b/src/luajit.c +index 2e2e9150..c76fe839 100644 +--- a/src/luajit.c ++++ b/src/luajit.c +@@ -149,22 +149,13 @@ static void print_version(void) + fputs(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n", stdout); + } + ++#include "lj_jit.h" ++uint32_t lua_ext_getjitflags(lua_State *L); ++ + static void print_jit_status(lua_State *L) + { +- int n; +- const char *s; +- lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); +- lua_getfield(L, -1, "jit"); /* Get jit.* module table. */ +- lua_remove(L, -2); +- lua_getfield(L, -1, "status"); +- lua_remove(L, -2); +- n = lua_gettop(L); +- lua_call(L, 0, LUA_MULTRET); +- fputs(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF", stdout); +- for (n++; (s = lua_tostring(L, n)); n++) { +- putc(' ', stdout); +- fputs(s, stdout); +- } ++ uint32_t flags = lua_ext_getjitflags(L); ++ fputs(flags & JIT_F_ON ? "JIT: ON" : "JIT: OFF", stdout); + putc('\n', stdout); + lua_settop(L, 0); /* clear stack */ + } From ee24f9d3bbe429bb9f5937eba4bd0bf467c2da18 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Sun, 19 Oct 2025 19:37:41 +0000 Subject: [PATCH 487/527] Format Rust code using rustfmt --- core/src/libs/lua/module.rs | 10 ++++------ core/src/util/module.rs | 8 ++++---- shell/core/src/lua.rs | 4 ++-- 3 files changed, 10 insertions(+), 12 deletions(-) diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index 0322e4b..0a4f4f0 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -26,17 +26,17 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::decl_lib_func; use crate::libs::Lib; -use crate::util::module::{Error, ModuleManager}; use crate::util::module::Result; +use crate::util::module::{Error, ModuleManager}; use crate::util::Namespace; -use crate::vm::Vm; -use crate::decl_lib_func; use crate::vm::core::destructor::Pool; use crate::vm::function::types::RFunction; use crate::vm::registry::named::Key; use crate::vm::registry::types::LuaRef; use crate::vm::value::types::RawPtr; +use crate::vm::Vm; static KEY: Key>> = Key::new("__module_manager__"); @@ -70,8 +70,6 @@ impl Lib for Module { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { register_module_manager(namespace.vm()); - namespace.add([ - ("load", RFunction::wrap(load_module)) - ]) + namespace.add([("load", RFunction::wrap(load_module))]) } } diff --git a/core/src/util/module.rs b/core/src/util/module.rs index 71576f3..6e8da3c 100644 --- a/core/src/util/module.rs +++ b/core/src/util/module.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use bp3d_os::module::loader::ModuleHandle; use crate::module::error::ErrorType; use crate::module::{TIME_VERSION, VERSION}; use crate::vm::error::{RuntimeError, TypeError, Utf8Error}; use crate::vm::Vm; use bp3d_debug::{error, info}; use bp3d_os::module::library::Library; +use bp3d_os::module::loader::ModuleHandle; +use bp3d_os::module::loader::ModuleLoader; use bp3d_os::module::Module; use bp3d_util::simple_error; use std::collections::HashSet; use std::ffi::CStr; -use bp3d_os::module::loader::ModuleLoader; simple_error! { pub Error { @@ -124,7 +124,7 @@ unsafe fn convert_module_error_to_vm_error( } pub struct ModuleManager { - set: HashSet + set: HashSet, } // This is safe because ModuleManager does not use thread locals or mutable globals of some kind. @@ -210,7 +210,7 @@ impl ModuleManager { lock.add_public_dependency("bp3d-lua", VERSION, ["-send"]); lock.add_public_dependency("time", TIME_VERSION, ["*"]); Self { - set: Default::default() + set: Default::default(), } } } diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 6fdd0cd..c5ebc50 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -34,19 +34,19 @@ use crate::scheduler::SchedulerPtr; use bp3d_debug::{debug, error, info, trace, warning}; use bp3d_lua::libs; use bp3d_lua::libs::Lib; +use bp3d_lua::libs::lua::Module; use bp3d_lua::vm::Vm; use bp3d_lua::vm::core::interrupt::{Signal, spawn_interruptible}; use bp3d_lua::vm::core::jit::JitOptions; use bp3d_lua::vm::core::load::{Code, Script}; use bp3d_lua::vm::value::any::Any; +use bp3d_os::module::loader::ModuleLoader; use std::cell::Cell; use std::path::PathBuf; use std::rc::Rc; use std::thread::JoinHandle; use std::time::Duration; -use bp3d_os::module::loader::ModuleLoader; use tokio::sync::mpsc; -use bp3d_lua::libs::lua::Module; const CHANNEL_BUFFER: usize = 32; From 32c22c5c7f57a3b731caebb26898a2ac8e3de299 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 20 Oct 2025 19:33:58 +0200 Subject: [PATCH 488/527] Fixed possible use-after free when trying to unload modules --- core/src/libs/lua/module.rs | 9 +++++++-- core/src/vm/registry/lua_ref.rs | 10 +++++----- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/core/src/libs/lua/module.rs b/core/src/libs/lua/module.rs index 0a4f4f0..7dd6118 100644 --- a/core/src/libs/lua/module.rs +++ b/core/src/libs/lua/module.rs @@ -46,8 +46,13 @@ fn register_module_manager(vm: &Vm) { panic!("A ModuleManager is already registered in the current Vm"); } let manager = ModuleManager::new(); - let value = Pool::attach_send(vm, Box::new(manager)); - let value = crate::vm::registry::lua_ref::LuaRef::new(vm, RawPtr::new(value)); + let mut value = Box::new(manager); + let value2 = &mut *value as *mut ModuleManager; + Pool::attach_post_close(vm, || { + let value = value; + drop(value); + }); + let value = crate::vm::registry::lua_ref::LuaRef::new(vm, RawPtr::new(value2)); KEY.set(value); } diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 385129b..54764f0 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -39,13 +39,13 @@ use std::marker::PhantomData; /// # Notes /// /// * The definition of a simple type in bp3d-lua is a type which does not hold a reference to -/// the [Vm]. This is typically the case of primitives like strings, integers, numbers, etc. Types -/// such as tables or functions are called complex in bp3d-lua as they require constant interactions -/// with the Lua stack represented by a [Vm] in order to operate on them. +/// the [Vm]. This is typically the case of primitives like strings, integers, numbers, etc. +/// Types such as tables or functions are called complex in bp3d-lua as they require constant +/// interactions with the Lua stack represented by a [Vm] in order to operate on them. /// /// * For complex types, no wrapper is needed as they already have a reference to the attached [Vm] -/// in their value type. Instead, complex types which can be saved in the registry directly -/// implements the registry [Value](crate::vm::registry::Value) trait. +/// in their value type. Instead, complex types which can be saved in the registry directly +/// implements the registry [Value](crate::vm::registry::Value) trait. /// /// # Safety /// From f220a6d909b0b99f59ed3fe175939855a86c36e4 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 22 Oct 2025 21:58:28 +0200 Subject: [PATCH 489/527] Added support to access the path instead of only the full path --- core/src/libs/lua/require.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 1a93eb6..66d62b8 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -46,7 +46,7 @@ simple_error! { } pub trait Source: Send + Sync { - fn run(&self, vm: &Vm, path: &str) -> crate::vm::Result; + fn run(&self, vm: &Vm, path: &str, full_path: &str) -> crate::vm::Result; } #[derive(Default)] @@ -69,7 +69,7 @@ impl Provider { let src = guard .get(source) .ok_or_else(|| Error::UnknownSource(source.into()))?; - let ret = src.run(vm, path)?; + let ret = src.run(vm, &path[id + 1..], path)?; Ok(ret) } } From c2888559928c190a8b2d34d0f66bdae1ce6f8c2d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 28 Oct 2025 21:35:56 +0100 Subject: [PATCH 490/527] Fixed bug in definitions --- definitions/bp3d.util.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/definitions/bp3d.util.lua b/definitions/bp3d.util.lua index 7f31291..bf07e89 100644 --- a/definitions/bp3d.util.lua +++ b/definitions/bp3d.util.lua @@ -124,7 +124,7 @@ bp3d.util.utf8.contains = function(src, needle) end --- Note: This function wil error if any of the inputs are not UTF-8 encoded. --- --- @param src string input source string. ---- @param pattern integer separator string. +--- @param pattern string separator string. --- @return string[] bp3d.util.utf8.split = function(src, pattern) end @@ -133,7 +133,7 @@ bp3d.util.utf8.split = function(src, pattern) end --- Note: This function wil error if any of the inputs are not UTF-8 encoded. --- --- @param src string input source string. ---- @param pattern integer search string. +--- @param pattern string search string. --- @param replacement string replacement string bp3d.util.utf8.replace = function(src, pattern, replacement) end From f6d7e5a3d7648469fd88874ec9832f25a7ecd1a8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 29 Oct 2025 19:20:25 +0100 Subject: [PATCH 491/527] Fixed bug in FromParam derive --- codegen/src/gen/from_param.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index 4da774d..1a880a1 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -197,7 +197,7 @@ impl Parser for FromParam { impl #generics bp3d_lua::vm::function::FromParam<#lifetime> for #name #generics { unsafe fn from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Self { #(#from_params)* - bp3d_lua::ffi::laux::luaL_error(vm.as_ptr(), "Unable to find a type satisfying constraints"); + bp3d_lua::ffi::laux::luaL_error(vm.as_ptr(), c"Unable to find a type satisfying constraints".as_ptr()); std::hint::unreachable_unchecked() } From 7810f9978f8f6b6d96a1445416785ac1b52dc3a8 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 29 Oct 2025 19:23:07 +0100 Subject: [PATCH 492/527] Added table.unpack as a shortcut to the deprecated unpack method --- core/src/vm/core/root_vm/common.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/vm/core/root_vm/common.rs b/core/src/vm/core/root_vm/common.rs index d66933c..ebda285 100644 --- a/core/src/vm/core/root_vm/common.rs +++ b/core/src/vm/core/root_vm/common.rs @@ -55,6 +55,7 @@ impl UnsafeRootVm { let mut vm = UnsafeRootVm(unsafe { Vm::from_raw(l) }); handle_root_vm_init(); unsafe { Pool::new_in_vm(&mut vm.0, is_send) }; + vm.0.run_code::<()>(c"table.unpack = unpack").unwrap(); vm } } From eb376adfe9fd25d4cb9b9bbb54f84fb9742a4317 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 30 Oct 2025 20:57:00 +0100 Subject: [PATCH 493/527] Fixed generation bug in FromParam --- codegen/src/gen/from_param.rs | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index 1a880a1..b6ea5ff 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -54,7 +54,7 @@ impl FromParam { pub struct Field { name: Ident, from_param: TokenStream, - try_from_param: TokenStream, + try_from_param: TokenStream } impl Parser for FromParam { @@ -112,7 +112,8 @@ impl Parser for FromParam { EnumVariant::MultiField(_) => panic!("Multi-field enum variants are not supported"), EnumVariant::None(variant) => { let name = self.name.clone(); - let vname = variant.to_string(); + //FIXME: Use decapitalize + maybe allow configuration for which case to use + let vname = variant.to_string().to_lowercase(); Field { name: variant.clone(), from_param: quote! { @@ -193,15 +194,31 @@ impl Parser for FromParam { .unwrap_or(quote! { '_ }); let from_params = parsed.iter().map(|field| &field.from_param); let try_from_params = parsed.iter().map(|field| &field.try_from_param); + let start_from_param = if self.is_simple_enum { + quote! { + let enum_name = <&str as bp3d_lua::vm::function::FromParam>::from_param(vm, index); + } + } else { + quote! {} + }; + let start_try_from_param = if self.is_simple_enum { + quote! { + let enum_name = <&str as bp3d_lua::vm::function::FromParam>::try_from_param(vm, index)?; + } + } else { + quote! {} + }; quote! { impl #generics bp3d_lua::vm::function::FromParam<#lifetime> for #name #generics { unsafe fn from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Self { + #start_from_param #(#from_params)* bp3d_lua::ffi::laux::luaL_error(vm.as_ptr(), c"Unable to find a type satisfying constraints".as_ptr()); std::hint::unreachable_unchecked() } fn try_from_param(vm: &#lifetime bp3d_lua::vm::Vm, index: i32) -> Option { + #start_try_from_param #(#try_from_params)* None } From 58a0c220202988e0cfe4ee1d1d21540fcb3c22a6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 30 Oct 2025 21:11:41 +0100 Subject: [PATCH 494/527] Fixed deadlock in require --- core/src/libs/lua/require.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/libs/lua/require.rs b/core/src/libs/lua/require.rs index 66d62b8..817eb8c 100644 --- a/core/src/libs/lua/require.rs +++ b/core/src/libs/lua/require.rs @@ -35,7 +35,7 @@ use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::Vm; use bp3d_util::simple_error; use std::collections::HashMap; -use std::sync::Mutex; +use std::sync::RwLock; simple_error! { pub Error { @@ -50,7 +50,7 @@ pub trait Source: Send + Sync { } #[derive(Default)] -pub struct Provider(Mutex>>); +pub struct Provider(RwLock>>); impl Provider { pub fn new() -> Self { @@ -58,14 +58,14 @@ impl Provider { } pub fn add_source(&self, name: String, source: S) { - let mut guard = self.0.lock().unwrap(); + let mut guard = self.0.write().unwrap(); guard.insert(name, Box::new(source)); } pub fn require(&self, vm: &Vm, path: &str) -> Result { let id = path.find('.').ok_or(Error::InvalidSyntax)?; let source = &path[..id]; - let guard = self.0.lock().unwrap(); + let guard = self.0.read().unwrap(); let src = guard .get(source) .ok_or_else(|| Error::UnknownSource(source.into()))?; From 1f20c4bf5058c1aa73c1adc472b1e2b7b0407a87 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 31 Oct 2025 23:52:53 +0100 Subject: [PATCH 495/527] Fixed big bug where lua stack is not cleaned if a scope fails --- core/src/vm/core/vm.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/core/vm.rs b/core/src/vm/core/vm.rs index 1fbc768..5442ef7 100644 --- a/core/src/vm/core/vm.rs +++ b/core/src/vm/core/vm.rs @@ -69,9 +69,9 @@ impl Vm { f: F, ) -> crate::vm::Result { let top = self.top(); - let r = f(self)?; + let r = f(self); unsafe { lua_settop(self.l, top) }; - Ok(r) + r } pub fn register_userdata(&self, case: impl NameConvert) -> crate::vm::Result<()> { From d1b70102d5714169d0bcd47989c1cd1d000c4fa3 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 2 Nov 2025 17:07:02 +0100 Subject: [PATCH 496/527] Fixed safety issue with registry LuaRef --- core/src/vm/registry/lua_ref.rs | 13 ++++++++++++- core/tests/test_vm_registry.rs | 6 ++++-- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/vm/registry/lua_ref.rs b/core/src/vm/registry/lua_ref.rs index 54764f0..dae56ec 100644 --- a/core/src/vm/registry/lua_ref.rs +++ b/core/src/vm/registry/lua_ref.rs @@ -114,8 +114,19 @@ impl<'a, T: SimpleValue<'a>> LuaRef<'a, T> { } } + /// Returns the registry value from the lua stack. #[inline(always)] - pub fn get(&self) -> T { + pub fn get<'b: 'a>(&'b self) -> T { + unsafe { SimpleValue::<'b>::from_lua(self.vm, self.index) } + } + + /// Returns the registry value from the lua stack. + /// + /// # Safety + /// + /// This function assumes the returned registry value won't be set while being borrowed. + #[inline(always)] + pub unsafe fn get_unchecked(&self) -> T { unsafe { T::from_lua(self.vm, self.index) } } diff --git a/core/tests/test_vm_registry.rs b/core/tests/test_vm_registry.rs index 29bfdd0..dd1b86a 100644 --- a/core/tests/test_vm_registry.rs +++ b/core/tests/test_vm_registry.rs @@ -84,7 +84,8 @@ fn test_vm_registry_string() { assert_eq!(vm.top(), top); // The string should have been popped from the stack like any normal // registry creation operation. { - let value = key.push(&vm).get(); + let motherfuckingrust = key.push(&vm); + let value = motherfuckingrust.get(); assert_eq!(value, "this is a test"); } // LuaRef automatically pops from the stack on drop, as simple values which are stored in the @@ -106,7 +107,8 @@ fn test_vm_registry_string_modify() { key.set(value); assert_eq!(vm.top(), top); { - let value = key.push(&vm).get(); + let motherfuckingrust = key.push(&vm); + let value = motherfuckingrust.get(); assert_eq!(value, "one more test"); } assert_eq!(vm.top(), top); From dab39551118a9401ea543d0cb3de75946f816ced Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 2 Nov 2025 20:53:24 +0100 Subject: [PATCH 497/527] Added support for lifetimes to userdata_func macros --- core/src/macro/userdata_func.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index 3d3b6f1..82197a1 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -86,7 +86,7 @@ macro_rules! decl_userdata_func { impl $obj_name { $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($this: &$obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: & $($lifetime)? $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; @@ -111,7 +111,7 @@ macro_rules! decl_userdata_func { impl $obj_name { $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($this: &$obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code + fn _func $(<$lifetime>)? ($this: & $($lifetime)? $obj_name$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code use $crate::vm::function::IntoParam; let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *const $obj_name; let vm = unsafe { $crate::vm::Vm::from_raw(l) }; From 8df15956d9fb673d2f9f266d28b57d282a8e4442 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Nov 2025 22:31:05 +0100 Subject: [PATCH 498/527] Added initial version of files library --- core/src/libs/files/chroot.rs | 205 +++++++++++++++++++++++++++++++ core/src/libs/files/interface.rs | 187 ++++++++++++++++++++++++++++ core/src/libs/files/lib.rs | 156 +++++++++++++++++++++++ core/src/libs/files/mod.rs | 58 +++++++++ core/src/libs/files/obj.rs | 85 +++++++++++++ core/src/libs/mod.rs | 3 + core/tests/test_vm_files.rs | 121 ++++++++++++++++++ 7 files changed, 815 insertions(+) create mode 100644 core/src/libs/files/chroot.rs create mode 100644 core/src/libs/files/interface.rs create mode 100644 core/src/libs/files/lib.rs create mode 100644 core/src/libs/files/mod.rs create mode 100644 core/src/libs/files/obj.rs create mode 100644 core/tests/test_vm_files.rs diff --git a/core/src/libs/files/chroot.rs b/core/src/libs/files/chroot.rs new file mode 100644 index 0000000..29c7437 --- /dev/null +++ b/core/src/libs/files/chroot.rs @@ -0,0 +1,205 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::borrow::Cow; +use std::collections::HashMap; +use std::ffi::OsStr; +use std::fmt::Display; +use std::ops::{BitAnd, BitOr}; +use std::path::Path; +use bp3d_debug::trace; +use crate::vm::core::destructor::Pool; +use crate::vm::registry::named::Key; +use crate::vm::registry::types::LuaRef; +use crate::vm::value::types::RawPtr; +use crate::vm::Vm; + +const CHROOT: Key> = Key::new("__chroot__"); + +pub fn with_chroot(vm: &Vm, f: impl FnOnce(&Path) -> R) -> R { + let s = CHROOT.push(vm); + match s { + None => f(Path::new("")), + Some(v) => { + let p = Path::new(unsafe { OsStr::from_encoded_bytes_unchecked(v.get()) }); + f(p) + } + } +} + +pub fn set_chroot(vm: &Vm, path: &Path) { + let mut bytes = path.as_os_str().as_encoded_bytes(); + if bytes[bytes.len() - 1] == b'/' { + bytes = &bytes[..bytes.len() - 1]; + } + let r = crate::vm::registry::lua_ref::LuaRef::new(vm, bytes); + CHROOT.set(r); +} + +#[derive(Debug)] +pub struct SandboxError; + +impl Display for SandboxError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "attempt to escape the sandbox") + } +} + +impl std::error::Error for SandboxError {} + +pub fn unsandbox<'a>(vm: &Vm, path: &'a str) -> Result, SandboxError> { + let mut level = 0; + for component in path.split('/') { + if component == ".." { + if level == 0 { + return Err(SandboxError); + } + level -= 2; + } else if component == "." || component == "" { + } else { + level += 1; + } + } + trace!({level}, "unsandbox {}", path); + if level < 0 { + return Err(SandboxError); + } + if path.len() > 0 && path.as_bytes()[0] == b'/' { + return Ok(Cow::Owned(with_chroot(vm, |root| root.join(&path[1..])))) + } + Ok(Cow::Borrowed(Path::new(path))) +} + +pub fn sandbox<'a>(vm: &Vm, path: &'a Path) -> Result, SandboxError> { + let pos = with_chroot(vm, |root| { + let src = path.as_os_str().as_encoded_bytes(); + let root = root.as_os_str().as_encoded_bytes(); + if !(src.len() >= root.len()) || !src.starts_with(root) { + return 0; + } + root.len() + }); + if pos == 0 { + return Err(SandboxError) + } + let mut src = &path.as_os_str().as_encoded_bytes()[pos..]; + if src.len() == 0 { + return Ok(Cow::Borrowed("/")); + } + if src[0] != b'/' { + src = &path.as_os_str().as_encoded_bytes()[pos - 1..]; + } + Ok(String::from_utf8_lossy(src)) +} + +#[derive(Copy, Clone, Eq, PartialEq, Debug)] +pub struct Permissions(u8); + +impl Permissions { + pub const R: Permissions = Permissions(0x1); + pub const W: Permissions = Permissions(0x2); + pub const X: Permissions = Permissions(0x4); + + pub const NONE: Permissions = Permissions(0x0); +} + +impl BitOr for Permissions { + type Output = Permissions; + + fn bitor(self, rhs: Self) -> Self::Output { + Permissions(self.0 | rhs.0) + } +} + +impl BitAnd for Permissions { + type Output = bool; + + fn bitand(self, rhs: Self) -> Self::Output { + self.0 & rhs.0 != 0 + } +} + +const PERMISSIONS: Key>> = Key::new("__permissions__"); + +#[derive(Default)] +struct PermissionManager { + permissions: HashMap, +} + +impl PermissionManager { + fn set_permissions(&mut self, path: &str, perms: Permissions) { + self.permissions.insert(path.into(), perms); + } + + fn get_permissions(&self, path: &str) -> Option { + self.permissions.get(path).cloned() + } + + fn set_permissions_vm(vm: &Vm, path: &str, permissions: Permissions) { + let mut value = PERMISSIONS.push(vm); + if value.is_none() { + let ptr = Pool::attach_send(vm, Box::new(PermissionManager::default())); + let r = crate::vm::registry::lua_ref::LuaRef::new(vm, RawPtr::new(ptr)); + PERMISSIONS.set(r); + value = PERMISSIONS.push(vm); + } + let value = value.unwrap(); + unsafe { (&mut *value.get().as_mut_ptr()).set_permissions(path, permissions) }; + } +} + +pub fn set_access(vm: &Vm, path: &str, perms: Permissions) { + if !path.starts_with("/") { + return; + } + PermissionManager::set_permissions_vm(vm, path, perms); +} + +pub fn access(vm: &Vm, path: &str) -> Permissions { + if !path.starts_with("/") { + return Permissions::NONE; + } + let perms = PERMISSIONS.push(vm); + if perms.is_none() { + return Permissions::NONE; + } + let perms = perms.unwrap().get(); + let perms = unsafe { &*perms.as_ptr() }; + let mut path = path; + while path.len() > 0 { + if let Some(perms) = perms.get_permissions(path) { + return perms; + } + let id = path.rfind('/'); + match id { + Some(pos) => path = &path[..pos], + None => break + } + } + perms.get_permissions("/").unwrap_or(Permissions::NONE) +} diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs new file mode 100644 index 0000000..de6af44 --- /dev/null +++ b/core/src/libs/files/interface.rs @@ -0,0 +1,187 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::borrow::Cow; +use std::path::{Path, PathBuf}; +use bp3d_debug::error; +use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; +use crate::libs::files::obj::PathWrapper; +use crate::util::core::SimpleDrop; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::util::LuaType; +use crate::vm::value::{FromLua, IntoLua}; +use crate::vm::Vm; + +#[derive(Debug, Eq, PartialEq)] +pub enum SandboxPath<'a> { + String(&'a str), + Path(&'a Path) +} + +#[derive(Debug, Eq, PartialEq)] +pub enum SandboxPathBuf { + String(String), + Path(PathBuf) +} + +impl From> for SandboxPathBuf { + fn from(value: SandboxPath<'_>) -> Self { + match value { + SandboxPath::String(v) => SandboxPathBuf::String(v.into()), + SandboxPath::Path(v) => SandboxPathBuf::Path(v.into()) + } + } +} + +impl SandboxPath<'_> { + /// Creates a new [SandboxPath] from an existing string. + pub fn from_str<'a>(vm: &Vm, path: &'a str) -> Result, SandboxError> { + unsandbox(vm, path)?; + Ok(SandboxPath::String(path)) + } + + /// Creates a new [SandboxPath] from an existing string. + /// + /// If the given string cannot be represented in the current sandbox configuration, a nil value + /// will be passed to Lua. + pub fn from_str_unchecked(path: &str) -> SandboxPath<'_> { + SandboxPath::String(path) + } + + /// Creates a new [SandboxPath] from an existing [Path]. + pub fn from_path<'a>(vm: &Vm, path: &'a Path) -> Result, SandboxError> { + sandbox(vm, path)?; + Ok(SandboxPath::Path(path)) + } + + /// Creates a new [SandboxPath] from an existing [Path]. + /// + /// This function allows passing paths from Rust to Lua which are outside the sandbox. + /// Use with caution. + pub fn from_path_unchecked(path: &Path) -> SandboxPath<'_> { + SandboxPath::Path(path) + } + + pub fn to_str(&self, vm: &Vm) -> Result, SandboxError> { + match self { + SandboxPath::String(v) => { + unsandbox(vm, v)?; + Ok(Cow::Borrowed(v)) + }, + SandboxPath::Path(v) => sandbox(vm, v) + } + } + + pub fn to_path(&self, vm: &Vm) -> Result, SandboxError> { + match self { + SandboxPath::String(v) => unsandbox(vm, *v), + SandboxPath::Path(v) => Ok(Cow::Borrowed(v)) + } + } + + pub fn access(&self, vm: &Vm) -> Permissions { + let path = match self.to_str(vm) { + Ok(v) => v, + Err(e) => { + error!("failed to read permissions for {:?}: {}", self, e); + return Permissions::NONE; + } + }; + access(vm, &path) + } +} + +impl<'a> FromLua<'a> for SandboxPath<'a> { + unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { + let wrapper: crate::vm::Result<&PathWrapper> = FromLua::from_lua(vm, index); + if let Ok(wrapper) = wrapper { + return SandboxPath::Path(wrapper.path()) + } + let s: &str = FromLua::from_lua_unchecked(vm, index); + SandboxPath::String(s) + } + + fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { + let wrapper: crate::vm::Result<&PathWrapper> = FromLua::from_lua(vm, index); + if let Ok(wrapper) = wrapper { + return Ok(SandboxPath::Path(wrapper.path())) + } + let s: &str = FromLua::from_lua(vm, index)?; + Ok(SandboxPath::String(s)) + } +} + +impl FromLua<'_> for SandboxPathBuf { + unsafe fn from_lua_unchecked(vm: &'_ Vm, index: i32) -> Self { + SandboxPath::from_lua_unchecked(vm, index).into() + } + + fn from_lua(vm: &'_ Vm, index: i32) -> crate::vm::Result { + SandboxPath::from_lua(vm, index).map(SandboxPathBuf::from) + } +} + +unsafe impl IntoLua for SandboxPath<'_> { + fn into_lua(self, vm: &Vm) -> u16 { + match self { + SandboxPath::String(v) => unsandbox(vm, v).map(|path| PathWrapper::new(path.into())).ok().into_lua(vm), + SandboxPath::Path(v) => PathWrapper::new(v.into()).into_lua(vm) + } + } +} + +unsafe impl SimpleDrop for SandboxPath<'_> {} + +impl LuaType for SandboxPath<'_> {} + +impl<'a> FromParam<'a> for SandboxPath<'a> { + unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { + let wrapper: Option<&PathWrapper> = FromParam::try_from_param(vm, index); + if let Some(wrapper) = wrapper { + return SandboxPath::Path(wrapper.path()) + } + let s: &str = FromParam::from_param(vm, index); + SandboxPath::String(s) + } + + fn try_from_param(vm: &'a Vm, index: i32) -> Option { + let wrapper: Option<&PathWrapper> = FromParam::try_from_param(vm, index); + if let Some(wrapper) = wrapper { + return Some(SandboxPath::Path(wrapper.path())) + } + let wrapper: Option<&str> = FromParam::try_from_param(vm, index); + wrapper.map(|v| SandboxPath::String(v)) + } +} + +unsafe impl IntoParam for SandboxPath<'_> { + #[inline(always)] + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self, vm) as _ + } +} diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs new file mode 100644 index 0000000..d709058 --- /dev/null +++ b/core/src/libs/files/lib.rs @@ -0,0 +1,156 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use bp3d_util::simple_error; +use crate::decl_lib_func; +use crate::libs::files::chroot::Permissions; +use crate::libs::files::SandboxPath; +use crate::vm::table::Table; + +simple_error! { + Error { + Io(std::io::Error) => "io error: {}", + Sandbox => "sandbox error", + Permission => "permission denied", + Unsupported => "unsupported operation" + } +} + +decl_lib_func! { + pub fn read_text(vm: &Vm, path: SandboxPath) -> Result { + if !(path.access(vm) & Permissions::R) { + return Err(Error::Permission); + } + //FIXME: refuse if data is too big. + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + std::fs::read_to_string(path).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn write_text(vm: &Vm, path: SandboxPath, data: &str) -> Result<(), Error> { + if !(path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + std::fs::write(path, data).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn copy_file(vm: &Vm, src_path: SandboxPath, dst_path: SandboxPath) -> Result<(), Error> { + if !(src_path.access(vm) & Permissions::R) { + return Err(Error::Permission); + } + if !(dst_path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let src_path = src_path.to_path(vm).map_err(|_| Error::Sandbox)?; + let dst_path = dst_path.to_path(vm).map_err(|_| Error::Sandbox)?; + if let Some(parent) = dst_path.parent() { + std::fs::create_dir_all(parent).map_err(Error::Io)?; + } + std::fs::copy(src_path, dst_path).map(|_| ()).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn symlink(vm: &Vm, src_path: SandboxPath, dst_path: SandboxPath) -> Result<(), Error> { + #[cfg(unix)] + { + if !(src_path.access(vm) & Permissions::R) { + return Err(Error::Permission); + } + if !(dst_path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let src_path = src_path.to_path(vm).map_err(|_| Error::Sandbox)?; + let dst_path = dst_path.to_path(vm).map_err(|_| Error::Sandbox)?; + return std::os::unix::fs::symlink(src_path, dst_path).map(|_| ()).map_err(Error::Io); + } + #[cfg(windows)] + return Err(Error::Unsupported); + } +} + +decl_lib_func! { + pub fn delete_dir(vm: &Vm, path: SandboxPath) -> Result<(), Error> { + if !(path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + std::fs::remove_dir_all(path).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn create_dir(vm: &Vm, path: SandboxPath) -> Result<(), Error> { + if !(path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + std::fs::create_dir_all(path).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn exists(vm: &Vm, path: SandboxPath) -> bool { + path.to_path(vm).map(|v| v.exists()).unwrap_or(false) + } +} + +decl_lib_func! { + pub fn list<'a>(vm: &Vm, path: SandboxPath) -> Result, Error> { + if !(path.access(vm) & Permissions::R) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + let mut tbl = Table::new(vm); + let files = path.read_dir().map_err(Error::Io)?; + for file in files { + let file = file.map_err(Error::Io)?; + let path = file.path(); + let name = file.file_name(); + let ty = file.file_type().map_err(Error::Io)?; + let mut subt = Table::with_capacity(vm, 0, 4); + subt.set(c"path", SandboxPath::from_path_unchecked(&path)).unwrap(); + subt.set(c"name", name.as_encoded_bytes()).unwrap(); + if ty.is_dir() { + subt.set(c"type", "dir").unwrap(); + } else if ty.is_file() { + subt.set(c"type", "file").unwrap(); + } else if ty.is_symlink() { + subt.set(c"type", "symlink").unwrap(); + } else { + subt.set(c"type", "other").unwrap(); + } + tbl.push(subt).unwrap(); + } + Ok(tbl) + } +} diff --git a/core/src/libs/files/mod.rs b/core/src/libs/files/mod.rs new file mode 100644 index 0000000..e8beb3e --- /dev/null +++ b/core/src/libs/files/mod.rs @@ -0,0 +1,58 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +mod interface; +mod obj; +pub mod chroot; +mod lib; + +pub use interface::{SandboxPath, SandboxPathBuf}; +use crate::libs::files::obj::PathWrapper; +use crate::libs::Lib; +use crate::util::Namespace; +use crate::vm::function::types::RFunction; + +pub struct Files; + +impl Lib for Files { + const NAMESPACE: &'static str = "bp3d.files"; + + fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { + namespace.add_userdata::(c"Path", crate::vm::userdata::case::Camel)?; + namespace.add([ + ("readText", RFunction::wrap(lib::read_text)), + ("writeText", RFunction::wrap(lib::write_text)), + ("copyFile", RFunction::wrap(lib::copy_file)), + ("symlink", RFunction::wrap(lib::symlink)), + ("exists", RFunction::wrap(lib::exists)), + ("list", RFunction::wrap(lib::list)), + ("createDir", RFunction::wrap(lib::create_dir)), + ("deleteDir", RFunction::wrap(lib::delete_dir)) + ]) + } +} diff --git a/core/src/libs/files/obj.rs b/core/src/libs/files/obj.rs new file mode 100644 index 0000000..e244e12 --- /dev/null +++ b/core/src/libs/files/obj.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::borrow::Cow; +use std::path::PathBuf; +use crate::{decl_lib_func, decl_userdata, impl_userdata}; +use crate::libs::files::chroot::{sandbox, unsandbox, SandboxError}; +use crate::libs::files::SandboxPath; + +decl_userdata!(pub struct PathWrapper(PathBuf)); + +impl PathWrapper { + pub fn new(path: PathBuf) -> Self { + Self(path) + } + + pub fn path(&self) -> &PathBuf { + &self.0 + } +} + +decl_lib_func! { + fn new(vm: &Vm, path: &str) -> Result { + unsandbox(vm, path).map(|v| PathWrapper(v.into())) + } +} + +impl_userdata! { + impl PathWrapper { + fn join(this: &Path, vm: &Vm, other: SandboxPath) -> Result { + let path = other.to_str(vm)?; + Ok(PathWrapper(this.0.join(path.as_ref()))) + } + + fn with_extension(this: &Path, extension: &str) -> PathWrapper { + PathWrapper(this.0.with_extension(extension)) + } + + fn with_name(this: &Path, name: &str) -> PathWrapper { + let mut path = this.0.clone(); + path.set_file_name(name); + PathWrapper(path) + } + + fn name(this: &Path) -> Option { + this.0.file_name().map(|v| v.to_string_lossy().into()) + } + + fn extension(this: &Path) -> Option { + this.0.extension().map(|v| v.to_string_lossy().into()) + } + + fn __tostring<'a>(this: &Path, vm: &Vm) -> Cow<'a, str> { + sandbox(vm, &this.0).unwrap_or(Cow::Borrowed("")) + } + } + static { + [fn new]; + } +} diff --git a/core/src/libs/mod.rs b/core/src/libs/mod.rs index d982220..aa0f187 100644 --- a/core/src/libs/mod.rs +++ b/core/src/libs/mod.rs @@ -38,6 +38,9 @@ pub mod util; #[cfg(feature = "libs")] pub mod os; +#[cfg(feature = "libs")] +pub mod files; + #[cfg(feature = "libs-core")] mod interface; diff --git a/core/tests/test_vm_files.rs b/core/tests/test_vm_files.rs new file mode 100644 index 0000000..1f0d3c7 --- /dev/null +++ b/core/tests/test_vm_files.rs @@ -0,0 +1,121 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::path::Path; +use bp3d_lua::libs::files::chroot::{access, set_access, set_chroot, Permissions}; +use bp3d_lua::libs::files::{Files, SandboxPath}; +use bp3d_lua::libs::Lib; +use bp3d_lua::vm::RootVm; + +#[test] +fn test_vm_files() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new("/root")); // for testing only + let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path = path.to_path(&vm).unwrap(); + assert_eq!(path, Path::new("/root/myfile")); + let spath: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/data'):join('myfile'):withExtension('txt')").unwrap(); + let path = spath.to_path(&vm).unwrap(); + assert_eq!(path, Path::new("/root/data/myfile.txt")); + let path = spath.to_str(&vm).unwrap(); + assert_eq!(path, "/data/myfile.txt"); + let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/')").unwrap(); + let path = path.to_path(&vm).unwrap(); + assert_eq!(path, Path::new("/root")); +} + +#[test] +fn test_vm_files_security() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new("/root")); // for testing only + vm.run_code::<()>(c"return bp3d.files.Path.new('../myfile')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/../myfile')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/./../myfile')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('.././myfile/.')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../myfile')").unwrap(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../../myfile')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/../data/myfile')").unwrap_err(); +} + +#[test] +fn test_vm_files_permissions() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new("/root")); // for testing only + set_access(&vm, "/rodata", Permissions::R); + set_access(&vm, "/rwdata", Permissions::R | Permissions::W); + set_access(&vm, "/data/myfile.lua", Permissions::R | Permissions::W | Permissions::X); + assert_eq!(access(&vm, "/rodata/myfile.txt"), Permissions::R); + assert_eq!(access(&vm, "/rodata.txt"), Permissions::NONE); + assert_eq!(access(&vm, "/"), Permissions::NONE); + assert_eq!(access(&vm, "/rodata"), Permissions::R); + assert_eq!(access(&vm, "/rwdata"), Permissions::R | Permissions::W); + assert_eq!(access(&vm, "/rwdata/myfile.txt"), Permissions::R | Permissions::W); + assert_eq!(access(&vm, "/data"), Permissions::NONE); + assert_eq!(access(&vm, "/data/myfile"), Permissions::NONE); + assert_eq!(access(&vm, "/data/myfile.txt"), Permissions::NONE); + assert_eq!(access(&vm, "/data/myfile.lua"), Permissions::R | Permissions::W | Permissions::X); +} + +#[test] +fn test_vm_files_permissions2() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new("/root")); // for testing only + set_access(&vm, "/", Permissions::R); + set_access(&vm, "/target", Permissions::R | Permissions::W); + set_access(&vm, "/bp3d-build", Permissions::R | Permissions::X); + assert_eq!(access(&vm, "/target/myfile"), Permissions::R | Permissions::W); + assert_eq!(access(&vm, "/bp3d-build/myfile.lua"), Permissions::R | Permissions::X); + assert_eq!(access(&vm, "/bp3d-build/package/myfile.lua"), Permissions::R | Permissions::X); + assert_eq!(access(&vm, "/target/aarch64/data/1/myfile"), Permissions::R | Permissions::W); + assert_eq!(access(&vm, "/myfile"), Permissions::R); + assert_eq!(access(&vm, "/obj/data/1/myfile"), Permissions::R); +} + +#[test] +fn test_vm_simple_chroot() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new(".")); + let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path = path.to_path(&vm).unwrap(); + assert_eq!(path, Path::new("./myfile")); +} + +#[test] +fn test_vm_simple_chroot2() { + let vm = RootVm::new(); + Files.register(&vm).unwrap(); + set_chroot(&vm, Path::new("./")); + let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path = path.to_path(&vm).unwrap(); + assert_eq!(path, Path::new("./myfile")); +} From 0ae15dea69169d7fc072d84b6bb7aabcc4e9d3dc Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Nov 2025 22:31:35 +0100 Subject: [PATCH 499/527] Use new chroot and access system in loadFile/runFile functions --- core/src/libs/lua/load.rs | 55 +++++++++++++++--------------------- core/src/libs/lua/options.rs | 15 +++------- shell/core/src/lua.rs | 3 +- 3 files changed, 28 insertions(+), 45 deletions(-) diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 7e0cc0e..090b6a8 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -32,9 +32,11 @@ use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::types::Function; -use crate::{decl_closure, decl_lib_func}; +use crate::decl_lib_func; use bp3d_util::simple_error; -use std::path::{Path, PathBuf}; +use std::path::Path; +use crate::libs::files::chroot::Permissions; +use crate::libs::files::SandboxPath; decl_lib_func! { fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> crate::vm::Result { @@ -64,30 +66,19 @@ decl_lib_func! { simple_error! { Error { - EscapeChroot(String) => "invalid path {}: attempt to escape chroot", + Sandbox => "attempt to escape the sandbox", + Permission => "permission denied", (impl From) Io(std::io::Error) => "io error: {}", (impl From) Vm(crate::vm::error::Error) => "lua error: {}" } } -fn parse_lua_path(chroot: &Path, path: &str) -> Result { - let iter = path.split("/"); - let mut cur_path = Vec::new(); - for component in iter { - if component == ".." { - if cur_path.pop().is_none() { - return Err(Error::EscapeChroot(path.into())); - } - } else if component != "." { - cur_path.push(component); +decl_lib_func! { + fn load_file<'a> (vm: &Vm, path: SandboxPath) -> (Option>, Option) { + if !(path.access(vm) & Permissions::X) { + return (None, Some("permission denied".into())) } - } - Ok(chroot.join(path)) -} - -decl_closure! { - fn load_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> (Option>, Option) { - let path = match parse_lua_path(chroot, path) { + let path = match path.to_path(vm) { Ok(v) => v, Err(e) => return (None, Some(e.to_string())) }; @@ -102,9 +93,12 @@ decl_closure! { } } -decl_closure! { - fn run_file<'a> |chroot: &Path| (vm: &Vm, path: &str) -> Result { - let path = parse_lua_path(chroot, path)?; +decl_lib_func! { + fn run_file<'a> (vm: &Vm, path: SandboxPath) -> Result { + if !(path.access(vm) & Permissions::X) { + return Err(Error::Permission) + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; let script = Script::from_path(path)?; let top = vm.top(); vm.run::(script)?; @@ -112,22 +106,17 @@ decl_closure! { } } -pub struct Load<'a>(pub Option<&'a Path>); +pub struct Load; -impl Lib for Load<'_> { +impl Lib for Load { const NAMESPACE: &'static str = "bp3d.lua"; fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add([ ("runString", RFunction::wrap(run_string)), ("loadString", RFunction::wrap(load_string)), - ])?; - if let Some(chroot) = self.0 { - namespace.add([ - ("loadFile", load_file(chroot)), - ("runFile", run_file(chroot)), - ])?; - } - Ok(()) + ("loadFile", RFunction::wrap(load_file)), + ("runFile", RFunction::wrap(run_file)), + ]) } } diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index 4edad58..6b80087 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -32,34 +32,27 @@ use crate::libs::lua::load::Load; use crate::libs::lua::require::{Provider, Require}; use crate::libs::Lib; use crate::vm::closure::arc::Shared; -use std::path::Path; #[derive(Default)] -pub struct Lua<'a> { - pub(super) load_chroot_path: Option<&'a Path>, +pub struct Lua { pub(super) provider: Option>, } -impl<'a> Lua<'a> { +impl Lua { pub fn new() -> Self { Self::default() } - pub fn load_chroot_path(mut self, path: &'a Path) -> Self { - self.load_chroot_path = Some(path); - self - } - pub fn provider(mut self, provider: Shared) -> Self { self.provider = Some(provider); self } - pub fn build(self) -> impl Lib + 'a { + pub fn build(self) -> impl Lib { ( Base, Call, - Load(self.load_chroot_path), + Load, Require(self.provider.unwrap_or_default()), ) } diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index c5ebc50..8798424 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -47,6 +47,7 @@ use std::rc::Rc; use std::thread::JoinHandle; use std::time::Duration; use tokio::sync::mpsc; +use bp3d_lua::libs::files::chroot::set_chroot; const CHANNEL_BUFFER: usize = 32; @@ -129,8 +130,8 @@ impl Lua { if let Err(e) = libs::util::Util.register(vm) { error!("Failed to load util library: {}", e); } + set_chroot(&vm, &args.data); if let Err(e) = libs::lua::Lua::new() - .load_chroot_path(&args.data) .build() .register(vm) { From 8fbe441bbf759add931437126ea636edc6bac30f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Nov 2025 23:04:34 +0100 Subject: [PATCH 500/527] Updated definitions for bp3d.lua and added new bp3d.files definitions --- definitions/bp3d.files.lua | 105 +++++++++++++++++++++++++++++++++++++ definitions/bp3d.lua.lua | 4 +- 2 files changed, 107 insertions(+), 2 deletions(-) create mode 100644 definitions/bp3d.files.lua diff --git a/definitions/bp3d.files.lua b/definitions/bp3d.files.lua new file mode 100644 index 0000000..7454cc3 --- /dev/null +++ b/definitions/bp3d.files.lua @@ -0,0 +1,105 @@ +-- Copyright (c) 2025, BlockProject 3D +-- +-- All rights reserved. +-- +-- Redistribution and use in source and binary forms, with or without modification, +-- are permitted provided that the following conditions are met: +-- +-- * Redistributions of source code must retain the above copyright notice, +-- this list of conditions and the following disclaimer. +-- * Redistributions in binary form must reproduce the above copyright notice, +-- this list of conditions and the following disclaimer in the documentation +-- and/or other materials provided with the distribution. +-- * Neither the name of BlockProject 3D nor the names of its contributors +-- may be used to endorse or promote products derived from this software +-- without specific prior written permission. +-- +-- THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +-- "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +-- LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +-- A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +-- CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +-- EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +-- PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +-- PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +-- LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +-- NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +-- SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--- @meta bp3d.files + +bp3d = {} +bp3d.files = {} + +--- @class Path +Path = {} + +--- Join this path with a new component. +--- +--- @param other string | Path path component to join with. +--- @return Path +function Path:join(other) end + +--- Changes the extension of this path. +--- +--- @param ext string the new extension to set. +--- @return Path +function Path:withExtension(ext) end + +--- Changes the file name of this path. +--- +--- @param name string the new file name to set. +--- @return Path +function Path:withName(name) end + +--- @return string whatever the file name. +function Path:name() end + +--- @return string whatever the file extension. +function Path:extension() end + +--- Read a text file. +--- +--- @param path string | Path the path to read from. +--- @return string +bp3d.files.readText = function(path) end + +--- Write a text file. +--- +--- @param path string | Path the path to write to. +--- @param data string the file content. +bp3d.files.writeText = function(path, data) end + +--- Copy a file. +--- +--- @param src string | Path the source path. +--- @param dst string | Path the destination path. +bp3d.files.copyFile = function(src, dst) end + +--- Creeates a symlink. +--- +--- @param src string | Path the source path. +--- @param dst string | Path the destination path. +bp3d.files.symlink = function(src, dst) end + +--- Creeates the directory at the specified path. +--- +--- @param path string | Path +bp3d.files.deleteDir = function(path) end + +--- Creeates a directory at the specified path. +--- +--- @param path string | Path +bp3d.files.createDir = function(path) end + +--- Returns true if the path exists. +--- +--- @param path string | Path +--- @return boolean +bp3d.files.exists = function(path) end + +--- List files and folders under the specified directory. +--- +--- @param path string | Path +--- @return [{ path: Path, name: string, type: "dir" | "file" | "symlink" | "other" }] +bp3d.files.list = function(path) end diff --git a/definitions/bp3d.lua.lua b/definitions/bp3d.lua.lua index 7559cde..b25621f 100644 --- a/definitions/bp3d.lua.lua +++ b/definitions/bp3d.lua.lua @@ -68,7 +68,7 @@ bp3d.lua.loadString = function(s, chunkname) end --- This avoids arbitrary code execution on files which the user does not whish to make visible to the Lua engine. --- --- @raises syntax error. ---- @param path string path to the file to load/compile. +--- @param path string | Path path to the file to load/compile. --- @return function whatever the compiled function. bp3d.lua.loadFile = function(path) end @@ -80,7 +80,7 @@ bp3d.lua.loadFile = function(path) end --- This avoids arbitrary code execution on files which the user does not whish to make visible to the Lua engine. --- --- @raises syntax or runtime error. ---- @param path string path to the file to load/compile and run. +--- @param path string | Path path to the file to load/compile and run. --- @return any[] whatever outputs returned by the function which was executed. bp3d.lua.runFile = function(path) end From 6aab4f0814aeeafd0cc97c0440c1cd0feb9215f9 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 4 Nov 2025 23:16:59 +0100 Subject: [PATCH 501/527] Added missing functions to SandboxPathBuf --- core/src/libs/files/interface.rs | 68 ++++++++++++++++++++++++++++++++ core/src/libs/lua/load.rs | 1 - 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs index de6af44..1b84930 100644 --- a/core/src/libs/files/interface.rs +++ b/core/src/libs/files/interface.rs @@ -27,6 +27,7 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. use std::borrow::Cow; +use std::ffi::OsStr; use std::path::{Path, PathBuf}; use bp3d_debug::error; use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; @@ -58,6 +59,52 @@ impl From> for SandboxPathBuf { } } +impl SandboxPathBuf { + /// Creates a new [SandboxPathBuf] from an existing string. + pub fn from_str(vm: &Vm, path: String) -> Result { + unsandbox(vm, &path)?; + Ok(SandboxPathBuf::String(path)) + } + + /// Creates a new [SandboxPathBuf] from an existing string. + /// + /// If the given string cannot be represented in the current sandbox configuration, a nil value + /// will be passed to Lua. + pub fn from_str_unchecked(path: String) -> SandboxPathBuf { + SandboxPathBuf::String(path) + } + + /// Creates a new [SandboxPathBuf] from an existing [PathBuf]. + pub fn from_path<'a>(vm: &Vm, path: PathBuf) -> Result { + sandbox(vm, &path)?; + Ok(SandboxPathBuf::Path(path)) + } + + /// Creates a new [SandboxPathBuf] from an existing [PathBuf]. + /// + /// This function allows passing paths from Rust to Lua which are outside the sandbox. + /// Use with caution. + pub fn from_path_unchecked(path: PathBuf) -> SandboxPathBuf { + SandboxPathBuf::Path(path) + } + + pub fn as_path(&self) -> SandboxPath<'_> { + match self { + SandboxPathBuf::String(v) => SandboxPath::String(v), + SandboxPathBuf::Path(v) => SandboxPath::Path(v) + } + } + + /// Returns the underlying path as raw [OsStr]. This function does not interpret the path + /// according to the current sandbox configuration. + pub fn as_os_str(&self) -> &OsStr { + match self { + SandboxPathBuf::String(v) => v.as_ref(), + SandboxPathBuf::Path(v) => v.as_os_str() + } + } +} + impl SandboxPath<'_> { /// Creates a new [SandboxPath] from an existing string. pub fn from_str<'a>(vm: &Vm, path: &'a str) -> Result, SandboxError> { @@ -87,6 +134,15 @@ impl SandboxPath<'_> { SandboxPath::Path(path) } + /// Returns the underlying path as raw [OsStr]. This function does not interpret the path + /// according to the current sandbox configuration. + pub fn as_os_str(&self) -> &OsStr { + match self { + SandboxPath::String(v) => v.as_ref(), + SandboxPath::Path(v) => v.as_os_str() + } + } + pub fn to_str(&self, vm: &Vm) -> Result, SandboxError> { match self { SandboxPath::String(v) => { @@ -185,3 +241,15 @@ unsafe impl IntoParam for SandboxPath<'_> { IntoLua::into_lua(self, vm) as _ } } + +unsafe impl IntoParam for SandboxPathBuf { + fn into_param(self, vm: &Vm) -> i32 { + IntoLua::into_lua(self.as_path(), vm) as _ + } +} + +unsafe impl IntoLua for SandboxPathBuf { + fn into_lua(self, vm: &Vm) -> u16 { + IntoLua::into_lua(self.as_path(), vm) + } +} diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 090b6a8..5c251d4 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -34,7 +34,6 @@ use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::types::Function; use crate::decl_lib_func; use bp3d_util::simple_error; -use std::path::Path; use crate::libs::files::chroot::Permissions; use crate::libs::files::SandboxPath; From 69f77fdaf7fd56904a60642c34db50ebaed3dfbe Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 00:07:28 +0100 Subject: [PATCH 502/527] Moved chroot escape check to its function --- core/src/libs/files/chroot.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/src/libs/files/chroot.rs b/core/src/libs/files/chroot.rs index 29c7437..94cf67b 100644 --- a/core/src/libs/files/chroot.rs +++ b/core/src/libs/files/chroot.rs @@ -72,12 +72,12 @@ impl Display for SandboxError { impl std::error::Error for SandboxError {} -pub fn unsandbox<'a>(vm: &Vm, path: &'a str) -> Result, SandboxError> { +pub fn is_escaping(path: &str) -> bool { let mut level = 0; for component in path.split('/') { if component == ".." { if level == 0 { - return Err(SandboxError); + return true; } level -= 2; } else if component == "." || component == "" { @@ -86,7 +86,11 @@ pub fn unsandbox<'a>(vm: &Vm, path: &'a str) -> Result, SandboxErr } } trace!({level}, "unsandbox {}", path); - if level < 0 { + level < 0 +} + +pub fn unsandbox<'a>(vm: &Vm, path: &'a str) -> Result, SandboxError> { + if is_escaping(path) { return Err(SandboxError); } if path.len() > 0 && path.as_bytes()[0] == b'/' { From 38bded5581678f95cffd2e2522480543da674ed7 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 00:07:59 +0100 Subject: [PATCH 503/527] Fixed symlink function in bp3d.files lib --- core/src/libs/files/interface.rs | 9 ++++++++- core/src/libs/files/lib.rs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs index 1b84930..c65ff62 100644 --- a/core/src/libs/files/interface.rs +++ b/core/src/libs/files/interface.rs @@ -30,7 +30,7 @@ use std::borrow::Cow; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use bp3d_debug::error; -use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; +use crate::libs::files::chroot::{access, is_escaping, sandbox, unsandbox, Permissions, SandboxError}; use crate::libs::files::obj::PathWrapper; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; @@ -170,6 +170,13 @@ impl SandboxPath<'_> { }; access(vm, &path) } + + pub fn is_relative(&self) -> bool { + match self { + SandboxPath::String(v) => !is_escaping(v) && !v.starts_with("/"), + SandboxPath::Path(v) => v.starts_with("/") + } + } } impl<'a> FromLua<'a> for SandboxPath<'a> { diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index d709058..cc65cbf 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -83,7 +83,7 @@ decl_lib_func! { pub fn symlink(vm: &Vm, src_path: SandboxPath, dst_path: SandboxPath) -> Result<(), Error> { #[cfg(unix)] { - if !(src_path.access(vm) & Permissions::R) { + if !src_path.is_relative() && !(src_path.access(vm) & Permissions::R) { return Err(Error::Permission); } if !(dst_path.access(vm) & Permissions::W) { From 51871090805723f515f33af1cf1b326068c43625 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 07:26:45 +0100 Subject: [PATCH 504/527] Fixed sandbox security for symlink --- core/src/libs/files/interface.rs | 6 +++--- core/src/libs/files/lib.rs | 11 +++++++++-- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs index c65ff62..47aa2ba 100644 --- a/core/src/libs/files/interface.rs +++ b/core/src/libs/files/interface.rs @@ -30,7 +30,7 @@ use std::borrow::Cow; use std::ffi::OsStr; use std::path::{Path, PathBuf}; use bp3d_debug::error; -use crate::libs::files::chroot::{access, is_escaping, sandbox, unsandbox, Permissions, SandboxError}; +use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; use crate::libs::files::obj::PathWrapper; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; @@ -173,8 +173,8 @@ impl SandboxPath<'_> { pub fn is_relative(&self) -> bool { match self { - SandboxPath::String(v) => !is_escaping(v) && !v.starts_with("/"), - SandboxPath::Path(v) => v.starts_with("/") + SandboxPath::String(v) => !v.starts_with("/"), + SandboxPath::Path(v) => !v.starts_with("/") } } } diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index cc65cbf..8b0a5b5 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -28,7 +28,7 @@ use bp3d_util::simple_error; use crate::decl_lib_func; -use crate::libs::files::chroot::Permissions; +use crate::libs::files::chroot::{access, sandbox, Permissions}; use crate::libs::files::SandboxPath; use crate::vm::table::Table; @@ -83,7 +83,14 @@ decl_lib_func! { pub fn symlink(vm: &Vm, src_path: SandboxPath, dst_path: SandboxPath) -> Result<(), Error> { #[cfg(unix)] { - if !src_path.is_relative() && !(src_path.access(vm) & Permissions::R) { + if src_path.is_relative() { + let first_part = dst_path.to_path(vm).map_err(|_| Error::Sandbox)?; + let path = first_part.join(src_path.as_os_str()); + let path = sandbox(vm, &path).map_err(|_| Error::Sandbox)?; + if !(access(vm, &path) & Permissions::R) { + return Err(Error::Permission); + } + } else if !(src_path.access(vm) & Permissions::R) { return Err(Error::Permission); } if !(dst_path.access(vm) & Permissions::W) { From 028bc9d53a88eb3153b576918f91c1190dac046f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 22:28:33 +0100 Subject: [PATCH 505/527] Added fix to reject files over 5Mb --- core/src/libs/files/lib.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index 8b0a5b5..1a5ecac 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -26,16 +26,22 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::fs::File; +use std::io::Read; use bp3d_util::simple_error; use crate::decl_lib_func; use crate::libs::files::chroot::{access, sandbox, Permissions}; use crate::libs::files::SandboxPath; use crate::vm::table::Table; +const MAX_FILE_SIZE: usize = 5000000; //5Mb + simple_error! { Error { Io(std::io::Error) => "io error: {}", Sandbox => "sandbox error", + TooLarge(usize) => "file is too large ({})", + Memory => "memory error", Permission => "permission denied", Unsupported => "unsupported operation" } @@ -46,9 +52,16 @@ decl_lib_func! { if !(path.access(vm) & Permissions::R) { return Err(Error::Permission); } - //FIXME: refuse if data is too big. let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; - std::fs::read_to_string(path).map_err(Error::Io) + let mut file = File::open(path).map_err(Error::Io)?; + let size = file.metadata().map(|m| m.len() as usize).ok().unwrap_or(usize::MAX); + if size > MAX_FILE_SIZE { + return Err(Error::TooLarge(size)); + } + let mut s = String::new(); + s.try_reserve_exact(size).map_err(|_| Error::Memory)?; + file.read_to_string(&mut s).map_err(Error::Io)?; + Ok(s) } } From 7e95312b0b5bb8a4367bce0951d9332543fd6d8f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 22:29:19 +0100 Subject: [PATCH 506/527] Rename obj to path --- core/src/libs/files/interface.rs | 2 +- core/src/libs/files/mod.rs | 4 ++-- core/src/libs/files/{obj.rs => path.rs} | 0 3 files changed, 3 insertions(+), 3 deletions(-) rename core/src/libs/files/{obj.rs => path.rs} (100%) diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs index 47aa2ba..598b812 100644 --- a/core/src/libs/files/interface.rs +++ b/core/src/libs/files/interface.rs @@ -31,7 +31,7 @@ use std::ffi::OsStr; use std::path::{Path, PathBuf}; use bp3d_debug::error; use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; -use crate::libs::files::obj::PathWrapper; +use crate::libs::files::path::PathWrapper; use crate::util::core::SimpleDrop; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::util::LuaType; diff --git a/core/src/libs/files/mod.rs b/core/src/libs/files/mod.rs index e8beb3e..f39702b 100644 --- a/core/src/libs/files/mod.rs +++ b/core/src/libs/files/mod.rs @@ -27,12 +27,12 @@ // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mod interface; -mod obj; +mod path; pub mod chroot; mod lib; pub use interface::{SandboxPath, SandboxPathBuf}; -use crate::libs::files::obj::PathWrapper; +use crate::libs::files::path::PathWrapper; use crate::libs::Lib; use crate::util::Namespace; use crate::vm::function::types::RFunction; diff --git a/core/src/libs/files/obj.rs b/core/src/libs/files/path.rs similarity index 100% rename from core/src/libs/files/obj.rs rename to core/src/libs/files/path.rs From 9ce52e510e78f7c667254fbdee0ff76d8b4faa6a Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 5 Nov 2025 23:05:07 +0100 Subject: [PATCH 507/527] Added new access function and File object --- core/src/libs/files/file.rs | 149 ++++++++++++++++++++++++++++++++++++ core/src/libs/files/lib.rs | 23 ++++++ core/src/libs/files/mod.rs | 6 +- 3 files changed, 177 insertions(+), 1 deletion(-) create mode 100644 core/src/libs/files/file.rs diff --git a/core/src/libs/files/file.rs b/core/src/libs/files/file.rs new file mode 100644 index 0000000..b8c6c4d --- /dev/null +++ b/core/src/libs/files/file.rs @@ -0,0 +1,149 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, Write}; +use bp3d_util::simple_error; +use crate::{decl_lib_func, decl_userdata, impl_userdata_mut}; +use crate::libs::files::chroot::Permissions; +use crate::libs::files::SandboxPath; + +const MAX_BUF_SIZE: usize = 65535; + +simple_error! { + Error { + Io(std::io::Error) => "io error: {}", + TooLarge(usize) => "internal buffer overflow ({})", + Sandbox => "sandbox error", + Permission => "permission denied" + } +} + +decl_userdata! { + pub struct FileWrapper { + file: File, + is_ro: bool, + buffer: [u8; MAX_BUF_SIZE] + } +} + +decl_lib_func! { + fn open(vm: &Vm, path: SandboxPath, mode: &str) -> Result { + let perms = path.access(vm); + let mut opts = OpenOptions::new(); + let mut is_ro = true; + if mode.contains("r") { + if !(perms & Permissions::R) { + return Err(Error::Permission); + } + opts.read(true); + } + if mode.contains("w") { + if !(perms & Permissions::W) { + return Err(Error::Permission); + } + is_ro = false; + opts.write(true); + } + if mode.contains("a") { + if !(perms & Permissions::W) || !(perms & Permissions::R) { + return Err(Error::Permission); + } + is_ro = false; + opts.append(true); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + let file = opts.open(path).map_err(Error::Io)?; + Ok(FileWrapper { + file, + is_ro, + buffer: [0; MAX_BUF_SIZE] + }) + } +} + +decl_lib_func! { + fn create(vm: &Vm, path: SandboxPath) -> Result { + let perms = path.access(vm); + if !(perms & Permissions::W) { + return Err(Error::Permission); + } + if !(perms & Permissions::R) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + let file = File::create(path).map_err(Error::Io)?; + Ok(FileWrapper { + file, + is_ro: false, + buffer: [0; MAX_BUF_SIZE] + }) + } +} + +impl_userdata_mut! { + impl FileWrapper { + fn read(this: &mut FileWrapper, size: usize) -> Result, Error> { + if size >= MAX_BUF_SIZE { + return Err(Error::TooLarge(size)); + } + let len = this.file.read(&mut this.buffer[..size]).map_err(Error::Io)?; + if len == 0 { + return Ok(None); + } + Ok(Some(&this.buffer[..len])) + } + + fn write(this: &mut FileWrapper, buf: &[u8]) -> Result { + if this.is_ro { + return Err(Error::Permission); + } + this.file.write(buf).map_err(Error::Io) + } + + fn seek_from_start(this: &mut FileWrapper, pos: u64) -> std::io::Result { + this.file.seek(std::io::SeekFrom::Start(pos)) + } + + fn seek_from_end(this: &mut FileWrapper, pos: i64) -> std::io::Result { + this.file.seek(std::io::SeekFrom::End(pos)) + } + + fn seek_from_cursor(this: &mut FileWrapper, pos: i64) -> std::io::Result { + this.file.seek(std::io::SeekFrom::Current(pos)) + } + + fn size(this: &FileWrapper) -> std::io::Result { + this.file.metadata().map(|m| m.len()) + } + } + static { + [fn open]; + [fn create]; + } +} diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index 1a5ecac..d4ef72c 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -174,3 +174,26 @@ decl_lib_func! { Ok(tbl) } } + +decl_lib_func! { + pub fn lua_access<'a>(vm: &Vm, path: SandboxPath) -> crate::vm::Result> { + let perms = path.access(vm); + let mut tbl = Table::new(vm); + if perms & Permissions::R { + tbl.set(c"r", true)?; + } else { + tbl.set(c"r", false)?; + } + if perms & Permissions::W { + tbl.set(c"w", true)?; + } else { + tbl.set(c"w", false)?; + } + if perms & Permissions::X { + tbl.set(c"x", true)?; + } else { + tbl.set(c"x", false)?; + } + Ok(tbl) + } +} diff --git a/core/src/libs/files/mod.rs b/core/src/libs/files/mod.rs index f39702b..a8a273d 100644 --- a/core/src/libs/files/mod.rs +++ b/core/src/libs/files/mod.rs @@ -30,8 +30,10 @@ mod interface; mod path; pub mod chroot; mod lib; +mod file; pub use interface::{SandboxPath, SandboxPathBuf}; +use crate::libs::files::file::FileWrapper; use crate::libs::files::path::PathWrapper; use crate::libs::Lib; use crate::util::Namespace; @@ -44,6 +46,7 @@ impl Lib for Files { fn load(&self, namespace: &mut Namespace) -> crate::vm::Result<()> { namespace.add_userdata::(c"Path", crate::vm::userdata::case::Camel)?; + namespace.add_userdata::(c"File", crate::vm::userdata::case::Camel)?; namespace.add([ ("readText", RFunction::wrap(lib::read_text)), ("writeText", RFunction::wrap(lib::write_text)), @@ -52,7 +55,8 @@ impl Lib for Files { ("exists", RFunction::wrap(lib::exists)), ("list", RFunction::wrap(lib::list)), ("createDir", RFunction::wrap(lib::create_dir)), - ("deleteDir", RFunction::wrap(lib::delete_dir)) + ("deleteDir", RFunction::wrap(lib::delete_dir)), + ("access", RFunction::wrap(lib::lua_access)) ]) } } From ff6e96c618bcdbee1d28495ea8d0d558206aed44 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Thu, 6 Nov 2025 21:44:08 +0100 Subject: [PATCH 508/527] Added definitions for new File type --- core/src/libs/files/file.rs | 13 +++---- core/src/vm/value/integer53.rs | 25 ++++++++++++++ definitions/bp3d.files.lua | 63 ++++++++++++++++++++++++++++++++++ 3 files changed, 95 insertions(+), 6 deletions(-) diff --git a/core/src/libs/files/file.rs b/core/src/libs/files/file.rs index b8c6c4d..e759f9b 100644 --- a/core/src/libs/files/file.rs +++ b/core/src/libs/files/file.rs @@ -32,6 +32,7 @@ use bp3d_util::simple_error; use crate::{decl_lib_func, decl_userdata, impl_userdata_mut}; use crate::libs::files::chroot::Permissions; use crate::libs::files::SandboxPath; +use crate::vm::value::types::UInt53; const MAX_BUF_SIZE: usize = 65535; @@ -108,22 +109,22 @@ decl_lib_func! { impl_userdata_mut! { impl FileWrapper { - fn read(this: &mut FileWrapper, size: usize) -> Result, Error> { - if size >= MAX_BUF_SIZE { - return Err(Error::TooLarge(size)); + fn read(this: &mut FileWrapper, size: UInt53) -> Result, Error> { + if size.to_usize() >= MAX_BUF_SIZE { + return Err(Error::TooLarge(size.to_usize())); } - let len = this.file.read(&mut this.buffer[..size]).map_err(Error::Io)?; + let len = this.file.read(&mut this.buffer[..size.to_usize()]).map_err(Error::Io)?; if len == 0 { return Ok(None); } Ok(Some(&this.buffer[..len])) } - fn write(this: &mut FileWrapper, buf: &[u8]) -> Result { + fn write(this: &mut FileWrapper, buf: &[u8]) -> Result { if this.is_ro { return Err(Error::Permission); } - this.file.write(buf).map_err(Error::Io) + this.file.write(buf).map(UInt53::from_usize_lossy).map_err(Error::Io) } fn seek_from_start(this: &mut FileWrapper, pos: u64) -> std::io::Result { diff --git a/core/src/vm/value/integer53.rs b/core/src/vm/value/integer53.rs index ebf3642..ffbf2a2 100644 --- a/core/src/vm/value/integer53.rs +++ b/core/src/vm/value/integer53.rs @@ -45,6 +45,16 @@ impl Int53 { pub const MIN: Int53 = Int53(-(2 << 51)); pub const MAX: Int53 = Int53((2 << 51) - 1); + pub const fn from_isize_lossy(value: isize) -> Int53 { + if (value as i64) < Self::MIN.0 { + Self::MIN + } else if (value as i64) > Self::MAX.0 { + Self::MAX + } else { + Int53(value as _) + } + } + pub const fn from_i64_lossy(value: i64) -> Int53 { if value < Self::MIN.0 { Self::MIN @@ -59,6 +69,11 @@ impl Int53 { pub const fn to_i64(self) -> i64 { self.0 } + + #[inline(always)] + pub const fn to_isize(self) -> isize { + self.0 as _ + } } impl UInt53 { @@ -70,10 +85,20 @@ impl UInt53 { UInt53(value & Self::MAX.0) } + #[inline(always)] + pub const fn from_usize_lossy(value: usize) -> UInt53 { + UInt53((value as u64) & Self::MAX.0) + } + #[inline(always)] pub const fn to_u64(self) -> u64 { self.0 } + + #[inline(always)] + pub const fn to_usize(self) -> usize { + self.0 as _ + } } impl From for i64 { diff --git a/definitions/bp3d.files.lua b/definitions/bp3d.files.lua index 7454cc3..99d5a35 100644 --- a/definitions/bp3d.files.lua +++ b/definitions/bp3d.files.lua @@ -58,6 +58,63 @@ function Path:name() end --- @return string whatever the file extension. function Path:extension() end +--- Creates a new Path object. +--- +--- @param path string the path to wrap. +--- @return Path +Path.new = function(path) end + +--- @class File +File = {} + +--- Read a block of data from the file. Returns a byte string of the content. +--- +--- @param size number number of bytes to read. +--- @return string +function File:read(size) end + +--- Writes the given block of data to the file. Returns the number of bytes written. +--- +--- @param data string byte string representing the block of data. +--- @return number +function File:write(data) end + +--- Seeks from the start of the file. +--- +--- @param pos number (uint64_t LuaJIT cdata type) +--- @return number uint64_t LuaJIT cdata type +function File:seekFromStart(pos) end + +--- Seeks from the end of the file. +--- +--- @param pos number (int64_t LuaJIT cdata type) +--- @return number uint64_t LuaJIT cdata type +function File:seekFromEnd(pos) end + +--- Seeks from the current cursor position. +--- +--- @param pos number (int64_t LuaJIT cdata type) +--- @return number uint64_t LuaJIT cdata type +function File:seekFromCursor(pos) end + +--- Returns the size of the file. +--- +--- @return number uint64_t LuaJIT cdata type +function File:size() end + +--- Opens a new file for read/write or append. +--- +--- @param path string | Path the path of the file. +--- @param mode string the file mode, r for read, w for write and a for append. +--- @return File +File.open = function(path, mode) end + +--- Creates a new file for writing. +--- +--- @param path string | Path the path of the file. +--- @return File +File.create = function(path) end + --- Read a text file. --- --- @param path string | Path the path to read from. @@ -103,3 +160,9 @@ bp3d.files.exists = function(path) end --- @param path string | Path --- @return [{ path: Path, name: string, type: "dir" | "file" | "symlink" | "other" }] bp3d.files.list = function(path) end + +--- Returns the permissions of the specified file path. +--- +--- @param path string | Path +--- @return { r: boolean, w: boolean, x: boolean } +bp3d.files.access = function(path) end From 05f6e0c6d046ec52cf97d3396f3969cdc3a8c910 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 12 Nov 2025 22:35:09 +0100 Subject: [PATCH 509/527] Updated to new version of bp3d-os and bp3d-debug --- build/Cargo.toml | 2 +- core/Cargo.toml | 4 ++-- shell/core/Cargo.toml | 4 ++-- testbin/Cargo.toml | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/build/Cargo.toml b/build/Cargo.toml index fbd0328..1f9791d 100644 --- a/build/Cargo.toml +++ b/build/Cargo.toml @@ -5,6 +5,6 @@ edition = "2024" publish = false [dependencies] -bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["fs"] } +bp3d-os = { version = "2.2.3", features = ["fs"] } phf = { version = "0.11.3", features = ["macros"] } cc = "1.2.15" diff --git a/core/Cargo.toml b/core/Cargo.toml index f3906a1..15d4391 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -15,8 +15,8 @@ publish = false [dependencies] bp3d-util = { version = "2.2.0", features = ["simple-error", "format", "string"] } -bp3d-debug = "1.0.0" -bp3d-os = { version = "2.1.0", features = [], optional = true } +bp3d-debug = "1.0.2" +bp3d-os = { version = "2.2.3", features = [], optional = true } time = { version = "0.3.41", features = ["formatting"], optional = true } itertools = { version = "0.14.0" } bp3d-lua-codegen = { version = "1.0.0-rc.1.0.0", path = "../codegen", optional = true } diff --git a/shell/core/Cargo.toml b/shell/core/Cargo.toml index de11c3a..0d230a0 100644 --- a/shell/core/Cargo.toml +++ b/shell/core/Cargo.toml @@ -6,11 +6,11 @@ publish = false [dependencies] bp3d-lua = { version = "1.0.0-rc.4.0.0", path = "../../core", features = ["root-vm", "util-module", "util-thread", "libs", "dynamic", "interrupt"] } -bp3d-debug = "1.0.0-rc.6.2.0" +bp3d-debug = "1.0.2" bp3d-net = { version = "1.0.0-rc.2.1.2", features = ["ipc"] } tokio = { version = "1.45.1", features = ["full"] } bp3d-util = { version = "2.2.0", features = ["result"] } bp3d-lua-shell-proto = { version = "0.1.0", path = "../proto" } bp3d-proto = "1.0.0-rc.5.0.1" clap = { version = "4.5.4", features = ["derive"] } -bp3d-os = { version = "2.1.0", features = ["time", "shell"] } +bp3d-os = { version = "2.2.3", features = ["time", "shell"] } diff --git a/testbin/Cargo.toml b/testbin/Cargo.toml index 60c7ced..d9ecfba 100644 --- a/testbin/Cargo.toml +++ b/testbin/Cargo.toml @@ -9,7 +9,7 @@ publish = false [dependencies] mlua = { version = "0.11.1", features = ["luajit"] } bp3d-lua = { version = "1.0.0-rc.4.0.0", path = "../core", features = ["root-vm"] } -bp3d-os = { version = "1.0.0-rc.4.2.1", features = ["time"] } +bp3d-os = { version = "2.2.3", features = ["time"] } [workspace] members = [] From cf8bb5e61748a013ce2286e0cf8b189695d41692 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 12 Nov 2025 22:09:17 +0100 Subject: [PATCH 510/527] Fixed build error under windows --- testbin/.cargo/config | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/testbin/.cargo/config b/testbin/.cargo/config index 446973d..9b9ea9c 100644 --- a/testbin/.cargo/config +++ b/testbin/.cargo/config @@ -5,3 +5,11 @@ rustc-link-lib = [] [target.aarch64-apple-darwin.lua] rustc-link-search = [] rustc-link-lib = [] + +[target.aarch64-pc-windows-msvc.lua] +rustc-link-search = [] +rustc-link-lib = [] + +[target.x86_64-pc-windows-msvc.lua] +rustc-link-search = [] +rustc-link-lib = [] From 0af03fd808e022b211edb19ffe4fd69c017238db Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Wed, 12 Nov 2025 22:21:39 +0100 Subject: [PATCH 511/527] Fixed sandbox function parsing error under windows --- core/src/libs/files/chroot.rs | 22 +++++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/core/src/libs/files/chroot.rs b/core/src/libs/files/chroot.rs index 94cf67b..a8bc7d3 100644 --- a/core/src/libs/files/chroot.rs +++ b/core/src/libs/files/chroot.rs @@ -111,14 +111,30 @@ pub fn sandbox<'a>(vm: &Vm, path: &'a Path) -> Result, SandboxError if pos == 0 { return Err(SandboxError) } + #[cfg(windows)] + let src = &path.as_os_str().as_encoded_bytes()[pos..]; + #[cfg(unix)] let mut src = &path.as_os_str().as_encoded_bytes()[pos..]; if src.len() == 0 { return Ok(Cow::Borrowed("/")); } - if src[0] != b'/' { - src = &path.as_os_str().as_encoded_bytes()[pos - 1..]; + #[cfg(windows)] + { + let src = String::from_utf8_lossy(src).replace("\\", "/"); + if src.as_bytes()[0] != b'/' { + let src = &src[pos - 1..]; + return Ok(Cow::Owned(src.into())); + } else { + return Ok(Cow::Owned(src)); + } + } + #[cfg(unix)] + { + if src[0] != b'/' { + src = &path.as_os_str().as_encoded_bytes()[pos - 1..]; + } + return Ok(String::from_utf8_lossy(src)); } - Ok(String::from_utf8_lossy(src)) } #[derive(Copy, Clone, Eq, PartialEq, Debug)] From 2780d29de575d7e562ab4d9f5763a12cd0847b97 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 12 Nov 2025 22:30:35 +0000 Subject: [PATCH 512/527] Format Rust code using rustfmt --- codegen/src/gen/from_param.rs | 2 +- core/src/libs/files/chroot.rs | 20 ++++----- core/src/libs/files/file.rs | 8 ++-- core/src/libs/files/interface.rs | 43 +++++++++--------- core/src/libs/files/lib.rs | 6 +-- core/src/libs/files/mod.rs | 10 ++--- core/src/libs/files/path.rs | 6 +-- core/src/libs/lua/load.rs | 6 +-- core/src/libs/lua/options.rs | 7 +-- core/tests/test_vm_files.rs | 75 ++++++++++++++++++++++++-------- shell/core/src/lua.rs | 7 +-- 11 files changed, 111 insertions(+), 79 deletions(-) diff --git a/codegen/src/gen/from_param.rs b/codegen/src/gen/from_param.rs index b6ea5ff..ea7b150 100644 --- a/codegen/src/gen/from_param.rs +++ b/codegen/src/gen/from_param.rs @@ -54,7 +54,7 @@ impl FromParam { pub struct Field { name: Ident, from_param: TokenStream, - try_from_param: TokenStream + try_from_param: TokenStream, } impl Parser for FromParam { diff --git a/core/src/libs/files/chroot.rs b/core/src/libs/files/chroot.rs index a8bc7d3..8032014 100644 --- a/core/src/libs/files/chroot.rs +++ b/core/src/libs/files/chroot.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::vm::core::destructor::Pool; +use crate::vm::registry::named::Key; +use crate::vm::registry::types::LuaRef; +use crate::vm::value::types::RawPtr; +use crate::vm::Vm; +use bp3d_debug::trace; use std::borrow::Cow; use std::collections::HashMap; use std::ffi::OsStr; use std::fmt::Display; use std::ops::{BitAnd, BitOr}; use std::path::Path; -use bp3d_debug::trace; -use crate::vm::core::destructor::Pool; -use crate::vm::registry::named::Key; -use crate::vm::registry::types::LuaRef; -use crate::vm::value::types::RawPtr; -use crate::vm::Vm; const CHROOT: Key> = Key::new("__chroot__"); @@ -85,7 +85,7 @@ pub fn is_escaping(path: &str) -> bool { level += 1; } } - trace!({level}, "unsandbox {}", path); + trace!({ level }, "unsandbox {}", path); level < 0 } @@ -94,7 +94,7 @@ pub fn unsandbox<'a>(vm: &Vm, path: &'a str) -> Result, SandboxErr return Err(SandboxError); } if path.len() > 0 && path.as_bytes()[0] == b'/' { - return Ok(Cow::Owned(with_chroot(vm, |root| root.join(&path[1..])))) + return Ok(Cow::Owned(with_chroot(vm, |root| root.join(&path[1..])))); } Ok(Cow::Borrowed(Path::new(path))) } @@ -109,7 +109,7 @@ pub fn sandbox<'a>(vm: &Vm, path: &'a Path) -> Result, SandboxError root.len() }); if pos == 0 { - return Err(SandboxError) + return Err(SandboxError); } #[cfg(windows)] let src = &path.as_os_str().as_encoded_bytes()[pos..]; @@ -218,7 +218,7 @@ pub fn access(vm: &Vm, path: &str) -> Permissions { let id = path.rfind('/'); match id { Some(pos) => path = &path[..pos], - None => break + None => break, } } perms.get_permissions("/").unwrap_or(Permissions::NONE) diff --git a/core/src/libs/files/file.rs b/core/src/libs/files/file.rs index e759f9b..c56c7bc 100644 --- a/core/src/libs/files/file.rs +++ b/core/src/libs/files/file.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fs::{File, OpenOptions}; -use std::io::{Read, Seek, Write}; -use bp3d_util::simple_error; -use crate::{decl_lib_func, decl_userdata, impl_userdata_mut}; use crate::libs::files::chroot::Permissions; use crate::libs::files::SandboxPath; use crate::vm::value::types::UInt53; +use crate::{decl_lib_func, decl_userdata, impl_userdata_mut}; +use bp3d_util::simple_error; +use std::fs::{File, OpenOptions}; +use std::io::{Read, Seek, Write}; const MAX_BUF_SIZE: usize = 65535; diff --git a/core/src/libs/files/interface.rs b/core/src/libs/files/interface.rs index 598b812..f9165d0 100644 --- a/core/src/libs/files/interface.rs +++ b/core/src/libs/files/interface.rs @@ -26,10 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use std::ffi::OsStr; -use std::path::{Path, PathBuf}; -use bp3d_debug::error; use crate::libs::files::chroot::{access, sandbox, unsandbox, Permissions, SandboxError}; use crate::libs::files::path::PathWrapper; use crate::util::core::SimpleDrop; @@ -37,24 +33,28 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::util::LuaType; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; +use bp3d_debug::error; +use std::borrow::Cow; +use std::ffi::OsStr; +use std::path::{Path, PathBuf}; #[derive(Debug, Eq, PartialEq)] pub enum SandboxPath<'a> { String(&'a str), - Path(&'a Path) + Path(&'a Path), } #[derive(Debug, Eq, PartialEq)] pub enum SandboxPathBuf { String(String), - Path(PathBuf) + Path(PathBuf), } impl From> for SandboxPathBuf { fn from(value: SandboxPath<'_>) -> Self { match value { SandboxPath::String(v) => SandboxPathBuf::String(v.into()), - SandboxPath::Path(v) => SandboxPathBuf::Path(v.into()) + SandboxPath::Path(v) => SandboxPathBuf::Path(v.into()), } } } @@ -91,7 +91,7 @@ impl SandboxPathBuf { pub fn as_path(&self) -> SandboxPath<'_> { match self { SandboxPathBuf::String(v) => SandboxPath::String(v), - SandboxPathBuf::Path(v) => SandboxPath::Path(v) + SandboxPathBuf::Path(v) => SandboxPath::Path(v), } } @@ -100,7 +100,7 @@ impl SandboxPathBuf { pub fn as_os_str(&self) -> &OsStr { match self { SandboxPathBuf::String(v) => v.as_ref(), - SandboxPathBuf::Path(v) => v.as_os_str() + SandboxPathBuf::Path(v) => v.as_os_str(), } } } @@ -139,7 +139,7 @@ impl SandboxPath<'_> { pub fn as_os_str(&self) -> &OsStr { match self { SandboxPath::String(v) => v.as_ref(), - SandboxPath::Path(v) => v.as_os_str() + SandboxPath::Path(v) => v.as_os_str(), } } @@ -148,15 +148,15 @@ impl SandboxPath<'_> { SandboxPath::String(v) => { unsandbox(vm, v)?; Ok(Cow::Borrowed(v)) - }, - SandboxPath::Path(v) => sandbox(vm, v) + } + SandboxPath::Path(v) => sandbox(vm, v), } } pub fn to_path(&self, vm: &Vm) -> Result, SandboxError> { match self { SandboxPath::String(v) => unsandbox(vm, *v), - SandboxPath::Path(v) => Ok(Cow::Borrowed(v)) + SandboxPath::Path(v) => Ok(Cow::Borrowed(v)), } } @@ -174,7 +174,7 @@ impl SandboxPath<'_> { pub fn is_relative(&self) -> bool { match self { SandboxPath::String(v) => !v.starts_with("/"), - SandboxPath::Path(v) => !v.starts_with("/") + SandboxPath::Path(v) => !v.starts_with("/"), } } } @@ -183,7 +183,7 @@ impl<'a> FromLua<'a> for SandboxPath<'a> { unsafe fn from_lua_unchecked(vm: &'a Vm, index: i32) -> Self { let wrapper: crate::vm::Result<&PathWrapper> = FromLua::from_lua(vm, index); if let Ok(wrapper) = wrapper { - return SandboxPath::Path(wrapper.path()) + return SandboxPath::Path(wrapper.path()); } let s: &str = FromLua::from_lua_unchecked(vm, index); SandboxPath::String(s) @@ -192,7 +192,7 @@ impl<'a> FromLua<'a> for SandboxPath<'a> { fn from_lua(vm: &'a Vm, index: i32) -> crate::vm::Result { let wrapper: crate::vm::Result<&PathWrapper> = FromLua::from_lua(vm, index); if let Ok(wrapper) = wrapper { - return Ok(SandboxPath::Path(wrapper.path())) + return Ok(SandboxPath::Path(wrapper.path())); } let s: &str = FromLua::from_lua(vm, index)?; Ok(SandboxPath::String(s)) @@ -212,8 +212,11 @@ impl FromLua<'_> for SandboxPathBuf { unsafe impl IntoLua for SandboxPath<'_> { fn into_lua(self, vm: &Vm) -> u16 { match self { - SandboxPath::String(v) => unsandbox(vm, v).map(|path| PathWrapper::new(path.into())).ok().into_lua(vm), - SandboxPath::Path(v) => PathWrapper::new(v.into()).into_lua(vm) + SandboxPath::String(v) => unsandbox(vm, v) + .map(|path| PathWrapper::new(path.into())) + .ok() + .into_lua(vm), + SandboxPath::Path(v) => PathWrapper::new(v.into()).into_lua(vm), } } } @@ -226,7 +229,7 @@ impl<'a> FromParam<'a> for SandboxPath<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { let wrapper: Option<&PathWrapper> = FromParam::try_from_param(vm, index); if let Some(wrapper) = wrapper { - return SandboxPath::Path(wrapper.path()) + return SandboxPath::Path(wrapper.path()); } let s: &str = FromParam::from_param(vm, index); SandboxPath::String(s) @@ -235,7 +238,7 @@ impl<'a> FromParam<'a> for SandboxPath<'a> { fn try_from_param(vm: &'a Vm, index: i32) -> Option { let wrapper: Option<&PathWrapper> = FromParam::try_from_param(vm, index); if let Some(wrapper) = wrapper { - return Some(SandboxPath::Path(wrapper.path())) + return Some(SandboxPath::Path(wrapper.path())); } let wrapper: Option<&str> = FromParam::try_from_param(vm, index); wrapper.map(|v| SandboxPath::String(v)) diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index d4ef72c..94ed831 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::fs::File; -use std::io::Read; -use bp3d_util::simple_error; use crate::decl_lib_func; use crate::libs::files::chroot::{access, sandbox, Permissions}; use crate::libs::files::SandboxPath; use crate::vm::table::Table; +use bp3d_util::simple_error; +use std::fs::File; +use std::io::Read; const MAX_FILE_SIZE: usize = 5000000; //5Mb diff --git a/core/src/libs/files/mod.rs b/core/src/libs/files/mod.rs index a8a273d..9de99be 100644 --- a/core/src/libs/files/mod.rs +++ b/core/src/libs/files/mod.rs @@ -26,18 +26,18 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -mod interface; -mod path; pub mod chroot; -mod lib; mod file; +mod interface; +mod lib; +mod path; -pub use interface::{SandboxPath, SandboxPathBuf}; use crate::libs::files::file::FileWrapper; use crate::libs::files::path::PathWrapper; use crate::libs::Lib; use crate::util::Namespace; use crate::vm::function::types::RFunction; +pub use interface::{SandboxPath, SandboxPathBuf}; pub struct Files; @@ -56,7 +56,7 @@ impl Lib for Files { ("list", RFunction::wrap(lib::list)), ("createDir", RFunction::wrap(lib::create_dir)), ("deleteDir", RFunction::wrap(lib::delete_dir)), - ("access", RFunction::wrap(lib::lua_access)) + ("access", RFunction::wrap(lib::lua_access)), ]) } } diff --git a/core/src/libs/files/path.rs b/core/src/libs/files/path.rs index e244e12..978d629 100644 --- a/core/src/libs/files/path.rs +++ b/core/src/libs/files/path.rs @@ -26,11 +26,11 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::borrow::Cow; -use std::path::PathBuf; -use crate::{decl_lib_func, decl_userdata, impl_userdata}; use crate::libs::files::chroot::{sandbox, unsandbox, SandboxError}; use crate::libs::files::SandboxPath; +use crate::{decl_lib_func, decl_userdata, impl_userdata}; +use std::borrow::Cow; +use std::path::PathBuf; decl_userdata!(pub struct PathWrapper(PathBuf)); diff --git a/core/src/libs/lua/load.rs b/core/src/libs/lua/load.rs index 5c251d4..eef364f 100644 --- a/core/src/libs/lua/load.rs +++ b/core/src/libs/lua/load.rs @@ -26,16 +26,16 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use crate::decl_lib_func; +use crate::libs::files::chroot::Permissions; +use crate::libs::files::SandboxPath; use crate::libs::interface::Lib; use crate::util::Namespace; use crate::vm::core::load::{Code, Script}; use crate::vm::function::types::RFunction; use crate::vm::value::any::{AnyParam, UncheckedAnyReturn}; use crate::vm::value::types::Function; -use crate::decl_lib_func; use bp3d_util::simple_error; -use crate::libs::files::chroot::Permissions; -use crate::libs::files::SandboxPath; decl_lib_func! { fn run_string(vm: &Vm, s: &str, chunkname: Option<&str>) -> crate::vm::Result { diff --git a/core/src/libs/lua/options.rs b/core/src/libs/lua/options.rs index 6b80087..2c3aec7 100644 --- a/core/src/libs/lua/options.rs +++ b/core/src/libs/lua/options.rs @@ -49,11 +49,6 @@ impl Lua { } pub fn build(self) -> impl Lib { - ( - Base, - Call, - Load, - Require(self.provider.unwrap_or_default()), - ) + (Base, Call, Load, Require(self.provider.unwrap_or_default())) } } diff --git a/core/tests/test_vm_files.rs b/core/tests/test_vm_files.rs index 1f0d3c7..35c65e0 100644 --- a/core/tests/test_vm_files.rs +++ b/core/tests/test_vm_files.rs @@ -26,21 +26,25 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::path::Path; use bp3d_lua::libs::files::chroot::{access, set_access, set_chroot, Permissions}; use bp3d_lua::libs::files::{Files, SandboxPath}; use bp3d_lua::libs::Lib; use bp3d_lua::vm::RootVm; +use std::path::Path; #[test] fn test_vm_files() { let vm = RootVm::new(); Files.register(&vm).unwrap(); set_chroot(&vm, Path::new("/root")); // for testing only - let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path: SandboxPath = vm + .run_code(c"return bp3d.files.Path.new('/myfile')") + .unwrap(); let path = path.to_path(&vm).unwrap(); assert_eq!(path, Path::new("/root/myfile")); - let spath: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/data'):join('myfile'):withExtension('txt')").unwrap(); + let spath: SandboxPath = vm + .run_code(c"return bp3d.files.Path.new('/data'):join('myfile'):withExtension('txt')") + .unwrap(); let path = spath.to_path(&vm).unwrap(); assert_eq!(path, Path::new("/root/data/myfile.txt")); let path = spath.to_str(&vm).unwrap(); @@ -55,13 +59,20 @@ fn test_vm_files_security() { let vm = RootVm::new(); Files.register(&vm).unwrap(); set_chroot(&vm, Path::new("/root")); // for testing only - vm.run_code::<()>(c"return bp3d.files.Path.new('../myfile')").unwrap_err(); - vm.run_code::<()>(c"return bp3d.files.Path.new('/../myfile')").unwrap_err(); - vm.run_code::<()>(c"return bp3d.files.Path.new('/./../myfile')").unwrap_err(); - vm.run_code::<()>(c"return bp3d.files.Path.new('.././myfile/.')").unwrap_err(); - vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../myfile')").unwrap(); - vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../../myfile')").unwrap_err(); - vm.run_code::<()>(c"return bp3d.files.Path.new('/../data/myfile')").unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('../myfile')") + .unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/../myfile')") + .unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/./../myfile')") + .unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('.././myfile/.')") + .unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../myfile')") + .unwrap(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/data/../../myfile')") + .unwrap_err(); + vm.run_code::<()>(c"return bp3d.files.Path.new('/../data/myfile')") + .unwrap_err(); } #[test] @@ -71,17 +82,27 @@ fn test_vm_files_permissions() { set_chroot(&vm, Path::new("/root")); // for testing only set_access(&vm, "/rodata", Permissions::R); set_access(&vm, "/rwdata", Permissions::R | Permissions::W); - set_access(&vm, "/data/myfile.lua", Permissions::R | Permissions::W | Permissions::X); + set_access( + &vm, + "/data/myfile.lua", + Permissions::R | Permissions::W | Permissions::X, + ); assert_eq!(access(&vm, "/rodata/myfile.txt"), Permissions::R); assert_eq!(access(&vm, "/rodata.txt"), Permissions::NONE); assert_eq!(access(&vm, "/"), Permissions::NONE); assert_eq!(access(&vm, "/rodata"), Permissions::R); assert_eq!(access(&vm, "/rwdata"), Permissions::R | Permissions::W); - assert_eq!(access(&vm, "/rwdata/myfile.txt"), Permissions::R | Permissions::W); + assert_eq!( + access(&vm, "/rwdata/myfile.txt"), + Permissions::R | Permissions::W + ); assert_eq!(access(&vm, "/data"), Permissions::NONE); assert_eq!(access(&vm, "/data/myfile"), Permissions::NONE); assert_eq!(access(&vm, "/data/myfile.txt"), Permissions::NONE); - assert_eq!(access(&vm, "/data/myfile.lua"), Permissions::R | Permissions::W | Permissions::X); + assert_eq!( + access(&vm, "/data/myfile.lua"), + Permissions::R | Permissions::W | Permissions::X + ); } #[test] @@ -92,10 +113,22 @@ fn test_vm_files_permissions2() { set_access(&vm, "/", Permissions::R); set_access(&vm, "/target", Permissions::R | Permissions::W); set_access(&vm, "/bp3d-build", Permissions::R | Permissions::X); - assert_eq!(access(&vm, "/target/myfile"), Permissions::R | Permissions::W); - assert_eq!(access(&vm, "/bp3d-build/myfile.lua"), Permissions::R | Permissions::X); - assert_eq!(access(&vm, "/bp3d-build/package/myfile.lua"), Permissions::R | Permissions::X); - assert_eq!(access(&vm, "/target/aarch64/data/1/myfile"), Permissions::R | Permissions::W); + assert_eq!( + access(&vm, "/target/myfile"), + Permissions::R | Permissions::W + ); + assert_eq!( + access(&vm, "/bp3d-build/myfile.lua"), + Permissions::R | Permissions::X + ); + assert_eq!( + access(&vm, "/bp3d-build/package/myfile.lua"), + Permissions::R | Permissions::X + ); + assert_eq!( + access(&vm, "/target/aarch64/data/1/myfile"), + Permissions::R | Permissions::W + ); assert_eq!(access(&vm, "/myfile"), Permissions::R); assert_eq!(access(&vm, "/obj/data/1/myfile"), Permissions::R); } @@ -105,7 +138,9 @@ fn test_vm_simple_chroot() { let vm = RootVm::new(); Files.register(&vm).unwrap(); set_chroot(&vm, Path::new(".")); - let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path: SandboxPath = vm + .run_code(c"return bp3d.files.Path.new('/myfile')") + .unwrap(); let path = path.to_path(&vm).unwrap(); assert_eq!(path, Path::new("./myfile")); } @@ -115,7 +150,9 @@ fn test_vm_simple_chroot2() { let vm = RootVm::new(); Files.register(&vm).unwrap(); set_chroot(&vm, Path::new("./")); - let path: SandboxPath = vm.run_code(c"return bp3d.files.Path.new('/myfile')").unwrap(); + let path: SandboxPath = vm + .run_code(c"return bp3d.files.Path.new('/myfile')") + .unwrap(); let path = path.to_path(&vm).unwrap(); assert_eq!(path, Path::new("./myfile")); } diff --git a/shell/core/src/lua.rs b/shell/core/src/lua.rs index 8798424..cdc540f 100644 --- a/shell/core/src/lua.rs +++ b/shell/core/src/lua.rs @@ -34,6 +34,7 @@ use crate::scheduler::SchedulerPtr; use bp3d_debug::{debug, error, info, trace, warning}; use bp3d_lua::libs; use bp3d_lua::libs::Lib; +use bp3d_lua::libs::files::chroot::set_chroot; use bp3d_lua::libs::lua::Module; use bp3d_lua::vm::Vm; use bp3d_lua::vm::core::interrupt::{Signal, spawn_interruptible}; @@ -47,7 +48,6 @@ use std::rc::Rc; use std::thread::JoinHandle; use std::time::Duration; use tokio::sync::mpsc; -use bp3d_lua::libs::files::chroot::set_chroot; const CHANNEL_BUFFER: usize = 32; @@ -131,10 +131,7 @@ impl Lua { error!("Failed to load util library: {}", e); } set_chroot(&vm, &args.data); - if let Err(e) = libs::lua::Lua::new() - .build() - .register(vm) - { + if let Err(e) = libs::lua::Lua::new().build().register(vm) { error!("Failed to load base library: {}", e); } info!("Loading bp3d-lua-shell library..."); From 6e159416888e6fa82157f00f0e9704f470d7c85d Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Mon, 22 Dec 2025 15:33:52 +0100 Subject: [PATCH 513/527] Added startsWith and endsWith to bp3d.util.string --- core/src/libs/util/string.rs | 14 ++++++++++++++ definitions/bp3d.util.lua | 14 ++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/core/src/libs/util/string.rs b/core/src/libs/util/string.rs index a611ad2..516b037 100644 --- a/core/src/libs/util/string.rs +++ b/core/src/libs/util/string.rs @@ -34,6 +34,18 @@ use crate::vm::table::Table; use bp3d_util::string::BufTools; use std::borrow::Cow; +decl_lib_func! { + fn starts_with(src: &[u8], prefix: &[u8]) -> bool { + src.starts_with(prefix) + } +} + +decl_lib_func! { + fn ends_with(src: &[u8], suffix: &[u8]) -> bool { + src.ends_with(suffix) + } +} + decl_lib_func! { fn contains(src: &[u8], needle: &[u8]) -> bool { if needle.is_empty() { @@ -78,6 +90,8 @@ impl Lib for String { ("split", RFunction::wrap(split)), ("capitalise", RFunction::wrap(capitalise)), ("decapitalise", RFunction::wrap(decapitalise)), + ("startsWith", RFunction::wrap(starts_with)), + ("endsWith", RFunction::wrap(ends_with)), ]) } } diff --git a/definitions/bp3d.util.lua b/definitions/bp3d.util.lua index bf07e89..2cd095d 100644 --- a/definitions/bp3d.util.lua +++ b/definitions/bp3d.util.lua @@ -35,6 +35,20 @@ bp3d.util.table = {} bp3d.util.utf8 = {} bp3d.util.num = {} +--- Checks if the given `src` string starts with the prefix `prefix`. +--- +--- @param src string the source string. +--- @param prefix string the string to search for. +--- @return boolean whatever true if `src` starts with `prefix`, false otherwise. +bp3d.util.string.startsWith = function(src, prefix) end + +--- Checks if the given `src` string ends with the suffix `suffix`. +--- +--- @param src string the source string. +--- @param suffix string the string to search for. +--- @return boolean whatever true if `src` ends with `suffix`, false otherwise. +bp3d.util.string.endsWith = function(src, suffix) end + --- Checks if the given sub string `needle` can be found in `src`. --- --- @param src string the source string. From 7092a79d6f855c1ab9e3c697fd961221ff770ae2 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 22:00:08 +0100 Subject: [PATCH 514/527] Fixed possible safety issue with context system --- core/src/vm/closure/context.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index d983411..846c395 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -42,11 +42,21 @@ pub struct Cell { ptr: *mut *const T, } +#[cfg(feature = "send")] +impl Cell { + pub fn new(ctx: Context) -> Self { + Self { ptr: ctx.ptr } + } +} + +#[cfg(not(feature = "send"))] impl Cell { pub fn new(ctx: Context) -> Self { Self { ptr: ctx.ptr } } +} +impl Cell { pub fn bind<'a>(&mut self, obj: &'a T) -> Guard<'a, T> { unsafe { *self.ptr = obj as _ }; Guard { @@ -60,11 +70,21 @@ pub struct CellMut { ptr: *mut *const T, } +#[cfg(feature = "send")] +impl CellMut { + pub fn new(ctx: ContextMut) -> Self { + Self { ptr: ctx.0.ptr } + } +} + +#[cfg(not(feature = "send"))] impl CellMut { pub fn new(ctx: ContextMut) -> Self { Self { ptr: ctx.0.ptr } } +} +impl CellMut { pub fn bind<'a>(&mut self, obj: &'a mut T) -> Guard<'a, T> { unsafe { *self.ptr = obj as _ }; Guard { From fb91e4ec9fe0f16fdab0f98adf0356bc589a771f Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 22:01:21 +0100 Subject: [PATCH 515/527] Updated error messages --- core/tests/safety/test_scope_safe_2.stderr | 6 +++++ .../safety/test_vm_threads_unsafe_type.stderr | 23 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/core/tests/safety/test_scope_safe_2.stderr b/core/tests/safety/test_scope_safe_2.stderr index 4cb3a04..cdac37f 100644 --- a/core/tests/safety/test_scope_safe_2.stderr +++ b/core/tests/safety/test_scope_safe_2.stderr @@ -14,3 +14,9 @@ error[E0597]: `vm` does not live long enough 36 | }); 37 | } | - `vm` dropped here while still borrowed + | +note: requirements that the value outlives `'static` introduced here + --> src/vm/core/vm.rs + | + | pub fn scope crate::vm::Result>( + | ^^^^^^^ ^^^^^^^^^^^^^^^^^^^^ diff --git a/core/tests/safety/test_vm_threads_unsafe_type.stderr b/core/tests/safety/test_vm_threads_unsafe_type.stderr index 850f173..77b5286 100644 --- a/core/tests/safety/test_vm_threads_unsafe_type.stderr +++ b/core/tests/safety/test_vm_threads_unsafe_type.stderr @@ -10,6 +10,20 @@ error[E0597]: `vm` does not live long enough | ----------------------------- argument requires that `vm` is borrowed for `'static` 38 | } | - `vm` dropped here while still borrowed + | +note: requirements that the value outlives `'static` introduced here + --> src/vm/core/vm.rs + | + | pub fn get_global<'a, R: FromLua<'a>>(&'a self, name: impl AnyStr) -> crate::vm::Result { + | ^^^^^^^^^^^ + | + ::: src/vm/thread/core.rs + | + | pub fn resume<'b, T: FromLua<'b>>(&'b self, args: impl IntoLua) -> crate::vm::Result> + | ^^^^^^^^^^^ + | where + | T: 'static, /* This clause ensures that a future call to collectgarbage or resume does + | ^^^^^^^ error[E0597]: `thread` does not live long enough --> tests/safety/test_vm_threads_unsafe_type.rs:37:19 @@ -24,3 +38,12 @@ error[E0597]: `thread` does not live long enough | argument requires that `thread` is borrowed for `'static` 38 | } | - `thread` dropped here while still borrowed + | +note: requirements that the value outlives `'static` introduced here + --> src/vm/thread/core.rs + | + | pub fn resume<'b, T: FromLua<'b>>(&'b self, args: impl IntoLua) -> crate::vm::Result> + | ^^^^^^^^^^^ + | where + | T: 'static, /* This clause ensures that a future call to collectgarbage or resume does + | ^^^^^^^ From 5240ee5129cb8be8b1dfc2ae05421efe42394667 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 23:09:08 +0100 Subject: [PATCH 516/527] Added function to access raw pointer from AnyUserData --- core/src/vm/userdata/any.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index e12f170..a15ea49 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -26,6 +26,7 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +use std::ffi::c_void; use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{ lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type, @@ -121,6 +122,11 @@ impl<'a> AnyUserData<'a> { Self { vm, index } } + /// Returns the underlying userdata pointer. + pub fn as_ptr(&self) -> *const c_void { + unsafe { lua_topointer(self.vm.as_ptr(), self.index) } + } + /// Returns a unique identifier to that table across the Vm it is attached to. pub fn uid(&self) -> usize { unsafe { lua_topointer(self.vm.as_ptr(), self.index) as _ } @@ -247,6 +253,11 @@ impl<'a> ImmutableAnyUserData<'a> { Self(AnyUserData::from_raw(vm, index)) } + /// Returns the underlying userdata pointer. + pub fn as_ptr(&self) -> *const c_void { + self.0.as_ptr() + } + /// Returns a unique identifier to that table across the Vm it is attached to. #[inline(always)] pub fn uid(&self) -> usize { From ce8afcbc571f9b5f28385ff2c35d7e434a4b83d6 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 23:15:20 +0100 Subject: [PATCH 517/527] Replace check_push_value by check_value_top to avoid duplicating values uselessly in AnyUserData --- core/src/vm/userdata/any.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index a15ea49..5fee9f0 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -38,7 +38,7 @@ use crate::vm::table::ImmutableTable; use crate::vm::userdata::{UserData, UserDataImmutable}; use crate::vm::util::LuaType; use crate::vm::value::types::Function; -use crate::vm::value::util::{check_get_metatable, check_push_value}; +use crate::vm::value::util::{check_get_metatable, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; use std::fmt::{Debug, Display}; @@ -213,7 +213,7 @@ impl<'a> FromLua<'a> for AnyUserData<'a> { unsafe impl IntoLua for &AnyUserData<'_> { #[inline(always)] fn into_lua(self, vm: &Vm) -> u16 { - check_push_value(self.vm, vm, self.index) + check_value_top(self.vm, vm, self.index) } } From 247da73ffa884b07db9d014d0c1cff1753d44544 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 23:15:50 +0100 Subject: [PATCH 518/527] Added implementation of Upvalue for AnyUserData --- core/src/vm/closure/core.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index a067080..4ddd308 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -34,6 +34,7 @@ use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::ffi::OsStr; use std::path::Path; +use crate::vm::userdata::AnyUserData; macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { @@ -141,3 +142,20 @@ impl IntoUpvalue for &Path { impl Upvalue for &Path { type From<'a> = &'a Path; } + +impl<'a> FromUpvalue<'a> for AnyUserData<'a> { + #[inline(always)] + unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { + AnyUserData::from_raw(vm, GLOBALSINDEX - index) + } +} + +impl IntoUpvalue for AnyUserData<'_> { + fn into_upvalue(self, vm: &Vm) -> u16 { + self.into_lua(vm) + } +} + +impl Upvalue for AnyUserData<'_> { + type From<'a> = AnyUserData<'a>; +} From c5e1a2527e471657480016c396c58629727e27db Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 23:27:31 +0100 Subject: [PATCH 519/527] Added implementation of FromParam for () --- core/src/vm/function/core.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/core/src/vm/function/core.rs b/core/src/vm/function/core.rs index 26c7ceb..c1f9db1 100644 --- a/core/src/vm/function/core.rs +++ b/core/src/vm/function/core.rs @@ -108,6 +108,20 @@ impl<'a> FromParam<'a> for &'a [u8] { } } +unsafe impl SimpleDrop for () {} + +impl LuaType for () {} + +impl FromParam<'_> for () { + unsafe fn from_param(_: &'_ Vm, _: i32) -> Self { + () + } + + fn try_from_param(_: &'_ Vm, _: i32) -> Option { + Some(()) + } +} + unsafe impl IntoParam for &str { #[inline(always)] fn into_param(self, vm: &Vm) -> i32 { From 0a382bfe9046ba8355925b73cac3dd41ae4c201b Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sat, 27 Dec 2025 23:36:57 +0100 Subject: [PATCH 520/527] Added initial version of new from_rust_temporary closure system --- core/src/vm/closure/mod.rs | 1 + core/src/vm/closure/rust.rs | 124 ++++++++++++++++++ core/src/vm/closure/types.rs | 54 +------- .../safety/test_rclosure_not_send.stderr | 8 +- core/tests/test_vm_closures.rs | 30 +++++ 5 files changed, 163 insertions(+), 54 deletions(-) create mode 100644 core/src/vm/closure/rust.rs diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 00aa348..80648a8 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -32,5 +32,6 @@ mod core; mod interface; pub mod rc; pub mod types; +mod rust; pub use interface::*; diff --git a/core/src/vm/closure/rust.rs b/core/src/vm/closure/rust.rs new file mode 100644 index 0000000..5c27ec9 --- /dev/null +++ b/core/src/vm/closure/rust.rs @@ -0,0 +1,124 @@ +// Copyright (c) 2025, BlockProject 3D +// +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without modification, +// are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright notice, +// this list of conditions and the following disclaimer in the documentation +// and/or other materials provided with the distribution. +// * Neither the name of BlockProject 3D nor the names of its contributors +// may be used to endorse or promote products derived from this software +// without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +// EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +use std::marker::PhantomData; +use crate::ffi::laux::luaL_error; +use crate::ffi::lua::{lua_newuserdata, lua_touserdata, State}; +use crate::vm::closure::FromUpvalue; +use crate::vm::closure::types::RClosure; +use crate::vm::function::{FromParam, IntoParam}; +use crate::vm::userdata::AnyUserData; +use crate::vm::value::types::RawPtr; +use crate::vm::Vm; + +pub struct Guard<'a, F> { + ptr: *mut *const F, + #[allow(unused)] // Hold the box until the guard should drop and delete the Box with it. + bx: Box, + vm: PhantomData<&'a Vm> +} + +impl<'a, F> Drop for Guard<'a, F> { + fn drop(&mut self) { + unsafe { *self.ptr = std::ptr::null() }; + } +} + +impl<'a> RClosure> { + pub fn from_rust_temporary R + 'a>(vm: &'a Vm, fun: F) -> (RClosure>, Guard<'a, F>) + where + for<'b> T: FromParam<'b>, + R: IntoParam + { + let mut bx = Box::new(fun); + let rptr = &mut *bx as *mut F; + unsafe { lua_newuserdata(vm.as_ptr(), size_of::<*mut F>()) }; + let ptr = unsafe { lua_touserdata(vm.as_ptr(), -1) } as *mut *const F; + unsafe { *ptr = rptr }; + let value = unsafe { AnyUserData::from_raw(vm, vm.top()) }; + extern "C-unwind" fn _cfunc R>(l: State) -> i32 + where + for<'a> T: FromParam<'a>, + R: IntoParam, + { + let vm = unsafe { Vm::from_raw(l) }; + let upvalue: RawPtr<*mut F> = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let ptr = unsafe { *upvalue.as_ptr() }; + if ptr.is_null() { + unsafe { luaL_error(vm.as_ptr(), c"Attempt to call a dropped temporary rust closure".as_ptr()) }; + unsafe { std::hint::unreachable_unchecked() }; + } + let res = unsafe { (*ptr)(args) }; + res.into_param(&vm) as _ + } + (RClosure::new(_cfunc::, value), Guard { ptr, bx, vm: PhantomData }) + } +} + +impl RClosure> { + fn __from_rust R + 'static>(ptr: *mut F) -> Self + where + for<'a> T: FromParam<'a>, + R: IntoParam + { + extern "C-unwind" fn _cfunc R>(l: State) -> i32 + where + for<'a> T: FromParam<'a>, + R: IntoParam, + { + let vm = unsafe { Vm::from_raw(l) }; + let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let res = unsafe { (*upvalue.as_ptr())(args) }; + res.into_param(&vm) as _ + } + RClosure::new(_cfunc::, RawPtr::new(ptr as _)) + } + + #[cfg(feature = "send")] + pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + where + for<'a> T: FromParam<'a>, + R: IntoParam, + F: Send, + { + let ptr = crate::vm::core::destructor::Pool::attach_send(vm, Box::new(fun)); + Self::__from_rust(ptr) + } + + #[cfg(not(feature = "send"))] + pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + where + for<'a> T: FromParam<'a>, + R: IntoParam, + { + let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); + Self::__from_rust(ptr) + } +} diff --git a/core/src/vm/closure/types.rs b/core/src/vm/closure/types.rs index 5525af2..6bcbed0 100644 --- a/core/src/vm/closure/types.rs +++ b/core/src/vm/closure/types.rs @@ -26,13 +26,13 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use crate::ffi::lua::{lua_pushcclosure, CFunction, State}; -use crate::vm::closure::{FromUpvalue, IntoUpvalue}; -use crate::vm::function::{FromParam, IntoParam}; -use crate::vm::value::types::RawPtr; +use crate::ffi::lua::{lua_pushcclosure, CFunction}; +use crate::vm::closure::IntoUpvalue; use crate::vm::value::IntoLua; use crate::vm::Vm; +pub use super::rust::Guard as RClosureGuard; + pub struct RClosure { func: CFunction, upvalue: T, @@ -59,49 +59,3 @@ unsafe impl IntoLua for RClosure { 1 } } - -impl RClosure> { - #[cfg(feature = "send")] - pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self - where - for<'a> T: FromParam<'a>, - R: IntoParam, - F: Send, - { - let ptr = crate::vm::core::destructor::Pool::attach_send(vm, Box::new(fun)); - extern "C-unwind" fn _cfunc R>(l: State) -> i32 - where - for<'a> T: FromParam<'a>, - R: IntoParam, - F: Send, - { - let vm = unsafe { Vm::from_raw(l) }; - let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; - let args: T = unsafe { FromParam::from_param(&vm, 1) }; - let res = unsafe { (*upvalue.as_ptr())(args) }; - res.into_param(&vm) as _ - } - RClosure::new(_cfunc::, RawPtr::new(ptr as _)) - } - - #[cfg(not(feature = "send"))] - pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self - where - for<'a> T: FromParam<'a>, - R: IntoParam, - { - let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); - extern "C-unwind" fn _cfunc R>(l: State) -> i32 - where - for<'a> T: FromParam<'a>, - R: IntoParam, - { - let vm = unsafe { Vm::from_raw(l) }; - let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; - let args: T = unsafe { FromParam::from_param(&vm, 1) }; - let res = unsafe { (*upvalue.as_ptr())(args) }; - res.into_param(&vm) as _ - } - RClosure::new(_cfunc::, RawPtr::new(ptr as _)) - } -} diff --git a/core/tests/safety/test_rclosure_not_send.stderr b/core/tests/safety/test_rclosure_not_send.stderr index bdde115..a463f7c 100644 --- a/core/tests/safety/test_rclosure_not_send.stderr +++ b/core/tests/safety/test_rclosure_not_send.stderr @@ -24,11 +24,11 @@ note: required because it's used within this closure | 40 | RClosure::from_rust(&vm, move |val: f32| format!("this is a test: {} - {:?}", val, ty)); | ^^^^^^^^^^^^^^^ -note: required by a bound in `RClosure::>::from_rust` - --> src/vm/closure/types.rs +note: required by a bound in `closure::rust::>>::from_rust` + --> src/vm/closure/rust.rs | | pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self | --------- required by a bound in this associated function ... - | F: Send, - | ^^^^ required by this bound in `RClosure::>::from_rust` + | F: Send, + | ^^^^ required by this bound in `closure::rust::>>::from_rust` diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 1c75e18..192186f 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -28,6 +28,7 @@ #![cfg(all(feature = "root-vm", feature = "util-namespace"))] +use std::cell::Cell; use bp3d_lua::decl_closure; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; @@ -144,3 +145,32 @@ fn test_vm_context() { assert_eq!(obj.value3.len(), 0); assert_eq!(top, vm.top()); } + +#[test] +fn test_vm_rust_closure_2() { + let vm = RootVm::new(); + let top = vm.top(); + let (closure, _guard) = RClosure::from_rust_temporary(&vm, |val: f32| format!("this is a test: {}", val)); + vm.set_global(c"test", closure).unwrap(); + assert_eq!(top, vm.top()); + let s: &str = vm.run_code(c"return test(42.42)").unwrap(); + assert_eq!(s, "this is a test: 42.42"); +} + +#[test] +fn test_vm_rust_closure_3() { + let vm = RootVm::new(); + let value = Cell::new(0); + vm.scope(|vm| { + let br = &value; + let (fun, _guard) = RClosure::from_rust_temporary(vm, |()| br.get()); + vm.set_global(c"test", fun)?; + let (fun2, _guard) = RClosure::from_rust_temporary(vm, |val: i32| br.set(val)); + vm.set_global(c"test2", fun2)?; + vm.run_code::<()>(c"assert(test() == 0)")?; + vm.run_code::<()>(c"test2(42)") + }).unwrap(); + assert!(vm.run_code::<()>(c"test()").is_err()); + assert!(vm.run_code::<()>(c"test2(4242)").is_err()); + assert_eq!(value.get(), 42); +} From cdd57482dee8a0db28d4a146132c7eae7f9e100c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 30 Dec 2025 12:21:41 +0100 Subject: [PATCH 521/527] Fixed safety issue in context system --- core/src/vm/closure/context.rs | 95 +++++++++++++++++++++++++++++++--- core/tests/test_vm_closures.rs | 6 +-- testbin/src/context.rs | 4 +- testbin/src/context_opt.rs | 6 +-- 4 files changed, 95 insertions(+), 16 deletions(-) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index 846c395..caa9ecd 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -64,6 +64,34 @@ impl Cell { ud: self.ptr, } } + + /// Unsafely binds a reference to this [Cell]. + /// + /// # Arguments + /// + /// * `obj`: the reference to the context object. + /// + /// returns: () + /// + /// # Safety + /// + /// The given object must be valid until a call to unbind, the [bind](Cell::bind) function must + /// also never be called until a call to [unbind_unchecked](Cell::unbind_unchecked). If any of + /// these constrains are not satisfied, this function is UB. + #[inline(always)] + pub unsafe fn bind_unchecked(&mut self, obj: &T) { + *self.ptr = obj as _; + } + + /// Unsafely unbinds the current reference bound in this [Cell]. + /// + /// # Safety + /// + /// No [Guard] should exist at this point otherwise this is UB. + #[inline(always)] + pub unsafe fn unbind_unchecked(&mut self) { + *self.ptr = std::ptr::null(); + } } pub struct CellMut { @@ -92,6 +120,34 @@ impl CellMut { ud: self.ptr, } } + + /// Unsafely binds a reference to this [CellMut]. + /// + /// # Arguments + /// + /// * `obj`: the reference to the context object. + /// + /// returns: () + /// + /// # Safety + /// + /// The given object must be valid until a call to unbind, the [bind](CellMut::bind) function must + /// also never be called until a call to [unbind_unchecked](CellMut::unbind_unchecked). If any of + /// these constrains are not satisfied, this function is UB. + #[inline(always)] + pub unsafe fn bind_unchecked(&mut self, obj: &mut T) { + *self.ptr = obj as _; + } + + /// Unsafely unbinds the current reference bound in this [CellMut]. + /// + /// # Safety + /// + /// No [Guard] should exist at this point otherwise this is UB. + #[inline(always)] + pub unsafe fn unbind_unchecked(&mut self) { + *self.ptr = std::ptr::null(); + } } pub struct Context { @@ -156,7 +212,18 @@ impl Drop for Guard<'_, T> { pub struct Ref<'a, T>(&'a T); #[repr(transparent)] -pub struct Mut<'a, T>(&'a mut T); +pub struct MutPtr(*mut *mut T); + +impl MutPtr { + pub fn borrow<'a>(self) -> Mut<'a, T> { + let value = unsafe { &mut **self.0 }; + unsafe { *self.0 = std::ptr::null_mut() }; + Mut { + value, + ptr: self.0 + } + } +} impl Deref for Ref<'_, T> { type Target = T; @@ -167,24 +234,36 @@ impl Deref for Ref<'_, T> { } } +pub struct Mut<'a, T> { + value: &'a mut T, + ptr: *mut *mut T +} + +impl<'a, T> Drop for Mut<'a, T> { + #[inline(always)] + fn drop(&mut self) { + unsafe { *self.ptr = self.value as *mut _ }; + } +} + impl Deref for Mut<'_, T> { type Target = T; #[inline(always)] fn deref(&self) -> &Self::Target { - self.0 + self.value } } impl DerefMut for Mut<'_, T> { #[inline(always)] fn deref_mut(&mut self) -> &mut Self::Target { - self.0 + self.value } } unsafe impl SimpleDrop for Ref<'_, T> {} -unsafe impl SimpleDrop for Mut<'_, T> {} +unsafe impl SimpleDrop for MutPtr {} impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { @@ -197,11 +276,11 @@ impl<'a, T: 'static> FromUpvalue<'a> for Ref<'a, T> { // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Ref(unsafe { &**ptr.as_ptr() }) + Ref(&**ptr.as_ptr()) } } -impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { +impl<'a, T: 'static> FromUpvalue<'a> for MutPtr { unsafe fn from_upvalue(vm: &'a Vm, index: i32) -> Self { let ptr: RawPtr<*mut T> = FromUpvalue::from_upvalue(vm, index); if (*ptr.as_ptr()).is_null() { @@ -212,7 +291,7 @@ impl<'a, T: 'static> FromUpvalue<'a> for Mut<'a, T> { // luaL_error raises a lua exception and unwinds, so this cannot be reached. std::hint::unreachable_unchecked(); } - Mut(unsafe { &mut **ptr.as_ptr() }) + MutPtr(ptr.as_mut_ptr()) } } @@ -235,5 +314,5 @@ impl Upvalue for Context { } impl Upvalue for ContextMut { - type From<'a> = Mut<'a, T>; + type From<'a> = MutPtr; } diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index 192186f..d64ba78 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -42,21 +42,21 @@ struct TestContext { decl_closure! { fn context_set_value |ctx: ContextMut| (val: i32) -> () { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value = val; } } decl_closure! { fn context_push |ctx: ContextMut| (val: u64) -> () { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value3.push(val); } } decl_closure! { fn context_pop |ctx: ContextMut| () -> Option { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value3.pop() } } diff --git a/testbin/src/context.rs b/testbin/src/context.rs index 52a50ef..169b1b2 100644 --- a/testbin/src/context.rs +++ b/testbin/src/context.rs @@ -38,14 +38,14 @@ struct TestContext { decl_closure! { fn context_push |ctx: ContextMut| (val: u64) -> () { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value3.push(val); } } decl_closure! { fn context_pop |ctx: ContextMut| () -> Option { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value3.pop() } } diff --git a/testbin/src/context_opt.rs b/testbin/src/context_opt.rs index 74720fa..c80af4e 100644 --- a/testbin/src/context_opt.rs +++ b/testbin/src/context_opt.rs @@ -39,14 +39,14 @@ struct TestContext { decl_closure! { fn context_push |ctx: ContextMut| (val: u32) -> () { - let mut ctx = ctx; + let mut ctx = ctx.borrow(); ctx.value3.push(val); } } decl_closure! { - fn context_pop |ctx: ContextMut| () -> Option { - let mut ctx = ctx; + fn context_pop |ctx1: ContextMut| () -> Option { + let mut ctx = ctx1.borrow(); ctx.value3.pop() } } From c4c0ae8a73f85d1670565f2c30acfa982786d052 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 30 Dec 2025 15:02:56 +0100 Subject: [PATCH 522/527] Fixed safety issues with mutable userdata types --- core/src/macro/userdata_func.rs | 26 -------------------------- core/src/vm/table/interface.rs | 15 ++++++++++++--- core/src/vm/userdata/any.rs | 15 ++++++++++++--- core/src/vm/userdata/core.rs | 5 +++++ core/src/vm/value/function.rs | 8 ++++++-- 5 files changed, 35 insertions(+), 34 deletions(-) diff --git a/core/src/macro/userdata_func.rs b/core/src/macro/userdata_func.rs index 82197a1..10fea03 100644 --- a/core/src/macro/userdata_func.rs +++ b/core/src/macro/userdata_func.rs @@ -28,32 +28,6 @@ #[macro_export] macro_rules! decl_userdata_func { - ( - $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident, $name: ident: &Vm$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block - ) => { - impl $obj_name { - $vis fn $fn_name() -> std::result::Result<$crate::vm::userdata::core::Function, $crate::vm::userdata::Error> { - extern "C-unwind" fn _cfunc(l: $crate::ffi::lua::State) -> i32 { - fn _func $(<$lifetime>)? ($this: &mut $obj_name, $name: &$($lifetime)? $crate::vm::Vm$(, $($arg_name: $arg_ty),*)?) -> $ret_ty $code - use $crate::vm::function::IntoParam; - let this_ptr = unsafe { $crate::ffi::laux::luaL_checkudata(l, 1, <$obj_name as $crate::vm::userdata::UserDataType>::FULL_TYPE.as_ptr()) } as *mut $obj_name; - let vm = unsafe { $crate::vm::Vm::from_raw(l) }; - #[inline(always)] - extern "C-unwind" fn _vmfunc $(<$lifetime>)? (this_ptr: *mut $obj_name, vm: &$($lifetime)? $crate::vm::Vm) -> i32 { - $($crate::decl_from_param_unchecked!(vm, 2, $($arg_name: $arg_ty)*);)? - let ret = _func(unsafe { &mut *this_ptr }, vm $(, $($arg_name),*)?); - ret.into_param(vm) as _ - } - _vmfunc(this_ptr, &vm) - } - let mut f = $crate::vm::userdata::core::Builder::new($crate::c_stringify!($fn_name), _cfunc); - f.mutable(); - f.arg::<&$obj_name>(); - $($(f.arg::<$arg_ty>();)*)? - unsafe { f.build() } - } - } - }; ( $vis: vis fn $fn_name: ident $(<$lifetime: lifetime>)? ($this: ident: &mut $obj_name: ident$(, $($arg_name: ident: $arg_ty: ty),*)?) -> $ret_ty: ty $code: block ) => { diff --git a/core/src/vm/table/interface.rs b/core/src/vm/table/interface.rs index aada139..e02722f 100644 --- a/core/src/vm/table/interface.rs +++ b/core/src/vm/table/interface.rs @@ -36,7 +36,7 @@ use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; use crate::vm::table::traits::{GetTable, SetTable}; use crate::vm::table::{ImmutableTable, Table}; -use crate::vm::util::LuaType; +use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; @@ -72,7 +72,12 @@ impl<'a> FromLua<'a> for Table<'a> { } } -impl LuaType for ImmutableTable<'_> {} +impl LuaType for ImmutableTable<'_> { + fn lua_type() -> Vec { + vec![TypeName::Some("table")] + } +} + unsafe impl SimpleDrop for ImmutableTable<'_> {} unsafe impl ImmutableValue for ImmutableTable<'_> {} @@ -113,7 +118,11 @@ unsafe impl IntoLua for Table<'_> { } } -impl LuaType for Table<'_> {} +impl LuaType for Table<'_> { + fn lua_type() -> Vec { + vec![TypeName::Some("table")] + } +} impl_registry_value!(crate::vm::registry::types::Table => Table); diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index 5fee9f0..e464ca7 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -36,7 +36,7 @@ use crate::util::LuaFunction; use crate::vm::error::{Error, TypeError}; use crate::vm::table::ImmutableTable; use crate::vm::userdata::{UserData, UserDataImmutable}; -use crate::vm::util::LuaType; +use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::types::Function; use crate::vm::value::util::{check_get_metatable, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; @@ -305,5 +305,14 @@ impl<'a> FromLua<'a> for ImmutableAnyUserData<'a> { unsafe impl SimpleDrop for ImmutableAnyUserData<'_> {} unsafe impl SimpleDrop for AnyUserData<'_> {} -impl LuaType for AnyUserData<'_> {} -impl LuaType for ImmutableAnyUserData<'_> {} +impl LuaType for AnyUserData<'_> { + fn lua_type() -> Vec { + vec![TypeName::Some("userdata")] + } +} + +impl LuaType for ImmutableAnyUserData<'_> { + fn lua_type() -> Vec { + vec![TypeName::Some("userdata")] + } +} diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index d8de4a9..5d11d82 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -94,6 +94,11 @@ impl Builder { if self.is_mutable { let initial = &self.args[0]; for v in self.args.iter().skip(1) { + if v == &TypeName::Some("function") || v == &TypeName::Some("table") || v == &TypeName::Some("userdata") { + // Forbid functions, tables and userdata in mutable userdata types. + // This is to ensure no mutable userdata may call back into itself. + return Err(Error::MutViolation(self.f.name)); + } if initial == v { return Err(Error::MutViolation(self.f.name)); } diff --git a/core/src/vm/value/function.rs b/core/src/vm/value/function.rs index b34dd97..8d8f6e4 100644 --- a/core/src/vm/value/function.rs +++ b/core/src/vm/value/function.rs @@ -33,7 +33,7 @@ use crate::util::core::SimpleDrop; use crate::vm::core::util::{pcall, push_error_handler}; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::registry::{FromIndex, Set}; -use crate::vm::util::LuaType; +use crate::vm::util::{LuaType, TypeName}; use crate::vm::value::util::{check_type_equals, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; @@ -82,7 +82,11 @@ impl Debug for Function<'_> { unsafe impl SimpleDrop for Function<'_> {} -impl LuaType for Function<'_> {} +impl LuaType for Function<'_> { + fn lua_type() -> Vec { + vec![TypeName::Some("function")] + } +} impl<'a> FromParam<'a> for Function<'a> { unsafe fn from_param(vm: &'a Vm, index: i32) -> Self { From 54143f28bee3e097ad33a2ab34af7c217fe76f1c Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Tue, 30 Dec 2025 15:09:53 +0100 Subject: [PATCH 523/527] Fixed unusable RClosure API --- core/src/vm/closure/rust.rs | 36 +++++++++---------- .../safety/test_rclosure_not_send.stderr | 2 +- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/core/src/vm/closure/rust.rs b/core/src/vm/closure/rust.rs index 5c27ec9..c7dcdbb 100644 --- a/core/src/vm/closure/rust.rs +++ b/core/src/vm/closure/rust.rs @@ -50,25 +50,25 @@ impl<'a, F> Drop for Guard<'a, F> { } impl<'a> RClosure> { - pub fn from_rust_temporary R + 'a>(vm: &'a Vm, fun: F) -> (RClosure>, Guard<'a, F>) + pub fn from_rust_temporary<'b, T, R, F: Fn(T) -> R + 'a>(vm: &'a Vm, fun: F) -> (RClosure>, Guard<'a, F>) where - for<'b> T: FromParam<'b>, + T: FromParam<'b>, R: IntoParam { - let mut bx = Box::new(fun); - let rptr = &mut *bx as *mut F; - unsafe { lua_newuserdata(vm.as_ptr(), size_of::<*mut F>()) }; + let bx = Box::new(fun); + let rptr = &*bx as *const F; + unsafe { lua_newuserdata(vm.as_ptr(), size_of::<*const F>()) }; let ptr = unsafe { lua_touserdata(vm.as_ptr(), -1) } as *mut *const F; unsafe { *ptr = rptr }; let value = unsafe { AnyUserData::from_raw(vm, vm.top()) }; - extern "C-unwind" fn _cfunc R>(l: State) -> i32 + extern "C-unwind" fn _cfunc<'a, T, R, F: Fn(T) -> R>(l: State) -> i32 where - for<'a> T: FromParam<'a>, + T: FromParam<'a>, R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; - let upvalue: RawPtr<*mut F> = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; - let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let upvalue: RawPtr<*const F> = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(std::mem::transmute(&vm), 1) }; let ptr = unsafe { *upvalue.as_ptr() }; if ptr.is_null() { unsafe { luaL_error(vm.as_ptr(), c"Attempt to call a dropped temporary rust closure".as_ptr()) }; @@ -82,19 +82,19 @@ impl<'a> RClosure> { } impl RClosure> { - fn __from_rust R + 'static>(ptr: *mut F) -> Self + fn __from_rust<'a, T, R, F: Fn(T) -> R + 'static>(ptr: *mut F) -> Self where - for<'a> T: FromParam<'a>, + T: FromParam<'a>, R: IntoParam { - extern "C-unwind" fn _cfunc R>(l: State) -> i32 + extern "C-unwind" fn _cfunc<'a, T, R, F: Fn(T) -> R>(l: State) -> i32 where - for<'a> T: FromParam<'a>, + T: FromParam<'a>, R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; - let args: T = unsafe { FromParam::from_param(&vm, 1) }; + let args: T = unsafe { FromParam::from_param(std::mem::transmute(&vm), 1) }; let res = unsafe { (*upvalue.as_ptr())(args) }; res.into_param(&vm) as _ } @@ -102,9 +102,9 @@ impl RClosure> { } #[cfg(feature = "send")] - pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + pub fn from_rust<'a, T, R, F: Fn(T) -> R + 'static>(vm: &Vm, fun: F) -> Self where - for<'a> T: FromParam<'a>, + T: FromParam<'a>, R: IntoParam, F: Send, { @@ -113,9 +113,9 @@ impl RClosure> { } #[cfg(not(feature = "send"))] - pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + pub fn from_rust<'a, T, R, F: Fn(T) -> R + 'static>(vm: &Vm, fun: F) -> Self where - for<'a> T: FromParam<'a>, + T: FromParam<'a>, R: IntoParam, { let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); diff --git a/core/tests/safety/test_rclosure_not_send.stderr b/core/tests/safety/test_rclosure_not_send.stderr index a463f7c..87bdd48 100644 --- a/core/tests/safety/test_rclosure_not_send.stderr +++ b/core/tests/safety/test_rclosure_not_send.stderr @@ -27,7 +27,7 @@ note: required because it's used within this closure note: required by a bound in `closure::rust::>>::from_rust` --> src/vm/closure/rust.rs | - | pub fn from_rust R + 'static>(vm: &Vm, fun: F) -> Self + | pub fn from_rust<'a, T, R, F: Fn(T) -> R + 'static>(vm: &Vm, fun: F) -> Self | --------- required by a bound in this associated function ... | F: Send, From 65cbe50b233988a4b780f875914a816100c0a0d8 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 30 Dec 2025 14:19:14 +0000 Subject: [PATCH 524/527] Format Rust code using rustfmt --- core/src/vm/closure/context.rs | 7 ++--- core/src/vm/closure/core.rs | 2 +- core/src/vm/closure/mod.rs | 2 +- core/src/vm/closure/rust.rs | 53 ++++++++++++++++++++++------------ core/src/vm/userdata/any.rs | 2 +- core/src/vm/userdata/core.rs | 5 +++- core/tests/test_vm_closures.rs | 8 +++-- 7 files changed, 48 insertions(+), 31 deletions(-) diff --git a/core/src/vm/closure/context.rs b/core/src/vm/closure/context.rs index caa9ecd..0195dc7 100644 --- a/core/src/vm/closure/context.rs +++ b/core/src/vm/closure/context.rs @@ -218,10 +218,7 @@ impl MutPtr { pub fn borrow<'a>(self) -> Mut<'a, T> { let value = unsafe { &mut **self.0 }; unsafe { *self.0 = std::ptr::null_mut() }; - Mut { - value, - ptr: self.0 - } + Mut { value, ptr: self.0 } } } @@ -236,7 +233,7 @@ impl Deref for Ref<'_, T> { pub struct Mut<'a, T> { value: &'a mut T, - ptr: *mut *mut T + ptr: *mut *mut T, } impl<'a, T> Drop for Mut<'a, T> { diff --git a/core/src/vm/closure/core.rs b/core/src/vm/closure/core.rs index 4ddd308..614926f 100644 --- a/core/src/vm/closure/core.rs +++ b/core/src/vm/closure/core.rs @@ -29,12 +29,12 @@ use crate::ffi::lua::GLOBALSINDEX; use crate::vm::closure::{FromUpvalue, IntoUpvalue, Upvalue}; use crate::vm::function::IntoParam; +use crate::vm::userdata::AnyUserData; use crate::vm::value::types::RawPtr; use crate::vm::value::{FromLua, IntoLua}; use crate::vm::Vm; use std::ffi::OsStr; use std::path::Path; -use crate::vm::userdata::AnyUserData; macro_rules! impl_from_upvalue_using_from_lua_unchecked { ($($t: ty),*) => { diff --git a/core/src/vm/closure/mod.rs b/core/src/vm/closure/mod.rs index 80648a8..5f39d49 100644 --- a/core/src/vm/closure/mod.rs +++ b/core/src/vm/closure/mod.rs @@ -31,7 +31,7 @@ pub mod context; mod core; mod interface; pub mod rc; -pub mod types; mod rust; +pub mod types; pub use interface::*; diff --git a/core/src/vm/closure/rust.rs b/core/src/vm/closure/rust.rs index c7dcdbb..452cf78 100644 --- a/core/src/vm/closure/rust.rs +++ b/core/src/vm/closure/rust.rs @@ -26,21 +26,21 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::marker::PhantomData; use crate::ffi::laux::luaL_error; use crate::ffi::lua::{lua_newuserdata, lua_touserdata, State}; -use crate::vm::closure::FromUpvalue; use crate::vm::closure::types::RClosure; +use crate::vm::closure::FromUpvalue; use crate::vm::function::{FromParam, IntoParam}; use crate::vm::userdata::AnyUserData; use crate::vm::value::types::RawPtr; use crate::vm::Vm; +use std::marker::PhantomData; pub struct Guard<'a, F> { ptr: *mut *const F, #[allow(unused)] // Hold the box until the guard should drop and delete the Box with it. bx: Box, - vm: PhantomData<&'a Vm> + vm: PhantomData<&'a Vm>, } impl<'a, F> Drop for Guard<'a, F> { @@ -50,10 +50,13 @@ impl<'a, F> Drop for Guard<'a, F> { } impl<'a> RClosure> { - pub fn from_rust_temporary<'b, T, R, F: Fn(T) -> R + 'a>(vm: &'a Vm, fun: F) -> (RClosure>, Guard<'a, F>) + pub fn from_rust_temporary<'b, T, R, F: Fn(T) -> R + 'a>( + vm: &'a Vm, + fun: F, + ) -> (RClosure>, Guard<'a, F>) where - T: FromParam<'b>, - R: IntoParam + T: FromParam<'b>, + R: IntoParam, { let bx = Box::new(fun); let rptr = &*bx as *const F; @@ -63,34 +66,46 @@ impl<'a> RClosure> { let value = unsafe { AnyUserData::from_raw(vm, vm.top()) }; extern "C-unwind" fn _cfunc<'a, T, R, F: Fn(T) -> R>(l: State) -> i32 where - T: FromParam<'a>, - R: IntoParam, + T: FromParam<'a>, + R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr<*const F> = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; let args: T = unsafe { FromParam::from_param(std::mem::transmute(&vm), 1) }; let ptr = unsafe { *upvalue.as_ptr() }; if ptr.is_null() { - unsafe { luaL_error(vm.as_ptr(), c"Attempt to call a dropped temporary rust closure".as_ptr()) }; + unsafe { + luaL_error( + vm.as_ptr(), + c"Attempt to call a dropped temporary rust closure".as_ptr(), + ) + }; unsafe { std::hint::unreachable_unchecked() }; } let res = unsafe { (*ptr)(args) }; res.into_param(&vm) as _ } - (RClosure::new(_cfunc::, value), Guard { ptr, bx, vm: PhantomData }) + ( + RClosure::new(_cfunc::, value), + Guard { + ptr, + bx, + vm: PhantomData, + }, + ) } } impl RClosure> { fn __from_rust<'a, T, R, F: Fn(T) -> R + 'static>(ptr: *mut F) -> Self where - T: FromParam<'a>, - R: IntoParam + T: FromParam<'a>, + R: IntoParam, { extern "C-unwind" fn _cfunc<'a, T, R, F: Fn(T) -> R>(l: State) -> i32 where - T: FromParam<'a>, - R: IntoParam, + T: FromParam<'a>, + R: IntoParam, { let vm = unsafe { Vm::from_raw(l) }; let upvalue: RawPtr = unsafe { FromUpvalue::from_upvalue(&vm, 1) }; @@ -104,9 +119,9 @@ impl RClosure> { #[cfg(feature = "send")] pub fn from_rust<'a, T, R, F: Fn(T) -> R + 'static>(vm: &Vm, fun: F) -> Self where - T: FromParam<'a>, - R: IntoParam, - F: Send, + T: FromParam<'a>, + R: IntoParam, + F: Send, { let ptr = crate::vm::core::destructor::Pool::attach_send(vm, Box::new(fun)); Self::__from_rust(ptr) @@ -115,8 +130,8 @@ impl RClosure> { #[cfg(not(feature = "send"))] pub fn from_rust<'a, T, R, F: Fn(T) -> R + 'static>(vm: &Vm, fun: F) -> Self where - T: FromParam<'a>, - R: IntoParam, + T: FromParam<'a>, + R: IntoParam, { let ptr = crate::vm::core::destructor::Pool::attach(vm, Box::new(fun)); Self::__from_rust(ptr) diff --git a/core/src/vm/userdata/any.rs b/core/src/vm/userdata/any.rs index e464ca7..10dfc30 100644 --- a/core/src/vm/userdata/any.rs +++ b/core/src/vm/userdata/any.rs @@ -26,7 +26,6 @@ // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -use std::ffi::c_void; use crate::ffi::laux::luaL_testudata; use crate::ffi::lua::{ lua_pushvalue, lua_replace, lua_settop, lua_topointer, lua_touserdata, lua_type, Type, @@ -41,6 +40,7 @@ use crate::vm::value::types::Function; use crate::vm::value::util::{check_get_metatable, check_value_top}; use crate::vm::value::{FromLua, ImmutableValue, IntoLua}; use crate::vm::Vm; +use std::ffi::c_void; use std::fmt::{Debug, Display}; pub struct AnyUserData<'a> { diff --git a/core/src/vm/userdata/core.rs b/core/src/vm/userdata/core.rs index 5d11d82..c49e438 100644 --- a/core/src/vm/userdata/core.rs +++ b/core/src/vm/userdata/core.rs @@ -94,7 +94,10 @@ impl Builder { if self.is_mutable { let initial = &self.args[0]; for v in self.args.iter().skip(1) { - if v == &TypeName::Some("function") || v == &TypeName::Some("table") || v == &TypeName::Some("userdata") { + if v == &TypeName::Some("function") + || v == &TypeName::Some("table") + || v == &TypeName::Some("userdata") + { // Forbid functions, tables and userdata in mutable userdata types. // This is to ensure no mutable userdata may call back into itself. return Err(Error::MutViolation(self.f.name)); diff --git a/core/tests/test_vm_closures.rs b/core/tests/test_vm_closures.rs index d64ba78..5924074 100644 --- a/core/tests/test_vm_closures.rs +++ b/core/tests/test_vm_closures.rs @@ -28,12 +28,12 @@ #![cfg(all(feature = "root-vm", feature = "util-namespace"))] -use std::cell::Cell; use bp3d_lua::decl_closure; use bp3d_lua::util::Namespace; use bp3d_lua::vm::closure::context::{CellMut, ContextMut}; use bp3d_lua::vm::closure::types::RClosure; use bp3d_lua::vm::RootVm; +use std::cell::Cell; struct TestContext { value: i32, @@ -150,7 +150,8 @@ fn test_vm_context() { fn test_vm_rust_closure_2() { let vm = RootVm::new(); let top = vm.top(); - let (closure, _guard) = RClosure::from_rust_temporary(&vm, |val: f32| format!("this is a test: {}", val)); + let (closure, _guard) = + RClosure::from_rust_temporary(&vm, |val: f32| format!("this is a test: {}", val)); vm.set_global(c"test", closure).unwrap(); assert_eq!(top, vm.top()); let s: &str = vm.run_code(c"return test(42.42)").unwrap(); @@ -169,7 +170,8 @@ fn test_vm_rust_closure_3() { vm.set_global(c"test2", fun2)?; vm.run_code::<()>(c"assert(test() == 0)")?; vm.run_code::<()>(c"test2(42)") - }).unwrap(); + }) + .unwrap(); assert!(vm.run_code::<()>(c"test()").is_err()); assert!(vm.run_code::<()>(c"test2(4242)").is_err()); assert_eq!(value.get(), 42); From 005c0f773e1f7bb9251daafad553aeb66d0f6dcf Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 2 Jan 2026 16:02:53 +0100 Subject: [PATCH 525/527] Added support for __eq to PathWrapper --- core/src/libs/files/path.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/core/src/libs/files/path.rs b/core/src/libs/files/path.rs index 978d629..364e0c9 100644 --- a/core/src/libs/files/path.rs +++ b/core/src/libs/files/path.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2025, BlockProject 3D +// Copyright (c) 2026, BlockProject 3D // // All rights reserved. // @@ -52,32 +52,36 @@ decl_lib_func! { impl_userdata! { impl PathWrapper { - fn join(this: &Path, vm: &Vm, other: SandboxPath) -> Result { + fn join(this: &PathWrapper, vm: &Vm, other: SandboxPath) -> Result { let path = other.to_str(vm)?; Ok(PathWrapper(this.0.join(path.as_ref()))) } - fn with_extension(this: &Path, extension: &str) -> PathWrapper { + fn with_extension(this: &PathWrapper, extension: &str) -> PathWrapper { PathWrapper(this.0.with_extension(extension)) } - fn with_name(this: &Path, name: &str) -> PathWrapper { + fn with_name(this: &PathWrapper, name: &str) -> PathWrapper { let mut path = this.0.clone(); path.set_file_name(name); PathWrapper(path) } - fn name(this: &Path) -> Option { + fn name(this: &PathWrapper) -> Option { this.0.file_name().map(|v| v.to_string_lossy().into()) } - fn extension(this: &Path) -> Option { + fn extension(this: &PathWrapper) -> Option { this.0.extension().map(|v| v.to_string_lossy().into()) } - fn __tostring<'a>(this: &Path, vm: &Vm) -> Cow<'a, str> { + fn __tostring<'a>(this: &PathWrapper, vm: &Vm) -> Cow<'a, str> { sandbox(vm, &this.0).unwrap_or(Cow::Borrowed("")) } + + fn __eq(this: &PathWrapper, other: SandboxPath) -> bool { + SandboxPath::Path(this.path()) == other + } } static { [fn new]; From 43ac23e9850c4b3a86aae710ebc8c875d1a54c11 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Fri, 2 Jan 2026 16:23:27 +0100 Subject: [PATCH 526/527] Added rename and delete functions to files lib --- core/src/libs/files/lib.rs | 37 +++++++++++++++++++++++++++++++++++-- core/src/libs/files/mod.rs | 4 +++- definitions/bp3d.files.lua | 11 +++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) diff --git a/core/src/libs/files/lib.rs b/core/src/libs/files/lib.rs index 94ed831..1e4dbd2 100644 --- a/core/src/libs/files/lib.rs +++ b/core/src/libs/files/lib.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2025, BlockProject 3D +// Copyright (c) 2026, BlockProject 3D // // All rights reserved. // @@ -32,7 +32,7 @@ use crate::libs::files::SandboxPath; use crate::vm::table::Table; use bp3d_util::simple_error; use std::fs::File; -use std::io::Read; +use std::io::{ErrorKind, Read}; const MAX_FILE_SIZE: usize = 5000000; //5Mb @@ -197,3 +197,36 @@ decl_lib_func! { Ok(tbl) } } + +decl_lib_func! { + pub fn delete(vm: &Vm, path: SandboxPath) -> Result<(), Error> { + if !(path.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let path = path.to_path(vm).map_err(|_| Error::Sandbox)?; + std::fs::remove_file(path).map_err(Error::Io) + } +} + +decl_lib_func! { + pub fn rename(vm: &Vm, src: SandboxPath, dst: SandboxPath) -> Result<(), Error> { + if !(src.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + if !(src.access(vm) & Permissions::R) { + return Err(Error::Permission); + } + if !(dst.access(vm) & Permissions::W) { + return Err(Error::Permission); + } + let src_path = src.to_path(vm).map_err(|_| Error::Sandbox)?; + let dst_path = dst.to_path(vm).map_err(|_| Error::Sandbox)?; + if !src_path.exists() { + return Err(Error::Io(std::io::Error::new(ErrorKind::NotFound, "source file not found"))); + } + if dst_path.exists() { + return Err(Error::Io(std::io::Error::new(ErrorKind::AlreadyExists, "destination file already exists"))); + } + std::fs::rename(src_path, dst_path).map_err(Error::Io) + } +} diff --git a/core/src/libs/files/mod.rs b/core/src/libs/files/mod.rs index 9de99be..60dc6cc 100644 --- a/core/src/libs/files/mod.rs +++ b/core/src/libs/files/mod.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2025, BlockProject 3D +// Copyright (c) 2026, BlockProject 3D // // All rights reserved. // @@ -57,6 +57,8 @@ impl Lib for Files { ("createDir", RFunction::wrap(lib::create_dir)), ("deleteDir", RFunction::wrap(lib::delete_dir)), ("access", RFunction::wrap(lib::lua_access)), + ("delete", RFunction::wrap(lib::delete)), + ("rename", RFunction::wrap(lib::rename)) ]) } } diff --git a/definitions/bp3d.files.lua b/definitions/bp3d.files.lua index 99d5a35..10365fb 100644 --- a/definitions/bp3d.files.lua +++ b/definitions/bp3d.files.lua @@ -166,3 +166,14 @@ bp3d.files.list = function(path) end --- @param path string | Path --- @return { r: boolean, w: boolean, x: boolean } bp3d.files.access = function(path) end + +--- Deletes a single file. +--- +--- @param path string | Path file path. +bp3d.files.delete = function(path) end + +--- Renames a file. This function fails if src does not exist or if dst already exists. +--- +--- @param src string | Path the source path. +--- @param dst string | Path the destination path. +bp3d.files.rename = function(src, dst) end From 1b934b46eb685a41218c00ad7d91170e9d000a49 Mon Sep 17 00:00:00 2001 From: Yuri Edward Date: Sun, 25 Jan 2026 22:16:46 +0100 Subject: [PATCH 527/527] Added lua_loadx binding --- core/src/ffi/lua.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/core/src/ffi/lua.rs b/core/src/ffi/lua.rs index 0613b8b..a0ba236 100644 --- a/core/src/ffi/lua.rs +++ b/core/src/ffi/lua.rs @@ -1,4 +1,4 @@ -// Copyright (c) 2025, BlockProject 3D +// Copyright (c) 2026, BlockProject 3D // // All rights reserved. // @@ -185,10 +185,18 @@ extern "C" { pub fn lua_pcall(l: State, nargs: c_int, nresults: c_int, errfunc: c_int) -> ThreadStatus; pub fn lua_cpcall(l: State, func: CFunction, ud: *mut c_void) -> c_int; pub fn lua_load( + l: State, + reader: Reader, + dt: *mut c_void, + chunkname: *const c_char + ) -> ThreadStatus; + + pub fn lua_loadx( l: State, reader: Reader, dt: *mut c_void, chunkname: *const c_char, + mode: *const c_char ) -> ThreadStatus; pub fn lua_dump(l: State, writer: Writer, data: *mut c_void) -> c_int;