pr6.3
This commit is contained in:
parent
35f5b8fa86
commit
29736e9577
@ -161,6 +161,15 @@ pub enum OpCode {
|
|||||||
/// Pops capture_count values (top-first), preserves order as [captured_1..captured_N]
|
/// Pops capture_count values (top-first), preserves order as [captured_1..captured_N]
|
||||||
/// and stores them inside the closure environment. Pushes a HeapRef to the closure.
|
/// and stores them inside the closure environment. Pushes a HeapRef to the closure.
|
||||||
MakeClosure = 0x52,
|
MakeClosure = 0x52,
|
||||||
|
/// Calls a closure value with hidden arg0 semantics (Model B).
|
||||||
|
/// Operand: arg_count (u32) — number of user-supplied args (excludes hidden arg0)
|
||||||
|
/// Stack before: [..., argN, ..., arg1, closure_ref]
|
||||||
|
/// Behavior:
|
||||||
|
/// - Pops `closure_ref` and validates it is a Closure.
|
||||||
|
/// - Pops `arg_count` user args.
|
||||||
|
/// - Fetches `fn_id` from the closure and creates a new call frame.
|
||||||
|
/// - Injects hidden arg0 = closure_ref, followed by user args as arg1..argN.
|
||||||
|
CallClosure = 0x53,
|
||||||
|
|
||||||
// --- 6.8 Peripherals and System ---
|
// --- 6.8 Peripherals and System ---
|
||||||
/// Invokes a system function (Firmware/OS).
|
/// Invokes a system function (Firmware/OS).
|
||||||
@ -222,6 +231,7 @@ impl TryFrom<u16> for OpCode {
|
|||||||
0x50 => Ok(OpCode::Call),
|
0x50 => Ok(OpCode::Call),
|
||||||
0x51 => Ok(OpCode::Ret),
|
0x51 => Ok(OpCode::Ret),
|
||||||
0x52 => Ok(OpCode::MakeClosure),
|
0x52 => Ok(OpCode::MakeClosure),
|
||||||
|
0x53 => Ok(OpCode::CallClosure),
|
||||||
0x70 => Ok(OpCode::Syscall),
|
0x70 => Ok(OpCode::Syscall),
|
||||||
0x80 => Ok(OpCode::FrameSync),
|
0x80 => Ok(OpCode::FrameSync),
|
||||||
_ => Err(format!("Invalid OpCode: 0x{:04X}", value)),
|
_ => Err(format!("Invalid OpCode: 0x{:04X}", value)),
|
||||||
@ -279,6 +289,7 @@ impl OpCode {
|
|||||||
OpCode::Call => 5,
|
OpCode::Call => 5,
|
||||||
OpCode::Ret => 4,
|
OpCode::Ret => 4,
|
||||||
OpCode::MakeClosure => 8,
|
OpCode::MakeClosure => 8,
|
||||||
|
OpCode::CallClosure => 6,
|
||||||
OpCode::Syscall => 1,
|
OpCode::Syscall => 1,
|
||||||
OpCode::FrameSync => 1,
|
OpCode::FrameSync => 1,
|
||||||
}
|
}
|
||||||
|
|||||||
@ -474,6 +474,18 @@ impl OpCodeSpecExt for OpCode {
|
|||||||
may_trap: false,
|
may_trap: false,
|
||||||
is_safepoint: false,
|
is_safepoint: false,
|
||||||
},
|
},
|
||||||
|
OpCode::CallClosure => OpcodeSpec {
|
||||||
|
name: "CALL_CLOSURE",
|
||||||
|
// One u32 immediate: arg_count (user args, excludes hidden arg0)
|
||||||
|
imm_bytes: 4,
|
||||||
|
// Dynamic: pops closure_ref + arg_count; keep 0 in spec layer
|
||||||
|
pops: 0,
|
||||||
|
pushes: 0,
|
||||||
|
is_branch: false,
|
||||||
|
is_terminator: false,
|
||||||
|
may_trap: true,
|
||||||
|
is_safepoint: false,
|
||||||
|
},
|
||||||
OpCode::Syscall => OpcodeSpec {
|
OpCode::Syscall => OpcodeSpec {
|
||||||
name: "SYSCALL",
|
name: "SYSCALL",
|
||||||
imm_bytes: 4,
|
imm_bytes: 4,
|
||||||
|
|||||||
@ -7,6 +7,7 @@ use prometeu_bytecode::ProgramImage;
|
|||||||
use prometeu_bytecode::Value;
|
use prometeu_bytecode::Value;
|
||||||
use crate::roots::{RootVisitor, visit_value_for_roots};
|
use crate::roots::{RootVisitor, visit_value_for_roots};
|
||||||
use crate::heap::Heap;
|
use crate::heap::Heap;
|
||||||
|
use crate::object::ObjectKind;
|
||||||
use prometeu_bytecode::{
|
use prometeu_bytecode::{
|
||||||
TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB,
|
TRAP_BAD_RET_SLOTS, TRAP_DIV_ZERO, TRAP_INVALID_FUNC, TRAP_INVALID_SYSCALL, TRAP_OOB,
|
||||||
TRAP_STACK_UNDERFLOW, TRAP_TYPE, TrapInfo,
|
TRAP_STACK_UNDERFLOW, TRAP_TYPE, TrapInfo,
|
||||||
@ -427,6 +428,109 @@ impl VirtualMachine {
|
|||||||
let href = self.heap.alloc_closure(fn_id, &temp);
|
let href = self.heap.alloc_closure(fn_id, &temp);
|
||||||
self.push(Value::HeapRef(href));
|
self.push(Value::HeapRef(href));
|
||||||
}
|
}
|
||||||
|
OpCode::CallClosure => {
|
||||||
|
// Operand carries the number of user-supplied arguments (arg1..argN).
|
||||||
|
let user_arg_count = instr
|
||||||
|
.imm_u32()
|
||||||
|
.map_err(|e| LogicalFrameEndingReason::Panic(format!("{:?}", e)))?
|
||||||
|
as usize;
|
||||||
|
|
||||||
|
// Pop the closure reference from the stack (top of stack).
|
||||||
|
let clos_val = self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?;
|
||||||
|
let href = match clos_val {
|
||||||
|
Value::HeapRef(h) => h,
|
||||||
|
other => {
|
||||||
|
return Err(self.trap(
|
||||||
|
TRAP_TYPE,
|
||||||
|
opcode as u16,
|
||||||
|
format!(
|
||||||
|
"CALL_CLOSURE expects a closure handle at TOS, got {:?}",
|
||||||
|
other
|
||||||
|
),
|
||||||
|
start_pc as u32,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Validate that the heap object is indeed a Closure.
|
||||||
|
let header = self.heap.header(href).ok_or_else(|| {
|
||||||
|
self.trap(
|
||||||
|
TRAP_OOB,
|
||||||
|
opcode as u16,
|
||||||
|
format!("Invalid heap handle in CALL_CLOSURE: {:?}", href),
|
||||||
|
start_pc as u32,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
if header.kind != ObjectKind::Closure {
|
||||||
|
return Err(self.trap(
|
||||||
|
TRAP_TYPE,
|
||||||
|
opcode as u16,
|
||||||
|
format!(
|
||||||
|
"CALL_CLOSURE on non-closure object kind {:?}",
|
||||||
|
header.kind
|
||||||
|
),
|
||||||
|
start_pc as u32,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pop user arguments from the operand stack (top-first), then fix order.
|
||||||
|
let mut user_args: Vec<Value> = Vec::with_capacity(user_arg_count);
|
||||||
|
for _ in 0..user_arg_count {
|
||||||
|
user_args.push(self.pop().map_err(|e| LogicalFrameEndingReason::Panic(e))?);
|
||||||
|
}
|
||||||
|
user_args.reverse(); // Now in logical order: arg1..argN
|
||||||
|
|
||||||
|
// Resolve target function id from the closure payload.
|
||||||
|
let fn_id = self.heap.closure_fn_id(href).ok_or_else(|| {
|
||||||
|
LogicalFrameEndingReason::Panic(
|
||||||
|
"Internal error: malformed closure object (missing fn_id)".into(),
|
||||||
|
)
|
||||||
|
})? as usize;
|
||||||
|
|
||||||
|
let callee = self.program.functions.get(fn_id).ok_or_else(|| {
|
||||||
|
self.trap(
|
||||||
|
TRAP_INVALID_FUNC,
|
||||||
|
opcode as u16,
|
||||||
|
format!("Invalid func_id {} from closure", fn_id),
|
||||||
|
start_pc as u32,
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
// Copy required fields to drop the immutable borrow before mutating self
|
||||||
|
let callee_param_slots = callee.param_slots as usize;
|
||||||
|
let callee_local_slots = callee.local_slots as usize;
|
||||||
|
let callee_code_offset = callee.code_offset as usize;
|
||||||
|
|
||||||
|
// Validate arity: param_slots must equal hidden arg0 + user_arg_count.
|
||||||
|
let expected_params = 1usize + user_arg_count;
|
||||||
|
if callee_param_slots != expected_params {
|
||||||
|
return Err(self.trap(
|
||||||
|
TRAP_TYPE,
|
||||||
|
opcode as u16,
|
||||||
|
format!(
|
||||||
|
"CALL_CLOSURE arg_count mismatch: function expects {} total params (including hidden arg0), got hidden+{}",
|
||||||
|
callee_param_slots, expected_params
|
||||||
|
),
|
||||||
|
start_pc as u32,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prepare the operand stack to match the direct CALL convention:
|
||||||
|
// push hidden arg0 (closure_ref) followed by arg1..argN.
|
||||||
|
self.push(Value::HeapRef(href));
|
||||||
|
for v in user_args.into_iter() { self.push(v); }
|
||||||
|
|
||||||
|
let stack_base = self
|
||||||
|
.operand_stack
|
||||||
|
.len()
|
||||||
|
.checked_sub(callee_param_slots)
|
||||||
|
.ok_or_else(|| LogicalFrameEndingReason::Panic("Stack underflow".into()))?;
|
||||||
|
|
||||||
|
// Allocate and zero-init local slots
|
||||||
|
for _ in 0..callee_local_slots { self.operand_stack.push(Value::Null); }
|
||||||
|
|
||||||
|
self.call_stack.push(CallFrame { return_pc: self.pc as u32, stack_base, func_idx: fn_id });
|
||||||
|
self.pc = callee_code_offset;
|
||||||
|
}
|
||||||
OpCode::PushConst => {
|
OpCode::PushConst => {
|
||||||
let idx = instr
|
let idx = instr
|
||||||
.imm_u32()
|
.imm_u32()
|
||||||
@ -2823,4 +2927,161 @@ mod tests {
|
|||||||
assert_eq!(env[1], Value::Int32(2));
|
assert_eq!(env[1], Value::Int32(2));
|
||||||
assert_eq!(env[2], Value::Int32(3));
|
assert_eq!(env[2], Value::Int32(3));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_closure_returns_constant() {
|
||||||
|
use prometeu_bytecode::{FunctionMeta, Value};
|
||||||
|
|
||||||
|
// F0 (entry): MAKE_CLOSURE fn=1, cap=0; CALL_CLOSURE argc=0; HALT
|
||||||
|
// F1 (callee): PUSH_I32 7; RET
|
||||||
|
let mut rom = Vec::new();
|
||||||
|
let f0_start = 0usize;
|
||||||
|
rom.extend_from_slice(&(OpCode::MakeClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&1u32.to_le_bytes()); // fn_id
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // capture_count
|
||||||
|
rom.extend_from_slice(&(OpCode::CallClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // argc = 0 user args
|
||||||
|
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||||
|
let f0_len = rom.len() - f0_start;
|
||||||
|
|
||||||
|
// F1 code
|
||||||
|
let f1_start = rom.len() as u32;
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&7i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||||
|
let f1_len = rom.len() as u32 - f1_start;
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
vm.program.functions = std::sync::Arc::from(vec![
|
||||||
|
FunctionMeta { code_offset: f0_start as u32, code_len: f0_len as u32, ..Default::default() },
|
||||||
|
FunctionMeta { code_offset: f1_start, code_len: f1_len, param_slots: 1, return_slots: 1, ..Default::default() },
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||||
|
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||||
|
assert_eq!(vm.operand_stack.len(), 1);
|
||||||
|
assert_eq!(vm.operand_stack[0], Value::Int32(7));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_closure_with_captures_ignored() {
|
||||||
|
use prometeu_bytecode::{FunctionMeta, Value};
|
||||||
|
|
||||||
|
// F0: PUSH_I32 123; MAKE_CLOSURE fn=1 cap=1; CALL_CLOSURE 0; HALT
|
||||||
|
// F1: PUSH_I32 42; RET
|
||||||
|
let mut rom = Vec::new();
|
||||||
|
let f0_start = 0usize;
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&123i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::MakeClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&1u32.to_le_bytes()); // fn_id
|
||||||
|
rom.extend_from_slice(&1u32.to_le_bytes()); // capture_count
|
||||||
|
rom.extend_from_slice(&(OpCode::CallClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // argc = 0
|
||||||
|
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||||
|
let f0_len = rom.len() - f0_start;
|
||||||
|
|
||||||
|
let f1_start = rom.len() as u32;
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&42i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||||
|
let f1_len = rom.len() as u32 - f1_start;
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
vm.program.functions = std::sync::Arc::from(vec![
|
||||||
|
FunctionMeta { code_offset: f0_start as u32, code_len: f0_len as u32, ..Default::default() },
|
||||||
|
FunctionMeta { code_offset: f1_start, code_len: f1_len, param_slots: 1, return_slots: 1, ..Default::default() },
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(100, &mut native, &mut ctx).unwrap();
|
||||||
|
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||||
|
assert_eq!(vm.operand_stack, vec![Value::Int32(42)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_call_closure_on_non_closure_traps() {
|
||||||
|
use prometeu_bytecode::FunctionMeta;
|
||||||
|
|
||||||
|
// F0: PUSH_I32 1; CALL_CLOSURE 0; HALT -> should TRAP_TYPE on CALL_CLOSURE
|
||||||
|
let mut rom = Vec::new();
|
||||||
|
let f0_start = 0usize;
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&1i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::CallClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes());
|
||||||
|
// Leave HALT for after run to ensure we trap before
|
||||||
|
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||||
|
let f0_len = rom.len() - f0_start;
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
vm.program.functions = std::sync::Arc::from(vec![FunctionMeta { code_offset: f0_start as u32, code_len: f0_len as u32, ..Default::default() }]);
|
||||||
|
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(10, &mut native, &mut ctx).unwrap();
|
||||||
|
match report.reason {
|
||||||
|
LogicalFrameEndingReason::Trap(info) => {
|
||||||
|
assert_eq!(info.code, TRAP_TYPE);
|
||||||
|
assert_eq!(info.opcode, OpCode::CallClosure as u16);
|
||||||
|
}
|
||||||
|
other => panic!("Expected Trap(TYPE) from CALL_CLOSURE on non-closure, got {:?}", other),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_nested_call_closure() {
|
||||||
|
use prometeu_bytecode::{FunctionMeta, Value};
|
||||||
|
|
||||||
|
// F0: MAKE_CLOSURE fn=1 cap=0; CALL_CLOSURE 0; CALL_CLOSURE 0; HALT
|
||||||
|
// F1: MAKE_CLOSURE fn=2 cap=0; RET // returns a closure
|
||||||
|
// F2: PUSH_I32 55; RET // returns constant
|
||||||
|
let mut rom = Vec::new();
|
||||||
|
// F0
|
||||||
|
let f0_start = 0usize;
|
||||||
|
rom.extend_from_slice(&(OpCode::MakeClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&1u32.to_le_bytes()); // fn_id = 1
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // cap=0
|
||||||
|
rom.extend_from_slice(&(OpCode::CallClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // argc=0 -> pushes a closure from F1
|
||||||
|
rom.extend_from_slice(&(OpCode::CallClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // argc=0 -> call returned closure F2
|
||||||
|
rom.extend_from_slice(&(OpCode::Halt as u16).to_le_bytes());
|
||||||
|
let f0_len = rom.len() - f0_start;
|
||||||
|
|
||||||
|
// F1
|
||||||
|
let f1_start = rom.len() as u32;
|
||||||
|
rom.extend_from_slice(&(OpCode::MakeClosure as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&2u32.to_le_bytes()); // fn_id = 2
|
||||||
|
rom.extend_from_slice(&0u32.to_le_bytes()); // cap=0
|
||||||
|
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes()); // return the HeapRef on stack
|
||||||
|
let f1_len = rom.len() as u32 - f1_start;
|
||||||
|
|
||||||
|
// F2
|
||||||
|
let f2_start = rom.len() as u32;
|
||||||
|
rom.extend_from_slice(&(OpCode::PushI32 as u16).to_le_bytes());
|
||||||
|
rom.extend_from_slice(&55i32.to_le_bytes());
|
||||||
|
rom.extend_from_slice(&(OpCode::Ret as u16).to_le_bytes());
|
||||||
|
let f2_len = rom.len() as u32 - f2_start;
|
||||||
|
|
||||||
|
let mut vm = new_test_vm(rom.clone(), vec![]);
|
||||||
|
vm.program.functions = std::sync::Arc::from(vec![
|
||||||
|
FunctionMeta { code_offset: f0_start as u32, code_len: f0_len as u32, ..Default::default() },
|
||||||
|
FunctionMeta { code_offset: f1_start, code_len: f1_len, param_slots: 1, return_slots: 1, ..Default::default() },
|
||||||
|
FunctionMeta { code_offset: f2_start, code_len: f2_len, param_slots: 1, return_slots: 1, ..Default::default() },
|
||||||
|
]);
|
||||||
|
|
||||||
|
let mut native = MockNative;
|
||||||
|
let mut ctx = HostContext::new(None);
|
||||||
|
vm.prepare_call("0");
|
||||||
|
let report = vm.run_budget(200, &mut native, &mut ctx).unwrap();
|
||||||
|
assert_eq!(report.reason, LogicalFrameEndingReason::Halted);
|
||||||
|
assert_eq!(vm.operand_stack, vec![Value::Int32(55)]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,96 +1,3 @@
|
|||||||
# PR-6.3 — CALL_CLOSURE (Model B Hidden Arg0)
|
|
||||||
|
|
||||||
## Briefing
|
|
||||||
|
|
||||||
Closures must be dynamically invokable.
|
|
||||||
|
|
||||||
Under Model B, invocation semantics are:
|
|
||||||
|
|
||||||
* The closure object itself becomes hidden `arg0`.
|
|
||||||
* User-supplied arguments become `arg1..argN`.
|
|
||||||
* Captures remain inside the closure and are accessed explicitly.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Target
|
|
||||||
|
|
||||||
Introduce opcode:
|
|
||||||
|
|
||||||
`CALL_CLOSURE arg_count`
|
|
||||||
|
|
||||||
Stack before call:
|
|
||||||
|
|
||||||
```
|
|
||||||
[..., argN, ..., arg1, closure_ref]
|
|
||||||
```
|
|
||||||
|
|
||||||
Execution steps:
|
|
||||||
|
|
||||||
1. Pop `closure_ref`.
|
|
||||||
2. Validate object is `ObjectKind::Closure`.
|
|
||||||
3. Pop `arg_count` arguments.
|
|
||||||
4. Read `fn_id` from closure object.
|
|
||||||
5. Create new call frame:
|
|
||||||
|
|
||||||
* Inject `closure_ref` as `arg0`.
|
|
||||||
* Append user arguments as `arg1..argN`.
|
|
||||||
6. Jump to function entry.
|
|
||||||
|
|
||||||
No environment copying into locals.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Work Items
|
|
||||||
|
|
||||||
1. Add `CALL_CLOSURE` opcode.
|
|
||||||
2. Implement dispatch logic.
|
|
||||||
3. Integrate with call frame creation.
|
|
||||||
4. Ensure stack discipline is preserved.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Acceptance Checklist
|
|
||||||
|
|
||||||
* [ ] CALL_CLOSURE implemented.
|
|
||||||
* [ ] closure_ref validated.
|
|
||||||
* [ ] arg_count respected.
|
|
||||||
* [ ] Hidden arg0 injected correctly.
|
|
||||||
* [ ] Errors thrown on non-closure call.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Tests
|
|
||||||
|
|
||||||
1. Closure returning constant.
|
|
||||||
2. Closure capturing value and using it.
|
|
||||||
3. Calling non-closure results in trap.
|
|
||||||
4. Nested closure calls work.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Junie Instructions
|
|
||||||
|
|
||||||
You MAY:
|
|
||||||
|
|
||||||
* Modify interpreter call logic.
|
|
||||||
* Add tests.
|
|
||||||
|
|
||||||
You MUST NOT:
|
|
||||||
|
|
||||||
* Change stack model.
|
|
||||||
* Introduce coroutine semantics.
|
|
||||||
* Modify GC.
|
|
||||||
|
|
||||||
If function signature metadata is insufficient to validate arg_count, STOP and ask.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## Definition of Done
|
|
||||||
|
|
||||||
Closures can be dynamically invoked with hidden arg0 semantics.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
# PR-6.4 — GC Traversal for Closures (Model B)
|
# PR-6.4 — GC Traversal for Closures (Model B)
|
||||||
|
|
||||||
## Briefing
|
## Briefing
|
||||||
|
|||||||
@ -1,3 +1,5 @@
|
|||||||
|
vamos as PR7s todas em um unico canvas markdown ingles, devem ser auto contidas com briefing, alvo, checklist, test quando necessario e comandos do que a Junie pode ou nao fazer (Junie eh task operator nao arquiteta ou assume nada, questiona quando necessario).
|
||||||
|
|
||||||
7 — Coroutines (único modelo de concorrência, cooperativo)
|
7 — Coroutines (único modelo de concorrência, cooperativo)
|
||||||
|
|
||||||
7.1. Definir objeto Coroutine no heap: stack/frames próprios, status, wake time, mailbox/queue se existir.
|
7.1. Definir objeto Coroutine no heap: stack/frames próprios, status, wake time, mailbox/queue se existir.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user