Skip to content

Commit 847bcbd

Browse files
committed
support cclosure
1 parent e644e2b commit 847bcbd

File tree

11 files changed

+698
-187
lines changed

11 files changed

+698
-187
lines changed

crates/luars/src/gc/gc_object.rs

Lines changed: 70 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,78 @@ pub struct GcTable {
165165
pub data: LuaTable,
166166
}
167167

168-
/// Lua function with embedded GC header
168+
/// C Function type - Rust function callable from Lua
169+
pub type CFunction = fn(&mut crate::lua_vm::LuaVM) -> crate::lua_vm::LuaResult<crate::lua_value::MultiValue>;
170+
171+
/// Function body - either Lua bytecode or C function
172+
pub enum FunctionBody {
173+
/// Lua function with bytecode chunk
174+
Lua(Rc<Chunk>),
175+
/// C function (native Rust function) - no upvalues
176+
C(CFunction),
177+
/// C closure with single inline upvalue (fast path for common case)
178+
/// Used by coroutine.wrap, ipairs iterator, etc.
179+
CClosureInline1(CFunction, LuaValue),
180+
}
181+
182+
/// Unified function with embedded GC header
183+
/// Supports both Lua closures and C closures (with upvalues)
169184
pub struct GcFunction {
170185
pub header: GcHeader,
171-
pub chunk: Rc<Chunk>,
172-
pub upvalues: Vec<UpvalueId>, // Upvalue IDs, not Rc
186+
pub body: FunctionBody,
187+
pub upvalues: Vec<UpvalueId>, // Upvalue IDs - used for Lua closures and C closures with >1 upvalue
188+
}
189+
190+
impl GcFunction {
191+
/// Check if this is a C function (any C variant)
192+
#[inline(always)]
193+
pub fn is_c_function(&self) -> bool {
194+
matches!(self.body, FunctionBody::C(_) | FunctionBody::CClosureInline1(_, _))
195+
}
196+
197+
/// Check if this is a Lua function
198+
#[inline(always)]
199+
pub fn is_lua_function(&self) -> bool {
200+
matches!(self.body, FunctionBody::Lua(_))
201+
}
202+
203+
/// Get the chunk if this is a Lua function
204+
#[inline(always)]
205+
pub fn chunk(&self) -> Option<&Rc<Chunk>> {
206+
match &self.body {
207+
FunctionBody::Lua(chunk) => Some(chunk),
208+
_ => None,
209+
}
210+
}
211+
212+
/// Get the chunk reference for Lua functions (panics if C function)
213+
/// Use this in contexts where we know it's a Lua function
214+
#[inline(always)]
215+
pub fn lua_chunk(&self) -> &Rc<Chunk> {
216+
match &self.body {
217+
FunctionBody::Lua(chunk) => chunk,
218+
_ => panic!("Called lua_chunk() on a C function"),
219+
}
220+
}
221+
222+
/// Get the C function pointer if this is any C function variant
223+
#[inline(always)]
224+
pub fn c_function(&self) -> Option<CFunction> {
225+
match &self.body {
226+
FunctionBody::C(f) => Some(*f),
227+
FunctionBody::CClosureInline1(f, _) => Some(*f),
228+
FunctionBody::Lua(_) => None,
229+
}
230+
}
231+
232+
/// Get inline upvalue 1 for CClosureInline1
233+
#[inline(always)]
234+
pub fn inline_upvalue1(&self) -> Option<LuaValue> {
235+
match &self.body {
236+
FunctionBody::CClosureInline1(_, uv) => Some(*uv),
237+
_ => None,
238+
}
239+
}
173240
}
174241

175242
/// Upvalue state - uses absolute stack index like Lua C implementation

crates/luars/src/gc/mod.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,8 @@ impl GC {
532532
GcObjectType::Function => {
533533
if let Some(func) = pool.functions.get(gc_id.index()) {
534534
let upvalue_ids = func.upvalues.clone();
535-
let constants = func.chunk.constants.clone();
535+
// C closures don't have constants
536+
let constants = func.chunk().map(|c| c.constants.clone()).unwrap_or_default();
536537

537538
if let Some(f) = pool.functions.get_mut(gc_id.index()) {
538539
if f.header.is_gray() {
@@ -1146,7 +1147,9 @@ impl GC {
11461147
let (should_mark, upvalue_ids, constants) = {
11471148
if let Some(func) = pool.functions.get(id.0) {
11481149
if func.header.is_white() {
1149-
(true, func.upvalues.clone(), func.chunk.constants.clone())
1150+
// C closures don't have constants
1151+
let consts = func.chunk().map(|c| c.constants.clone()).unwrap_or_default();
1152+
(true, func.upvalues.clone(), consts)
11501153
} else {
11511154
(false, vec![], vec![])
11521155
}

crates/luars/src/gc/object_pool.rs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ use crate::{
1313
FunctionId, GcFunction, GcHeader, GcString, GcTable, GcThread, GcUpvalue, LuaString, LuaTable,
1414
LuaValue, StringId, TableId, ThreadId, UpvalueId, UpvalueState, UserdataId,
1515
};
16+
use crate::gc::gc_object::{FunctionBody, CFunction};
1617
use std::rc::Rc;
1718

1819
// ============ Pool Storage ============
@@ -1025,15 +1026,39 @@ impl ObjectPool {
10251026

10261027
// ==================== Function Operations ====================
10271028

1029+
/// Create a Lua function (closure with bytecode chunk)
10281030
#[inline]
10291031
pub fn create_function(&mut self, chunk: Rc<Chunk>, upvalue_ids: Vec<UpvalueId>) -> FunctionId {
10301032
let gc_func = GcFunction {
10311033
header: GcHeader::default(),
1032-
chunk,
1034+
body: FunctionBody::Lua(chunk),
10331035
upvalues: upvalue_ids,
10341036
};
10351037
FunctionId(self.functions.alloc(gc_func))
10361038
}
1039+
1040+
/// Create a C closure (native function with upvalues)
1041+
#[inline]
1042+
pub fn create_c_closure(&mut self, func: CFunction, upvalue_ids: Vec<UpvalueId>) -> FunctionId {
1043+
let gc_func = GcFunction {
1044+
header: GcHeader::default(),
1045+
body: FunctionBody::C(func),
1046+
upvalues: upvalue_ids,
1047+
};
1048+
FunctionId(self.functions.alloc(gc_func))
1049+
}
1050+
1051+
/// Create a C closure with single inline upvalue (fast path)
1052+
/// This avoids UpvalueId allocation for common single-upvalue C closures
1053+
#[inline]
1054+
pub fn create_c_closure_inline1(&mut self, func: CFunction, upvalue: LuaValue) -> FunctionId {
1055+
let gc_func = GcFunction {
1056+
header: GcHeader::default(),
1057+
body: FunctionBody::CClosureInline1(func, upvalue),
1058+
upvalues: Vec::new(),
1059+
};
1060+
FunctionId(self.functions.alloc(gc_func))
1061+
}
10371062

10381063
#[inline(always)]
10391064
pub fn get_function(&self, id: FunctionId) -> Option<&GcFunction> {

0 commit comments

Comments
 (0)